Botan 3.0.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#include <botan/internal/loadstor.h>
11
12namespace Botan {
13
15 m_chacha(StreamCipher::create("ChaCha")),
16 m_poly1305(MessageAuthenticationCode::create("Poly1305"))
17 {
18 if(!m_chacha || !m_poly1305)
19 throw Algorithm_Not_Found("ChaCha20Poly1305");
20 }
21
23 {
24 return (n == 8 || n == 12 || n == 24);
25 }
26
28 {
29 return 1;
30 }
31
33 {
34 return 128;
35 }
36
38 {
39 m_chacha->clear();
40 m_poly1305->clear();
41 reset();
42 }
43
45 {
46 m_ad.clear();
47 m_ctext_len = 0;
48 m_nonce_len = 0;
49 }
50
52 {
53 return m_chacha->has_keying_material();
54 }
55
56void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length)
57 {
58 m_chacha->set_key(key, length);
59 }
60
61void ChaCha20Poly1305_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad)
62 {
63 BOTAN_ARG_CHECK(idx == 0, "ChaCha20Poly1305: cannot handle non-zero index in set_associated_data_n");
64 if(m_ctext_len > 0 || m_nonce_len > 0)
65 throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
66 m_ad.assign(ad.begin(), ad.end());
67 }
68
70 {
71 uint8_t len8[8] = { 0 };
72 store_le(static_cast<uint64_t>(len), len8);
73 m_poly1305->update(len8, 8);
74 }
75
76void 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 m_ctext_len = 0;
82 m_nonce_len = nonce_len;
83
84 m_chacha->set_iv(nonce, nonce_len);
85
86 uint8_t first_block[64];
87 m_chacha->write_keystream(first_block, sizeof(first_block));
88
89 m_poly1305->set_key(first_block, 32);
90 // Remainder of first block is discarded
91 secure_scrub_memory(first_block, sizeof(first_block));
92
93 m_poly1305->update(m_ad);
94
95 if(cfrg_version())
96 {
97 if(m_ad.size() % 16)
98 {
99 const uint8_t zeros[16] = { 0 };
100 m_poly1305->update(zeros, 16 - m_ad.size() % 16);
101 }
102 }
103 else
104 {
105 update_len(m_ad.size());
106 }
107 }
108
109size_t ChaCha20Poly1305_Encryption::process_msg(uint8_t buf[], size_t sz)
110 {
111 m_chacha->cipher1(buf, sz);
112 m_poly1305->update(buf, sz); // poly1305 of ciphertext
113 m_ctext_len += sz;
114 return sz;
115 }
116
117void ChaCha20Poly1305_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
118 {
119 update(buffer, offset);
120 if(cfrg_version())
121 {
122 if(m_ctext_len % 16)
123 {
124 const uint8_t zeros[16] = { 0 };
125 m_poly1305->update(zeros, 16 - m_ctext_len % 16);
126 }
127 update_len(m_ad.size());
128 }
130
131 buffer.resize(buffer.size() + tag_size());
132 m_poly1305->final(&buffer[buffer.size() - tag_size()]);
133 m_ctext_len = 0;
134 m_nonce_len = 0;
135 }
136
137size_t ChaCha20Poly1305_Decryption::process_msg(uint8_t buf[], size_t sz)
138 {
139 m_poly1305->update(buf, sz); // poly1305 of ciphertext
140 m_chacha->cipher1(buf, sz);
141 m_ctext_len += sz;
142 return sz;
143 }
144
145void ChaCha20Poly1305_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset)
146 {
147 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
148 const size_t sz = buffer.size() - offset;
149 uint8_t* buf = buffer.data() + offset;
150
151 BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
152
153 const size_t remaining = sz - tag_size();
154
155 if(remaining)
156 {
157 m_poly1305->update(buf, remaining); // poly1305 of ciphertext
158 m_chacha->cipher1(buf, remaining);
159 m_ctext_len += remaining;
160 }
161
162 if(cfrg_version())
163 {
164 if(m_ctext_len % 16)
165 {
166 const uint8_t zeros[16] = { 0 };
167 m_poly1305->update(zeros, 16 - m_ctext_len % 16);
168 }
169 update_len(m_ad.size());
170 }
171
173
174 uint8_t mac[16];
175 m_poly1305->final(mac);
176
177 const uint8_t* included_tag = &buf[remaining];
178
179 m_ctext_len = 0;
180 m_nonce_len = 0;
181
182 if(!constant_time_compare(mac, included_tag, tag_size()))
183 throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
184 buffer.resize(offset + remaining);
185 }
186
187}
#define BOTAN_ARG_CHECK(expr, msg)
Definition: assert.h:36
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
bool has_keying_material() const override final
std::unique_ptr< MessageAuthenticationCode > m_poly1305
void set_associated_data_n(size_t idx, std::span< const uint8_t > ad) override final
int(* update)(CTX *, const void *, CC_LONG len)
Definition: alg_id.cpp:12
constexpr void store_le(uint16_t in, uint8_t out[2])
Definition: loadstor.h:465
void secure_scrub_memory(void *ptr, size_t n)
Definition: os_utils.cpp:81
bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len)
Definition: mem_ops.h:82