diff --git a/tee/tee_kernel/src/tee/crypto/authenc.rs b/tee/tee_kernel/src/tee/crypto/authenc.rs new file mode 100644 index 0000000000000000000000000000000000000000..35843e4f0f3352928bbc1f558fcc04f0f5420d4f --- /dev/null +++ b/tee/tee_kernel/src/tee/crypto/authenc.rs @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! AEAD buffering (OP-TEE semantics). +//! +//! mbedtls multipart CCM/GCM only produce correct results when payload is fed in a +//! single `cipher_update` (after AAD). Incremental updates and `Cipher::clone` corrupt +//! shared state. We accumulate AAD/payload chunks and re-run AEAD from scratch on each +//! update/final, returning only bytes not yet delivered to the caller. +//! +//! GCM long nonces: OP-TEE accepts arbitrary nonce length (SP 800-38D GHASH derivation). +//! mbedtls `cipher_set_iv` rejects IV > 16 bytes before GCM mode can call `gcm_starts`; see +//! `cipher_gcm_set_nonce`. + +use alloc::{vec, vec::Vec}; + +use mbedtls::cipher::raw::{Cipher, CipherId, CipherMode, Operation}; +use mbedtls_sys_auto::{ + GCM_DECRYPT, GCM_ENCRYPT, MODE_GCM, cipher_context_t, gcm_context, gcm_starts, +}; +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, TEE_ERROR_MAC_INVALID, TEE_ERROR_SHORT_BUFFER, TEE_OperationMode, +}; + +use crate::tee::TeeResult; + +const GCM_BLOCK_SIZE: usize = 16; +/// mbedtls `MBEDTLS_MAX_IV_LENGTH`; longer GCM nonces must use `gcm_starts` directly. +const MBEDTLS_MAX_IV_LENGTH: usize = 16; + +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. +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 { + 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, + } + } + + 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(()) + } + + 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 { + cipher + .update_ad(&self.buffer) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + } + self.committed = true; + Ok(()) + } + + fn aad_bytes(&self) -> &[u8] { + &self.buffer + } + + fn enter_payload_phase_buffered_only(&mut self) -> TeeResult { + if self.payload_started { + return Ok(()); + } + self.payload_started = true; + Ok(()) + } + + 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, + } + } +} + +/// CCM or GCM payload replay state. +enum TeeAuthencPayloadCtx { + Ccm(TeeAuthencCcmCtx), + Gcm(TeeAuthencGcmCtx), +} + +impl TeeAuthencPayloadCtx { + fn fresh_standby_cipher(&self, algo: u32, key: &[u8]) -> TeeResult { + match self { + Self::Ccm(ccm) => ccm.fresh_standby_cipher(algo, key), + Self::Gcm(gcm) => gcm.fresh_standby_cipher(algo, key), + } + } + + fn update_payload( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: &[u8], + output: &mut [u8], + ) -> TeeResult { + match self { + Self::Ccm(ccm) => ccm.update_payload(algo, key, aad, input, output), + Self::Gcm(gcm) => gcm.update_payload(algo, key, aad, input, output), + } + } + + fn enc_final( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &mut [u8], + ) -> TeeResult { + match self { + Self::Ccm(ccm) => ccm.enc_final(algo, key, aad, input, output, tag), + Self::Gcm(gcm) => gcm.enc_final(algo, key, aad, input, output, tag), + } + } + + fn dec_final( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &[u8], + ) -> TeeResult { + match self { + Self::Ccm(ccm) => ccm.dec_final(algo, key, aad, input, output, tag), + Self::Gcm(gcm) => gcm.dec_final(algo, key, aad, input, output, tag), + } + } +} + +impl Clone for TeeAuthencPayloadCtx { + fn clone(&self) -> Self { + match self { + Self::Ccm(ccm) => Self::Ccm(ccm.clone()), + Self::Gcm(gcm) => Self::Gcm(gcm.clone()), + } + } +} + +/// Buffered AAD and payload for one AEAD operation (CCM or GCM). +pub(crate) struct TeeAuthencCtx { + aad: TeeAuthencAadCtx, + payload: TeeAuthencPayloadCtx, +} + +impl TeeAuthencCtx { + pub(crate) fn new_ccm( + algo: u32, + nonce: &[u8], + payload_len: usize, + aad_len: usize, + tag_len: usize, + mode: TEE_OperationMode, + ) -> Self { + Self { + aad: TeeAuthencAadCtx::new(algo, Some(aad_len)), + payload: TeeAuthencPayloadCtx::Ccm(TeeAuthencCcmCtx::new( + nonce, + payload_len, + aad_len, + tag_len, + mode, + )), + } + } + + pub(crate) fn new_gcm(algo: u32, nonce: &[u8], mode: TEE_OperationMode) -> Self { + Self { + aad: TeeAuthencAadCtx::new(algo, None), + payload: TeeAuthencPayloadCtx::Gcm(TeeAuthencGcmCtx::new(nonce, mode)), + } + } + + pub(crate) fn append_aad(&mut self, data: &[u8]) -> TeeResult { + self.aad.append_aad(data) + } + + pub(crate) fn aad_bytes(&self) -> &[u8] { + self.aad.aad_bytes() + } + + /// Enter payload phase: GCM replay feeds AAD itself; CCM flushes AAD to `cipher`. + pub(crate) fn enter_payload_phase(&mut self, cipher: &mut Cipher) -> TeeResult { + match &self.payload { + TeeAuthencPayloadCtx::Gcm(_) => self.aad.enter_payload_phase_buffered_only(), + TeeAuthencPayloadCtx::Ccm(_) => self.aad.enter_payload_phase(cipher), + } + } + + pub(crate) fn fresh_standby_cipher(&self, algo: u32, key: &[u8]) -> TeeResult { + self.payload.fresh_standby_cipher(algo, key) + } + + pub(crate) fn update_payload( + &mut self, + algo: u32, + key: &[u8], + input: &[u8], + output: &mut [u8], + ) -> TeeResult { + let aad = self.aad.aad_bytes(); + self.payload.update_payload(algo, key, aad, input, output) + } + + pub(crate) fn enc_final( + &mut self, + algo: u32, + key: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &mut [u8], + ) -> TeeResult { + let aad = self.aad.aad_bytes(); + self.payload.enc_final(algo, key, aad, input, output, tag) + } + + pub(crate) fn dec_final( + &mut self, + algo: u32, + key: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &[u8], + ) -> TeeResult { + let aad = self.aad.aad_bytes(); + self.payload.dec_final(algo, key, aad, input, output, tag) + } +} + +impl Clone for TeeAuthencCtx { + fn clone(&self) -> Self { + Self { + aad: self.aad.clone(), + payload: self.payload.clone(), + } + } +} + +fn gcm_mode(op: Operation) -> TeeResult { + match op { + Operation::Encrypt => Ok(GCM_ENCRYPT), + Operation::Decrypt => Ok(GCM_DECRYPT), + Operation::None => Err(TEE_ERROR_BAD_PARAMETERS), + } +} + +unsafe fn cipher_gcm_context(cipher: &mut Cipher) -> TeeResult<*mut gcm_context> { + let ctx = cipher as *mut Cipher as *mut cipher_context_t; + let ctx = unsafe { &*ctx }; + if ctx.cipher_info.is_null() || unsafe { (*ctx.cipher_info).mode != MODE_GCM } { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + Ok(ctx.cipher_ctx as *mut gcm_context) +} + +/// Prepare GCM after `set_key`: short nonces use `set_iv`; long nonces defer `gcm_starts` to +/// `cipher_gcm_update_ad` because mbedtls stores IV in a 16-byte `cipher_context_t::iv` buffer. +pub(crate) fn cipher_gcm_set_nonce(cipher: &mut Cipher, nonce: &[u8]) -> TeeResult { + if nonce.is_empty() { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + if nonce.len() <= MBEDTLS_MAX_IV_LENGTH { + cipher.set_iv(nonce).map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + } + cipher.reset().map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + Ok(()) +} + +/// Feed AAD to GCM with OP-TEE semantics (including nonces longer than 16 bytes). +pub(crate) fn cipher_gcm_update_ad( + cipher: &mut Cipher, + op: Operation, + nonce: &[u8], + ad: &[u8], +) -> TeeResult { + if nonce.is_empty() { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + if nonce.len() <= MBEDTLS_MAX_IV_LENGTH { + cipher.update_ad(ad).map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + return Ok(()); + } + let mode = gcm_mode(op)?; + unsafe { + let gcm = cipher_gcm_context(cipher)?; + if gcm_starts( + gcm, + mode, + nonce.as_ptr(), + nonce.len(), + ad.as_ptr(), + ad.len(), + ) != 0 + { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + } + Ok(()) +} + +pub(crate) fn cipher_uses_ccm_payload_buffer(algo: u32) -> bool { + matches!(algo, TEE_ALG_AES_CCM | TEE_ALG_SM4_CCM) +} + +pub(crate) fn cipher_uses_gcm_payload_buffer(algo: u32) -> bool { + matches!(algo, TEE_ALG_AES_GCM | TEE_ALG_SM4_GCM) +} + +pub(crate) fn cipher_uses_authenc_payload_buffer(algo: u32) -> bool { + cipher_uses_ccm_payload_buffer(algo) || cipher_uses_gcm_payload_buffer(algo) +} + +fn ccm_cipher_id(algo: u32) -> Option { + match algo { + TEE_ALG_AES_CCM => Some(CipherId::Aes), + TEE_ALG_SM4_CCM => Some(CipherId::SM4), + _ => None, + } +} + +fn gcm_cipher_id(algo: u32) -> Option { + match algo { + TEE_ALG_AES_GCM => Some(CipherId::Aes), + TEE_ALG_SM4_GCM => Some(CipherId::SM4), + _ => None, + } +} + +/// Buffered CCM payload for one AE operation. +pub(crate) struct TeeAuthencCcmCtx { + nonce: Vec, + payload_len: usize, + aad_len: usize, + tag_len: usize, + encrypt: bool, + buffer: Vec, + emitted: usize, +} + +impl TeeAuthencCcmCtx { + pub(crate) fn new( + nonce: &[u8], + payload_len: usize, + aad_len: usize, + tag_len: usize, + mode: TEE_OperationMode, + ) -> Self { + let encrypt = mode == TEE_OperationMode::TEE_MODE_ENCRYPT; + Self { + nonce: nonce.to_vec(), + payload_len, + aad_len, + tag_len, + encrypt, + buffer: Vec::new(), + emitted: 0, + } + } + + fn append(&mut self, data: &[u8]) -> TeeResult { + let new_len = self + .buffer + .len() + .checked_add(data.len()) + .ok_or(TEE_ERROR_BAD_PARAMETERS)?; + if new_len > self.payload_len { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + self.buffer.extend_from_slice(data); + Ok(()) + } + + /// Fresh mbedtls context after `starts_ccm` (no AAD/payload fed). Used for `TEE_CopyOperation` + /// instead of `Cipher::clone`, which corrupts CCM multipart state. + pub(crate) fn fresh_standby_cipher(&self, algo: u32, key: &[u8]) -> TeeResult { + self.setup_cipher(algo, key) + } + + fn setup_cipher(&self, algo: u32, key: &[u8]) -> TeeResult { + let cipher_id = ccm_cipher_id(algo).ok_or(TEE_ERROR_BAD_PARAMETERS)?; + let mut cipher = Cipher::setup(cipher_id, CipherMode::CCM, (key.len() * 8) as u32) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let op = if self.encrypt { + Operation::Encrypt + } else { + Operation::Decrypt + }; + cipher + .set_key(op, key) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + cipher + .set_iv(&self.nonce) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + cipher.reset().map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + cipher + .starts_ccm(self.payload_len, self.aad_len, self.tag_len) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + Ok(cipher) + } + + fn replay_process( + &self, + algo: u32, + key: &[u8], + aad: &[u8], + scratch: &mut [u8], + ) -> TeeResult { + let mut cipher = self.setup_cipher(algo, key)?; + cipher + .update_ad_ccm(aad) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let n = cipher + .update(&self.buffer, scratch) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + if n != self.buffer.len() { + return Err(TEE_ERROR_BAD_STATE); + } + Ok(n) + } + + pub(crate) fn update_payload( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: &[u8], + output: &mut [u8], + ) -> TeeResult { + self.append(input)?; + let mut scratch = vec![0u8; self.buffer.len() + GCM_BLOCK_SIZE]; + let n = self.replay_process(algo, key, aad, &mut scratch)?; + let new_len = n - self.emitted; + if output.len() < new_len { + return Err(TEE_ERROR_SHORT_BUFFER); + } + output[..new_len].copy_from_slice(&scratch[self.emitted..n]); + self.emitted = n; + Ok(new_len) + } + + pub(crate) fn enc_final( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &mut [u8], + ) -> TeeResult { + if let Some(data) = input { + self.append(data)?; + } + if self.buffer.len() != self.payload_len { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + let mut scratch = vec![0u8; self.buffer.len() + GCM_BLOCK_SIZE]; + let mut cipher = self.setup_cipher(algo, key)?; + cipher + .update_ad_ccm(aad) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let n = cipher + .update(&self.buffer, &mut scratch) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let tail_len = n - self.emitted; + if output.len() < tail_len { + return Err(TEE_ERROR_SHORT_BUFFER); + } + if tail_len > 0 { + output[..tail_len].copy_from_slice(&scratch[self.emitted..n]); + } + cipher + .write_tag(tag) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + self.emitted = n; + Ok(tail_len) + } + + pub(crate) fn dec_final( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &[u8], + ) -> TeeResult { + if let Some(data) = input { + self.append(data)?; + } + if self.buffer.len() != self.payload_len { + return Err(TEE_ERROR_BAD_PARAMETERS); + } + let mut scratch = vec![0u8; self.buffer.len() + GCM_BLOCK_SIZE]; + let mut cipher = self.setup_cipher(algo, key)?; + cipher + .update_ad_ccm(aad) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let n = cipher + .update(&self.buffer, &mut scratch) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let tail_len = n - self.emitted; + if output.len() < tail_len { + return Err(TEE_ERROR_SHORT_BUFFER); + } + if tail_len > 0 { + output[..tail_len].copy_from_slice(&scratch[self.emitted..n]); + } + cipher.check_tag(tag).map_err(|_| TEE_ERROR_MAC_INVALID)?; + self.emitted = n; + Ok(tail_len) + } +} + +impl Clone for TeeAuthencCcmCtx { + fn clone(&self) -> Self { + Self { + nonce: self.nonce.clone(), + payload_len: self.payload_len, + aad_len: self.aad_len, + tag_len: self.tag_len, + encrypt: self.encrypt, + buffer: self.buffer.clone(), + emitted: self.emitted, + } + } +} + +/// Buffered GCM payload for one AE operation. +pub(crate) struct TeeAuthencGcmCtx { + nonce: Vec, + encrypt: bool, + buffer: Vec, + emitted: usize, +} + +impl TeeAuthencGcmCtx { + pub(crate) fn new(nonce: &[u8], mode: TEE_OperationMode) -> Self { + let encrypt = mode == TEE_OperationMode::TEE_MODE_ENCRYPT; + Self { + nonce: nonce.to_vec(), + encrypt, + buffer: Vec::new(), + emitted: 0, + } + } + + fn append(&mut self, data: &[u8]) -> TeeResult { + self.buffer + .len() + .checked_add(data.len()) + .ok_or(TEE_ERROR_BAD_PARAMETERS)?; + self.buffer.extend_from_slice(data); + Ok(()) + } + + /// Fresh mbedtls GCM context (AAD/payload not fed). Used for `TEE_CopyOperation`. + pub(crate) fn fresh_standby_cipher(&self, algo: u32, key: &[u8]) -> TeeResult { + self.setup_cipher(algo, key) + } + + fn setup_cipher(&self, algo: u32, key: &[u8]) -> TeeResult { + let cipher_id = gcm_cipher_id(algo).ok_or(TEE_ERROR_BAD_PARAMETERS)?; + let mut cipher = Cipher::setup(cipher_id, CipherMode::GCM, (key.len() * 8) as u32) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + let op = if self.encrypt { + Operation::Encrypt + } else { + Operation::Decrypt + }; + cipher + .set_key(op, key) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + cipher_gcm_set_nonce(&mut cipher, &self.nonce)?; + Ok(cipher) + } + + fn gcm_operation(&self) -> Operation { + if self.encrypt { + Operation::Encrypt + } else { + Operation::Decrypt + } + } + + fn replay_process( + &self, + algo: u32, + key: &[u8], + aad: &[u8], + scratch: &mut [u8], + ) -> TeeResult { + let mut cipher = self.setup_cipher(algo, key)?; + cipher_gcm_update_ad(&mut cipher, self.gcm_operation(), &self.nonce, aad)?; + if self.buffer.is_empty() { + return Ok(0); + } + let n = cipher + .update(&self.buffer, scratch) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + if n != self.buffer.len() { + return Err(TEE_ERROR_BAD_STATE); + } + Ok(n) + } + + pub(crate) fn update_payload( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: &[u8], + output: &mut [u8], + ) -> TeeResult { + self.append(input)?; + let mut scratch = vec![0u8; self.buffer.len() + GCM_BLOCK_SIZE]; + let n = self.replay_process(algo, key, aad, &mut scratch)?; + let new_len = n - self.emitted; + if output.len() < new_len { + return Err(TEE_ERROR_SHORT_BUFFER); + } + output[..new_len].copy_from_slice(&scratch[self.emitted..n]); + self.emitted = n; + Ok(new_len) + } + + pub(crate) fn enc_final( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &mut [u8], + ) -> TeeResult { + if let Some(data) = input { + self.append(data)?; + } + let mut scratch = vec![0u8; self.buffer.len() + GCM_BLOCK_SIZE]; + let mut cipher = self.setup_cipher(algo, key)?; + cipher_gcm_update_ad(&mut cipher, self.gcm_operation(), &self.nonce, aad)?; + let n = if self.buffer.is_empty() { + 0 + } else { + cipher + .update(&self.buffer, &mut scratch) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)? + }; + let tail_len = n - self.emitted; + if output.len() < tail_len { + return Err(TEE_ERROR_SHORT_BUFFER); + } + if tail_len > 0 { + output[..tail_len].copy_from_slice(&scratch[self.emitted..n]); + } + cipher + .write_tag(tag) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + self.emitted = n; + Ok(tail_len) + } + + pub(crate) fn dec_final( + &mut self, + algo: u32, + key: &[u8], + aad: &[u8], + input: Option<&[u8]>, + output: &mut [u8], + tag: &[u8], + ) -> TeeResult { + if let Some(data) = input { + self.append(data)?; + } + let mut scratch = vec![0u8; self.buffer.len() + GCM_BLOCK_SIZE]; + let mut cipher = self.setup_cipher(algo, key)?; + cipher_gcm_update_ad(&mut cipher, self.gcm_operation(), &self.nonce, aad)?; + let n = if self.buffer.is_empty() { + 0 + } else { + cipher + .update(&self.buffer, &mut scratch) + .map_err(|_| TEE_ERROR_BAD_PARAMETERS)? + }; + let tail_len = n - self.emitted; + if output.len() < tail_len { + return Err(TEE_ERROR_SHORT_BUFFER); + } + if tail_len > 0 { + output[..tail_len].copy_from_slice(&scratch[self.emitted..n]); + } + cipher.check_tag(tag).map_err(|_| TEE_ERROR_MAC_INVALID)?; + self.emitted = n; + Ok(tail_len) + } +} + +impl Clone for TeeAuthencGcmCtx { + fn clone(&self) -> Self { + Self { + nonce: self.nonce.clone(), + encrypt: self.encrypt, + buffer: self.buffer.clone(), + emitted: self.emitted, + } + } +} diff --git a/tee/tee_kernel/src/tee/crypto/authenc_aad.rs b/tee/tee_kernel/src/tee/crypto/authenc_aad.rs deleted file mode 100644 index b94cca85138c57ff34e04771abb7a702ef65e5ad..0000000000000000000000000000000000000000 --- a/tee/tee_kernel/src/tee/crypto/authenc_aad.rs +++ /dev/null @@ -1,119 +0,0 @@ -// 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 2ddd2c7008978290df310b314c0817412081bfb6..387e8dc095db51b90d35a54a24ed870a6bf26706 100644 --- a/tee/tee_kernel/src/tee/crypto/crypto.rs +++ b/tee/tee_kernel/src/tee/crypto/crypto.rs @@ -30,7 +30,10 @@ 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}, + authenc::{ + TeeAuthencCtx, cipher_gcm_set_nonce, cipher_uses_authenc_aad_buffer, + cipher_uses_ccm_payload_buffer, cipher_uses_gcm_payload_buffer, + }, crypto_impl::{ EccAlgoKeyPair, EccComKeyPair, EccKeypair, Sm2DsaKeyPair, Sm2KepKeyPair, Sm2PkeKeyPair, crypto_ecc_keypair_ops, crypto_ecc_keypair_ops_generate, @@ -685,16 +688,64 @@ pub(crate) fn crypto_cipher_init( pending: [0; TeeCipherCtx::PENDING_MAX], pending_len: 0, xts, - authenc_aad: None, + authenc: None, })); Ok(()) } fn tee_cipher_ctx_flush_authenc_aad(op: &mut TeeCipherCtx) -> TeeResult { - let Some(aad) = &mut op.authenc_aad else { + let Some(authenc) = &mut op.authenc else { return Ok(()); }; - aad.enter_payload_phase(&mut op.cipher) + authenc.enter_payload_phase(&mut op.cipher) +} + +fn tee_cipher_ctx_replay_key( + cs: &TeeCrypState, + op: &TeeCipherCtx, +) -> TeeResult, alloc::vec::Vec)>> { + let Some(authenc) = &op.authenc else { + return Ok(None); + }; + let key = cryp_state_symmetric_key(cs)?; + let aad = authenc.aad_bytes().to_vec(); + Ok(Some((key, aad))) +} + +/// Copy cipher operation state for `TEE_CopyOperation` / `tee_cryp_state_copy`. +/// +/// CCM/GCM payload uses replay buffers; cloning `Cipher` shares broken mbedtls multipart state. +/// Only `authenc` buffers are cloned; dst gets a fresh standby `Cipher`. +pub(crate) fn crypto_cipher_ctx_copy_from( + cs: &TeeCrypState, + src: &TeeCipherCtx, +) -> TeeResult { + let cipher = if let Some(authenc) = &src.authenc { + let key = cryp_state_symmetric_key(cs)?; + authenc.fresh_standby_cipher(cs.algo, &key)? + } else { + src.cipher.clone() + }; + Ok(TeeCipherCtx { + cipher, + pending: src.pending, + pending_len: src.pending_len, + xts: src.xts.clone(), + authenc: src.authenc.clone(), + }) +} + +fn cryp_state_symmetric_key(cs: &TeeCrypState) -> TeeResult> { + let key1 = cs.key1.ok_or(TEE_ERROR_BAD_PARAMETERS)?; + let obj = tee_obj_get(key1 as tee_obj_id_type)?; + let guard = obj.lock(); + if guard.attr.is_empty() { + return Err(TEE_ERROR_BAD_STATE); + } + let TeeCryptObj::obj_secret(secret) = &guard.attr[0] else { + return Err(TEE_ERROR_BAD_STATE); + }; + Ok(secret.key().to_vec()) } fn cipher_uses_ecb_pending(algo: u32) -> bool { @@ -719,6 +770,12 @@ pub(crate) fn crypto_cipher_max_output_len( let block_buffered = cipher_uses_ecb_pending(cs_guard.algo) || op.xts.is_some(); let max_out = if block_buffered { (op.pending_len + input_len) / block_size * block_size + } else if op.authenc.is_some() { + // CCM/GCM replay returns only the new chunk (`input_len` bytes). + input_len + } else if cipher_uses_authenc_aad_buffer(cs_guard.algo) { + // GCM: match mbedtls `Cipher::update` slack (`in_len + block_size`). + input_len.saturating_add(block_size) } else { input_len + block_size }; @@ -799,10 +856,18 @@ pub(crate) fn crypto_cipher_update( ) -> TeeResult { let mut cs_guard = cs.lock(); let algo = cs_guard.algo; + let replay_key = match &cs_guard.ctx { + CrypCtx::CipherCtx(op) => tee_cipher_ctx_replay_key(&cs_guard, op)?, + _ => None, + }; 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((key, _aad)) = replay_key { + let authenc = op.authenc.as_mut().unwrap(); + return authenc.update_payload(algo, &key, input, output); + } if let Some(xts) = &mut op.xts { let stream = xts.stream(); let n = aes_xts_update_buffered( @@ -916,8 +981,12 @@ pub(crate) fn crypto_authenc_init( cipher .set_key(cipher_op, key) .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; - cipher.set_iv(nonce).map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; - cipher.reset().map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + if cipher_uses_gcm_payload_buffer(algo) { + cipher_gcm_set_nonce(&mut cipher, nonce)?; + } else { + cipher.set_iv(nonce).map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + cipher.reset().map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + } if cipher_mode == CipherMode::CCM { let payload_len = payload_len.ok_or(TEE_ERROR_BAD_PARAMETERS)?; let aad_len = aad_len.ok_or(TEE_ERROR_BAD_PARAMETERS)?; @@ -926,8 +995,20 @@ 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)) + let authenc = if cipher_uses_ccm_payload_buffer(algo) { + let payload_len = payload_len.ok_or(TEE_ERROR_BAD_PARAMETERS)?; + let aad_len = aad_len.ok_or(TEE_ERROR_BAD_PARAMETERS)?; + let tag_len = tag_len.ok_or(TEE_ERROR_BAD_PARAMETERS)?; + Some(TeeAuthencCtx::new_ccm( + algo, + nonce, + payload_len, + aad_len, + tag_len, + mode, + )) + } else if cipher_uses_gcm_payload_buffer(algo) { + Some(TeeAuthencCtx::new_gcm(algo, nonce, mode)) } else { None }; @@ -937,7 +1018,7 @@ pub(crate) fn crypto_authenc_init( pending: [0; TeeCipherCtx::PENDING_MAX], pending_len: 0, xts: None, - authenc_aad, + authenc, })); Ok(()) } else { @@ -948,10 +1029,10 @@ pub(crate) fn crypto_authenc_init( pub(crate) fn crypto_authenc_update_aad(cs: Arc>, aad: &[u8]) -> TeeResult { let mut cs_guard = cs.lock(); if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { - let Some(aad_ctx) = &mut op.authenc_aad else { + let Some(authenc) = &mut op.authenc else { return Err(TEE_ERROR_BAD_PARAMETERS); }; - aad_ctx.append_aad(aad) + authenc.append_aad(aad) } else { Err(TEE_ERROR_BAD_PARAMETERS) } @@ -964,9 +1045,18 @@ pub(crate) fn crypto_authenc_enc_final( tag: &mut [u8], ) -> TeeResult { let mut cs_guard = cs.lock(); - let mut res: usize = 0; + let algo = cs_guard.algo; + let replay_key = match &cs_guard.ctx { + CrypCtx::CipherCtx(op) => tee_cipher_ctx_replay_key(&cs_guard, op)?, + _ => None, + }; if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { tee_cipher_ctx_flush_authenc_aad(op)?; + if let Some((key, _aad)) = replay_key { + let authenc = op.authenc.as_mut().unwrap(); + return authenc.enc_final(algo, &key, input, output, tag); + } + let mut res: usize = 0; if let Some(input) = input { res = op .cipher @@ -989,9 +1079,18 @@ pub(crate) fn crypto_authenc_dec_final( tag: &[u8], ) -> TeeResult { let mut cs_guard = cs.lock(); - let mut res: usize = 0; + let algo = cs_guard.algo; + let replay_key = match &cs_guard.ctx { + CrypCtx::CipherCtx(op) => tee_cipher_ctx_replay_key(&cs_guard, op)?, + _ => None, + }; if let CrypCtx::CipherCtx(op) = &mut cs_guard.ctx { tee_cipher_ctx_flush_authenc_aad(op)?; + if let Some((key, _aad)) = replay_key { + let authenc = op.authenc.as_mut().unwrap(); + return authenc.dec_final(algo, &key, input, output, tag); + } + let mut res: usize = 0; if let Some(input) = input { res = op .cipher @@ -1000,7 +1099,7 @@ pub(crate) fn crypto_authenc_dec_final( } op.cipher .check_tag(tag) - .map_err(|_| TEE_ERROR_BAD_PARAMETERS)?; + .map_err(|_| TEE_ERROR_MAC_INVALID)?; Ok(res) } else { Err(TEE_ERROR_BAD_PARAMETERS) diff --git a/tee/tee_kernel/src/tee/crypto/mod.rs b/tee/tee_kernel/src/tee/crypto/mod.rs index 6335fb1087cfc5b84be6661287642f0c671f3d81..a07f16b8a7a5cce9b49bdbe6ec930924ab8770f9 100644 --- a/tee/tee_kernel/src/tee/crypto/mod.rs +++ b/tee/tee_kernel/src/tee/crypto/mod.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. pub mod aes_xts; -pub mod authenc_aad; +pub mod authenc; #[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 2ca78b533adb98ccd3be8a1fca5c63e901a21d25..7b7e0ace84ad0437b85db35fcdc9609fc5b62e67 100644 --- a/tee/tee_kernel/src/tee/tee_svc_cryp2.rs +++ b/tee/tee_kernel/src/tee/tee_svc_cryp2.rs @@ -95,9 +95,9 @@ use crate::{ crypto_acipher_rsanopad_encrypt, crypto_acipher_rsassa_sign, crypto_acipher_rsassa_verify, crypto_acipher_sm2_pke_decrypt, crypto_acipher_sm2_pke_encrypt, crypto_authenc_dec_final, crypto_authenc_enc_final, - crypto_authenc_init, crypto_authenc_update_aad, crypto_cipher_final, - crypto_cipher_init, crypto_cipher_max_output_len, crypto_cipher_update, - crypto_ecc_init, crypto_rsa_init, + crypto_authenc_init, crypto_authenc_update_aad, crypto_cipher_ctx_copy_from, + crypto_cipher_final, crypto_cipher_init, crypto_cipher_max_output_len, + crypto_cipher_update, crypto_ecc_init, crypto_rsa_init, }, }, libmbedtls::bignum::BigNum, @@ -193,8 +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, + /// Buffered AAD and payload for CCM/GCM (mbedtls multipart AEAD is unreliable). + pub authenc: Option, } impl TeeCipherCtx { @@ -208,7 +208,7 @@ impl Clone for TeeCipherCtx { pending: self.pending, pending_len: self.pending_len, xts: self.xts.clone(), - authenc_aad: self.authenc_aad.clone(), + authenc: self.authenc.clone(), } } } @@ -806,13 +806,14 @@ pub fn tee_cryp_state_copy(dst_id: u32, src_id: u32) -> TeeResult { } match tee_alg_get_class(src_guard.algo) { - TEE_OPERATION_CIPHER => { + TEE_OPERATION_CIPHER | TEE_OPERATION_AE => { let CrypCtx::CipherCtx(src_cipher) = &src_guard.ctx else { return Err(TEE_ERROR_BAD_STATE); }; + let copied = crypto_cipher_ctx_copy_from(&src_guard, src_cipher)?; match &mut dst_guard.ctx { - CrypCtx::CipherCtx(dst_cipher) => *dst_cipher = src_cipher.clone(), - _ => dst_guard.ctx = CrypCtx::CipherCtx(src_cipher.clone()), + CrypCtx::CipherCtx(dst_cipher) => **dst_cipher = copied, + _ => dst_guard.ctx = CrypCtx::CipherCtx(Box::new(copied)), } } TEE_OPERATION_DIGEST => { @@ -2494,6 +2495,103 @@ pub mod tests_cryp { assert_eq!(&out2_dst[..16], &out2_src[..16]); } + /// regression_4005 case 0: partial AE update, then `TEE_CopyOperation` / `ENC_FINAL` on both ops. + #[unittest::def_test(custom)] + fn test_cryp_state_copy_aes_ccm_after_partial_ae() { + let mut src_state: u32 = 0; + let mut dst_state: u32 = 0; + let mut src_obj_id = TestUserValue::::from_value(0).unwrap(); + let mut dst_obj_id = TestUserValue::::from_value(0).unwrap(); + + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, src_obj_id.as_user_ref()).unwrap(); + let src_obj_id = src_obj_id.read(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, dst_obj_id.as_user_ref()).unwrap(); + let dst_obj_id = dst_obj_id.read(); + + let key = [ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, + 0x4e, 0x4f, + ]; + let nonce = [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16]; + let aad = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; + let plain = [0x20, 0x21, 0x22, 0x23]; + let cipher_expect = [0x71, 0x62, 0x01, 0x5b]; + let tag_expect = [0x4d, 0xac, 0x25, 0x5d]; + + for obj_id in [src_obj_id, dst_obj_id] { + let obj = tee_obj_get(obj_id as tee_obj_id_type).unwrap(); + let mut guard = obj.lock(); + let mut secret = tee_cryp_obj_secret_wrapper::new(32); + secret.set_secret_data(&key).unwrap(); + let _ = core::mem::replace(&mut guard.attr[0], TeeCryptObj::obj_secret(secret)); + } + + tee_cryp_state_alloc( + TEE_ALG_AES_CCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(src_obj_id as _), + None, + &mut src_state, + ) + .unwrap(); + tee_cryp_state_alloc( + TEE_ALG_AES_CCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(dst_obj_id as _), + None, + &mut dst_state, + ) + .unwrap(); + + tee_cryp_authenc_init( + src_state, + &nonce, + Some(aad.len()), + Some(tag_expect.len()), + Some(plain.len()), + ) + .unwrap(); + tee_cryp_authenc_update_aad(src_state, &aad[..4]).unwrap(); + tee_cryp_authenc_update_aad(src_state, &aad[4..]).unwrap(); + + let mut out_src = [0u8; 32]; + let n = tee_cryp_authenc_update_payload(src_state, &plain[..2], &mut out_src).unwrap(); + assert_eq!(n, 2); + + tee_cryp_state_copy(dst_state, src_state).unwrap(); + + let mut out_src_tail = [0u8; 32]; + let mut out_dst_tail = [0u8; 32]; + let mut tag_src = [0u8; 4]; + let mut tag_dst = [0u8; 4]; + + let tail_len = tee_cryp_authenc_enc_final( + src_state, + Some(&plain[2..]), + &mut out_src_tail, + &mut tag_src, + ) + .unwrap(); + let tail_len2 = tee_cryp_authenc_enc_final( + dst_state, + Some(&plain[2..]), + &mut out_dst_tail, + &mut tag_dst, + ) + .unwrap(); + + assert_eq!(tail_len, tail_len2); + assert_eq!(&out_src[..2], &cipher_expect[..2]); + assert_eq!(&out_src_tail[..tail_len], &cipher_expect[2..]); + assert_eq!(&out_dst_tail[..tail_len2], &cipher_expect[2..]); + assert_eq!(tag_src, tag_expect); + assert_eq!(tag_dst, tag_expect); + + // regression_4005 case 0: dual FREE_OPERATION — must not corrupt heap via CCM clone. + tee_cryp_state_free(src_state).unwrap(); + tee_cryp_state_free(dst_state).unwrap(); + } + #[unittest::def_test(custom)] fn test_cryp_cmac_aes() { let mut state: u32 = 0; @@ -3187,6 +3285,234 @@ pub mod tests_cryp { ); } + /// OP-TEE xtest regression_4005 AE case 6/7: AES-GCM vect1 (empty AAD/payload). + #[unittest::def_test(custom)] + fn test_cryp_aes_gcm_vect1_empty_payload() { + let key = [0u8; 16]; + let nonce = [0u8; 12]; + let tag_expect = [ + 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, + 0x45, 0x5a, + ]; + + let mut enc_obj_id = TestUserValue::::from_value(0).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, enc_obj_id.as_user_ref()).unwrap(); + let enc_obj_id = enc_obj_id.read(); + let obj = tee_obj_get(enc_obj_id as tee_obj_id_type).unwrap(); + let mut obj_guard = obj.lock(); + 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); + + let mut enc_state: u32 = 0; + tee_cryp_state_alloc( + TEE_ALG_AES_GCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(enc_obj_id as _), + None, + &mut enc_state, + ) + .unwrap(); + tee_cryp_authenc_init(enc_state, &nonce, None, None, None).unwrap(); + + let mut out = [0u8; 16]; + let mut tag = [0u8; 16]; + let n = tee_cryp_authenc_enc_final(enc_state, None, &mut out, &mut tag).unwrap(); + assert_eq!(n, 0); + assert_eq!(tag, tag_expect); + + let mut dec_obj_id = TestUserValue::::from_value(0).unwrap(); + let mut dec_obj2_id = TestUserValue::::from_value(0).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, dec_obj_id.as_user_ref()).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, dec_obj2_id.as_user_ref()).unwrap(); + let dec_obj_id = dec_obj_id.read(); + let dec_obj2_id = dec_obj2_id.read(); + for obj_id in [dec_obj_id, dec_obj2_id] { + let obj = tee_obj_get(obj_id as tee_obj_id_type).unwrap(); + let mut obj_guard = obj.lock(); + 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)); + } + + let mut dec_state: u32 = 0; + let mut dec_state2: u32 = 0; + tee_cryp_state_alloc( + TEE_ALG_AES_GCM, + TEE_OperationMode::TEE_MODE_DECRYPT, + Some(dec_obj_id as _), + None, + &mut dec_state, + ) + .unwrap(); + tee_cryp_state_alloc( + TEE_ALG_AES_GCM, + TEE_OperationMode::TEE_MODE_DECRYPT, + Some(dec_obj2_id as _), + None, + &mut dec_state2, + ) + .unwrap(); + tee_cryp_authenc_init(dec_state, &nonce, None, None, None).unwrap(); + tee_cryp_state_copy(dec_state2, dec_state).unwrap(); + + let mut plain = [0u8; 16]; + let n = tee_cryp_authenc_dec_final(dec_state, None, &mut plain, &tag_expect).unwrap(); + assert_eq!(n, 0); + + let mut plain2 = [0u8; 16]; + let n2 = tee_cryp_authenc_dec_final(dec_state2, None, &mut plain2, &tag_expect).unwrap(); + assert_eq!(n2, 0); + } + + /// OP-TEE xtest regression_4005 AE case 8: AES-GCM vect2, 9+7 payload split + copy. + #[unittest::def_test(custom)] + fn test_cryp_aes_gcm_vect2_split_encrypt_copy() { + let key = [0u8; 16]; + let nonce = [0u8; 12]; + let plain = [0u8; 16]; + let cipher_expect = [ + 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, + 0xfe, 0x78, + ]; + let tag_expect = [ + 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, + 0xbd, 0xdf, + ]; + + let mut obj_id = TestUserValue::::from_value(0).unwrap(); + let mut obj2_id = TestUserValue::::from_value(0).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, obj_id.as_user_ref()).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, obj2_id.as_user_ref()).unwrap(); + let obj_id = obj_id.read(); + let obj2_id = obj2_id.read(); + for id in [obj_id, obj2_id] { + let obj = tee_obj_get(id as tee_obj_id_type).unwrap(); + let mut guard = obj.lock(); + let mut secret = tee_cryp_obj_secret_wrapper::new(32); + secret.set_secret_data(&key).unwrap(); + let _ = core::mem::replace(&mut guard.attr[0], TeeCryptObj::obj_secret(secret)); + } + + let mut src_state: u32 = 0; + let mut dst_state: u32 = 0; + tee_cryp_state_alloc( + TEE_ALG_AES_GCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(obj_id as _), + None, + &mut src_state, + ) + .unwrap(); + tee_cryp_state_alloc( + TEE_ALG_AES_GCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(obj2_id as _), + None, + &mut dst_state, + ) + .unwrap(); + tee_cryp_authenc_init(src_state, &nonce, None, None, None).unwrap(); + + let mut out = [0u8; 32]; + let n = tee_cryp_authenc_update_payload(src_state, &plain[..9], &mut out).unwrap(); + assert_eq!(n, 9); + assert_eq!(&out[..9], &cipher_expect[..9]); + + tee_cryp_state_copy(dst_state, src_state).unwrap(); + + let mut tag_src = [0u8; 16]; + let mut tag_dst = [0u8; 16]; + let tail_src = + tee_cryp_authenc_enc_final(src_state, Some(&plain[9..]), &mut out[9..], &mut tag_src) + .unwrap(); + let tail_dst = + tee_cryp_authenc_enc_final(dst_state, Some(&plain[9..]), &mut out[9..], &mut tag_dst) + .unwrap(); + + assert_eq!(tail_src, 7); + assert_eq!(tail_dst, 7); + assert_eq!(&out[..16], cipher_expect); + assert_eq!(tag_src, tag_expect); + assert_eq!(tag_dst, tag_expect); + } + + /// OP-TEE xtest regression_4005 AE case 14: AES-GCM vect6, 60-byte nonce. + #[unittest::def_test(custom)] + fn test_cryp_aes_gcm_vect6_long_nonce_encrypt() { + let key = [ + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, + 0x83, 0x08, + ]; + let nonce = [ + 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, 0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, + 0x69, 0xaa, 0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, 0xe4, 0xc3, 0x03, 0xd2, + 0xa3, 0x18, 0xa7, 0x28, 0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39, 0xfc, 0xf0, + 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54, 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57, + 0xa6, 0x37, 0xb3, 0x9b, + ]; + let aad = [ + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, + 0xbe, 0xef, 0xab, 0xad, 0xda, 0xd2, + ]; + let plain = [ + 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, + 0x26, 0x9a, 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, + 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, + 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39, + ]; + let cipher_expect = [ + 0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6, 0x03, 0xa0, 0x33, 0xac, 0xa1, 0x3f, + 0xb8, 0x94, 0xbe, 0x91, 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8, 0xba, 0x26, 0x2a, 0x3c, + 0xca, 0x7e, 0x2c, 0xa7, 0x01, 0xe4, 0xa9, 0xa4, 0xfb, 0xa4, 0x3c, 0x90, 0xcc, 0xdc, + 0xb2, 0x81, 0xd4, 0x8c, 0x7c, 0x6f, 0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4, 0x17, 0x03, + 0x4c, 0x34, 0xae, 0xe5, + ]; + let tag_expect = [ + 0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa, 0x46, 0x2a, 0xf4, 0x3c, 0x16, 0x99, + 0xd0, 0x50, + ]; + + let mut obj_id = TestUserValue::::from_value(0).unwrap(); + syscall_cryp_obj_alloc(TEE_TYPE_AES as _, 128, obj_id.as_user_ref()).unwrap(); + let obj_id = obj_id.read(); + let obj = tee_obj_get(obj_id as tee_obj_id_type).unwrap(); + let mut guard = obj.lock(); + let mut secret = tee_cryp_obj_secret_wrapper::new(32); + secret.set_secret_data(&key).unwrap(); + let _ = core::mem::replace(&mut guard.attr[0], TeeCryptObj::obj_secret(secret)); + drop(guard); + + let mut state: u32 = 0; + tee_cryp_state_alloc( + TEE_ALG_AES_GCM, + TEE_OperationMode::TEE_MODE_ENCRYPT, + Some(obj_id as _), + None, + &mut state, + ) + .unwrap(); + tee_cryp_authenc_init(state, &nonce, None, None, None).unwrap(); + + tee_cryp_authenc_update_aad(state, &aad[..5]).unwrap(); + tee_cryp_authenc_update_aad(state, &aad[5..]).unwrap(); + + let mut out = [0u8; 64]; + let mut n = 0usize; + for chunk in plain.chunks(9) { + n += tee_cryp_authenc_update_payload(state, chunk, &mut out[n..]).unwrap(); + } + assert_eq!(n, plain.len()); + + let mut tag = [0u8; 16]; + let tail = tee_cryp_authenc_enc_final(state, None, &mut out[n..], &mut tag).unwrap(); + assert_eq!(tail, 0); + assert_eq!(&out[..plain.len()], cipher_expect); + assert_eq!(tag, tag_expect); + } + #[unittest::def_test(custom)] fn test_cryp_authenc_aad_rejected_after_payload() { let mut state: u32 = 0;