From 115a119a27858f45ab51bf640d61c9ce8c84335e Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Mon, 27 Nov 2023 21:40:10 -0800 Subject: [PATCH] ecdsa: provide der-encoded signatures --- Cargo.lock | 49 +++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +++ src/ecdsa/signer.rs | 38 +++++++++++++++++++++++++++++++++++ tests/ecdsa/mod.rs | 34 ++++++++++++++++++++++++++++++- 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5a76f6c..6e8eef6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,10 +232,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.3.9" @@ -333,6 +346,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +[[package]] +name = "flagset" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" + [[package]] name = "generic-array" version = "0.14.7" @@ -754,6 +773,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -801,9 +831,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -925,6 +955,19 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "x509-cert" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25eefca1d99701da3a57feb07e5079fc62abba059fc139e98c13bbb250f3ef29" +dependencies = [ + "const-oid", + "der", + "sha1", + "signature", + "spki", +] + [[package]] name = "yubihsm" version = "0.42.1" @@ -953,11 +996,13 @@ dependencies = [ "serde_json", "sha2", "signature", + "spki", "subtle", "thiserror", "time", "tiny_http", "uuid", + "x509-cert", "zeroize", ] diff --git a/Cargo.toml b/Cargo.toml index 4de1b3eb..84dca90f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ hmac = { version = "0.12", optional = true } k256 = { version = "0.13", optional = true, features = ["ecdsa", "sha256"] } pbkdf2 = { version = "0.12", optional = true, default-features = false, features = ["hmac"] } serde_json = { version = "1", optional = true } +spki = { version = "0.7.3", optional = true, default-features = false } rusb = { version = "0.9", optional = true } sha2 = { version = "0.10", optional = true } tiny_http = { version = "0.12", optional = true } @@ -53,9 +54,11 @@ tiny_http = { version = "0.12", optional = true } ed25519-dalek = "2" once_cell = "1" p256 = { version = "0.13", features = ["ecdsa"] } +x509-cert = { version = "0.2.4", features = ["builder"] } [features] default = ["http", "passwords", "setup"] +der-signer = ["spki", "sha2/oid"] http-server = ["tiny_http"] http = [] mockhsm = ["digest", "ecdsa/arithmetic", "ed25519-dalek", "p256/ecdsa", "secp256k1"] diff --git a/src/ecdsa/signer.rs b/src/ecdsa/signer.rs index 81f7cf7d..7b1a579c 100644 --- a/src/ecdsa/signer.rs +++ b/src/ecdsa/signer.rs @@ -20,6 +20,15 @@ use std::ops::Add; #[cfg(feature = "secp256k1")] use super::{secp256k1::RecoveryId, Secp256k1}; +#[cfg(feature = "der-signer")] +use { + ecdsa::{der, hazmat::DigestPrimitive}, + spki::{ + der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, + SignatureAlgorithmIdentifier, + }, +}; + /// ECDSA signature provider for yubihsm-client #[derive(signature::Signer)] pub struct Signer @@ -195,3 +204,32 @@ where self.sign_prehash(&digest.finalize()) } } + +#[cfg(feature = "der-signer")] +impl DigestSigner> for Signer +where + C: CurveAlgorithm + CurveArithmetic + PointCompression + PrimeCurve + DigestPrimitive, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: sec1::ModulusSize, + ecdsa::der::MaxSize: ArrayLength, + as Add>::Output: Add + ArrayLength, + Self: DigestSigner>, +{ + fn try_sign_digest(&self, digest: C::Digest) -> Result, Error> { + DigestSigner::>::try_sign_digest(self, digest).map(Into::into) + } +} + +#[cfg(feature = "der-signer")] +impl SignatureAlgorithmIdentifier for Signer +where + C: CurveAlgorithm + CurveArithmetic + PointCompression + PrimeCurve, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: sec1::ModulusSize, + Signature: AssociatedAlgorithmIdentifier>, +{ + type Params = AnyRef<'static>; + + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = + Signature::::ALGORITHM_IDENTIFIER; +} diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index 24215567..a6b68c8e 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -14,6 +14,19 @@ use yubihsm::{ object, Client, }; +#[cfg(feature = "der-signer")] +use { + ::ecdsa::{der, signature::Keypair}, + spki::SubjectPublicKeyInfoOwned, + std::{str::FromStr, time::Duration}, + x509_cert::{ + builder::{Builder, CertificateBuilder, Profile}, + name::Name, + serial_number::SerialNumber, + time::Validity, + }, +}; + #[cfg(feature = "secp256k1")] use { ::ecdsa::signature::{digest::Digest, DigestSigner, DigestVerifier}, @@ -69,7 +82,7 @@ fn ecdsa_nistp256_sign_test() { let signer = create_signer::(201); let verify_key = p256::ecdsa::VerifyingKey::from_encoded_point(signer.public_key()).unwrap(); - let signature = signer.sign(TEST_MESSAGE); + let signature: ecdsa::Signature = signer.sign(TEST_MESSAGE); assert!(verify_key.verify(TEST_MESSAGE, &signature).is_ok()); } @@ -105,3 +118,22 @@ fn ecdsa_secp256k1_sign_recover_test() { let signer_pk = PublicKey::from_encoded_point(signer.public_key()).unwrap(); assert_eq!(&recovered_pk, &signer_pk); } + +#[cfg(feature = "der-signer")] +#[test] +fn ecdsa_nistp256_ca() { + let signer = create_signer::(204); + + let serial_number = SerialNumber::from(42u32); + let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); + let profile = Profile::Root; + let subject = + Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap(); + let pub_key = SubjectPublicKeyInfoOwned::from_key(signer.verifying_key()).unwrap(); + + let builder = + CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer) + .expect("Create certificate"); + + builder.build::>().unwrap(); +}