Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
45 changes: 43 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,27 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"

[[package]]
name = "aes"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8"
dependencies = [
"cipher",
"cpubits",
"cpufeatures",
]

[[package]]
name = "aes-kw"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40e4645e6ea320665abf87e13821f9a37ab204b34bcb18e34e7d1dcf2366516e"
dependencies = [
"aes",
"const-oid",
]

[[package]]
name = "aho-corasick"
version = "1.1.3"
Expand Down Expand Up @@ -730,6 +751,16 @@ dependencies = [
"half",
]

[[package]]
name = "cipher"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea"
dependencies = [
"crypto-common",
"inout",
]

[[package]]
name = "clap"
version = "4.5.46"
Expand Down Expand Up @@ -1039,6 +1070,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
name = "crypto"
version = "0.0.0"
dependencies = [
"aes-kw",
"anyhow",
"der",
"getrandom 0.4.2",
Expand Down Expand Up @@ -3653,6 +3685,15 @@ dependencies = [
"serde",
]

[[package]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
"hybrid-array",
]

[[package]]
name = "input_core"
version = "0.0.0"
Expand Down Expand Up @@ -7701,12 +7742,12 @@ name = "test_igvm_agent_lib"
version = "0.0.0"
dependencies = [
"base64 0.22.1",
"crypto",
"get_resources",
"getrandom 0.4.2",
"inspect",
"openhcl_attestation_protocol",
"rsa",
"serde_json",
"sha2",
"thiserror 2.0.16",
"tracing",
"zerocopy",
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ crc32fast = { version = "1.3.2", default-features = false }
flate2 = "1.1"

# --- Cryptography & secure computing ---
aes-kw = "0.3.0"
constant_time_eq = "0.5"
der = "0.8"
getrandom = "0.4"
Expand Down
33 changes: 15 additions & 18 deletions openhcl/underhill_attestation/src/key_protector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ pub(crate) enum GetKeysFromKeyProtectorError {
#[error("failed to unwrap the ingress DEK entry with RSA-OAEP in KeyProtector")]
IngressDekRsaUnwrap(#[source] crypto::rsa::RsaError),
#[error("failed to unwrap the ingress DEK entry with AES-WRAP-WITH-PADDING in KeyProtector")]
IngressDekAesUnwrap(#[source] crypto::aes_key_wrap::AesKeyWrapError),
IngressDekAesUnwrap(#[source] crypto::aes_kwp::AesKeyWrapError),
#[error("failed to unwrap the egress DEK entry with RSA-OAEP in KeyProtector")]
EgressDekRsaUnwrap(#[source] crypto::rsa::RsaError),
#[error("failed to unwrap the egress DEK entry with AES-WRAP-WITH-PADDING in KeyProtector")]
EgressDekAesUnwrap(#[source] crypto::aes_key_wrap::AesKeyWrapError),
EgressDekAesUnwrap(#[source] crypto::aes_kwp::AesKeyWrapError),
#[error("failed to wrap the egress key with RSA-OAEP")]
EgressKeyRsaWrap(#[source] crypto::rsa::RsaError),
#[error("failed to wrap the egress key with AES-WRAP-WITH-PADDING")]
EgressKeyAesWrap(#[source] crypto::aes_key_wrap::AesKeyWrapError),
EgressKeyAesWrap(#[source] crypto::aes_kwp::AesKeyWrapError),
}

/// AES-Wrapped AES key size (32-byte with 8-byte padding)
Expand Down Expand Up @@ -163,13 +163,12 @@ impl KeyProtectorExt for KeyProtector {

// The DEK buffer should contain an AES-wrapped key.
let dek_buffer = &self.dek[ingress_idx].dek_buffer;
let aes_unwrapped_key =
crypto::aes_key_wrap::AesKeyWrap::new(&rsa_unwrapped_key)
.and_then(|kw| {
kw.unwrapper()?
.unwrap(&dek_buffer[..AES_WRAPPED_AES_KEY_LENGTH])
})
.map_err(GetKeysFromKeyProtectorError::IngressDekAesUnwrap)?;
let aes_unwrapped_key = crypto::aes_kwp::AesKeyWrap::new(&rsa_unwrapped_key)
.and_then(|kw| {
kw.unwrapper()?
.unwrap(&dek_buffer[..AES_WRAPPED_AES_KEY_LENGTH])
})
.map_err(GetKeysFromKeyProtectorError::IngressDekAesUnwrap)?;

if aes_unwrapped_key.len() != AES_GCM_KEY_LENGTH {
Err(GetKeysFromKeyProtectorError::InvalidAesUnwrapOutputSize {
Expand Down Expand Up @@ -208,7 +207,7 @@ impl KeyProtectorExt for KeyProtector {
let dek_buffer = self.dek[egress_idx].dek_buffer;
let old_egress_key = if let Some(unwrapping_key) = &des_key {
// The DEK buffer should contain an AES-wrapped key.
crypto::aes_key_wrap::AesKeyWrap::new(unwrapping_key)
crypto::aes_kwp::AesKeyWrap::new(unwrapping_key)
.and_then(|kw| {
kw.unwrapper()?
.unwrap(&dek_buffer[..AES_WRAPPED_AES_KEY_LENGTH])
Expand All @@ -235,7 +234,7 @@ impl KeyProtectorExt for KeyProtector {

let new_egress_key = if let Some(wrapping_key) = des_key {
// Create an AES wrapped key
crypto::aes_key_wrap::AesKeyWrap::new(&wrapping_key)
crypto::aes_kwp::AesKeyWrap::new(&wrapping_key)
.and_then(|kw| kw.wrapper()?.wrap(&encrypt_egress_key))
.map_err(GetKeysFromKeyProtectorError::EgressKeyAesWrap)?
} else {
Expand Down Expand Up @@ -396,8 +395,7 @@ mod tests {

// Test DEK wrapped by the test DES key (AES-256)
let des = generate_aes_256();
let result =
crypto::aes_key_wrap::AesKeyWrap::new(&des).and_then(|kw| kw.wrapper()?.wrap(&dek));
let result = crypto::aes_kwp::AesKeyWrap::new(&des).and_then(|kw| kw.wrapper()?.wrap(&dek));
assert!(result.is_ok());
let aes_wrapped_dek = result.unwrap();

Expand Down Expand Up @@ -461,7 +459,7 @@ mod tests {
assert!(result.is_ok());
let des_key = result.unwrap();

let result = crypto::aes_key_wrap::AesKeyWrap::new(&des_key).and_then(|kw| {
let result = crypto::aes_kwp::AesKeyWrap::new(&des_key).and_then(|kw| {
kw.unwrapper()?
.unwrap(&key_protector.dek[egress_index].dek_buffer[..AES_WRAPPED_AES_KEY_LENGTH])
});
Expand Down Expand Up @@ -503,7 +501,7 @@ mod tests {
assert!(result.is_ok());
let des_key = result.unwrap();

let result = crypto::aes_key_wrap::AesKeyWrap::new(&des_key).and_then(|kw| {
let result = crypto::aes_kwp::AesKeyWrap::new(&des_key).and_then(|kw| {
kw.unwrapper()?
.unwrap(&key_protector.dek[egress_index].dek_buffer[..AES_WRAPPED_AES_KEY_LENGTH])
});
Expand All @@ -522,8 +520,7 @@ mod tests {

// Test DEK wrapped by the test DES key (AES-256)
let des = generate_aes_256();
let result =
crypto::aes_key_wrap::AesKeyWrap::new(&des).and_then(|kw| kw.wrapper()?.wrap(&dek));
let result = crypto::aes_kwp::AesKeyWrap::new(&des).and_then(|kw| kw.wrapper()?.wrap(&dek));
assert!(result.is_ok());
let mut aes_wrapped_dek = result.unwrap();

Expand Down
6 changes: 3 additions & 3 deletions openhcl/underhill_attestation/src/secure_key_release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub(crate) enum Pkcs11RsaAesKeyUnwrapError {
#[error("RSA unwrap failed")]
RsaUnwrap(#[source] crypto::rsa::RsaError),
#[error("AES unwrap failed")]
AesUnwrap(#[source] crypto::aes_key_wrap::AesKeyWrapError),
AesUnwrap(#[source] crypto::aes_kwp::AesKeyWrapError),
#[error("failed to parse PKCS#8 DER as RSA key")]
ParsePkcs8Der(#[source] crypto::rsa::RsaError),
}
Expand Down Expand Up @@ -94,7 +94,7 @@ fn pkcs11_rsa_aes_key_unwrap(
let unwrapped_aes_key = unwrapping_rsa_key
.oaep_decrypt(wrapped_aes_key, HashAlgorithm::Sha1)
.map_err(Pkcs11RsaAesKeyUnwrapError::RsaUnwrap)?;
let unwrapped_rsa_key = crypto::aes_key_wrap::AesKeyWrap::new(&unwrapped_aes_key)
let unwrapped_rsa_key = crypto::aes_kwp::AesKeyWrap::new(&unwrapped_aes_key)
.and_then(|kw| kw.unwrapper()?.unwrap(wrapped_rsa_key))
.map_err(Pkcs11RsaAesKeyUnwrapError::AesUnwrap)?;
let unwrapped_rsa_key = RsaKeyPair::from_pkcs8_der(&unwrapped_rsa_key)
Expand Down Expand Up @@ -421,7 +421,7 @@ mod tests {
let wrapped_aes_key = wrapping_rsa_key
.oaep_encrypt(&wrapping_aes_key, crypto::rsa::HashAlgorithm::Sha1)
.unwrap();
let wrapped_target_key = crypto::aes_key_wrap::AesKeyWrap::new(&wrapping_aes_key)
let wrapped_target_key = crypto::aes_kwp::AesKeyWrap::new(&wrapping_aes_key)
.unwrap()
.wrapper()
.unwrap()
Expand Down
2 changes: 2 additions & 0 deletions support/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ symcrypt = [
# Note that some of these crates have known security issues.
# This backend should not be used in production scenarios.
rust = [
"dep:aes-kw",
"dep:der",
"dep:getrandom",
"dep:pkcs1",
Expand All @@ -49,6 +50,7 @@ openssl = { workspace = true, optional = true }

symcrypt = { workspace = true, optional = true }

aes-kw = { workspace = true, optional = true }
der = { workspace = true, optional = true }
getrandom = { workspace = true, optional = true, features = ["sys_rng"] }
pkcs1 = { workspace = true, optional = true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@

//! AES key wrap with padding (RFC 5649).

#![cfg(openssl)]
#![cfg(any(openssl, rust))]

#[cfg(openssl)]
mod ossl;
#[cfg(openssl)]
use ossl as sys;

#[cfg(rust)]
mod rust;
#[cfg(rust)]
use rust as sys;

use thiserror::Error;

/// An error for AES key wrap operations.
Expand All @@ -19,9 +24,14 @@ pub enum AesKeyWrapError {
#[error("invalid wrapping key size {0}")]
InvalidKeySize(usize),
/// A backend cryptographic error occurred.
#[cfg(not(rust))]
#[error("AES key wrap error")]
#[expect(private_interfaces)] // Will go away after refactoring this algo
#[expect(private_interfaces)]
Backend(#[source] super::BackendError),
/// A backend cryptographic error occurred.
#[cfg(rust)]
#[error("AES key wrap error during {1}: {0}")]
Backend(String, &'static str),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self: hide variants

}

/// AES key wrap with padding (RFC 5649).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,29 +60,26 @@ impl AesKeyWrapInner {

impl AesKeyWrapCtxInner<'_> {
pub fn wrap(&mut self, payload: &[u8]) -> Result<Vec<u8>, AesKeyWrapError> {
let padding = 8 - payload.len() % 8;
let mut output = vec![0; payload.len() + padding + 16];
let count = self
.ctx
.cipher_update(payload, Some(&mut output))
let mut output = Vec::with_capacity(payload.len() + 16);
self.ctx
.cipher_update_vec(payload, &mut output)
.map_err(|e| err(e, "wrapping key"))?;
// DEVNOTE: Skip the `cipher_final()`, which is effectively a no-op for this operation
// according to OpenSSL implementation.
output.truncate(count);
self.ctx
.cipher_final_vec(&mut output)
.map_err(|e| err(e, "finalizing key wrap"))?;
Ok(output)
}
}

impl AesKeyUnwrapCtxInner<'_> {
pub fn unwrap(&mut self, wrapped_payload: &[u8]) -> Result<Vec<u8>, AesKeyWrapError> {
let mut output = vec![0; wrapped_payload.len() + 16];
let count = self
.ctx
.cipher_update(wrapped_payload, Some(&mut output))
let mut output = Vec::with_capacity(wrapped_payload.len() + 16);
self.ctx
.cipher_update_vec(wrapped_payload, &mut output)
.map_err(|e| err(e, "unwrapping key"))?;
// DEVNOTE: Skip the `cipher_final()`, which is effectively a no-op for this operation
// according to OpenSSL implementation.
output.truncate(count);
self.ctx
.cipher_final_vec(&mut output)
.map_err(|e| err(e, "finalizing key unwrap"))?;
Ok(output)
}
}
73 changes: 73 additions & 0 deletions support/crypto/src/aes_kwp/rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! AES key wrap with padding (RFC 5649) using the `aes-kw` RustCrypto crate.

use super::AesKeyWrapError;
use aes_kw::KwpAes128;
use aes_kw::KwpAes192;
use aes_kw::KwpAes256;
use aes_kw::cipher::KeyInit;

pub enum AesKeyWrapInner {
Aes128(KwpAes128),
Aes192(KwpAes192),
Aes256(KwpAes256),
}

pub struct AesKeyWrapCtxInner<'a>(&'a AesKeyWrapInner);

pub struct AesKeyUnwrapCtxInner<'a>(&'a AesKeyWrapInner);

impl AesKeyWrapInner {
pub fn new(key: &[u8]) -> Result<Self, AesKeyWrapError> {
Ok(match key.len() {
16 => Self::Aes128(KwpAes128::new_from_slice(key).unwrap()),
24 => Self::Aes192(KwpAes192::new_from_slice(key).unwrap()),
32 => Self::Aes256(KwpAes256::new_from_slice(key).unwrap()),
Comment on lines +25 to +27
n => return Err(AesKeyWrapError::InvalidKeySize(n)),
})
}

pub fn wrap_ctx(&self) -> Result<AesKeyWrapCtxInner<'_>, AesKeyWrapError> {
Ok(AesKeyWrapCtxInner(self))
}

pub fn unwrap_ctx(&self) -> Result<AesKeyUnwrapCtxInner<'_>, AesKeyWrapError> {
Ok(AesKeyUnwrapCtxInner(self))
}
}

fn err(e: aes_kw::Error, op: &'static str) -> AesKeyWrapError {
AesKeyWrapError::Backend(e.to_string(), op)
}

impl AesKeyWrapCtxInner<'_> {
pub fn wrap(&mut self, payload: &[u8]) -> Result<Vec<u8>, AesKeyWrapError> {
let mut out = vec![0u8; payload.len() + 16];
let written_len = match self.0 {
AesKeyWrapInner::Aes128(k) => k.wrap_key(payload, &mut out),
AesKeyWrapInner::Aes192(k) => k.wrap_key(payload, &mut out),
AesKeyWrapInner::Aes256(k) => k.wrap_key(payload, &mut out),
}
.map_err(|e| err(e, "wrapping key"))?
.len();
out.truncate(written_len);
Ok(out)
}
}

impl AesKeyUnwrapCtxInner<'_> {
pub fn unwrap(&mut self, wrapped_payload: &[u8]) -> Result<Vec<u8>, AesKeyWrapError> {
let mut out = vec![0u8; wrapped_payload.len() + 16];
let written_len = match self.0 {
AesKeyWrapInner::Aes128(k) => k.unwrap_key(wrapped_payload, &mut out),
AesKeyWrapInner::Aes192(k) => k.unwrap_key(wrapped_payload, &mut out),
AesKeyWrapInner::Aes256(k) => k.unwrap_key(wrapped_payload, &mut out),
}
.map_err(|e| err(e, "unwrapping key"))?
.len();
out.truncate(written_len);
Ok(out)
}
}
2 changes: 1 addition & 1 deletion support/crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

pub mod aes_256_cbc;
pub mod aes_256_gcm;
pub mod aes_key_wrap;
pub mod aes_kwp;
pub mod hmac_sha_256;
pub mod kdf;
pub mod pkcs7;
Expand Down
Loading
Loading