Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions cryptoki/src/mechanism/aes_ctr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2026 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
//! AES-CTR mechanism types

use crate::error::Error;
use cryptoki_sys::*;

/// Parameters for AES-CTR.
///
/// This structure wraps a `CK_AES_CTR_PARAMS` structure.
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct AesCtrParams {
inner: CK_AES_CTR_PARAMS,
}

impl AesCtrParams {
/// Construct AES-CTR parameters.
///
/// # Arguments
///
/// `counter_bits` - The number of least significant bits of `block` that
/// make up the counter, i.e. that are incremented for every block that is
/// processed. Must be between 1 and 128.
///
/// `block` - The initial value of the counter block: the nonce, followed by
/// the initial value of the counter in the `counter_bits` least significant
/// bits.
///
/// # Errors
///
/// This function returns [`Error::InvalidValue`] if `counter_bits` is zero
Comment thread
sietseringers marked this conversation as resolved.
/// or larger than the AES block size of 128 bits.
pub fn new(counter_bits: u8, block: [u8; 16]) -> Result<Self, Error> {
if counter_bits == 0 || counter_bits > 128 {
return Err(Error::InvalidValue);
Comment thread
hug-dev marked this conversation as resolved.
}

Ok(AesCtrParams {
inner: CK_AES_CTR_PARAMS {
ulCounterBits: counter_bits.into(),
cb: block,
},
})
}
}
10 changes: 10 additions & 0 deletions cryptoki/src/mechanism/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! Data types for mechanisms

pub mod aead;
pub mod aes_ctr;
pub mod dsa;
pub mod eddsa;
pub mod ekdf;
Expand Down Expand Up @@ -1053,6 +1054,13 @@ pub enum Mechanism<'a> {
/// the plaintext to be recovered from the ciphertext. Therefore no length
/// should be provided when unwrapping keys with this mechanism.
AesCbcPad([u8; 16]),
/// AES-CTR mechanism
///
/// The parameter to this mechanism is the counter block, together with the
/// number of bits of it that make up the counter.
///
/// The plaintext may be any size; the ciphertext has the same length.
AesCtr(aes_ctr::AesCtrParams),
/// AES in ECB mode
AesEcb,
/// AES key wrap
Expand Down Expand Up @@ -1336,6 +1344,7 @@ impl Mechanism<'_> {
Mechanism::AesEcb => MechanismType::AES_ECB,
Mechanism::AesCbc(_) => MechanismType::AES_CBC,
Mechanism::AesCbcPad(_) => MechanismType::AES_CBC_PAD,
Mechanism::AesCtr(_) => MechanismType::AES_CTR,
Mechanism::AesKeyWrap => MechanismType::AES_KEY_WRAP,
Mechanism::AesKeyWrapPad => MechanismType::AES_KEY_WRAP_PAD,
Mechanism::AesGcm(_) => MechanismType::AES_GCM,
Expand Down Expand Up @@ -1461,6 +1470,7 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
Mechanism::AesCbc(params) | Mechanism::AesCbcPad(params) => {
make_mechanism(mechanism, params)
}
Mechanism::AesCtr(params) => make_mechanism(mechanism, params),
Mechanism::AesCbcEncryptData(params) => make_mechanism(mechanism, params),
Mechanism::DesCbc(params)
| Mechanism::Des3Cbc(params)
Expand Down
40 changes: 40 additions & 0 deletions cryptoki/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::thread;

use cryptoki::mechanism::aes_ctr::AesCtrParams;
use cryptoki::mechanism::ekdf::AesCbcDeriveParams;
use testresult::TestResult;

Expand Down Expand Up @@ -2332,6 +2333,45 @@ fn aes_cbc_pad_encrypt() -> TestResult {
Ok(())
}

#[test]
#[serial]
fn aes_ctr_encrypt() -> TestResult {
// Encrypt two blocks of zeros with AES-128-CTR, with a zero key and a zero
// counter block
let key = vec![0; 16];
let plain = [0; 32];
let expected_cipher = [
0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b, 0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b,
0x2e, 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7,
0x45, 0x5a,
];

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),
Attribute::Decrypt(true),
];
let key_handle = session.create_object(&template)?;
let mechanism = Mechanism::AesCtr(AesCtrParams::new(128, [0; 16])?);
let cipher = session.encrypt(&mechanism, key_handle, &plain)?;
assert_eq!(expected_cipher[..], cipher[..]);

// In CTR mode decryption is the same operation as encryption.
let decrypted = session.decrypt(&mechanism, key_handle, &cipher)?;
assert_eq!(plain[..], decrypted[..]);

session.close()?;
pkcs11.finalize()?;

Ok(())
}

#[test]
#[serial]
fn update_attributes_key() -> TestResult {
Expand Down
Loading