1# Copyright 2017 Donald Stufft and individual contributors
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14from typing import Optional
15
16from nacl import exceptions as exc
17from nacl._sodium import ffi, lib
18from nacl.exceptions import ensure
19
20"""
21Implementations of authenticated encription with associated data (*AEAD*)
22constructions building on the chacha20 stream cipher and the poly1305
23authenticator
24"""
25
26crypto_aead_chacha20poly1305_ietf_KEYBYTES: int = (
27 lib.crypto_aead_chacha20poly1305_ietf_keybytes()
28)
29crypto_aead_chacha20poly1305_ietf_NSECBYTES: int = (
30 lib.crypto_aead_chacha20poly1305_ietf_nsecbytes()
31)
32crypto_aead_chacha20poly1305_ietf_NPUBBYTES: int = (
33 lib.crypto_aead_chacha20poly1305_ietf_npubbytes()
34)
35crypto_aead_chacha20poly1305_ietf_ABYTES: int = (
36 lib.crypto_aead_chacha20poly1305_ietf_abytes()
37)
38crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
39 lib.crypto_aead_chacha20poly1305_ietf_messagebytes_max()
40)
41_aead_chacha20poly1305_ietf_CRYPTBYTES_MAX = (
42 crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
43 + crypto_aead_chacha20poly1305_ietf_ABYTES
44)
45
46crypto_aead_chacha20poly1305_KEYBYTES: int = (
47 lib.crypto_aead_chacha20poly1305_keybytes()
48)
49crypto_aead_chacha20poly1305_NSECBYTES: int = (
50 lib.crypto_aead_chacha20poly1305_nsecbytes()
51)
52crypto_aead_chacha20poly1305_NPUBBYTES: int = (
53 lib.crypto_aead_chacha20poly1305_npubbytes()
54)
55crypto_aead_chacha20poly1305_ABYTES: int = (
56 lib.crypto_aead_chacha20poly1305_abytes()
57)
58crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX: int = (
59 lib.crypto_aead_chacha20poly1305_messagebytes_max()
60)
61_aead_chacha20poly1305_CRYPTBYTES_MAX = (
62 crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
63 + crypto_aead_chacha20poly1305_ABYTES
64)
65
66crypto_aead_xchacha20poly1305_ietf_KEYBYTES: int = (
67 lib.crypto_aead_xchacha20poly1305_ietf_keybytes()
68)
69crypto_aead_xchacha20poly1305_ietf_NSECBYTES: int = (
70 lib.crypto_aead_xchacha20poly1305_ietf_nsecbytes()
71)
72crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: int = (
73 lib.crypto_aead_xchacha20poly1305_ietf_npubbytes()
74)
75crypto_aead_xchacha20poly1305_ietf_ABYTES: int = (
76 lib.crypto_aead_xchacha20poly1305_ietf_abytes()
77)
78crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
79 lib.crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
80)
81_aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX = (
82 crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
83 + crypto_aead_xchacha20poly1305_ietf_ABYTES
84)
85
86
87def crypto_aead_chacha20poly1305_ietf_encrypt(
88 message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
89) -> bytes:
90 """
91 Encrypt the given ``message`` using the IETF ratified chacha20poly1305
92 construction described in RFC7539.
93
94 :param message:
95 :type message: bytes
96 :param aad:
97 :type aad: Optional[bytes]
98 :param nonce:
99 :type nonce: bytes
100 :param key:
101 :type key: bytes
102 :return: authenticated ciphertext
103 :rtype: bytes
104 """
105 ensure(
106 isinstance(message, bytes),
107 "Input message type must be bytes",
108 raising=exc.TypeError,
109 )
110
111 mlen = len(message)
112
113 ensure(
114 mlen <= crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX,
115 "Message must be at most {} bytes long".format(
116 crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
117 ),
118 raising=exc.ValueError,
119 )
120
121 ensure(
122 isinstance(aad, bytes) or (aad is None),
123 "Additional data must be bytes or None",
124 raising=exc.TypeError,
125 )
126
127 ensure(
128 isinstance(nonce, bytes)
129 and len(nonce) == crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
130 "Nonce must be a {} bytes long bytes sequence".format(
131 crypto_aead_chacha20poly1305_ietf_NPUBBYTES
132 ),
133 raising=exc.TypeError,
134 )
135
136 ensure(
137 isinstance(key, bytes)
138 and len(key) == crypto_aead_chacha20poly1305_ietf_KEYBYTES,
139 "Key must be a {} bytes long bytes sequence".format(
140 crypto_aead_chacha20poly1305_ietf_KEYBYTES
141 ),
142 raising=exc.TypeError,
143 )
144
145 if aad:
146 _aad = aad
147 aalen = len(aad)
148 else:
149 _aad = ffi.NULL
150 aalen = 0
151
152 mxout = mlen + crypto_aead_chacha20poly1305_ietf_ABYTES
153
154 clen = ffi.new("unsigned long long *")
155
156 ciphertext = ffi.new("unsigned char[]", mxout)
157
158 res = lib.crypto_aead_chacha20poly1305_ietf_encrypt(
159 ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
160 )
161
162 ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
163 return ffi.buffer(ciphertext, clen[0])[:]
164
165
166def crypto_aead_chacha20poly1305_ietf_decrypt(
167 ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
168) -> bytes:
169 """
170 Decrypt the given ``ciphertext`` using the IETF ratified chacha20poly1305
171 construction described in RFC7539.
172
173 :param ciphertext:
174 :type ciphertext: bytes
175 :param aad:
176 :type aad: Optional[bytes]
177 :param nonce:
178 :type nonce: bytes
179 :param key:
180 :type key: bytes
181 :return: message
182 :rtype: bytes
183 """
184 ensure(
185 isinstance(ciphertext, bytes),
186 "Input ciphertext type must be bytes",
187 raising=exc.TypeError,
188 )
189
190 clen = len(ciphertext)
191
192 ensure(
193 clen <= _aead_chacha20poly1305_ietf_CRYPTBYTES_MAX,
194 "Ciphertext must be at most {} bytes long".format(
195 _aead_chacha20poly1305_ietf_CRYPTBYTES_MAX
196 ),
197 raising=exc.ValueError,
198 )
199
200 ensure(
201 isinstance(aad, bytes) or (aad is None),
202 "Additional data must be bytes or None",
203 raising=exc.TypeError,
204 )
205
206 ensure(
207 isinstance(nonce, bytes)
208 and len(nonce) == crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
209 "Nonce must be a {} bytes long bytes sequence".format(
210 crypto_aead_chacha20poly1305_ietf_NPUBBYTES
211 ),
212 raising=exc.TypeError,
213 )
214
215 ensure(
216 isinstance(key, bytes)
217 and len(key) == crypto_aead_chacha20poly1305_ietf_KEYBYTES,
218 "Key must be a {} bytes long bytes sequence".format(
219 crypto_aead_chacha20poly1305_ietf_KEYBYTES
220 ),
221 raising=exc.TypeError,
222 )
223
224 mxout = clen - crypto_aead_chacha20poly1305_ietf_ABYTES
225
226 mlen = ffi.new("unsigned long long *")
227 message = ffi.new("unsigned char[]", mxout)
228
229 if aad:
230 _aad = aad
231 aalen = len(aad)
232 else:
233 _aad = ffi.NULL
234 aalen = 0
235
236 res = lib.crypto_aead_chacha20poly1305_ietf_decrypt(
237 message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
238 )
239
240 ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
241
242 return ffi.buffer(message, mlen[0])[:]
243
244
245def crypto_aead_chacha20poly1305_encrypt(
246 message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
247) -> bytes:
248 """
249 Encrypt the given ``message`` using the "legacy" construction
250 described in draft-agl-tls-chacha20poly1305.
251
252 :param message:
253 :type message: bytes
254 :param aad:
255 :type aad: Optional[bytes]
256 :param nonce:
257 :type nonce: bytes
258 :param key:
259 :type key: bytes
260 :return: authenticated ciphertext
261 :rtype: bytes
262 """
263 ensure(
264 isinstance(message, bytes),
265 "Input message type must be bytes",
266 raising=exc.TypeError,
267 )
268
269 mlen = len(message)
270
271 ensure(
272 mlen <= crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX,
273 "Message must be at most {} bytes long".format(
274 crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
275 ),
276 raising=exc.ValueError,
277 )
278
279 ensure(
280 isinstance(aad, bytes) or (aad is None),
281 "Additional data must be bytes or None",
282 raising=exc.TypeError,
283 )
284
285 ensure(
286 isinstance(nonce, bytes)
287 and len(nonce) == crypto_aead_chacha20poly1305_NPUBBYTES,
288 "Nonce must be a {} bytes long bytes sequence".format(
289 crypto_aead_chacha20poly1305_NPUBBYTES
290 ),
291 raising=exc.TypeError,
292 )
293
294 ensure(
295 isinstance(key, bytes)
296 and len(key) == crypto_aead_chacha20poly1305_KEYBYTES,
297 "Key must be a {} bytes long bytes sequence".format(
298 crypto_aead_chacha20poly1305_KEYBYTES
299 ),
300 raising=exc.TypeError,
301 )
302
303 if aad:
304 _aad = aad
305 aalen = len(aad)
306 else:
307 _aad = ffi.NULL
308 aalen = 0
309
310 mlen = len(message)
311 mxout = mlen + crypto_aead_chacha20poly1305_ietf_ABYTES
312
313 clen = ffi.new("unsigned long long *")
314
315 ciphertext = ffi.new("unsigned char[]", mxout)
316
317 res = lib.crypto_aead_chacha20poly1305_encrypt(
318 ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
319 )
320
321 ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
322 return ffi.buffer(ciphertext, clen[0])[:]
323
324
325def crypto_aead_chacha20poly1305_decrypt(
326 ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
327) -> bytes:
328 """
329 Decrypt the given ``ciphertext`` using the "legacy" construction
330 described in draft-agl-tls-chacha20poly1305.
331
332 :param ciphertext: authenticated ciphertext
333 :type ciphertext: bytes
334 :param aad:
335 :type aad: Optional[bytes]
336 :param nonce:
337 :type nonce: bytes
338 :param key:
339 :type key: bytes
340 :return: message
341 :rtype: bytes
342 """
343 ensure(
344 isinstance(ciphertext, bytes),
345 "Input ciphertext type must be bytes",
346 raising=exc.TypeError,
347 )
348
349 clen = len(ciphertext)
350
351 ensure(
352 clen <= _aead_chacha20poly1305_CRYPTBYTES_MAX,
353 "Ciphertext must be at most {} bytes long".format(
354 _aead_chacha20poly1305_CRYPTBYTES_MAX
355 ),
356 raising=exc.ValueError,
357 )
358
359 ensure(
360 isinstance(aad, bytes) or (aad is None),
361 "Additional data must be bytes or None",
362 raising=exc.TypeError,
363 )
364
365 ensure(
366 isinstance(nonce, bytes)
367 and len(nonce) == crypto_aead_chacha20poly1305_NPUBBYTES,
368 "Nonce must be a {} bytes long bytes sequence".format(
369 crypto_aead_chacha20poly1305_NPUBBYTES
370 ),
371 raising=exc.TypeError,
372 )
373
374 ensure(
375 isinstance(key, bytes)
376 and len(key) == crypto_aead_chacha20poly1305_KEYBYTES,
377 "Key must be a {} bytes long bytes sequence".format(
378 crypto_aead_chacha20poly1305_KEYBYTES
379 ),
380 raising=exc.TypeError,
381 )
382
383 mxout = clen - crypto_aead_chacha20poly1305_ABYTES
384
385 mlen = ffi.new("unsigned long long *")
386 message = ffi.new("unsigned char[]", mxout)
387
388 if aad:
389 _aad = aad
390 aalen = len(aad)
391 else:
392 _aad = ffi.NULL
393 aalen = 0
394
395 res = lib.crypto_aead_chacha20poly1305_decrypt(
396 message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
397 )
398
399 ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
400
401 return ffi.buffer(message, mlen[0])[:]
402
403
404def crypto_aead_xchacha20poly1305_ietf_encrypt(
405 message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
406) -> bytes:
407 """
408 Encrypt the given ``message`` using the long-nonces xchacha20poly1305
409 construction.
410
411 :param message:
412 :type message: bytes
413 :param aad:
414 :type aad: Optional[bytes]
415 :param nonce:
416 :type nonce: bytes
417 :param key:
418 :type key: bytes
419 :return: authenticated ciphertext
420 :rtype: bytes
421 """
422 ensure(
423 isinstance(message, bytes),
424 "Input message type must be bytes",
425 raising=exc.TypeError,
426 )
427
428 mlen = len(message)
429
430 ensure(
431 mlen <= crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX,
432 "Message must be at most {} bytes long".format(
433 crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
434 ),
435 raising=exc.ValueError,
436 )
437
438 ensure(
439 isinstance(aad, bytes) or (aad is None),
440 "Additional data must be bytes or None",
441 raising=exc.TypeError,
442 )
443
444 ensure(
445 isinstance(nonce, bytes)
446 and len(nonce) == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
447 "Nonce must be a {} bytes long bytes sequence".format(
448 crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
449 ),
450 raising=exc.TypeError,
451 )
452
453 ensure(
454 isinstance(key, bytes)
455 and len(key) == crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
456 "Key must be a {} bytes long bytes sequence".format(
457 crypto_aead_xchacha20poly1305_ietf_KEYBYTES
458 ),
459 raising=exc.TypeError,
460 )
461
462 if aad:
463 _aad = aad
464 aalen = len(aad)
465 else:
466 _aad = ffi.NULL
467 aalen = 0
468
469 mlen = len(message)
470 mxout = mlen + crypto_aead_xchacha20poly1305_ietf_ABYTES
471
472 clen = ffi.new("unsigned long long *")
473
474 ciphertext = ffi.new("unsigned char[]", mxout)
475
476 res = lib.crypto_aead_xchacha20poly1305_ietf_encrypt(
477 ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
478 )
479
480 ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
481 return ffi.buffer(ciphertext, clen[0])[:]
482
483
484def crypto_aead_xchacha20poly1305_ietf_decrypt(
485 ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
486) -> bytes:
487 """
488 Decrypt the given ``ciphertext`` using the long-nonces xchacha20poly1305
489 construction.
490
491 :param ciphertext: authenticated ciphertext
492 :type ciphertext: bytes
493 :param aad:
494 :type aad: Optional[bytes]
495 :param nonce:
496 :type nonce: bytes
497 :param key:
498 :type key: bytes
499 :return: message
500 :rtype: bytes
501 """
502 ensure(
503 isinstance(ciphertext, bytes),
504 "Input ciphertext type must be bytes",
505 raising=exc.TypeError,
506 )
507
508 clen = len(ciphertext)
509
510 ensure(
511 clen <= _aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX,
512 "Ciphertext must be at most {} bytes long".format(
513 _aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX
514 ),
515 raising=exc.ValueError,
516 )
517
518 ensure(
519 isinstance(aad, bytes) or (aad is None),
520 "Additional data must be bytes or None",
521 raising=exc.TypeError,
522 )
523
524 ensure(
525 isinstance(nonce, bytes)
526 and len(nonce) == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
527 "Nonce must be a {} bytes long bytes sequence".format(
528 crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
529 ),
530 raising=exc.TypeError,
531 )
532
533 ensure(
534 isinstance(key, bytes)
535 and len(key) == crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
536 "Key must be a {} bytes long bytes sequence".format(
537 crypto_aead_xchacha20poly1305_ietf_KEYBYTES
538 ),
539 raising=exc.TypeError,
540 )
541
542 mxout = clen - crypto_aead_xchacha20poly1305_ietf_ABYTES
543 mlen = ffi.new("unsigned long long *")
544 message = ffi.new("unsigned char[]", mxout)
545
546 if aad:
547 _aad = aad
548 aalen = len(aad)
549 else:
550 _aad = ffi.NULL
551 aalen = 0
552
553 res = lib.crypto_aead_xchacha20poly1305_ietf_decrypt(
554 message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
555 )
556
557 ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
558
559 return ffi.buffer(message, mlen[0])[:]