1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// src/lib.rs - aez C-to-Rust bindings
// Copyright (C) 2019  Katzenpost Developers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! [AEZv5](https://web.cs.ucdavis.edu/~rogaway/aez).
//!
//! > AEZ is an authenticated-encryption (AE) scheme optimized for ease of correct
//! > use (“AE made EZ”). It was invented by Viet Tung Hoang, Ted Krovetz, and
//! > Phillip Rogaway. The algorithm encrypts a plaintext by appending to it a fixed
//! > authentication block (some zero bits) and then enciphering the resulting string
//! > with an arbitrary-input-length blockcipher, this tweaked by the nonce, AD, and
//! > authenticator length. The approach results in strong security and usability
//! > properties, including nonce-reuse misuse resistance, automatic exploitation of
//! > decryption-verified redundancy, and arbitrary, user-selectable length expansion.
//! > AEZ is parallelizable and its computational cost is roughly that of OCB. On recent
//! > Intel processors, AEZ runs at about 0.7 cpb.
//!
//! The C implementation is compiled assuming AES-NI support. There is no software fallback
//! implemented in this crate.
//!
//! ```
//! # use aez::Aez;
//! # let secret_key = [0u8; 48];
//! // The secret key may be any byte slice. 48 bytes are recommended.
//! let cipher = Aez::new(&secret_key);
//!
//! // Expand the ciphertext by 16 bytes for authentication.
//! let mut pt = b"Hello world!".to_vec();
//! let mut ct = vec![0u8; pt.len() + 16];
//!
//! // Encrypt the message with a nonce, and optionally additional data.
//! cipher.encrypt(&[0], None, &pt, &mut ct);
//!
//! // Decrypt and validate the ciphertext.
//! cipher.decrypt(&[0], None, &ct, &mut pt).expect("invalid ciphertext");
//!
//! // Message decrypted!
//! assert_eq!(pt, b"Hello world!");
//! ```

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
compile_error!("AEZ requires x86 or x86_64.");

#[repr(C)]
#[repr(align(16))]
pub struct Aez([u8; 144]);

extern "C" {
    fn aez_setup(key: *const u8, keylen: u32, ctx: &mut Aez);
    fn aez_encrypt(
        ctx: &Aez,
        n: *const u8,
        nbytes: u32,
        ad: *const u8,
        adbytes: u32,
        abytes: u32,
        src: *const u8,
        bytes: u32,
        dst: *mut u8,
    );
    fn aez_decrypt(
        ctx: &Aez,
        n: *const u8,
        nbytes: u32,
        ad: *const u8,
        adbytes: u32,
        abytes: u32,
        src: *const u8,
        bytes: u32,
        dst: *mut u8,
    ) -> isize;
}

impl Aez {
    /// Create a new Aez instance keyed with variable length. Aez recommends a 48 byte key.
    pub fn new(key: &[u8]) -> Self {
        let mut aez = Aez([0u8; 144]);

        unsafe {
            aez_setup(key.as_ptr(), key.len() as u32, &mut aez);
        }

        aez
    }

    /// Encrypt a message. The nonce length must be `1..=16`. The ciphertext may be up to 16 bytes
    /// larger than the message, these extra bytes add authentication. Additionally, the ciphertext
    /// must not be larger than `2^32 - 1`.
    ///
    /// Will panic if the above constraints are broken.
    pub fn encrypt<'a>(&self, n: &[u8], ad: impl Into<Option<&'a [u8]>>, pt: &[u8], ct: &mut [u8]) {
        assert!(
            ct.len() >= pt.len(),
            "Ciphertext must not be smaller than the plaintext."
        );
        assert!(ct.len() < core::u32::MAX as usize, "ciphertext length too long");
        assert!(
            ct.len() - pt.len() <= 16,
            "tau is bounded up to 16 for this C implementation."
        );
        assert!(n.len() > 0, "Nonce must not have length zero.");

        let ad = ad.into();

        unsafe {
            aez_encrypt(
                self,
                n.as_ptr(),
                n.len() as u32,
                if let Some(ad) = ad {
                    ad.as_ptr()
                } else {
                    core::ptr::null()
                },
                if let Some(ad) = ad {
                    ad.len() as u32
                } else {
                    0
                },
                ct.len() as u32 - pt.len() as u32,
                pt.as_ptr(),
                pt.len() as u32,
                ct.as_mut_ptr(),
            );
        }
    }

    /// Decrypt a message. The nonce length must be `1..=16`. The ciphertext may be up to 16 bytes
    /// larger than the message, these extra bytes add authentication. Additionally, the ciphertext
    /// must not be larger than `2^32 - 1`.
    ///
    /// Will panic if the above constraints are broken.
    pub fn decrypt<'a>(
        &self,
        n: &[u8],
        ad: impl Into<Option<&'a [u8]>>,
        ct: &[u8],
        pt: &mut [u8],
    ) -> Result<(), ()> {
        assert!(
            ct.len() >= pt.len(),
            "Ciphertext must not be smaller than the plaintext."
        );
        assert!(ct.len() < core::u32::MAX as usize, "ciphertext length too long");
        assert!(
            ct.len() - pt.len() <= 16,
            "tau is bounded up to 16 for this C implementation."
        );
        assert!(n.len() != 0 && n.len() <= 16, "invalid nonce");

        let ad = ad.into();

        match unsafe {
            aez_decrypt(
                self,
                n.as_ptr(),
                n.len() as u32,
                if let Some(ad) = ad {
                    ad.as_ptr()
                } else {
                    core::ptr::null()
                },
                if let Some(ad) = ad {
                    ad.len() as u32
                } else {
                    0
                },
                ct.len() as u32 - pt.len() as u32,
                ct.as_ptr(),
                ct.len() as u32,
                pt.as_mut_ptr(),
            )
        } {
            0 => Ok(()),
            _ => Err(()),
        }
    }
}