From 224d346f0982894254e3893fe0e325eb15f0d573 Mon Sep 17 00:00:00 2001 From: "Tony Arcieri (iqlusion)" Date: Mon, 12 Jul 2021 14:33:51 -0700 Subject: [PATCH] yubikey-cli v0.4.0 (#284) --- Cargo.lock | 2 +- cli/CHANGELOG.md | 13 ++++-- cli/Cargo.toml | 2 +- cli/src/commands/status.rs | 4 +- cli/src/lib.rs | 91 +++----------------------------------- cli/src/terminal.rs | 83 ++++++++++++++++++++++++++++++++-- 6 files changed, 99 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e49ceb..4788b9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -998,7 +998,7 @@ dependencies = [ [[package]] name = "yubikey-cli" -version = "0.4.0-pre" +version = "0.4.0" dependencies = [ "env_logger", "gumdrop", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index d7b00c1..6b7ed0e 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -4,16 +4,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.4.0 (2021-07-12) +### Changed +- Switch to renamed `yubikey` crate ([#283]) +- Bump MSRV to 1.51+ ([#283]) + +[#283]: https://github.com/iqlusioninc/yubikey.rs/pull/283 + ## 0.3.0 (2021-03-22) ### Changed -- Bump `yubikey` dependency to v0.3 ([#240]) +- Bump `yubikey-piv` dependency to v0.3 ([#240]) [#240]: https://github.com/iqlusioninc/yubikey.rs/pull/240 ## 0.2.0 (2021-01-30) ### Changed - Bump MSRV to 1.46+ ([#208]) -- Bump `yubikey` dependency to v0.2 ([#220]) +- Bump `yubikey-piv` dependency to v0.2 ([#220]) [#208]: https://github.com/iqlusioninc/yubikey.rs/pull/208 [#220]: https://github.com/iqlusioninc/yubikey.rs/pull/220 @@ -23,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `status` command ([#72], [#74]) ### Changed -- Bump `yubikey` to v0.1.0 ([#180]) +- Bump `yubikey-piv` to v0.1.0 ([#180]) - Bump `x509-parser` to v0.8 ([#181]) - Bump `sha2` to v0.9 ([#182]) - Rename `list` command to `readers`; improve usage ([#71]) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f439004..2ee420e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yubikey-cli" -version = "0.4.0-pre" +version = "0.4.0" description = """ Command-line interface for performing encryption and signing using RSA/ECC keys stored on YubiKey devices. diff --git a/cli/src/commands/status.rs b/cli/src/commands/status.rs index 9e1ca6d..5ad5115 100644 --- a/cli/src/commands/status.rs +++ b/cli/src/commands/status.rs @@ -1,13 +1,11 @@ //! Print device status -use crate::terminal::STDOUT; +use crate::terminal::{print_cert_info, STDOUT}; use gumdrop::Options; use std::io::{self, Write}; use termcolor::{ColorSpec, StandardStreamLock, WriteColor}; use yubikey::{piv::*, YubiKey}; -use crate::print_cert_info; - // String to use for `None` const NONE_STR: &str = ""; diff --git a/cli/src/lib.rs b/cli/src/lib.rs index f4e962b..59990ee 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,92 +1,13 @@ //! `yubikey` command-line utility. +//! +//! The goal of this tool is to provide functionality similar to `yubico-piv-tool` +//! but implemented in pure Rust. +//! +//! It also serves as a demonstration/example of how to use the `yubikey` crate. #![forbid(unsafe_code)] -#![warn( - missing_docs, - rust_2018_idioms, - unused_lifetimes, - unused_qualifications -)] +#![warn(missing_docs, rust_2018_idioms, unused_qualifications)] #[macro_use] pub mod terminal; - pub mod commands; - -use log::debug; -use sha2::{Digest, Sha256}; -use std::io::{self, Write}; -use std::str; -use subtle_encoding::hex; -use termcolor::{ColorSpec, StandardStreamLock, WriteColor}; -use x509_parser::parse_x509_certificate; -use yubikey::{certificate::Certificate, piv::*, YubiKey}; - -/// Write information about certificate found in slot a la yubico-piv-tool output. -pub fn print_cert_info( - yubikey: &mut YubiKey, - slot: SlotId, - stream: &mut StandardStreamLock<'_>, -) -> io::Result<()> { - let cert = match Certificate::read(yubikey, slot) { - Ok(c) => c, - Err(e) => { - debug!("error reading certificate in slot {:?}: {}", slot, e); - return Ok(()); - } - }; - let buf = cert.into_buffer(); - - if !buf.is_empty() { - let fingerprint = Sha256::digest(&buf); - let slot_id: u8 = slot.into(); - print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?; - match parse_x509_certificate(&buf) { - Ok((_rem, cert)) => { - print_cert_attr( - stream, - "Algorithm", - cert.tbs_certificate.subject_pki.algorithm.algorithm, - )?; - - print_cert_attr(stream, "Subject", cert.tbs_certificate.subject)?; - print_cert_attr(stream, "Issuer", cert.tbs_certificate.issuer)?; - print_cert_attr( - stream, - "Fingerprint", - str::from_utf8(hex::encode(fingerprint).as_slice()).unwrap(), - )?; - print_cert_attr( - stream, - "Not Before", - cert.tbs_certificate.validity.not_before.to_rfc2822(), - )?; - print_cert_attr( - stream, - "Not After", - cert.tbs_certificate.validity.not_after.to_rfc2822(), - )?; - } - _ => { - println!("Failed to parse certificate"); - return Ok(()); - } - }; - } - - Ok(()) -} - -/// Print a status attribute -fn print_cert_attr( - stream: &mut StandardStreamLock<'_>, - name: &str, - value: impl ToString, -) -> io::Result<()> { - stream.set_color(ColorSpec::new().set_bold(true))?; - write!(stream, "{:>12}:", name)?; - stream.reset()?; - writeln!(stream, " {}", value.to_string())?; - stream.flush()?; - Ok(()) -} diff --git a/cli/src/terminal.rs b/cli/src/terminal.rs index 3be4ec6..bda7f34 100644 --- a/cli/src/terminal.rs +++ b/cli/src/terminal.rs @@ -1,9 +1,17 @@ //! Status messages use lazy_static::lazy_static; -use std::io::{self, Write}; -use std::sync::Mutex; -use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; +use log::debug; +use sha2::{Digest, Sha256}; +use std::{ + io::{self, Write}, + str, + sync::Mutex, +}; +use subtle_encoding::hex; +use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor}; +use x509_parser::parse_x509_certificate; +use yubikey::{certificate::Certificate, piv::*, YubiKey}; /// Print a success status message (in green if colors are enabled) #[macro_export] @@ -163,3 +171,72 @@ impl Status { Ok(()) } } + +/// Write information about certificate found in slot a la yubico-piv-tool output. +pub fn print_cert_info( + yubikey: &mut YubiKey, + slot: SlotId, + stream: &mut StandardStreamLock<'_>, +) -> io::Result<()> { + let cert = match Certificate::read(yubikey, slot) { + Ok(c) => c, + Err(e) => { + debug!("error reading certificate in slot {:?}: {}", slot, e); + return Ok(()); + } + }; + let buf = cert.into_buffer(); + + if !buf.is_empty() { + let fingerprint = Sha256::digest(&buf); + let slot_id: u8 = slot.into(); + print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?; + match parse_x509_certificate(&buf) { + Ok((_rem, cert)) => { + print_cert_attr( + stream, + "Algorithm", + cert.tbs_certificate.subject_pki.algorithm.algorithm, + )?; + + print_cert_attr(stream, "Subject", cert.tbs_certificate.subject)?; + print_cert_attr(stream, "Issuer", cert.tbs_certificate.issuer)?; + print_cert_attr( + stream, + "Fingerprint", + str::from_utf8(hex::encode(fingerprint).as_slice()).unwrap(), + )?; + print_cert_attr( + stream, + "Not Before", + cert.tbs_certificate.validity.not_before.to_rfc2822(), + )?; + print_cert_attr( + stream, + "Not After", + cert.tbs_certificate.validity.not_after.to_rfc2822(), + )?; + } + _ => { + println!("Failed to parse certificate"); + return Ok(()); + } + }; + } + + Ok(()) +} + +/// Print a status attribute +fn print_cert_attr( + stream: &mut StandardStreamLock<'_>, + name: &str, + value: impl ToString, +) -> io::Result<()> { + stream.set_color(ColorSpec::new().set_bold(true))?; + write!(stream, "{:>12}:", name)?; + stream.reset()?; + writeln!(stream, " {}", value.to_string())?; + stream.flush()?; + Ok(()) +}