Botan 3.12.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/loadstor.h>
15
16namespace Botan {
17
24
26 return (n == 8 || n == 12 || n == 24);
27}
28
30 return 1;
31}
32
34 return 128;
35}
36
38 m_chacha->clear();
39 m_poly1305->clear();
40 reset();
41}
42
44 m_ad.clear();
45 m_ctext_len = 0;
46 m_nonce_len = 0;
47}
48
50 return m_chacha->has_keying_material();
51}
52
53void ChaCha20Poly1305_Mode::key_schedule(std::span<const uint8_t> key) {
54 m_chacha->set_key(key);
55}
56
57void ChaCha20Poly1305_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
58 BOTAN_ARG_CHECK(idx == 0, "ChaCha20Poly1305: cannot handle non-zero index in set_associated_data_n");
59 if(m_ctext_len > 0 || m_nonce_len > 0) {
60 throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
61 }
62 m_ad.assign(ad.begin(), ad.end());
63}
64
66 uint8_t len8[8] = {0};
67 store_le(len, len8);
68 m_poly1305->update(len8, 8);
69}
70
71void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
72 if(!valid_nonce_length(nonce_len)) {
73 throw Invalid_IV_Length(name(), nonce_len);
74 }
75
76 m_ctext_len = 0;
77 m_nonce_len = nonce_len;
78
79 m_chacha->set_iv(nonce, nonce_len);
80
81 uint8_t first_block[64];
82 m_chacha->write_keystream(first_block, sizeof(first_block));
83
84 m_poly1305->set_key(first_block, 32);
85 // Remainder of first block is discarded
86 secure_scrub_memory(first_block, sizeof(first_block));
87
88 m_poly1305->update(m_ad);
89
90 if(cfrg_version()) {
91 if(m_ad.size() % 16 != 0) {
92 const uint8_t zeros[16] = {0};
93 m_poly1305->update(zeros, 16 - m_ad.size() % 16);
94 }
95 } else {
96 update_len(m_ad.size());
97 }
98}
99
100size_t ChaCha20Poly1305_Encryption::process_msg(uint8_t buf[], size_t sz) {
101 m_chacha->cipher1(buf, sz);
102 m_poly1305->update(buf, sz); // poly1305 of ciphertext
103 m_ctext_len += sz;
104
105 // RFC 8439 limits messages to 2^38-64 bytes
106 constexpr uint64_t MAX_CHACHA20POLY1305_INPUT = (static_cast<uint64_t>(1) << 38) - 64;
107 if(cfrg_version() && m_ctext_len > MAX_CHACHA20POLY1305_INPUT) {
108 throw Invalid_State("ChaCha20Poly1305 message length limit exceeded");
109 }
110
111 return sz;
112}
113
114void ChaCha20Poly1305_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
115 update(buffer, offset);
116 if(cfrg_version()) {
117 if(m_ctext_len % 16 != 0) {
118 const uint8_t zeros[16] = {0};
119 const size_t padding = static_cast<size_t>(16 - m_ctext_len % 16);
120 m_poly1305->update(zeros, padding);
121 }
122 update_len(m_ad.size());
123 }
125
126 buffer.resize(buffer.size() + tag_size());
127 m_poly1305->final(&buffer[buffer.size() - tag_size()]);
128 m_ctext_len = 0;
129 m_nonce_len = 0;
130}
131
132size_t ChaCha20Poly1305_Decryption::process_msg(uint8_t buf[], size_t sz) {
133 m_poly1305->update(buf, sz); // poly1305 of ciphertext
134 m_chacha->cipher1(buf, sz);
135 m_ctext_len += sz;
136
137 constexpr uint64_t MAX_CHACHA20POLY1305_INPUT = (static_cast<uint64_t>(1) << 38) - 64;
138 if(cfrg_version() && m_ctext_len > MAX_CHACHA20POLY1305_INPUT) {
139 throw Invalid_State("ChaCha20Poly1305 message length limit exceeded");
140 }
141
142 return sz;
143}
144
145void ChaCha20Poly1305_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
146 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
147 const size_t sz = buffer.size() - offset;
148 uint8_t* buf = buffer.data() + offset;
149
150 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
151
152 const size_t remaining = sz - tag_size();
153
154 if(remaining > 0) {
155 m_poly1305->update(buf, remaining); // poly1305 of ciphertext
156 m_chacha->cipher1(buf, remaining);
157 m_ctext_len += remaining;
158 }
159
160 if(cfrg_version()) {
161 if(m_ctext_len % 16 != 0) {
162 const uint8_t zeros[16] = {0};
163 const size_t padding = static_cast<size_t>(16 - m_ctext_len % 16);
164 m_poly1305->update(zeros, padding);
165 }
166 update_len(m_ad.size());
167 }
168
170
171 uint8_t mac[16];
172 m_poly1305->final(mac);
173
174 const uint8_t* included_tag = &buf[remaining];
175
176 m_ctext_len = 0;
177 m_nonce_len = 0;
178
179 if(!CT::is_equal(mac, included_tag, tag_size()).as_bool()) {
180 clear_mem(std::span{buffer}.subspan(offset, remaining));
181 throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
182 }
183 buffer.resize(offset + remaining);
184}
185
186} // namespace Botan
#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:59
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
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:68
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118