Botan 3.13.0
Crypto and TLS for C&
chacha20poly1305.cpp
Go to the documentation of this file.
1/*
2* ChaCha20Poly1305 AEAD
3* (C) 2014,2016,2018 Jack Lloyd
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/chacha20poly1305.h>
10
11#include <botan/exceptn.h>
12#include <botan/mem_ops.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/int_utils.h>
15#include <botan/internal/loadstor.h>
16
17namespace Botan {
18
25
27 return (n == 8 || n == 12 || n == 24);
28}
29
31 return 1;
32}
33
35 return 128;
36}
37
39 m_chacha->clear();
40 m_poly1305->clear();
41 m_ad.clear();
42 reset();
43}
44
49
51 return m_chacha->has_keying_material();
52}
53
54void ChaCha20Poly1305_Mode::key_schedule(std::span<const uint8_t> key) {
55 m_chacha->set_key(key);
56 // Clear any per-message state; AD is preserved per AEAD contract
57 // (ChaCha20Poly1305 advertises associated_data_requires_key() == false).
58 reset();
59}
60
61void ChaCha20Poly1305_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
62 BOTAN_ARG_CHECK(idx == 0, "ChaCha20Poly1305: cannot handle non-zero index in set_associated_data_n");
63 if(m_ctext_len > 0 || m_nonce_len > 0) {
64 throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
65 }
66 m_ad.assign(ad.begin(), ad.end());
67}
68
70 uint8_t len8[8] = {0};
71 store_le(len, len8);
72 m_poly1305->update(len8, 8);
73}
74
75void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
77
78 if(!valid_nonce_length(nonce_len)) {
79 throw Invalid_IV_Length(name(), nonce_len);
80 }
81
82 m_ctext_len = 0;
83 m_nonce_len = nonce_len;
84
85 m_chacha->set_iv(nonce, nonce_len);
86
87 uint8_t first_block[64];
88 m_chacha->write_keystream(first_block, sizeof(first_block));
89
90 m_poly1305->set_key(first_block, 32);
91 // Remainder of first block is discarded
92 secure_scrub_memory(first_block, sizeof(first_block));
93
94 m_poly1305->update(m_ad);
95
96 if(cfrg_version()) {
97 if(m_ad.size() % 16 != 0) {
98 const uint8_t zeros[16] = {0};
99 m_poly1305->update(zeros, 16 - m_ad.size() % 16);
100 }
101 } else {
102 update_len(m_ad.size());
103 }
104}
105
106size_t ChaCha20Poly1305_Encryption::output_length(size_t input_length) const {
107 return add_or_throw(input_length, tag_size(), "ChaCha20Poly1305 input too large");
108}
109
110size_t ChaCha20Poly1305_Encryption::process_msg(uint8_t buf[], size_t sz) {
112 m_chacha->cipher1(buf, sz);
113 m_poly1305->update(buf, sz); // poly1305 of ciphertext
114 m_ctext_len += sz;
115
116 // RFC 8439 limits messages to 2^38-64 bytes
117 constexpr uint64_t MAX_CHACHA20POLY1305_INPUT = (static_cast<uint64_t>(1) << 38) - 64;
118 if(cfrg_version() && m_ctext_len > MAX_CHACHA20POLY1305_INPUT) {
119 throw Invalid_State("ChaCha20Poly1305 message length limit exceeded");
120 }
121
122 return sz;
123}
124
125void ChaCha20Poly1305_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
127 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
128 update(buffer, offset);
129 if(cfrg_version()) {
130 if(m_ctext_len % 16 != 0) {
131 const uint8_t zeros[16] = {0};
132 const size_t padding = static_cast<size_t>(16 - m_ctext_len % 16);
133 m_poly1305->update(zeros, padding);
134 }
135 update_len(m_ad.size());
136 }
138
139 const auto new_size = checked_add(buffer.size(), tag_size());
140 if(!new_size.has_value()) {
141 throw Invalid_State("ChaCha20Poly1305 message length limit exceeded");
142 }
143 buffer.resize(new_size.value());
144 m_poly1305->final(&buffer[buffer.size() - tag_size()]);
145 m_ctext_len = 0;
146 m_nonce_len = 0;
147}
148
149size_t ChaCha20Poly1305_Decryption::output_length(size_t input_length) const {
150 BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
151 return input_length - tag_size();
152}
153
154size_t ChaCha20Poly1305_Decryption::process_msg(uint8_t buf[], size_t sz) {
156 m_poly1305->update(buf, sz); // poly1305 of ciphertext
157 m_chacha->cipher1(buf, sz);
158 m_ctext_len += sz;
159
160 constexpr uint64_t MAX_CHACHA20POLY1305_INPUT = (static_cast<uint64_t>(1) << 38) - 64;
161 if(cfrg_version() && m_ctext_len > MAX_CHACHA20POLY1305_INPUT) {
162 throw Invalid_State("ChaCha20Poly1305 message length limit exceeded");
163 }
164
165 return sz;
166}
167
168void ChaCha20Poly1305_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
170 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
171 const size_t sz = buffer.size() - offset;
172 uint8_t* buf = buffer.data() + offset;
173
174 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
175
176 const size_t remaining = sz - tag_size();
177
178 if(remaining > 0) {
179 // Route through process_msg so the RFC 8439 length limit is enforced for
180 // one-shot decryption too (finish() calls finish_msg() directly).
181 process_msg(buf, remaining);
182 }
183
184 if(cfrg_version()) {
185 if(m_ctext_len % 16 != 0) {
186 const uint8_t zeros[16] = {0};
187 const size_t padding = static_cast<size_t>(16 - m_ctext_len % 16);
188 m_poly1305->update(zeros, padding);
189 }
190 update_len(m_ad.size());
191 }
192
194
195 uint8_t mac[16];
196 m_poly1305->final(mac);
197
198 const uint8_t* included_tag = &buf[remaining];
199
200 m_ctext_len = 0;
201 m_nonce_len = 0;
202
203 if(!CT::is_equal(mac, included_tag, tag_size()).as_bool()) {
204 clear_mem(std::span{buffer}.subspan(offset, remaining));
205 throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
206 }
207 buffer.resize(offset + remaining);
208}
209
210} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
static std::unique_ptr< AEAD_Mode > create(std::string_view algo, Cipher_Dir direction, std::string_view provider="")
Definition aead.cpp:63
size_t output_length(size_t input_length) const override
size_t output_length(size_t input_length) const override
bool has_keying_material() const final
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) final
bool valid_nonce_length(size_t n) const override
secure_vector< uint8_t > m_ad
size_t ideal_granularity() const override
std::string name() const override
std::unique_ptr< StreamCipher > m_chacha
size_t update_granularity() const override
size_t tag_size() const override
std::unique_ptr< MessageAuthenticationCode > m_poly1305
void update(T &buffer, size_t offset=0)
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
constexpr std::optional< T > checked_add(T a, T b)
Definition int_utils.h:19
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:25
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:736
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118