Skip to content

Commit 1e40f45

Browse files
committed
ecdsa: provide der-encoded signatures
1 parent 9aedc98 commit 1e40f45

File tree

4 files changed

+121
-3
lines changed

4 files changed

+121
-3
lines changed

Cargo.lock

Lines changed: 47 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ hmac = { version = "0.12", optional = true }
4545
k256 = { version = "0.13", optional = true, features = ["ecdsa", "sha256"] }
4646
pbkdf2 = { version = "0.12", optional = true, default-features = false, features = ["hmac"] }
4747
serde_json = { version = "1", optional = true }
48+
spki = { version = "0.7.3", optional = true, default-features = false }
4849
rusb = { version = "0.9", optional = true }
4950
sha2 = { version = "0.10", optional = true }
5051
tiny_http = { version = "0.12", optional = true }
@@ -53,9 +54,11 @@ tiny_http = { version = "0.12", optional = true }
5354
ed25519-dalek = "2"
5455
once_cell = "1"
5556
p256 = { version = "0.13", features = ["ecdsa"] }
57+
x509-cert = { version = "0.2.4", features = ["builder"] }
5658

5759
[features]
5860
default = ["http", "passwords", "setup"]
61+
der-signer = ["spki", "sha2/oid"]
5962
http-server = ["tiny_http"]
6063
http = []
6164
mockhsm = ["digest", "ecdsa/arithmetic", "ed25519-dalek", "p256/ecdsa", "secp256k1"]

src/ecdsa/signer.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ use std::ops::Add;
2020
#[cfg(feature = "secp256k1")]
2121
use super::{secp256k1::RecoveryId, Secp256k1};
2222

23+
#[cfg(feature = "der-signer")]
24+
use {
25+
ecdsa::{der, hazmat::DigestPrimitive},
26+
spki::{
27+
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
28+
SignatureAlgorithmIdentifier,
29+
},
30+
};
31+
2332
/// ECDSA signature provider for yubihsm-client
2433
#[derive(signature::Signer)]
2534
pub struct Signer<C>
@@ -195,3 +204,32 @@ where
195204
self.sign_prehash(&digest.finalize())
196205
}
197206
}
207+
208+
#[cfg(feature = "der-signer")]
209+
impl<C> DigestSigner<C::Digest, der::Signature<C>> for Signer<C>
210+
where
211+
C: CurveAlgorithm + CurveArithmetic + PointCompression + PrimeCurve + DigestPrimitive,
212+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
213+
FieldBytesSize<C>: sec1::ModulusSize,
214+
ecdsa::der::MaxSize<C>: ArrayLength<u8>,
215+
<FieldBytesSize<C> as Add>::Output: Add<ecdsa::der::MaxOverhead> + ArrayLength<u8>,
216+
Self: DigestSigner<C::Digest, Signature<C>>,
217+
{
218+
fn try_sign_digest(&self, digest: C::Digest) -> Result<der::Signature<C>, Error> {
219+
DigestSigner::<C::Digest, Signature<C>>::try_sign_digest(self, digest).map(Into::into)
220+
}
221+
}
222+
223+
#[cfg(feature = "der-signer")]
224+
impl<C> SignatureAlgorithmIdentifier for Signer<C>
225+
where
226+
C: CurveAlgorithm + CurveArithmetic + PointCompression + PrimeCurve,
227+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
228+
FieldBytesSize<C>: sec1::ModulusSize,
229+
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
230+
{
231+
type Params = AnyRef<'static>;
232+
233+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
234+
Signature::<C>::ALGORITHM_IDENTIFIER;
235+
}

tests/ecdsa/mod.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ use yubihsm::{
1414
object, Client,
1515
};
1616

17+
#[cfg(feature = "der-signer")]
18+
use {
19+
::ecdsa::{der, signature::Keypair},
20+
spki::SubjectPublicKeyInfoOwned,
21+
std::{str::FromStr, time::Duration},
22+
x509_cert::{
23+
builder::{Builder, CertificateBuilder, Profile},
24+
name::Name,
25+
serial_number::SerialNumber,
26+
time::Validity,
27+
},
28+
};
29+
1730
#[cfg(feature = "secp256k1")]
1831
use {
1932
::ecdsa::signature::{digest::Digest, DigestSigner, DigestVerifier},
@@ -69,7 +82,7 @@ fn ecdsa_nistp256_sign_test() {
6982
let signer = create_signer::<NistP256>(201);
7083
let verify_key = p256::ecdsa::VerifyingKey::from_encoded_point(signer.public_key()).unwrap();
7184

72-
let signature = signer.sign(TEST_MESSAGE);
85+
let signature: ecdsa::Signature<NistP256> = signer.sign(TEST_MESSAGE);
7386
assert!(verify_key.verify(TEST_MESSAGE, &signature).is_ok());
7487
}
7588

@@ -105,3 +118,22 @@ fn ecdsa_secp256k1_sign_recover_test() {
105118
let signer_pk = PublicKey::from_encoded_point(signer.public_key()).unwrap();
106119
assert_eq!(&recovered_pk, &signer_pk);
107120
}
121+
122+
#[cfg(feature = "der-signer")]
123+
#[test]
124+
fn ecdsa_nistp256_ca() {
125+
let signer = create_signer::<NistP256>(204);
126+
127+
let serial_number = SerialNumber::from(42u32);
128+
let validity = Validity::from_now(Duration::new(5, 0)).unwrap();
129+
let profile = Profile::Root;
130+
let subject =
131+
Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap();
132+
let pub_key = SubjectPublicKeyInfoOwned::from_key(signer.verifying_key()).unwrap();
133+
134+
let builder =
135+
CertificateBuilder::new(profile, serial_number, validity, subject, pub_key, &signer)
136+
.expect("Create certificate");
137+
138+
builder.build::<der::Signature<NistP256>>().unwrap();
139+
}

0 commit comments

Comments
 (0)