feat: works with OpenSSH (algorithm ECDSA)
This commit is contained in:
123
Cargo.lock
generated
123
Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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<Mutex<Option<YubiKey>>> = Lazy::new(Default::default);
|
||||
static CACHED_PIN: Lazy<Mutex<Option<String>>> = Lazy::new(Default::default);
|
||||
static ENABLE_RETIRED: Lazy<bool> = 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<String> {
|
||||
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<Option<String>>,
|
||||
yubikey: Mutex<Option<YubiKey>>,
|
||||
slot_objects: Mutex<Option<Vec<SlotObject>>>,
|
||||
}
|
||||
|
||||
@@ -41,42 +64,26 @@ impl YubikeyPivBackend {
|
||||
YubikeyPivBackend::default()
|
||||
}
|
||||
|
||||
fn run_with_yubikey<F>(&self, verify: bool, mut callback: F) -> P11Result<()>
|
||||
pub(crate) fn run_with_yubikey<F, T>(verify: bool, mut callback: F) -> P11Result<T>
|
||||
where
|
||||
F: FnMut(&mut YubiKey) -> P11Result<()>,
|
||||
F: FnMut(&mut YubiKey) -> P11Result<T>,
|
||||
{
|
||||
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<String> {
|
||||
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<Option<Arc<dyn P11PrivateKey>>> {
|
||||
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<Arc<dyn P11PrivateKey>> = 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<Option<Box<dyn P11PublicKey>>> {
|
||||
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<Box<dyn P11PublicKey>> = 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<Vec<Arc<dyn P11PrivateKey>>> {
|
||||
println!("[find_all_private_keys]");
|
||||
// println!("[find_all_private_keys]");
|
||||
self.init_slot_objects()?;
|
||||
let mut private_keys: Vec<Arc<dyn P11PrivateKey>> = vec![];
|
||||
self.for_each_slot_objects(|slot_object| {
|
||||
@@ -216,7 +208,7 @@ impl Backend for YubikeyPivBackend {
|
||||
fn find_all_public_keys(
|
||||
&self,
|
||||
) -> P11Result<Vec<Arc<dyn P11PublicKey>>> {
|
||||
println!("[find_all_public_keys]");
|
||||
// println!("[find_all_public_keys]");
|
||||
self.init_slot_objects()?;
|
||||
let mut public_keys: Vec<Arc<dyn P11PublicKey>> = 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)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u8> {
|
||||
|
||||
@@ -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<Vec<u8>> {
|
||||
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<Option<Box<dyn PublicKey>>> {
|
||||
// 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<Self> {
|
||||
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<Self>) {
|
||||
// yubikey-piv-pkcs11 just cannot delete public key
|
||||
// TODO ... yubikey-piv-pkcs11 just cannot delete public key
|
||||
}
|
||||
|
||||
fn algorithm(&self) -> KeyAlgorithm {
|
||||
|
||||
@@ -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<u8>,
|
||||
pub algorithm: KeyAlgorithm,
|
||||
@@ -29,10 +31,15 @@ pub struct SlotObject {
|
||||
}
|
||||
|
||||
impl SlotObject {
|
||||
pub fn new(algorithm: KeyAlgorithm, label: String, certificate_der: Vec<u8>, public_key_der: Vec<u8>) -> Self {
|
||||
pub fn new(algorithm_id: AlgorithmId, slot_id: SlotId, certificate_der: Vec<u8>, public_key_der: Vec<u8>) -> 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,
|
||||
|
||||
@@ -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<u8>, Vec<u8>)> {
|
||||
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<u8> {
|
||||
if bytes[0] == 0 {
|
||||
bytes[1..].to_vec()
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user