1 /** 2 * ChaCha20Poly1305 AEAD 3 * 4 * Copyright: 5 * (C) 2014 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.modes.aead.chacha20poly1305; 12 13 import botan.constants; 14 static if (BOTAN_HAS_AEAD_CHACHA20_POLY1305): 15 import botan.modes.aead.aead; 16 import botan.stream.stream_cipher; 17 import botan.mac.mac; 18 import botan.algo_base.transform; 19 import botan.block.block_cipher; 20 import botan.libstate.libstate; 21 import botan.utils.loadstor; 22 import botan.utils.mem_ops; 23 24 /** 25 * Base class 26 * See draft-irtf-cfrg-chacha20-poly1305-03 for specification 27 * If a nonce of 64 bits is used the older version described in 28 * draft-agl-tls-chacha20poly1305-04 is used instead. 29 */ 30 abstract class ChaCha20Poly1305Mode : AEADMode, Transformation 31 { 32 public: 33 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { 34 if(!validNonceLength(nonce_len)) 35 throw new InvalidIVLength(name, nonce_len); 36 37 m_ctext_len = 0; 38 m_nonce_len = nonce_len; 39 40 m_chacha.setIv(nonce, nonce_len); 41 42 { 43 ubyte[64] zeros; 44 m_chacha.cipher1(zeros.ptr, zeros.length); 45 m_poly1305.setKey(zeros.ptr, 32); 46 } 47 // Remainder of output is discard 48 49 m_poly1305.update(m_ad); 50 51 if(cfrgVersion()) { 52 if (m_ad.length % 16) { 53 ubyte[64] zeros; 54 m_poly1305.update(zeros.ptr, 16 - m_ad.length() % 16); 55 } 56 } 57 else { 58 updateLength(m_ad.length); 59 } 60 61 return SecureVector!ubyte(); 62 } 63 override void setAssociatedData(const(ubyte)* ad, size_t ad_len) { 64 if(m_ctext_len) 65 throw new Exception("Too late to set AD for ChaCha20Poly1305"); 66 m_ad = SecureVector!ubyte(ad[0 .. ad_len]); 67 } 68 69 override @property string name() const { return "ChaCha20Poly1305"; } 70 override size_t updateGranularity() const { return 64; } 71 override KeyLengthSpecification keySpec() const { return KeyLengthSpecification(32); } 72 override bool validNonceLength(size_t n) const { return (n == 8 || n == 12); } 73 override size_t tagSize() const { return 16; } 74 75 override void clear() { 76 m_chacha.clear(); 77 m_poly1305.clear(); 78 m_ad.clear(); 79 m_ctext_len = 0; 80 } 81 82 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 83 override void keySchedule(const(ubyte)* key, size_t length) { 84 m_chacha.setKey(key, length); 85 } 86 87 protected: 88 this() { 89 AlgorithmFactory af = globalState().algorithmFactory(); 90 m_chacha = af.makeStreamCipher("ChaCha"); 91 m_poly1305 = af.makeMac("Poly1305"); 92 } 93 94 Unique!StreamCipher m_chacha; 95 Unique!MessageAuthenticationCode m_poly1305; 96 97 SecureVector!ubyte m_ad; 98 size_t m_nonce_len; 99 size_t m_ctext_len; 100 101 bool cfrgVersion() const { return m_nonce_len == 12; } 102 void updateLength(size_t len) { 103 ubyte[8] len8; 104 storeLittleEndian(cast(ulong)len, len8.ptr); 105 m_poly1305.update(len8.ptr, 8); 106 } 107 } 108 109 /** 110 * ChaCha20Poly1305 Encryption 111 */ 112 final class ChaCha20Poly1305Encryption : ChaCha20Poly1305Mode, Transformation 113 { 114 public: 115 this() { super(); } 116 override size_t outputLength(size_t input_length) const { return input_length + tagSize(); } 117 118 override size_t minimumFinalSize() const { return 0; } 119 120 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 121 { 122 assert(buffer.length >= offset, "Offset is sane"); 123 const size_t sz = buffer.length - offset; 124 ubyte* buf = buffer.ptr + offset; 125 126 m_chacha.cipher1(buf, sz); 127 m_poly1305.update(buf, sz); // poly1305 of ciphertext 128 m_ctext_len += sz; 129 130 } 131 132 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 133 { 134 update(buffer, offset); 135 if(cfrgVersion()) 136 { 137 if (m_ctext_len % 16) 138 { 139 ubyte[16] zeros; 140 m_poly1305.update(zeros.ptr, 16-m_ctext_len%16); 141 } 142 updateLength(m_ad.length); 143 } 144 updateLength(m_ctext_len); 145 146 const SecureVector!ubyte mac = m_poly1305.finished(); 147 buffer ~= mac.ptr[0 .. tagSize()]; 148 m_ctext_len = 0; 149 150 } 151 152 // Interface fallthrough 153 override string provider() const { return "core"; } 154 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 155 override size_t updateGranularity() const { return super.updateGranularity(); } 156 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 157 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 158 override @property string name() const { return super.name; } 159 override void clear() { return super.clear(); } 160 }; 161 162 /** 163 * ChaCha20Poly1305 Decryption 164 */ 165 final class ChaCha20Poly1305Decryption : ChaCha20Poly1305Mode, Transformation 166 { 167 public: 168 169 this() { super(); } 170 override size_t outputLength(size_t input_length) const { 171 assert(input_length > tagSize(), "Sufficient input"); 172 return input_length - tagSize(); 173 } 174 175 override size_t minimumFinalSize() const { return 0; } 176 177 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) { 178 assert(buffer.length >= offset, "Offset is sane"); 179 const size_t sz = buffer.length - offset; 180 ubyte* buf = buffer.ptr + offset; 181 182 m_poly1305.update(buf, sz); // poly1305 of ciphertext 183 m_chacha.cipher1(buf, sz); 184 m_ctext_len += sz; 185 186 } 187 188 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) { 189 assert(buffer.length >= offset, "Offset is sane"); 190 const size_t sz = buffer.length - offset; 191 ubyte* buf = buffer.ptr + offset; 192 193 assert(sz >= tagSize(), "Have the tag as part of final input"); 194 195 const size_t remaining = sz - tagSize(); 196 197 if(remaining) { 198 m_poly1305.update(buf, remaining); // poly1305 of ciphertext 199 m_chacha.cipher1(buf, remaining); 200 m_ctext_len += remaining; 201 } 202 203 if(cfrgVersion()) { 204 if (m_ctext_len % 16) 205 { 206 ubyte[16] zeros; 207 m_poly1305.update(zeros.ptr, 16-m_ctext_len%16); 208 } 209 updateLength(m_ad.length); 210 } 211 212 updateLength(m_ctext_len); 213 const SecureVector!ubyte mac = m_poly1305.finished(); 214 215 const ubyte* included_tag = buf + remaining; 216 217 m_ctext_len = 0; 218 219 if(!sameMem(mac.ptr, included_tag, tagSize())) 220 throw new IntegrityFailure("ChaCha20Poly1305 tag check failed"); 221 222 buffer.resize(offset + remaining); 223 } 224 225 // Interface fallthrough 226 override string provider() const { return "core"; } 227 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 228 override size_t updateGranularity() const { return super.updateGranularity(); } 229 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 230 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 231 override @property string name() const { return super.name; } 232 override void clear() { return super.clear(); } 233 } 234