|
| 1 | +#![no_std] |
| 2 | +#![cfg_attr(docsrs, feature(doc_cfg))] |
| 3 | +#![doc = include_str!("../README.md")] |
| 4 | +#![doc( |
| 5 | + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", |
| 6 | + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" |
| 7 | +)] |
| 8 | +#![deny(unsafe_code)] |
| 9 | +#![warn(missing_docs, rust_2018_idioms)] |
| 10 | + |
| 11 | +//! # Usage |
| 12 | +//! |
| 13 | +//! Simple usage (allocating, no associated data): |
| 14 | +//! |
| 15 | +#![cfg_attr(feature = "getrandom", doc = "```")] |
| 16 | +#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")] |
| 17 | +//! # fn main() -> Result<(), Box<dyn core::error::Error>> { |
| 18 | +//! // NOTE: requires the `getrandom` feature is enabled |
| 19 | +//! |
| 20 | +//! use dndk_gcm::{ |
| 21 | +//! aead::{Aead, AeadCore, Key, KeyInit}, |
| 22 | +//! DndkGcm, Nonce |
| 23 | +//! }; |
| 24 | +//! |
| 25 | +//! let key = Key::<DndkGcm>::from_slice(&[0u8; 32]); |
| 26 | +//! let cipher = DndkGcm::new(key); |
| 27 | +//! let nonce = Nonce::from_slice(&[0u8; 24]); // 192-bits; MUST be unique per message |
| 28 | +//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())?; |
| 29 | +//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())?; |
| 30 | +//! assert_eq!(&plaintext, b"plaintext message"); |
| 31 | +//! # Ok(()) |
| 32 | +//! # } |
| 33 | +//! ``` |
| 34 | +
|
| 35 | +pub use aead; |
| 36 | +pub use aes; |
| 37 | +pub use aes_gcm; |
| 38 | + |
| 39 | +use aead::{ |
| 40 | + AeadCore, AeadInOut, Error, KeyInit, KeySizeUser, TagPosition, array::Array, inout::InOutBuf, |
| 41 | +}; |
| 42 | +use aes::Aes256; |
| 43 | +use aes_gcm::Aes256Gcm; |
| 44 | +use cipher::{BlockCipherEncrypt, BlockSizeUser, consts::U12}; |
| 45 | + |
| 46 | +/// DNDK-GCM with a 24-byte nonce (KC_Choice = 0). |
| 47 | +#[derive(Clone)] |
| 48 | +pub struct DndkGcm { |
| 49 | + aes: Aes256, |
| 50 | +} |
| 51 | + |
| 52 | +type KeySize = <Aes256Gcm as KeySizeUser>::KeySize; |
| 53 | + |
| 54 | +/// DNDK-GCM nonce (24 bytes). |
| 55 | +pub type Nonce = aes_gcm::Nonce<cipher::consts::U24>; |
| 56 | + |
| 57 | +/// DNDK-GCM key. |
| 58 | +pub type Key<B = Aes256> = aes_gcm::Key<B>; |
| 59 | + |
| 60 | +/// DNDK-GCM tag. |
| 61 | +pub type Tag<Size = <Aes256Gcm as AeadCore>::TagSize> = aes_gcm::Tag<Size>; |
| 62 | + |
| 63 | +/// Maximum length of plaintext. |
| 64 | +pub const P_MAX: u64 = (1 << 36) - 32; |
| 65 | + |
| 66 | +/// Maximum length of associated data. |
| 67 | +pub const A_MAX: u64 = (1 << 61) - 1; |
| 68 | + |
| 69 | +/// Maximum length of ciphertext. |
| 70 | +pub const C_MAX: u64 = (1 << 36) - 32; |
| 71 | + |
| 72 | +impl AeadCore for DndkGcm { |
| 73 | + type NonceSize = cipher::consts::U24; |
| 74 | + type TagSize = <Aes256Gcm as AeadCore>::TagSize; |
| 75 | + const TAG_POSITION: TagPosition = TagPosition::Postfix; |
| 76 | +} |
| 77 | + |
| 78 | +impl KeySizeUser for DndkGcm { |
| 79 | + type KeySize = KeySize; |
| 80 | +} |
| 81 | + |
| 82 | +impl KeyInit for DndkGcm { |
| 83 | + fn new(key: &Key) -> Self { |
| 84 | + Self { |
| 85 | + aes: Aes256::new(key), |
| 86 | + } |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +impl AeadInOut for DndkGcm { |
| 91 | + fn encrypt_inout_detached( |
| 92 | + &self, |
| 93 | + nonce: &Nonce, |
| 94 | + associated_data: &[u8], |
| 95 | + buffer: InOutBuf<'_, '_, u8>, |
| 96 | + ) -> Result<Tag, Error> { |
| 97 | + if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX { |
| 98 | + return Err(Error); |
| 99 | + } |
| 100 | + |
| 101 | + let (gcm_iv, key) = derive_key_and_iv::<24>(&self.aes, nonce.as_slice()); |
| 102 | + Aes256Gcm::new(&key).encrypt_inout_detached(&gcm_iv, associated_data, buffer) |
| 103 | + } |
| 104 | + |
| 105 | + fn decrypt_inout_detached( |
| 106 | + &self, |
| 107 | + nonce: &Nonce, |
| 108 | + associated_data: &[u8], |
| 109 | + buffer: InOutBuf<'_, '_, u8>, |
| 110 | + tag: &Tag, |
| 111 | + ) -> Result<(), Error> { |
| 112 | + if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX { |
| 113 | + return Err(Error); |
| 114 | + } |
| 115 | + |
| 116 | + let (gcm_iv, key) = derive_key_and_iv::<24>(&self.aes, nonce.as_slice()); |
| 117 | + Aes256Gcm::new(&key).decrypt_inout_detached(&gcm_iv, associated_data, buffer, tag) |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +type Block = Array<u8, <Aes256 as BlockSizeUser>::BlockSize>; |
| 122 | + |
| 123 | +type GcmIv = aes_gcm::Nonce<U12>; |
| 124 | + |
| 125 | +type DerivedKey = Key<Aes256Gcm>; |
| 126 | + |
| 127 | +fn derive_key_and_iv<const LN: usize>(aes: &Aes256, nonce: &[u8]) -> (GcmIv, DerivedKey) { |
| 128 | + debug_assert_eq!(nonce.len(), LN); |
| 129 | + |
| 130 | + // Algorithm 1 (KC_Choice = 0): pad nonce, split into head/tail, derive DK and 12-byte IV. |
| 131 | + let mut npadded = [0u8; 27]; |
| 132 | + npadded[..LN].copy_from_slice(nonce); |
| 133 | + |
| 134 | + let mut gcm_iv = GcmIv::default(); |
| 135 | + gcm_iv.copy_from_slice(&npadded[15..27]); |
| 136 | + |
| 137 | + let config_byte = 8u8 * ((LN - 12) as u8); |
| 138 | + |
| 139 | + let mut b0 = Block::default(); |
| 140 | + b0[..15].copy_from_slice(&npadded[..15]); |
| 141 | + b0[15] = config_byte; |
| 142 | + |
| 143 | + let mut b1 = b0; |
| 144 | + b1[15] = config_byte.wrapping_add(1); |
| 145 | + |
| 146 | + let mut b2 = b0; |
| 147 | + b2[15] = config_byte.wrapping_add(2); |
| 148 | + |
| 149 | + let mut x0 = b0; |
| 150 | + let mut x1 = b1; |
| 151 | + let mut x2 = b2; |
| 152 | + aes.encrypt_block(&mut x0); |
| 153 | + aes.encrypt_block(&mut x1); |
| 154 | + aes.encrypt_block(&mut x2); |
| 155 | + |
| 156 | + let mut y1 = x1; |
| 157 | + let mut y2 = x2; |
| 158 | + for i in 0..y1.len() { |
| 159 | + y1[i] ^= x0[i]; |
| 160 | + y2[i] ^= x0[i]; |
| 161 | + } |
| 162 | + |
| 163 | + let mut key = DerivedKey::default(); |
| 164 | + key[..16].copy_from_slice(&y1); |
| 165 | + key[16..].copy_from_slice(&y2); |
| 166 | + |
| 167 | + (gcm_iv, key) |
| 168 | +} |
0 commit comments