Skip to content

Commit 6cbf8db

Browse files
committed
Rough adding of chacha20poly1305
1 parent b74b068 commit 6cbf8db

File tree

3 files changed

+159
-2
lines changed

3 files changed

+159
-2
lines changed

scripts/build_ffi.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def make_flags(prefix, fips):
215215

216216
flags.append("--disable-md5")
217217
flags.append("--disable-sha224")
218-
flags.append("--disable-poly1305")
218+
flags.append("--enable-poly1305")
219219

220220
# asymmetric ciphers
221221
flags.append("--enable-rsa")
@@ -367,6 +367,7 @@ def get_features(local_wolfssl, features):
367367
features["WC_RNG_SEED_CB"] = 1 if '#define WC_RNG_SEED_CB' in defines else 0
368368
features["AESGCM_STREAM"] = 1 if '#define WOLFSSL_AESGCM_STREAM' in defines else 0
369369
features["RSA_PSS"] = 1 if '#define WC_RSA_PSS' in defines else 0
370+
features["CHACHA20_POLY1305"] = 1 if '#define HAVE_CHACHA' & '#define HAVE_POLY1305' in defines else 0
370371

371372
if '#define HAVE_FIPS' in defines:
372373
if not fips:
@@ -439,6 +440,8 @@ def build_ffi(local_wolfssl, features):
439440
#include <wolfssl/wolfcrypt/ed25519.h>
440441
#include <wolfssl/wolfcrypt/ed448.h>
441442
#include <wolfssl/wolfcrypt/curve25519.h>
443+
#include <wolfssl/wolfcrypt/poly1305.h>
444+
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
442445
"""
443446

444447
init_source_string = """
@@ -474,6 +477,7 @@ def build_ffi(local_wolfssl, features):
474477
int WC_RNG_SEED_CB_ENABLED = """ + str(features["WC_RNG_SEED_CB"]) + """;
475478
int AESGCM_STREAM_ENABLED = """ + str(features["AESGCM_STREAM"]) + """;
476479
int RSA_PSS_ENABLED = """ + str(features["RSA_PSS"]) + """;
480+
int CHACHA20_POLY1305_ENABLED = """ + str(features["CHACHA20_POLY1305"]) + """;
477481
"""
478482

479483
ffibuilder.set_source( "wolfcrypt._ffi", init_source_string,
@@ -508,6 +512,7 @@ def build_ffi(local_wolfssl, features):
508512
extern int WC_RNG_SEED_CB_ENABLED;
509513
extern int AESGCM_STREAM_ENABLED;
510514
extern int RSA_PSS_ENABLED;
515+
extern int CHACHA20_POLY1305_ENABLED;
511516
512517
typedef unsigned char byte;
513518
typedef unsigned int word32;
@@ -625,15 +630,34 @@ def build_ffi(local_wolfssl, features):
625630
word32 authTagSz);
626631
"""
627632

633+
628634
if features["CHACHA"]:
629635
cdef += """
630636
typedef struct { ...; } ChaCha;
631-
632637
int wc_Chacha_SetKey(ChaCha*, const byte*, word32);
633638
int wc_Chacha_SetIV(ChaCha*, const byte*, word32);
634639
int wc_Chacha_Process(ChaCha*, byte*, const byte*,word32);
635640
"""
636641

642+
if features["CHACHA20_POLY1305"]:
643+
cdef += """
644+
typedef struct { ...; } ChaChaPoly_Aead;
645+
int wc_ChaCha20Poly1305_Encrypt(const byte inKey, const byte inIV, const byte* inAAD,
646+
word32 inAADLen, const byte* inPlaintext, word32 inPlaintextLen, byte* outCiphertext,
647+
byte outAuthTag);
648+
int wc_ChaCha20Poly1305_Decrypt( const byte inKey, const byte inIV, const byte* inAAD,
649+
word32 inAADLen, const byte* inCiphertext, word32 inCiphertextLen,
650+
const byte inAuthTag, byte* outPlaintext);
651+
int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead,
652+
const byte* inAAD, word32 inAADLen);
653+
int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, const byte inKey, const byte inIV,
654+
int isEncrypt);
655+
int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead,
656+
const byte* inData, byte* outData, word32 dataLen);
657+
int wc_ChaCha20Poly1305_Final(byte*, word32);
658+
int wc_ChaCha20Poly1305_CheckTag(const byte* authtag, const byte* authTagChk);
659+
"""
660+
637661
if features["HMAC"]:
638662
cdef += """
639663
typedef struct { ...; } Hmac;
@@ -927,6 +951,7 @@ def main(ffibuilder):
927951
"WC_RNG_SEED_CB": 0,
928952
"AESGCM_STREAM": 1,
929953
"RSA_PSS": 1,
954+
"CHACHA20_POLY1305": 1
930955
}
931956

932957
# Ed448 requires SHAKE256, which isn't part of the Windows build, yet.

tests/test_ciphers.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
if _lib.AES_ENABLED:
3636
from wolfcrypt.ciphers import Aes
3737

38+
if _lib.CHACHA20_POLY1305_ENABLED:
39+
from wolfcrypt.ciphers import ChaCha20Poly1305
40+
3841
if _lib.CHACHA_ENABLED:
3942
from wolfcrypt.ciphers import ChaCha
4043

@@ -73,6 +76,14 @@ def vectors():
7376
ciphertext=h2b("959492575f4281532ccc9d4677a233cb"),
7477
ciphertext_ctr = h2b('287528ddf484b1055debbe751eb52b8a')
7578
)
79+
if _lib.CHACHA20_POLY1305_ENABLED:
80+
vectorArray[ChaCha20Poly1305]=TestVector(
81+
key="0123456789abcdef",
82+
iv="1234567890abcdef",
83+
plaintext=t2b("now is the time "),
84+
ciphertext=h2b("959492575f4281532ccc9d4677a233cb"),
85+
ciphertext_ctr = h2b('287528ddf484b1055debbe751eb52b8a')
86+
)
7687
if _lib.CHACHA_ENABLED:
7788
vectorArray[ChaCha]=TestVector(
7889
key="0123456789abcdef01234567890abcdef",
@@ -325,7 +336,18 @@ def test_chacha_enc_dec(chacha_obj):
325336
dec = chacha_obj.decrypt(cyt)
326337
assert plaintext == dec
327338

339+
if _lib.CHACHA20_POLY1305_ENABLED:
340+
@pytest.fixture
341+
def chacha20_poly1305_obj(vectors):
342+
r = ChaCha20Poly1305(vectors[ChaCha20Poly1305].key, vectors[ChaCha20Poly1305].iv)
343+
return r
328344

345+
@pytest.fixture
346+
def test_chacha20_poly1305_enc_dec(chacha20_poly1305_obj):
347+
plaintext = t2b("Everyone gets Friday off.")
348+
cyt = chacha20_poly1305_obj.encrypt(plaintext)
349+
dec = chacha20_poly1305_obj.decrypt(cyt)
350+
assert plaintext == dec
329351

330352

331353
if _lib.RSA_ENABLED:

wolfcrypt/ciphers.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ def final(self, authTag=None):
369369
raise WolfCryptError("Decryption error (%d)" % ret)
370370

371371

372+
372373
if _lib.CHACHA_ENABLED:
373374
class ChaCha(_Cipher):
374375
"""
@@ -424,6 +425,115 @@ def set_iv(self, nonce, counter = 0):
424425
self._IV_counter = counter
425426
self._set_key(0)
426427

428+
if _lib.CHACHA20_POLY1305_ENABLED:
429+
class ChaCha20Poly1305(object):
430+
"""
431+
ChaCha20 Poly1305
432+
"""
433+
block_size = 16
434+
_key_sizes = [16, 24, 32]
435+
_native_type = "ChaCha *"
436+
_aad = bytes()
437+
_tag_bytes = 16
438+
_mode = None
439+
generatedCipherText = bytes()
440+
generatedPlainText = bytes()
441+
442+
def __init__(self, key, IV, isEncrypt):
443+
"""
444+
tag_bytes is the number of bytes to use for the authentication tag during encryption
445+
"""
446+
key = t2b(key)
447+
IV = t2b(IV)
448+
isEncrypt = True
449+
if len(key) not in self._key_sizes:
450+
raise ValueError("key must be %s in length, not %d" %
451+
(self._key_sizes, len(key)))
452+
self._native_object = _ffi.new(self._native_type)
453+
_lib.wc_AesInit(self._native_object, _ffi.NULL, -2)
454+
ret = _lib.wc_ChaCha20Poly1305_Init(self._native_object, self._aad, len(self._aad),
455+
key, len(key), IV, len(IV), isEncrypt)
456+
if ret < 0:
457+
raise WolfCryptError("Init error (%d)" % ret)
458+
459+
def set_aad(self, data):
460+
"""
461+
Set the additional authentication data for the stream
462+
"""
463+
if self._mode is not None:
464+
raise WolfCryptError("AAD can only be set before encrypt() or decrypt() is called")
465+
self._aad = t2b(data)
466+
467+
def get_aad(self):
468+
return self._aad
469+
470+
def encrypt(self, data):
471+
"""
472+
Add more data to the encryption stream
473+
"""
474+
data = t2b(data)
475+
aad = bytes()
476+
if self._mode is None:
477+
self._mode = _ENCRYPTION
478+
aad = self._aad
479+
elif self._mode == _DECRYPTION:
480+
raise WolfCryptError("Class instance already in use for decryption")
481+
self._buf = _ffi.new("byte[%d]" % (len(data)))
482+
ret = _lib.wc_ChaCha20Poly1305_UpdateData(self._native_type, aad, len(aad))
483+
if ret < 0:
484+
raise WolfCryptError("Decryption error (%d)" % ret)
485+
return bytes(self._buf)
486+
487+
def decrypt(self, data):
488+
"""
489+
Add more data to the decryption stream
490+
"""
491+
aad = bytes()
492+
data = t2b(data)
493+
if self._mode is None:
494+
self._mode = _DECRYPTION
495+
aad = self._aad
496+
elif self._mode == _ENCRYPTION:
497+
raise WolfCryptError("Class instance already in use for decryption")
498+
self._buf = _ffi.new("byte[%d]" % (len(data)))
499+
ret = _lib.wc_ChaCha20Poly1305_Decrypt(self._key, self_IV, aad, len(aad),
500+
generatedCipherText, len(generatedCipherText),
501+
authTag, generatedPlainText)
502+
if ret < 0:
503+
raise WolfCryptError("Decryption error (%d)" % ret)
504+
return bytes(self._buf)
505+
506+
def checkTag(self, authTag):
507+
"""
508+
Check the authentication tag for the stream
509+
"""
510+
authTag = t2b(authTag)
511+
ret = _lib.wc_ChaCha20Poly1305_CheckTag(authTag, len(authTag))
512+
if ret < 0:
513+
raise WolfCryptError("Decryption error (%d)" % ret)
514+
515+
def final(self, authTag=None):
516+
"""
517+
When encrypting, finalize the stream and return an authentication tag for the stream.
518+
When decrypting, verify the authentication tag for the stream.
519+
The authTag parameter is only used for decrypting.
520+
"""
521+
if self._mode is None:
522+
raise WolfCryptError("Final called with no encryption or decryption")
523+
elif self._mode == _ENCRYPTION:
524+
authTag = _ffi.new("byte[%d]" % self._tag_bytes)
525+
ret = _lib.wc_ChaCha20Poly1305_Final(self._native_type, authTag)
526+
if ret < 0:
527+
raise WolfCryptError("Encryption error (%d)" % ret)
528+
return _ffi.buffer(authTag)[:]
529+
else:
530+
if authTag is None:
531+
raise WolfCryptError("authTag parameter required")
532+
authTag = t2b(authTag)
533+
ret = _lib.wc_ChaCha20Poly1305_Final(self._native_type, authTag)
534+
if ret < 0:
535+
raise WolfCryptError("Decryption error (%d)" % ret)
536+
427537
if _lib.DES3_ENABLED:
428538
class Des3(_Cipher):
429539
"""

0 commit comments

Comments
 (0)