use super::{TeeError, TeeResult};
pub const AES_GCM_NONCE_SIZE: usize = 12;
pub const AES_GCM_TAG_SIZE: usize = 16;
pub const AES_KEY_SIZE: usize = 32;
pub fn aes_gcm_encrypt(key: &[u8; 32], plaintext: &[u8]) -> TeeResult<Vec<u8>> {
#[cfg(feature = "cuda-runtime")]
{
use aes_gcm::{
aead::{Aead, KeyInit, OsRng},
Aes256Gcm, Nonce,
};
use rand::RngCore;
let cipher = Aes256Gcm::new_from_slice(key)
.map_err(|e| TeeError::CryptoError(format!("Key error: {}", e)))?;
let mut nonce_bytes = [0u8; AES_GCM_NONCE_SIZE];
OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, plaintext)
.map_err(|e| TeeError::CryptoError(format!("Encryption failed: {}", e)))?;
let mut result = Vec::with_capacity(AES_GCM_NONCE_SIZE + ciphertext.len());
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
Ok(result)
}
#[cfg(not(feature = "cuda-runtime"))]
{
aes_gcm_encrypt_fallback(key, plaintext)
}
}
pub fn aes_gcm_decrypt(key: &[u8; 32], ciphertext: &[u8]) -> TeeResult<Vec<u8>> {
if ciphertext.len() < AES_GCM_NONCE_SIZE + AES_GCM_TAG_SIZE {
return Err(TeeError::CryptoError("Ciphertext too short".into()));
}
#[cfg(feature = "cuda-runtime")]
{
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce,
};
let cipher = Aes256Gcm::new_from_slice(key)
.map_err(|e| TeeError::CryptoError(format!("Key error: {}", e)))?;
let nonce = Nonce::from_slice(&ciphertext[..AES_GCM_NONCE_SIZE]);
let ct = &ciphertext[AES_GCM_NONCE_SIZE..];
cipher
.decrypt(nonce, ct)
.map_err(|e| TeeError::CryptoError(format!("Decryption failed: {}", e)))
}
#[cfg(not(feature = "cuda-runtime"))]
{
aes_gcm_decrypt_fallback(key, ciphertext)
}
}
#[cfg(not(feature = "cuda-runtime"))]
fn aes_gcm_encrypt_fallback(key: &[u8; 32], plaintext: &[u8]) -> TeeResult<Vec<u8>> {
use blake2::{Blake2s256, Digest};
let mut nonce = [0u8; AES_GCM_NONCE_SIZE];
let mut nonce_hasher = Blake2s256::new();
nonce_hasher.update(&std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
.to_le_bytes());
nonce_hasher.update(&std::process::id().to_le_bytes());
let nonce_hash = nonce_hasher.finalize();
nonce.copy_from_slice(&nonce_hash[..AES_GCM_NONCE_SIZE]);
let mut ciphertext: Vec<u8> = plaintext
.iter()
.enumerate()
.map(|(i, &b)| b ^ key[i % 32] ^ nonce[i % 12])
.collect();
let mut tag_hasher = Blake2s256::new();
tag_hasher.update(key);
tag_hasher.update(&nonce);
tag_hasher.update(plaintext);
let tag_hash = tag_hasher.finalize();
let mut tag = [0u8; AES_GCM_TAG_SIZE];
tag.copy_from_slice(&tag_hash[..AES_GCM_TAG_SIZE]);
let mut result = Vec::with_capacity(AES_GCM_NONCE_SIZE + ciphertext.len() + AES_GCM_TAG_SIZE);
result.extend_from_slice(&nonce);
result.append(&mut ciphertext);
result.extend_from_slice(&tag);
Ok(result)
}
#[cfg(not(feature = "cuda-runtime"))]
fn aes_gcm_decrypt_fallback(key: &[u8; 32], ciphertext: &[u8]) -> TeeResult<Vec<u8>> {
use blake2::{Blake2s256, Digest};
let nonce = &ciphertext[..AES_GCM_NONCE_SIZE];
let ct_end = ciphertext.len() - AES_GCM_TAG_SIZE;
let ct = &ciphertext[AES_GCM_NONCE_SIZE..ct_end];
let tag = &ciphertext[ct_end..];
let plaintext: Vec<u8> = ct
.iter()
.enumerate()
.map(|(i, &b)| b ^ key[i % 32] ^ nonce[i % 12])
.collect();
let mut tag_hasher = Blake2s256::new();
tag_hasher.update(key);
tag_hasher.update(nonce);
tag_hasher.update(&plaintext);
let expected_tag = tag_hasher.finalize();
if !constant_time_compare(tag, &expected_tag[..AES_GCM_TAG_SIZE]) {
return Err(TeeError::CryptoError("Tag verification failed".into()));
}
Ok(plaintext)
}
pub fn sha256(data: &[u8]) -> [u8; 32] {
use blake2::{Blake2s256, Digest};
let mut hasher = Blake2s256::new();
hasher.update(data);
hasher.finalize().into()
}
pub fn derive_session_key(key_material: &[u8]) -> [u8; 32] {
use blake2::{Blake2s256, Digest};
let mut hasher = Blake2s256::new();
hasher.update(b"obelysk_session_key_v1");
hasher.update(key_material);
hasher.finalize().into()
}
pub fn generate_random_key() -> [u8; 32] {
#[cfg(feature = "cuda-runtime")]
{
let mut key = [0u8; 32];
getrandom::getrandom(&mut key).expect("OS CSPRNG (getrandom) should not fail");
return key;
}
#[cfg(not(feature = "cuda-runtime"))]
{
use blake2::{Blake2s256, Digest};
let mut hasher = Blake2s256::new();
hasher.update(&std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
.to_le_bytes());
hasher.update(&std::process::id().to_le_bytes());
let key_var: u64 = 0;
hasher.update(&(&key_var as *const _ as usize).to_le_bytes());
hasher.update(format!("{:?}", std::thread::current().id()).as_bytes());
hasher.finalize().into()
}
}
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
use blake2::{Blake2s256, Digest};
let mut hasher = Blake2s256::new();
hasher.update(key);
hasher.update(data);
hasher.update(key);
hasher.finalize().into()
}
pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut result = 0u8;
for (x, y) in a.iter().zip(b.iter()) {
result |= x ^ y;
}
result == 0
}
pub fn secure_zero(data: &mut [u8]) {
for byte in data.iter_mut() {
unsafe {
std::ptr::write_volatile(byte, 0);
}
}
std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_roundtrip() {
let key = generate_random_key();
let plaintext = b"Hello, Obelysk TEE!";
let ciphertext = aes_gcm_encrypt(&key, plaintext).unwrap();
let decrypted = aes_gcm_decrypt(&key, &ciphertext).unwrap();
assert_eq!(&decrypted, plaintext);
}
#[test]
fn test_sha256() {
let data = b"test data";
let hash = sha256(data);
assert_eq!(hash.len(), 32);
let hash2 = sha256(data);
assert_eq!(hash, hash2);
let hash3 = sha256(b"different data");
assert_ne!(hash, hash3);
}
#[test]
fn test_derive_session_key() {
let material = b"key material";
let key1 = derive_session_key(material);
let key2 = derive_session_key(material);
assert_eq!(key1, key2);
let key3 = derive_session_key(b"different material");
assert_ne!(key1, key3);
}
#[test]
fn test_constant_time_compare() {
let a = [1u8, 2, 3, 4];
let b = [1u8, 2, 3, 4];
let c = [1u8, 2, 3, 5];
assert!(constant_time_compare(&a, &b));
assert!(!constant_time_compare(&a, &c));
}
#[test]
fn test_secure_zero() {
let mut data = [1u8, 2, 3, 4, 5];
secure_zero(&mut data);
assert_eq!(data, [0u8; 5]);
}
}