crypto_aead.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  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.
  14. from typing import Optional
  15. from nacl import exceptions as exc
  16. from nacl._sodium import ffi, lib
  17. from nacl.exceptions import ensure
  18. """
  19. Implementations of authenticated encription with associated data (*AEAD*)
  20. constructions building on the chacha20 stream cipher and the poly1305
  21. authenticator
  22. """
  23. crypto_aead_chacha20poly1305_ietf_KEYBYTES: int = (
  24. lib.crypto_aead_chacha20poly1305_ietf_keybytes()
  25. )
  26. crypto_aead_chacha20poly1305_ietf_NSECBYTES: int = (
  27. lib.crypto_aead_chacha20poly1305_ietf_nsecbytes()
  28. )
  29. crypto_aead_chacha20poly1305_ietf_NPUBBYTES: int = (
  30. lib.crypto_aead_chacha20poly1305_ietf_npubbytes()
  31. )
  32. crypto_aead_chacha20poly1305_ietf_ABYTES: int = (
  33. lib.crypto_aead_chacha20poly1305_ietf_abytes()
  34. )
  35. crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
  36. lib.crypto_aead_chacha20poly1305_ietf_messagebytes_max()
  37. )
  38. _aead_chacha20poly1305_ietf_CRYPTBYTES_MAX = (
  39. crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
  40. + crypto_aead_chacha20poly1305_ietf_ABYTES
  41. )
  42. crypto_aead_chacha20poly1305_KEYBYTES: int = (
  43. lib.crypto_aead_chacha20poly1305_keybytes()
  44. )
  45. crypto_aead_chacha20poly1305_NSECBYTES: int = (
  46. lib.crypto_aead_chacha20poly1305_nsecbytes()
  47. )
  48. crypto_aead_chacha20poly1305_NPUBBYTES: int = (
  49. lib.crypto_aead_chacha20poly1305_npubbytes()
  50. )
  51. crypto_aead_chacha20poly1305_ABYTES: int = (
  52. lib.crypto_aead_chacha20poly1305_abytes()
  53. )
  54. crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX: int = (
  55. lib.crypto_aead_chacha20poly1305_messagebytes_max()
  56. )
  57. _aead_chacha20poly1305_CRYPTBYTES_MAX = (
  58. crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
  59. + crypto_aead_chacha20poly1305_ABYTES
  60. )
  61. crypto_aead_xchacha20poly1305_ietf_KEYBYTES: int = (
  62. lib.crypto_aead_xchacha20poly1305_ietf_keybytes()
  63. )
  64. crypto_aead_xchacha20poly1305_ietf_NSECBYTES: int = (
  65. lib.crypto_aead_xchacha20poly1305_ietf_nsecbytes()
  66. )
  67. crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: int = (
  68. lib.crypto_aead_xchacha20poly1305_ietf_npubbytes()
  69. )
  70. crypto_aead_xchacha20poly1305_ietf_ABYTES: int = (
  71. lib.crypto_aead_xchacha20poly1305_ietf_abytes()
  72. )
  73. crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
  74. lib.crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
  75. )
  76. _aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX = (
  77. crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
  78. + crypto_aead_xchacha20poly1305_ietf_ABYTES
  79. )
  80. def crypto_aead_chacha20poly1305_ietf_encrypt(
  81. message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
  82. ) -> bytes:
  83. """
  84. Encrypt the given ``message`` using the IETF ratified chacha20poly1305
  85. construction described in RFC7539.
  86. :param message:
  87. :type message: bytes
  88. :param aad:
  89. :type aad: Optional[bytes]
  90. :param nonce:
  91. :type nonce: bytes
  92. :param key:
  93. :type key: bytes
  94. :return: authenticated ciphertext
  95. :rtype: bytes
  96. """
  97. ensure(
  98. isinstance(message, bytes),
  99. "Input message type must be bytes",
  100. raising=exc.TypeError,
  101. )
  102. mlen = len(message)
  103. ensure(
  104. mlen <= crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX,
  105. "Message must be at most {} bytes long".format(
  106. crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
  107. ),
  108. raising=exc.ValueError,
  109. )
  110. ensure(
  111. isinstance(aad, bytes) or (aad is None),
  112. "Additional data must be bytes or None",
  113. raising=exc.TypeError,
  114. )
  115. ensure(
  116. isinstance(nonce, bytes)
  117. and len(nonce) == crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
  118. "Nonce must be a {} bytes long bytes sequence".format(
  119. crypto_aead_chacha20poly1305_ietf_NPUBBYTES
  120. ),
  121. raising=exc.TypeError,
  122. )
  123. ensure(
  124. isinstance(key, bytes)
  125. and len(key) == crypto_aead_chacha20poly1305_ietf_KEYBYTES,
  126. "Key must be a {} bytes long bytes sequence".format(
  127. crypto_aead_chacha20poly1305_ietf_KEYBYTES
  128. ),
  129. raising=exc.TypeError,
  130. )
  131. if aad:
  132. _aad = aad
  133. aalen = len(aad)
  134. else:
  135. _aad = ffi.NULL
  136. aalen = 0
  137. mxout = mlen + crypto_aead_chacha20poly1305_ietf_ABYTES
  138. clen = ffi.new("unsigned long long *")
  139. ciphertext = ffi.new("unsigned char[]", mxout)
  140. res = lib.crypto_aead_chacha20poly1305_ietf_encrypt(
  141. ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
  142. )
  143. ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
  144. return ffi.buffer(ciphertext, clen[0])[:]
  145. def crypto_aead_chacha20poly1305_ietf_decrypt(
  146. ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
  147. ) -> bytes:
  148. """
  149. Decrypt the given ``ciphertext`` using the IETF ratified chacha20poly1305
  150. construction described in RFC7539.
  151. :param ciphertext:
  152. :type ciphertext: bytes
  153. :param aad:
  154. :type aad: Optional[bytes]
  155. :param nonce:
  156. :type nonce: bytes
  157. :param key:
  158. :type key: bytes
  159. :return: message
  160. :rtype: bytes
  161. """
  162. ensure(
  163. isinstance(ciphertext, bytes),
  164. "Input ciphertext type must be bytes",
  165. raising=exc.TypeError,
  166. )
  167. clen = len(ciphertext)
  168. ensure(
  169. clen <= _aead_chacha20poly1305_ietf_CRYPTBYTES_MAX,
  170. "Ciphertext must be at most {} bytes long".format(
  171. _aead_chacha20poly1305_ietf_CRYPTBYTES_MAX
  172. ),
  173. raising=exc.ValueError,
  174. )
  175. ensure(
  176. isinstance(aad, bytes) or (aad is None),
  177. "Additional data must be bytes or None",
  178. raising=exc.TypeError,
  179. )
  180. ensure(
  181. isinstance(nonce, bytes)
  182. and len(nonce) == crypto_aead_chacha20poly1305_ietf_NPUBBYTES,
  183. "Nonce must be a {} bytes long bytes sequence".format(
  184. crypto_aead_chacha20poly1305_ietf_NPUBBYTES
  185. ),
  186. raising=exc.TypeError,
  187. )
  188. ensure(
  189. isinstance(key, bytes)
  190. and len(key) == crypto_aead_chacha20poly1305_ietf_KEYBYTES,
  191. "Key must be a {} bytes long bytes sequence".format(
  192. crypto_aead_chacha20poly1305_ietf_KEYBYTES
  193. ),
  194. raising=exc.TypeError,
  195. )
  196. mxout = clen - crypto_aead_chacha20poly1305_ietf_ABYTES
  197. mlen = ffi.new("unsigned long long *")
  198. message = ffi.new("unsigned char[]", mxout)
  199. if aad:
  200. _aad = aad
  201. aalen = len(aad)
  202. else:
  203. _aad = ffi.NULL
  204. aalen = 0
  205. res = lib.crypto_aead_chacha20poly1305_ietf_decrypt(
  206. message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
  207. )
  208. ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
  209. return ffi.buffer(message, mlen[0])[:]
  210. def crypto_aead_chacha20poly1305_encrypt(
  211. message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
  212. ) -> bytes:
  213. """
  214. Encrypt the given ``message`` using the "legacy" construction
  215. described in draft-agl-tls-chacha20poly1305.
  216. :param message:
  217. :type message: bytes
  218. :param aad:
  219. :type aad: Optional[bytes]
  220. :param nonce:
  221. :type nonce: bytes
  222. :param key:
  223. :type key: bytes
  224. :return: authenticated ciphertext
  225. :rtype: bytes
  226. """
  227. ensure(
  228. isinstance(message, bytes),
  229. "Input message type must be bytes",
  230. raising=exc.TypeError,
  231. )
  232. mlen = len(message)
  233. ensure(
  234. mlen <= crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX,
  235. "Message must be at most {} bytes long".format(
  236. crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
  237. ),
  238. raising=exc.ValueError,
  239. )
  240. ensure(
  241. isinstance(aad, bytes) or (aad is None),
  242. "Additional data must be bytes or None",
  243. raising=exc.TypeError,
  244. )
  245. ensure(
  246. isinstance(nonce, bytes)
  247. and len(nonce) == crypto_aead_chacha20poly1305_NPUBBYTES,
  248. "Nonce must be a {} bytes long bytes sequence".format(
  249. crypto_aead_chacha20poly1305_NPUBBYTES
  250. ),
  251. raising=exc.TypeError,
  252. )
  253. ensure(
  254. isinstance(key, bytes)
  255. and len(key) == crypto_aead_chacha20poly1305_KEYBYTES,
  256. "Key must be a {} bytes long bytes sequence".format(
  257. crypto_aead_chacha20poly1305_KEYBYTES
  258. ),
  259. raising=exc.TypeError,
  260. )
  261. if aad:
  262. _aad = aad
  263. aalen = len(aad)
  264. else:
  265. _aad = ffi.NULL
  266. aalen = 0
  267. mlen = len(message)
  268. mxout = mlen + crypto_aead_chacha20poly1305_ietf_ABYTES
  269. clen = ffi.new("unsigned long long *")
  270. ciphertext = ffi.new("unsigned char[]", mxout)
  271. res = lib.crypto_aead_chacha20poly1305_encrypt(
  272. ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
  273. )
  274. ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
  275. return ffi.buffer(ciphertext, clen[0])[:]
  276. def crypto_aead_chacha20poly1305_decrypt(
  277. ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
  278. ) -> bytes:
  279. """
  280. Decrypt the given ``ciphertext`` using the "legacy" construction
  281. described in draft-agl-tls-chacha20poly1305.
  282. :param ciphertext: authenticated ciphertext
  283. :type ciphertext: bytes
  284. :param aad:
  285. :type aad: Optional[bytes]
  286. :param nonce:
  287. :type nonce: bytes
  288. :param key:
  289. :type key: bytes
  290. :return: message
  291. :rtype: bytes
  292. """
  293. ensure(
  294. isinstance(ciphertext, bytes),
  295. "Input ciphertext type must be bytes",
  296. raising=exc.TypeError,
  297. )
  298. clen = len(ciphertext)
  299. ensure(
  300. clen <= _aead_chacha20poly1305_CRYPTBYTES_MAX,
  301. "Ciphertext must be at most {} bytes long".format(
  302. _aead_chacha20poly1305_CRYPTBYTES_MAX
  303. ),
  304. raising=exc.ValueError,
  305. )
  306. ensure(
  307. isinstance(aad, bytes) or (aad is None),
  308. "Additional data must be bytes or None",
  309. raising=exc.TypeError,
  310. )
  311. ensure(
  312. isinstance(nonce, bytes)
  313. and len(nonce) == crypto_aead_chacha20poly1305_NPUBBYTES,
  314. "Nonce must be a {} bytes long bytes sequence".format(
  315. crypto_aead_chacha20poly1305_NPUBBYTES
  316. ),
  317. raising=exc.TypeError,
  318. )
  319. ensure(
  320. isinstance(key, bytes)
  321. and len(key) == crypto_aead_chacha20poly1305_KEYBYTES,
  322. "Key must be a {} bytes long bytes sequence".format(
  323. crypto_aead_chacha20poly1305_KEYBYTES
  324. ),
  325. raising=exc.TypeError,
  326. )
  327. mxout = clen - crypto_aead_chacha20poly1305_ABYTES
  328. mlen = ffi.new("unsigned long long *")
  329. message = ffi.new("unsigned char[]", mxout)
  330. if aad:
  331. _aad = aad
  332. aalen = len(aad)
  333. else:
  334. _aad = ffi.NULL
  335. aalen = 0
  336. res = lib.crypto_aead_chacha20poly1305_decrypt(
  337. message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
  338. )
  339. ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
  340. return ffi.buffer(message, mlen[0])[:]
  341. def crypto_aead_xchacha20poly1305_ietf_encrypt(
  342. message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
  343. ) -> bytes:
  344. """
  345. Encrypt the given ``message`` using the long-nonces xchacha20poly1305
  346. construction.
  347. :param message:
  348. :type message: bytes
  349. :param aad:
  350. :type aad: Optional[bytes]
  351. :param nonce:
  352. :type nonce: bytes
  353. :param key:
  354. :type key: bytes
  355. :return: authenticated ciphertext
  356. :rtype: bytes
  357. """
  358. ensure(
  359. isinstance(message, bytes),
  360. "Input message type must be bytes",
  361. raising=exc.TypeError,
  362. )
  363. mlen = len(message)
  364. ensure(
  365. mlen <= crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX,
  366. "Message must be at most {} bytes long".format(
  367. crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX
  368. ),
  369. raising=exc.ValueError,
  370. )
  371. ensure(
  372. isinstance(aad, bytes) or (aad is None),
  373. "Additional data must be bytes or None",
  374. raising=exc.TypeError,
  375. )
  376. ensure(
  377. isinstance(nonce, bytes)
  378. and len(nonce) == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
  379. "Nonce must be a {} bytes long bytes sequence".format(
  380. crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
  381. ),
  382. raising=exc.TypeError,
  383. )
  384. ensure(
  385. isinstance(key, bytes)
  386. and len(key) == crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
  387. "Key must be a {} bytes long bytes sequence".format(
  388. crypto_aead_xchacha20poly1305_ietf_KEYBYTES
  389. ),
  390. raising=exc.TypeError,
  391. )
  392. if aad:
  393. _aad = aad
  394. aalen = len(aad)
  395. else:
  396. _aad = ffi.NULL
  397. aalen = 0
  398. mlen = len(message)
  399. mxout = mlen + crypto_aead_xchacha20poly1305_ietf_ABYTES
  400. clen = ffi.new("unsigned long long *")
  401. ciphertext = ffi.new("unsigned char[]", mxout)
  402. res = lib.crypto_aead_xchacha20poly1305_ietf_encrypt(
  403. ciphertext, clen, message, mlen, _aad, aalen, ffi.NULL, nonce, key
  404. )
  405. ensure(res == 0, "Encryption failed.", raising=exc.CryptoError)
  406. return ffi.buffer(ciphertext, clen[0])[:]
  407. def crypto_aead_xchacha20poly1305_ietf_decrypt(
  408. ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
  409. ) -> bytes:
  410. """
  411. Decrypt the given ``ciphertext`` using the long-nonces xchacha20poly1305
  412. construction.
  413. :param ciphertext: authenticated ciphertext
  414. :type ciphertext: bytes
  415. :param aad:
  416. :type aad: Optional[bytes]
  417. :param nonce:
  418. :type nonce: bytes
  419. :param key:
  420. :type key: bytes
  421. :return: message
  422. :rtype: bytes
  423. """
  424. ensure(
  425. isinstance(ciphertext, bytes),
  426. "Input ciphertext type must be bytes",
  427. raising=exc.TypeError,
  428. )
  429. clen = len(ciphertext)
  430. ensure(
  431. clen <= _aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX,
  432. "Ciphertext must be at most {} bytes long".format(
  433. _aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX
  434. ),
  435. raising=exc.ValueError,
  436. )
  437. ensure(
  438. isinstance(aad, bytes) or (aad is None),
  439. "Additional data must be bytes or None",
  440. raising=exc.TypeError,
  441. )
  442. ensure(
  443. isinstance(nonce, bytes)
  444. and len(nonce) == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
  445. "Nonce must be a {} bytes long bytes sequence".format(
  446. crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
  447. ),
  448. raising=exc.TypeError,
  449. )
  450. ensure(
  451. isinstance(key, bytes)
  452. and len(key) == crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
  453. "Key must be a {} bytes long bytes sequence".format(
  454. crypto_aead_xchacha20poly1305_ietf_KEYBYTES
  455. ),
  456. raising=exc.TypeError,
  457. )
  458. mxout = clen - crypto_aead_xchacha20poly1305_ietf_ABYTES
  459. mlen = ffi.new("unsigned long long *")
  460. message = ffi.new("unsigned char[]", mxout)
  461. if aad:
  462. _aad = aad
  463. aalen = len(aad)
  464. else:
  465. _aad = ffi.NULL
  466. aalen = 0
  467. res = lib.crypto_aead_xchacha20poly1305_ietf_decrypt(
  468. message, mlen, ffi.NULL, ciphertext, clen, _aad, aalen, nonce, key
  469. )
  470. ensure(res == 0, "Decryption failed.", raising=exc.CryptoError)
  471. return ffi.buffer(message, mlen[0])[:]