Skip to content

Commit ff533d8

Browse files
authored
Merge pull request #244 from mcaneris/main
feat: revise EdDSA mechanism to support optional params
2 parents 0e1bdb7 + 068bc6d commit ff533d8

File tree

3 files changed

+300
-9
lines changed

3 files changed

+300
-9
lines changed

cryptoki/src/mechanism/eddsa.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
//! EdDSA mechanism types
2+
3+
use cryptoki_sys::*;
4+
use std::{convert::TryInto, ffi::c_void, marker::PhantomData, ptr::null_mut};
5+
6+
/// EdDSA signature schemes.
7+
///
8+
/// The EdDSA mechanism, denoted CKM_EDDSA, is a mechanism for
9+
/// single-part and multipart signatures and verification for
10+
/// EdDSA. This mechanism implements the five EdDSA signature
11+
/// schemes defined in RFC 8032 and RFC 8410.
12+
///
13+
/// For curves according to RFC 8032, this mechanism has an
14+
/// optional parameter, a CK_EDDSA_PARAMS structure.
15+
///
16+
/// | Signature Scheme | Mechanism Param | phFlag | Context Data |
17+
/// |------------------|-----------------|--------|--------------|
18+
/// | Ed25519 | Not Required | N/A | N/A |
19+
/// | Ed25519ctx | Required | False | Optional |
20+
/// | Ed25519ph | Required | True | Optional |
21+
/// | Ed448 | Required | False | Optional |
22+
/// | Ed448ph | Required | True | Optional |
23+
///
24+
/// The absence or presence of the parameter as well as its
25+
/// content is used to identify which signature scheme is to be
26+
/// used.
27+
#[derive(Debug, Clone, Copy)]
28+
pub enum EddsaSignatureScheme<'a> {
29+
/// Pure EdDSA mode where the scheme is implicitly defined
30+
/// by the curve.
31+
Pure,
32+
/// Ed25519 signature scheme without additional parameters.
33+
Ed25519,
34+
/// Ed25519 signature scheme with optional context-specific
35+
/// data.
36+
Ed25519ctx(&'a [u8]),
37+
/// Ed25519 signature scheme with pre-hashing and optional
38+
/// context-specific data.
39+
Ed25519ph(&'a [u8]),
40+
/// Ed448 signature scheme with optional context-specific data.
41+
Ed448(&'a [u8]),
42+
/// Ed448 signature scheme with pre-hashing and optional
43+
/// context-specific data.
44+
Ed448ph(&'a [u8]),
45+
}
46+
47+
impl EddsaSignatureScheme<'_> {
48+
/// Convert an `EddsaSignatureScheme` into the corresponding
49+
/// parameters.
50+
///
51+
/// This function prepares the appropriate parameters for
52+
/// the mechanism based on the signature scheme variant.
53+
///
54+
/// # Returns
55+
///
56+
/// A pointer the mechanism-specific parameters.
57+
///
58+
/// For `Pure` and `Ed25519`, this returns `null_mut()` as no
59+
/// additional parameters are required. For other schemes, a
60+
/// pointer to the an `CK_EDDSA_PARAMS` structure is returned.
61+
pub fn into_params(&self) -> *mut c_void {
62+
match self {
63+
EddsaSignatureScheme::Pure | EddsaSignatureScheme::Ed25519 => null_mut(),
64+
EddsaSignatureScheme::Ed448(context) | EddsaSignatureScheme::Ed25519ctx(context) => {
65+
&CK_EDDSA_PARAMS {
66+
phFlag: false.into(),
67+
pContextData: context.as_ptr() as *mut _,
68+
ulContextDataLen: context
69+
.len()
70+
.try_into()
71+
.expect("usize can not fit in CK_ULONG"),
72+
} as *const CK_EDDSA_PARAMS as *mut _
73+
}
74+
EddsaSignatureScheme::Ed448ph(context) | EddsaSignatureScheme::Ed25519ph(context) => {
75+
&CK_EDDSA_PARAMS {
76+
phFlag: true.into(),
77+
pContextData: context.as_ptr() as *mut _,
78+
ulContextDataLen: context
79+
.len()
80+
.try_into()
81+
.expect("usize can not fit in CK_ULONG"),
82+
} as *const CK_EDDSA_PARAMS as *mut _
83+
}
84+
}
85+
}
86+
}
87+
88+
/// EdDSA parameters.
89+
///
90+
/// The EdDSA mechanism, denoted CKM_EDDSA, is a mechanism for
91+
/// single-part and multipart signatures and verification for
92+
/// EdDSA. This mechanism implements the five EdDSA signature
93+
/// schemes defined in RFC 8032 and RFC 8410.
94+
///
95+
/// For curves according to RFC 8032, this mechanism has an
96+
/// optional parameter, a CK_EDDSA_PARAMS structure.
97+
///
98+
/// The absence or presence of the parameter as well as its
99+
/// content is used to identify which signature scheme is to be
100+
/// used.
101+
///
102+
/// | Signature Scheme | Mechanism Param | phFlag | Context Data |
103+
/// |------------------|-----------------|--------|--------------|
104+
/// | Ed25519 | Not Required | N/A | N/A |
105+
/// | Ed25519ctx | Required | False | Optional |
106+
/// | Ed25519ph | Required | True | Optional |
107+
/// | Ed448 | Required | False | Optional |
108+
/// | Ed448ph | Required | True | Optional |
109+
///
110+
/// This structure wraps a `CK_EDDSA_PARAMS` structure.
111+
#[derive(Copy, Debug, Clone)]
112+
#[repr(transparent)]
113+
pub struct EddsaParams<'a> {
114+
inner: Option<CK_EDDSA_PARAMS>,
115+
_marker: PhantomData<&'a [u8]>,
116+
}
117+
118+
impl EddsaParams<'_> {
119+
/// Construct EdDSA parameters.
120+
///
121+
/// # Arguments
122+
///
123+
/// * `params` - The CK_EDDSA_PARAMS structure.
124+
///
125+
/// # Returns
126+
///
127+
/// A new EddsaParams struct.
128+
pub fn new(scheme: EddsaSignatureScheme) -> Self {
129+
let params =
130+
match scheme {
131+
EddsaSignatureScheme::Pure | EddsaSignatureScheme::Ed25519 => None,
132+
EddsaSignatureScheme::Ed25519ctx(context)
133+
| EddsaSignatureScheme::Ed448(context) => Some({
134+
CK_EDDSA_PARAMS {
135+
phFlag: false.into(),
136+
pContextData: context.as_ptr() as *mut _,
137+
ulContextDataLen: context
138+
.len()
139+
.try_into()
140+
.expect("usize can not fit in CK_ULONG"),
141+
}
142+
}),
143+
EddsaSignatureScheme::Ed25519ph(context)
144+
| EddsaSignatureScheme::Ed448ph(context) => Some({
145+
CK_EDDSA_PARAMS {
146+
phFlag: true.into(),
147+
pContextData: context.as_ptr() as *mut _,
148+
ulContextDataLen: context
149+
.len()
150+
.try_into()
151+
.expect("usize can not fit in CK_ULONG"),
152+
}
153+
}),
154+
};
155+
156+
Self {
157+
inner: params,
158+
_marker: PhantomData,
159+
}
160+
}
161+
162+
/// Retrieve the inner `CK_EDDSA_PARAMS` struct, if present.
163+
///
164+
/// This method provides a reference to the `CK_EDDSA_PARAMS`
165+
/// struct encapsulated within the `EddsaParams`, if the signature
166+
/// scheme requires additional parameters.
167+
///
168+
/// # Returns
169+
///
170+
/// `Some(&CK_EDDSA_PARAMS)` if the signature scheme has associated
171+
/// parameters, otherwise `None`.
172+
pub fn inner(&self) -> Option<&CK_EDDSA_PARAMS> {
173+
self.inner.as_ref()
174+
}
175+
}

cryptoki/src/mechanism/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! Data types for mechanisms
44
55
pub mod aead;
6+
pub mod eddsa;
67
pub mod ekdf;
78
pub mod elliptic_curve;
89
pub mod hkdf;
@@ -902,11 +903,16 @@ pub enum Mechanism<'a> {
902903
EcdsaSha512,
903904
/// EDDSA mechanism
904905
///
906+
/// This mechanism has an optional parameter, a CK_EDDSA_PARAMS
907+
/// structure. The absence or presence of the parameter as well
908+
/// as its content is used to identify which signature scheme
909+
/// is to be used.
910+
///
905911
/// Note: EdDSA is not part of the PKCS#11 v2.40 standard and as
906-
/// such may not be understood by the backend. It is included here
907-
/// because some vendor implementations support it through the
908-
/// v2.40 interface.
909-
Eddsa,
912+
/// such may not be understood by some backends. It is included
913+
/// here because some vendor implementations support it through
914+
/// the v2.40 interface.
915+
Eddsa(eddsa::EddsaParams<'a>),
910916

911917
// SHA-n
912918
/// SHA-1 mechanism
@@ -1001,7 +1007,7 @@ impl Mechanism<'_> {
10011007
Mechanism::EccKeyPairGen => MechanismType::ECC_KEY_PAIR_GEN,
10021008
Mechanism::EccEdwardsKeyPairGen => MechanismType::ECC_EDWARDS_KEY_PAIR_GEN,
10031009
Mechanism::EccMontgomeryKeyPairGen => MechanismType::ECC_MONTGOMERY_KEY_PAIR_GEN,
1004-
Mechanism::Eddsa => MechanismType::EDDSA,
1010+
Mechanism::Eddsa(_) => MechanismType::EDDSA,
10051011
Mechanism::Ecdh1Derive(_) => MechanismType::ECDH1_DERIVE,
10061012
Mechanism::Ecdsa => MechanismType::ECDSA,
10071013
Mechanism::EcdsaSha1 => MechanismType::ECDSA_SHA1,
@@ -1073,6 +1079,14 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
10731079
| Mechanism::Sha512RsaPkcsPss(params) => make_mechanism(mechanism, params),
10741080
Mechanism::RsaPkcsOaep(params) => make_mechanism(mechanism, params),
10751081
Mechanism::Ecdh1Derive(params) => make_mechanism(mechanism, params),
1082+
Mechanism::Eddsa(params) => match params.inner() {
1083+
None => CK_MECHANISM {
1084+
mechanism,
1085+
pParameter: null_mut(),
1086+
ulParameterLen: 0,
1087+
},
1088+
Some(params) => make_mechanism(mechanism, params),
1089+
},
10761090
Mechanism::HkdfDerive(params) | Mechanism::HkdfData(params) => {
10771091
make_mechanism(mechanism, params)
10781092
}
@@ -1098,7 +1112,6 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
10981112
| Mechanism::EccKeyPairGen
10991113
| Mechanism::EccEdwardsKeyPairGen
11001114
| Mechanism::EccMontgomeryKeyPairGen
1101-
| Mechanism::Eddsa
11021115
| Mechanism::Ecdsa
11031116
| Mechanism::EcdsaSha1
11041117
| Mechanism::EcdsaSha224

cryptoki/tests/basic.rs

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use common::init_pins;
77
use cryptoki::context::Function;
88
use cryptoki::error::{Error, RvError};
99
use cryptoki::mechanism::aead::GcmParams;
10+
use cryptoki::mechanism::eddsa::{EddsaParams, EddsaSignatureScheme};
1011
use cryptoki::mechanism::rsa::{PkcsMgfType, PkcsOaepParams, PkcsOaepSource};
1112
use cryptoki::mechanism::{Mechanism, MechanismType};
1213
use cryptoki::object::{
@@ -72,7 +73,7 @@ fn sign_verify() -> TestResult {
7273

7374
#[test]
7475
#[serial]
75-
fn sign_verify_ed25519() -> TestResult {
76+
fn sign_verify_eddsa() -> TestResult {
7677
let (pkcs11, slot) = init_pins();
7778

7879
let session = pkcs11.open_rw_session(slot)?;
@@ -99,9 +100,111 @@ fn sign_verify_ed25519() -> TestResult {
99100

100101
let data = [0xFF, 0x55, 0xDD];
101102

102-
let signature = session.sign(&Mechanism::Eddsa, private, &data)?;
103+
let scheme = EddsaSignatureScheme::Pure;
103104

104-
session.verify(&Mechanism::Eddsa, public, &data, &signature)?;
105+
let params = EddsaParams::new(scheme);
106+
107+
let signature = session.sign(&Mechanism::Eddsa(params), private, &data)?;
108+
109+
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
110+
111+
session.destroy_object(public)?;
112+
session.destroy_object(private)?;
113+
114+
Ok(())
115+
}
116+
117+
#[test]
118+
#[serial]
119+
fn sign_verify_eddsa_with_ed25519_schemes() -> TestResult {
120+
let (pkcs11, slot) = init_pins();
121+
122+
let session = pkcs11.open_rw_session(slot)?;
123+
124+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
125+
126+
let mechanism = Mechanism::EccEdwardsKeyPairGen;
127+
128+
let pub_key_template = vec![
129+
Attribute::Token(true),
130+
Attribute::Private(false),
131+
Attribute::Verify(true),
132+
// Ed25519 OID
133+
// See: https://github.com/opendnssec/SoftHSMv2/blob/ac70dc398b236e4522101930e790008936489e2d/src/lib/test/SignVerifyTests.cpp#L173
134+
Attribute::EcParams(vec![
135+
0x13, 0x0c, 0x65, 0x64, 0x77, 0x61, 0x72, 0x64, 0x73, 0x32, 0x35, 0x35, 0x31, 0x39,
136+
]),
137+
];
138+
139+
let priv_key_template = vec![Attribute::Token(true)];
140+
141+
let (public, private) =
142+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
143+
144+
let data = [0xFF, 0x55, 0xDD];
145+
146+
let schemes = [
147+
EddsaSignatureScheme::Ed25519,
148+
EddsaSignatureScheme::Ed25519ctx(b"context"),
149+
EddsaSignatureScheme::Ed25519ph(&[]),
150+
EddsaSignatureScheme::Ed25519ph(b"context"),
151+
];
152+
153+
for scheme in schemes {
154+
let params = EddsaParams::new(scheme);
155+
156+
let signature = session.sign(&Mechanism::Eddsa(params), private, &data)?;
157+
158+
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
159+
}
160+
161+
session.destroy_object(public)?;
162+
session.destroy_object(private)?;
163+
164+
Ok(())
165+
}
166+
167+
#[test]
168+
#[serial]
169+
fn sign_verify_eddsa_with_ed448_schemes() -> TestResult {
170+
let (pkcs11, slot) = init_pins();
171+
172+
let session = pkcs11.open_rw_session(slot)?;
173+
174+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
175+
176+
let mechanism = Mechanism::EccEdwardsKeyPairGen;
177+
178+
let pub_key_template = vec![
179+
Attribute::Token(true),
180+
Attribute::Private(false),
181+
Attribute::Verify(true),
182+
// Ed448 OID
183+
// See: https://github.com/opendnssec/SoftHSMv2/blob/ac70dc398b236e4522101930e790008936489e2d/src/lib/test/SignVerifyTests.cpp#L173
184+
Attribute::EcParams(vec![
185+
0x13, 0x0a, 0x65, 0x64, 0x77, 0x61, 0x72, 0x64, 0x73, 0x34, 0x34, 0x38,
186+
]),
187+
];
188+
189+
let priv_key_template = vec![Attribute::Token(true)];
190+
191+
let (public, private) =
192+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
193+
194+
let data = [0xFF, 0x55, 0xDD];
195+
196+
let schemes = [
197+
EddsaSignatureScheme::Ed448(b"context"),
198+
EddsaSignatureScheme::Ed448ph(b"context"),
199+
];
200+
201+
for scheme in schemes {
202+
let params = EddsaParams::new(scheme);
203+
204+
let signature = session.sign(&Mechanism::Eddsa(params), private, &data)?;
205+
206+
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
207+
}
105208

106209
session.destroy_object(public)?;
107210
session.destroy_object(private)?;

0 commit comments

Comments
 (0)