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
// Copyright 2020 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

#[allow(unused_macros)]
macro_rules! impl_aead {
    ($impl:ident, $name:expr, $key_len:ident, $nonce_len:ident, $tag_len:ident) => {
        impl $crate::ciphers::traits::Aead for $impl {
            type KeyLength = $key_len;
            type NonceLength = $nonce_len;
            type TagLength = $tag_len;

            const NAME: &'static str = $name;

            /// Warning: type conversions on the tag type can be tricky. instead of `&mut tag.try_into().unwrap()` use
            /// `(&mut tag).try_into().unwrap()`
            fn encrypt(
                key: &$crate::ciphers::traits::Key<Self>,
                nonce: &$crate::ciphers::traits::Nonce<Self>,
                associated_data: &[u8],
                plaintext: &[u8],
                ciphertext: &mut [u8],
                tag: &mut $crate::ciphers::traits::Tag<Self>,
            ) -> crate::Result<usize> {
                use aead::{AeadInPlace, KeyInit};

                if plaintext.len() > ciphertext.len() {
                    return Err($crate::Error::BufferSize {
                        name: "ciphertext",
                        needs: plaintext.len(),
                        has: ciphertext.len(),
                    });
                }

                ciphertext[..plaintext.len()].copy_from_slice(plaintext);

                let out: $crate::ciphers::traits::Tag<Self> = Self::new(key)
                    .encrypt_in_place_detached(nonce, associated_data, ciphertext)
                    .map_err(|_| $crate::Error::CipherError { alg: Self::NAME })?;

                tag.copy_from_slice(&out);

                Ok(plaintext.len())
            }

            fn decrypt(
                key: &$crate::ciphers::traits::Key<Self>,
                nonce: &$crate::ciphers::traits::Nonce<Self>,
                associated_data: &[u8],
                plaintext: &mut [u8],
                ciphertext: &[u8],
                tag: &$crate::ciphers::traits::Tag<Self>,
            ) -> crate::Result<usize> {
                use aead::{AeadInPlace, KeyInit};

                if ciphertext.len() > plaintext.len() {
                    return Err($crate::Error::BufferSize {
                        name: "plaintext",
                        needs: ciphertext.len(),
                        has: plaintext.len(),
                    });
                }

                plaintext[..ciphertext.len()].copy_from_slice(ciphertext);

                Self::new(key)
                    .decrypt_in_place_detached(nonce, associated_data, plaintext, tag)
                    .map_err(|_| $crate::Error::CipherError { alg: Self::NAME })?;

                Ok(ciphertext.len())
            }
        }

        /// A helper function to encrypt `plaintext` with `key`.
        /// The return value is arbitrarily chosen as `nonce || tag || ciphertext` for historic reasons, mainly
        /// compatibility with out wallet libraries. The nonce is randomly chosen.
        #[cfg(all(feature = "random", feature = "std"))]
        pub fn aead_encrypt(key: &[u8], plaintext: &[u8]) -> crate::Result<Vec<u8>> {
            if key.len() != <$impl as $crate::ciphers::traits::Aead>::KEY_LENGTH {
                return Err($crate::Error::BufferSize {
                    name: "key",
                    needs: <$impl as $crate::ciphers::traits::Aead>::KEY_LENGTH,
                    has: key.len(),
                });
            }

            let mut nonce = [0; <$impl as $crate::ciphers::traits::Aead>::NONCE_LENGTH];
            let mut tag = vec![0; <$impl as $crate::ciphers::traits::Aead>::TAG_LENGTH];
            let mut ciphertext = vec![0; plaintext.len()];

            crate::utils::rand::fill(&mut nonce)?;

            <$impl as $crate::ciphers::traits::Aead>::encrypt(
                $crate::ciphers::traits::Key::<$impl>::from_slice(&key),
                $crate::ciphers::traits::Nonce::<$impl>::from_slice(&nonce),
                &[],
                plaintext,
                &mut ciphertext,
                $crate::ciphers::traits::Tag::<$impl>::from_mut_slice(&mut tag),
            )?;

            let mut ret = nonce.to_vec();
            ret.append(&mut tag);
            ret.append(&mut ciphertext);

            Ok(ret)
        }

        /// A helper function to decrypt `ciphertext` with `key`.
        /// The input value is assumed to be `nonce || tag || ciphertext` for historic reason, mainly compatibility with
        /// out wallet libraries.
        #[cfg(feature = "std")]
        pub fn aead_decrypt(key: &[u8], ciphertext: &[u8]) -> crate::Result<Vec<u8>> {
            if key.len() != <$impl as $crate::ciphers::traits::Aead>::KEY_LENGTH {
                return Err($crate::Error::BufferSize {
                    name: "key",
                    needs: <$impl as $crate::ciphers::traits::Aead>::KEY_LENGTH,
                    has: key.len(),
                });
            }

            let nonce = &ciphertext[..<$impl as $crate::ciphers::traits::Aead>::NONCE_LENGTH];
            let tag = &ciphertext[<$impl as $crate::ciphers::traits::Aead>::NONCE_LENGTH
                ..<$impl as $crate::ciphers::traits::Aead>::NONCE_LENGTH
                    + <$impl as $crate::ciphers::traits::Aead>::TAG_LENGTH];
            let ciphertext = &ciphertext[<$impl as $crate::ciphers::traits::Aead>::NONCE_LENGTH
                + <$impl as $crate::ciphers::traits::Aead>::TAG_LENGTH..];
            let mut plaintext = vec![0u8; ciphertext.len()];

            <$impl as $crate::ciphers::traits::Aead>::decrypt(
                $crate::ciphers::traits::Key::<$impl>::from_slice(&key),
                $crate::ciphers::traits::Nonce::<$impl>::from_slice(&nonce),
                &[],
                &mut plaintext,
                ciphertext,
                $crate::ciphers::traits::Tag::<$impl>::from_slice(&tag),
            )?;

            Ok(plaintext)
        }
    };
}