Fix ChaCha20Poly1305 to be singleshot
parent
a6779c2e30
commit
01219a4659
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
# test_aesgcmstream.py
|
||||
# test_chacha20poly1305.py
|
||||
#
|
||||
# Copyright (C) 2022 wolfSSL Inc.
|
||||
#
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
from wolfcrypt._ffi import lib as _lib
|
||||
|
||||
if _lib.CHACHA20_POLY1305_ENABLED:
|
||||
from collections import namedtuple
|
||||
import pytest
|
||||
from wolfcrypt.utils import t2b
|
||||
from wolfcrypt.exceptions import WolfCryptError
|
||||
|
|
@ -32,20 +31,34 @@ if _lib.CHACHA20_POLY1305_ENABLED:
|
|||
from wolfcrypt.ciphers import ChaCha20Poly1305
|
||||
|
||||
def test_encrypt_decrypt():
|
||||
key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
|
||||
key = h2b(key)
|
||||
iv = "07000000404142434445464748" #Chnage from C test
|
||||
iv = h2b(iv)
|
||||
aad = "50515253c0c1c2c3c4c5c6c7" #Change from C test
|
||||
aad = h2b(aad)
|
||||
plaintext1 = "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e"
|
||||
plaintext1 = h2b(plaintext1)
|
||||
cipher1 = "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"
|
||||
authTag = "1ae10b594f09e26a7e902ecbd0600691"
|
||||
chacha = ChaCha20Poly1305(key, iv, aad)
|
||||
generatedChipherText, generatedAuthTag = chacha.encrypt(plaintext1)
|
||||
assert h2b(cipher1) == generatedChipherText
|
||||
assert h2b(authTag) == generatedAuthTag
|
||||
chachadec = ChaCha20Poly1305(key, iv, aad)
|
||||
generatedPlaintextdec = chachadec.decrypt(generatedAuthTag, generatedChipherText)#takes in the generated authtag made by encrypt and decrypts and produces the plaintext
|
||||
assert generatedPlaintextdec == t2b("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.")
|
||||
key = h2b("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
|
||||
iv = h2b("07000000404142434445464748")
|
||||
aad = h2b("50515253c0c1c2c3c4c5c6c7")
|
||||
plaintext = h2b("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e")
|
||||
expected_ciphertext = h2b("d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116")
|
||||
expected_authTag = h2b("1ae10b594f09e26a7e902ecbd0600691")
|
||||
|
||||
chacha = ChaCha20Poly1305(key)
|
||||
ciphertext, authTag = chacha.encrypt(aad, iv, plaintext)
|
||||
assert ciphertext == expected_ciphertext
|
||||
assert authTag == expected_authTag
|
||||
|
||||
decrypted = chacha.decrypt(aad, iv, authTag, ciphertext)
|
||||
assert decrypted == t2b("Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.")
|
||||
|
||||
def test_invalid_key_size():
|
||||
with pytest.raises(ValueError):
|
||||
ChaCha20Poly1305(b"tooshort")
|
||||
|
||||
def test_decrypt_bad_tag():
|
||||
key = h2b("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
|
||||
iv = h2b("07000000404142434445464748")
|
||||
aad = h2b("50515253c0c1c2c3c4c5c6c7")
|
||||
plaintext = b"hello world"
|
||||
|
||||
chacha = ChaCha20Poly1305(key)
|
||||
ciphertext, authTag = chacha.encrypt(aad, iv, plaintext)
|
||||
|
||||
bad_tag = b"\x00" * 16
|
||||
with pytest.raises(WolfCryptError):
|
||||
chacha.decrypt(aad, iv, bad_tag, ciphertext)
|
||||
|
|
|
|||
|
|
@ -538,133 +538,71 @@ if _lib.CHACHA_ENABLED:
|
|||
if _lib.CHACHA20_POLY1305_ENABLED:
|
||||
class ChaCha20Poly1305(object):
|
||||
"""
|
||||
ChaCha20 Poly1305
|
||||
"""
|
||||
block_size = 16
|
||||
_key_sizes = [16, 24, 32]
|
||||
_native_type = "ChaChaPoly_Aead *"
|
||||
_aad = None
|
||||
_tag_bytes = 16
|
||||
_mode = None
|
||||
_key = bytes()
|
||||
_IV = bytes()
|
||||
ChaCha20-Poly1305 AEAD cipher.
|
||||
|
||||
def __init__(self, key, IV, aad, tag_bytes=16):
|
||||
"""
|
||||
tag_bytes is the number of bytes to use for the authentication tag during encryption
|
||||
"""
|
||||
One-shot encrypt/decrypt interface (non-streaming).
|
||||
"""
|
||||
_key_sizes = [32]
|
||||
_tag_bytes = 16
|
||||
|
||||
def __init__(self, key):
|
||||
self._key = t2b(key)
|
||||
self._IV = t2b(IV)
|
||||
self._aad = t2b(aad)
|
||||
if len(self._key) not in self._key_sizes:
|
||||
raise ValueError("key must be %s in length, not %d" %
|
||||
(self._key_sizes, len(self._key)))
|
||||
self._native_object = _ffi.new(self._native_type)
|
||||
self._mode = None
|
||||
ret = _lib.wc_ChaCha20Poly1305_Init(
|
||||
self._native_object,
|
||||
_ffi.from_buffer(self._key),
|
||||
_ffi.from_buffer(self._IV),
|
||||
1
|
||||
)
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Init error (%d)" % ret)
|
||||
|
||||
def set_aad(self, data):
|
||||
def encrypt(self, aad, iv, plaintext):
|
||||
"""
|
||||
Set the additional authentication data for the stream
|
||||
"""
|
||||
if self._mode is not None:
|
||||
raise WolfCryptError("AAD can only be set before encrypt() or decrypt() is called")
|
||||
self._aad = t2b(data)
|
||||
Encrypt plaintext data using the IV/nonce provided. The
|
||||
associated data (aad) is not encrypted but is included in the
|
||||
authentication tag.
|
||||
|
||||
def get_aad(self):
|
||||
return self._aad
|
||||
|
||||
def encrypt(self, inPlainText):
|
||||
Returns a tuple of (ciphertext, authTag).
|
||||
"""
|
||||
Add more data to the encryption stream
|
||||
"""
|
||||
inPlainText = t2b(inPlainText)
|
||||
if self._mode is None:
|
||||
self._mode = _ENCRYPTION
|
||||
aad = self._aad
|
||||
elif self._mode == _DECRYPTION:
|
||||
raise WolfCryptError("Class instance already in use for decryption")
|
||||
outGeneratedCipherText = _ffi.new("byte[%d]" % (len(inPlainText)))
|
||||
outGeneratedAuthTag = _ffi.new("byte[%d]" % self._tag_bytes)
|
||||
aad = t2b(aad)
|
||||
iv = t2b(iv)
|
||||
plaintext = t2b(plaintext)
|
||||
ciphertext = _ffi.new("byte[%d]" % len(plaintext))
|
||||
authTag = _ffi.new("byte[%d]" % self._tag_bytes)
|
||||
ret = _lib.wc_ChaCha20Poly1305_Encrypt(
|
||||
_ffi.from_buffer(self._key),
|
||||
_ffi.from_buffer(self._IV),
|
||||
_ffi.from_buffer(iv),
|
||||
_ffi.from_buffer(aad),
|
||||
len(aad),
|
||||
_ffi.from_buffer(inPlainText),
|
||||
len(inPlainText),
|
||||
outGeneratedCipherText,
|
||||
outGeneratedAuthTag
|
||||
_ffi.from_buffer(plaintext),
|
||||
len(plaintext),
|
||||
ciphertext,
|
||||
authTag
|
||||
)
|
||||
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Encryption error (%d)" % ret)
|
||||
return bytes(outGeneratedCipherText), bytes(outGeneratedAuthTag)
|
||||
return bytes(ciphertext), bytes(authTag)
|
||||
|
||||
def decrypt(self, inGeneratedAuthTag, inGeneratedCipher):
|
||||
def decrypt(self, aad, iv, authTag, ciphertext):
|
||||
"""
|
||||
Add more data to the decryption stream
|
||||
Decrypt the ciphertext using the IV/nonce and authentication tag
|
||||
provided. The integrity of the associated data (aad) is checked.
|
||||
|
||||
Returns the decrypted plaintext.
|
||||
"""
|
||||
inGeneratedCipher = t2b(inGeneratedCipher)
|
||||
inGeneratedAuthTag = t2b(inGeneratedAuthTag)
|
||||
if self._mode is None:
|
||||
self._mode = _DECRYPTION
|
||||
aad = self._aad
|
||||
elif self._mode == _ENCRYPTION:
|
||||
raise WolfCryptError("Class instance already in use for decryption")
|
||||
outPlainText = _ffi.new("byte[%d]" % (len(inGeneratedCipher)))
|
||||
aad = t2b(aad)
|
||||
iv = t2b(iv)
|
||||
authTag = t2b(authTag)
|
||||
ciphertext = t2b(ciphertext)
|
||||
plaintext = _ffi.new("byte[%d]" % len(ciphertext))
|
||||
ret = _lib.wc_ChaCha20Poly1305_Decrypt(
|
||||
_ffi.from_buffer(self._key),
|
||||
_ffi.from_buffer(self._IV),
|
||||
_ffi.from_buffer(iv),
|
||||
_ffi.from_buffer(aad),
|
||||
len(aad),
|
||||
_ffi.from_buffer(inGeneratedCipher),
|
||||
len(inGeneratedCipher),
|
||||
_ffi.from_buffer(inGeneratedAuthTag),
|
||||
outPlainText
|
||||
_ffi.from_buffer(ciphertext),
|
||||
len(ciphertext),
|
||||
_ffi.from_buffer(authTag),
|
||||
plaintext
|
||||
)
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Decryption error (%d)" % ret)
|
||||
return bytes(outPlainText)
|
||||
|
||||
def checkTag(self, authTag):
|
||||
"""
|
||||
Check the authentication tag for the stream
|
||||
"""
|
||||
authTag = t2b(authTag)
|
||||
ret = _lib.wc_ChaCha20Poly1305_CheckTag(authTag, len(authTag))
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Decryption error (%d)" % ret)
|
||||
|
||||
def final(self, authTag=None):
|
||||
"""
|
||||
When encrypting, finalize the stream and return an authentication tag for the stream.
|
||||
When decrypting, verify the authentication tag for the stream.
|
||||
The authTag parameter is only used for decrypting.
|
||||
"""
|
||||
if self._mode is None:
|
||||
raise WolfCryptError("Final called with no encryption or decryption")
|
||||
elif self._mode == _ENCRYPTION:
|
||||
authTag = _ffi.new("byte[%d]" % self._tag_bytes)
|
||||
ret = _lib.wc_ChaCha20Poly1305_Final(self._native_type, authTag)
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Encryption error (%d)" % ret)
|
||||
return _ffi.buffer(authTag)[:]
|
||||
else:
|
||||
if authTag is None:
|
||||
raise WolfCryptError("authTag parameter required")
|
||||
authTag = t2b(authTag)
|
||||
self._native_object = _ffi.new(self._native_type)
|
||||
ret = _lib.wc_ChaCha20Poly1305_Final(self._native_type, authTag)
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Decryption error (%d)" % ret)
|
||||
return bytes(plaintext)
|
||||
|
||||
if _lib.DES3_ENABLED:
|
||||
class Des3(_Cipher):
|
||||
|
|
|
|||
Loading…
Reference in New Issue