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
//! Authenticated encryption interface definitions.

use crate::cipher::Cipher;

/// A AEAD mode of operation.
pub trait Aead {
    /// Adds associated data `ad`.
    ///
    /// Can be invoked multiple times *prior to invoking*
    /// [`Aead::encrypt`] or [`Aead::decrypt`] to provide the
    /// additional authenticated data in a streaming fashion.
    fn update(&mut self, ad: &[u8]);

    /// Encrypts one block `src` to `dst`.
    fn encrypt(&mut self, dst: &mut [u8], src: &[u8]);
    /// Decrypts one block `src` to `dst`.
    fn decrypt(&mut self, dst: &mut [u8], src: &[u8]);

    /// Produce the digest.
    fn digest(&mut self, digest: &mut [u8]);

    /// Length of the digest in bytes.
    fn digest_size(&self) -> usize;
}

/// Internal interface to Nettle's AEAD modes.
///
/// Some of Nettle's AEAD modes require additional authenticated data
/// to be updated in multiples of the ciphers block size (except for
/// the last chunk), and will either silently mis-compute the digest
/// (EAX, OCB), or abort(2) (GCM).
///
/// For these modes, we use [`AdStream`] to stream the AAD, and trait
/// [`AeadInternal`] as raw interface.
pub trait AeadInternal {
    /// Adds associated data `ad`.
    ///
    /// Note: can be invoked multiple times to stream the additional
    /// authenticated data, but the length of all but the last chunk
    /// **MUST BE** of a multiple of the cipher's block size.  Failure
    /// to adhere to that may lead to Nettle aborting or silently
    /// mis-computing the digest.
    ///
    /// Note: **MUST NOT BE** invoked after invoking
    /// [`AeadInternal::_encrypt`] or [`AeadInternal::_decrypt`].
    /// Failure to adhere to that may lead to Nettle aborting or
    /// silently mis-computing.
    fn update_internal(&mut self, ad: &[u8]);

    /// Encrypts one block `src` to `dst`.
    fn encrypt_internal(&mut self, dst: &mut [u8], src: &[u8]);
    /// Decrypts one block `src` to `dst`.
    fn decrypt_internal(&mut self, dst: &mut [u8], src: &[u8]);

    /// Produce the digest.
    fn digest_internal(&mut self, digest: &mut [u8]);

    /// Length of the digest in bytes.
    fn digest_size_internal(&self) -> usize;
}

/// Streaming AAD support for EAX, OCB, and GCM.
///
/// Some of Nettle's AEAD modes require additional authenticated data
/// to be updated in multiples of the ciphers block size (except for
/// the last chunk), and will either silently mis-compute the digest
/// (EAX, OCB), or abort(2) (GCM).
///
/// For these modes, we use [`AdStream`] to stream the AAD, and trait
/// [`AeadInternal`] as raw interface.
pub struct AdStream<C: Cipher> {
    buffer: Vec<u8>,
    finalized: bool,
    cipher: std::marker::PhantomData<*const C>,
}

impl<C: Cipher> Default for AdStream<C> {
    fn default() -> Self {
        AdStream {
            buffer: Vec::with_capacity(C::BLOCK_SIZE),
            finalized: false,
            cipher: std::marker::PhantomData,
        }
    }
}

impl<C: Cipher> AdStream<C> {
    /// Stream the given AAD.
    ///
    /// Note: additional data provided after invoking
    /// [`AdStream::finalize`] is **discarded** and an error is
    /// emitted to stderr.  This will be an error in future versions.
    pub fn update(&mut self, aad: &[u8], context: &mut dyn AeadInternal) {
        self.stream(aad, false, context);
    }

    /// Finalize the AAD streaming.
    ///
    /// After calling this function, no further AAD may be streamed.
    /// This function is idempotent.
    pub fn finalize(&mut self, context: &mut dyn AeadInternal) {
        if ! self.finalized {
            self.stream(&[], true, context);
        }
    }

    /// Stream the given AAD and finalize streaming.
    ///
    /// After calling this function with `last = true`, no further AAD
    /// may be streamed.
    ///
    /// Note: additional data provided after invoking
    /// [`AdStream::finalize`] or this function with `last = true` is
    /// **discarded** and an error is emitted to stderr.  This will be
    /// an error in future versions.
    fn stream(&mut self, mut data: &[u8], last: bool,
              context: &mut dyn AeadInternal)
    {
        debug_assert!(self.buffer.len() < C::BLOCK_SIZE);

        // XXX: This should return an error or be made impossible
        // using type states.
        if self.finalized {
            eprintln!("nettle::aead: AAD supplied after cipher use");
            return;
        }

        if self.buffer.len() + data.len() < C::BLOCK_SIZE {
            // No full block.  Buffer.
            self.buffer.extend_from_slice(data);
        } else if self.buffer.is_empty() {
            // No data in buffer, update digest with all whole blocks
            // from `data`.
            let n_chunks = data.len() / C::BLOCK_SIZE;
            let n_chunks_len = n_chunks * C::BLOCK_SIZE;
            context.update_internal(&data[..n_chunks_len]);

            // Finally, buffer the rest, if any.
            self.buffer.extend_from_slice(&data[n_chunks_len..]);
        } else {
            // Some buffered data.  Fill up to the BLOCK_SIZE and
            // update digest from that.
            let missing = (C::BLOCK_SIZE - self.buffer.len()).min(data.len());
            self.buffer.extend_from_slice(&data[..missing]);
            debug_assert_eq!(self.buffer.len(), C::BLOCK_SIZE);
            context.update_internal(&self.buffer[..]);
            self.buffer.clear();
            data = &data[missing..];

            // Update digest with all whole blocks from `data`.
            let n_chunks = data.len() / C::BLOCK_SIZE;
            let n_chunks_len = n_chunks * C::BLOCK_SIZE;
            context.update_internal(&data[..n_chunks_len]);

            // Finally, buffer the rest, if any.
            self.buffer.extend_from_slice(&data[n_chunks_len..]);
        }

        if last {
            if ! self.buffer.is_empty() {
                context.update_internal(&self.buffer[..]);
                self.buffer.clear();
            }
            self.finalized = true;
        }

        debug_assert!(self.buffer.len() < C::BLOCK_SIZE);
    }
}