Coverage Report

Created: 2020-02-14 15:38

/src/botan/src/lib/modes/aead/chacha20poly1305/chacha20poly1305.cpp
Line
Count
Source (jump to first uncovered line)
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/chacha20poly1305.h>
10
#include <botan/loadstor.h>
11
12
namespace Botan {
13
14
ChaCha20Poly1305_Mode::ChaCha20Poly1305_Mode() :
15
   m_chacha(StreamCipher::create("ChaCha")),
16
   m_poly1305(MessageAuthenticationCode::create("Poly1305"))
17
129
   {
18
129
   if(!m_chacha || !m_poly1305)
19
0
      throw Algorithm_Not_Found("ChaCha20Poly1305");
20
129
   }
21
22
bool ChaCha20Poly1305_Mode::valid_nonce_length(size_t n) const
23
180
   {
24
180
   return (n == 8 || n == 12 || n == 24);
25
180
   }
26
27
void ChaCha20Poly1305_Mode::clear()
28
0
   {
29
0
   m_chacha->clear();
30
0
   m_poly1305->clear();
31
0
   reset();
32
0
   }
33
34
void ChaCha20Poly1305_Mode::reset()
35
0
   {
36
0
   m_ad.clear();
37
0
   m_ctext_len = 0;
38
0
   m_nonce_len = 0;
39
0
   }
40
41
void ChaCha20Poly1305_Mode::key_schedule(const uint8_t key[], size_t length)
42
129
   {
43
129
   m_chacha->set_key(key, length);
44
129
   }
45
46
void ChaCha20Poly1305_Mode::set_associated_data(const uint8_t ad[], size_t length)
47
180
   {
48
180
   if(m_ctext_len > 0 || m_nonce_len > 0)
49
0
      throw Invalid_State("Cannot set AD for ChaCha20Poly1305 while processing a message");
50
180
   m_ad.assign(ad, ad + length);
51
180
   }
52
53
void ChaCha20Poly1305_Mode::update_len(size_t len)
54
360
   {
55
360
   uint8_t len8[8] = { 0 };
56
360
   store_le(static_cast<uint64_t>(len), len8);
57
360
   m_poly1305->update(len8, 8);
58
360
   }
59
60
void ChaCha20Poly1305_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
61
180
   {
62
180
   if(!valid_nonce_length(nonce_len))
63
0
      throw Invalid_IV_Length(name(), nonce_len);
64
180
65
180
   m_ctext_len = 0;
66
180
   m_nonce_len = nonce_len;
67
180
68
180
   m_chacha->set_iv(nonce, nonce_len);
69
180
70
180
   uint8_t first_block[64];
71
180
   m_chacha->write_keystream(first_block, sizeof(first_block));
72
180
73
180
   m_poly1305->set_key(first_block, 32);
74
180
   // Remainder of first block is discarded
75
180
   secure_scrub_memory(first_block, sizeof(first_block));
76
180
77
180
   m_poly1305->update(m_ad);
78
180
79
180
   if(cfrg_version())
80
180
      {
81
180
      if(m_ad.size() % 16)
82
180
         {
83
180
         const uint8_t zeros[16] = { 0 };
84
180
         m_poly1305->update(zeros, 16 - m_ad.size() % 16);
85
180
         }
86
180
      }
87
0
   else
88
0
      {
89
0
      update_len(m_ad.size());
90
0
      }
91
180
   }
92
93
size_t ChaCha20Poly1305_Encryption::process(uint8_t buf[], size_t sz)
94
126
   {
95
126
   m_chacha->cipher1(buf, sz);
96
126
   m_poly1305->update(buf, sz); // poly1305 of ciphertext
97
126
   m_ctext_len += sz;
98
126
   return sz;
99
126
   }
100
101
void ChaCha20Poly1305_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
102
126
   {
103
126
   update(buffer, offset);
104
126
   if(cfrg_version())
105
126
      {
106
126
      if(m_ctext_len % 16)
107
60
         {
108
60
         const uint8_t zeros[16] = { 0 };
109
60
         m_poly1305->update(zeros, 16 - m_ctext_len % 16);
110
60
         }
111
126
      update_len(m_ad.size());
112
126
      }
113
126
   update_len(m_ctext_len);
114
126
115
126
   buffer.resize(buffer.size() + tag_size());
116
126
   m_poly1305->final(&buffer[buffer.size() - tag_size()]);
117
126
   m_ctext_len = 0;
118
126
   m_nonce_len = 0;
119
126
   }
120
121
size_t ChaCha20Poly1305_Decryption::process(uint8_t buf[], size_t sz)
122
0
   {
123
0
   m_poly1305->update(buf, sz); // poly1305 of ciphertext
124
0
   m_chacha->cipher1(buf, sz);
125
0
   m_ctext_len += sz;
126
0
   return sz;
127
0
   }
128
129
void ChaCha20Poly1305_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
130
54
   {
131
54
   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
132
54
   const size_t sz = buffer.size() - offset;
133
54
   uint8_t* buf = buffer.data() + offset;
134
54
135
54
   BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");
136
54
137
54
   const size_t remaining = sz - tag_size();
138
54
139
54
   if(remaining)
140
52
      {
141
52
      m_poly1305->update(buf, remaining); // poly1305 of ciphertext
142
52
      m_chacha->cipher1(buf, remaining);
143
52
      m_ctext_len += remaining;
144
52
      }
145
54
146
54
   if(cfrg_version())
147
54
      {
148
54
      if(m_ctext_len % 16)
149
39
         {
150
39
         const uint8_t zeros[16] = { 0 };
151
39
         m_poly1305->update(zeros, 16 - m_ctext_len % 16);
152
39
         }
153
54
      update_len(m_ad.size());
154
54
      }
155
54
156
54
   update_len(m_ctext_len);
157
54
158
54
   uint8_t mac[16];
159
54
   m_poly1305->final(mac);
160
54
161
54
   const uint8_t* included_tag = &buf[remaining];
162
54
163
54
   m_ctext_len = 0;
164
54
   m_nonce_len = 0;
165
54
166
54
   if(!constant_time_compare(mac, included_tag, tag_size()))
167
54
      throw Invalid_Authentication_Tag("ChaCha20Poly1305 tag check failed");
168
0
   buffer.resize(offset + remaining);
169
0
   }
170
171
}