Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding 2 new mechanisms: generic key generation and key derivation via encryption #178

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
55 changes: 55 additions & 0 deletions cryptoki/src/mechanism/ekdf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2023 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
//! Mechanisms of key derivation by data encryption
//! See: <https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/pkcs11-spec-v3.1-os.html#_Toc111203514>

use std::{convert::TryInto, marker::PhantomData, slice};

/// AES CBC derivation parameters.
///
/// The mechanisms will function by performing the encryption over the data provided using the base
/// key. The resulting cipher text shall be used to create the key value of the resulting key.
///
/// This structure wraps a `CK_AES_CBC_ENCRYPT_DATA_PARAMS` structure.
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct AesCbcDeriveParams<'a> {
inner: cryptoki_sys::CK_AES_CBC_ENCRYPT_DATA_PARAMS,

/// Marker type to ensure we don't outlive the data
_marker: PhantomData<&'a [u8]>,
}

impl<'a> AesCbcDeriveParams<'a> {
/// Construct parameters for key derivation via encryption (EKDF).
///
/// # Arguments
///
/// * `iv` - The initialization vector
///
/// * `data` - Data that will be encryption with the base key to obtain
/// the new key from the resulted cypher.
pub fn new(iv: [u8; 16], data: &'a [u8]) -> Self {
Self {
inner: cryptoki_sys::CK_AES_CBC_ENCRYPT_DATA_PARAMS {
iv,
pData: data.as_ptr() as *mut _,
length: data
.len()
.try_into()
.expect("data length does not fit in CK_ULONG"),
},
_marker: PhantomData,
}
}

/// The initialization vector.
pub fn iv(&self) -> &'a [u8] {
unsafe { slice::from_raw_parts(self.inner.iv.as_ptr(), self.inner.iv.len()) }
}

/// The data.
pub fn data(&self) -> &'a [u8] {
unsafe { slice::from_raw_parts(self.inner.pData, self.inner.length as _) }
}
}
21 changes: 19 additions & 2 deletions cryptoki/src/mechanism/mechanism_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use bitflags::bitflags;
use cryptoki_sys::*;
use std::fmt::Debug;
use std::fmt::{Debug, Formatter};

bitflags! {
struct MechanismInfoFlags: CK_FLAGS {
Expand Down Expand Up @@ -79,7 +79,6 @@ impl MechanismInfo {
}

/// True if the mechanism can be used to digest a message
///
// TODO See [`Session::digest`](crate::session::Session::digest)
pub fn digest(&self) -> bool {
self.flags.contains(MechanismInfoFlags::DIGEST)
Expand Down Expand Up @@ -219,6 +218,24 @@ impl MechanismInfo {
}
}

impl std::fmt::Display for MechanismInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#?}", self.flags)?;

if self.min_key_size == 0 && self.max_key_size == 0 {
return Ok(());
}

write!(f, ", min_key_size={}", self.min_key_size)?;

if self.max_key_size != 0 {
write!(f, ", max_key_size={}", self.max_key_size)?;
}

Ok(())
}
}

#[doc(hidden)]
impl From<CK_MECHANISM_INFO> for MechanismInfo {
fn from(val: CK_MECHANISM_INFO) -> Self {
Expand Down
48 changes: 37 additions & 11 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 ekdf;
pub mod elliptic_curve;
mod mechanism_info;
pub mod rsa;
Expand All @@ -16,6 +17,7 @@ use std::fmt::Formatter;
use std::ops::Deref;
use std::ptr::null_mut;

use crate::mechanism::rsa::PkcsOaepParams;
pub use mechanism_info::MechanismInfo;

#[derive(Copy, Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -64,6 +66,11 @@ impl MechanismType {
/// AES-GCM mechanism
pub const AES_GCM: MechanismType = MechanismType { val: CKM_AES_GCM };

/// Derivation via encryption
pub const AES_CBC_ENCRYPT_DATA: MechanismType = MechanismType {
val: CKM_AES_CBC_ENCRYPT_DATA,
};

// RSA
/// PKCS #1 RSA key pair generation mechanism
pub const RSA_PKCS_KEY_PAIR_GEN: MechanismType = MechanismType {
Expand Down Expand Up @@ -241,6 +248,10 @@ impl MechanismType {
pub const SHA512_RSA_PKCS_PSS: MechanismType = MechanismType {
val: CKM_SHA512_RSA_PKCS_PSS,
};
/// GENERIC-SECRET-KEY-GEN mechanism
pub const GENERIC_SECRET_KEY_GEN: MechanismType = MechanismType {
val: CKM_GENERIC_SECRET_KEY_GEN,
};

pub(crate) fn stringify(mech: CK_MECHANISM_TYPE) -> String {
match mech {
Expand Down Expand Up @@ -629,6 +640,7 @@ impl TryFrom<CK_MECHANISM_TYPE> for MechanismType {
fn try_from(mechanism_type: CK_MECHANISM_TYPE) -> Result<Self, Self::Error> {
match mechanism_type {
CKM_AES_KEY_GEN => Ok(MechanismType::AES_KEY_GEN),
CKM_AES_CBC_ENCRYPT_DATA => Ok(MechanismType::AES_CBC_ENCRYPT_DATA),
CKM_RSA_PKCS_KEY_PAIR_GEN => Ok(MechanismType::RSA_PKCS_KEY_PAIR_GEN),
CKM_RSA_PKCS => Ok(MechanismType::RSA_PKCS),
CKM_RSA_PKCS_PSS => Ok(MechanismType::RSA_PKCS_PSS),
Expand All @@ -648,6 +660,7 @@ impl TryFrom<CK_MECHANISM_TYPE> for MechanismType {
CKM_SHA256_RSA_PKCS => Ok(MechanismType::SHA256_RSA_PKCS),
CKM_SHA384_RSA_PKCS => Ok(MechanismType::SHA384_RSA_PKCS),
CKM_SHA512_RSA_PKCS => Ok(MechanismType::SHA512_RSA_PKCS),
CKM_GENERIC_SECRET_KEY_GEN => Ok(MechanismType::GENERIC_SECRET_KEY_GEN),
other => {
error!("Mechanism type {} is not supported.", other);
Err(Error::NotSupported)
Expand Down Expand Up @@ -689,6 +702,14 @@ pub enum Mechanism<'a> {
AesKeyWrapPad,
/// AES-GCM mechanism
AesGcm(aead::GcmParams<'a>),
/// AES-CBC-ENCRYPT-DATA mechanism
///
/// The parameter to this mechanism is the initialization vector and the message to encrypt. These mechanisms allow
/// derivation of keys using the result of an encryption operation as the key value.
///
/// For derivation, the message length must be a multiple of the block
/// size. See <https://www.cryptsoft.com/pkcs11doc/v220/>.
AesCbcEncryptData(ekdf::AesCbcDeriveParams<'a>),

// RSA
/// PKCS #1 RSA key pair generation mechanism
Expand All @@ -701,7 +722,7 @@ pub enum Mechanism<'a> {
RsaPkcsPss(rsa::PkcsPssParams),
/// Multi-purpose mechanism based on the RSA public-key cryptosystem and the OAEP block format
/// defined in PKCS #1
RsaPkcsOaep(rsa::PkcsOaepParams<'a>),
RsaPkcsOaep(PkcsOaepParams<'a>),
/// Multi-purpose mechanism based on the RSA public-key cryptosystem. This is so-called "raw"
/// RSA, as assumed in X.509.
RsaX509,
Expand Down Expand Up @@ -816,6 +837,9 @@ pub enum Mechanism<'a> {
Sha384RsaPkcsPss(rsa::PkcsPssParams),
/// SHA256-RSA-PKCS-PSS mechanism
Sha512RsaPkcsPss(rsa::PkcsPssParams),

/// GENERIC-SECRET-KEY-GEN mechanism
GenericSecretKeyGen,
}

impl Mechanism<'_> {
Expand All @@ -829,7 +853,7 @@ impl Mechanism<'_> {
Mechanism::AesKeyWrap => MechanismType::AES_KEY_WRAP,
Mechanism::AesKeyWrapPad => MechanismType::AES_KEY_WRAP_PAD,
Mechanism::AesGcm(_) => MechanismType::AES_GCM,

Mechanism::AesCbcEncryptData(_) => MechanismType::AES_CBC_ENCRYPT_DATA,
Mechanism::RsaPkcsKeyPairGen => MechanismType::RSA_PKCS_KEY_PAIR_GEN,
Mechanism::RsaPkcs => MechanismType::RSA_PKCS,
Mechanism::RsaPkcsPss(_) => MechanismType::RSA_PKCS_PSS,
Expand Down Expand Up @@ -874,6 +898,8 @@ impl Mechanism<'_> {
Mechanism::Sha256RsaPkcsPss(_) => MechanismType::SHA256_RSA_PKCS_PSS,
Mechanism::Sha384RsaPkcsPss(_) => MechanismType::SHA384_RSA_PKCS_PSS,
Mechanism::Sha512RsaPkcsPss(_) => MechanismType::SHA512_RSA_PKCS_PSS,

Mechanism::GenericSecretKeyGen => MechanismType::GENERIC_SECRET_KEY_GEN,
}
}
}
Expand All @@ -886,6 +912,7 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
Mechanism::AesCbc(params) | Mechanism::AesCbcPad(params) => {
make_mechanism(mechanism, params)
}
Mechanism::AesCbcEncryptData(params) => make_mechanism(mechanism, params),
Mechanism::DesCbc(params)
| Mechanism::Des3Cbc(params)
| Mechanism::DesCbcPad(params)
Expand Down Expand Up @@ -936,7 +963,8 @@ impl From<&Mechanism<'_>> for CK_MECHANISM {
| Mechanism::Sha224RsaPkcs
| Mechanism::Sha256RsaPkcs
| Mechanism::Sha384RsaPkcs
| Mechanism::Sha512RsaPkcs => CK_MECHANISM {
| Mechanism::Sha512RsaPkcs
| Mechanism::GenericSecretKeyGen => CK_MECHANISM {
mechanism,
pParameter: null_mut(),
ulParameterLen: 0,
Expand All @@ -961,7 +989,7 @@ fn make_mechanism<T>(mechanism: CK_MECHANISM_TYPE, param: &T) -> CK_MECHANISM {

#[cfg(feature = "psa-crypto-conversions")]
#[allow(deprecated)]
impl TryFrom<psa_crypto::types::algorithm::Algorithm> for Mechanism {
impl TryFrom<psa_crypto::types::algorithm::Algorithm> for Mechanism<'_> {
type Error = Error;

fn try_from(alg: psa_crypto::types::algorithm::Algorithm) -> Result<Self, Self::Error> {
Expand Down Expand Up @@ -989,13 +1017,11 @@ impl TryFrom<psa_crypto::types::algorithm::Algorithm> for Mechanism {
Ok(Mechanism::Ecdsa)
}
Algorithm::AsymmetricEncryption(AsymmetricEncryption::RsaOaep { hash_alg }) => {
Ok(Mechanism::RsaPkcsOaep(rsa::PkcsOaepParams {
hash_alg: Mechanism::try_from(Algorithm::from(hash_alg))?.mechanism_type(),
mgf: rsa::PkcsMgfType::from_psa_crypto_hash(hash_alg)?,
source: rsa::PkcsOaepSourceType::DATA_SPECIFIED,
source_data: std::ptr::null(),
source_data_len: 0.into(),
}))
Ok(Mechanism::RsaPkcsOaep(PkcsOaepParams::new(
Mechanism::try_from(Algorithm::from(hash_alg))?.mechanism_type(),
rsa::PkcsMgfType::from_psa_crypto_hash(hash_alg)?,
rsa::PkcsOaepSource::empty(),
)))
}
alg => {
error!("{:?} is not a supported algorithm", alg);
Expand Down
82 changes: 82 additions & 0 deletions cryptoki/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use serial_test::serial;
use std::collections::HashMap;
use std::thread;

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

#[test]
Expand Down Expand Up @@ -1105,3 +1106,84 @@ fn wait_for_slot_event() {
res
);
}

#[test]
#[serial]
fn generate_generic_secret_key() -> TestResult {
let (pkcs11, slot) = init_pins();
let session = pkcs11.open_rw_session(slot)?;
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

let key_label = Attribute::Label(b"test_generic_secret_key_gen".to_vec());
let key_template = vec![
Attribute::Class(ObjectClass::SECRET_KEY),
Attribute::KeyType(KeyType::GENERIC_SECRET),
Attribute::Token(true),
Attribute::Sensitive(true),
Attribute::Private(true),
Attribute::ValueLen(512.into()),
key_label.clone(),
];

let key = session.generate_key(&Mechanism::GenericSecretKeyGen, &key_template)?;
let attributes_result = session.find_objects(&[key_label])?.remove(0);
assert_eq!(key, attributes_result);

Ok(())
}

#[test]
#[serial]
fn ekdf_aes_cbc_encrypt_data() -> TestResult {
let (pkcs11, slot) = init_pins();
let session = pkcs11.open_rw_session(slot)?;
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;

// key template
let key_template = vec![
Attribute::Class(ObjectClass::SECRET_KEY),
Attribute::KeyType(KeyType::AES),
Attribute::Token(true),
Attribute::Sensitive(true),
Attribute::Private(true),
Attribute::ValueLen(32.into()),
Attribute::Derive(true),
];

// generate master key
let master_key_label = Attribute::Label(b"test_aes_cbc_encrypt_data_master_key".to_vec());
let mut master_key_template = key_template.clone();
master_key_template.insert(0, master_key_label.clone());

let master_key = session.generate_key(&Mechanism::AesKeyGen, &master_key_template)?;
assert_eq!(
master_key,
session.find_objects(&[master_key_label])?.remove(0)
);

// generate a derived pair
let derived_key_label = Attribute::Label(b"test_aes_cbc_encrypt_data_child_key".to_vec());
let mut derived_key_template = key_template.clone();
derived_key_template.insert(0, derived_key_label.clone());

// ============================================== IMPORTANT ==============================================
// When using this derivation method in production, be aware that it's better to keep first bytes of data
// filled with actual data (e.g., derivation path) - this shall cause CBC mode to propagate randomness to
// remaining 128 bit-wide AES blocks.
// Otherwise, if filling only last bytes, you are risking to keep first N of 128 bit-wide chunks of your
// derived private key the same for all child keys. If deriving a key for 256-bit AES, this means half of
// the key to be static.
// =======================================================================================================
let aes_cbc_derive_params = AesCbcDeriveParams::new([0u8; 16], [1u8; 32].as_slice());
let derived_key = session.derive_key(
&Mechanism::AesCbcEncryptData(aes_cbc_derive_params),
master_key,
&derived_key_template,
)?;
assert_eq!(
derived_key,
session.find_objects(&[derived_key_label])?.remove(0)
);

Ok(())
}
Loading