Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7f68cb7

Browse files
committedNov 23, 2023
WIP: ecdsa support
Signed-off-by: Arthur Gautier <[email protected]>
1 parent a3f5248 commit 7f68cb7

File tree

5 files changed

+394
-2
lines changed

5 files changed

+394
-2
lines changed
 

‎Cargo.lock

+133-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎cryptoki-rustcrypto/Cargo.toml

+3
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

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

‎cryptoki-rustcrypto/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod ecdsa;
12
pub mod rsa;

‎cryptoki-rustcrypto/tests/ecdsa.rs

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

0 commit comments

Comments
 (0)
Please sign in to comment.