|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | +// Copyright 2025 Keylime Authors |
| 3 | + |
| 4 | +use crate::crypto::{AES_128_KEY_LEN, AES_256_KEY_LEN}; |
| 5 | +use serde::{Deserialize, Serialize}; |
| 6 | +use thiserror::Error; |
| 7 | + |
| 8 | +#[derive(Debug, Error)] |
| 9 | +pub enum SymmKeyError { |
| 10 | + // Invalid key size for AES |
| 11 | + #[error("invalid AES key size: {0}")] |
| 12 | + InvalidKeySize(usize), |
| 13 | + |
| 14 | + // Incompatible sizes for XOR |
| 15 | + #[error("cannot XOR slices of different sizes")] |
| 16 | + XorIncompatibleSizes, |
| 17 | +} |
| 18 | + |
| 19 | +// a vector holding keys |
| 20 | +pub type KeySet = Vec<SymmKey>; |
| 21 | + |
| 22 | +// a key of len AES_128_KEY_LEN or AES_256_KEY_LEN |
| 23 | +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] |
| 24 | +pub struct SymmKey { |
| 25 | + bytes: Vec<u8>, |
| 26 | +} |
| 27 | + |
| 28 | +impl SymmKey { |
| 29 | + pub fn xor(&self, other: &Self) -> Result<Self, SymmKeyError> { |
| 30 | + let my_bytes = self.as_ref(); |
| 31 | + let other_bytes = other.as_ref(); |
| 32 | + if my_bytes.len() != other_bytes.len() { |
| 33 | + return Err(SymmKeyError::XorIncompatibleSizes); |
| 34 | + } |
| 35 | + let mut outbuf = vec![0u8; my_bytes.len()]; |
| 36 | + for (out, (x, y)) in |
| 37 | + outbuf.iter_mut().zip(my_bytes.iter().zip(other_bytes)) |
| 38 | + { |
| 39 | + *out = x ^ y; |
| 40 | + } |
| 41 | + Ok(Self { bytes: outbuf }) |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +impl AsRef<[u8]> for SymmKey { |
| 46 | + fn as_ref(&self) -> &[u8] { |
| 47 | + self.bytes.as_slice() |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +impl TryFrom<&[u8]> for SymmKey { |
| 52 | + type Error = SymmKeyError; |
| 53 | + |
| 54 | + fn try_from(v: &[u8]) -> std::result::Result<Self, SymmKeyError> { |
| 55 | + match v.len() { |
| 56 | + AES_128_KEY_LEN | AES_256_KEY_LEN => { |
| 57 | + Ok(SymmKey { bytes: v.to_vec() }) |
| 58 | + } |
| 59 | + other => Err(SymmKeyError::InvalidKeySize(other)), |
| 60 | + } |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +#[cfg(test)] |
| 65 | +mod tests { |
| 66 | + use super::*; |
| 67 | + |
| 68 | + #[test] |
| 69 | + fn test_convert() { |
| 70 | + let a_128: [u8; AES_128_KEY_LEN] = [0; AES_128_KEY_LEN]; |
| 71 | + let a_256: [u8; AES_256_KEY_LEN] = [0; AES_256_KEY_LEN]; |
| 72 | + let a_unknown: [u8; 127] = [0; 127]; |
| 73 | + |
| 74 | + let r_128 = SymmKey::try_from(a_128.as_ref()); |
| 75 | + assert!(r_128.is_ok()); |
| 76 | + |
| 77 | + let r_256 = SymmKey::try_from(a_256.as_ref()); |
| 78 | + assert!(r_256.is_ok()); |
| 79 | + |
| 80 | + let r_unknown = SymmKey::try_from(a_unknown.as_ref()); |
| 81 | + assert!(r_unknown.is_err()); |
| 82 | + } |
| 83 | + |
| 84 | + #[test] |
| 85 | + fn test_xor() { |
| 86 | + // Input for 128 bits keys |
| 87 | + let a: [u8; AES_128_KEY_LEN] = [0xA0; AES_128_KEY_LEN]; |
| 88 | + let b: [u8; AES_128_KEY_LEN] = [0x0A; AES_128_KEY_LEN]; |
| 89 | + let axb: [u8; AES_128_KEY_LEN] = [0xAA; AES_128_KEY_LEN]; |
| 90 | + let r_128 = |
| 91 | + SymmKey::try_from(axb.as_ref()).expect("failed to convert"); |
| 92 | + |
| 93 | + // Input for 256 bits keys |
| 94 | + let c: [u8; AES_256_KEY_LEN] = [0xA0; AES_256_KEY_LEN]; |
| 95 | + let d: [u8; AES_256_KEY_LEN] = [0x0A; AES_256_KEY_LEN]; |
| 96 | + let cxd: [u8; AES_256_KEY_LEN] = [0xAA; AES_256_KEY_LEN]; |
| 97 | + let r_256 = |
| 98 | + SymmKey::try_from(cxd.as_ref()).expect("failed to convert"); |
| 99 | + |
| 100 | + // Test for each set of inputs |
| 101 | + for (i, j, expected) in [ |
| 102 | + (a.as_ref(), b.as_ref(), &r_128), |
| 103 | + (c.as_ref(), d.as_ref(), &r_256), |
| 104 | + ] { |
| 105 | + let k_i = |
| 106 | + SymmKey::try_from(i).expect("failed to get key from slice"); |
| 107 | + let k_j = |
| 108 | + SymmKey::try_from(j).expect("failed to get key from slice"); |
| 109 | + let result = k_i.xor(&k_j); |
| 110 | + assert!(result.is_ok()); |
| 111 | + |
| 112 | + let out = result.expect("xor failed"); |
| 113 | + assert_eq!(&out, expected); |
| 114 | + } |
| 115 | + } |
| 116 | +} |
0 commit comments