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