diff --git a/Cargo.toml b/Cargo.toml index 543a3ef..28fcf82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,11 +44,11 @@ wasm_experimental = ["ring/wasm32_unknown_unknown_js"] # if you need to work with fido data, without needing to generate it. fido-support = ["ctap-hid-fido2", "fido-lite"] fido-support-mozilla = ["authenticator", "fido-lite"] -fido-lite = ["minicbor", "x509-parser"] +fido-lite = ["minicbor", "x509-cert"] rsa-signing = ["simple_asn1", "num-bigint"] x509-support = ["der-parser", "x509", "x509-parser"] yubikey-support = ["rcgen", "yubikey", "yubikey-lite"] -yubikey-lite = ["x509-support", "der", "x509-cert"] +yubikey-lite = ["x509-support", "x509-cert", "p256", "p384"] [dependencies] base64 = "0.13" @@ -67,8 +67,9 @@ rcgen = { version = "0.11", optional = true } x509 = { version = "0.2", optional = true } x509-parser = { version = "0.15", features = ["verify"], optional = true } der-parser = { version = "5", optional = true } -der = { version = "0.7", optional = true } x509-cert = { version = "0.2", optional = true } +p256 = { version = "0.13.2", optional = true } +p384 = { version = "0.13.1", optional = true } # Dependencies for encrypted-keys aes = { version = "0.7", features = ["ctr"], optional = true } @@ -97,8 +98,6 @@ env_logger = "0.8.2" hex = "0.4.2" clap = "3.0.5" criterion = "0.3" -p256 = "*" -p384 = "*" [[bench]] name = "certs_per_second" @@ -113,6 +112,10 @@ required-features = ["yubikey-support"] name = "yk-provision" required-features = ["yubikey-support"] +[[example]] +name = "yk-generate-csr" +required-features = ["yubikey-support"] + [[example]] name = "sign-cert-with-yubikey" required-features = ["yubikey-support"] diff --git a/examples/yk-generate-csr.rs b/examples/yk-generate-csr.rs new file mode 100644 index 0000000..98ac54e --- /dev/null +++ b/examples/yk-generate-csr.rs @@ -0,0 +1,89 @@ +use std::env; + +use clap::{Arg, Command}; + +use sshcerts::yubikey::piv::Yubikey; +use sshcerts::yubikey::piv::{RetiredSlotId, SlotId}; + +use x509_parser::prelude::*; + +use std::convert::TryFrom; + +fn slot_parser(slot: &str) -> Option { + // If first character is R, then we need to parse the nice + // notation + if (slot.len() == 2 || slot.len() == 3) && slot.starts_with('R') { + let slot_value = slot[1..].parse::(); + match slot_value { + Ok(v) if v <= 20 => Some(SlotId::try_from(0x81_u8 + v).unwrap()), + _ => None, + } + } else if slot.len() == 4 && slot.starts_with("0x") { + let slot_value = hex::decode(&slot[2..]).unwrap()[0]; + Some(SlotId::try_from(slot_value).unwrap()) + } else { + None + } +} + +fn slot_validator(slot: &str) -> Result<(), String> { + match slot_parser(slot) { + Some(_) => Ok(()), + None => Err(String::from( + "Provided slot was not valid. Should be R1 - R20 or a raw hex identifier", + )), + } +} + +/// This routine will generate a new PIV key and use the new key to create a CSR signer. +/// Then use this CSR signer to sign a random blob and then verify the signature. +fn main() { + env_logger::init(); + let matches = Command::new("yk-generate-csr") + .version(env!("CARGO_PKG_VERSION")) + .author("Thanh Nguyen slot_parser(x).unwrap(), + None => SlotId::Retired(RetiredSlotId::R17), + }; + let pin = matches.value_of("pin").unwrap(); + let mgm_key = &hex::decode(matches.value_of("management-key").unwrap()).unwrap(); + + let mut yk = Yubikey::new().unwrap(); + yk.unlock(pin.as_bytes(), mgm_key).unwrap(); + + let csr = yk.generate_csr(&slot, "TestCSR").unwrap(); + + let (_, parsed_csr) = + x509_parser::certification_request::X509CertificationRequest::from_der(&csr).unwrap(); + parsed_csr.verify_signature().unwrap(); +} diff --git a/examples/yk-provision.rs b/examples/yk-provision.rs index bf102f7..1a4cbad 100644 --- a/examples/yk-provision.rs +++ b/examples/yk-provision.rs @@ -30,15 +30,18 @@ fn provision_new_key( let mut yk = Yubikey::new().unwrap(); yk.unlock(pin.as_bytes(), mgm_key).unwrap(); match alg { - "p256" => match yk.provision::(&slot, subject, policy, PinPolicy::Never) { - Ok(pk) => { - println!("New hardware backed SSH Public Key: {}", pk); + "p256" => { + println!("Using P256"); + match yk.provision_p256(&slot, subject, policy, PinPolicy::Never) { + Ok(pk) => { + println!("New hardware backed SSH Public Key: {}", pk); + } + Err(e) => panic!("Could not provision device with new key: {:?}", e), } - Err(e) => panic!("Could not provision device with new key: {:?}", e), }, _ => { println!("Using P384"); - match yk.provision::(&slot, subject, policy, PinPolicy::Never) { + match yk.provision_p384(&slot, subject, policy, PinPolicy::Never) { Ok(pk) => { println!("New hardware backed SSH Public Key: {}", pk); } diff --git a/src/fido/generate/mozilla.rs b/src/fido/generate/mozilla.rs index f17642d..e7a21d9 100644 --- a/src/fido/generate/mozilla.rs +++ b/src/fido/generate/mozilla.rs @@ -8,8 +8,8 @@ use crate::{ PrivateKey, }; -use ring::rand::SecureRandom; -use ring::rand::SystemRandom; +use ring::digest; +use ring::rand::{SecureRandom, SystemRandom}; use crate::fido::Error as FidoError; @@ -47,12 +47,20 @@ pub fn generate_new_ssh_key( }; manager.add_u2f_usb_hid_platform_transports(); - let mut chall_bytes = [0u8; 32]; - SystemRandom::new().fill(&mut chall_bytes).unwrap(); + // This forms the challenge + let mut client_data = [0u8; 32]; + // Fill it with random data because we don't support taking in + // challenge data at this point. + SystemRandom::new().fill(&mut client_data).unwrap(); + + // Hash the data because that is what will actually be signed + let client_data_digest = digest::digest(&digest::SHA256, &client_data); + let mut client_data_hash = [0u8; 32]; + client_data_hash.copy_from_slice(client_data_digest.as_ref()); let origin = application.to_string(); let ctap_args = RegisterArgs { - client_data_hash: chall_bytes, + client_data_hash, relying_party: RelyingParty { id: origin.clone(), name: None, @@ -170,7 +178,7 @@ pub fn generate_new_ssh_key( auth_data: raw_auth_data, auth_data_sig, intermediate, - challenge: chall_bytes.to_vec(), + challenge: client_data_hash.into(), alg, }; diff --git a/src/fido/verification.rs b/src/fido/verification.rs index 5b272aa..ddac684 100644 --- a/src/fido/verification.rs +++ b/src/fido/verification.rs @@ -10,7 +10,8 @@ use super::AuthData; use ring::signature::{UnparsedPublicKey, ECDSA_P256_SHA256_ASN1}; -const YUBICO_U2F_ROOT_CA: &str = "-----BEGIN CERTIFICATE----- +/// From https://developers.yubico.com/PKI/yubico-ca-certs.txt +const YUBICO_U2F_ROOT_CA_457200631: &str = "-----BEGIN CERTIFICATE----- MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290 @@ -30,6 +31,116 @@ sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw== -----END CERTIFICATE-----"; +/// From https://developers.yubico.com/PKI/yubico-ca-certs.txt +const YUBICO_ATTESTATION_ROOT_1: &str = "-----BEGIN CERTIFICATE----- +MIIDPjCCAiagAwIBAgIUXzeiEDJEOTt14F5n0o6Zf/bBwiUwDQYJKoZIhvcNAQEN +BQAwJDEiMCAGA1UEAwwZWXViaWNvIEF0dGVzdGF0aW9uIFJvb3QgMTAgFw0yNDEy +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowJDEiMCAGA1UEAwwZWXViaWNvIEF0 +dGVzdGF0aW9uIFJvb3QgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AMZ6/TxM8rIT+EaoPvG81ontMOo/2mQ2RBwJHS0QZcxVaNXvl12LUhBZ5LmiBScI +Zd1Rnx1od585h+/dhK7hEm7JAALkKKts1fO53KGNLZujz5h3wGncr4hyKF0G74b/ +U3K9hE5mGND6zqYchCRAHfrYMYRDF4YL0X4D5nGdxvppAy6nkEmtWmMnwO3i0TAu +csrbE485HvGM4r0VpgVdJpvgQjiTJCTIq+D35hwtT8QDIv+nGvpcyi5wcIfCkzyC +imJukhYy6KoqNMKQEdpNiSOvWyDMTMt1bwCvEzpw91u+msUt4rj0efnO9s0ZOwdw +MRDnH4xgUl5ZLwrrPkfC1/0CAwEAAaNmMGQwHQYDVR0OBBYEFNLu71oijTptXCOX +PfKF1SbxJXuSMB8GA1UdIwQYMBaAFNLu71oijTptXCOXPfKF1SbxJXuSMBIGA1Ud +EwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IB +AQC3IW/sgB9pZ8apJNjxuGoX+FkILks0wMNrdXL/coUvsrhzsvl6mePMrbGJByJ1 +XnquB5sgcRENFxdQFma3mio8Upf1owM1ZreXrJ0mADG2BplqbJnxiyYa+R11reIF +TWeIhMNcZKsDZrFAyPuFjCWSQvJmNWe9mFRYFgNhXJKkXIb5H1XgEDlwiedYRM7V +olBNlld6pRFKlX8ust6OTMOeADl2xNF0m1LThSdeuXvDyC1g9+ILfz3S6OIYgc3i +roRcFD354g7rKfu67qFAw9gC4yi0xBTPrY95rh4/HqaUYCA/L8ldRk6H7Xk35D+W +Vpmq2Sh/xT5HiFuhf4wJb0bK +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_ATTESTATION_INTERMEDIATE_A_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIUUcmMXzRIFOgGTK0Tb3gEuZYZkBIwDQYJKoZIhvcNAQEL +BQAwJDEiMCAGA1UEAwwZWXViaWNvIEF0dGVzdGF0aW9uIFJvb3QgMTAgFw0yNDEy +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowLjEsMCoGA1UEAwwjWXViaWNvIEF0 +dGVzdGF0aW9uIEludGVybWVkaWF0ZSBBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDm555bWY9WW+tOY0rIWHldh+aNanoCZCFh7Gk3YZrQmPUw0hkS +G6qYHQtP+fZyS33VErvg+BQqnmumgNhfxFrkwEZELeidBcC8C4Ag4nqqiPWpzsvI +17NcxYlInLNLFcZY/+gOiN6ZOTihO5/vBZMbj9riaAcqliYmNGJPgTcMGaEAyMzE +MNy2nm6Ep+pjP5aF6gi21t/UQFsuJ1j2Rj/ynM/SdRt+ecal5OYotxHkFbL9vvv2 +A2Ov5ITZClw4bOS9npypQimOZ5QAYytmYaQpWl/pMYz6zSj8RqkVDNEJGqNfTKA2 +ivLYwX6lSttMPapg0J84l9X0voVN/FpS4VCVAgMBAAGjZjBkMB0GA1UdDgQWBBQg +KFAhG6RaW+hTy52dxeT8bC96HzAfBgNVHSMEGDAWgBTS7u9aIo06bVwjlz3yhdUm +8SV7kjASBgNVHRMBAf8ECDAGAQH/AgECMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAQEAYMzgLrJLIr0OovQnAZrRIGuabiHSUKSmbLRWpRkWeAtsChDE +HpXcJ/bgDNKYWoHqQ8xRUjB4CyepYevc3YlrG8o7zHxpfVcaoL5SeuJkzHxKn4bT +aSp9+Mvwamnp64kZMiNbFLknfP9kYKoRHkMWheRJ1UsP1z4ScmkCeILfsMs6vqov +qjWClFsJpBcsluYHWF7bBJ1n4Rwg+ATEopY4IgGv6Zvwc+A9r+AT2hqpoSkYoAl+ +ANYwgslOf9sJe0V+TA9YY/UlaBmPPTd0//r9wvcePWZkPjKoAC/zUNhfDbh4LV8G +Hs3lyX2XomL/LNc8JYzyIaDEhGQveoPhh/tr1g== +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_ATTESTATION_INTERMEDIATE_B_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIUDqERw+4RnGSggxgUewJFEPDRZ3YwDQYJKoZIhvcNAQEL +BQAwJDEiMCAGA1UEAwwZWXViaWNvIEF0dGVzdGF0aW9uIFJvb3QgMTAgFw0yNDEy +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowLjEsMCoGA1UEAwwjWXViaWNvIEF0 +dGVzdGF0aW9uIEludGVybWVkaWF0ZSBCIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDI7XnH+ZvDwMCQU8M8ZeV5qscublvVYaaRt3Ybaxn9godLx5sw +H0lXrdgjh5h7FpVgCgYYX7E4bl1vbzULemrMWT8N3WMGUe8QAJbBeioV7W/E+hTZ +P/0SKJVa3ewKBo6ULeMnfQZDrVORAk8wTLq2v5Llj5vMj7JtOotKa9J7nHS8kLmz +XXSaj0SwEPh5OAZUTNV4zs1bvoTAQQWrL4/J9QuKt6WCFE5nUNiRQcEbVF8mlqK2 +bx2z6okVltyDVLCxYbpUTELvY1usR3DTGPUoIClOm4crpwnDRLVHvjYePGBB//pE +yzxA/gcScxjwaH1ZUw9bnSbHyurKqbTa1KvjAgMBAAGjZjBkMB0GA1UdDgQWBBTq +t0KQngx7ZHrbVHwDunxOn9ihYTAfBgNVHSMEGDAWgBTS7u9aIo06bVwjlz3yhdUm +8SV7kjASBgNVHRMBAf8ECDAGAQH/AgECMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAQEAqQaCWMxTGqVVX7Sk7kkJmUueTSYKuU6+KBBSgwIRnlw9K7He +1IpxZ0hdwpPNikKjmcyFgFPzhImwHJgxxuT90Pw3vYOdcJJNktDg35PXOfzSn15c +FAx1RO0mPTmIb8dXiEWOpzoXvdwXDM41ZaCDYMT7w4IQtMyvE7xUBZq2bjtAnq/N +DUA7be4H8H3ipC+/+NKlUrcUh+j48K67WI0u1m6FeQueBA7n06j825rqDqsaLs9T +b7KAHAw8PmrWaNPG2kjKerxPEfecivlFawp2RWZvxrVtn3TV2SBxyCJCkXsND05d +CErVHSJIs+BdtTVNY9AwtyPmnyb0v4mSTzvWdw== +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_FIDO_ATTESTATION_A_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIUTnbbGIR2NHvzqIKFAeQwG1XBis0wDQYJKoZIhvcNAQEL +BQAwLjEsMCoGA1UEAwwjWXViaWNvIEF0dGVzdGF0aW9uIEludGVybWVkaWF0ZSBB +IDEwIBcNMjQxMjAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMCYxJDAiBgNVBAMM +G1l1YmljbyBGSURPIEF0dGVzdGF0aW9uIEEgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAOsXj3k04Ban4TYdtZKqD/OPJxyDyaPmCBUFUiaZIgTteZnj +3X25DhgpZZXsC4D0ydIcrlA6wNUInORL/L9zBbTEIMAVMGo6g7UKAmb2MF6AHbnh +YJd9eikupVNWShHNYNc4GBdO1YN6AfUqvJhHbe3V4SNMPmBREKJPVz7ThwgmggTe +8Ws2K0/wsqv2wSE7pbCBsUZhIX51bZM3pqDwJPTmRFEvt0/6tG5eO8F3j14OXqfE +hmjn1VvxKDYQOLZAxCwwgC0P4CdfWv3y8PSR8I354hO1Y+GzNjvIqX38NKLywuIY +HFerOxNlxEMBvFhYBuRuYAkkgUaPqN6UBhsILrsCAwEAAaNmMGQwHQYDVR0OBBYE +FCCoRHhiyNnbnXRWIL6ZBXoBX9YTMB8GA1UdIwQYMBaAFCAoUCEbpFpb6FPLnZ3F +5PxsL3ofMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4IBAQCQFafJI1/5Wg9CEEimE1RP54RgQwTNTOOQsLACTe+rItlF +QzC9ZDhrV828yX7jzy+AAsp3izK7T1th2dl7m+tu0sw2Pa/olc02nt6PyIw348ga +HzhI1+0KE45qxvFDeL2lMxbPfCYvyEEaYzjiQELU5951pXGWyKMa/4fLtO+ZKOXh +MuVeq4rXDPI54W6JHOiAaiKdiw+5e3c2kt/jFIQtM6vMXg9LNFzdjETNt20VX9Qe +vRpFZfucMG9wCaQDoFlPzpTMJKhPev/imJmZYhKfr0lLcemtqjIxLAoqZdOYfHBg +6+vAcdPI/iauGpUAv7X+UKNmDwjZ2BaH4sLwhB2m +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_FIDO_ATTESTATION_B_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIUR38mq26Sf2szVV2BdG6WEN7kuWUwDQYJKoZIhvcNAQEL +BQAwLjEsMCoGA1UEAwwjWXViaWNvIEF0dGVzdGF0aW9uIEludGVybWVkaWF0ZSBC +IDEwIBcNMjQxMjAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMCYxJDAiBgNVBAMM +G1l1YmljbyBGSURPIEF0dGVzdGF0aW9uIEIgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBANY0Wb9oPoRoKoQyWPaJpz11vrWTg6zTtmNj2VoKRnyvKGRq +pzb83w5l6YA96UYkYBDQP0ilO2DPe6wWqVR5zDfRzdcH8bh+L7dGGvae6hRTZhkF +kCpXDs4HccknrDf8FClJ7He39Jf42/G1Qm2zz9WWmrPXtgiK/x05GjsQfGuDG1zf +5QTUUie8lwymK3TfdOvNeeJAAPe2pn7ItfRb+rVrNWiDzlRn2vNnZ2wPo4wH/WJ6 +dhXZG+rMWT+a6Bocg1UfIw6kdunG4bTpZzsvacFYyR0mpf+DeOnpSWAmywJWHvTl +f2YXxFyeXcTACdQlcMNGJ2VhZQ48xtP5/RBP/8kCAwEAAaNmMGQwHQYDVR0OBBYE +FChy42okiqcTS1iqa/HRWjkBn4H/MB8GA1UdIwQYMBaAFOq3QpCeDHtkettUfAO6 +fE6f2KFhMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4IBAQAn+RHIPbtMEDNdT1g8H/RitAkUdLgAt1tWGWnlj9knbv4/ +4GlX7C9p45efPO9/aZL6OV1XRKBi6KmtBW5K7nuYEnMx/5BqBSbLT7rhduC49TBe +Mb9PHdXsTlSVNYefr1dGidr4j0xVBQLb1rknDAbdWDzKfvnayKO8Frwe7Hx843MG +/rJ+c0XruUvbfVTCHLiIWhM7oNDhL8xob6xUo9KLKcSL+ItYsO3/9Wb8Q9GjsqL4 +FXsDcG1SaYh7KpfuMmOixqzJZO2nIicPYRg1I2SuiUfYO70tmdHcbl+kSQmSYt7r +q4viILg2Gx3j9rITuWTjbaUaSSQxgOmMSHuyzMAC +-----END CERTIFICATE-----"; + /// Defines the transports supported by the FIDO standard #[derive(Clone, Debug, PartialEq)] pub enum Transport { @@ -132,8 +243,82 @@ fn extract_certificate_extension_data( Ok(valid_attestation) } +/// Verify that the intermediates are chained to the root CA. +fn verify_intermediates( + parsed_intermediate: &X509Certificate<'_>, + ca_pems: Vec<&str>, +) -> Result<(), Error> { + // There has to be at least the root CA + if ca_pems.is_empty() { + return Err(Error::InvalidSignature); + } + + let mut ca_parsed_pems = vec![]; + + // Parse all the pems + for pem in ca_pems { + let (_, parsed_pem) = parse_x509_pem(pem.as_bytes()).map_err(|_| Error::ParsingError)?; + ca_parsed_pems.push(parsed_pem); + } + + // Parse the root CA + let root_ca = ca_parsed_pems.first().ok_or(Error::ParsingError)?; + let mut parent_ca = Pem::parse_x509(&root_ca).map_err(|_| Error::ParsingError)?; + + // Iteratively verify the chain + for intermediate_ca in ca_parsed_pems.iter().skip(1) { + let intermediate_ca = Pem::parse_x509(&intermediate_ca).map_err(|_| Error::ParsingError)?; + + // Check the parent CA has signed this intermediate, return error if not + intermediate_ca + .verify_signature(Some(&parent_ca.tbs_certificate.public_key())) + .map_err(|_| Error::InvalidSignature)?; + + parent_ca = intermediate_ca; + } + + // Check the parent intermediate CA has signed the final intermediate, return error if not + parsed_intermediate + .verify_signature(Some(&parent_ca.tbs_certificate.public_key())) + .map_err(|_| Error::InvalidSignature)?; + + Ok(()) +} + +/// Verify that the intermediate chains to some Yubico root CA for FIDO attestation +/// We try all known Yubico Root CAs for backward compatibility +fn verify_yubico_intermediates(parsed_intermediate: &X509Certificate<'_>) -> Result<(), Error> { + if verify_intermediates( + &parsed_intermediate, + vec![ + YUBICO_ATTESTATION_ROOT_1, + YUBICO_ATTESTATION_INTERMEDIATE_A_1, + YUBICO_FIDO_ATTESTATION_A_1, + ], + ) + .is_ok() + { + return Ok(()); + } + + if verify_intermediates( + &parsed_intermediate, + vec![ + YUBICO_ATTESTATION_ROOT_1, + YUBICO_ATTESTATION_INTERMEDIATE_B_1, + YUBICO_FIDO_ATTESTATION_B_1, + ], + ) + .is_ok() + { + return Ok(()); + } + + verify_intermediates(&parsed_intermediate, vec![YUBICO_U2F_ROOT_CA_457200631]) +} + /// Verify a provided U2F attestation, signature, and certificate are valid -/// against the root. If no root is given, the Yubico U2F Root is used. +/// against the root. If no root is given, the Yubico U2F Root and FIDO root are used. pub fn verify_auth_data( auth_data: &[u8], auth_data_signature: &[u8], @@ -145,20 +330,16 @@ pub fn verify_auth_data( match alg { // Verify using ECDSA256 -7 => { - let root_ca_pem = root_pem.unwrap_or(YUBICO_U2F_ROOT_CA); - - // Parse the U2F root CA - let (_, root_ca) = - parse_x509_pem(root_ca_pem.as_bytes()).map_err(|_| Error::ParsingError)?; - let root_ca = Pem::parse_x509(&root_ca).map_err(|_| Error::ParsingError)?; - let (_, parsed_intermediate) = X509Certificate::from_der(intermediate).map_err(|_| Error::ParsingError)?; - // Check the root CA has signed the intermediate, return error if not - parsed_intermediate - .verify_signature(Some(&root_ca.tbs_certificate.public_key())) - .map_err(|_| Error::InvalidSignature)?; + // If a custom root CA is provided, we use that for verification. + // If not, we will try all the known Yubico Root CAs for backward compatibility + if let Some(pem) = root_pem { + verify_intermediates(&parsed_intermediate, vec![pem])?; + } else { + verify_yubico_intermediates(&parsed_intermediate)?; + } // Extract public key from verified intermediate certificate let key_bytes = parsed_intermediate diff --git a/src/yubikey/piv/management.rs b/src/yubikey/piv/management.rs index a3cc01e..2e27791 100644 --- a/src/yubikey/piv/management.rs +++ b/src/yubikey/piv/management.rs @@ -4,7 +4,7 @@ use ring::digest; use yubikey::certificate::Certificate; use yubikey::piv::{attest, sign_data as yk_sign_data, AlgorithmId, SlotId}; -use yubikey::{MgmKey, YubiKey}; +use yubikey::{MgmKey, Serial, YubiKey}; use yubikey::{PinPolicy, TouchPolicy}; use super::{Error, Result}; @@ -144,6 +144,12 @@ impl super::Yubikey { Ok(()) } + /// Fetch the serial nummbere of the Yubikey + pub fn serial(&mut self) -> Result { + let serial = self.yk.serial(); + Ok(serial) + } + /// Check to see that a provided Yubikey and slot is configured for signing pub fn configured(&mut self, slot: &SlotId) -> Result { let cert = Certificate::read(&mut self.yk, *slot)?; @@ -237,6 +243,32 @@ impl super::Yubikey { self.ssh_cert_fetch_pubkey(slot) } + /// Provisions the YubiKey with a new certificate generated on the device. + /// Only keys that are generated this way can use the attestation functionality. + /// This is a nongeneric version to generate a p384 key + pub fn provision_p384( + &mut self, + slot: &SlotId, + common_name: &str, + touch_policy: TouchPolicy, + pin_policy: PinPolicy, + ) -> Result { + self.provision::(slot, common_name, touch_policy, pin_policy) + } + + /// Provisions the YubiKey with a new certificate generated on the device. + /// Only keys that are generated this way can use the attestation functionality. + /// This is a nongeneric version to generate a p256 key + pub fn provision_p256( + &mut self, + slot: &SlotId, + common_name: &str, + touch_policy: TouchPolicy, + pin_policy: PinPolicy, + ) -> Result { + self.provision::(slot, common_name, touch_policy, pin_policy) + } + /// Take data, an algorithm, and a slot and attempt to sign the data field /// /// If the requested algorithm doesn't match the key in the slot (or the slot diff --git a/src/yubikey/verification.rs b/src/yubikey/verification.rs index 8722d91..8c707c2 100644 --- a/src/yubikey/verification.rs +++ b/src/yubikey/verification.rs @@ -6,7 +6,8 @@ use x509_parser::prelude::*; use std::convert::TryInto; -const YUBICO_PIV_ROOT_CA: &str = "-----BEGIN CERTIFICATE----- +/// From https://developers.yubico.com/PKI/yubico-ca-certs.txt +const YUBICO_PIV_ROOT_CA_263751: &str = "-----BEGIN CERTIFICATE----- MIIDFzCCAf+gAwIBAgIDBAZHMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNVBAMMIFl1 YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAwMDAwMFoY DzIwNTIwNDE3MDAwMDAwWjArMSkwJwYDVQQDDCBZdWJpY28gUElWIFJvb3QgQ0Eg @@ -26,6 +27,116 @@ Fqyi4+JE014cSgR57Jcu3dZiehB6UtAPgad9L5cNvua/IWRmm+ANy3O2LH++Pyl8 SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7 -----END CERTIFICATE-----"; +/// From https://developers.yubico.com/PKI/yubico-ca-certs.txt +const YUBICO_ATTESTATION_ROOT_1: &str = "-----BEGIN CERTIFICATE----- +MIIDPjCCAiagAwIBAgIUXzeiEDJEOTt14F5n0o6Zf/bBwiUwDQYJKoZIhvcNAQEN +BQAwJDEiMCAGA1UEAwwZWXViaWNvIEF0dGVzdGF0aW9uIFJvb3QgMTAgFw0yNDEy +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowJDEiMCAGA1UEAwwZWXViaWNvIEF0 +dGVzdGF0aW9uIFJvb3QgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AMZ6/TxM8rIT+EaoPvG81ontMOo/2mQ2RBwJHS0QZcxVaNXvl12LUhBZ5LmiBScI +Zd1Rnx1od585h+/dhK7hEm7JAALkKKts1fO53KGNLZujz5h3wGncr4hyKF0G74b/ +U3K9hE5mGND6zqYchCRAHfrYMYRDF4YL0X4D5nGdxvppAy6nkEmtWmMnwO3i0TAu +csrbE485HvGM4r0VpgVdJpvgQjiTJCTIq+D35hwtT8QDIv+nGvpcyi5wcIfCkzyC +imJukhYy6KoqNMKQEdpNiSOvWyDMTMt1bwCvEzpw91u+msUt4rj0efnO9s0ZOwdw +MRDnH4xgUl5ZLwrrPkfC1/0CAwEAAaNmMGQwHQYDVR0OBBYEFNLu71oijTptXCOX +PfKF1SbxJXuSMB8GA1UdIwQYMBaAFNLu71oijTptXCOXPfKF1SbxJXuSMBIGA1Ud +EwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBDQUAA4IB +AQC3IW/sgB9pZ8apJNjxuGoX+FkILks0wMNrdXL/coUvsrhzsvl6mePMrbGJByJ1 +XnquB5sgcRENFxdQFma3mio8Upf1owM1ZreXrJ0mADG2BplqbJnxiyYa+R11reIF +TWeIhMNcZKsDZrFAyPuFjCWSQvJmNWe9mFRYFgNhXJKkXIb5H1XgEDlwiedYRM7V +olBNlld6pRFKlX8ust6OTMOeADl2xNF0m1LThSdeuXvDyC1g9+ILfz3S6OIYgc3i +roRcFD354g7rKfu67qFAw9gC4yi0xBTPrY95rh4/HqaUYCA/L8ldRk6H7Xk35D+W +Vpmq2Sh/xT5HiFuhf4wJb0bK +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_ATTESTATION_INTERMEDIATE_A_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIUUcmMXzRIFOgGTK0Tb3gEuZYZkBIwDQYJKoZIhvcNAQEL +BQAwJDEiMCAGA1UEAwwZWXViaWNvIEF0dGVzdGF0aW9uIFJvb3QgMTAgFw0yNDEy +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowLjEsMCoGA1UEAwwjWXViaWNvIEF0 +dGVzdGF0aW9uIEludGVybWVkaWF0ZSBBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDm555bWY9WW+tOY0rIWHldh+aNanoCZCFh7Gk3YZrQmPUw0hkS +G6qYHQtP+fZyS33VErvg+BQqnmumgNhfxFrkwEZELeidBcC8C4Ag4nqqiPWpzsvI +17NcxYlInLNLFcZY/+gOiN6ZOTihO5/vBZMbj9riaAcqliYmNGJPgTcMGaEAyMzE +MNy2nm6Ep+pjP5aF6gi21t/UQFsuJ1j2Rj/ynM/SdRt+ecal5OYotxHkFbL9vvv2 +A2Ov5ITZClw4bOS9npypQimOZ5QAYytmYaQpWl/pMYz6zSj8RqkVDNEJGqNfTKA2 +ivLYwX6lSttMPapg0J84l9X0voVN/FpS4VCVAgMBAAGjZjBkMB0GA1UdDgQWBBQg +KFAhG6RaW+hTy52dxeT8bC96HzAfBgNVHSMEGDAWgBTS7u9aIo06bVwjlz3yhdUm +8SV7kjASBgNVHRMBAf8ECDAGAQH/AgECMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAQEAYMzgLrJLIr0OovQnAZrRIGuabiHSUKSmbLRWpRkWeAtsChDE +HpXcJ/bgDNKYWoHqQ8xRUjB4CyepYevc3YlrG8o7zHxpfVcaoL5SeuJkzHxKn4bT +aSp9+Mvwamnp64kZMiNbFLknfP9kYKoRHkMWheRJ1UsP1z4ScmkCeILfsMs6vqov +qjWClFsJpBcsluYHWF7bBJ1n4Rwg+ATEopY4IgGv6Zvwc+A9r+AT2hqpoSkYoAl+ +ANYwgslOf9sJe0V+TA9YY/UlaBmPPTd0//r9wvcePWZkPjKoAC/zUNhfDbh4LV8G +Hs3lyX2XomL/LNc8JYzyIaDEhGQveoPhh/tr1g== +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_ATTESTATION_INTERMEDIATE_B_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSDCCAjCgAwIBAgIUDqERw+4RnGSggxgUewJFEPDRZ3YwDQYJKoZIhvcNAQEL +BQAwJDEiMCAGA1UEAwwZWXViaWNvIEF0dGVzdGF0aW9uIFJvb3QgMTAgFw0yNDEy +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowLjEsMCoGA1UEAwwjWXViaWNvIEF0 +dGVzdGF0aW9uIEludGVybWVkaWF0ZSBCIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDI7XnH+ZvDwMCQU8M8ZeV5qscublvVYaaRt3Ybaxn9godLx5sw +H0lXrdgjh5h7FpVgCgYYX7E4bl1vbzULemrMWT8N3WMGUe8QAJbBeioV7W/E+hTZ +P/0SKJVa3ewKBo6ULeMnfQZDrVORAk8wTLq2v5Llj5vMj7JtOotKa9J7nHS8kLmz +XXSaj0SwEPh5OAZUTNV4zs1bvoTAQQWrL4/J9QuKt6WCFE5nUNiRQcEbVF8mlqK2 +bx2z6okVltyDVLCxYbpUTELvY1usR3DTGPUoIClOm4crpwnDRLVHvjYePGBB//pE +yzxA/gcScxjwaH1ZUw9bnSbHyurKqbTa1KvjAgMBAAGjZjBkMB0GA1UdDgQWBBTq +t0KQngx7ZHrbVHwDunxOn9ihYTAfBgNVHSMEGDAWgBTS7u9aIo06bVwjlz3yhdUm +8SV7kjASBgNVHRMBAf8ECDAGAQH/AgECMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQsFAAOCAQEAqQaCWMxTGqVVX7Sk7kkJmUueTSYKuU6+KBBSgwIRnlw9K7He +1IpxZ0hdwpPNikKjmcyFgFPzhImwHJgxxuT90Pw3vYOdcJJNktDg35PXOfzSn15c +FAx1RO0mPTmIb8dXiEWOpzoXvdwXDM41ZaCDYMT7w4IQtMyvE7xUBZq2bjtAnq/N +DUA7be4H8H3ipC+/+NKlUrcUh+j48K67WI0u1m6FeQueBA7n06j825rqDqsaLs9T +b7KAHAw8PmrWaNPG2kjKerxPEfecivlFawp2RWZvxrVtn3TV2SBxyCJCkXsND05d +CErVHSJIs+BdtTVNY9AwtyPmnyb0v4mSTzvWdw== +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_PIV_ATTESTATION_A_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSTCCAjGgAwIBAgIUSiefkiKiicP9B63XwO7fKqevCkQwDQYJKoZIhvcNAQEL +BQAwLjEsMCoGA1UEAwwjWXViaWNvIEF0dGVzdGF0aW9uIEludGVybWVkaWF0ZSBB +IDEwIBcNMjQxMjAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMCUxIzAhBgNVBAMM +Gll1YmljbyBQSVYgQXR0ZXN0YXRpb24gQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAyGCyrZjNrdPfChdDe4JWd+4TMLr8nbugcKJz12egglWi7oy5 +L9GT99/if9i1OrONdpEt0YrCa+qMb+dJJ0WUa8M5zXYnUDpn72vhFjH+Anb9P9+v ++ZrRqaj/jnR/MYP7NpVpeLHiH2dRCe/PX/NH1XE41GvdUEncDtqUUGaXUea0DfDY +McRDpPT2Qn5e8rn9FjzDA37SbOVuws5VlFTDzDdqR0FnqeWeIW0DFu17rzCqXcaB +VRDnQLTc5EEPDTpiRrQE/Ag+7Wg9ieLrueos75YMQ1EIkfjL49OBVogU1A7kwRGv +OnG8l7sYaY8LZ2b5FROe2hKqmsIy600qjn6b/QIDAQABo2YwZDAdBgNVHQ4EFgQU +hAuLXXtpQVBkcsbqyFlj6LVAadgwHwYDVR0jBBgwFoAUIChQIRukWlvoU8udncXk +/Gwveh8wEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAFxL/2oFjxkLh2KVnFKdhy7Nf7MmEfYXDDFSx1rFDn445jHO +UP5kxQPbZc9r53jdvL5W0SQBqBjqA95PYh0r1CPMFsFJdiFXli8Hf3NQ0bTkeFSN +G3LsQCOKMb+o2WjYU3vHkRVjKgKGLxysxxKxGfMUcXdJ0qM6ZVeRHehC2zy7XuI6 +TQn7/V0ZHXjk7So7dUV55xQde094/3cCTnh9Q3j2aqMjkGx6tDboCsz/+W+tne7W +nMHG92ZiAAmOkP2bABjan461Qty/qBXPHomkfjqNbjUTluPXiMLYKCXHIyKwdkX6 +cphouSMU3QOTsb35Y2PeWNk54xu+Eds/3nhRMso= +-----END CERTIFICATE-----"; + +/// From https://developers.yubico.com/PKI/yubico-intermediate.pem +const YUBICO_PIV_ATTESTATION_B_1: &str = "-----BEGIN CERTIFICATE----- +MIIDSTCCAjGgAwIBAgIUWVf2oJG+t1qP8t8TicWgJ2KYan4wDQYJKoZIhvcNAQEL +BQAwLjEsMCoGA1UEAwwjWXViaWNvIEF0dGVzdGF0aW9uIEludGVybWVkaWF0ZSBC +IDEwIBcNMjQxMjAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMCUxIzAhBgNVBAMM +Gll1YmljbyBQSVYgQXR0ZXN0YXRpb24gQiAxMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAv7WBL9/5AKxSpCMoL63183WqRtFrOHY7tdyuGtoidoYWQrxV +aV9S+ZwH0aynh0IzD5A/PvCtuxdtL5w2cAI3tgsborOlEert4IZ904CZQfq3ooar +1an/wssbtMpPOQkC3MQiqrUyHlFS2BTbuwbBXY66lSVX/tGRuUgnBdfBJtcQKS6M +O4bU5ndPQqhGPyzcyY1LvlfzK7KJ1r/bixCRFqjhJRnPs0Czpg6rkRrFgC6cd5bK +1UgTsJy+3wrIqkv4CeV3EhSVnhnQjZgIrdIcI5WZ8T1Oq3OhMlWmY0K0dy/oZdP/ +bpbG2qbyHLa6gprLT/qChQWLmffxn6D2DAB1zQIDAQABo2YwZDAdBgNVHQ4EFgQU +M0Nt3QHo7eGzaKMZn2SmXT74vpcwHwYDVR0jBBgwFoAU6rdCkJ4Me2R621R8A7p8 +Tp/YoWEwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI +hvcNAQELBQADggEBAI0HwoS84fKMUyIof1LdUXvyeAMmEwW7+nVETvxNNlTMuwv7 +zPJ4XZAm9Fv95tz9CqZBj6l1PAPQn6Zht9LQA92OF7W7buuXuxuusBTgLM0C1iX2 +CGXqY/k/uSNvi3ZYfrpd44TIrfrr8bCG9ux7B5ZCRqb8adDUm92Yz3lK1aX2M6Cw +jC9IZVTXQWhLyP8Ys3p7rb20CO2jJzV94deJ/+AsEb+bnCQImPat1GDKwrBosar+ +BxtU7k6kgkxZ0G384O59GFXqnwkbw2b5HhORvOsX7nhOUhePFufzi1vT1g8Tzbwr ++TUfTwo2biKHHcI762KGtp8o6Bcv5y8WgExFuWY= +-----END CERTIFICATE-----"; + /// Represents the collection of data that has been validated from /// the client leaf certificate, all the way up to the root CA. #[derive(Debug)] @@ -104,6 +215,80 @@ fn extract_certificate_extension_data( }) } +/// Verify that the intermediates are chained to the root CA. +fn verify_intermediates( + parsed_intermediate: &X509Certificate<'_>, + ca_pems: Vec<&str>, +) -> Result<(), Error> { + // There has to be at least the root CA + if ca_pems.is_empty() { + return Err(Error::InvalidSignature); + } + + let mut ca_parsed_pems = vec![]; + + // Parse all the pems + for pem in ca_pems { + let (_, parsed_pem) = parse_x509_pem(pem.as_bytes()).map_err(|_| Error::ParsingError)?; + ca_parsed_pems.push(parsed_pem); + } + + // Parse the root CA + let root_ca = ca_parsed_pems.first().ok_or(Error::ParsingError)?; + let mut parent_ca = Pem::parse_x509(&root_ca).map_err(|_| Error::ParsingError)?; + + // Iteratively verify the chain + for intermediate_ca in ca_parsed_pems.iter().skip(1) { + let intermediate_ca = Pem::parse_x509(&intermediate_ca).map_err(|_| Error::ParsingError)?; + + // Check the parent CA has signed this intermediate, return error if not + intermediate_ca + .verify_signature(Some(&parent_ca.tbs_certificate.public_key())) + .map_err(|_| Error::InvalidSignature)?; + + parent_ca = intermediate_ca; + } + + // Check the parent intermediate CA has signed the final intermediate, return error if not + parsed_intermediate + .verify_signature(Some(&parent_ca.tbs_certificate.public_key())) + .map_err(|_| Error::InvalidSignature)?; + + Ok(()) +} + +/// Verify a provided Yubikey attestation certification and intermediate +/// certificate are valid against the Yubico Attestation Root CA. +fn verify_yubico_intermediates(parsed_intermediate: &X509Certificate<'_>) -> Result<(), Error> { + if verify_intermediates( + &parsed_intermediate, + vec![ + YUBICO_ATTESTATION_ROOT_1, + YUBICO_ATTESTATION_INTERMEDIATE_A_1, + YUBICO_PIV_ATTESTATION_A_1, + ], + ) + .is_ok() + { + return Ok(()); + } + + if verify_intermediates( + &parsed_intermediate, + vec![ + YUBICO_ATTESTATION_ROOT_1, + YUBICO_ATTESTATION_INTERMEDIATE_B_1, + YUBICO_PIV_ATTESTATION_B_1, + ], + ) + .is_ok() + { + return Ok(()); + } + + verify_intermediates(&parsed_intermediate, vec![YUBICO_PIV_ROOT_CA_263751]) +} + /// Verify a provided yubikey attestation certification and intermediate /// certificate are valid against the Yubico attestation Root CA. pub fn verify_certificate_chain( @@ -111,32 +296,34 @@ pub fn verify_certificate_chain( intermediate: &[u8], root_pem: Option<&str>, ) -> Result { - let root_ca_pem = root_pem.unwrap_or(YUBICO_PIV_ROOT_CA); + let (_, parsed_intermediate) = X509Certificate::from_der(intermediate) + .map_err(|_| Error::ParsingError)?; - // Parse the root ca - let (_, root_ca) = parse_x509_pem(root_ca_pem.as_bytes()).unwrap(); - let root_ca = Pem::parse_x509(&root_ca).unwrap(); + // If a custom root CA is provided, we use that for verification. + // If not, we will try all the known Yubico Root CAs for backward compatibility + if let Some(pem) = root_pem { + verify_intermediates(&parsed_intermediate, vec![pem])?; + } else { + verify_yubico_intermediates(&parsed_intermediate)?; + } - // Parse the certificates - let (_, parsed_intermediate) = - parse_x509_certificate(intermediate).map_err(|_| Error::ParsingError)?; + // Parse the client cert let (_, parsed_client) = parse_x509_certificate(client).map_err(|_| Error::ParsingError)?; - // Validate that the provided intermediate certificate is signed by the Yubico Attestation Root CA - parsed_intermediate - .verify_signature(Some(&root_ca.tbs_certificate.subject_pki)) - .map_err(|_| Error::InvalidSignature)?; - - // Validate that the provided certificate is signed by the intermediate CA + // Validate that the provided client certificate is signed by the intermediate CA parsed_client .verify_signature(Some(&parsed_intermediate.tbs_certificate.subject_pki)) .map_err(|_| Error::InvalidSignature)?; + println!("Extract public key from client cert"); + // Extract the certificate public key and convert to an sshcerts PublicKey let public_key = match extract_ssh_pubkey_from_x509_certificate(client) { Ok(ssh) => ssh, Err(_) => return Err(Error::ParsingError), }; + println!("Extract extensions from client cert"); + extract_certificate_extension_data(public_key, &parsed_client) } diff --git a/tests/fido-lite.rs b/tests/fido-lite.rs index c1ab872..17b5581 100644 --- a/tests/fido-lite.rs +++ b/tests/fido-lite.rs @@ -3,6 +3,68 @@ use sshcerts::fido::*; use ring::digest; +const YUBIKEY_5C_NFC_5_7_4_AUTH_DATA_ED25519: [u8; 225] = [ + 24, 215, 9, 61, 183, 202, 121, 173, 239, 111, 95, 74, 89, 136, 195, 47, 121, 236, 155, 71, 59, + 195, 142, 93, 31, 100, 49, 19, 43, 73, 121, 223, 65, 0, 0, 0, 2, 215, 120, 30, 93, 227, 83, 70, + 170, 175, 226, 60, 164, 159, 19, 51, 42, 0, 128, 98, 98, 229, 4, 183, 103, 139, 128, 52, 133, + 73, 67, 19, 182, 97, 7, 69, 202, 137, 239, 194, 92, 4, 4, 141, 211, 236, 86, 50, 174, 36, 25, + 121, 3, 188, 192, 34, 181, 231, 164, 247, 91, 236, 16, 48, 104, 25, 60, 88, 199, 131, 196, 98, + 193, 93, 133, 128, 173, 125, 249, 164, 163, 48, 61, 66, 63, 2, 30, 36, 176, 179, 86, 214, 43, + 196, 0, 17, 77, 172, 8, 144, 50, 203, 164, 224, 25, 33, 247, 107, 194, 50, 80, 86, 230, 12, + 200, 220, 208, 44, 31, 128, 145, 203, 205, 110, 249, 52, 73, 133, 207, 37, 198, 9, 146, 183, + 31, 150, 63, 228, 237, 64, 83, 253, 178, 185, 180, 182, 191, 164, 1, 1, 3, 39, 32, 6, 33, 88, + 32, 49, 157, 86, 25, 233, 182, 125, 243, 238, 178, 127, 166, 95, 30, 135, 219, 202, 200, 10, + 252, 115, 193, 102, 75, 210, 246, 2, 7, 27, 57, 51, 49, +]; + +const YUBIKEY_5C_NFC_5_7_4_AUTH_SIG_ED25519: [u8; 72] = [ + 48, 70, 2, 33, 0, 253, 18, 60, 143, 204, 99, 61, 141, 84, 31, 48, 3, 97, 146, 206, 116, 205, + 23, 226, 251, 141, 172, 254, 200, 201, 200, 94, 174, 196, 232, 191, 156, 2, 33, 0, 236, 235, + 250, 190, 18, 41, 99, 204, 96, 21, 146, 184, 56, 77, 175, 88, 187, 93, 203, 175, 83, 3, 150, + 40, 245, 82, 192, 227, 128, 241, 184, 148, +]; + +const YUBIKEY_5C_NFC_5_7_4_CHALLENGE_ED25519: [u8; 32] = [ + 218, 43, 84, 240, 214, 90, 54, 239, 73, 96, 123, 70, 114, 116, 188, 66, 101, 174, 210, 190, + 106, 26, 124, 2, 48, 14, 158, 215, 185, 48, 59, 252, +]; + +const YUBIKEY_5C_NFC_5_7_4_INTERMEDIATE: [u8; 731] = [ + 48, 130, 2, 215, 48, 130, 1, 191, 160, 3, 2, 1, 2, 2, 9, 0, 181, 31, 70, 127, 92, 146, 129, 57, + 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 48, 38, 49, 36, 48, 34, 6, 3, 85, 4, + 3, 12, 27, 89, 117, 98, 105, 99, 111, 32, 70, 73, 68, 79, 32, 65, 116, 116, 101, 115, 116, 97, + 116, 105, 111, 110, 32, 66, 32, 49, 48, 32, 23, 13, 50, 52, 49, 50, 48, 49, 48, 48, 48, 48, 48, + 48, 90, 24, 15, 57, 57, 57, 57, 49, 50, 51, 49, 50, 51, 53, 57, 53, 57, 90, 48, 117, 49, 11, + 48, 9, 6, 3, 85, 4, 6, 19, 2, 83, 69, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 89, 117, 98, 105, + 99, 111, 32, 65, 66, 49, 34, 48, 32, 6, 3, 85, 4, 11, 12, 25, 65, 117, 116, 104, 101, 110, 116, + 105, 99, 97, 116, 111, 114, 32, 65, 116, 116, 101, 115, 116, 97, 116, 105, 111, 110, 49, 46, + 48, 44, 6, 3, 85, 4, 3, 12, 37, 89, 117, 98, 105, 99, 111, 32, 70, 73, 68, 79, 32, 69, 69, 32, + 83, 101, 114, 105, 97, 108, 32, 49, 52, 48, 54, 57, 54, 54, 55, 51, 57, 56, 52, 52, 51, 55, 48, + 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, + 91, 49, 68, 249, 200, 24, 0, 95, 15, 104, 216, 30, 216, 204, 75, 50, 21, 139, 193, 38, 21, 237, + 235, 86, 19, 36, 46, 30, 181, 227, 135, 211, 247, 22, 185, 45, 243, 59, 182, 172, 188, 233, 93, + 231, 105, 63, 3, 28, 86, 63, 243, 7, 76, 222, 73, 33, 38, 127, 190, 193, 110, 86, 144, 233, + 163, 129, 129, 48, 127, 48, 19, 6, 10, 43, 6, 1, 4, 1, 130, 196, 10, 13, 1, 4, 5, 4, 3, 5, 7, + 4, 48, 34, 6, 9, 43, 6, 1, 4, 1, 130, 196, 10, 2, 4, 21, 49, 46, 51, 46, 54, 46, 49, 46, 52, + 46, 49, 46, 52, 49, 52, 56, 50, 46, 49, 46, 55, 48, 19, 6, 11, 43, 6, 1, 4, 1, 130, 229, 28, 2, + 1, 1, 4, 4, 3, 2, 4, 48, 48, 33, 6, 11, 43, 6, 1, 4, 1, 130, 229, 28, 1, 1, 4, 4, 18, 4, 16, + 215, 120, 30, 93, 227, 83, 70, 170, 175, 226, 60, 164, 159, 19, 51, 42, 48, 12, 6, 3, 85, 29, + 19, 1, 1, 255, 4, 2, 48, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, + 1, 0, 13, 245, 58, 88, 199, 17, 70, 58, 18, 10, 7, 187, 196, 155, 3, 4, 45, 60, 249, 165, 0, + 45, 239, 11, 113, 34, 188, 225, 108, 59, 148, 177, 83, 88, 150, 48, 121, 196, 75, 16, 236, 30, + 27, 157, 180, 233, 12, 81, 170, 142, 158, 124, 57, 170, 128, 34, 10, 47, 240, 224, 137, 243, + 136, 130, 204, 165, 49, 201, 126, 234, 8, 79, 248, 135, 193, 194, 73, 178, 174, 117, 73, 125, + 115, 150, 190, 245, 44, 220, 1, 200, 161, 25, 239, 113, 82, 16, 85, 66, 175, 135, 32, 146, 7, + 208, 215, 36, 37, 14, 88, 33, 212, 16, 190, 165, 244, 0, 106, 208, 233, 221, 254, 15, 55, 102, + 67, 4, 190, 94, 183, 96, 179, 29, 19, 246, 201, 223, 82, 107, 184, 246, 3, 192, 130, 210, 174, + 121, 234, 91, 244, 191, 236, 141, 92, 172, 193, 254, 182, 221, 154, 108, 60, 59, 8, 255, 100, + 197, 23, 235, 201, 71, 88, 213, 76, 83, 232, 102, 121, 154, 179, 180, 235, 206, 121, 148, 215, + 188, 111, 223, 59, 197, 14, 41, 197, 22, 198, 180, 96, 162, 182, 244, 157, 161, 107, 15, 25, + 76, 145, 171, 139, 181, 64, 6, 48, 172, 109, 5, 230, 148, 193, 22, 180, 246, 149, 169, 180, + 209, 139, 95, 48, 194, 134, 37, 113, 163, 66, 221, 228, 108, 43, 19, 148, 246, 210, 38, 223, + 224, 246, 208, 68, 255, 11, 24, 82, 74, 195, 81, +]; + const YUBIKEY_BIO_AUTH_DATA_ED25519: [u8; 225] = [ 159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8, 69, 0, 0, 0, 4, 216, 82, 45, 159, 87, 91, @@ -127,6 +189,7 @@ const YUBIKEY_5C_NFC_INTERMEDIATE: [u8; 705] = [ ]; #[test] +/// This uses the old YUBICO_U2F_ROOT_CA_457200631 fn verify_and_parse_auth_data_yubikey_bio() { let hash_challenge = digest::digest(&digest::SHA256, &YUBIKEY_BIO_CHALLENGE_ED25519); let verified_data = verification::verify_auth_data( @@ -159,6 +222,7 @@ fn verify_and_parse_auth_data_yubikey_bio() { } #[test] +/// This uses the old YUBICO_U2F_ROOT_CA_457200631 fn verify_and_parse_auth_data_yubikey_5c_nfc() { let hash_challenge = digest::digest(&digest::SHA256, &YUBIKEY_5C_NFC_CHALLENGE_ED25519); let verified_data = verification::verify_auth_data( @@ -191,3 +255,39 @@ fn verify_and_parse_auth_data_yubikey_5c_nfc() { hex::decode("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08").unwrap() ); } + +#[test] +/// YUBICO_ATTESTATION_ROOT_1 was rolled out for Yubikey 5c 5.7.4 so this will test the new +/// FIDO root CA +fn verify_and_parse_auth_data_yubikey_5c_nfc_5_7_4() { + let hash_challenge = digest::digest(&digest::SHA256, &YUBIKEY_5C_NFC_5_7_4_CHALLENGE_ED25519); + let verified_data = verification::verify_auth_data( + &YUBIKEY_5C_NFC_5_7_4_AUTH_DATA_ED25519, + &YUBIKEY_5C_NFC_5_7_4_AUTH_SIG_ED25519, + &hash_challenge.as_ref(), + -7, + &YUBIKEY_5C_NFC_5_7_4_INTERMEDIATE, + None, + ) + .unwrap(); + + // Verify data pulled from the intermediate certificate + assert_eq!( + verified_data.transports, + Some(vec![Transport::USB, Transport::NFC]) + ); + assert_eq!( + verified_data.aaguid, + Some(hex::decode("d7781e5de35346aaafe23ca49f13332a").unwrap()) + ); + + // Verify data pulled from the auth data + assert_eq!( + verified_data.auth_data.aaguid, + verified_data.aaguid.unwrap() + ); + assert_eq!( + verified_data.auth_data.rpid_hash, + hex::decode("18d7093db7ca79adef6f5f4a5988c32f79ec9b473bc38e5d1f6431132b4979df").unwrap() + ); +} diff --git a/tests/yubikey-lite.rs b/tests/yubikey-lite.rs index da40ce1..3756b4a 100644 --- a/tests/yubikey-lite.rs +++ b/tests/yubikey-lite.rs @@ -93,6 +93,100 @@ const YUBIKEY_5CI_INTERMEDIATE: [u8; 746] = [ 0xaf, 0x34, 0x22, 0xad, 0x63, 0x3e, 0xf7, 0x66, 0xe6, 0x0c, ]; +const YUBIKEY_5C_5_7_4_ATTESTATION: [u8; 631] = [ + 0x30, 0x82, 0x02, 0x73, 0x30, 0x82, 0x01, 0x5b, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x01, + 0xae, 0x6a, 0x32, 0xa8, 0x8e, 0x74, 0x5d, 0xba, 0x5d, 0xf3, 0x0c, 0x0e, 0xeb, 0x16, 0xfd, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x22, + 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x59, 0x75, 0x62, 0x69, 0x4b, + 0x65, 0x79, 0x20, 0x50, 0x49, 0x56, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, + 0x39, 0x35, 0x39, 0x5a, 0x30, 0x25, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x1a, 0x59, 0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x49, 0x56, 0x20, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x38, 0x32, 0x30, 0x76, 0x30, 0x10, 0x06, + 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, + 0x62, 0x00, 0x04, 0xd7, 0xb4, 0x2c, 0x12, 0x0e, 0xb2, 0x97, 0xb3, 0xf3, 0x2d, 0xe9, 0x7c, 0xb3, + 0x76, 0xe9, 0x77, 0xfd, 0x2e, 0x0c, 0x47, 0x97, 0xba, 0x28, 0xe7, 0xe8, 0x0e, 0x36, 0xae, 0x15, + 0xb1, 0x18, 0x0a, 0x09, 0xfc, 0xcd, 0x64, 0xb5, 0x1f, 0xa1, 0xb8, 0x00, 0x13, 0x69, 0x4c, 0x47, + 0x5c, 0x0f, 0xae, 0x55, 0xb1, 0x28, 0x65, 0x7c, 0xb4, 0xf7, 0x15, 0x63, 0x6a, 0xc1, 0x5e, 0x33, + 0x5b, 0x96, 0xd8, 0x0b, 0x5b, 0x53, 0x57, 0x1d, 0xb5, 0xf3, 0x26, 0xeb, 0xc9, 0x79, 0x07, 0x64, + 0xc4, 0xb2, 0x63, 0x6d, 0xb1, 0x7c, 0x21, 0x5c, 0x02, 0x86, 0x61, 0xf8, 0x3d, 0x60, 0xa4, 0x39, + 0x47, 0x3b, 0xe2, 0xa3, 0x4e, 0x30, 0x4c, 0x30, 0x11, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0xc4, 0x0a, 0x03, 0x03, 0x04, 0x03, 0x05, 0x07, 0x04, 0x30, 0x14, 0x06, 0x0a, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a, 0x03, 0x07, 0x04, 0x06, 0x02, 0x04, 0x01, 0xfa, 0x05, 0x3a, + 0x30, 0x10, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a, 0x03, 0x08, 0x04, 0x02, + 0x01, 0x02, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a, 0x03, 0x09, + 0x04, 0x01, 0x03, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x5e, 0xb9, 0x1c, 0x19, 0x7b, 0x1a, 0x50, 0x7f, 0xed, + 0xc5, 0xaf, 0x2e, 0xa5, 0x72, 0xbf, 0x17, 0x9d, 0x0c, 0x5f, 0x1b, 0xa3, 0x7f, 0x11, 0x59, 0xea, + 0xe5, 0xb2, 0x03, 0xcd, 0x84, 0xa2, 0x69, 0x5f, 0x51, 0x43, 0xb7, 0x78, 0x42, 0xc7, 0xf9, 0x51, + 0x95, 0x4c, 0x86, 0x83, 0x8c, 0x73, 0xca, 0x79, 0x5c, 0x06, 0xfa, 0xa9, 0x38, 0xd0, 0xc3, 0xe6, + 0x4e, 0x94, 0x4f, 0x26, 0x1a, 0x8f, 0x5a, 0x17, 0x58, 0x0c, 0xf7, 0x54, 0x71, 0x00, 0x8c, 0x20, + 0x1b, 0x62, 0xd9, 0xb0, 0x4e, 0xd0, 0xe5, 0x07, 0x08, 0x68, 0x4c, 0x11, 0x83, 0xc5, 0x0e, 0x69, + 0x99, 0xbc, 0x78, 0xaf, 0xd5, 0xbe, 0xe8, 0x2c, 0x3b, 0xe5, 0xdd, 0xf6, 0x1d, 0x97, 0x1c, 0x81, + 0x93, 0x29, 0x20, 0x5d, 0xe7, 0x8a, 0xfd, 0x42, 0x7d, 0xe8, 0x72, 0x59, 0xcc, 0x33, 0xb9, 0x80, + 0x68, 0xc1, 0xa1, 0xa7, 0x6a, 0xc5, 0xa7, 0x54, 0xa3, 0xbe, 0xe8, 0x0f, 0xbd, 0xb3, 0x6e, 0x05, + 0x72, 0xe9, 0x2f, 0x40, 0xd5, 0x61, 0xd7, 0x5d, 0x85, 0xf1, 0xab, 0xa9, 0xd6, 0xf5, 0x27, 0xe2, + 0xd2, 0x22, 0x19, 0xd0, 0x1a, 0x21, 0xff, 0xbf, 0x53, 0xc5, 0xb5, 0x5d, 0x2d, 0xcb, 0xbf, 0x2d, + 0xf7, 0xd7, 0xe6, 0x77, 0xad, 0x90, 0xb9, 0xd7, 0x96, 0x95, 0x68, 0xc2, 0x3c, 0xf8, 0x73, 0xc1, + 0xc6, 0xd0, 0x7f, 0xb9, 0xf3, 0xb1, 0x43, 0x48, 0xa9, 0x62, 0x24, 0x5e, 0x91, 0x2b, 0x98, 0x6a, + 0xa3, 0xc9, 0x29, 0x5d, 0x0a, 0xc1, 0x03, 0x0c, 0xeb, 0xca, 0x61, 0x15, 0x0c, 0x00, 0xc8, 0x49, + 0x78, 0xbf, 0xb4, 0xc5, 0x19, 0xe2, 0x27, 0xf4, 0xe6, 0x14, 0xb6, 0xf4, 0x64, 0x32, 0xcf, 0x31, + 0xe0, 0x21, 0x22, 0x8c, 0xba, 0x96, 0xc5, 0xf8, 0x78, 0x84, 0x93, 0xd7, 0x01, 0x2b, 0xc4, 0xa0, + 0xb2, 0xe6, 0xb1, 0x40, 0x3e, 0xad, 0xfd, +]; + +const YUBIKEY_5C_5_7_4_INTERMEDIATE: [u8; 761] = [ + 0x30, 0x82, 0x02, 0xf5, 0x30, 0x82, 0x01, 0xdd, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0x95, 0xdc, 0x17, 0x99, 0x3b, 0x2d, 0xb7, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x25, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x1a, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x50, 0x49, 0x56, 0x20, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x42, 0x20, 0x31, 0x30, 0x20, + 0x17, 0x0d, 0x32, 0x34, 0x31, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, + 0x0f, 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, + 0x30, 0x22, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x59, 0x75, 0x62, + 0x69, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x49, 0x56, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xb7, 0xc6, 0xb0, 0x99, 0x54, 0x26, 0x54, 0x85, 0x5b, 0xd5, 0x35, + 0x2e, 0x23, 0xc9, 0x7b, 0x62, 0x74, 0x1c, 0x4c, 0xb4, 0xc1, 0xa7, 0x31, 0x6b, 0x9d, 0xdc, 0xd1, + 0x41, 0x1e, 0xc6, 0xbf, 0xd9, 0xb6, 0x09, 0xab, 0x21, 0xff, 0xc2, 0x56, 0x1f, 0x24, 0xa8, 0x4e, + 0x2a, 0xa4, 0xcb, 0x39, 0x8a, 0x07, 0xb2, 0xab, 0x1a, 0x99, 0xa8, 0xb8, 0xdd, 0x36, 0xa6, 0x59, + 0x99, 0xd6, 0x05, 0x7c, 0x72, 0x4b, 0xe8, 0x02, 0x92, 0xa6, 0x90, 0x9c, 0x59, 0x89, 0xda, 0x1c, + 0xde, 0xab, 0x4a, 0x3e, 0x8e, 0x80, 0x1a, 0x62, 0x76, 0x0f, 0x6e, 0x69, 0x09, 0x4d, 0xc7, 0xed, + 0x37, 0x7d, 0x79, 0xfb, 0x18, 0x91, 0xd0, 0x85, 0x66, 0x20, 0xbd, 0x35, 0xfd, 0xa9, 0xcc, 0xe6, + 0x7e, 0x4a, 0xa1, 0xda, 0xb8, 0x78, 0x6b, 0x5e, 0x01, 0x83, 0x68, 0xa9, 0x80, 0x17, 0x8f, 0xc3, + 0x0b, 0xcf, 0x52, 0x27, 0x47, 0x49, 0xda, 0xb2, 0xb5, 0xf3, 0x20, 0xab, 0xe1, 0xf5, 0x63, 0x36, + 0xe8, 0xb9, 0xa9, 0x97, 0xc8, 0x77, 0x5c, 0xa5, 0x6f, 0xce, 0x3c, 0xf3, 0x9e, 0xa1, 0x3c, 0x2f, + 0xf9, 0x7e, 0x1f, 0xaf, 0x76, 0xe5, 0x76, 0x9c, 0x00, 0x06, 0xa1, 0xc3, 0x4f, 0xf9, 0x8c, 0xff, + 0x39, 0xe1, 0xd7, 0xa7, 0x61, 0x42, 0xee, 0xd0, 0x94, 0xf1, 0x60, 0x9c, 0x6d, 0xeb, 0x34, 0xae, + 0x9d, 0x74, 0x22, 0x1d, 0x56, 0xe1, 0x6c, 0xf8, 0xba, 0x98, 0x95, 0x4e, 0xd4, 0xb5, 0xef, 0x9f, + 0x41, 0xb5, 0x69, 0xf0, 0x58, 0xab, 0x6a, 0xfc, 0x5c, 0xcd, 0x46, 0xf5, 0x19, 0x14, 0x4a, 0x30, + 0xd9, 0x16, 0x1a, 0xd0, 0x3a, 0x9b, 0x93, 0xfc, 0xef, 0x3c, 0x80, 0xc3, 0xcb, 0xd4, 0x6e, 0x73, + 0x25, 0xb1, 0xf9, 0x63, 0xea, 0x96, 0x87, 0x03, 0x03, 0xa9, 0xcd, 0x77, 0xc0, 0x4f, 0x8f, 0xc6, + 0x93, 0xae, 0x54, 0x86, 0xa5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x29, 0x30, 0x27, 0x30, 0x11, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a, 0x03, 0x03, 0x04, 0x03, 0x05, 0x07, + 0x04, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, + 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4b, 0x32, 0x50, 0xcd, 0xc6, 0xc5, 0x40, + 0x96, 0x48, 0x79, 0x62, 0x4d, 0xf7, 0x05, 0xf7, 0xea, 0xf9, 0x04, 0x42, 0x0b, 0x7d, 0x1f, 0xf9, + 0xd9, 0xc7, 0x69, 0x45, 0xf6, 0x89, 0x59, 0x27, 0x56, 0xfb, 0xf6, 0xc7, 0x51, 0xf7, 0xdd, 0x19, + 0x64, 0x43, 0xe8, 0xf7, 0x24, 0x63, 0x70, 0x10, 0x06, 0x98, 0x1a, 0xa7, 0x7c, 0xc1, 0x83, 0x31, + 0x39, 0xca, 0xad, 0x8a, 0xbf, 0x70, 0x35, 0x6e, 0x70, 0x05, 0xa3, 0xa0, 0xc7, 0x8c, 0xf0, 0xa7, + 0xc1, 0xaa, 0x2c, 0xf6, 0x11, 0x66, 0xfd, 0x85, 0x62, 0x77, 0x18, 0xd6, 0xbe, 0x2d, 0x1b, 0xac, + 0x7c, 0x0a, 0x1c, 0x71, 0x86, 0x41, 0xd4, 0xa6, 0x43, 0x32, 0xc0, 0x1b, 0xbf, 0xda, 0x69, 0xba, + 0x5f, 0x69, 0x57, 0xc4, 0xba, 0xb4, 0x03, 0x04, 0x15, 0x28, 0x64, 0x09, 0xc1, 0xc5, 0xd3, 0x0f, + 0xb4, 0xb6, 0x92, 0xf8, 0x71, 0xa1, 0x01, 0x56, 0xd3, 0x83, 0x28, 0x7d, 0x82, 0x56, 0x6b, 0x7e, + 0xa6, 0x62, 0xfd, 0xaa, 0x0c, 0xe7, 0xb2, 0x41, 0xe7, 0x20, 0x59, 0xf0, 0x60, 0xb3, 0x51, 0xac, + 0xcd, 0xe8, 0x27, 0x44, 0xe5, 0x39, 0xc4, 0xb3, 0xea, 0x28, 0x55, 0x05, 0xdf, 0x40, 0xd3, 0xf6, + 0xc0, 0xd9, 0x0c, 0xd8, 0x83, 0x0e, 0x13, 0xf0, 0x8d, 0x5d, 0xaf, 0xd4, 0x56, 0x9f, 0x6c, 0xdc, + 0x91, 0xfb, 0x86, 0x5a, 0x16, 0xfd, 0x53, 0x68, 0xca, 0x72, 0xb9, 0x4a, 0x53, 0x2b, 0x27, 0xe6, + 0x9c, 0xa1, 0xe8, 0x4c, 0xdd, 0xf0, 0xcc, 0xe0, 0xd8, 0x08, 0xa4, 0x4f, 0xaa, 0xf1, 0xe8, 0xbc, + 0xfa, 0xc8, 0xd4, 0x93, 0xc3, 0x44, 0x76, 0x27, 0xbe, 0xbc, 0xf2, 0x04, 0x82, 0x94, 0xbc, 0x64, + 0x41, 0x97, 0xd7, 0x4d, 0xf4, 0x92, 0xe4, 0x5f, 0xc3, 0x8e, 0xc6, 0x1b, 0x80, 0x89, 0x10, 0xdf, + 0x5f, 0x6c, 0x1d, 0xa3, 0x6a, 0xac, 0xd4, 0xfc, 0xc8, +]; + #[test] pub fn test_5c_attestation_verification() { let attested_key_data = verification::verify_certificate_chain( @@ -107,3 +201,18 @@ pub fn test_5c_attestation_verification() { assert_eq!(attested_key_data.touch_policy, 3); assert_eq!(attested_key_data.pin_policy, 1); } + +#[test] +pub fn test_5c_5_7_4_attestation_verification() { + let attested_key_data = verification::verify_certificate_chain( + &YUBIKEY_5C_5_7_4_ATTESTATION, + &YUBIKEY_5C_5_7_4_INTERMEDIATE, + None, + ) + .unwrap(); + + assert_eq!(attested_key_data.serial, 33162554); + assert_eq!(attested_key_data.firmware, "5.7.4"); + assert_eq!(attested_key_data.touch_policy, 2); + assert_eq!(attested_key_data.pin_policy, 1); +}