From 071e04adb9536f9699f53cdca6a0b12df2127045 Mon Sep 17 00:00:00 2001 From: duyuqi Date: Fri, 29 May 2026 16:29:07 +0800 Subject: [PATCH] Supports authend aad data streaming updates Signed-off-by: duyuqi --- tee/tee_kernel/src/tee/crypto/authenc_aad.rs | 119 +++++++++++++++++++ tee/tee_kernel/src/tee/crypto/crypto.rs | 35 ++++-- tee/tee_kernel/src/tee/crypto/mod.rs | 1 + tee/tee_kernel/src/tee/tee_svc_cryp2.rs | 101 ++++++++++++++++ 4 files changed, 245 insertions(+), 11 deletions(-) create mode 100644 tee/tee_kernel/src/tee/crypto/authenc_aad.rs diff --git a/tee/tee_kernel/src/tee/crypto/authenc_aad.rs b/tee/tee_kernel/src/tee/crypto/authenc_aad.rs new file mode 100644 index 00000000..b94cca85 --- /dev/null +++ b/tee/tee_kernel/src/tee/crypto/authenc_aad.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Buffered additional authenticated data for AEAD (OP-TEE semantics). +//! +//! mbedtls allows exactly one `cipher_update_ad` / `cipher_update_ad_ccm` per message. +//! The kernel accumulates `TEE_AEUpdateAAD` chunks and flushes once before payload +//! processing, matching OP-TEE's multi-call `crypto_authenc_update_aad` behavior. + +use alloc::vec::Vec; + +use mbedtls::cipher::raw::Cipher; +use tee_raw_sys::{ + TEE_ALG_AES_CCM, TEE_ALG_AES_GCM, TEE_ALG_SM4_CCM, TEE_ALG_SM4_GCM, TEE_ERROR_BAD_PARAMETERS, + TEE_ERROR_BAD_STATE, +}; + +use crate::tee::TeeResult; + +pub(crate) fn cipher_uses_authenc_aad_buffer(algo: u32) -> bool { + matches!( + algo, + TEE_ALG_AES_GCM | TEE_ALG_SM4_GCM | TEE_ALG_AES_CCM | TEE_ALG_SM4_CCM + ) +} + +fn cipher_uses_ccm(algo: u32) -> bool { + matches!(algo, TEE_ALG_AES_CCM | TEE_ALG_SM4_CCM) +} + +/// Accumulated AAD for one AE operation; flushed to mbedtls before the first payload byte. +pub(crate) struct TeeAuthencAadCtx { + buffer: Vec, + /// CCM: total AAD length passed to `TEE_AEInit` / `starts_ccm`. + expected_len: Option, + committed: bool, + payload_started: bool, + is_ccm: bool, +} + +impl TeeAuthencAadCtx { + pub(crate) fn new(algo: u32, aad_len: Option) -> Self { + let is_ccm = cipher_uses_ccm(algo); + Self { + buffer: Vec::new(), + expected_len: if is_ccm { aad_len } else { None }, + committed: false, + payload_started: false, + is_ccm, + } + } + + /// Append one `TEE_AEUpdateAAD` chunk (may be called multiple times before payload). + pub(crate) fn append_aad(&mut self, data: &[u8]) -> TeeResult { + if self.payload_started { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + if self.committed { + return Err(TEE_ERROR_BAD_STATE); + } + if let Some(expected) = self.expected_len { + let new_len = self + .buffer + .len() + .checked_add(data.len()) + .ok_or(TEE_ERROR_BAD_PARAMETERS)?; + if new_len > expected { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + } + self.buffer.extend_from_slice(data); + Ok(()) + } + + /// Feed accumulated AAD to mbedtls (once). Called before the first payload update/final. + pub(crate) fn flush_to_cipher(&mut self, cipher: &mut Cipher) -> TeeResult { + if self.committed { + return Ok(()); + } + if self.is_ccm { + let expected = self.expected_len.ok_or(TEE_ERROR_BAD_PARAMETERS)?; + if self.buffer.len() != expected { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + cipher + .update_ad_ccm(&self.buffer) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + } else if !self.buffer.is_empty() { + cipher + .update_ad(&self.buffer) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + } + self.committed = true; + Ok(()) + } + + /// Flush buffered AAD to mbedtls before the first payload byte (idempotent). + pub(crate) fn enter_payload_phase(&mut self, cipher: &mut Cipher) -> TeeResult { + if self.payload_started { + return Ok(()); + } + self.flush_to_cipher(cipher)?; + self.payload_started = true; + Ok(()) + } +} + +impl Clone for TeeAuthencAadCtx { + fn clone(&self) -> Self { + Self { + buffer: self.buffer.clone(), + expected_len: self.expected_len, + committed: self.committed, + payload_started: self.payload_started, + is_ccm: self.is_ccm, + } + } +} diff --git a/tee/tee_kernel/src/tee/crypto/crypto.rs b/tee/tee_kernel/src/tee/crypto/crypto.rs index a7df9a85..2ddd2c70 100644 --- a/tee/tee_kernel/src/tee/crypto/crypto.rs +++ b/tee/tee_kernel/src/tee/crypto/crypto.rs @@ -30,6 +30,7 @@ use crate::tee::{ TeeCipherXtsCtx, aes_xts_final_buffered, aes_xts_init, aes_xts_update_buffered, cipher_uses_aes_xts_kernel, }, + authenc_aad::{TeeAuthencAadCtx, cipher_uses_authenc_aad_buffer}, crypto_impl::{ EccAlgoKeyPair, EccComKeyPair, EccKeypair, Sm2DsaKeyPair, Sm2KepKeyPair, Sm2PkeKeyPair, crypto_ecc_keypair_ops, crypto_ecc_keypair_ops_generate, @@ -684,10 +685,18 @@ pub(crate) fn crypto_cipher_init( pending: [0; TeeCipherCtx::PENDING_MAX], pending_len: 0, xts, + authenc_aad: None, })); Ok(()) } +fn tee_cipher_ctx_flush_authenc_aad(op: &mut TeeCipherCtx) -> TeeResult { + let Some(aad) = &mut op.authenc_aad else { + return Ok(()); + }; + aad.enter_payload_phase(&mut op.cipher) +} + fn cipher_uses_ecb_pending(algo: u32) -> bool { matches!( algo, @@ -791,6 +800,9 @@ pub(crate) fn crypto_cipher_update( let mut cs_guard = cs.lock(); let algo = cs_guard.algo; if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { + if cipher_uses_authenc_aad_buffer(algo) { + tee_cipher_ctx_flush_authenc_aad(op)?; + } if let Some(xts) = &mut op.xts { let stream = xts.stream(); let n = aes_xts_update_buffered( @@ -914,12 +926,18 @@ pub(crate) fn crypto_authenc_init( .starts_ccm(payload_len, aad_len, tag_len) .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; } + let authenc_aad = if cipher_uses_authenc_aad_buffer(algo) { + Some(TeeAuthencAadCtx::new(algo, aad_len)) + } else { + None + }; cs_guard.state = CrypState::Initialized; cs_guard.ctx = CrypCtx::CipherCtx(Box::new(TeeCipherCtx { cipher, pending: [0; TeeCipherCtx::PENDING_MAX], pending_len: 0, xts: None, + authenc_aad, })); Ok(()) } else { @@ -929,18 +947,11 @@ pub(crate) fn crypto_authenc_init( pub(crate) fn crypto_authenc_update_aad(cs: Arc>, aad: &[u8]) -> TeeResult { let mut cs_guard = cs.lock(); - let algo = cs_guard.algo; if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { - match algo { - TEE_ALG_AES_CCM | TEE_ALG_SM4_CCM => op - .cipher - .update_ad_ccm(aad) - .map_err(|_| TEE_ERROR_BAD_PARAMETERS), - _ => op - .cipher - .update_ad(aad) - .map_err(|_| TEE_ERROR_BAD_PARAMETERS), - } + let Some(aad_ctx) = &mut op.authenc_aad else { + return Err(TEE_ERROR_BAD_PARAMETERS); + }; + aad_ctx.append_aad(aad) } else { Err(TEE_ERROR_BAD_PARAMETERS) } @@ -955,6 +966,7 @@ pub(crate) fn crypto_authenc_enc_final( let mut cs_guard = cs.lock(); let mut res: usize = 0; if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { + tee_cipher_ctx_flush_authenc_aad(op)?; if let Some(input) = input { res = op .cipher @@ -979,6 +991,7 @@ pub(crate) fn crypto_authenc_dec_final( let mut cs_guard = cs.lock(); let mut res: usize = 0; if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { + tee_cipher_ctx_flush_authenc_aad(op)?; if let Some(input) = input { res = op .cipher diff --git a/tee/tee_kernel/src/tee/crypto/mod.rs b/tee/tee_kernel/src/tee/crypto/mod.rs index 19f35780..6335fb10 100644 --- a/tee/tee_kernel/src/tee/crypto/mod.rs +++ b/tee/tee_kernel/src/tee/crypto/mod.rs @@ -2,6 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. pub mod aes_xts; +pub mod authenc_aad; #[allow(clippy::module_inception)] pub mod crypto; pub mod crypto_impl; diff --git a/tee/tee_kernel/src/tee/tee_svc_cryp2.rs b/tee/tee_kernel/src/tee/tee_svc_cryp2.rs index d38a8584..2ca78b53 100644 --- a/tee/tee_kernel/src/tee/tee_svc_cryp2.rs +++ b/tee/tee_kernel/src/tee/tee_svc_cryp2.rs @@ -193,6 +193,8 @@ pub(crate) struct TeeCipherCtx { pub pending_len: usize, /// Stateful AES-XTS (mbedtls `cipher_update` does not continue tweak across calls). pub xts: Option, + /// Buffered AAD for GCM/CCM (`mbedtls_cipher_update_ad` is single-shot). + pub authenc_aad: Option, } impl TeeCipherCtx { @@ -206,6 +208,7 @@ impl Clone for TeeCipherCtx { pending: self.pending, pending_len: self.pending_len, xts: self.xts.clone(), + authenc_aad: self.authenc_aad.clone(), } } } @@ -3112,6 +3115,104 @@ pub mod tests_cryp { ); } + #[unittest::def_test(custom)] + fn test_cryp_sm4_gcm_split_aad_matches_one_shot() { + let mut state: u32 = 0; + let mut obj_id = TestUserValue::::from_value(0).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_SM4 as _, 128, obj_id.as_user_ref()).unwrap(); + let obj_id = obj_id.read(); + syscall_obj_generate_key(obj_id as c_ulong, 128, core::ptr::null(), 0).unwrap(); + + let obj = tee_obj_get(obj_id as tee_obj_id_type).unwrap(); + let mut obj_guard = obj.lock(); + let key = [ + 0x69, 0xEE, 0xDF, 0x37, 0x77, 0xE5, 0x94, 0xC3, 0x0E, 0x94, 0xE9, 0xC5, 0xE2, 0xBC, + 0xE4, 0x67, + ]; + let mut secret = tee_cryp_obj_secret_wrapper::new(32); + secret.set_secret_data(&key).unwrap(); + let _ = core::mem::replace(&mut obj_guard.attr[0], TeeCryptObj::obj_secret(secret)); + drop(obj_guard); + + tee_cryp_state_alloc( + TEE_ALG_SM4_GCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(obj_id as _), + None, + &mut state, + ) + .unwrap(); + + let data: [u8; 64] = [ + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + ]; + let nonce = [ + 0xA3, 0x33, 0x06, 0x38, 0xA8, 0x09, 0xBA, 0x35, 0x8D, 0x6C, 0x09, 0x8E, + ]; + let ad = [ + 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, + 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2, + ]; + + tee_cryp_authenc_init(state, &nonce, None, None, None).unwrap(); + tee_cryp_authenc_update_aad(state, &ad[..10]).unwrap(); + tee_cryp_authenc_update_aad(state, &ad[10..]).unwrap(); + + let mut out = [0u8; 80]; + let mut tag = [0u8; 16]; + let n = tee_cryp_authenc_update_payload(state, &data, &mut out).unwrap(); + tee_cryp_authenc_enc_final(state, None, &mut out[n..], &mut tag).unwrap(); + + assert_eq!(n, 64); + assert_eq!( + &out[..64], + [ + 0x0C, 0x29, 0xFC, 0x49, 0x07, 0x11, 0x9F, 0x99, 0xC4, 0x92, 0xE2, 0xFA, 0x7B, 0x63, + 0x3F, 0x4E, 0x16, 0x5B, 0xE5, 0x35, 0x85, 0xAB, 0xED, 0x71, 0x8B, 0xA3, 0x9C, 0xAB, + 0x80, 0xA0, 0x63, 0x92, 0x73, 0x1E, 0x5C, 0xE6, 0xE3, 0x58, 0x1D, 0xCA, 0xF1, 0x19, + 0x03, 0x7D, 0x99, 0x8A, 0x0F, 0x52, 0x2D, 0x68, 0x0A, 0x9D, 0xCB, 0x40, 0x5A, 0xAD, + 0xF8, 0x00, 0xC0, 0xC7, 0x98, 0xBA, 0xE3, 0x8A + ] + ); + assert_eq!( + tag, + [ + 0x19, 0x7F, 0x6C, 0xC5, 0x52, 0x3D, 0xA3, 0x6A, 0x3B, 0x2C, 0x42, 0x92, 0x44, 0xC4, + 0x70, 0xAA + ] + ); + } + + #[unittest::def_test(custom)] + fn test_cryp_authenc_aad_rejected_after_payload() { + let mut state: u32 = 0; + let mut obj_id = TestUserValue::::from_value(0).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_SM4 as _, 128, obj_id.as_user_ref()).unwrap(); + let obj_id = obj_id.read(); + syscall_obj_generate_key(obj_id as c_ulong, 128, core::ptr::null(), 0).unwrap(); + + tee_cryp_state_alloc( + TEE_ALG_SM4_GCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(obj_id as _), + None, + &mut state, + ) + .unwrap(); + + let nonce = [0u8; 12]; + let data = [0u8; 16]; + let mut out = [0u8; 32]; + tee_cryp_authenc_init(state, &nonce, None, None, None).unwrap(); + tee_cryp_authenc_update_payload(state, &data, &mut out).unwrap(); + let res = tee_cryp_authenc_update_aad(state, b"late"); + assert_eq!(res.err(), Some(TEE_ERROR_BAD_PARAMETERS)); + } + #[unittest::def_test(custom)] fn test_cryp_authenc_requires_ae_state() { let mut obj_id = TestUserValue::::from_value(0).unwrap(); -- Gitee