xaes_256_gcm/
lib.rs

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//! use xaes_256_gcm::{
19//!     Xaes256Gcm, Nonce, Key,
20//!     aead::{Aead, AeadCore, KeyInit},
21//! };
22//!
23//! let key = Xaes256Gcm::generate_key().expect("generate key");
24//! let cipher = Xaes256Gcm::new(&key);
25//! let nonce = Xaes256Gcm::generate_nonce().expect("Generate nonce"); // 192-bits
26//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?;
27//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
28//! assert_eq!(&plaintext, b"plaintext message");
29//! # Ok(())
30//! # }
31//! ```
32
33pub use aead;
34pub use aes;
35pub use aes_gcm;
36
37use core::ops::{Div, Mul};
38
39use aead::{
40    AeadCore, AeadInOut, Error, KeyInit, KeySizeUser, TagPosition, array::Array, inout::InOutBuf,
41};
42use aes::Aes256;
43use aes_gcm::Aes256Gcm;
44use cipher::{BlockCipherEncrypt, BlockSizeUser, consts::U2};
45
46/// XAES-256-GCM
47#[derive(Clone)]
48pub struct Xaes256Gcm {
49    aes: Aes256,
50    k1: Block,
51}
52
53type KeySize = <Aes256Gcm as KeySizeUser>::KeySize;
54type NonceSize = <<Aes256Gcm as AeadCore>::NonceSize as Mul<U2>>::Output;
55type TagSize = <Aes256Gcm as AeadCore>::TagSize;
56type Block = Array<u8, <Aes256 as BlockSizeUser>::BlockSize>;
57
58/// XAES-256-GCM nonce.
59pub type Nonce<Size = NonceSize> = aes_gcm::Nonce<Size>;
60
61/// XAES-256-GCM key.
62pub type Key<B = Aes256> = aes_gcm::Key<B>;
63
64/// XAES-256-GCM tag.
65pub type Tag<Size = TagSize> = aes_gcm::Tag<Size>;
66
67/// Maximum length of plaintext.
68pub const P_MAX: u64 = 1 << 36;
69
70/// Maximum length of associated data.
71// pub const A_MAX: u64 = 1 << 61;
72pub const A_MAX: u64 = 1 << 36;
73
74/// Maximum length of ciphertext.
75pub const C_MAX: u64 = (1 << 36) + 16;
76
77impl AeadCore for Xaes256Gcm {
78    type NonceSize = NonceSize;
79    type TagSize = TagSize;
80    const TAG_POSITION: TagPosition = TagPosition::Postfix;
81}
82
83impl KeySizeUser for Xaes256Gcm {
84    type KeySize = KeySize;
85}
86
87impl KeyInit for Xaes256Gcm {
88    // Implements step 1 and 2 of the spec.
89    fn new(key: &Key) -> Self {
90        let aes = Aes256::new(key);
91
92        // L = AES-256ₖ(0¹²⁸)
93        let mut k1 = Block::default();
94        aes.encrypt_block(&mut k1);
95
96        // If MSB₁(L) = 0 then K1 = L << 1 Else K1 = (L << 1) ⊕ 0¹²⁰10000111
97        let mut msb = 0;
98        for i in (0..k1.len()).rev() {
99            let new_msb = k1[i] >> 7;
100            k1[i] = (k1[i] << 1) | msb;
101            msb = new_msb;
102        }
103
104        let b = k1.len() - 1;
105        k1[b] ^= msb * 0b10000111;
106
107        Self { aes, k1 }
108    }
109}
110
111impl AeadInOut for Xaes256Gcm {
112    fn encrypt_inout_detached(
113        &self,
114        nonce: &Nonce,
115        associated_data: &[u8],
116        buffer: InOutBuf<'_, '_, u8>,
117    ) -> Result<Tag, Error> {
118        if buffer.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
119            return Err(Error);
120        }
121
122        let (n1, n) = nonce.split_ref::<<NonceSize as Div<U2>>::Output>();
123        let k = self.derive_key(n1);
124        Aes256Gcm::new(&k).encrypt_inout_detached(n, associated_data, buffer)
125    }
126
127    fn decrypt_inout_detached(
128        &self,
129        nonce: &Nonce,
130        associated_data: &[u8],
131        buffer: InOutBuf<'_, '_, u8>,
132        tag: &Tag,
133    ) -> Result<(), Error> {
134        if buffer.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
135            return Err(Error);
136        }
137
138        let (n1, n) = nonce.split_ref::<<NonceSize as Div<U2>>::Output>();
139        let k = self.derive_key(n1);
140        Aes256Gcm::new(&k).decrypt_inout_detached(n, associated_data, buffer, tag)
141    }
142}
143
144impl Xaes256Gcm {
145    // Implements steps 3 - 5 of the spec.
146    fn derive_key(&self, n1: &Nonce<<NonceSize as Div<U2>>::Output>) -> Key<Aes256Gcm> {
147        // M1 = 0x00 || 0x01 || X || 0x00 || N[:12]
148        let mut m1 = Block::default();
149        m1[..4].copy_from_slice(&[0, 1, b'X', 0]);
150        m1[4..].copy_from_slice(n1);
151
152        // M2 = 0x00 || 0x02 || X || 0x00 || N[:12]
153        let mut m2 = Block::default();
154        m2[..4].copy_from_slice(&[0, 2, b'X', 0]);
155        m2[4..].copy_from_slice(n1);
156
157        // Kₘ = AES-256ₖ(M1 ⊕ K1)
158        // Kₙ = AES-256ₖ(M2 ⊕ K1)
159        // Kₓ = Kₘ || Kₙ = AES-256ₖ(M1 ⊕ K1) || AES-256ₖ(M2 ⊕ K1)
160        let mut key: Key<Aes256Gcm> = Array::default();
161        let (km, kn) = key.split_ref_mut::<<KeySize as Div<U2>>::Output>();
162        for i in 0..km.len() {
163            km[i] = m1[i] ^ self.k1[i];
164        }
165        for i in 0..kn.len() {
166            kn[i] = m2[i] ^ self.k1[i];
167        }
168
169        self.aes.encrypt_block(km);
170        self.aes.encrypt_block(kn);
171        key
172    }
173}