feat: works with errors

This commit is contained in:
2024-08-15 23:19:05 +08:00
parent 5d410e76b2
commit e31190d4e5
7 changed files with 207 additions and 137 deletions

7
Cargo.lock generated
View File

@@ -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",

View File

@@ -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 }

View File

@@ -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<Option<String>>,
yubikey: Mutex<Option<YubiKey>>,
slot_objects: Mutex<Option<Vec<SlotObject>>>,
}
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<F>(&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<Vec<Box<dyn P11Certificate>>> {
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<dyn P11Certificate> = 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<Box<dyn P11Certificate>> = 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<Option<Arc<dyn P11PrivateKey>>> {
println!("[find_private_key]");
match query {
self.init_slot_objects()?;
let mut private_key: Option<Arc<dyn P11PrivateKey>> = None;
self.for_each_slot_objects(|slot_object| {
if private_key.is_none() {
match &query {
KeySearchOptions::Label(label) => {
println!(">>> find private key >>>: {}", label);
if &slot_object.label == label {
private_key = Some(Arc::from(slot_object.to_private_key()?));
}
}
KeySearchOptions::PublicKeyHash(public_key_hash) => {
println!(">>> find private key >>>: {:?}", 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()?));
}
}
// TODO ...
Ok(None)
}
}
Ok(())
})?;
Ok(private_key)
}
#[instrument]
@@ -136,35 +178,52 @@ impl Backend for YubikeyPivBackend {
query: P11KeySearchOptions,
) -> P11Result<Option<Box<dyn P11PublicKey>>> {
println!("[find_public_key]");
match query {
self.init_slot_objects()?;
let mut public_key: Option<Box<dyn P11PublicKey>> = None;
self.for_each_slot_objects(|slot_object| {
if public_key.is_none() {
match &query {
KeySearchOptions::Label(label) => {
println!(">>> find public key >>>: {}", label);
if &slot_object.label == label {
public_key = Some(slot_object.to_public_key()?);
}
}
KeySearchOptions::PublicKeyHash(public_key_hash) => {
println!(">>> find public key >>>: {:?}", public_key_hash);
if hex::encode(&slot_object.public_key_hash) == hex::encode(public_key_hash) {
public_key = Some(slot_object.to_public_key()?);
}
}
// TODO ...
Ok(None)
}
}
Ok(())
})?;
Ok(public_key)
}
fn find_all_private_keys(
&self,
) -> P11Result<Vec<Arc<dyn P11PrivateKey>>> {
println!("[find_all_private_keys]");
// TODO ...
Ok(vec![])
self.init_slot_objects()?;
let mut private_keys: Vec<Arc<dyn P11PrivateKey>> = 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<Vec<Arc<dyn P11PublicKey>>> {
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<Arc<dyn P11PublicKey>> = vec![];
self.for_each_slot_objects(|slot_object| {
public_keys.push(Arc::from(slot_object.to_public_key()?));
Ok(())
})?;
Ok(public_keys)
}
#[instrument]

View File

@@ -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<u8>,
slot_object: SlotObject,
public_key: YubikeyPivPublicKey,
}
impl YubikeyPivCertificate {
pub fn new(label: String, identity: String, certificate_der: Vec<u8>, public_key_der: Vec<u8>) -> P11Result<Self> {
let public_key = YubikeyPivPublicKey::new(
label.clone(), KeyAlgorithm::Ecc, public_key_der,
)?;
Ok(Self {
label,
identity,
pub fn new(slot_object: SlotObject) -> P11Result<Self> {
let public_key = YubikeyPivPublicKey::new(slot_object.clone())?;
Ok(YubikeyPivCertificate {
slot_object,
public_key,
certificate_der,
})
}
// pub fn new(identity: impl Into<SecIdentity>) -> Result<Self> {
// 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<u8> {
self.certificate_der.clone()
self.slot_object.certificate_der.clone()
}
fn public_key(&self) -> &dyn P11PublicKey {

View File

@@ -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<u8>,
algorithm: KeyAlgorithm,
pub_key: Option<YubikeyPivPublicKey>,
slot_object: SlotObject,
}
impl YubikeyPivPrivateKey {
// #[instrument]
// pub fn new(
// sec_key: SecKey,
// label: impl Into<String> + Debug,
// pub_key: Option<YubikeyPivPublicKey>,
// ) -> Result<Self> {
// 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<Self> {
Ok(YubikeyPivPrivateKey {
slot_object,
})
}
}
impl PrivateKey for YubikeyPivPrivateKey {
#[instrument]
fn public_key_hash(&self) -> Vec<u8> {
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<Vec<u8>> {
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<u8>,
public_key_hash: Vec<u8>,
algorithm: KeyAlgorithm,
slot_object: SlotObject,
}
impl YubikeyPivPublicKey {
#[instrument]
pub fn new(label: String, algorithm: KeyAlgorithm, public_key_der: Vec<u8>) -> P11Result<Self> {
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<Self> {
Ok(YubikeyPivPublicKey {
slot_object,
})
}
// #[instrument]
// pub fn new(sec_key: SecKey, label: impl Into<String> + Debug) -> Result<Self> {
// 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<u8> {
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<u8> {
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
}
}

View File

@@ -25,6 +25,7 @@ pub mod certificate;
pub mod key;
mod pinentry;
mod util;
mod slot;
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -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<u8>,
pub algorithm: KeyAlgorithm,
pub public_key_der: Vec<u8>,
pub certificate_der: Vec<u8>,
}
impl SlotObject {
pub fn new(algorithm: KeyAlgorithm, label: String, certificate_der: Vec<u8>, public_key_der: Vec<u8>) -> 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<Box<dyn PrivateKey>> {
Ok(Box::new(YubikeyPivPrivateKey::new(self.clone())?))
}
pub fn to_public_key(&self) -> P11Result<Box<dyn PublicKey>> {
Ok(Box::new(YubikeyPivPublicKey::new(self.clone())?))
}
pub fn to_certificate(&self) -> P11Result<Box<dyn Certificate>> {
Ok(Box::new(YubikeyPivCertificate::new(self.clone())?))
}
}