Skip to content
Open
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
82 changes: 82 additions & 0 deletions ddi/tbor/types/src/ecc_generate_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Host-side wrapper for the TBOR `EccGenerateKey` command.
//!
//! `EccGenerateKey` is an **in-session** command (Crypto-Officer or
//! Crypto-User) that generates a fresh ECC keypair and returns the private
//! key as a **masked** blob plus the wire public key. The private key is
//! not stored on-device; the caller holds the masked blob and passes it
//! back to [`EccSign`](crate::ecc_sign) / [`EcdhDerive`](crate::ecdh_derive).

use alloc::vec::Vec;

use crate::tbor;

/// TBOR opcode for `EccGenerateKey`.
pub const TBOR_OP_ECC_GENERATE_KEY: u8 = 0x17;

/// Minimum masked ECC private-key envelope length (P-256).
pub const MASKED_ECC_KEY_MIN_LEN: usize = 8 + 12 + 96 + 32 + 16;
/// Maximum masked ECC private-key envelope length (P-521).
pub const MASKED_ECC_KEY_MAX_LEN: usize = 8 + 12 + 96 + 68 + 16;
/// Maximum wire public-key length (`x ‖ y`, P-521 padded).
pub const ECC_PUB_KEY_MAX_LEN: usize = 136;

/// `EccCurve` discriminant for NIST P-256.
pub const ECC_CURVE_P256: u8 = 1;
/// `EccCurve` discriminant for NIST P-384.
pub const ECC_CURVE_P384: u8 = 2;
/// `EccCurve` discriminant for NIST P-521.
pub const ECC_CURVE_P521: u8 = 3;

/// Host-facing TBOR `EccGenerateKey` request.
#[tbor(opcode = TBOR_OP_ECC_GENERATE_KEY, session_ctrl = in_session)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct TborEccGenerateKeyReq {
/// Session id this request is bound to.
#[tbor(session_id)]
pub session_id: u16,

/// Requested key scope as the 1-byte `KeyScope` discriminant.
pub scope: u8,

/// NIST curve as the 1-byte `EccCurve` discriminant (see
/// [`ECC_CURVE_P256`] / [`ECC_CURVE_P384`] / [`ECC_CURVE_P521`]).
pub curve: u8,
}

/// Host-facing TBOR `EccGenerateKey` response.
#[tbor(response)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TborEccGenerateKeyResp {
/// The generated private key, masked under the scope's masking key.
#[tbor(max_len = 200)]
pub masked_key: Vec<u8>,

/// The wire public key `x ‖ y` (little-endian, P-521 padded).
#[tbor(max_len = 136)]
pub pub_key: Vec<u8>,
}

#[cfg(test)]
mod tests {
use azihsm_ddi_tbor_types::TborOpReq;

use super::*;

#[test]
fn request_encodes_scope_and_curve() {
let req = TborEccGenerateKeyReq {
session_id: 5,
scope: 0b011,
curve: ECC_CURVE_P384,
};
let mut buf = [0u8; 256];
let frame = req.encode_request(&mut buf).expect("encode");
assert!(
frame.contains(&ECC_CURVE_P384),
"encoded frame must carry the curve discriminant",
);
}
}
84 changes: 84 additions & 0 deletions ddi/tbor/types/src/ecc_sign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Host-side wrapper for the TBOR `EccSign` command.
//!
//! `EccSign` is an **in-session** command (Crypto-Officer or Crypto-User)
//! that produces a raw ECDSA `r ‖ s` signature over a host-supplied
//! **pre-computed digest** using a caller-held **masked** ECC private key.
//! Firmware does no hashing — the caller supplies the digest in wire
//! little-endian order.

use alloc::vec::Vec;

use crate::tbor;

/// TBOR opcode for `EccSign`.
pub const TBOR_OP_ECC_SIGN: u8 = 0x18;

/// Maximum digest length (bytes): the SHA-512 digest.
pub const ECC_DIGEST_MAX_LEN: usize = 64;
/// Maximum wire ECDSA signature length (`r ‖ s`, P-521 padded).
pub const ECC_SIG_MAX_LEN: usize = 136;

/// `HashAlgo` discriminant for a SHA-256 digest.
pub const ECC_DIGEST_SHA256: u8 = 1;
/// `HashAlgo` discriminant for a SHA-384 digest.
pub const ECC_DIGEST_SHA384: u8 = 2;
/// `HashAlgo` discriminant for a SHA-512 digest.
pub const ECC_DIGEST_SHA512: u8 = 3;

/// Host-facing TBOR `EccSign` request.
#[tbor(opcode = TBOR_OP_ECC_SIGN, session_ctrl = in_session)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TborEccSignReq {
/// Session id this request is bound to.
#[tbor(session_id)]
pub session_id: u16,

/// The masked ECC private key (from `EccGenerateKey` / `UnwrapKey`).
#[tbor(min_len = 164, max_len = 200)]
pub masked_key: Vec<u8>,

/// Hash algorithm of the supplied digest, 1-byte `HashAlgo` (see
/// [`ECC_DIGEST_SHA256`] / [`ECC_DIGEST_SHA384`] / [`ECC_DIGEST_SHA512`]).
pub digest_algo: u8,

/// The pre-computed message digest in wire little-endian order, exactly
/// the algorithm's digest length (32 / 48 / 64 B).
#[tbor(max_len = 64)]
pub digest: Vec<u8>,
}

/// Host-facing TBOR `EccSign` response.
#[tbor(response)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TborEccSignResp {
/// Raw ECDSA `r ‖ s`, each component little-endian and padded to the
/// curve wire coordinate length (64 / 96 / 136 B).
#[tbor(max_len = 136)]
pub signature: Vec<u8>,
}

#[cfg(test)]
mod tests {
use azihsm_ddi_tbor_types::TborOpReq;

use super::*;

#[test]
fn request_encodes_digest() {
let req = TborEccSignReq {
session_id: 7,
masked_key: alloc::vec![0x11u8; 164],
digest_algo: ECC_DIGEST_SHA256,
digest: alloc::vec![0x22u8; 32],
};
let mut buf = [0u8; 512];
let frame = req.encode_request(&mut buf).expect("encode");
assert!(
frame.windows(4).any(|w| w == [0x22u8; 4]),
"encoded frame must carry the digest bytes",
);
}
}
78 changes: 78 additions & 0 deletions ddi/tbor/types/src/ecdh_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Host-side wrapper for the TBOR `EcdhDerive` command.
//!
//! `EcdhDerive` is an **in-session** command (Crypto-Officer or
//! Crypto-User) that derives an ECDH shared secret from a caller-held
//! **masked** local ECC private key and a host-supplied peer public key,
//! and returns the derived secret as a **masked** blob under the requested
//! scope's masking key.

use alloc::vec::Vec;

use crate::tbor;

/// TBOR opcode for `EcdhDerive`.
pub const TBOR_OP_ECDH_DERIVE: u8 = 0x19;

/// Maximum peer public-key length (`x ‖ y`, P-521 padded).
pub const ECDH_PEER_PUB_MAX_LEN: usize = 136;
/// Minimum masked derived-secret envelope length (P-256).
pub const MASKED_SECRET_MIN_LEN: usize = 8 + 12 + 96 + 32 + 16;
/// Maximum masked derived-secret envelope length (P-521).
pub const MASKED_SECRET_MAX_LEN: usize = 8 + 12 + 96 + 66 + 16;

/// Host-facing TBOR `EcdhDerive` request.
#[tbor(opcode = TBOR_OP_ECDH_DERIVE, session_ctrl = in_session)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TborEcdhDeriveReq {
/// Session id this request is bound to.
#[tbor(session_id)]
pub session_id: u16,

/// Requested key scope (masks the derived secret), 1-byte `KeyScope`.
pub scope: u8,

/// The masked local ECC private key (from `EccGenerateKey` /
/// `UnwrapKey`).
#[tbor(min_len = 164, max_len = 200)]
pub masked_key: Vec<u8>,

/// The peer's wire public key `x ‖ y` (little-endian, P-521 padded).
#[tbor(max_len = 136)]
pub peer_pub_key: Vec<u8>,
}

/// Host-facing TBOR `EcdhDerive` response.
#[tbor(response)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TborEcdhDeriveResp {
/// The derived ECDH shared secret, masked under the scope's masking
/// key.
#[tbor(max_len = 198)]
pub masked_secret: Vec<u8>,
}

#[cfg(test)]
mod tests {
use azihsm_ddi_tbor_types::TborOpReq;

use super::*;

#[test]
fn request_encodes_peer_pub() {
let req = TborEcdhDeriveReq {
session_id: 7,
scope: 0b011,
masked_key: alloc::vec![0x11u8; 164],
peer_pub_key: alloc::vec![0x22u8; 64],
};
let mut buf = [0u8; 512];
let frame = req.encode_request(&mut buf).expect("encode");
assert!(
frame.windows(4).any(|w| w == [0x22u8; 4]),
"encoded frame must carry the peer public-key bytes",
);
}
}
61 changes: 61 additions & 0 deletions ddi/tbor/types/src/get_unwrapping_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Host-side wrapper for the TBOR `GetUnwrappingKey` command.
//!
//! `GetUnwrappingKey` is an **in-session** command (Crypto-Officer or
//! Crypto-User) that returns the partition's RSA-2048 **unwrapping**
//! public key, which the host uses to RSA-AES key-wrap a payload for a
//! future `UnwrapKey` import. The unwrapping key is a device-provisioned
//! partition-internal key; only its public half is returned.
//!
//! RSA key generation is expensive, so the key is materialised lazily; an
//! absent key surfaces as `PendingKeyGeneration` (the host retries).

use crate::tbor;

/// TBOR opcode for `GetUnwrappingKey`.
pub const TBOR_OP_GET_UNWRAPPING_KEY: u8 = 0x13;

/// Wire length of the RSA-2048 unwrapping public key in HSM format:
/// `n_le(256) ‖ e_le(4)`.
pub const UNWRAPPING_PUB_KEY_LEN: usize = 256 + 4;

/// Host-facing TBOR `GetUnwrappingKey` request.
#[tbor(opcode = TBOR_OP_GET_UNWRAPPING_KEY, session_ctrl = in_session)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct TborGetUnwrappingKeyReq {
/// Session id this request is bound to. Cross-checked against the
/// SQE-carried session id by the dispatcher.
#[tbor(session_id)]
pub session_id: u16,
}

/// Host-facing TBOR `GetUnwrappingKey` response.
#[tbor(response)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TborGetUnwrappingKeyResp {
/// The RSA-2048 unwrapping public key in HSM wire format
/// (`n_le(256) ‖ e_le(4)`), exactly [`UNWRAPPING_PUB_KEY_LEN`] (260)
/// bytes.
pub pub_key: [u8; UNWRAPPING_PUB_KEY_LEN],
}

#[cfg(test)]
mod tests {
use azihsm_ddi_tbor_types::TborOpReq;

use super::*;

#[test]
fn request_encodes_session_id() {
let req = TborGetUnwrappingKeyReq { session_id: 11 };
let mut buf = [0u8; 128];
let frame = req.encode_request(&mut buf).expect("encode");
// The session id (11) must appear in the encoded frame.
assert!(
frame.contains(&11),
"encoded frame must carry the session id"
);
}
}
10 changes: 10 additions & 0 deletions ddi/tbor/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ impl From<SessionControlKind> for u8 {
}

mod api_rev;
mod ecc_generate_key;
mod ecc_sign;
mod ecdh_derive;
mod evidence;
mod get_unwrapping_key;
mod key_report;
mod part_final;
mod part_info;
Expand All @@ -97,9 +101,14 @@ mod session_close;
mod session_open_finish;
mod session_open_init;
mod status;
mod unwrap_key;

pub use api_rev::*;
pub use ecc_generate_key::*;
pub use ecc_sign::*;
pub use ecdh_derive::*;
pub use evidence::*;
pub use get_unwrapping_key::*;
pub use key_report::*;
pub use part_final::*;
pub use part_info::*;
Expand All @@ -117,6 +126,7 @@ pub use session_close::*;
pub use session_open_finish::*;
pub use session_open_init::*;
pub use status::*;
pub use unwrap_key::*;

/// Trait implemented by host-side TBOR request value types.
///
Expand Down
Loading