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 ubyte[64] zeros; 43 m_chacha.cipher1(zeros.ptr, zeros.length); 44 45 m_poly1305.setKey(zeros.ptr, 32); 46 // Remainder of output is discard 47 48 m_poly1305.update(m_ad); 49 50 if(cfrgVersion()) { 51 auto padding = Vector!ubyte(16 - m_ad.length % 16); 52 m_poly1305.update(padding); 53 } 54 else { 55 updateLength(m_ad.length); 56 } 57 58 return SecureVector!ubyte(); 59 } 60 override void setAssociatedData(const(ubyte)* ad, size_t ad_len) { 61 if(m_ctext_len) 62 throw new Exception("Too late to set AD for ChaCha20Poly1305"); 63 m_ad = SecureVector!ubyte(ad[0 .. ad_len]); 64 } 65 66 override @property string name() const { return "ChaCha20Poly1305"; } 67 override size_t updateGranularity() const { return 64; } 68 override KeyLengthSpecification keySpec() const { return KeyLengthSpecification(32); } 69 override bool validNonceLength(size_t n) const { return (n == 8 || n == 12); } 70 override size_t tagSize() const { return 16; } 71 72 override void clear() { 73 m_chacha.clear(); 74 m_poly1305.clear(); 75 m_ad.clear(); 76 m_ctext_len = 0; 77 } 78 79 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 80 override void keySchedule(const(ubyte)* key, size_t length) { 81 m_chacha.setKey(key, length); 82 } 83 84 protected: 85 this() { 86 AlgorithmFactory af = globalState().algorithmFactory(); 87 m_chacha = af.makeStreamCipher("ChaCha"); 88 m_poly1305 = af.makeMac("Poly1305"); 89 } 90 91 Unique!StreamCipher m_chacha; 92 Unique!MessageAuthenticationCode m_poly1305; 93 94 SecureVector!ubyte m_ad; 95 size_t m_nonce_len; 96 size_t m_ctext_len; 97 98 bool cfrgVersion() const { return m_nonce_len == 12; } 99 void updateLength(size_t len) { 100 ubyte[8] len8; 101 storeLittleEndian(cast(ulong)len, len8.ptr); 102 m_poly1305.update(len8.ptr, 8); 103 } 104 } 105 106 /** 107 * ChaCha20Poly1305 Encryption 108 */ 109 final class ChaCha20Poly1305Encryption : ChaCha20Poly1305Mode, Transformation 110 { 111 public: 112 this() { super(); } 113 override size_t outputLength(size_t input_length) const { return input_length + tagSize(); } 114 115 override size_t minimumFinalSize() const { return 0; } 116 117 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) 118 { 119 assert(buffer.length >= offset, "Offset is sane"); 120 const size_t sz = buffer.length - offset; 121 ubyte* buf = buffer.ptr + offset; 122 123 m_chacha.cipher1(buf, sz); 124 m_poly1305.update(buf, sz); // poly1305 of ciphertext 125 m_ctext_len += sz; 126 127 } 128 129 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) 130 { 131 update(buffer, offset); 132 if(cfrgVersion()) 133 { 134 auto padding = Vector!ubyte(16 - m_ctext_len % 16); 135 m_poly1305.update(padding); 136 updateLength(m_ad.length); 137 } 138 updateLength(m_ctext_len); 139 140 const SecureVector!ubyte mac = m_poly1305.finished(); 141 buffer ~= mac.ptr[0 .. tagSize()]; 142 m_ctext_len = 0; 143 144 } 145 146 // Interface fallthrough 147 override string provider() const { return "core"; } 148 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 149 override size_t updateGranularity() const { return super.updateGranularity(); } 150 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 151 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 152 override @property string name() const { return super.name; } 153 override void clear() { return super.clear(); } 154 }; 155 156 /** 157 * ChaCha20Poly1305 Decryption 158 */ 159 final class ChaCha20Poly1305Decryption : ChaCha20Poly1305Mode, Transformation 160 { 161 public: 162 163 this() { super(); } 164 override size_t outputLength(size_t input_length) const { 165 assert(input_length > tagSize(), "Sufficient input"); 166 return input_length - tagSize(); 167 } 168 169 override size_t minimumFinalSize() const { return 0; } 170 171 override void update(ref SecureVector!ubyte buffer, size_t offset = 0) { 172 assert(buffer.length >= offset, "Offset is sane"); 173 const size_t sz = buffer.length - offset; 174 ubyte* buf = buffer.ptr + offset; 175 176 m_poly1305.update(buf, sz); // poly1305 of ciphertext 177 m_chacha.cipher1(buf, sz); 178 m_ctext_len += sz; 179 180 } 181 182 override void finish(ref SecureVector!ubyte buffer, size_t offset = 0) { 183 assert(buffer.length >= offset, "Offset is sane"); 184 const size_t sz = buffer.length - offset; 185 ubyte* buf = buffer.ptr + offset; 186 187 assert(sz >= tagSize(), "Have the tag as part of final input"); 188 189 const size_t remaining = sz - tagSize(); 190 191 if(remaining) { 192 m_poly1305.update(buf, remaining); // poly1305 of ciphertext 193 m_chacha.cipher1(buf, remaining); 194 m_ctext_len += remaining; 195 } 196 197 if(cfrgVersion()) { 198 for(size_t i = 0; i != 16 - m_ctext_len % 16; ++i) 199 m_poly1305.update(0); 200 updateLength(m_ad.length); 201 } 202 203 updateLength(m_ctext_len); 204 const SecureVector!ubyte mac = m_poly1305.finished(); 205 206 const ubyte* included_tag = buf + remaining; 207 208 m_ctext_len = 0; 209 210 if(!sameMem(mac.ptr, included_tag, tagSize())) 211 throw new IntegrityFailure("ChaCha20Poly1305 tag check failed"); 212 213 buffer.resize(offset + remaining); 214 } 215 216 // Interface fallthrough 217 override string provider() const { return "core"; } 218 override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); } 219 override size_t updateGranularity() const { return super.updateGranularity(); } 220 override size_t defaultNonceLength() const { return super.defaultNonceLength(); } 221 override bool validNonceLength(size_t nonce_len) const { return super.validNonceLength(nonce_len); } 222 override @property string name() const { return super.name; } 223 override void clear() { return super.clear(); } 224 } 225