diff --git a/Cargo.lock b/Cargo.lock index a865bb2..7dbdf86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,14 +11,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl 0.1.0", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", +] + [[package]] name = "asn1-rs" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", + "asn1-rs-derive 0.5.0", + "asn1-rs-impl 0.2.0", "displaydoc", "nom", "num-traits", @@ -27,6 +42,18 @@ dependencies = [ "time", ] +[[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 1.0.109", + "synstructure 0.12.6", +] + [[package]] name = "asn1-rs-derive" version = "0.5.0" @@ -35,8 +62,19 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", - "syn", - "synstructure", + "syn 2.0.64", + "synstructure 0.13.1", +] + +[[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 1.0.109", ] [[package]] @@ -47,7 +85,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -87,7 +125,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.64", "which", ] @@ -222,13 +260,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", +] + [[package]] name = "der-parser" version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ - "asn1-rs", + "asn1-rs 0.6.1", "displaydoc", "nom", "num-bigint", @@ -244,7 +295,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -285,7 +336,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -591,6 +642,7 @@ dependencies = [ name = "native-pkcs11-piv" version = "0.2.18" dependencies = [ + "der-parser 8.2.0", "hex", "native-pkcs11-traits", "p256", @@ -709,7 +761,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" dependencies = [ - "asn1-rs", + "asn1-rs 0.6.1", ] [[package]] @@ -888,7 +940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.64", ] [[package]] @@ -1148,7 +1200,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -1171,7 +1223,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -1259,7 +1311,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.64", ] [[package]] @@ -1268,6 +1320,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.64" @@ -1279,6 +1342,18 @@ 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 1.0.109", + "unicode-xid", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -1287,7 +1362,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -1307,7 +1382,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -1369,7 +1444,7 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -1391,7 +1466,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] [[package]] @@ -1466,6 +1541,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[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.9.1" @@ -1686,9 +1767,9 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ - "asn1-rs", + "asn1-rs 0.6.1", "data-encoding", - "der-parser", + "der-parser 9.0.0", "lazy_static", "nom", "oid-registry", @@ -1747,5 +1828,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.64", ] diff --git a/native-pkcs11-piv/Cargo.toml b/native-pkcs11-piv/Cargo.toml index b41c329..7b21b39 100644 --- a/native-pkcs11-piv/Cargo.toml +++ b/native-pkcs11-piv/Cargo.toml @@ -31,6 +31,7 @@ yubikey = { version = "0.8.0", features = ["untested"] } sha1 = "0.10" x509-parser = "0.16.0" hex = "0.4.3" +der-parser = "8.2.0" [dev-dependencies] serial_test = { version = "3.1.1", default-features = false } diff --git a/native-pkcs11-piv/src/piv/backend.rs b/native-pkcs11-piv/src/piv/backend.rs index c065433..3cae38a 100644 --- a/native-pkcs11-piv/src/piv/backend.rs +++ b/native-pkcs11-piv/src/piv/backend.rs @@ -16,23 +16,46 @@ use std::sync::{Arc, Mutex}; use tracing::instrument; use x509_cert::der::Encode; -use yubikey::piv::AlgorithmId; +use yubikey::piv::SlotId; use yubikey::YubiKey; use crate::piv::slot::SlotObject; use crate::piv::util::get_algorithm_id_by_certificate; +use native_pkcs11_traits::once_cell::sync::Lazy; use native_pkcs11_traits::Certificate as P11Certificate; use native_pkcs11_traits::KeyAlgorithm as P11KeyAlgorithm; use native_pkcs11_traits::KeySearchOptions as P11KeySearchOptions; use native_pkcs11_traits::PrivateKey as P11PrivateKey; use native_pkcs11_traits::PublicKey as P11PublicKey; use native_pkcs11_traits::Result as P11Result; -use native_pkcs11_traits::{Backend, KeyAlgorithm, KeySearchOptions}; +use native_pkcs11_traits::{Backend, KeySearchOptions}; + +static YUBIKEY: Lazy>> = Lazy::new(Default::default); +static CACHED_PIN: Lazy>> = Lazy::new(Default::default); +static ENABLE_RETIRED: Lazy = Lazy::new(|| { + std::env::var("ENABLE_RETIRED").ok().as_deref().map(|v| { + v == "true" || v == "yes" || v == "on" || v == "1" + }).unwrap_or(false) +}); + +fn clear_pin() -> () { + let mut cached_pin = CACHED_PIN.lock().unwrap(); + if cached_pin.is_some() { + *cached_pin = None; + } +} + +fn prepare_pin() -> P11Result { + let mut cached_pin = CACHED_PIN.lock().unwrap(); + if cached_pin.is_none() { + let pin = crate::piv::pinentry::get_pin()?; + *cached_pin = Some(pin); + } + Ok(cached_pin.as_deref().unwrap().to_string()) +} #[derive(Debug, Default)] pub struct YubikeyPivBackend { - cached_pin: Mutex>, - yubikey: Mutex>, slot_objects: Mutex>>, } @@ -41,42 +64,26 @@ impl YubikeyPivBackend { YubikeyPivBackend::default() } - fn run_with_yubikey(&self, verify: bool, mut callback: F) -> P11Result<()> + pub(crate) fn run_with_yubikey(verify: bool, mut callback: F) -> P11Result where - F: FnMut(&mut YubiKey) -> P11Result<()>, + F: FnMut(&mut YubiKey) -> P11Result, { - let mut yubikey = self.yubikey.lock().unwrap(); + let mut yubikey = YUBIKEY.lock().unwrap(); if yubikey.is_none() { *yubikey = Some(YubiKey::open()?); } let mut yk = yubikey.as_mut().unwrap(); if verify { - let pin = self.prepare_pin()?; + let pin = prepare_pin()?; let verify_result = yk.verify_pin(pin.as_bytes()); if verify_result.is_err() { - self.clear_pin(); + clear_pin(); } verify_result?; } callback(&mut yk) } - fn clear_pin(&self) -> () { - let mut cached_pin = self.cached_pin.lock().unwrap(); - if cached_pin.is_some() { - *cached_pin = None; - } - } - - fn prepare_pin(&self) -> P11Result { - let mut cached_pin = self.cached_pin.lock().unwrap(); - if cached_pin.is_none() { - let pin = crate::piv::pinentry::get_pin()?; - *cached_pin = Some(pin); - } - Ok(cached_pin.as_deref().unwrap().to_string()) - } - fn init_slot_objects(&self) -> P11Result<()> { let mut slot_objects_opt = self.slot_objects.lock().unwrap(); if slot_objects_opt.is_some() { @@ -84,21 +91,22 @@ impl YubikeyPivBackend { } let mut slot_objects = vec![]; - self.run_with_yubikey(false, |yk| { + YubikeyPivBackend::run_with_yubikey(false, |yk| { let keys = yk.piv_keys()?; for key in keys { - let slot_id = key.slot().to_string(); + let slot_id = key.slot(); + if !*ENABLE_RETIRED && matches!(slot_id, SlotId::Retired(_)) { + // SKIP RETIRED + continue; + } let certificate_der = key.certificate().cert.to_der()?; let public_key_der = key.certificate().cert.tbs_certificate.subject_public_key_info.to_der()?; let algorithm_id = get_algorithm_id_by_certificate(key.certificate())?; - let algorithm = match algorithm_id { - AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => KeyAlgorithm::Rsa, - AlgorithmId::EccP256 | AlgorithmId::EccP384 => KeyAlgorithm::Ecc, - }; + // println!(">>> {} {:?} {}", &slot_id, algorithm_id, &key.certificate().cert.tbs_certificate.subject); // TODO remove let slot_object = SlotObject::new( - algorithm, + algorithm_id, slot_id, certificate_der, public_key_der, @@ -149,22 +157,14 @@ impl Backend for YubikeyPivBackend { &self, query: P11KeySearchOptions, ) -> P11Result>> { - println!("[find_private_key]"); + let query_id = get_query_id(&query); + // println!("[find_private_key] {}", query_id); self.init_slot_objects()?; let mut private_key: Option> = None; self.for_each_slot_objects(|slot_object| { if private_key.is_none() { - match &query { - KeySearchOptions::Label(label) => { - if &slot_object.label == label { - private_key = Some(Arc::from(slot_object.to_private_key()?)); - } - } - KeySearchOptions::PublicKeyHash(public_key_hash) => { - if hex::encode(&slot_object.public_key_hash) == hex::encode(public_key_hash) { - private_key = Some(Arc::from(slot_object.to_private_key()?)); - } - } + if format!("label:{}", &slot_object.slot_id) == query_id || format!("hash:{}", hex::encode(&slot_object.public_key_hash)) == query_id { + private_key = Some(Arc::from(slot_object.to_private_key()?)); } } Ok(()) @@ -177,22 +177,14 @@ impl Backend for YubikeyPivBackend { &self, query: P11KeySearchOptions, ) -> P11Result>> { - println!("[find_public_key]"); + let query_id = get_query_id(&query); + // println!("[find_public_key] {}", query_id); self.init_slot_objects()?; let mut public_key: Option> = None; self.for_each_slot_objects(|slot_object| { if public_key.is_none() { - match &query { - KeySearchOptions::Label(label) => { - if &slot_object.label == label { - public_key = Some(slot_object.to_public_key()?); - } - } - KeySearchOptions::PublicKeyHash(public_key_hash) => { - if hex::encode(&slot_object.public_key_hash) == hex::encode(public_key_hash) { - public_key = Some(slot_object.to_public_key()?); - } - } + if format!("label:{}", &slot_object.slot_id) == query_id || format!("hash:{}", hex::encode(&slot_object.public_key_hash)) == query_id { + public_key = Some(slot_object.to_public_key()?); } } Ok(()) @@ -203,7 +195,7 @@ impl Backend for YubikeyPivBackend { fn find_all_private_keys( &self, ) -> P11Result>> { - println!("[find_all_private_keys]"); + // println!("[find_all_private_keys]"); self.init_slot_objects()?; let mut private_keys: Vec> = vec![]; self.for_each_slot_objects(|slot_object| { @@ -216,7 +208,7 @@ impl Backend for YubikeyPivBackend { fn find_all_public_keys( &self, ) -> P11Result>> { - println!("[find_all_public_keys]"); + // println!("[find_all_public_keys]"); self.init_slot_objects()?; let mut public_keys: Vec> = vec![]; self.for_each_slot_objects(|slot_object| { @@ -235,3 +227,10 @@ impl Backend for YubikeyPivBackend { Err("Generate key not supported, please use ykman, URL: https://hatter.in/ykman")? } } + +fn get_query_id(query: &P11KeySearchOptions) -> String { + match query { + KeySearchOptions::Label(label) => format!("label:{}", label), + KeySearchOptions::PublicKeyHash(public_key_hash) => format!("hash:{}", hex::encode(public_key_hash)), + } +} diff --git a/native-pkcs11-piv/src/piv/certificate.rs b/native-pkcs11-piv/src/piv/certificate.rs index ff40376..d323539 100644 --- a/native-pkcs11-piv/src/piv/certificate.rs +++ b/native-pkcs11-piv/src/piv/certificate.rs @@ -38,7 +38,7 @@ impl YubikeyPivCertificate { impl Debug for YubikeyPivCertificate { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("KeychainCertificate") - .field("label", &self.slot_object.label) + .field("label", &self.slot_object.slot_id) .field("identity", &self.slot_object.public_key_hash) .finish() } @@ -46,7 +46,7 @@ impl Debug for YubikeyPivCertificate { impl P11Certificate for YubikeyPivCertificate { fn label(&self) -> String { - self.slot_object.label.clone() + self.slot_object.slot_id.to_string() } fn to_der(&self) -> Vec { diff --git a/native-pkcs11-piv/src/piv/key.rs b/native-pkcs11-piv/src/piv/key.rs index d7a97bc..955ddfd 100644 --- a/native-pkcs11-piv/src/piv/key.rs +++ b/native-pkcs11-piv/src/piv/key.rs @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt::Debug; - -use tracing::instrument; - use crate::piv::slot::SlotObject; +use crate::piv::util::parse_ecdsa_signature; +use crate::YubikeyPivBackend; use native_pkcs11_traits::Result as P11Result; use native_pkcs11_traits::{Backend, KeyAlgorithm, PrivateKey, PublicKey, SignatureAlgorithm}; +use std::fmt::Debug; +use tracing::instrument; +use yubikey::piv::sign_data; #[derive(Debug)] pub enum Algorithm { @@ -48,7 +49,7 @@ impl PrivateKey for YubikeyPivPrivateKey { #[instrument] fn label(&self) -> String { - self.slot_object.label.clone() + self.slot_object.slot_id.to_string() } #[instrument] @@ -57,13 +58,27 @@ impl PrivateKey for YubikeyPivPrivateKey { algorithm: &SignatureAlgorithm, data: &[u8], ) -> P11Result> { - println!(">> CALL: sign"); + // println!(">> CALL: sign, slot_id: {}, algorithm_id: {:?}, algorithm: {:?}, data: {}", + // self.slot_object.slot_id, + // self.slot_object.algorithm_id, + // algorithm, + // hex::encode(data) + // ); match algorithm { - SignatureAlgorithm::Ecdsa => {} - _ => return Err("RSA algorithm not supported.")?, + SignatureAlgorithm::Ecdsa => { + YubikeyPivBackend::run_with_yubikey(true, |yubikey| { + let signature = sign_data(yubikey, + data, + self.slot_object.algorithm_id, + self.slot_object.slot_id, + )?; + let (mut r, s) = parse_ecdsa_signature(&signature)?; + r.extend_from_slice(&s); + Ok(r) + }) + } + _ => Err("RSA algorithm not supported.")?, } - // TODO sign data or hash?? - Ok(vec![]) } #[instrument] @@ -80,8 +95,7 @@ impl PrivateKey for YubikeyPivPrivateKey { &self, _backend: &dyn Backend, ) -> P11Result>> { - // TODO ... - Ok(None) + Ok(Some(self.slot_object.to_public_key()?)) } } @@ -93,9 +107,7 @@ pub struct YubikeyPivPublicKey { impl YubikeyPivPublicKey { #[instrument] pub fn new(slot_object: SlotObject) -> P11Result { - Ok(YubikeyPivPublicKey { - slot_object, - }) + Ok(YubikeyPivPublicKey { slot_object }) } } @@ -107,7 +119,7 @@ impl PublicKey for YubikeyPivPublicKey { #[instrument] fn label(&self) -> String { - self.slot_object.label.clone() + self.slot_object.slot_id.to_string() } #[instrument] @@ -122,21 +134,28 @@ impl PublicKey for YubikeyPivPublicKey { data: &[u8], signature: &[u8], ) -> P11Result<()> { - println!(">> CALL: verify"); + println!(">> CALL: verify, algorithm: {:?}, data: {}, signature: {}", + algorithm, hex::encode(data), hex::encode(signature) + ); match algorithm { SignatureAlgorithm::Ecdsa => {} - _ => return Err("RSA algorithm not supported.")?, + SignatureAlgorithm::RsaRaw => {} + SignatureAlgorithm::RsaPkcs1v15Raw => {} + SignatureAlgorithm::RsaPkcs1v15Sha1 => {} + SignatureAlgorithm::RsaPkcs1v15Sha256 => {} + SignatureAlgorithm::RsaPkcs1v15Sha384 => {} + SignatureAlgorithm::RsaPkcs1v15Sha512 => {} + SignatureAlgorithm::RsaPss { + digest: _, + mask_generation_function: _, + salt_length: _, + } => {} } - // let result = self.sec_key.verify_signature(algorithm, data, signature)?; - // if !result { - // return Err("verify failed")?; - // } - // TODO ... - Ok(()) + Err(format!("Not supported algorithm: {:?}", algorithm))? } fn delete(self: Box) { - // yubikey-piv-pkcs11 just cannot delete public key + // TODO ... yubikey-piv-pkcs11 just cannot delete public key } fn algorithm(&self) -> KeyAlgorithm { diff --git a/native-pkcs11-piv/src/piv/slot.rs b/native-pkcs11-piv/src/piv/slot.rs index 5af1746..9f4d49a 100644 --- a/native-pkcs11-piv/src/piv/slot.rs +++ b/native-pkcs11-piv/src/piv/slot.rs @@ -17,10 +17,12 @@ use crate::key::{YubikeyPivPrivateKey, YubikeyPivPublicKey}; use crate::piv::util::sha1_bytes; use native_pkcs11_traits::{Certificate, PublicKey, Result as P11Result}; use native_pkcs11_traits::{KeyAlgorithm, PrivateKey}; +use yubikey::piv::{AlgorithmId, SlotId}; #[derive(Debug, Clone)] pub struct SlotObject { - pub label: String, + pub slot_id: SlotId, + pub algorithm_id: AlgorithmId, // EQUALS: sha1_bytes(&public_key_der) pub public_key_hash: Vec, pub algorithm: KeyAlgorithm, @@ -29,10 +31,15 @@ pub struct SlotObject { } impl SlotObject { - pub fn new(algorithm: KeyAlgorithm, label: String, certificate_der: Vec, public_key_der: Vec) -> Self { + pub fn new(algorithm_id: AlgorithmId, slot_id: SlotId, certificate_der: Vec, public_key_der: Vec) -> Self { + let algorithm = match algorithm_id { + AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => KeyAlgorithm::Rsa, + AlgorithmId::EccP256 | AlgorithmId::EccP384 => KeyAlgorithm::Ecc, + }; let public_key_hash = sha1_bytes(&public_key_der); SlotObject { - label, + slot_id, + algorithm_id, public_key_hash, algorithm, public_key_der, diff --git a/native-pkcs11-piv/src/piv/util.rs b/native-pkcs11-piv/src/piv/util.rs index 7f1ed27..448c457 100644 --- a/native-pkcs11-piv/src/piv/util.rs +++ b/native-pkcs11-piv/src/piv/util.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use der_parser::ber::BerObjectContent; use p256::ecdsa::signature::digest::Digest; use p256::pkcs8::ObjectIdentifier; use sha1::Sha1; @@ -19,8 +20,8 @@ use spki::der::{Decode, Encode}; use spki::SubjectPublicKeyInfoOwned; use x509_parser::prelude::FromDer; use x509_parser::public_key::RSAPublicKey; -use yubikey::Certificate; use yubikey::piv::AlgorithmId; +use yubikey::Certificate; use native_pkcs11_traits::Result as P11Result; @@ -75,3 +76,34 @@ pub fn get_algorithm_id(public_key_info: &SubjectPublicKeyInfoOwned) -> P11Resul } Err(format!("Unknown algorithm: {}", public_key_info.algorithm.oid))? } + +pub fn parse_ecdsa_signature(signature: &[u8]) -> P11Result<(Vec, Vec)> { + let (_, parsed_signature) = der_parser::parse_der(signature)?; + match parsed_signature.content { + BerObjectContent::Sequence(sequence) => { + if sequence.len() == 2 { + let r = match &sequence[0].content { + BerObjectContent::Integer(r) => r, + _ => Err("Bad ECDSA signature ([0] format]).")? + }; + let s = match &sequence[1].content { + BerObjectContent::Integer(s) => s, + _ => Err("Bad ECDSA signature ([1] format]).")? + }; + Ok((remove_leading_zero(r), remove_leading_zero(s))) + } else { + Err("Bad ECDSA signature (length).")? + } + } + _ => Err("Bad ECDSA signature (format).")?, + } +} + +fn remove_leading_zero(bytes: &[u8]) -> Vec { + if bytes[0] == 0 { + bytes[1..].to_vec() + } else { + bytes.to_vec() + } +} +