diff --git a/Cargo.lock b/Cargo.lock index 60efea9..4eca4d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,45 @@ dependencies = [ "memchr", ] +[[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", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -164,6 +203,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "der" version = "0.7.9" @@ -177,6 +222,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "der_derive" version = "0.7.2" @@ -188,6 +247,15 @@ dependencies = [ "syn", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "des" version = "0.8.1" @@ -209,6 +277,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -381,6 +460,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.4.0" @@ -508,11 +593,13 @@ dependencies = [ "rsa", "secrecy", "serial_test", + "sha1", "spki", "thiserror", "tracing", "tracing-error", "x509-cert", + "x509-parser", "yubikey", ] @@ -545,6 +632,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -563,6 +660,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -593,6 +696,15 @@ dependencies = [ "libm", ] +[[package]] +name = "oid-registry" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -750,6 +862,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -934,6 +1052,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[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.38.34" @@ -1145,6 +1272,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -1175,6 +1313,37 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tls_codec" version = "0.4.1" @@ -1504,6 +1673,23 @@ dependencies = [ "tls_codec", ] +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "yubikey" version = "0.8.0" diff --git a/README.md b/README.md index a58ce8c..bb2914c 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,16 @@ Yubikey PIV PKCS#11 > Fork form: https://github.com/google/native-pkcs11 +
+ +Alias: +```shell +alias p11='pkcs11-tool --module /FULLPATH/libyubikey_piv_pkcs11.dylib' +``` + +List certificates +```shell +p11 --list-object --type cert +``` + + diff --git a/native-pkcs11-piv/Cargo.toml b/native-pkcs11-piv/Cargo.toml index fcb9bbf..74b75a9 100644 --- a/native-pkcs11-piv/Cargo.toml +++ b/native-pkcs11-piv/Cargo.toml @@ -28,6 +28,8 @@ tracing = "0.1.40" tracing-error = { version = "0.2.0", default-features = false } x509-cert = { version = "0.2.5", default-features = false } yubikey = { version = "0.8.0", features = ["untested"] } +sha1 = "0.10" +x509-parser = "0.16.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 c07b7af..bd77097 100644 --- a/native-pkcs11-piv/src/piv/backend.rs +++ b/native-pkcs11-piv/src/piv/backend.rs @@ -15,6 +15,9 @@ 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}; @@ -25,6 +28,9 @@ 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; + #[derive(Debug, Default)] pub struct YubikeyPivBackend { cached_pin: Mutex>, @@ -82,8 +88,26 @@ impl Backend for YubikeyPivBackend { fn find_all_certificates( &self, ) -> P11Result>> { - // TODO ... - Ok(vec![]) + 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())?; + 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); + } + } + Ok(()) + })?; + Ok(certs) } #[instrument] @@ -92,8 +116,12 @@ impl Backend for YubikeyPivBackend { query: P11KeySearchOptions, ) -> P11Result>> { match query { - KeySearchOptions::Label(label) => {} - KeySearchOptions::PublicKeyHash(public_key_hash) => {} + KeySearchOptions::Label(label) => { + println!(">>> find private key >>>: {}", label); + } + KeySearchOptions::PublicKeyHash(public_key_hash) => { + println!(">>> find private key >>>: {:?}", public_key_hash); + } } // TODO ... Ok(None) @@ -105,8 +133,12 @@ impl Backend for YubikeyPivBackend { query: P11KeySearchOptions, ) -> P11Result>> { match query { - KeySearchOptions::Label(label) => {} - KeySearchOptions::PublicKeyHash(public_key_hash) => {} + KeySearchOptions::Label(label) => { + println!(">>> find public key >>>: {}", label); + } + KeySearchOptions::PublicKeyHash(public_key_hash) => { + println!(">>> find public key >>>: {:?}", public_key_hash); + } } // TODO ... Ok(None) @@ -122,6 +154,9 @@ impl Backend for YubikeyPivBackend { fn find_all_public_keys( &self, ) -> P11Result>> { + // self.find_all_certificates().map(|c|{ + // c.as_mut().map(|c| ) + // }) // TODO ... Ok(vec![]) } diff --git a/native-pkcs11-piv/src/piv/certificate.rs b/native-pkcs11-piv/src/piv/certificate.rs index 7610a2d..42854d7 100644 --- a/native-pkcs11-piv/src/piv/certificate.rs +++ b/native-pkcs11-piv/src/piv/certificate.rs @@ -14,8 +14,9 @@ use std::fmt::Debug; -use native_pkcs11_traits::Certificate as P11Certificate; +use native_pkcs11_traits::{Certificate as P11Certificate, KeyAlgorithm}; use native_pkcs11_traits::PublicKey as P11PublicKey; +use native_pkcs11_traits::Result as P11Result; use crate::key::YubikeyPivPublicKey; @@ -27,6 +28,17 @@ pub struct YubikeyPivCertificate { } 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, + public_key, + certificate_der, + }) + } // pub fn new(identity: impl Into) -> Result { // let identity: SecIdentity = identity.into(); // let label = identity.certificate().unwrap().subject_summary(); diff --git a/native-pkcs11-piv/src/piv/key.rs b/native-pkcs11-piv/src/piv/key.rs index 6b101c2..ba18aaa 100644 --- a/native-pkcs11-piv/src/piv/key.rs +++ b/native-pkcs11-piv/src/piv/key.rs @@ -19,6 +19,8 @@ use tracing::instrument; use native_pkcs11_traits::{Backend, KeyAlgorithm, PrivateKey, PublicKey, SignatureAlgorithm}; use native_pkcs11_traits::Result as P11Result; +use crate::piv::util::sha1_bytes; + #[derive(Debug)] pub enum Algorithm { RSA, @@ -107,6 +109,16 @@ pub struct YubikeyPivPublicKey { } 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, + }) + } // #[instrument] // pub fn new(sec_key: SecKey, label: impl Into + Debug) -> Result { // let der = sec_key diff --git a/native-pkcs11-piv/src/piv/mod.rs b/native-pkcs11-piv/src/piv/mod.rs index 3dca96d..206959f 100644 --- a/native-pkcs11-piv/src/piv/mod.rs +++ b/native-pkcs11-piv/src/piv/mod.rs @@ -24,6 +24,7 @@ mod backend; pub mod certificate; pub mod key; mod pinentry; +mod util; pub type Result = std::result::Result; diff --git a/native-pkcs11-piv/src/piv/util.rs b/native-pkcs11-piv/src/piv/util.rs new file mode 100644 index 0000000..9e42f4e --- /dev/null +++ b/native-pkcs11-piv/src/piv/util.rs @@ -0,0 +1,63 @@ +use p256::ecdsa::signature::digest::Digest; +use p256::pkcs8::ObjectIdentifier; +use sha1::Sha1; +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 native_pkcs11_traits::Result as P11Result; + +const RSA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); +const ECC: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"); +// NIST recommended curves +// secp192r1 – {1.2.840.10045.3.1.1} +// secp224r1 – {1.3.132.0.33} +// secp256r1 – {1.2.840.10045.3.1.7} +// secp384r1 – {1.3.132.0.34} +// secp521r1 – {1.3.132.0.35} +const ECC_P256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7"); +const ECC_P384: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.132.0.34"); + + +pub fn sha1_bytes(input: &[u8]) -> Vec { + let mut digest = Sha1::default(); + digest.update(input); + digest.finalize().to_vec() +} + +pub fn get_algorithm_id_by_certificate(certificate: &Certificate) -> P11Result { + let tbs_certificate = &certificate.cert.tbs_certificate; + get_algorithm_id(&tbs_certificate.subject_public_key_info) +} + +pub fn get_algorithm_id(public_key_info: &SubjectPublicKeyInfoOwned) -> P11Result { + if public_key_info.algorithm.oid == RSA { + let rsa_public_key = RSAPublicKey::from_der(public_key_info.subject_public_key.raw_bytes())?; + let starts_with_0 = rsa_public_key.1.modulus.starts_with(&[0]); + let public_key_bits = (rsa_public_key.1.modulus.len() - if starts_with_0 { 1 } else { 0 }) * 8; + if public_key_bits == 1024 { + return Ok(AlgorithmId::Rsa1024); + } + if public_key_bits == 2048 { + return Ok(AlgorithmId::Rsa2048); + } + Err(format!("Unknown rsa bits: {}", public_key_bits))?; + } + if public_key_info.algorithm.oid == ECC { + if let Some(any) = &public_key_info.algorithm.parameters { + let any_parameter_der = any.to_der()?; + let any_parameter_oid = ObjectIdentifier::from_der(&any_parameter_der)?; + if any_parameter_oid == ECC_P256 { + return Ok(AlgorithmId::EccP256); + } + if any_parameter_oid == ECC_P384 { + return Ok(AlgorithmId::EccP384); + } + Err(format!("Unknown any parameter oid: {}", any_parameter_oid))?; + } + } + Err(format!("Unknown algorithm: {}", public_key_info.algorithm.oid))? +} diff --git a/native-pkcs11-piv/test-codesigned.sh b/native-pkcs11-piv/test-codesigned.sh deleted file mode 100755 index ca1468a..0000000 --- a/native-pkcs11-piv/test-codesigned.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Copyright 2022 Google LLC -# -# 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. - - -set -e - -# https://stackoverflow.com/questions/59895/how-do-i-get-the-directory-where-a-bash-script-is-located-from-within-the-script -cd $(dirname -- "$( readlink -f -- "$0"; )") - -cargo build --bin signedtest - -./codesigning-testing/codesign.sh $PWD/../target/debug/signedtest - -(../target/debug/signedtest && echo SUCCESS) || (echo FAIL && exit 1) diff --git a/native-pkcs11/src/lib.rs b/native-pkcs11/src/lib.rs index d54da18..a46e75b 100644 --- a/native-pkcs11/src/lib.rs +++ b/native-pkcs11/src/lib.rs @@ -277,7 +277,7 @@ cryptoki_fn!( } ); -#[cfg(not(feature = "custom-function-list"))] +// #[cfg(not(feature = "custom-function-list"))] cryptoki_fn!( unsafe fn C_GetFunctionList(ppFunctionList: CK_FUNCTION_LIST_PTR_PTR) { not_null!(ppFunctionList); diff --git a/native-pkcs11/src/object_store.rs b/native-pkcs11/src/object_store.rs index 48af12f..cd59b82 100644 --- a/native-pkcs11/src/object_store.rs +++ b/native-pkcs11/src/object_store.rs @@ -15,22 +15,23 @@ use std::collections::HashMap; use cached::{Cached, TimedCache}; +use tracing::{instrument, warn}; + use native_pkcs11_core::{ - attribute::{Attribute, AttributeType, Attributes}, + attribute::{Attribute, Attributes, AttributeType}, Result, }; use native_pkcs11_traits::{backend, KeySearchOptions}; use pkcs11_sys::{ + CK_OBJECT_HANDLE, CKO_CERTIFICATE, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY, CKO_SECRET_KEY, CKP_BASELINE_PROVIDER, - CK_OBJECT_HANDLE, }; -use tracing::{instrument, warn}; -use crate::{object::Object, Error}; +use crate::{Error, object::Object}; #[derive(Debug)] pub struct ObjectStore { @@ -200,13 +201,15 @@ impl Default for ObjectStore { mod tests { use std::vec; - use native_pkcs11_traits::{backend, random_label, KeyAlgorithm}; - use pkcs11_sys::CKO_PRIVATE_KEY; use serial_test::serial; - use super::*; + use native_pkcs11_traits::{backend, KeyAlgorithm, random_label}; + use pkcs11_sys::CKO_PRIVATE_KEY; + use crate::tests::test_init; + use super::*; + #[test] #[serial] fn test_object_store() {