diff --git a/Cargo.lock b/Cargo.lock index 4eca4d3..a865bb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,6 +406,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hkdf" version = "0.12.4" @@ -585,6 +591,7 @@ dependencies = [ name = "native-pkcs11-piv" version = "0.2.18" dependencies = [ + "hex", "native-pkcs11-traits", "p256", "pinentry", diff --git a/native-pkcs11-piv/Cargo.toml b/native-pkcs11-piv/Cargo.toml index 74b75a9..b41c329 100644 --- a/native-pkcs11-piv/Cargo.toml +++ b/native-pkcs11-piv/Cargo.toml @@ -30,6 +30,7 @@ x509-cert = { version = "0.2.5", default-features = false } yubikey = { version = "0.8.0", features = ["untested"] } sha1 = "0.10" x509-parser = "0.16.0" +hex = "0.4.3" [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 2d3029a..c065433 100644 --- a/native-pkcs11-piv/src/piv/backend.rs +++ b/native-pkcs11-piv/src/piv/backend.rs @@ -16,25 +16,24 @@ use std::sync::{Arc, Mutex}; use tracing::instrument; use x509_cert::der::Encode; -use x509_parser::nom::Parser; use yubikey::piv::AlgorithmId; use yubikey::YubiKey; -use native_pkcs11_traits::{Backend, KeySearchOptions}; +use crate::piv::slot::SlotObject; +use crate::piv::util::get_algorithm_id_by_certificate; 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 crate::certificate::YubikeyPivCertificate; -use crate::piv::util::get_algorithm_id_by_certificate; +use native_pkcs11_traits::{Backend, KeyAlgorithm, KeySearchOptions}; #[derive(Debug, Default)] pub struct YubikeyPivBackend { cached_pin: Mutex>, yubikey: Mutex>, + slot_objects: Mutex>>, } impl YubikeyPivBackend { @@ -77,6 +76,53 @@ impl YubikeyPivBackend { } 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() { + return Ok(()); + } + + let mut slot_objects = vec![]; + self.run_with_yubikey(false, |yk| { + let keys = yk.piv_keys()?; + for key in keys { + let slot_id = key.slot().to_string(); + 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, + slot_id, + certificate_der, + public_key_der, + ); + slot_objects.push(slot_object); + } + Ok(()) + })?; + *slot_objects_opt = Some(slot_objects); + Ok(()) + } + + fn for_each_slot_objects(&self, mut callback: F) -> P11Result<()> + where + F: FnMut(&SlotObject) -> P11Result<()>, + { + let slot_objects = &self.slot_objects.lock().unwrap(); + for slot_objects in slot_objects.iter() { + for slot_object in slot_objects { + callback(slot_object)?; + } + } + Ok(()) + } } impl Backend for YubikeyPivBackend { @@ -88,25 +134,11 @@ impl Backend for YubikeyPivBackend { fn find_all_certificates( &self, ) -> P11Result>> { - println!("[find_all_certificates]"); - let mut certs = vec![]; - self.run_with_yubikey(false, |yk| { - let keys = yk.piv_keys()?; - for key in keys { - 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())?; - println!(">>> {:?} {}", algorithm_id, &key.certificate().cert.tbs_certificate.subject); - if algorithm_id == AlgorithmId::EccP256 || algorithm_id == AlgorithmId::EccP384 { - let cert: Box = Box::new(YubikeyPivCertificate::new( - key.slot().to_string(), - key.slot().to_string(), - certificate_der, - public_key_der, - )?); - certs.push(cert); - } - } + // println!("[find_all_certificates]"); + self.init_slot_objects()?; + let mut certs: Vec> = vec![]; + self.for_each_slot_objects(|slot_object| { + certs.push(slot_object.to_certificate()?); Ok(()) })?; Ok(certs) @@ -118,16 +150,26 @@ impl Backend for YubikeyPivBackend { query: P11KeySearchOptions, ) -> P11Result>> { println!("[find_private_key]"); - match query { - KeySearchOptions::Label(label) => { - println!(">>> find private key >>>: {}", label); + 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()?)); + } + } + } } - KeySearchOptions::PublicKeyHash(public_key_hash) => { - println!(">>> find private key >>>: {:?}", public_key_hash); - } - } - // TODO ... - Ok(None) + Ok(()) + })?; + Ok(private_key) } #[instrument] @@ -136,35 +178,52 @@ impl Backend for YubikeyPivBackend { query: P11KeySearchOptions, ) -> P11Result>> { println!("[find_public_key]"); - match query { - KeySearchOptions::Label(label) => { - println!(">>> find public key >>>: {}", label); + 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()?); + } + } + } } - KeySearchOptions::PublicKeyHash(public_key_hash) => { - println!(">>> find public key >>>: {:?}", public_key_hash); - } - } - // TODO ... - Ok(None) + Ok(()) + })?; + Ok(public_key) } fn find_all_private_keys( &self, ) -> P11Result>> { println!("[find_all_private_keys]"); - // TODO ... - Ok(vec![]) + self.init_slot_objects()?; + let mut private_keys: Vec> = vec![]; + self.for_each_slot_objects(|slot_object| { + private_keys.push(Arc::from(slot_object.to_private_key()?)); + Ok(()) + })?; + Ok(private_keys) } fn find_all_public_keys( &self, ) -> P11Result>> { println!("[find_all_public_keys]"); - // self.find_all_certificates().map(|c|{ - // c.as_mut().map(|c| ) - // }) - // TODO ... - Ok(vec![]) + self.init_slot_objects()?; + let mut public_keys: Vec> = vec![]; + self.for_each_slot_objects(|slot_object| { + public_keys.push(Arc::from(slot_object.to_public_key()?)); + Ok(()) + })?; + Ok(public_keys) } #[instrument] diff --git a/native-pkcs11-piv/src/piv/certificate.rs b/native-pkcs11-piv/src/piv/certificate.rs index 42854d7..ff40376 100644 --- a/native-pkcs11-piv/src/piv/certificate.rs +++ b/native-pkcs11-piv/src/piv/certificate.rs @@ -14,60 +14,43 @@ use std::fmt::Debug; -use native_pkcs11_traits::{Certificate as P11Certificate, KeyAlgorithm}; +use crate::key::YubikeyPivPublicKey; +use crate::piv::slot::SlotObject; +use native_pkcs11_traits::Certificate as P11Certificate; use native_pkcs11_traits::PublicKey as P11PublicKey; use native_pkcs11_traits::Result as P11Result; -use crate::key::YubikeyPivPublicKey; - pub struct YubikeyPivCertificate { - pub label: String, - pub identity: String, - pub public_key: YubikeyPivPublicKey, - certificate_der: Vec, + slot_object: SlotObject, + public_key: YubikeyPivPublicKey, } impl YubikeyPivCertificate { - pub fn new(label: String, identity: String, certificate_der: Vec, public_key_der: Vec) -> P11Result { - let public_key = YubikeyPivPublicKey::new( - label.clone(), KeyAlgorithm::Ecc, public_key_der, - )?; - Ok(Self { - label, - identity, + pub fn new(slot_object: SlotObject) -> P11Result { + let public_key = YubikeyPivPublicKey::new(slot_object.clone())?; + Ok(YubikeyPivCertificate { + slot_object, public_key, - certificate_der, }) } - // pub fn new(identity: impl Into) -> Result { - // let identity: SecIdentity = identity.into(); - // let label = identity.certificate().unwrap().subject_summary(); - // let pk = identity.certificate()?.public_key()?; - // Ok(Self { - // certificate_der: identity.certificate()?.to_der(), - // label: label.clone(), - // identity, - // public_key: YubikeyPivPublicKey::new(pk, label)?, - // }) - // } } impl Debug for YubikeyPivCertificate { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("KeychainCertificate") - .field("label", &self.label) - .field("identity", &self.identity) + .field("label", &self.slot_object.label) + .field("identity", &self.slot_object.public_key_hash) .finish() } } impl P11Certificate for YubikeyPivCertificate { fn label(&self) -> String { - self.label.to_string() + self.slot_object.label.clone() } fn to_der(&self) -> Vec { - self.certificate_der.clone() + self.slot_object.certificate_der.clone() } fn public_key(&self) -> &dyn P11PublicKey { diff --git a/native-pkcs11-piv/src/piv/key.rs b/native-pkcs11-piv/src/piv/key.rs index ba18aaa..d7a97bc 100644 --- a/native-pkcs11-piv/src/piv/key.rs +++ b/native-pkcs11-piv/src/piv/key.rs @@ -16,10 +16,9 @@ use std::fmt::Debug; use tracing::instrument; -use native_pkcs11_traits::{Backend, KeyAlgorithm, PrivateKey, PublicKey, SignatureAlgorithm}; +use crate::piv::slot::SlotObject; use native_pkcs11_traits::Result as P11Result; - -use crate::piv::util::sha1_bytes; +use native_pkcs11_traits::{Backend, KeyAlgorithm, PrivateKey, PublicKey, SignatureAlgorithm}; #[derive(Debug)] pub enum Algorithm { @@ -29,41 +28,27 @@ pub enum Algorithm { #[derive(Debug)] pub struct YubikeyPivPrivateKey { - // sec_key: SecKey, - label: String, - public_key_hash: Vec, - algorithm: KeyAlgorithm, - pub_key: Option, + slot_object: SlotObject, } impl YubikeyPivPrivateKey { - // #[instrument] - // pub fn new( - // sec_key: SecKey, - // label: impl Into + Debug, - // pub_key: Option, - // ) -> Result { - // let label = label.into(); - // let public_key_hash = sec_key.application_label().ok_or("no application_label")?; - // Ok(Self { - // algorithm: sec_key_algorithm(&sec_key)?, - // sec_key, - // label, - // public_key_hash, - // pub_key, - // }) - // } + #[instrument] + pub fn new(slot_object: SlotObject) -> P11Result { + Ok(YubikeyPivPrivateKey { + slot_object, + }) + } } impl PrivateKey for YubikeyPivPrivateKey { #[instrument] fn public_key_hash(&self) -> Vec { - self.public_key_hash.clone() + self.slot_object.public_key_hash.clone() } #[instrument] fn label(&self) -> String { - self.label.clone() + self.slot_object.label.clone() } #[instrument] @@ -72,6 +57,7 @@ impl PrivateKey for YubikeyPivPrivateKey { algorithm: &SignatureAlgorithm, data: &[u8], ) -> P11Result> { + println!(">> CALL: sign"); match algorithm { SignatureAlgorithm::Ecdsa => {} _ => return Err("RSA algorithm not supported.")?, @@ -82,12 +68,12 @@ impl PrivateKey for YubikeyPivPrivateKey { #[instrument] fn delete(&self) { - // yubikey-piv-pkcs11 just cannot delete private key + // TODO ... yubikey-piv-pkcs11 just cannot delete private key } #[instrument] fn algorithm(&self) -> KeyAlgorithm { - self.algorithm + self.slot_object.algorithm } fn find_public_key( @@ -101,54 +87,32 @@ impl PrivateKey for YubikeyPivPrivateKey { #[derive(Debug, Clone)] pub struct YubikeyPivPublicKey { - // pub sec_key: SecKey, - pub label: String, - der: Vec, - public_key_hash: Vec, - algorithm: KeyAlgorithm, + slot_object: SlotObject, } impl YubikeyPivPublicKey { #[instrument] - pub fn new(label: String, algorithm: KeyAlgorithm, public_key_der: Vec) -> P11Result { - let public_key_hash = sha1_bytes(&public_key_der); - Ok(Self { - label, - der: public_key_der, - public_key_hash, - algorithm, + pub fn new(slot_object: SlotObject) -> P11Result { + Ok(YubikeyPivPublicKey { + slot_object, }) } - // #[instrument] - // pub fn new(sec_key: SecKey, label: impl Into + Debug) -> Result { - // let der = sec_key - // .external_representation() - // .ok_or("no external representation")?; - // let key_ty = sec_key_algorithm(&sec_key)?; - // Ok(Self { - // public_key_hash: sec_key.application_label().ok_or("no application_label")?, - // sec_key, - // label: label.into(), - // der: der.to_vec(), - // algorithm: key_ty, - // }) - // } } impl PublicKey for YubikeyPivPublicKey { #[instrument] fn public_key_hash(&self) -> Vec { - self.public_key_hash.clone() + self.slot_object.public_key_hash.clone() } #[instrument] fn label(&self) -> String { - self.label.clone() + self.slot_object.label.clone() } #[instrument] fn to_der(&self) -> Vec { - self.der.clone() + self.slot_object.public_key_der.clone() } #[instrument] @@ -158,6 +122,7 @@ impl PublicKey for YubikeyPivPublicKey { data: &[u8], signature: &[u8], ) -> P11Result<()> { + println!(">> CALL: verify"); match algorithm { SignatureAlgorithm::Ecdsa => {} _ => return Err("RSA algorithm not supported.")?, @@ -175,6 +140,6 @@ impl PublicKey for YubikeyPivPublicKey { } fn algorithm(&self) -> KeyAlgorithm { - self.algorithm + self.slot_object.algorithm } } diff --git a/native-pkcs11-piv/src/piv/mod.rs b/native-pkcs11-piv/src/piv/mod.rs index 206959f..aeac82f 100644 --- a/native-pkcs11-piv/src/piv/mod.rs +++ b/native-pkcs11-piv/src/piv/mod.rs @@ -25,6 +25,7 @@ pub mod certificate; pub mod key; mod pinentry; mod util; +mod slot; pub type Result = std::result::Result; diff --git a/native-pkcs11-piv/src/piv/slot.rs b/native-pkcs11-piv/src/piv/slot.rs new file mode 100644 index 0000000..5af1746 --- /dev/null +++ b/native-pkcs11-piv/src/piv/slot.rs @@ -0,0 +1,54 @@ +// Copyright 2024 Hatter Jiang +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::certificate::YubikeyPivCertificate; +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}; + +#[derive(Debug, Clone)] +pub struct SlotObject { + pub label: String, + // EQUALS: sha1_bytes(&public_key_der) + pub public_key_hash: Vec, + pub algorithm: KeyAlgorithm, + pub public_key_der: Vec, + pub certificate_der: Vec, +} + +impl SlotObject { + pub fn new(algorithm: KeyAlgorithm, label: String, certificate_der: Vec, public_key_der: Vec) -> Self { + let public_key_hash = sha1_bytes(&public_key_der); + SlotObject { + label, + public_key_hash, + algorithm, + public_key_der, + certificate_der, + } + } + + pub fn to_private_key(&self) -> P11Result> { + Ok(Box::new(YubikeyPivPrivateKey::new(self.clone())?)) + } + + pub fn to_public_key(&self) -> P11Result> { + Ok(Box::new(YubikeyPivPublicKey::new(self.clone())?)) + } + + pub fn to_certificate(&self) -> P11Result> { + Ok(Box::new(YubikeyPivCertificate::new(self.clone())?)) + } +} \ No newline at end of file