Skip to content

Commit 6cf3045

Browse files
committed
Add support for Message based encryption and decryption
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
1 parent 62543aa commit 6cf3045

File tree

6 files changed

+448
-1
lines changed

6 files changed

+448
-1
lines changed

cryptoki/src/mechanism/aead.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,105 @@ impl<'a> GcmParams<'a> {
8989
self.inner.ulTagBits.into()
9090
}
9191
}
92+
93+
/// The GCM generator function for the Initialization Vector
94+
#[derive(Debug, Clone, Copy)]
95+
pub enum GeneratorFunction {
96+
/// `CKG_NO_GENERATE` no IV generation is done.
97+
NoGenerate,
98+
/// `CKG_GENERATE` the non-fixed part of IV is generated internally
99+
Generate,
100+
/// `CKG_GENERATE_COUNTER` the non-fixed part of IV is generated internally by incrementing
101+
/// counter. Initially zero.
102+
GenerateCounter,
103+
/// `CKG_GENERATE_RANDOM` the non-fixed part of IV is generated internally by PRNG
104+
GenerateRandom,
105+
/// `CKG_GENERATE_COUNTER_XOR` the non-fixed part of IV xored with incrementing counter.
106+
GenerateCounterXor,
107+
}
108+
109+
/// Parameters for message based AES-GCM operations.
110+
#[derive(Debug, Copy, Clone)]
111+
#[repr(transparent)]
112+
pub struct GcmMessageParams<'a> {
113+
inner: CK_GCM_MESSAGE_PARAMS,
114+
_marker: PhantomData<&'a mut [u8]>,
115+
}
116+
117+
impl<'a> GcmMessageParams<'a> {
118+
/// Construct GCM parameters for message based operations
119+
///
120+
/// # Arguments
121+
///
122+
/// `iv` - The initialization vector. This must be non-empty. In PKCS#11
123+
/// 3.0, the maximum length of the IV is 256 bytes. A 12-byte IV may be
124+
/// processed more efficiently than other lengths.
125+
///
126+
/// `iv_fixed_bits` - number of bits of the original IV to preserve when
127+
/// generating an new IV. These bits are counted from the Most significant
128+
/// bits (to the right).
129+
///
130+
/// `iv_generator` - Function used to generate a new IV. Each IV must be
131+
/// unique for a given session.
132+
///
133+
/// `tag` - The buffer to store the tag. Either to be passed in or returned if generated by
134+
/// token.
135+
///
136+
/// # Errors
137+
/// This function returns an error if the length of `iv` does not
138+
/// fit into an [Ulong].
139+
pub fn new(
140+
iv: &'a mut [u8],
141+
iv_fixed_bits: Ulong,
142+
iv_generator: GeneratorFunction,
143+
tag: &'a mut [u8],
144+
) -> Result<Self, Error> {
145+
let tag_bits = tag.len() * 8;
146+
Ok(GcmMessageParams {
147+
inner: CK_GCM_MESSAGE_PARAMS {
148+
pIv: iv.as_mut_ptr(),
149+
ulIvLen: iv.len().try_into()?,
150+
ulIvFixedBits: iv_fixed_bits.into(),
151+
ivGenerator: match iv_generator {
152+
GeneratorFunction::NoGenerate => CKG_NO_GENERATE,
153+
GeneratorFunction::Generate => CKG_GENERATE,
154+
GeneratorFunction::GenerateCounter => CKG_GENERATE_COUNTER,
155+
GeneratorFunction::GenerateRandom => CKG_GENERATE_RANDOM,
156+
GeneratorFunction::GenerateCounterXor => CKG_GENERATE_COUNTER_XOR,
157+
},
158+
pTag: tag.as_mut_ptr(),
159+
ulTagBits: tag_bits.try_into()?,
160+
},
161+
_marker: PhantomData,
162+
})
163+
}
164+
165+
/// The initialization vector.
166+
pub fn iv(&mut self) -> &mut [u8] {
167+
// SAFETY: In the constructor, the IV always comes from a &'a mut [u8]
168+
unsafe { slice::from_raw_parts_mut(self.inner.pIv, self.inner.ulIvLen as _) }
169+
}
170+
171+
/// The length, in bits, of fixed part of the IV.
172+
pub fn iv_fixed_bits(&self) -> Ulong {
173+
self.inner.ulIvFixedBits.into()
174+
}
175+
176+
/// The IV generator.
177+
pub fn iv_generator(&self) -> GeneratorFunction {
178+
match self.inner.ivGenerator {
179+
CKG_NO_GENERATE => GeneratorFunction::NoGenerate,
180+
CKG_GENERATE => GeneratorFunction::Generate,
181+
CKG_GENERATE_COUNTER => GeneratorFunction::GenerateCounter,
182+
CKG_GENERATE_RANDOM => GeneratorFunction::GenerateRandom,
183+
CKG_GENERATE_COUNTER_XOR => GeneratorFunction::GenerateCounterXor,
184+
_ => unreachable!(),
185+
}
186+
}
187+
188+
/// The authentication tag.
189+
pub fn tag(&self) -> &'a [u8] {
190+
// SAFETY: In the constructor, the tag always comes from a &'a [u8]
191+
unsafe { slice::from_raw_parts(self.inner.pTag, (self.inner.ulTagBits / 8) as _) }
192+
}
193+
}

cryptoki/src/mechanism/mechanism_info.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ bitflags! {
2929
const EC_OID = CKF_EC_OID;
3030
const EC_UNCOMPRESS = CKF_EC_UNCOMPRESS;
3131
const EC_COMPRESS = CKF_EC_COMPRESS;
32+
const MESSAGE_ENCRYPT = CKF_MESSAGE_ENCRYPT;
33+
const MESSAGE_DECRYPT = CKF_MESSAGE_DECRYPT;
34+
const MULTI_MESSAGE = CKF_MULTI_MESSAGE;
3235
}
3336
}
3437

@@ -228,6 +231,27 @@ impl MechanismInfo {
228231
pub fn ec_compressed(&self) -> bool {
229232
self.flags.contains(MechanismInfoFlags::EC_COMPRESS)
230233
}
234+
235+
/// True if the mechanism can be used to encrypt messages
236+
///
237+
/// See [`Session::encrypt_message`](crate::session::Session::encrypt_message)
238+
pub fn message_encrypt(&self) -> bool {
239+
self.flags.contains(MechanismInfoFlags::MESSAGE_ENCRYPT)
240+
}
241+
242+
/// True if the mechanism can be used to decrypt encrypted messages
243+
///
244+
/// See [`Session::decrypt`](crate::session::Session::decrypt_message)
245+
pub fn message_decrypt(&self) -> bool {
246+
self.flags.contains(MechanismInfoFlags::MESSAGE_DECRYPT)
247+
}
248+
249+
/// True if the mechanism can be used with encrypt/decrypt_message_begin API.
250+
/// One of message_* flag must also be set.
251+
///
252+
pub fn multi_message(&self) -> bool {
253+
self.flags.contains(MechanismInfoFlags::MULTI_MESSAGE)
254+
}
231255
}
232256

233257
impl std::fmt::Display for MechanismInfo {
@@ -269,7 +293,8 @@ mod test {
269293
HW | ENCRYPT | DECRYPT | DIGEST | SIGN | SIGN_RECOVER | VERIFY | \
270294
VERIFY_RECOVER | GENERATE | GENERATE_KEY_PAIR | WRAP | UNWRAP | DERIVE | \
271295
EXTENSION | EC_F_P | EC_F_2M | EC_ECPARAMETERS | EC_NAMEDCURVE | \
272-
EC_OID | EC_UNCOMPRESS | EC_COMPRESS";
296+
EC_OID | EC_UNCOMPRESS | EC_COMPRESS | MESSAGE_ENCRYPT | MESSAGE_DECRYPT | \
297+
MULTI_MESSAGE";
273298
let all = MechanismInfoFlags::all();
274299
let observed = format!("{all:#?}");
275300
println!("{observed}");

cryptoki/src/mechanism/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,9 @@ pub enum Mechanism<'a> {
805805
AesKeyWrapPad,
806806
/// AES-GCM mechanism
807807
AesGcm(aead::GcmParams<'a>),
808+
/// AES-GCM mechanism with message based API and parameters
809+
// TODO Should we reuse the AesGcm and use Option<> to select parameter?
810+
AesGcmMessage(aead::GcmMessageParams<'a>),
808811
/// AES-CBC-ENCRYPT-DATA mechanism
809812
///
810813
/// The parameter to this mechanism is the initialization vector and the message to encrypt. These mechanisms allow
@@ -986,6 +989,7 @@ impl Mechanism<'_> {
986989
Mechanism::AesKeyWrap => MechanismType::AES_KEY_WRAP,
987990
Mechanism::AesKeyWrapPad => MechanismType::AES_KEY_WRAP_PAD,
988991
Mechanism::AesGcm(_) => MechanismType::AES_GCM,
992+
Mechanism::AesGcmMessage(_) => MechanismType::AES_GCM,
989993
Mechanism::AesCbcEncryptData(_) => MechanismType::AES_CBC_ENCRYPT_DATA,
990994
Mechanism::AesCMac => MechanismType::AES_CMAC,
991995
Mechanism::RsaPkcsKeyPairGen => MechanismType::RSA_PKCS_KEY_PAIR_GEN,
@@ -1072,6 +1076,13 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
10721076
.try_into()
10731077
.expect("usize can not fit in CK_ULONG"),
10741078
},
1079+
Mechanism::AesGcmMessage(params) => CK_MECHANISM {
1080+
mechanism,
1081+
pParameter: params as *const _ as *mut c_void,
1082+
ulParameterLen: size_of::<CK_GCM_MESSAGE_PARAMS>()
1083+
.try_into()
1084+
.expect("usize can not fit in CK_ULONG"),
1085+
},
10751086
Mechanism::RsaPkcsPss(params)
10761087
| Mechanism::Sha1RsaPkcsPss(params)
10771088
| Mechanism::Sha256RsaPkcsPss(params)
@@ -1153,3 +1164,26 @@ fn make_mechanism<T>(mechanism: CK_MECHANISM_TYPE, param: &T) -> CK_MECHANISM {
11531164
.expect("usize can not fit in CK_ULONG"),
11541165
}
11551166
}
1167+
1168+
/// Type defining a specific mechanism parameters used for message based operations
1169+
#[derive(Debug)]
1170+
pub enum MessageParam<'a> {
1171+
/// AES-GCM mechanism with message based API and parameters
1172+
AesGcmMessage(aead::GcmMessageParams<'a>),
1173+
}
1174+
1175+
impl MessageParam<'_> {
1176+
pub(crate) fn as_ptr(&self) -> *mut ::std::os::raw::c_void {
1177+
match self {
1178+
MessageParam::AesGcmMessage(param) => param as *const _ as *mut c_void,
1179+
}
1180+
}
1181+
1182+
pub(crate) fn len(&self) -> CK_ULONG {
1183+
match self {
1184+
MessageParam::AesGcmMessage(_) => size_of::<CK_GCM_MESSAGE_PARAMS>()
1185+
.try_into()
1186+
.expect("usize can not fit in CK_ULONG"),
1187+
}
1188+
}
1189+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright 2025 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! Encrypting data
4+
5+
use crate::context::Function;
6+
use crate::error::{Result, Rv};
7+
use crate::mechanism::{Mechanism, MessageParam};
8+
use crate::object::ObjectHandle;
9+
use crate::session::Session;
10+
use cryptoki_sys::*;
11+
use std::convert::TryInto;
12+
13+
impl Session {
14+
/// Prepare a session for one or more Message-based decryption using the same mechanism and key
15+
pub fn message_decrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> {
16+
let mut mechanism: CK_MECHANISM = mechanism.into();
17+
18+
unsafe {
19+
Rv::from(get_pkcs11!(self.client(), C_MessageDecryptInit)(
20+
self.handle(),
21+
&mut mechanism as CK_MECHANISM_PTR,
22+
key.handle(),
23+
))
24+
.into_result(Function::MessageDecryptInit)?;
25+
}
26+
27+
Ok(())
28+
}
29+
30+
/// Decrypts a message in single part
31+
pub fn decrypt_message(
32+
&self,
33+
param: &MessageParam,
34+
aad: &[u8],
35+
encrypted_data: &[u8],
36+
) -> Result<Vec<u8>> {
37+
let mut data_len = 0;
38+
// Get the output buffer length
39+
unsafe {
40+
Rv::from(get_pkcs11!(self.client(), C_DecryptMessage)(
41+
self.handle(),
42+
param.as_ptr(),
43+
param.len(),
44+
aad.as_ptr() as *mut u8,
45+
aad.len().try_into()?,
46+
encrypted_data.as_ptr() as *mut u8,
47+
encrypted_data.len().try_into()?,
48+
std::ptr::null_mut(),
49+
&mut data_len,
50+
))
51+
.into_result(Function::DecryptMessage)?;
52+
}
53+
54+
let mut data = vec![0; data_len.try_into()?];
55+
56+
unsafe {
57+
Rv::from(get_pkcs11!(self.client(), C_DecryptMessage)(
58+
self.handle(),
59+
param.as_ptr(),
60+
param.len(),
61+
aad.as_ptr() as *mut u8,
62+
aad.len().try_into()?,
63+
encrypted_data.as_ptr() as *mut u8,
64+
encrypted_data.len().try_into()?,
65+
data.as_mut_ptr(),
66+
&mut data_len,
67+
))
68+
.into_result(Function::DecryptMessage)?;
69+
}
70+
71+
data.resize(data_len.try_into()?, 0);
72+
73+
Ok(data)
74+
}
75+
76+
/// Begin multi-part message decryption operation
77+
pub fn decrypt_message_begin(&self, param: MessageParam, aad: &[u8]) -> Result<()> {
78+
unsafe {
79+
Rv::from(get_pkcs11!(self.client(), C_DecryptMessageBegin)(
80+
self.handle(),
81+
param.as_ptr(),
82+
param.len(),
83+
aad.as_ptr() as *mut u8,
84+
aad.len().try_into()?,
85+
))
86+
.into_result(Function::DecryptMessageBegin)
87+
}
88+
}
89+
90+
/// Continue mutli-part message decryption operation
91+
pub fn decrypt_message_next(
92+
&self,
93+
param: MessageParam,
94+
encrypted_data: &[u8],
95+
end: bool,
96+
) -> Result<Vec<u8>> {
97+
let mut data_len = 0;
98+
// Get the output buffer length
99+
unsafe {
100+
Rv::from(get_pkcs11!(self.client(), C_DecryptMessageNext)(
101+
self.handle(),
102+
param.as_ptr(),
103+
param.len(),
104+
encrypted_data.as_ptr() as *mut u8,
105+
encrypted_data.len().try_into()?,
106+
std::ptr::null_mut(),
107+
&mut data_len,
108+
if end { CKF_END_OF_MESSAGE } else { 0 },
109+
))
110+
.into_result(Function::DecryptMessageNext)?;
111+
}
112+
let mut data = vec![0; data_len.try_into()?];
113+
unsafe {
114+
Rv::from(get_pkcs11!(self.client(), C_DecryptMessageNext)(
115+
self.handle(),
116+
param.as_ptr(),
117+
param.len(),
118+
encrypted_data.as_ptr() as *mut u8,
119+
encrypted_data.len().try_into()?,
120+
data.as_mut_ptr(),
121+
&mut data_len,
122+
if end { CKF_END_OF_MESSAGE } else { 0 },
123+
))
124+
.into_result(Function::DecryptMessageNext)?;
125+
}
126+
data.resize(data_len.try_into()?, 0);
127+
128+
Ok(data)
129+
}
130+
131+
/// Finishes Message-based decryption process
132+
pub fn message_decrypt_final(&self) -> Result<()> {
133+
unsafe {
134+
Rv::from(get_pkcs11!(self.client(), C_MessageDecryptFinal)(
135+
self.handle(),
136+
))
137+
.into_result(Function::MessageDecryptFinal)?;
138+
}
139+
140+
Ok(())
141+
}
142+
}

0 commit comments

Comments
 (0)