Skip to content

Commit c300a0e

Browse files
committed
Introduce SecureOwnedKey
1 parent 54f7b48 commit c300a0e

File tree

1 file changed

+142
-6
lines changed

1 file changed

+142
-6
lines changed

platform/impls/rustcrypto/src/controller.rs

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl MacError for CryptoError {
7777
}
7878
}
7979

80-
/// Simple byte array key wrapper for software implementations
80+
/// Simple byte array key wrapper for software implementations (borrowed data)
8181
#[derive(Debug, Clone)]
8282
pub struct ByteArrayKey<'a>(&'a [u8]);
8383

@@ -93,6 +93,97 @@ impl<'a> ByteArrayKey<'a> {
9393

9494
impl<'a> KeyHandle for ByteArrayKey<'a> {}
9595

96+
/// Secure owned key type that owns its data on the stack (no allocation required)
97+
/// Maximum key size is 128 bytes to support all HMAC variants (SHA-512 block size)
98+
/// This solves the lifetime issue where ByteArrayKey<'static> cannot be created from local data
99+
#[derive(Debug, Clone)]
100+
pub struct SecureOwnedKey {
101+
data: [u8; 128], // Fixed size buffer - no allocation needed
102+
len: usize, // Actual key length
103+
}
104+
105+
impl SecureOwnedKey {
106+
/// Maximum supported key length (128 bytes for SHA-512 block size)
107+
pub const MAX_KEY_SIZE: usize = 128;
108+
109+
/// Create a new secure key by copying the provided bytes
110+
/// Returns error if key is too large for our fixed buffer
111+
pub fn new(bytes: &[u8]) -> Result<Self, CryptoError> {
112+
if bytes.len() > Self::MAX_KEY_SIZE {
113+
return Err(CryptoError::InvalidKeyLength);
114+
}
115+
116+
let mut data = [0u8; 128];
117+
data[..bytes.len()].copy_from_slice(bytes);
118+
119+
Ok(Self {
120+
data,
121+
len: bytes.len(),
122+
})
123+
}
124+
125+
/// Create a secure key from a fixed-size array (zero-copy for arrays up to 128 bytes)
126+
pub fn from_array<const N: usize>(array: [u8; N]) -> Result<Self, CryptoError> {
127+
if N > Self::MAX_KEY_SIZE {
128+
return Err(CryptoError::InvalidKeyLength);
129+
}
130+
131+
let mut data = [0u8; 128];
132+
data[..N].copy_from_slice(&array);
133+
134+
Ok(Self { data, len: N })
135+
}
136+
137+
/// Get the key bytes as a slice (only the valid portion)
138+
pub fn as_bytes(&self) -> &[u8] {
139+
&self.data[..self.len]
140+
}
141+
142+
/// Get the key length
143+
pub fn len(&self) -> usize {
144+
self.len
145+
}
146+
147+
/// Check if the key is empty
148+
pub fn is_empty(&self) -> bool {
149+
self.len == 0
150+
}
151+
}
152+
153+
impl KeyHandle for SecureOwnedKey {}
154+
155+
impl TryFrom<&[u8]> for SecureOwnedKey {
156+
type Error = CryptoError;
157+
158+
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
159+
Self::new(bytes)
160+
}
161+
}
162+
163+
impl<const N: usize> TryFrom<[u8; N]> for SecureOwnedKey {
164+
type Error = CryptoError;
165+
166+
fn try_from(array: [u8; N]) -> Result<Self, Self::Error> {
167+
Self::from_array(array)
168+
}
169+
}
170+
171+
impl<const N: usize> TryFrom<&[u8; N]> for SecureOwnedKey {
172+
type Error = CryptoError;
173+
174+
fn try_from(array: &[u8; N]) -> Result<Self, Self::Error> {
175+
Self::new(array)
176+
}
177+
}
178+
179+
// Implement Drop to securely zero the key data when dropped
180+
impl Drop for SecureOwnedKey {
181+
fn drop(&mut self) {
182+
// Securely zero the key data
183+
self.data.fill(0);
184+
}
185+
}
186+
96187
/// Digest contexts for different SHA algorithms
97188
pub struct DigestContext256(Sha256);
98189
pub struct DigestContext384(Sha384);
@@ -270,7 +361,7 @@ impl DigestOp for DigestContext512 {
270361

271362
// MAC initialization - creates HMAC-SHA256 context
272363
impl MacInit<HmacSha2_256> for RustCryptoController {
273-
type Key = ByteArrayKey<'static>;
364+
type Key = SecureOwnedKey;
274365
type Context = MacContext256;
275366
type Output = [u8; 32]; // HMAC-SHA256 output size
276367

@@ -283,7 +374,7 @@ impl MacInit<HmacSha2_256> for RustCryptoController {
283374

284375
// MAC initialization - creates HMAC-SHA384 context
285376
impl MacInit<HmacSha2_384> for RustCryptoController {
286-
type Key = ByteArrayKey<'static>;
377+
type Key = SecureOwnedKey;
287378
type Context = MacContext384;
288379
type Output = [u8; 48]; // HMAC-SHA384 output size
289380

@@ -296,7 +387,7 @@ impl MacInit<HmacSha2_384> for RustCryptoController {
296387

297388
// MAC initialization - creates HMAC-SHA512 context
298389
impl MacInit<HmacSha2_512> for RustCryptoController {
299-
type Key = ByteArrayKey<'static>;
390+
type Key = SecureOwnedKey;
300391
type Context = MacContext512;
301392
type Output = [u8; 64]; // HMAC-SHA512 output size
302393

@@ -430,7 +521,7 @@ mod tests {
430521
#[test]
431522
#[allow(clippy::unwrap_used)]
432523
fn test_mac_operations() {
433-
let key = ByteArrayKey::new(b"super secret key");
524+
let key = SecureOwnedKey::new(b"super secret key").unwrap();
434525

435526
// Test HMAC-SHA256
436527
let controller = RustCryptoController::new();
@@ -466,7 +557,7 @@ mod tests {
466557
let (hash256, controller) = digest_ctx.finalize().unwrap();
467558

468559
// Use recovered controller for HMAC-SHA384
469-
let key = ByteArrayKey::new(b"key");
560+
let key = SecureOwnedKey::new(b"key").unwrap();
470561
let mac_ctx = MacInit::<HmacSha2_384>::init(controller, HmacSha2_384, key).unwrap();
471562
let mac_ctx = mac_ctx.update(b"test data").unwrap();
472563
let (mac384, controller) = mac_ctx.finalize().unwrap();
@@ -484,4 +575,49 @@ mod tests {
484575
let hash512_bytes = hash512.as_bytes();
485576
assert_eq!(hash512_bytes.len(), 64); // SHA-512 output
486577
}
578+
579+
#[test]
580+
#[allow(clippy::unwrap_used)]
581+
fn test_secure_owned_key() {
582+
// Test creation from slice
583+
let key1 = SecureOwnedKey::new(b"test key").unwrap();
584+
assert_eq!(key1.as_bytes(), b"test key");
585+
assert_eq!(key1.len(), 8);
586+
assert!(!key1.is_empty());
587+
588+
// Test creation from fixed array
589+
let key2 = SecureOwnedKey::from_array(*b"another key").unwrap();
590+
assert_eq!(key2.as_bytes(), b"another key");
591+
assert_eq!(key2.len(), 11);
592+
593+
// Test TryFrom trait implementations
594+
let key3: SecureOwnedKey = b"from slice".as_slice().try_into().unwrap();
595+
assert_eq!(key3.as_bytes(), b"from slice");
596+
597+
let key4: SecureOwnedKey = (*b"from fixed array").try_into().unwrap();
598+
assert_eq!(key4.as_bytes(), b"from fixed array");
599+
600+
let key5: SecureOwnedKey = b"from array ref".try_into().unwrap();
601+
assert_eq!(key5.as_bytes(), b"from array ref");
602+
603+
// Test cloning
604+
let key6 = key1.clone();
605+
assert_eq!(key6.as_bytes(), key1.as_bytes());
606+
607+
// Test empty key
608+
let empty_key = SecureOwnedKey::new(&[]).unwrap();
609+
assert!(empty_key.is_empty());
610+
assert_eq!(empty_key.len(), 0);
611+
612+
// Test maximum key size (should succeed)
613+
let max_key_data = [0u8; SecureOwnedKey::MAX_KEY_SIZE];
614+
let max_key = SecureOwnedKey::new(&max_key_data).unwrap();
615+
assert_eq!(max_key.len(), SecureOwnedKey::MAX_KEY_SIZE);
616+
617+
// Test oversized key (should fail)
618+
let oversized_data = [0u8; SecureOwnedKey::MAX_KEY_SIZE + 1];
619+
let result = SecureOwnedKey::new(&oversized_data);
620+
assert!(result.is_err());
621+
assert_eq!(result.unwrap_err(), CryptoError::InvalidKeyLength);
622+
}
487623
}

0 commit comments

Comments
 (0)