From 9cf1010c0e4c60b712f485d8173c90d9881bd847 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sat, 1 Apr 2023 09:06:57 -0700 Subject: [PATCH 1/6] Use x509-cert certificate builder --- Cargo.lock | 61 ++-- Cargo.toml | 8 +- src/certificate.rs | 741 +++++++++++++++---------------------------- src/error.rs | 6 + src/piv.rs | 54 ++-- tests/integration.rs | 96 ++---- 6 files changed, 354 insertions(+), 612 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dafa7773..f5b2a98a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,12 +200,6 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" -[[package]] -name = "cookie-factory" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" - [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -300,6 +294,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" dependencies = [ "const-oid", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] @@ -318,6 +314,18 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "der_derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63898447d5453a504531990fb79708be1087effb2da9b2704f54dbdf8b6890e4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "des" version = "0.8.1" @@ -352,11 +360,12 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.1" +version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a" +checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" dependencies = [ "der", + "digest", "elliptic-curve", "rfc6979", "signature", @@ -364,9 +373,9 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" dependencies = [ "base16ct", "crypto-bigint", @@ -427,6 +436,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "flagset" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499" + [[package]] name = "generic-array" version = "0.14.6" @@ -1023,9 +1038,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest", "rand_core", @@ -1349,13 +1364,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] -name = "x509" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3cec94c3999f31341553f358ef55f65fc031291a022cd42ec0ce7219560c76" +name = "x509-cert" +version = "0.2.1" +source = "git+https://github.com/RustCrypto/formats#98957ae314cd8a9aa19eca95ba3aa9048159264f" dependencies = [ - "chrono", - "cookie-factory", + "const-oid", + "der", + "sha1", + "signature", + "spki", ] [[package]] @@ -1382,9 +1399,10 @@ version = "0.8.0-pre.0" dependencies = [ "base16ct", "chrono", - "cookie-factory", - "der-parser", + "const-oid", + "der", "des", + "ecdsa", "elliptic-curve", "env_logger", "hmac", @@ -1406,8 +1424,7 @@ dependencies = [ "signature", "subtle", "uuid", - "x509", - "x509-parser", + "x509-cert", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index 93018fe7..3a8c59eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,9 @@ members = [".", "cli"] [dependencies] chrono = "0.4.23" -cookie-factory = "0.3" -der-parser = "8" +const-oid = "0.9.2" des = "0.8" +der = "0.7.1" elliptic-curve = "0.13" hex = { package = "base16ct", version = "0.2", features = ["alloc"] } hmac = "0.12" @@ -32,6 +32,7 @@ nom = "7" num-bigint-dig = { version = "0.8", features = ["rand"] } num-traits = "0.2" num-integer = "0.1" +ecdsa = { version = "0.16.2", features = ["digest", "pem"] } p256 = "0.13" p384 = "0.13" pbkdf2 = { version = "0.12", default-features = false, features = ["hmac"] } @@ -43,8 +44,7 @@ sha1 = { version = "0.10", features = ["oid"] } sha2 = { version = "0.10", features = ["oid"] } subtle = "2" uuid = { version = "1.2", features = ["v4"] } -x509 = "0.2" -x509-parser = "0.14" +x509-cert = { git = "https://github.com/RustCrypto/formats", features = [ "builder", "hazmat" ] } zeroize = "1" [dev-dependencies] diff --git a/src/certificate.rs b/src/certificate.rs index d65dd368..12347aa9 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -33,90 +33,28 @@ use crate::{ consts::CB_OBJ_MAX, error::{Error, Result}, - piv::{sign_data, AlgorithmId, SlotId}, + piv::SlotId, serialization::*, transaction::Transaction, yubikey::YubiKey, Buffer, }; -use chrono::{DateTime, Utc}; -use elliptic_curve::sec1::EncodedPoint as EcPublicKey; use log::error; -use num_bigint_dig::BigUint; -use p256::NistP256; -use p384::NistP384; -use rsa::{traits::PublicKeyParts, RsaPublicKey}; -use sha2::{Digest, Sha256}; -use std::fmt::Display; -use std::{fmt, ops::DerefMut}; -use x509::{der::Oid, RelativeDistinguishedName}; -use x509_parser::{parse_x509_certificate, x509::SubjectPublicKeyInfo}; +use x509_cert::{ + builder::{CertificateBuilder, Profile}, + certificate::Version, + der::{self, referenced::OwnedToRef, Decode, Encode}, + name::Name, + serial_number::SerialNumber, + spki::{SubjectPublicKeyInfoOwned, SubjectPublicKeyInfoRef}, + time::Validity, +}; use zeroize::Zeroizing; -// TODO: Make these der_parser::oid::Oid constants when it has const fn support. -const OID_RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.1"; -const OID_EC_PUBLIC_KEY: &str = "1.2.840.10045.2.1"; -const OID_NIST_P256: &str = "1.2.840.10045.3.1.7"; -const OID_NIST_P384: &str = "1.3.132.0.34"; - const TAG_CERT: u8 = 0x70; const TAG_CERT_COMPRESS: u8 = 0x71; const TAG_CERT_LRC: u8 = 0xFE; -/// A serial number for a [`Certificate`]. -#[derive(Clone, Debug)] -pub struct Serial(BigUint); - -impl From for Serial { - fn from(num: BigUint) -> Serial { - Serial(num) - } -} - -impl From<[u8; 20]> for Serial { - fn from(bytes: [u8; 20]) -> Serial { - Serial(BigUint::from_bytes_be(&bytes)) - } -} - -impl TryFrom<&[u8]> for Serial { - type Error = Error; - - fn try_from(bytes: &[u8]) -> Result { - if bytes.len() <= 20 { - Ok(Serial(BigUint::from_bytes_be(bytes))) - } else { - Err(Error::ParseError) - } - } -} - -impl Serial { - fn to_bytes(&self) -> Vec { - self.0.to_bytes_be() - } - /// Returns itself formatted as x509 compatible hex string - pub fn as_x509_hex(&self) -> String { - let data = self.to_bytes(); - let raw_hex_string = format!("{:02X?}", data); - raw_hex_string - .replace(", ", ":") - .replace([']', '['], "") - .to_lowercase() - } - /// Returns itself formatted as x509 compatible int string - pub fn as_x509_int(&self) -> String { - let Serial(buint) = self; - format!("{}", buint) - } -} - -impl Display for Serial { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.as_x509_hex()) - } -} - /// Information about how a [`Certificate`] is stored within a YubiKey. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CertInfo { @@ -148,210 +86,11 @@ impl From for u8 { } } -impl x509::AlgorithmIdentifier for AlgorithmId { - type AlgorithmOid = &'static [u64]; - - fn algorithm(&self) -> Self::AlgorithmOid { - match self { - // RSA encryption - AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => &[1, 2, 840, 113_549, 1, 1, 1], - // EC Public Key - AlgorithmId::EccP256 | AlgorithmId::EccP384 => &[1, 2, 840, 10045, 2, 1], - } - } - - fn parameters( - &self, - w: cookie_factory::WriteContext, - ) -> cookie_factory::GenResult { - use x509::der::write::der_oid; - - // From [RFC 5480](https://tools.ietf.org/html/rfc5480#section-2.1.1): - // ```text - // ECParameters ::= CHOICE { - // namedCurve OBJECT IDENTIFIER - // -- implicitCurve NULL - // -- specifiedCurve SpecifiedECDomain - // } - // ``` - match self { - AlgorithmId::EccP256 => der_oid(&[1, 2, 840, 10045, 3, 1, 7][..])(w), - AlgorithmId::EccP384 => der_oid(&[1, 3, 132, 0, 34][..])(w), - _ => Ok(w), - } - } -} - -/// Information about a public key within a [`Certificate`]. -#[derive(Clone, Eq, PartialEq)] -pub enum PublicKeyInfo { - /// RSA keys - Rsa { - /// RSA algorithm - algorithm: AlgorithmId, - - /// Public key - pubkey: RsaPublicKey, - }, - - /// EC P-256 keys - EcP256(EcPublicKey), - - /// EC P-384 keys - EcP384(EcPublicKey), -} - -impl fmt::Debug for PublicKeyInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "PublicKeyInfo({:?})", self.algorithm()) - } -} - -impl PublicKeyInfo { - fn parse(subject_pki: &SubjectPublicKeyInfo<'_>) -> Result { - match subject_pki.algorithm.algorithm.to_string().as_str() { - OID_RSA_ENCRYPTION => { - let pubkey = read_pki::rsa_pubkey(&subject_pki.subject_public_key.data)?; - - Ok(PublicKeyInfo::Rsa { - algorithm: match pubkey.n().bits() { - 1024 => AlgorithmId::Rsa1024, - 2048 => AlgorithmId::Rsa2048, - _ => return Err(Error::AlgorithmError), - }, - pubkey, - }) - } - OID_EC_PUBLIC_KEY => { - let key_bytes = &subject_pki.subject_public_key.data; - let algorithm_parameters = subject_pki - .algorithm - .parameters - .as_ref() - .ok_or(Error::InvalidObject)?; - - match read_pki::ec_parameters(algorithm_parameters)? { - AlgorithmId::EccP256 => EcPublicKey::::from_bytes(key_bytes) - .map(PublicKeyInfo::EcP256) - .map_err(|_| Error::InvalidObject), - AlgorithmId::EccP384 => EcPublicKey::::from_bytes(key_bytes) - .map(PublicKeyInfo::EcP384) - .map_err(|_| Error::InvalidObject), - _ => Err(Error::AlgorithmError), - } - } - _ => Err(Error::InvalidObject), - } - } - - /// Returns the algorithm that this public key can be used with. - pub fn algorithm(&self) -> AlgorithmId { - match self { - PublicKeyInfo::Rsa { algorithm, .. } => *algorithm, - PublicKeyInfo::EcP256(_) => AlgorithmId::EccP256, - PublicKeyInfo::EcP384(_) => AlgorithmId::EccP384, - } - } -} - -impl x509::SubjectPublicKeyInfo for PublicKeyInfo { - type AlgorithmId = AlgorithmId; - type SubjectPublicKey = Vec; - - fn algorithm_id(&self) -> AlgorithmId { - self.algorithm() - } - - fn public_key(&self) -> Vec { - match self { - PublicKeyInfo::Rsa { pubkey, .. } => { - cookie_factory::gen_simple(write_pki::rsa_pubkey(pubkey), vec![]) - .expect("can write to Vec") - } - PublicKeyInfo::EcP256(pubkey) => pubkey.as_bytes().to_vec(), - PublicKeyInfo::EcP384(pubkey) => pubkey.as_bytes().to_vec(), - } - } -} - -/// Digest algorithms. -/// -/// See RFC 4055 and RFC 8017. -enum DigestId { - /// Secure Hash Algorithm 256 (SHA256) - Sha256, -} - -impl x509::AlgorithmIdentifier for DigestId { - type AlgorithmOid = &'static [u64]; - - fn algorithm(&self) -> Self::AlgorithmOid { - match self { - // See https://tools.ietf.org/html/rfc4055#section-2.1 - DigestId::Sha256 => &[2, 16, 840, 1, 101, 3, 4, 2, 1], - } - } - - fn parameters( - &self, - w: cookie_factory::WriteContext, - ) -> cookie_factory::GenResult { - // Parameters are an explicit NULL - // See https://tools.ietf.org/html/rfc8017#appendix-A.2.4 - x509::der::write::der_null()(w) - } -} - -enum SignatureId { - /// Public-Key Cryptography Standards (PKCS) #1 version 1.5 signature algorithm with - /// Secure Hash Algorithm 256 (SHA256) and Rivest, Shamir and Adleman (RSA) encryption - /// - /// See RFC 4055 and RFC 8017. - Sha256WithRsaEncryption, - - /// Elliptic Curve Digital Signature Algorithm (DSA) coupled with the Secure Hash - /// Algorithm 256 (SHA256) algorithm - /// - /// See RFC 5758. - EcdsaWithSha256, -} - -impl x509::AlgorithmIdentifier for SignatureId { - type AlgorithmOid = &'static [u64]; - - fn algorithm(&self) -> Self::AlgorithmOid { - match self { - SignatureId::Sha256WithRsaEncryption => &[1, 2, 840, 113_549, 1, 1, 11], - SignatureId::EcdsaWithSha256 => &[1, 2, 840, 10045, 4, 3, 2], - } - } - - fn parameters( - &self, - w: cookie_factory::WriteContext, - ) -> cookie_factory::GenResult { - // No parameters for any SignatureId - Ok(w) - } -} - /// Certificates #[derive(Clone, Debug)] pub struct Certificate { - serial: Serial, - #[allow(dead_code)] - issuer: String, - subject: String, - subject_pki: PublicKeyInfo, - data: Buffer, -} - -impl<'a> TryFrom<&'a [u8]> for Certificate { - type Error = Error; - - fn try_from(bytes: &'a [u8]) -> Result { - Self::from_bytes(bytes.to_vec()) - } + /// Inner certificate + pub cert: x509_cert::Certificate, } impl Certificate { @@ -360,112 +99,36 @@ impl Certificate { /// /// `extensions` is optional; if empty, no extensions will be included. Due to the /// need for an `O: Oid` type parameter, users who do not have any extensions should - /// use the workaround `let extensions: &[x509::Extension<'_, &[u64]>] = &[];`. - pub fn generate_self_signed( + /// use the workaround `let extensions: &[x509_cert::Extension<'_, &[u64]>] = &[];`. + pub fn generate_self_signed( yubikey: &mut YubiKey, key: SlotId, - serial: impl Into, - not_after: Option>, - subject: &[RelativeDistinguishedName<'_>], - subject_pki: PublicKeyInfo, - extensions: &[x509::Extension<'_, O>], - ) -> Result { - let serial = serial.into(); - - let mut tbs_cert = Buffer::new(Vec::with_capacity(CB_OBJ_MAX)); - - let signature_algorithm = match subject_pki.algorithm() { - AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => SignatureId::Sha256WithRsaEncryption, - AlgorithmId::EccP256 | AlgorithmId::EccP384 => SignatureId::EcdsaWithSha256, - }; - - cookie_factory::gen( - x509::write::tbs_certificate( - &serial.to_bytes(), - &signature_algorithm, - // Issuer and subject are the same in self-signed certificates. - subject, - Utc::now(), - not_after, - subject, - &subject_pki, - extensions, - ), - tbs_cert.deref_mut(), - ) - .expect("can serialize to Vec"); - - let signature = match signature_algorithm { - SignatureId::Sha256WithRsaEncryption => { - use cookie_factory::{combinator::slice, sequence::tuple}; - use x509::{ - der::write::{der_octet_string, der_sequence}, - write::algorithm_identifier, - }; - - let em_len = if let AlgorithmId::Rsa1024 = subject_pki.algorithm() { - 128 - } else { - 256 - }; - - let h = Sha256::digest(&tbs_cert); - - let t = cookie_factory::gen_simple( - der_sequence(( - algorithm_identifier(&DigestId::Sha256), - der_octet_string(&h), - )), - vec![], - ) - .expect("can serialize into Vec"); - - let em = cookie_factory::gen_simple( - tuple(( - slice(&[0x00, 0x01]), - slice(&vec![0xff; em_len - t.len() - 3]), - slice(&[0x00]), - slice(t), - )), - vec![], - ) - .expect("can serialize to Vec"); - - sign_data(yubikey, &em, subject_pki.algorithm(), key) - } - SignatureId::EcdsaWithSha256 => sign_data( - yubikey, - &Sha256::digest(&tbs_cert), - subject_pki.algorithm(), - key, - ), - }?; - - let mut data = Buffer::new(Vec::with_capacity(CB_OBJ_MAX)); - - cookie_factory::gen( - x509::write::certificate(&tbs_cert, &signature_algorithm, &signature), - data.deref_mut(), - ) - .expect("can serialize to Vec"); - - let (issuer, subject) = parse_x509_certificate(&data) - .map(|(_, cert)| { - ( - cert.tbs_certificate.issuer.to_string(), - cert.tbs_certificate.subject.to_string(), - ) - }) - .expect("We just serialized this correctly"); - - let cert = Certificate { + serial: SerialNumber, + validity: Validity, + subject: Name, + subject_pki: SubjectPublicKeyInfoOwned, + extensions: F, + ) -> Result + where + F: FnOnce(&mut CertificateBuilder<'_, yubikey_signer::Signer<'_, KT>>) -> der::Result<()>, + { + let mut signer = yubikey_signer::Signer::new(yubikey, key, subject_pki.owned_to_ref())?; + let mut builder = CertificateBuilder::new( + Profile::Manual { issuer: None }, + Version::V3, serial, - issuer, + validity, subject, subject_pki, - data, - }; + &mut signer, + ) + .map_err(|_| Error::KeyError)?; + + // Add custom extensions + extensions(&mut builder)?; + let cert = builder.build().map_err(|_| Error::KeyError)?; + let cert = Self { cert }; cert.write(yubikey, key, CertInfo::Uncompressed)?; Ok(cert) @@ -480,13 +143,14 @@ impl Certificate { return Err(Error::InvalidObject); } - Certificate::from_bytes(buf) + Self::from_bytes(buf) } /// Write this certificate into the YubiKey in the given slot pub fn write(&self, yubikey: &mut YubiKey, slot: SlotId, certinfo: CertInfo) -> Result<()> { let txn = yubikey.begin_transaction()?; - write_certificate(&txn, slot, Some(&self.data), certinfo) + let data = self.cert.to_der().map_err(|_| Error::InvalidObject)?; + write_certificate(&txn, slot, Some(&data), certinfo) } /// Delete a certificate located at the given slot of the given YubiKey @@ -506,55 +170,27 @@ impl Certificate { return Err(Error::SizeError); } - let parsed_cert = match parse_x509_certificate(&cert) { - Ok((_, cert)) => cert, - _ => return Err(Error::InvalidObject), - }; - - let serial = Serial::try_from(parsed_cert.tbs_certificate.serial.to_bytes_be().as_slice()) - .map_err(|_| Error::InvalidObject)?; - let issuer = parsed_cert.tbs_certificate.issuer.to_string(); - let subject = parsed_cert.tbs_certificate.subject.to_string(); - let subject_pki = PublicKeyInfo::parse(&parsed_cert.tbs_certificate.subject_pki)?; - - Ok(Certificate { - serial, - issuer, - subject, - subject_pki, - data: cert, - }) - } - - /// Returns the serial number of the certificate. - pub fn serial(&self) -> &Serial { - &self.serial + x509_cert::Certificate::from_der(&cert) + .map(|cert| Self { cert }) + .map_err(|_| Error::InvalidObject) } /// Returns the Issuer field of the certificate. - pub fn issuer(&self) -> &str { - &self.issuer + pub fn issuer(&self) -> String { + self.cert.tbs_certificate.issuer.to_string() } /// Returns the SubjectName field of the certificate. - pub fn subject(&self) -> &str { - &self.subject + pub fn subject(&self) -> String { + self.cert.tbs_certificate.subject.to_string() } /// Returns the SubjectPublicKeyInfo field of the certificate. - pub fn subject_pki(&self) -> &PublicKeyInfo { - &self.subject_pki - } - - /// Extract the inner buffer - pub fn into_buffer(self) -> Buffer { - self.data - } -} - -impl AsRef<[u8]> for Certificate { - fn as_ref(&self) -> &[u8] { - self.data.as_ref() + pub fn subject_pki(&self) -> SubjectPublicKeyInfoRef<'_> { + self.cert + .tbs_certificate + .subject_public_key_info + .owned_to_ref() } } @@ -606,97 +242,216 @@ pub(crate) fn write_certificate( txn.save_object(object_id, &buf[..offset]) } -mod read_pki { - use der_parser::{ - asn1_rs::Any, - ber::BerObjectContent, - der::{parse_der_integer, parse_der_sequence_defined_g, DerObject}, - error::BerError, +pub mod yubikey_signer { + //! Signer implementation for yubikey + + use crate::{ + error::{Error, Result}, + piv::AlgorithmId, + piv::{sign_data, SlotId}, + YubiKey, + }; + use der::{ + asn1::{Any, OctetString}, + oid::db::rfc5912, + Encode, Sequence, }; - use nom::{combinator, sequence::pair, IResult}; - use rsa::{BigUint, RsaPublicKey}; - - use super::{OID_NIST_P256, OID_NIST_P384}; - use crate::{piv::AlgorithmId, Error, Result}; - - /// From [RFC 8017](https://tools.ietf.org/html/rfc8017#appendix-A.1.1): - /// ```text - /// RSAPublicKey ::= SEQUENCE { - /// modulus INTEGER, -- n - /// publicExponent INTEGER -- e - /// } - /// ``` - pub(super) fn rsa_pubkey(encoded: &[u8]) -> Result { - fn parse_rsa_pubkey(i: &[u8]) -> IResult<&[u8], (DerObject<'_>, DerObject<'_>), BerError> { - parse_der_sequence_defined_g(|i, _| pair(parse_der_integer, parse_der_integer)(i))(i) + use sha2::{Digest, Sha256, Sha384}; + use signature::{Keypair, SignatureEncoding}; + use std::{cell::RefCell, fmt, io::Write, marker::PhantomData}; + use x509_cert::spki::{ + self, AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, EncodePublicKey, + SubjectPublicKeyInfoRef, + }; + + type SigResult = core::result::Result; + + /// Key to be used to sign certificates + pub trait KeyType { + /// Error returned when working with signature + type Error: Into + fmt::Debug; + /// The signature type returned by the signer + type Signature: SignatureEncoding + + for<'s> TryFrom<&'s [u8], Error = Self::Error> + + fmt::Debug; + /// The public key used to verify signature + type VerifyingKey: EncodePublicKey + + DynSignatureAlgorithmIdentifier + + Clone + + From; + /// Public key type used to load the SPKI formatted key + type PublicKey: for<'a> TryFrom, Error = spki::Error>; + + /// The algorithm used when talking with the yubikey + const ALGORITHM: AlgorithmId; + + /// Prepare buffer before submitting it for signature + fn prepare(input: &[u8]) -> SigResult>; + /// Read back the signature from the device + fn read_signature(input: &[u8]) -> SigResult; + } + + impl KeyType for p256::NistP256 { + const ALGORITHM: AlgorithmId = AlgorithmId::EccP256; + type Error = ecdsa::Error; + type Signature = p256::ecdsa::DerSignature; + type VerifyingKey = p256::ecdsa::VerifyingKey; + type PublicKey = p256::ecdsa::VerifyingKey; + + fn prepare(input: &[u8]) -> SigResult> { + Ok(Sha256::digest(input).to_vec()) } - fn rsa_pubkey_parts(i: &[u8]) -> IResult<&[u8], (BigUint, BigUint), BerError> { - combinator::map(parse_rsa_pubkey, |(modulus, public_exponent)| { - let n = match modulus.content { - BerObjectContent::Integer(s) => BigUint::from_bytes_be(s), - _ => panic!("expected DER integer"), - }; - let e = match public_exponent.content { - BerObjectContent::Integer(s) => BigUint::from_bytes_be(s), - _ => panic!("expected DER integer"), - }; - - (n, e) - })(i) + fn read_signature(input: &[u8]) -> SigResult { + Self::Signature::from_bytes(input) } + } - let (n, e) = match rsa_pubkey_parts(encoded) { - Ok((_, res)) => res, - _ => return Err(Error::InvalidObject), - }; + impl KeyType for p384::NistP384 { + const ALGORITHM: AlgorithmId = AlgorithmId::EccP384; + type Error = ecdsa::Error; + type Signature = p384::ecdsa::DerSignature; + type VerifyingKey = p384::ecdsa::VerifyingKey; + type PublicKey = p384::ecdsa::VerifyingKey; - RsaPublicKey::new(n, e).map_err(|_| Error::InvalidObject) + fn prepare(input: &[u8]) -> SigResult> { + Ok(Sha384::digest(input).to_vec()) + } + + fn read_signature(input: &[u8]) -> SigResult { + Self::Signature::from_bytes(input) + } + } + + /// Trait used to handle subtypes of RSA keys + pub trait RsaLength { + /// The length of the RSA key in bits + const BIT_LENGTH: usize; + /// The algorithm to use when talking with the Yubikey. + const ALGORITHM: AlgorithmId; + } + + /// RSA 1024 bits key + pub struct Rsa1024; + + impl RsaLength for Rsa1024 { + const BIT_LENGTH: usize = 1024; + const ALGORITHM: AlgorithmId = AlgorithmId::Rsa1024; + } + + /// RSA 2048 bits key + pub struct Rsa2048; + + impl RsaLength for Rsa2048 { + const BIT_LENGTH: usize = 2048; + const ALGORITHM: AlgorithmId = AlgorithmId::Rsa2048; } - /// From [RFC 5480](https://tools.ietf.org/html/rfc5480#section-2.1.1): - /// ```text - /// ECParameters ::= CHOICE { - /// namedCurve OBJECT IDENTIFIER - /// -- implicitCurve NULL - /// -- specifiedCurve SpecifiedECDomain - /// } - /// ``` - pub(super) fn ec_parameters(parameters: &Any<'_>) -> Result { - let curve_oid = parameters.as_oid().map_err(|_| Error::InvalidObject)?; - - match curve_oid.to_string().as_str() { - OID_NIST_P256 => Ok(AlgorithmId::EccP256), - OID_NIST_P384 => Ok(AlgorithmId::EccP384), - _ => Err(Error::AlgorithmError), + /// RSA keys used to sign certificates + pub struct YubiRsa { + _len: PhantomData, + } + + impl KeyType for YubiRsa { + type Error = signature::Error; + type Signature = rsa::pkcs1v15::Signature; + type VerifyingKey = rsa::pkcs1v15::VerifyingKey; + type PublicKey = rsa::RsaPublicKey; + const ALGORITHM: AlgorithmId = N::ALGORITHM; + + fn prepare(input: &[u8]) -> SigResult> { + let hashed = Sha256::digest(input).to_vec(); + + OctetString::new(hashed) + .map_err(|e| e.into()) + .and_then(Self::emsa_pkcs1_1_5) + .map_err(signature::Error::from_source) + } + + fn read_signature(input: &[u8]) -> SigResult { + Self::Signature::try_from(input) } } -} -mod write_pki { - use cookie_factory::{SerializeFn, WriteContext}; - use rsa::{traits::PublicKeyParts, BigUint, RsaPublicKey}; - use std::io::Write; - use x509::der::write::{der_integer, der_sequence}; + impl YubiRsa { + /// https://www.rfc-editor.org/rfc/rfc8017#section-9.2 + fn emsa_pkcs1_1_5(digest: OctetString) -> Result> { + /// https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.4 + #[derive(Debug, Sequence)] + struct DigestInfo { + digest_algorithm: AlgorithmIdentifierOwned, + digest: OctetString, + } + + let em_len = N::BIT_LENGTH / 8; - /// Encodes a usize as an ASN.1 integer using DER. - fn der_integer_biguint<'a, W: Write + 'a>(num: &'a BigUint) -> impl SerializeFn + 'a { - move |w: WriteContext| der_integer(&num.to_bytes_be())(w) + let null = Any::null(); + + let t = DigestInfo { + digest_algorithm: AlgorithmIdentifierOwned { + oid: rfc5912::ID_SHA_256, + parameters: Some(null), + }, + digest, + }; + let t = t.to_der()?; + + let ps = vec![0xff; em_len - t.len() - 3]; + assert!(ps.len() >= 8, "spec violation"); + + let mut out = Vec::with_capacity(em_len); + out.write(&[0x00, 0x01]).map_err(|_| Error::MemoryError)?; + out.write(&ps).map_err(|_| Error::MemoryError)?; + out.write(&[0x00]).map_err(|_| Error::MemoryError)?; + out.write(&t).map_err(|_| Error::MemoryError)?; + Ok(out) + } + } + + /// The entrypoint to sign data with the yubikey. + pub struct Signer<'y, KT: KeyType> { + yubikey: RefCell<&'y mut YubiKey>, + key: SlotId, + public_key: KT::VerifyingKey, + } + + impl<'y, KT: KeyType> Signer<'y, KT> { + pub(crate) fn new( + yubikey: &'y mut YubiKey, + key: SlotId, + subject_pki: SubjectPublicKeyInfoRef<'_>, + ) -> Result { + let public_key = KT::PublicKey::try_from(subject_pki).map_err(|_| Error::ParseError)?; + let public_key = public_key.into(); + + Ok(Self { + yubikey: RefCell::new(yubikey), + key, + public_key, + }) + } } - /// From [RFC 8017](https://tools.ietf.org/html/rfc8017#appendix-A.1.1): - /// ```text - /// RSAPublicKey ::= SEQUENCE { - /// modulus INTEGER, -- n - /// publicExponent INTEGER -- e - /// } - /// ``` - pub(super) fn rsa_pubkey<'a, W: Write + 'a>( - pubkey: &'a RsaPublicKey, - ) -> impl SerializeFn + 'a { - der_sequence(( - der_integer_biguint(pubkey.n()), - der_integer_biguint(pubkey.e()), - )) + impl<'y, KT: KeyType> Keypair for Signer<'y, KT> { + type VerifyingKey = KT::VerifyingKey; + fn verifying_key(&self) -> ::VerifyingKey { + self.public_key.clone() + } + } + + impl<'y, KT: KeyType> signature::Signer for Signer<'y, KT> { + fn try_sign(&self, msg: &[u8]) -> SigResult { + let data = KT::prepare(msg)?; + + let out = sign_data( + &mut self.yubikey.borrow_mut(), + &data, + KT::ALGORITHM, + self.key, + ) + .map_err(signature::Error::from_source)?; + let out = KT::read_signature(&out)?; + Ok(out) + } } } diff --git a/src/error.rs b/src/error.rs index cf759c99..cdd47b38 100644 --- a/src/error.rs +++ b/src/error.rs @@ -179,3 +179,9 @@ impl std::error::Error for Error { } } } + +impl From for Error { + fn from(_err: x509_cert::der::Error) -> Error { + Error::ParseError + } +} diff --git a/src/piv.rs b/src/piv.rs index 78ba16d3..62a34356 100644 --- a/src/piv.rs +++ b/src/piv.rs @@ -44,7 +44,7 @@ use crate::{ apdu::{Ins, StatusWords}, - certificate::{self, Certificate, PublicKeyInfo}, + certificate::{self, Certificate}, consts::CB_OBJ_MAX, error::{Error, Result}, policy::{PinPolicy, TouchPolicy}, @@ -53,15 +53,16 @@ use crate::{ yubikey::YubiKey, Buffer, ObjectId, }; -use elliptic_curve::sec1::EncodedPoint as EcPublicKey; +use elliptic_curve::{sec1::EncodedPoint as EcPublicKey, PublicKey}; use log::{debug, error, warn}; use p256::NistP256; use p384::NistP384; -use rsa::{BigUint, RsaPublicKey}; +use rsa::{pkcs8::EncodePublicKey, BigUint, RsaPublicKey}; use std::{ fmt::{Display, Formatter}, str::FromStr, }; +use x509_cert::{der::Decode, spki::SubjectPublicKeyInfoOwned}; #[cfg(feature = "untested")] use { @@ -591,7 +592,7 @@ pub fn generate( algorithm: AlgorithmId, pin_policy: PinPolicy, touch_policy: TouchPolicy, -) -> Result { +) -> Result { // Keygen messages // TODO(tarcieri): extract these into an I18N-handling type? const SZ_SETTING_ROCA: &str = "Enable_Unsafe_Keygen_ROCA"; @@ -955,7 +956,7 @@ pub struct SlotMetadata { /// Imported or generated key pub origin: Option, /// Pub key of the key - pub public: Option, + pub public: Option, /// Whether PIN PUK and management key are default pub default: Option, /// Number of retries left @@ -1110,7 +1111,7 @@ fn read_public_key( algorithm: AlgorithmId, input: &[u8], skip_asn1_tag: bool, -) -> Result { +) -> Result { // TODO(str4d): Response is wrapped in an ASN.1 TLV: // // 0x7f 0x49 -> Application | Constructed | 0x49 @@ -1159,14 +1160,17 @@ fn read_public_key( } let exp = exp_tlv.value.to_vec(); - Ok(PublicKeyInfo::Rsa { - algorithm, - pubkey: RsaPublicKey::new( - BigUint::from_bytes_be(&modulus), - BigUint::from_bytes_be(&exp), - ) - .map_err(|_| Error::InvalidObject)?, - }) + let pubkey = RsaPublicKey::new( + BigUint::from_bytes_be(&modulus), + BigUint::from_bytes_be(&exp), + ) + .map_err(|_| Error::InvalidObject)?; + Ok(SubjectPublicKeyInfoOwned::from_der( + pubkey + .to_public_key_der() + .map_err(|_| Error::ParseError)? + .as_bytes(), + )?) } AlgorithmId::EccP256 | AlgorithmId::EccP384 => { // 2-byte ASN.1 tag, 1-byte length (because all supported EC pubkey lengths @@ -1194,16 +1198,22 @@ fn read_public_key( let point = tlv.value.to_vec(); - match algorithm { - AlgorithmId::EccP256 => { - EcPublicKey::::from_bytes(point).map(PublicKeyInfo::EcP256) - } - AlgorithmId::EccP384 => { - EcPublicKey::::from_bytes(point).map(PublicKeyInfo::EcP384) - } + let pubkey = match algorithm { + AlgorithmId::EccP256 => PublicKey::::try_from( + EcPublicKey::::from_bytes(point).map_err(|_| Error::InvalidObject)?, + ) + .map_err(|_| Error::InvalidObject)? + .to_public_key_der(), + AlgorithmId::EccP384 => PublicKey::::try_from( + EcPublicKey::::from_bytes(point).map_err(|_| Error::InvalidObject)?, + ) + .map_err(|_| Error::InvalidObject)? + .to_public_key_der(), _ => return Err(Error::AlgorithmError), } - .map_err(|_| Error::InvalidObject) + .map_err(|_| Error::InvalidObject)?; + + Ok(SubjectPublicKeyInfoOwned::from_der(pubkey.as_bytes())?) } } } diff --git a/tests/integration.rs b/tests/integration.rs index 45d80654..1854c930 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -6,14 +6,15 @@ use log::trace; use once_cell::sync::Lazy; use rand_core::{OsRng, RngCore}; -use rsa::pkcs1v15; +use rsa::{pkcs1v15, RsaPublicKey}; use sha2::{Digest, Sha256}; use signature::hazmat::PrehashVerifier; -use std::{env, str::FromStr, sync::Mutex}; -use x509::RelativeDistinguishedName; +use std::{env, str::FromStr, sync::Mutex, time::Duration}; +use x509_cert::{der::Encode, name::Name, serial_number::SerialNumber, time::Validity}; use yubikey::{ certificate, - certificate::{Certificate, PublicKeyInfo}, + certificate::yubikey_signer, + certificate::Certificate, piv::{self, AlgorithmId, Key, ManagementSlotId, RetiredSlotId, SlotId}, Error, MgmKey, PinPolicy, Serial, TouchPolicy, YubiKey, }; @@ -147,7 +148,7 @@ fn test_set_mgmkey() { // Certificate support // -fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate { +fn generate_self_signed_cert() -> Certificate { let mut yubikey = YUBIKEY.lock().unwrap(); assert!(yubikey.verify_pin(b"123456").is_ok()); @@ -159,25 +160,28 @@ fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate { let generated = piv::generate( &mut yubikey, slot, - algorithm, + KT::ALGORITHM, PinPolicy::Default, TouchPolicy::Default, ) .unwrap(); - let mut serial = [0u8; 20]; + // 0x80 0x00 ... (20bytes) is invalid because of high MSB (serial will keep the sign) + // we'll limit ourselves to 19 bytes serial. + let mut serial = [0u8; 19]; OsRng.fill_bytes(&mut serial); + let serial = SerialNumber::new(&serial[..]).expect("serial can't be more than 20 bytes long"); + let validity = Validity::from_now(Duration::new(500000, 0)).unwrap(); // Generate a self-signed certificate for the new key. - let extensions: &[x509::Extension<'_, &[u64]>] = &[]; - let cert_result = Certificate::generate_self_signed( + let cert_result = Certificate::generate_self_signed::<_, KT>( &mut yubikey, slot, serial, - None, - &[RelativeDistinguishedName::common_name("testSubject")], + validity, + Name::from_str("CN=testSubject").expect("parse name"), generated, - extensions, + |_builder| Ok(()), ); assert!(cert_result.is_ok()); @@ -189,18 +193,16 @@ fn generate_self_signed_cert(algorithm: AlgorithmId) -> Certificate { #[test] #[ignore] fn generate_self_signed_rsa_cert() { - let cert = generate_self_signed_cert(AlgorithmId::Rsa1024); + let cert = generate_self_signed_cert::>(); // // Verify that the certificate is signed correctly // - let pubkey = match cert.subject_pki() { - PublicKeyInfo::Rsa { pubkey, .. } => pkcs1v15::VerifyingKey::::from(pubkey.clone()), - _ => unreachable!(), - }; + let pubkey = RsaPublicKey::try_from(cert.subject_pki()).expect("valid rsa key"); + let pubkey = pkcs1v15::VerifyingKey::::new_with_prefix(pubkey); - let data = cert.as_ref(); + let data = cert.cert.to_der().expect("serialize certificate"); let tbs_cert_len = u16::from_be_bytes(data[6..8].try_into().unwrap()) as usize; let msg = &data[4..8 + tbs_cert_len]; let sig = pkcs1v15::Signature::try_from(&data[data.len() - 128..]).unwrap(); @@ -212,24 +214,20 @@ fn generate_self_signed_rsa_cert() { #[test] #[ignore] fn generate_self_signed_ec_cert() { - let cert = generate_self_signed_cert(AlgorithmId::EccP256); + let cert = generate_self_signed_cert::(); // // Verify that the certificate is signed correctly // - let pubkey = match cert.subject_pki() { - PublicKeyInfo::EcP256(pubkey) => pubkey, - _ => unreachable!(), - }; + let vk = p256::ecdsa::VerifyingKey::try_from(cert.subject_pki()).expect("ecdsa key expected"); - let data = cert.as_ref(); + let data = cert.cert.to_der().expect("serialize certificate"); let tbs_cert_len = data[6] as usize; let sig_algo_len = data[7 + tbs_cert_len + 1] as usize; let sig_start = 7 + tbs_cert_len + 2 + sig_algo_len + 3; let msg = &data[4..7 + tbs_cert_len]; let sig = p256::ecdsa::Signature::from_der(&data[sig_start..]).unwrap(); - let vk = p256::ecdsa::VerifyingKey::from_sec1_bytes(pubkey.as_bytes()).unwrap(); use p256::ecdsa::signature::Verifier; assert!(vk.verify(msg, &sig).is_ok()); @@ -269,11 +267,11 @@ fn test_slot_id_display() { assert_eq!( format!("{}", SlotId::Management(ManagementSlotId::Pin)), - "PIN" + "Pin" ); assert_eq!( format!("{}", SlotId::Management(ManagementSlotId::Puk)), - "PUK" + "Puk" ); assert_eq!( format!("{}", SlotId::Management(ManagementSlotId::Management)), @@ -310,50 +308,6 @@ fn test_read_metadata() { assert_eq!(metadata.public, Some(generated)); } -#[test] -#[ignore] -fn test_serial_string_conversions() { - //2^152+1 - let serial: [u8; 20] = [ - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, - ]; - - let s = certificate::Serial::from(serial); - assert_eq!( - s.as_x509_int(), - "5708990770823839524233143877797980545530986497" - ); - assert_eq!( - s.as_x509_hex(), - "01:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01" - ); - - let serial2: [u8; 20] = [ - 0xA1, 0xF3, 0x02, 0x30, 0x76, 0x01, 0x32, 0x48, 0x09, 0x9C, 0x10, 0xAA, 0x3F, 0xA0, 0x54, - 0x0D, 0xC0, 0xB7, 0x65, 0x01, - ]; - - let s2 = certificate::Serial::from(serial2); - assert_eq!( - s2.as_x509_int(), - "924566785900861696177829411010986812227211191553" - ); - assert_eq!( - s2.as_x509_hex(), - "a1:f3:02:30:76:01:32:48:09:9c:10:aa:3f:a0:54:0d:c0:b7:65:01" - ); - - let serial3: [u8; 20] = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x3F, 0xA0, 0x54, - 0x0D, 0xC0, 0xB7, 0x65, 0x01, - ]; - - let s3 = certificate::Serial::from(serial3); - assert_eq!(s3.as_x509_int(), "3140531249369331492097"); - assert_eq!(s3.as_x509_hex(), "aa:3f:a0:54:0d:c0:b7:65:01"); -} - #[test] #[ignore] fn test_parse_cert_from_der() { From d9fb5510b0f7e4108ddf453d9bd0c501c7f17089 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Mon, 17 Apr 2023 10:52:43 -0700 Subject: [PATCH 2/6] Fix cli --- Cargo.lock | 200 +------------------------------------------- Cargo.toml | 5 +- cli/Cargo.toml | 2 +- cli/src/terminal.rs | 72 ++++++---------- src/certificate.rs | 12 ++- 5 files changed, 41 insertions(+), 250 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5b2a98a..5232e619 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,45 +20,6 @@ dependencies = [ "libc", ] -[[package]] -name = "asn1-rs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time 0.3.17", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -71,12 +32,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64ct" version = "1.5.3" @@ -132,7 +87,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", - "time 0.1.45", + "time", "wasm-bindgen", "winapi", ] @@ -281,12 +236,6 @@ dependencies = [ "syn", ] -[[package]] -name = "data-encoding" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" - [[package]] name = "der" version = "0.7.3" @@ -300,20 +249,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "der-parser" -version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - [[package]] name = "der_derive" version = "0.7.0" @@ -347,17 +282,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "displaydoc" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ecdsa" version = "0.16.6" @@ -569,12 +493,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - [[package]] name = "js-sys" version = "0.3.60" @@ -651,17 +569,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint-dig" version = "0.8.2" @@ -711,15 +618,6 @@ dependencies = [ "libm", ] -[[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" -dependencies = [ - "asn1-rs", -] - [[package]] name = "once_cell" version = "1.17.0" @@ -956,15 +854,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - [[package]] name = "rustix" version = "0.36.6" @@ -1091,18 +980,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "termcolor" version = "1.1.3" @@ -1112,26 +989,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "time" version = "0.1.45" @@ -1143,33 +1000,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "time" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" -dependencies = [ - "time-core", -] - [[package]] name = "typenum" version = "1.16.0" @@ -1188,12 +1018,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "uuid" version = "1.2.2" @@ -1366,7 +1190,7 @@ checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "x509-cert" version = "0.2.1" -source = "git+https://github.com/RustCrypto/formats#98957ae314cd8a9aa19eca95ba3aa9048159264f" +source = "git+https://github.com/RustCrypto/formats#8c29e1298aac72f56bdabeacf2be9e7b2da9d20e" dependencies = [ "const-oid", "der", @@ -1375,24 +1199,6 @@ dependencies = [ "spki", ] -[[package]] -name = "x509-parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" -dependencies = [ - "asn1-rs", - "base64", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time 0.3.17", -] - [[package]] name = "yubikey" version = "0.8.0-pre.0" @@ -1439,7 +1245,7 @@ dependencies = [ "once_cell", "sha2", "termcolor", - "x509-parser", + "x509-cert", "yubikey", ] diff --git a/Cargo.toml b/Cargo.toml index 3a8c59eb..183922bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,9 @@ rust-version = "1.65" [workspace] members = [".", "cli"] +[workspace.dependencies] +x509-cert = { git = "https://github.com/RustCrypto/formats", features = [ "builder", "hazmat" ] } + [dependencies] chrono = "0.4.23" const-oid = "0.9.2" @@ -44,7 +47,7 @@ sha1 = { version = "0.10", features = ["oid"] } sha2 = { version = "0.10", features = ["oid"] } subtle = "2" uuid = { version = "1.2", features = ["v4"] } -x509-cert = { git = "https://github.com/RustCrypto/formats", features = [ "builder", "hazmat" ] } +x509-cert.workspace = true zeroize = "1" [dev-dependencies] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a6b0b2e3..e23197e5 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -22,5 +22,5 @@ log = "0.4" once_cell = "1" sha2 = "0.10" termcolor = "1" -x509-parser = "0.14" +x509-cert.workspace = true yubikey = { version = "0.8.0-pre.0", path = ".." } diff --git a/cli/src/terminal.rs b/cli/src/terminal.rs index 19dc5696..ed836ad1 100644 --- a/cli/src/terminal.rs +++ b/cli/src/terminal.rs @@ -9,7 +9,7 @@ use std::{ sync::Mutex, }; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, StandardStreamLock, WriteColor}; -use x509_parser::parse_x509_certificate; +use x509_cert::der::Encode; use yubikey::{certificate::Certificate, piv::*, YubiKey}; /// Print a success status message (in green if colors are enabled) @@ -180,52 +180,30 @@ pub fn print_cert_info( 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", - &hex::upper::encode_string(&fingerprint), - )?; - print_cert_attr( - stream, - "Not Before", - cert.tbs_certificate - .validity - .not_before - .to_rfc2822() - .unwrap(), - )?; - print_cert_attr( - stream, - "Not After", - cert.tbs_certificate - .validity - .not_after - .to_rfc2822() - .unwrap(), - )?; - } - _ => { - println!("Failed to parse certificate"); - return Ok(()); - } - }; - } + let cert = &cert.cert; + + let fingerprint = Sha256::digest(&cert.to_der().unwrap()); + let slot_id: u8 = slot.into(); + print_cert_attr(stream, "Slot", format!("{:x}", slot_id))?; + print_cert_attr( + stream, + "Algorithm", + cert.tbs_certificate.subject_public_key_info.algorithm.oid, + )?; + + print_cert_attr(stream, "Subject", &cert.tbs_certificate.subject)?; + print_cert_attr(stream, "Issuer", &cert.tbs_certificate.issuer)?; + print_cert_attr( + stream, + "Fingerprint", + &hex::upper::encode_string(&fingerprint), + )?; + print_cert_attr( + stream, + "Not Before", + cert.tbs_certificate.validity.not_before, + )?; + print_cert_attr(stream, "Not After", cert.tbs_certificate.validity.not_after)?; Ok(()) } diff --git a/src/certificate.rs b/src/certificate.rs index 12347aa9..7b953244 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -42,7 +42,6 @@ use crate::{ use log::error; use x509_cert::{ builder::{CertificateBuilder, Profile}, - certificate::Version, der::{self, referenced::OwnedToRef, Decode, Encode}, name::Name, serial_number::SerialNumber, @@ -112,15 +111,14 @@ impl Certificate { where F: FnOnce(&mut CertificateBuilder<'_, yubikey_signer::Signer<'_, KT>>) -> der::Result<()>, { - let mut signer = yubikey_signer::Signer::new(yubikey, key, subject_pki.owned_to_ref())?; + let signer = yubikey_signer::Signer::new(yubikey, key, subject_pki.owned_to_ref())?; let mut builder = CertificateBuilder::new( Profile::Manual { issuer: None }, - Version::V3, serial, validity, subject, subject_pki, - &mut signer, + &signer, ) .map_err(|_| Error::KeyError)?; @@ -439,6 +437,12 @@ pub mod yubikey_signer { } } + impl<'y, KT: KeyType> DynSignatureAlgorithmIdentifier for Signer<'y, KT> { + fn signature_algorithm_identifier(&self) -> spki::Result { + self.verifying_key().signature_algorithm_identifier() + } + } + impl<'y, KT: KeyType> signature::Signer for Signer<'y, KT> { fn try_sign(&self, msg: &[u8]) -> SigResult { let data = KT::prepare(msg)?; From 0868a2edc6a2cf91183693e86d130ac75fd1b965 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Fri, 21 Apr 2023 15:20:29 -0700 Subject: [PATCH 3/6] bump to latest x509 builder --- Cargo.lock | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5232e619..99932d0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,7 +127,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -216,7 +216,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 1.0.107", ] [[package]] @@ -233,14 +233,14 @@ checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] name = "der" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +checksum = "86b14af2045fa69ed2b7a48934bebb842d0f33e73e96e78766ecb14bb5347a11" dependencies = [ "const-oid", "der_derive", @@ -251,14 +251,14 @@ dependencies = [ [[package]] name = "der_derive" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63898447d5453a504531990fb79708be1087effb2da9b2704f54dbdf8b6890e4" +checksum = "114792ba6b7545d3f3dd693794aed3a312a67795cd577fcc725c148d84fabe32" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -743,7 +743,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "version_check", ] @@ -760,18 +760,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -980,6 +980,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -1066,7 +1077,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -1088,7 +1099,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1190,7 +1201,7 @@ checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "x509-cert" version = "0.2.1" -source = "git+https://github.com/RustCrypto/formats#8c29e1298aac72f56bdabeacf2be9e7b2da9d20e" +source = "git+https://github.com/RustCrypto/formats#c9caa0441d7e0e255fe4a8660f5e0bbdfafb5995" dependencies = [ "const-oid", "der", From b295d8abd4485b3178f36df3e5dc119727931644 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Thu, 27 Apr 2023 10:34:29 -0700 Subject: [PATCH 4/6] Bump rsa to 0.9.2 --- Cargo.lock | 24 ++++++++++++++---------- Cargo.toml | 7 ++++--- src/certificate.rs | 8 ++++---- tests/integration.rs | 2 +- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99932d0f..b5fdb0bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b14af2045fa69ed2b7a48934bebb842d0f33e73e96e78766ecb14bb5347a11" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" dependencies = [ "const-oid", "der_derive", @@ -284,15 +284,16 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.6" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ "der", "digest", "elliptic-curve", "rfc6979", "signature", + "spki", ] [[package]] @@ -835,9 +836,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd2017d3e6d67384f301f8b06fbf4567afc576430a61624d845eb04d2b30a72" +checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" dependencies = [ "byteorder", "const-oid", @@ -849,7 +850,9 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core", + "sha2", "signature", + "spki", "subtle", "zeroize", ] @@ -949,9 +952,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spki" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -1200,8 +1203,9 @@ checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "x509-cert" -version = "0.2.1" -source = "git+https://github.com/RustCrypto/formats#c9caa0441d7e0e255fe4a8660f5e0bbdfafb5995" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077a1672d8ecfa5c1fe69fa7c5d043962e4e32866d4375495536261c0b4781f5" dependencies = [ "const-oid", "der", diff --git a/Cargo.toml b/Cargo.toml index 183922bc..a8ffd3cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rust-version = "1.65" members = [".", "cli"] [workspace.dependencies] -x509-cert = { git = "https://github.com/RustCrypto/formats", features = [ "builder", "hazmat" ] } +x509-cert = { version = "0.2.3", features = [ "builder", "hazmat" ] } [dependencies] chrono = "0.4.23" @@ -35,16 +35,17 @@ nom = "7" num-bigint-dig = { version = "0.8", features = ["rand"] } num-traits = "0.2" num-integer = "0.1" -ecdsa = { version = "0.16.2", features = ["digest", "pem"] } +ecdsa = { version = "0.16.7", features = ["digest", "pem"] } p256 = "0.13" p384 = "0.13" pbkdf2 = { version = "0.12", default-features = false, features = ["hmac"] } pcsc = "2.3.1" rand_core = { version = "0.6", features = ["std"] } -rsa = "0.9" +rsa = { version = "0.9.2", features = ["sha2"] } secrecy = "0.8" sha1 = { version = "0.10", features = ["oid"] } sha2 = { version = "0.10", features = ["oid"] } +signature = "2.0.0" subtle = "2" uuid = { version = "1.2", features = ["v4"] } x509-cert.workspace = true diff --git a/src/certificate.rs b/src/certificate.rs index 7b953244..4d3f86d0 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -41,7 +41,7 @@ use crate::{ }; use log::error; use x509_cert::{ - builder::{CertificateBuilder, Profile}, + builder::{Builder, CertificateBuilder, Profile}, der::{self, referenced::OwnedToRef, Decode, Encode}, name::Name, serial_number::SerialNumber, @@ -255,11 +255,11 @@ pub mod yubikey_signer { Encode, Sequence, }; use sha2::{Digest, Sha256, Sha384}; - use signature::{Keypair, SignatureEncoding}; + use signature::Keypair; use std::{cell::RefCell, fmt, io::Write, marker::PhantomData}; use x509_cert::spki::{ self, AlgorithmIdentifierOwned, DynSignatureAlgorithmIdentifier, EncodePublicKey, - SubjectPublicKeyInfoRef, + SignatureBitStringEncoding, SubjectPublicKeyInfoRef, }; type SigResult = core::result::Result; @@ -269,7 +269,7 @@ pub mod yubikey_signer { /// Error returned when working with signature type Error: Into + fmt::Debug; /// The signature type returned by the signer - type Signature: SignatureEncoding + type Signature: SignatureBitStringEncoding + for<'s> TryFrom<&'s [u8], Error = Self::Error> + fmt::Debug; /// The public key used to verify signature diff --git a/tests/integration.rs b/tests/integration.rs index 1854c930..8fa38205 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -200,7 +200,7 @@ fn generate_self_signed_rsa_cert() { // let pubkey = RsaPublicKey::try_from(cert.subject_pki()).expect("valid rsa key"); - let pubkey = pkcs1v15::VerifyingKey::::new_with_prefix(pubkey); + let pubkey = pkcs1v15::VerifyingKey::::new(pubkey); let data = cert.cert.to_der().expect("serialize certificate"); let tbs_cert_len = u16::from_be_bytes(data[6..8].try_into().unwrap()) as usize; From 69481c3300786a2f48483f320dbb08cbd37e5dc3 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Tue, 13 Jun 2023 20:13:25 -0700 Subject: [PATCH 5/6] extraneous const-oid dep --- Cargo.lock | 1 - Cargo.toml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5fdb0bb..cb78361a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1220,7 +1220,6 @@ version = "0.8.0-pre.0" dependencies = [ "base16ct", "chrono", - "const-oid", "der", "des", "ecdsa", diff --git a/Cargo.toml b/Cargo.toml index a8ffd3cb..3b79c2b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,8 @@ x509-cert = { version = "0.2.3", features = [ "builder", "hazmat" ] } [dependencies] chrono = "0.4.23" -const-oid = "0.9.2" -des = "0.8" der = "0.7.1" +des = "0.8" elliptic-curve = "0.13" hex = { package = "base16ct", version = "0.2", features = ["alloc"] } hmac = "0.12" From 07b4ac3b0e4336b7e5e93ab08997c490c8ec551a Mon Sep 17 00:00:00 2001 From: Carl Wallace Date: Mon, 10 Jul 2023 11:39:29 -0400 Subject: [PATCH 6/6] add Attestation to list of slots. make Signer::new public. --- src/certificate.rs | 3 ++- src/piv.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/certificate.rs b/src/certificate.rs index 4d3f86d0..4922613a 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -414,7 +414,8 @@ pub mod yubikey_signer { } impl<'y, KT: KeyType> Signer<'y, KT> { - pub(crate) fn new( + /// Create new Signer + pub fn new( yubikey: &'y mut YubiKey, key: SlotId, subject_pki: SubjectPublicKeyInfoRef<'_>, diff --git a/src/piv.rs b/src/piv.rs index 62a34356..a035179c 100644 --- a/src/piv.rs +++ b/src/piv.rs @@ -442,10 +442,11 @@ impl ManagementSlotId { } /// Personal Identity Verification (PIV) key slots -pub const SLOTS: [SlotId; 27] = [ +pub const SLOTS: [SlotId; 28] = [ SlotId::Authentication, SlotId::Signature, SlotId::KeyManagement, + SlotId::Attestation, SlotId::Retired(RetiredSlotId::R1), SlotId::Retired(RetiredSlotId::R2), SlotId::Retired(RetiredSlotId::R3),