Skip to content

Commit 489e10d

Browse files
committed
Implement 'sign[_final]_into()'
These '_into()' variants can be used when the caller knows the expected buffer size, and has allocated a buffer for the signature themselves. It requires fewer calls into the underlying library and can save on heap allocations. Signed-off-by: arya dradjica <arya@nlnetlabs.nl>
1 parent f5a650d commit 489e10d

2 files changed

Lines changed: 175 additions & 15 deletions

File tree

cryptoki/src/session/signing_macing.rs

Lines changed: 140 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,38 @@
33
//! Signing and authentication functions
44
55
use crate::context::Function;
6+
#[cfg(doc)]
7+
use crate::error::RvError;
68
use crate::error::{Result, Rv};
79
use crate::mechanism::Mechanism;
810
use crate::object::ObjectHandle;
911
use crate::session::Session;
1012
use cryptoki_sys::*;
1113
use std::convert::TryInto;
1214

15+
/// # Generating and Verifying Signatures
16+
///
17+
/// Several functions are provided for signing data and verifying signatures.
18+
/// This includes message authentication codes (MACs). The signed data can be
19+
/// provided in one-shot and streaming modes.
1320
impl Session {
14-
/// Sign data in single-part
21+
/// Sign data in one-shot mode.
22+
///
23+
/// `data` should be a byte sequence representing the input message. It will
24+
/// be signed using the specified key, and the resulting signature will be
25+
/// returned in a `Vec`.
26+
///
27+
/// Use [`Self::sign_into()`] if (an upper bound for) the size of the
28+
/// signature is known, to avoid the heap allocation of `Vec`. Use
29+
/// [`Self::sign_init()`] etc. if the input data is being streamed (i.e. it
30+
/// is not all immediately available).
1531
pub fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result<Vec<u8>> {
16-
let mut mechanism: CK_MECHANISM = mechanism.into();
1732
let mut signature_len = 0;
1833

19-
unsafe {
20-
Rv::from(get_pkcs11!(self.client(), C_SignInit)(
21-
self.handle(),
22-
&mut mechanism as CK_MECHANISM_PTR,
23-
key.handle(),
24-
))
25-
.into_result(Function::SignInit)?;
26-
}
34+
// Initialize the signing operation.
35+
self.sign_init(mechanism, key)?;
2736

28-
// Get the output buffer length
37+
// Get the output buffer length.
2938
unsafe {
3039
Rv::from(get_pkcs11!(self.client(), C_Sign)(
3140
self.handle(),
@@ -37,9 +46,10 @@ impl Session {
3746
.into_result(Function::Sign)?;
3847
}
3948

49+
// Allocate the output buffer.
4050
let mut signature = vec![0; signature_len.try_into()?];
4151

42-
//TODO: we should add a new error instead of those unwrap!
52+
// Perform the actual signing.
4353
unsafe {
4454
Rv::from(get_pkcs11!(self.client(), C_Sign)(
4555
self.handle(),
@@ -51,11 +61,68 @@ impl Session {
5161
.into_result(Function::Sign)?;
5262
}
5363

64+
// Limit the output buffer to the size of the generated signature.
5465
signature.truncate(signature_len.try_into()?);
5566

5667
Ok(signature)
5768
}
5869

70+
/// Sign data into the given buffer.
71+
///
72+
/// `data` should be a byte sequence representing the input message. It will
73+
/// be signed using the specified key, and the resulting signature will be
74+
/// written to the buffer `sig`.
75+
///
76+
/// `sig` should be large enough to store the signature. The number of
77+
/// filled bytes will be returned, such that `sig[0..size]` contains the
78+
/// prepared signature. `sig[size..]` might be modified.
79+
///
80+
/// Use [`Self::sign()`] if (an upper bound for) the size of the signature
81+
/// is not known. Use [`Self::sign_init()`] etc. if the input data is being
82+
/// streamed (i.e. it is not all immediately available).
83+
///
84+
/// ## Errors
85+
///
86+
/// Returns [`RvError::BufferTooSmall`] if the generated signature does not
87+
/// fit in `sig`. `sig` might be modified. The size of the actual signature
88+
/// is **not** returned. This method should only be used if the caller knows
89+
/// an upper bound for the signature size.
90+
pub fn sign_into(
91+
&self,
92+
mechanism: &Mechanism,
93+
key: ObjectHandle,
94+
data: &[u8],
95+
sig: &mut [u8],
96+
) -> Result<usize> {
97+
// The size of the signature buffer, into which 'C_Sign' will write the
98+
// size of the generated signature.
99+
let sig_buf_len = sig.len().try_into()?;
100+
let mut sig_len = sig_buf_len;
101+
102+
// Initialize the signing operation.
103+
self.sign_init(mechanism, key)?;
104+
105+
// Perform the actual signing.
106+
unsafe {
107+
Rv::from(get_pkcs11!(self.client(), C_Sign)(
108+
self.handle(),
109+
data.as_ptr() as *mut u8,
110+
data.len().try_into()?,
111+
sig.as_mut_ptr(),
112+
&mut sig_len,
113+
))
114+
.into_result(Function::Sign)?;
115+
}
116+
117+
assert!(
118+
sig_len <= sig_buf_len,
119+
"'C_Sign' succeeded but increased 'sig_len', possibly indicating out-of-bounds accesses"
120+
);
121+
122+
// NOTE: As checked above, 'sig_len <= sig_buf_len <= usize::MAX'.
123+
Ok(sig_len as usize)
124+
}
125+
59126
/// Starts new multi-part signing operation
60127
pub fn sign_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> {
61128
let mut mechanism: CK_MECHANISM = mechanism.into();
@@ -87,12 +154,20 @@ impl Session {
87154
Ok(())
88155
}
89156

90-
/// Finalizes ongoing multi-part signing operation,
91-
/// returning the signature
157+
/// Complete an ongoing streaming signing operation.
158+
///
159+
/// This must be preceded by [`Self::sign_init()`] and zero or more calls
160+
/// to [`Self::sign_update()`]. This method will terminate the multi-part
161+
/// signing operation. The resulting signature will be returned in a `Vec`.
162+
///
163+
/// Use [`Self::sign_final_into()`] if (an upper bound for) the size of
164+
/// the signature is known, to avoid the heap allocation of `Vec`. Use
165+
/// [`Self::sign()`] if the input data is entirely available in a single
166+
/// buffer (i.e. does not have to be streamed).
92167
pub fn sign_final(&self) -> Result<Vec<u8>> {
93168
let mut signature_len = 0;
94169

95-
// Get the output buffer length
170+
// Get the output buffer length.
96171
unsafe {
97172
Rv::from(get_pkcs11!(self.client(), C_SignFinal)(
98173
self.handle(),
@@ -102,8 +177,10 @@ impl Session {
102177
.into_result(Function::SignFinal)?;
103178
}
104179

180+
// Allocate the output buffer.
105181
let mut signature = vec![0; signature_len.try_into()?];
106182

183+
// Perform the actual signing.
107184
unsafe {
108185
Rv::from(get_pkcs11!(self.client(), C_SignFinal)(
109186
self.handle(),
@@ -113,11 +190,59 @@ impl Session {
113190
.into_result(Function::SignFinal)?;
114191
}
115192

193+
// Limit the output buffer to the size of the generated signature.
116194
signature.truncate(signature_len.try_into()?);
117195

118196
Ok(signature)
119197
}
120198

199+
/// Complete an ongoing multi-part signing operation, writing the signature
200+
/// into the given buffer.
201+
///
202+
/// This must be preceded by [`Self::sign_init()`] and zero or more calls
203+
/// to [`Self::sign_update()`]. This method will terminate the multi-part
204+
/// signing operation, and write the signature to the buffer `sig`.
205+
///
206+
/// `sig` should be large enough to store the signature. The number of
207+
/// filled bytes will be returned, such that `sig[0..size]` contains the
208+
/// prepared signature. `sig[size..]` might be modified.
209+
///
210+
/// Use [`Self::sign_final()`] if (an upper bound for) the size of the
211+
/// signature is not known. Use [`Self::sign_into()`] if the input data
212+
/// is entirely available in a single buffer (i.e. does not have to be
213+
/// streamed).
214+
///
215+
/// ## Errors
216+
///
217+
/// Returns [`RvError::BufferTooSmall`] if the generated signature does not
218+
/// fit in `sig`. `sig` might be modified. The size of the actual signature
219+
/// is **not** returned. This method should only be used if the caller knows
220+
/// an upper bound for the signature size.
221+
pub fn sign_final_into(&self, sig: &mut [u8]) -> Result<usize> {
222+
// The size of the signature buffer, into which 'C_SignFinal' will write
223+
// the size of the generated signature.
224+
let sig_buf_len = sig.len().try_into()?;
225+
let mut sig_len = sig_buf_len;
226+
227+
// Perform the underlying finalization.
228+
unsafe {
229+
Rv::from(get_pkcs11!(self.client(), C_SignFinal)(
230+
self.handle(),
231+
sig.as_mut_ptr(),
232+
&mut sig_len,
233+
))
234+
.into_result(Function::SignFinal)?;
235+
}
236+
237+
assert!(
238+
sig_len <= sig_buf_len,
239+
"'C_SignFinal' succeeded but increased 'sig_len', possibly indicating out-of-bounds accesses"
240+
);
241+
242+
// NOTE: As checked above, 'sig_len <= sig_buf_len <= usize::MAX'.
243+
Ok(sig_len as usize)
244+
}
245+
121246
/// Verify data in single-part
122247
pub fn verify(
123248
&self,

cryptoki/tests/basic.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ fn sign_verify() -> TestResult {
7575
// verify the signature
7676
session.verify(&Mechanism::RsaPkcs, public, &data, &signature)?;
7777

78+
// sign into a user-provided buffer with it
79+
let mut signature = [0u8; 2048 / 8];
80+
let sig_len = session.sign_into(&Mechanism::RsaPkcs, private, &data, &mut signature)?;
81+
82+
// verify the signature
83+
session.verify(&Mechanism::RsaPkcs, public, &data, &signature[..sig_len])?;
84+
7885
// delete keys
7986
session.destroy_object(public)?;
8087
session.destroy_object(private)?;
@@ -122,6 +129,12 @@ fn sign_verify_eddsa() -> TestResult {
122129

123130
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
124131

132+
let mut signature = [0u8; 64];
133+
let sig_len = session.sign_into(&Mechanism::Eddsa(params), private, &data, &mut signature)?;
134+
assert_eq!(sig_len, 64);
135+
136+
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
137+
125138
session.destroy_object(public)?;
126139
session.destroy_object(private)?;
127140

@@ -177,6 +190,13 @@ fn sign_verify_eddsa_with_ed25519_schemes() -> TestResult {
177190
let signature = session.sign(&Mechanism::Eddsa(params), private, &data)?;
178191

179192
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
193+
194+
let mut signature = [0u8; 64];
195+
let sig_len =
196+
session.sign_into(&Mechanism::Eddsa(params), private, &data, &mut signature)?;
197+
assert_eq!(sig_len, 64);
198+
199+
session.verify(&Mechanism::Eddsa(params), public, &data, &signature)?;
180200
}
181201

182202
session.destroy_object(public)?;
@@ -285,6 +305,21 @@ fn sign_verify_multipart() -> TestResult {
285305
}
286306
session.verify_final(&signature)?;
287307

308+
// Sign data into a user-provided buffer
309+
session.sign_init(&Mechanism::Sha256RsaPkcs, priv_key)?;
310+
for part in data.chunks(3) {
311+
session.sign_update(part)?;
312+
}
313+
let mut signature = [0u8; 2048 / 8];
314+
let sig_len = session.sign_final_into(&mut signature)?;
315+
316+
// Verify signature from the user-provided buffer
317+
session.verify_init(&Mechanism::Sha256RsaPkcs, pub_key)?;
318+
for part in data.chunks(3) {
319+
session.verify_update(part)?;
320+
}
321+
session.verify_final(&signature[..sig_len])?;
322+
288323
// Delete keys
289324
session.destroy_object(pub_key)?;
290325
session.destroy_object(priv_key)?;

0 commit comments

Comments
 (0)