Skip to content

Commit 90e734c

Browse files
committed
ecdsa support
Signed-off-by: Arthur Gautier <baloo@superbaloo.net>
1 parent 1db24aa commit 90e734c

File tree

4 files changed

+272
-0
lines changed

4 files changed

+272
-0
lines changed

cryptoki-rustcrypto/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ readme = "README.md"
88
keywords = ["pkcs11", "cryptoki", "hsm"]
99
categories = ["cryptography", "hardware-support"]
1010
license = "Apache-2.0"
11+
repository = "https://github.com/parallaxsecond/rust-cryptoki"
1112

1213
[dependencies]
1314
cryptoki = { path = "../cryptoki", version = "0.6.1" }
1415
der = "0.7.8"
16+
ecdsa = "0.16.9"
17+
p256 = { version = "0.13.2", features = ["pkcs8"] }
1518
rsa = "0.9"
1619
signature = { version = "2.2.0", features = ["digest"] }
1720
sha1 = { version = "0.10", features = ["oid"] }

cryptoki-rustcrypto/src/ecdsa.rs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use cryptoki::{
5+
mechanism::Mechanism,
6+
object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle},
7+
session::Session,
8+
};
9+
use der::{
10+
asn1::{ObjectIdentifier, OctetStringRef},
11+
oid::AssociatedOid,
12+
AnyRef, Decode, Encode,
13+
};
14+
use ecdsa::{
15+
elliptic_curve::{
16+
generic_array::ArrayLength,
17+
sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
18+
AffinePoint, CurveArithmetic, FieldBytesSize, PublicKey,
19+
},
20+
hazmat::DigestPrimitive,
21+
PrimeCurve, Signature, VerifyingKey,
22+
};
23+
use signature::digest::Digest;
24+
use spki::{
25+
AlgorithmIdentifier, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
26+
SignatureAlgorithmIdentifier,
27+
};
28+
use std::{convert::TryFrom, ops::Add};
29+
use thiserror::Error;
30+
31+
#[derive(Error, Debug)]
32+
pub enum Error {
33+
#[error("Cryptoki error: {0}")]
34+
Cryptoki(#[from] cryptoki::error::Error),
35+
36+
#[error("Private key missing attribute: {0}")]
37+
MissingAttribute(AttributeType),
38+
39+
#[error("Elliptic curve error: {0}")]
40+
Ecdsa(#[from] ecdsa::elliptic_curve::Error),
41+
}
42+
43+
pub trait SignAlgorithm: PrimeCurve + CurveArithmetic + AssociatedOid + DigestPrimitive {
44+
fn sign_mechanism() -> Mechanism<'static>;
45+
}
46+
47+
impl SignAlgorithm for p256::NistP256 {
48+
fn sign_mechanism() -> Mechanism<'static> {
49+
Mechanism::Ecdsa
50+
}
51+
}
52+
53+
pub struct Signer<C: SignAlgorithm> {
54+
session: Session,
55+
_public_key: ObjectHandle,
56+
private_key: ObjectHandle,
57+
verifying_key: VerifyingKey<C>,
58+
}
59+
60+
impl<C: SignAlgorithm> Signer<C>
61+
where
62+
FieldBytesSize<C>: ModulusSize,
63+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
64+
{
65+
pub fn new(session: Session, label: &[u8]) -> Result<Self, Error> {
66+
// First we'll lookup a private key with that label.
67+
let template = vec![
68+
Attribute::Token(true),
69+
Attribute::Private(true),
70+
Attribute::Label(label.to_vec()),
71+
Attribute::Class(ObjectClass::PRIVATE_KEY),
72+
Attribute::KeyType(KeyType::EC),
73+
Attribute::EcParams(C::OID.to_der().unwrap()),
74+
Attribute::Sign(true),
75+
];
76+
77+
let private_key = session.find_objects(&template)?.remove(0);
78+
let attribute_pk = session.get_attributes(private_key, &[AttributeType::Id])?;
79+
80+
// Second we'll lookup a public key with the same label/ec params/ec point
81+
let mut template = vec![
82+
Attribute::Private(false),
83+
Attribute::Label(label.to_vec()),
84+
Attribute::Class(ObjectClass::PUBLIC_KEY),
85+
Attribute::KeyType(KeyType::EC),
86+
Attribute::EcParams(C::OID.to_der().unwrap()),
87+
];
88+
let mut id = None;
89+
for attribute in attribute_pk {
90+
match attribute {
91+
Attribute::Id(i) if id.is_none() => {
92+
template.push(Attribute::Id(i.clone()));
93+
id = Some(i);
94+
}
95+
_ => {}
96+
}
97+
}
98+
99+
let public_key = session.find_objects(&template)?.remove(0);
100+
let attribute_pk = session.get_attributes(public_key, &[AttributeType::EcPoint])?;
101+
102+
let mut ec_point = None;
103+
for attribute in attribute_pk {
104+
match attribute {
105+
Attribute::EcPoint(p) if ec_point.is_none() => {
106+
ec_point = Some(p);
107+
}
108+
_ => {}
109+
}
110+
}
111+
112+
let ec_point = ec_point.ok_or(Error::MissingAttribute(AttributeType::EcPoint))?;
113+
114+
// documented as "DER-encoding of ANSI X9.62 ECPoint value Q"
115+
// https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/pkcs11-spec-v3.1-os.html#_Toc111203418
116+
// https://www.rfc-editor.org/rfc/rfc5480#section-2.2
117+
let ec_point = OctetStringRef::from_der(&ec_point).unwrap();
118+
let public = PublicKey::<C>::from_sec1_bytes(ec_point.as_bytes())?;
119+
let verifying_key = public.into();
120+
121+
Ok(Self {
122+
session,
123+
private_key,
124+
_public_key: public_key,
125+
verifying_key,
126+
})
127+
}
128+
129+
pub fn into_session(self) -> Session {
130+
self.session
131+
}
132+
}
133+
134+
impl<C: SignAlgorithm> AssociatedAlgorithmIdentifier for Signer<C>
135+
where
136+
C: AssociatedOid,
137+
{
138+
type Params = ObjectIdentifier;
139+
140+
const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> =
141+
PublicKey::<C>::ALGORITHM_IDENTIFIER;
142+
}
143+
144+
impl<C: SignAlgorithm> signature::Keypair for Signer<C> {
145+
type VerifyingKey = VerifyingKey<C>;
146+
147+
fn verifying_key(&self) -> Self::VerifyingKey {
148+
self.verifying_key
149+
}
150+
}
151+
152+
impl<C: SignAlgorithm> signature::Signer<Signature<C>> for Signer<C>
153+
where
154+
<<C as ecdsa::elliptic_curve::Curve>::FieldBytesSize as Add>::Output: ArrayLength<u8>,
155+
{
156+
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, signature::Error> {
157+
println!("try sign");
158+
159+
let msg = C::Digest::digest(msg);
160+
161+
let bytes = self
162+
.session
163+
.sign(&C::sign_mechanism(), self.private_key, &msg)
164+
.map_err(Error::Cryptoki)
165+
.map_err(Box::new)
166+
.map_err(signature::Error::from_source)?;
167+
168+
let signature = Signature::try_from(bytes.as_slice())?;
169+
170+
Ok(signature)
171+
}
172+
}
173+
174+
impl<C: SignAlgorithm> SignatureAlgorithmIdentifier for Signer<C>
175+
where
176+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
177+
FieldBytesSize<C>: ModulusSize,
178+
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
179+
{
180+
type Params = AnyRef<'static>;
181+
182+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
183+
Signature::<C>::ALGORITHM_IDENTIFIER;
184+
}

cryptoki-rustcrypto/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2023 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
pub mod ecdsa;
45
pub mod rsa;

cryptoki-rustcrypto/tests/ecdsa.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
mod common;
5+
6+
use crate::common::USER_PIN;
7+
use common::init_pins;
8+
use cryptoki::{
9+
mechanism::Mechanism,
10+
object::{Attribute, KeyType},
11+
session::UserType,
12+
types::AuthPin,
13+
};
14+
use cryptoki_rustcrypto::ecdsa;
15+
use der::Encode;
16+
use p256::pkcs8::AssociatedOid;
17+
use serial_test::serial;
18+
use signature::{Keypair, Signer, Verifier};
19+
use testresult::TestResult;
20+
21+
#[test]
22+
#[serial]
23+
fn sign_verify() -> TestResult {
24+
let (pkcs11, slot) = init_pins();
25+
26+
// open a session
27+
let session = pkcs11.open_rw_session(slot)?;
28+
29+
// log in the session
30+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
31+
32+
// get mechanism
33+
let mechanism = Mechanism::EccKeyPairGen;
34+
35+
let secp256r1_oid: Vec<u8> = p256::NistP256::OID.to_der().unwrap();
36+
println!("oid: {:x?}", secp256r1_oid);
37+
38+
let label = b"demo-signer";
39+
40+
// pub key template
41+
let pub_key_template = vec![
42+
Attribute::Token(true),
43+
Attribute::Private(false),
44+
Attribute::KeyType(KeyType::EC),
45+
Attribute::Verify(true),
46+
Attribute::EcParams(secp256r1_oid.clone()),
47+
Attribute::Label(label.to_vec()),
48+
];
49+
50+
// priv key template
51+
let priv_key_template = vec![
52+
Attribute::Token(true),
53+
Attribute::Private(true),
54+
//Attribute::KeyType(KeyType::EC),
55+
//Attribute::EcParams(secp256r1_oid),
56+
//Attribute::Sensitive(true),
57+
//Attribute::Extractable(false),
58+
Attribute::Sign(true),
59+
Attribute::Label(label.to_vec()),
60+
];
61+
62+
// generate a key pair
63+
let (public, private) =
64+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
65+
66+
// data to sign
67+
let data = [0xFF, 0x55, 0xDD];
68+
69+
let signer =
70+
ecdsa::Signer::<p256::NistP256>::new(session, label).expect("Lookup keys from HSM");
71+
72+
let signature = signer.sign(&data);
73+
74+
let verifying_key = signer.verifying_key();
75+
verifying_key.verify(&data, &signature)?;
76+
77+
let session = signer.into_session();
78+
79+
// delete keys
80+
session.destroy_object(public)?;
81+
session.destroy_object(private)?;
82+
83+
Ok(())
84+
}

0 commit comments

Comments
 (0)