From d7245693a7bf05ace1bbd02147ab1e8a7b57802c Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 1 May 2020 16:07:39 +0000 Subject: [PATCH] rust: Add peripheral-side GCM tag checking (and testing) --- rust/cryptest/src/main.rs | 21 ++++++++++++-- rust/k210-shared/src/soc/aes.rs | 51 ++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/rust/cryptest/src/main.rs b/rust/cryptest/src/main.rs index a496b2b..a2adccd 100644 --- a/rust/cryptest/src/main.rs +++ b/rust/cryptest/src/main.rs @@ -429,7 +429,10 @@ fn main() -> ! { cipher_mode::CBC => "CBC", cipher_mode::GCM => "GCM", }).unwrap(); - aes::run( + + // copy in tag for verification check + tag_out[0..tv.tag.len()].copy_from_slice(tv.tag); + let vs = aes::run( aes, tv.cipher_mode, encrypt_sel::ENCRYPTION, @@ -450,6 +453,12 @@ fn main() -> ! { write!(stdout, " ").unwrap(); if tv.cipher_mode == cipher_mode::GCM { + let vs = vs.unwrap(); + if vs { + write!(stdout, "HWTAGMATCH ").unwrap(); + } else { + write!(stdout, "HWTAGMISMATCH ").unwrap(); + } if &tag_out[0..tv.tag.len()] == tv.tag { write!(stdout, "TAGMATCH").unwrap(); } else { @@ -466,7 +475,9 @@ fn main() -> ! { let mut pt_out = [0u8; 128]; let mut tag_out2 = [0u8; 16]; - aes::run( + // copy in tag for verification check + tag_out2[0..tv.tag.len()].copy_from_slice(tv.tag); + let vs = aes::run( aes, tv.cipher_mode, encrypt_sel::DECRYPTION, @@ -486,6 +497,12 @@ fn main() -> ! { } write!(stdout, " ").unwrap(); if tv.cipher_mode == cipher_mode::GCM { + let vs = vs.unwrap(); + if vs { + write!(stdout, "HWTAGMATCH ").unwrap(); + } else { + write!(stdout, "HWTAGMISMATCH ").unwrap(); + } if &tag_out2[0..tv.tag.len()] == tv.tag { write!(stdout, "TAGMATCH").unwrap(); } else { diff --git a/rust/k210-shared/src/soc/aes.rs b/rust/k210-shared/src/soc/aes.rs index 90e9ecf..0764b9d 100644 --- a/rust/k210-shared/src/soc/aes.rs +++ b/rust/k210-shared/src/soc/aes.rs @@ -96,31 +96,45 @@ fn finish( aes: &mut pac::AES, cipher_mode: cipher_mode, tag: Option<&mut [u8]>, - ) { + ) -> Option { + let mut tag_chk_status: Option = None; unsafe { if cipher_mode == cipher_mode::GCM { - // Read and store tag, if requested - // TODO: the engine also supports writing a tag through gcm_in_tag - // and verifying it, presumably in linear time. + // GO through tag verification (this is mandatory for GCM mode, + // otherwise the engine will hang) while aes.tag_in_flag.read().tag_in_flag() != DATA_IN_FLAG_A::CAN_INPUT { atomic::compiler_fence(Ordering::SeqCst) } - // Write a fake tag - aes.gcm_in_tag[0].write(|w| w.bits(0)); - aes.gcm_in_tag[1].write(|w| w.bits(0)); - aes.gcm_in_tag[2].write(|w| w.bits(0)); - aes.gcm_in_tag[3].write(|w| w.bits(0)); + if let Some(ref tag) = tag { + // If there is a tag passed in, verify it + for i in 0..4 { + aes.gcm_in_tag[3-i].write(|w| w.bits( + u32::from_be_bytes(tag[i*4..i*4+4].try_into().unwrap()))); + } + } else { + // Write a fake tag + for i in 0..4 { + aes.gcm_in_tag[3-i].write(|w| w.bits(0)); + } + } + // Wait until tag was checked while aes.tag_chk.read().tag_chk() == TAG_CHK_A::BUSY { atomic::compiler_fence(Ordering::SeqCst) } if let Some(tag) = tag { + // Store tag check status, but only if there was a tag to check + tag_chk_status = Some(aes.tag_chk.read().tag_chk() == TAG_CHK_A::SUCCESS); + + // Read and store tag, if requested for i in 0..4 { let val = aes.gcm_out_tag[3 - i].read().bits(); tag[i*4..i*4+4].copy_from_slice(&val.to_be_bytes()); } } + + aes.tag_clear.write(|w| w.bits(0)); } // Wait until AES engine finished @@ -128,10 +142,16 @@ fn finish( atomic::compiler_fence(Ordering::SeqCst) } } + tag_chk_status } /** AES operation (encrypt or decrypt) using hardware engine. Takes a &mut * AES as only one operation can be active at a time. + * In GCM mode, a mutable array can be passed which both provides the + * tag to check against, as well as receives the computed tag. + * Returns tag validation status (true if input tag matches computed tag, + * false otherwise). This comparison is done in hardware in (one would expect) + * constant time. * * Supported modes: * @@ -151,7 +171,7 @@ pub fn run( ind: &[u8], outd: &mut [u8], tag: Option<&mut [u8]>, - ) + ) -> Option { setup(aes, cipher_mode, encrypt_sel, key, iv, aad, ind.len()); @@ -172,7 +192,7 @@ pub fn run( } - finish(aes, cipher_mode, tag); + finish(aes, cipher_mode, tag) } /** Iterator-based interface. */ @@ -213,10 +233,15 @@ impl <'a, I> Iterator for OutIterator<'a, I> impl <'a, I> OutIterator<'a, I> where I: Iterator { - pub fn finish(&mut self, tag: Option<&mut [u8]>) { + + /** + * Returns tag validation status (true if input tag matches computed tag, + * false otherwise). None if no tag was provided or the mode was not GCM. + */ + pub fn finish(&mut self, tag: Option<&mut [u8]>) -> Option { assert!(self.iptr >= self.len && self.optr >= self.len); - finish(self.aes, self.cipher_mode, tag); self.finished = true; + finish(self.aes, self.cipher_mode, tag) } }