Skip to content

Commit a1d7d18

Browse files
jhagborgftxwiktor-k
authored andcommitted
Add AES-GCM mechanism
Co-authored-by: Wiktor Kwapisiewicz <[email protected]> Signed-off-by: Wiktor Kwapisiewicz <[email protected]>
1 parent 6855e9a commit a1d7d18

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

cryptoki/src/mechanism/aead.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2023 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//! AEAD block cipher mechanism types
4+
5+
use crate::types::Ulong;
6+
use cryptoki_sys::*;
7+
use std::convert::TryInto;
8+
use std::marker::PhantomData;
9+
use std::slice;
10+
11+
/// Parameters for AES-GCM.
12+
#[derive(Debug, Clone, Copy)]
13+
#[repr(transparent)]
14+
pub struct GcmParams<'a> {
15+
inner: CK_GCM_PARAMS,
16+
_marker: PhantomData<&'a [u8]>,
17+
}
18+
19+
impl<'a> GcmParams<'a> {
20+
/// Construct GCM parameters.
21+
///
22+
/// # Arguments
23+
///
24+
/// `iv` - The initialization vector. This must be non-empty. In PKCS#11
25+
/// 2.40, the maximum length of the IV is 256 bytes. A 12-byte IV may be
26+
/// processed more efficiently than other lengths.
27+
///
28+
/// `aad` - The additional authenticated data. This data is authenticated
29+
/// but not encrypted. This may be between 0 and 2^32-1 bytes.
30+
///
31+
/// `tag_bits` - The length, in **bits**, of the authentication tag. Must
32+
/// be between 0 and 128. The tag is appended to the end of the
33+
/// ciphertext.
34+
///
35+
/// # Panics
36+
///
37+
/// This function panics if the length of `iv` or `aad` does not
38+
/// fit into an [Ulong].
39+
pub fn new(iv: &'a [u8], aad: &'a [u8], tag_bits: Ulong) -> Self {
40+
// The ulIvBits parameter seems to be missing from the 2.40 spec,
41+
// although it is included in the header file. In [1], OASIS clarified
42+
// that the header file is normative. In 3.0, they added the parameter
43+
// to the spec, but it seems to be unused:
44+
//
45+
// > Do not use ulIvBits to specify the length of the initialization
46+
// > vector, but ulIvLen instead.
47+
//
48+
// Further, in v3.0, the IV is permitted to be up to 2^32-1 bytes,
49+
// which would cause ulIvBits to overflow on platforms where
50+
// sizeof(CK_ULONG) = 4.
51+
//
52+
// In light of all this, we include ulIvBits in the struct, but always
53+
// set it to zero.
54+
//
55+
// [1]: https://www.oasis-open.org/committees/document.php?document_id=58032&wg_abbrev=pkcs11
56+
GcmParams {
57+
inner: CK_GCM_PARAMS {
58+
pIv: iv.as_ptr() as *mut _,
59+
ulIvLen: iv
60+
.len()
61+
.try_into()
62+
.expect("iv length does not fit in CK_ULONG"),
63+
ulIvBits: 0,
64+
pAAD: aad.as_ptr() as *mut _,
65+
ulAADLen: aad
66+
.len()
67+
.try_into()
68+
.expect("aad length does not fit in CK_ULONG"),
69+
ulTagBits: tag_bits.into(),
70+
},
71+
_marker: PhantomData,
72+
}
73+
}
74+
75+
/// The initialization vector.
76+
pub fn iv(&self) -> &'a [u8] {
77+
// SAFETY: In the constructor, the IV always comes from a &'a [u8]
78+
unsafe { slice::from_raw_parts(self.inner.pIv, self.inner.ulIvLen as _) }
79+
}
80+
81+
/// The additional authenticated data.
82+
pub fn aad(&self) -> &'a [u8] {
83+
// SAEFTY: In the constructor, the AAD always comes from a &'a [u8]
84+
unsafe { slice::from_raw_parts(self.inner.pAAD, self.inner.ulAADLen as _) }
85+
}
86+
87+
/// The length, in bits, of the authentication tag.
88+
pub fn tag_bits(&self) -> Ulong {
89+
self.inner.ulTagBits.into()
90+
}
91+
}

cryptoki/src/mechanism/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
//! Data types for mechanisms
44
5+
pub mod aead;
56
pub mod elliptic_curve;
67
mod mechanism_info;
78
pub mod rsa;
@@ -60,6 +61,8 @@ impl MechanismType {
6061
pub const AES_KEY_WRAP_PAD: MechanismType = MechanismType {
6162
val: CKM_AES_KEY_WRAP_PAD,
6263
};
64+
/// AES-GCM mechanism
65+
pub const AES_GCM: MechanismType = MechanismType { val: CKM_AES_GCM };
6366

6467
// RSA
6568
/// PKCS #1 RSA key pair generation mechanism
@@ -684,6 +687,8 @@ pub enum Mechanism<'a> {
684687
AesKeyWrap,
685688
/// AES key wrap with padding block
686689
AesKeyWrapPad,
690+
/// AES-GCM mechanism
691+
AesGcm(aead::GcmParams<'a>),
687692

688693
// RSA
689694
/// PKCS #1 RSA key pair generation mechanism
@@ -823,6 +828,7 @@ impl Mechanism<'_> {
823828
Mechanism::AesCbcPad(_) => MechanismType::AES_CBC_PAD,
824829
Mechanism::AesKeyWrap => MechanismType::AES_KEY_WRAP,
825830
Mechanism::AesKeyWrapPad => MechanismType::AES_KEY_WRAP_PAD,
831+
Mechanism::AesGcm(_) => MechanismType::AES_GCM,
826832

827833
Mechanism::RsaPkcsKeyPairGen => MechanismType::RSA_PKCS_KEY_PAIR_GEN,
828834
Mechanism::RsaPkcs => MechanismType::RSA_PKCS,
@@ -884,6 +890,13 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
884890
| Mechanism::Des3Cbc(params)
885891
| Mechanism::DesCbcPad(params)
886892
| Mechanism::Des3CbcPad(params) => make_mechanism(mechanism, params),
893+
Mechanism::AesGcm(params) => CK_MECHANISM {
894+
mechanism,
895+
pParameter: params as *const _ as *mut c_void,
896+
ulParameterLen: std::mem::size_of::<CK_GCM_PARAMS>()
897+
.try_into()
898+
.expect("usize can not fit in CK_ULONG"),
899+
},
887900
Mechanism::RsaPkcsPss(params)
888901
| Mechanism::Sha1RsaPkcsPss(params)
889902
| Mechanism::Sha256RsaPkcsPss(params)

cryptoki/tests/basic.rs

+65
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod common;
55
use crate::common::{SO_PIN, USER_PIN};
66
use common::init_pins;
77
use cryptoki::error::{Error, RvError};
8+
use cryptoki::mechanism::aead::GcmParams;
89
use cryptoki::mechanism::Mechanism;
910
use cryptoki::object::{Attribute, AttributeInfo, AttributeType, KeyType, ObjectClass};
1011
use cryptoki::session::{SessionState, UserType};
@@ -938,3 +939,67 @@ fn sha256_digest() -> TestResult {
938939

939940
Ok(())
940941
}
942+
943+
#[test]
944+
#[serial]
945+
// Currently empty AAD crashes SoftHSM, see: https://github.com/opendnssec/SoftHSMv2/issues/605
946+
#[ignore]
947+
fn aes_gcm_no_aad() -> TestResult {
948+
// Encrypt two blocks of zeros with AES-128-GCM
949+
let key = vec![0; 16];
950+
let iv = [0; 12];
951+
let aad = [];
952+
let plain = [0; 32];
953+
let expected_cipher_and_tag = [
954+
0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe,
955+
0x78, 0xf7, 0x95, 0xaa, 0xab, 0x49, 0x4b, 0x59, 0x23, 0xf7, 0xfd, 0x89, 0xff, 0x94, 0x8b,
956+
0xc1, 0xe0, 0x40, 0x49, 0x0a, 0xf4, 0x80, 0x56, 0x06, 0xb2, 0xa3, 0xa2, 0xe7, 0x93,
957+
];
958+
959+
let (pkcs11, slot) = init_pins();
960+
let session = pkcs11.open_rw_session(slot)?;
961+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
962+
963+
let template = [
964+
Attribute::Class(ObjectClass::SECRET_KEY),
965+
Attribute::KeyType(KeyType::AES),
966+
Attribute::Value(key),
967+
Attribute::Encrypt(true),
968+
];
969+
let key_handle = session.create_object(&template)?;
970+
let mechanism = Mechanism::AesGcm(GcmParams::new(&iv, &aad, 96.into()));
971+
let cipher_and_tag = session.encrypt(&mechanism, key_handle, &plain)?;
972+
assert_eq!(expected_cipher_and_tag[..], cipher_and_tag[..]);
973+
Ok(())
974+
}
975+
976+
#[test]
977+
#[serial]
978+
fn aes_gcm_with_aad() -> TestResult {
979+
// Encrypt a block of zeros with AES-128-GCM.
980+
// Use another block of zeros for AAD.
981+
let key = vec![0; 16];
982+
let iv = [0; 12];
983+
let aad = [0; 16];
984+
let plain = [0; 16];
985+
let expected_cipher_and_tag = [
986+
0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe,
987+
0x78, 0xd2, 0x4e, 0x50, 0x3a, 0x1b, 0xb0, 0x37, 0x07, 0x1c, 0x71, 0xb3, 0x5d,
988+
];
989+
990+
let (pkcs11, slot) = init_pins();
991+
let session = pkcs11.open_rw_session(slot)?;
992+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
993+
994+
let template = [
995+
Attribute::Class(ObjectClass::SECRET_KEY),
996+
Attribute::KeyType(KeyType::AES),
997+
Attribute::Value(key),
998+
Attribute::Encrypt(true),
999+
];
1000+
let key_handle = session.create_object(&template)?;
1001+
let mechanism = Mechanism::AesGcm(GcmParams::new(&iv, &aad, 96.into()));
1002+
let cipher_and_tag = session.encrypt(&mechanism, key_handle, &plain)?;
1003+
assert_eq!(expected_cipher_and_tag[..], cipher_and_tag[..]);
1004+
Ok(())
1005+
}

0 commit comments

Comments
 (0)