feat: works with OpenSSH (algorithm ECDSA)

This commit is contained in:
2024-08-16 23:16:24 +08:00
parent e31190d4e5
commit 2c25c5d824
7 changed files with 249 additions and 110 deletions

123
Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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