Coverage Report

Created: 2024-11-21 07:03

/src/cryptopp/chachapoly.cpp
Line
Count
Source (jump to first uncovered line)
1
// chachapoly.cpp - written and placed in the public domain by Jeffrey Walton
2
//                  RFC 8439, Section 2.8, AEAD Construction, http://tools.ietf.org/html/rfc8439
3
4
#include "pch.h"
5
#include "chachapoly.h"
6
#include "algparam.h"
7
#include "misc.h"
8
9
#if CRYPTOPP_MSC_VERSION
10
# pragma warning(disable: 4244)
11
#endif
12
13
NAMESPACE_BEGIN(CryptoPP)
14
15
////////////////////////////// IETF ChaChaTLS //////////////////////////////
16
17
// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was
18
// predicated on BlockCiphers, where the key and key schedule could be
19
// calculated independent of the IV being used. However, the ChaCha and
20
// ChaCha20Poly1305 construction combines key setup and IV. That is, both are
21
// needed to key or rekey the cipher. Even a simple Resync() requires us to
22
// regenerate the initial state for both ChaCha20 and Poly1305.
23
void ChaCha20Poly1305_Base::RekeyCipherAndMac(const byte *userKey, size_t keylength, const NameValuePairs &params)
24
37
{
25
  // Derive MAC key
26
37
  AlgorithmParameters block0 = MakeParameters("InitialBlock", (word64)0, true);
27
37
  AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block0));
28
29
  // Only the first 256-bits are used to key the MAC
30
37
  SecByteBlock derived(NULLPTR, 32);
31
37
  AccessSymmetricCipher().ProcessString(derived, derived.size());
32
33
  // Key the Poly1305 MAC
34
37
  AccessMAC().SetKey(derived, derived.size(), params);
35
36
  // Key the ChaCha20 cipher
37
37
  AlgorithmParameters block1 = MakeParameters("InitialBlock", (word64)1, true);
38
37
  AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block1));
39
37
}
40
41
void ChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t userKeyLength, const NameValuePairs &params)
42
27
{
43
27
  CRYPTOPP_ASSERT(userKey && userKeyLength == 32);
44
27
  m_userKey.Assign(userKey, userKeyLength);
45
46
  // ChaCha/Poly1305 initial state depends on both the key and IV. The
47
  // IV may or may not be present during the call to SetKeyWithoutResync.
48
  // If the IV is present, the framework will call SetKeyWithoutResync
49
  // followed by Resynchronize which calls Resync. In this case we defer
50
  // calculating the initial state until the call to Resynchronize.
51
  // If the IV is not present, it avoids calling ChaCha's SetKey without
52
  // an IV, which results in an exception. In this case the user will need
53
  // to call Resynchronize to key ChaCha and Poly1305.
54
  // RekeyCipherAndMac(userKey, userKeyLength, params);
55
27
  CRYPTOPP_UNUSED(params);
56
27
}
57
58
void ChaCha20Poly1305_Base::Resync(const byte *iv, size_t len)
59
37
{
60
37
  CRYPTOPP_ASSERT(iv && len == 12);
61
37
  RekeyCipherAndMac(m_userKey, m_userKey.SizeInBytes(),
62
37
    MakeParameters(Name::IV(), ConstByteArrayParameter(iv,len)));
63
37
}
64
65
size_t ChaCha20Poly1305_Base::AuthenticateBlocks(const byte *data, size_t len)
66
24
{
67
24
  AccessMAC().Update(data, len);
68
24
  return 0;
69
24
}
70
71
void ChaCha20Poly1305_Base::AuthenticateLastHeaderBlock()
72
18
{
73
  // Pad to a multiple of 16 or 0
74
18
  const byte zero[16] = {0};
75
18
  size_t pad = (16U - (m_totalHeaderLength % 16)) % 16;
76
18
  AccessMAC().Update(zero, pad);
77
18
}
78
79
void ChaCha20Poly1305_Base::AuthenticateLastConfidentialBlock()
80
13
{
81
  // Pad to a multiple of 16 or 0
82
13
  const byte zero[16] = {0};
83
13
  size_t pad = (16U - (m_totalMessageLength % 16)) % 16;
84
13
  AccessMAC().Update(zero, pad);
85
13
}
86
87
void ChaCha20Poly1305_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
88
13
{
89
13
  CRYPTOPP_ALIGN_DATA(8) byte length[2*sizeof(word64)];
90
13
  PutWord(true, LITTLE_ENDIAN_ORDER, length+0, m_totalHeaderLength);
91
13
  PutWord(true, LITTLE_ENDIAN_ORDER, length+8, m_totalMessageLength);
92
13
  AccessMAC().Update(length, sizeof(length));
93
13
  AccessMAC().TruncatedFinal(mac, macSize);
94
13
  m_state = State_KeySet;
95
13
}
96
97
void ChaCha20Poly1305_Base::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength)
98
5
{
99
5
  Resynchronize(iv, ivLength);
100
5
  Update(aad, aadLength);
101
5
  ProcessString(ciphertext, message, messageLength);
102
5
  TruncatedFinal(mac, macSize);
103
5
}
104
105
bool ChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength)
106
13
{
107
13
  Resynchronize(iv, ivLength);
108
13
  Update(aad, aadLength);
109
13
  ProcessString(message, ciphertext, ciphertextLength);
110
13
  return TruncatedVerify(mac, macLength);
111
13
}
112
113
////////////////////////////// IETF XChaCha20 draft //////////////////////////////
114
115
// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was
116
// predicated on BlockCiphers, where the key and key schedule could be
117
// calculated independent of the IV being used. However, the ChaCha and
118
// ChaCha20Poly1305 construction combines key setup and IV. That is, both are
119
// needed to key or rekey the cipher. Even a simple Resync() requires us to
120
// regenerate the initial state for both ChaCha20 and Poly1305.
121
void XChaCha20Poly1305_Base::RekeyCipherAndMac(const byte *userKey, size_t keylength, const NameValuePairs &params)
122
2
{
123
  // Derive MAC key
124
2
  AlgorithmParameters block0 = MakeParameters("InitialBlock", (word64)0, true);
125
2
  AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block0));
126
127
  // Only the first 256-bits are used to key the MAC
128
2
  SecByteBlock derived(NULLPTR, 32);
129
2
  AccessSymmetricCipher().ProcessString(derived, derived.size());
130
131
  // Key the Poly1305 MAC
132
2
  AccessMAC().SetKey(derived, derived.size(), params);
133
134
  // Key the ChaCha20 cipher
135
2
  AlgorithmParameters block1 = MakeParameters("InitialBlock", (word64)1, true);
136
2
  AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block1));
137
2
}
138
139
void XChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t userKeyLength, const NameValuePairs &params)
140
13
{
141
13
  CRYPTOPP_ASSERT(userKey && userKeyLength == 32);
142
13
  m_userKey.Assign(userKey, userKeyLength);
143
144
  // XChaCha20/Poly1305 initial state depends on both the key and IV. The
145
  // IV may or may not be present during the call to SetKeyWithoutResync.
146
  // If the IV is present, the framework will call SetKeyWithoutResync
147
  // followed by Resynchronize which calls Resync. In this case we defer
148
  // calculating the initial state until the call to Resynchronize.
149
  // If the IV is not present, it avoids calling ChaCha's SetKey without
150
  // an IV, which results in an exception. In this case the user will need
151
  // to call Resynchronize to key ChaCha and Poly1305.
152
  // RekeyCipherAndMac(userKey, userKeyLength, params);
153
13
  CRYPTOPP_UNUSED(params);
154
13
}
155
156
void XChaCha20Poly1305_Base::Resync(const byte *iv, size_t len)
157
2
{
158
2
  CRYPTOPP_ASSERT(iv && len == 24);
159
2
  RekeyCipherAndMac(m_userKey, m_userKey.SizeInBytes(),
160
2
    MakeParameters(Name::IV(), ConstByteArrayParameter(iv,len)));
161
2
}
162
163
size_t XChaCha20Poly1305_Base::AuthenticateBlocks(const byte *data, size_t len)
164
0
{
165
0
  AccessMAC().Update(data, len);
166
0
  return 0;
167
0
}
168
169
void XChaCha20Poly1305_Base::AuthenticateLastHeaderBlock()
170
0
{
171
  // Pad to a multiple of 16 or 0
172
0
  const byte zero[16] = {0};
173
0
  size_t pad = (16 - (m_totalHeaderLength % 16)) % 16;
174
0
  AccessMAC().Update(zero, pad);
175
0
}
176
177
void XChaCha20Poly1305_Base::AuthenticateLastConfidentialBlock()
178
0
{
179
  // Pad to a multiple of 16 or 0
180
0
  const byte zero[16] = {0};
181
0
  size_t pad = (16 - (m_totalMessageLength % 16)) % 16;
182
0
  AccessMAC().Update(zero, pad);
183
0
}
184
185
void XChaCha20Poly1305_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
186
0
{
187
0
  CRYPTOPP_ALIGN_DATA(8) byte length[2*sizeof(word64)];
188
0
  PutWord(true, LITTLE_ENDIAN_ORDER, length+0, m_totalHeaderLength);
189
0
  PutWord(true, LITTLE_ENDIAN_ORDER, length+8, m_totalMessageLength);
190
0
  AccessMAC().Update(length, sizeof(length));
191
0
  AccessMAC().TruncatedFinal(mac, macSize);
192
0
  m_state = State_KeySet;
193
0
}
194
195
void XChaCha20Poly1305_Base::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength)
196
0
{
197
0
  Resynchronize(iv, ivLength);
198
0
  Update(aad, aadLength);
199
0
  ProcessString(ciphertext, message, messageLength);
200
0
  TruncatedFinal(mac, macSize);
201
0
}
202
203
bool XChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength)
204
0
{
205
0
  Resynchronize(iv, ivLength);
206
0
  Update(aad, aadLength);
207
0
  ProcessString(message, ciphertext, ciphertextLength);
208
0
  return TruncatedVerify(mac, macLength);
209
0
}
210
211
NAMESPACE_END