Skip to content

Commit

Permalink
Add AES-GCM mechanism
Browse files Browse the repository at this point in the history
Co-authored-by: Wiktor Kwapisiewicz <[email protected]>
Signed-off-by: Wiktor Kwapisiewicz <[email protected]>
  • Loading branch information
2 people authored and ionut-arm committed May 30, 2023
1 parent 6855e9a commit a1d7d18
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
91 changes: 91 additions & 0 deletions cryptoki/src/mechanism/aead.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2023 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
//! AEAD block cipher mechanism types

use crate::types::Ulong;
use cryptoki_sys::*;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::slice;

/// Parameters for AES-GCM.
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct GcmParams<'a> {
inner: CK_GCM_PARAMS,
_marker: PhantomData<&'a [u8]>,
}

impl<'a> GcmParams<'a> {
/// Construct GCM parameters.
///
/// # Arguments
///
/// `iv` - The initialization vector. This must be non-empty. In PKCS#11
/// 2.40, the maximum length of the IV is 256 bytes. A 12-byte IV may be
/// processed more efficiently than other lengths.
///
/// `aad` - The additional authenticated data. This data is authenticated
/// but not encrypted. This may be between 0 and 2^32-1 bytes.
///
/// `tag_bits` - The length, in **bits**, of the authentication tag. Must
/// be between 0 and 128. The tag is appended to the end of the
/// ciphertext.
///
/// # Panics
///
/// This function panics if the length of `iv` or `aad` does not
/// fit into an [Ulong].
pub fn new(iv: &'a [u8], aad: &'a [u8], tag_bits: Ulong) -> Self {
// The ulIvBits parameter seems to be missing from the 2.40 spec,
// although it is included in the header file. In [1], OASIS clarified
// that the header file is normative. In 3.0, they added the parameter
// to the spec, but it seems to be unused:
//
// > Do not use ulIvBits to specify the length of the initialization
// > vector, but ulIvLen instead.
//
// Further, in v3.0, the IV is permitted to be up to 2^32-1 bytes,
// which would cause ulIvBits to overflow on platforms where
// sizeof(CK_ULONG) = 4.
//
// In light of all this, we include ulIvBits in the struct, but always
// set it to zero.
//
// [1]: https://www.oasis-open.org/committees/document.php?document_id=58032&wg_abbrev=pkcs11
GcmParams {
inner: CK_GCM_PARAMS {
pIv: iv.as_ptr() as *mut _,
ulIvLen: iv
.len()
.try_into()
.expect("iv length does not fit in CK_ULONG"),
ulIvBits: 0,
pAAD: aad.as_ptr() as *mut _,
ulAADLen: aad
.len()
.try_into()
.expect("aad length does not fit in CK_ULONG"),
ulTagBits: tag_bits.into(),
},
_marker: PhantomData,
}
}

/// The initialization vector.
pub fn iv(&self) -> &'a [u8] {
// SAFETY: In the constructor, the IV always comes from a &'a [u8]
unsafe { slice::from_raw_parts(self.inner.pIv, self.inner.ulIvLen as _) }
}

/// The additional authenticated data.
pub fn aad(&self) -> &'a [u8] {
// SAEFTY: In the constructor, the AAD always comes from a &'a [u8]
unsafe { slice::from_raw_parts(self.inner.pAAD, self.inner.ulAADLen as _) }
}

/// The length, in bits, of the authentication tag.
pub fn tag_bits(&self) -> Ulong {
self.inner.ulTagBits.into()
}
}
13 changes: 13 additions & 0 deletions cryptoki/src/mechanism/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
//! Data types for mechanisms

pub mod aead;
pub mod elliptic_curve;
mod mechanism_info;
pub mod rsa;
Expand Down Expand Up @@ -60,6 +61,8 @@ impl MechanismType {
pub const AES_KEY_WRAP_PAD: MechanismType = MechanismType {
val: CKM_AES_KEY_WRAP_PAD,
};
/// AES-GCM mechanism
pub const AES_GCM: MechanismType = MechanismType { val: CKM_AES_GCM };

// RSA
/// PKCS #1 RSA key pair generation mechanism
Expand Down Expand Up @@ -684,6 +687,8 @@ pub enum Mechanism<'a> {
AesKeyWrap,
/// AES key wrap with padding block
AesKeyWrapPad,
/// AES-GCM mechanism
AesGcm(aead::GcmParams<'a>),

// RSA
/// PKCS #1 RSA key pair generation mechanism
Expand Down Expand Up @@ -823,6 +828,7 @@ impl Mechanism<'_> {
Mechanism::AesCbcPad(_) => MechanismType::AES_CBC_PAD,
Mechanism::AesKeyWrap => MechanismType::AES_KEY_WRAP,
Mechanism::AesKeyWrapPad => MechanismType::AES_KEY_WRAP_PAD,
Mechanism::AesGcm(_) => MechanismType::AES_GCM,

Mechanism::RsaPkcsKeyPairGen => MechanismType::RSA_PKCS_KEY_PAIR_GEN,
Mechanism::RsaPkcs => MechanismType::RSA_PKCS,
Expand Down Expand Up @@ -884,6 +890,13 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
| Mechanism::Des3Cbc(params)
| Mechanism::DesCbcPad(params)
| Mechanism::Des3CbcPad(params) => make_mechanism(mechanism, params),
Mechanism::AesGcm(params) => CK_MECHANISM {
mechanism,
pParameter: params as *const _ as *mut c_void,
ulParameterLen: std::mem::size_of::<CK_GCM_PARAMS>()
.try_into()
.expect("usize can not fit in CK_ULONG"),
},
Mechanism::RsaPkcsPss(params)
| Mechanism::Sha1RsaPkcsPss(params)
| Mechanism::Sha256RsaPkcsPss(params)
Expand Down
65 changes: 65 additions & 0 deletions cryptoki/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod common;
use crate::common::{SO_PIN, USER_PIN};
use common::init_pins;
use cryptoki::error::{Error, RvError};
use cryptoki::mechanism::aead::GcmParams;
use cryptoki::mechanism::Mechanism;
use cryptoki::object::{Attribute, AttributeInfo, AttributeType, KeyType, ObjectClass};
use cryptoki::session::{SessionState, UserType};
Expand Down Expand Up @@ -938,3 +939,67 @@ fn sha256_digest() -> TestResult {

Ok(())
}

#[test]
#[serial]
// Currently empty AAD crashes SoftHSM, see: https://github.com/opendnssec/SoftHSMv2/issues/605
#[ignore]
fn aes_gcm_no_aad() -> TestResult {
// Encrypt two blocks of zeros with AES-128-GCM
let key = vec![0; 16];
let iv = [0; 12];
let aad = [];
let plain = [0; 32];
let expected_cipher_and_tag = [
0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe,
0x78, 0xf7, 0x95, 0xaa, 0xab, 0x49, 0x4b, 0x59, 0x23, 0xf7, 0xfd, 0x89, 0xff, 0x94, 0x8b,
0xc1, 0xe0, 0x40, 0x49, 0x0a, 0xf4, 0x80, 0x56, 0x06, 0xb2, 0xa3, 0xa2, 0xe7, 0x93,
];

let (pkcs11, slot) = init_pins();
let session = pkcs11.open_rw_session(slot)?;
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

let template = [
Attribute::Class(ObjectClass::SECRET_KEY),
Attribute::KeyType(KeyType::AES),
Attribute::Value(key),
Attribute::Encrypt(true),
];
let key_handle = session.create_object(&template)?;
let mechanism = Mechanism::AesGcm(GcmParams::new(&iv, &aad, 96.into()));
let cipher_and_tag = session.encrypt(&mechanism, key_handle, &plain)?;
assert_eq!(expected_cipher_and_tag[..], cipher_and_tag[..]);
Ok(())
}

#[test]
#[serial]
fn aes_gcm_with_aad() -> TestResult {
// Encrypt a block of zeros with AES-128-GCM.
// Use another block of zeros for AAD.
let key = vec![0; 16];
let iv = [0; 12];
let aad = [0; 16];
let plain = [0; 16];
let expected_cipher_and_tag = [
0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe,
0x78, 0xd2, 0x4e, 0x50, 0x3a, 0x1b, 0xb0, 0x37, 0x07, 0x1c, 0x71, 0xb3, 0x5d,
];

let (pkcs11, slot) = init_pins();
let session = pkcs11.open_rw_session(slot)?;
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

let template = [
Attribute::Class(ObjectClass::SECRET_KEY),
Attribute::KeyType(KeyType::AES),
Attribute::Value(key),
Attribute::Encrypt(true),
];
let key_handle = session.create_object(&template)?;
let mechanism = Mechanism::AesGcm(GcmParams::new(&iv, &aad, 96.into()));
let cipher_and_tag = session.encrypt(&mechanism, key_handle, &plain)?;
assert_eq!(expected_cipher_and_tag[..], cipher_and_tag[..]);
Ok(())
}

0 comments on commit a1d7d18

Please sign in to comment.