ChaCha20#
How to use the crypto_condor.primitives.ChaCha20
module to test implementations
of ChaCha20.
Supported parameters#
There are two supported modes of operation: ChaCha20 on its own and the construction
with Poly1305. They are defined by the Mode
enum.
Two operations can be performed: ENCRYPT
and
DECRYPT
. These are defined by the Operation
enum.
Test an implementation directly#
- crypto_condor.primitives.ChaCha20.test(encrypt, decrypt, mode, *, resilience=True)#
Tests an implementation of ChaCha20(-Poly1305) encryption and decryption.
It runs test vectors on the given functions. These functions must conform to the
Encrypt
andDecrypt
protocols.- Parameters:
- Keyword Arguments:
resilience – If True, runs Wycheproof test vectors.
- Returns:
A dictionary of results.
If resilience is True, Wycheproof vectors are used. The results are indexed by
Wycheproof/encrypt
andWycheproof/decrypt
respectively.- Return type:
Example
Let’s test PyCryptodome’s implementation of ChaCha20.
We start by importing the ChaCha20 modules of crypto-condor and PyCryptodome.
>>> from crypto_condor.primitives import ChaCha20 >>> from Crypto.Cipher import ChaCha20 as pyChaCha20
We need to wrap PyCryptodome’s to match the signature of
Encrypt
andDecrypt
. In both cases we want to match the first overload, as it is the one that corresponds to ChaCha20.>>> def my_enc( ... key: bytes, ... plaintext: bytes, ... nonce: bytes, ... *, ... init_counter: int = 0 ... ) -> bytes: ... cipher = pyChaCha20.new(key=key, nonce=nonce) ... if init_counter > 0: ... cipher.seek(64 * init_counter) ... return cipher.encrypt(plaintext) >>> def my_dec( ... key: bytes, ... ciphertext: bytes, ... nonce: bytes, ... *, ... init_counter: int = 0 ... ) -> bytes: ... cipher = pyChaCha20.new(key=key, nonce=nonce) ... if init_counter > 0: ... cipher.seek(64 * init_counter) ... return cipher.decrypt(ciphertext)
And we test the functions we defined.
>>> mode = ChaCha20.Mode.CHACHA20 >>> results_dict = ChaCha20.test(my_enc, my_dec, mode) [Wycheproof] ... >>> assert results_dict.check()
Test the output of an implementation#
Note
From the CLI you can test the file with the test output ChaCha20
command.
- crypto_condor.primitives.ChaCha20.verify_file(filename, mode, operation)#
Tests the output of an implementation.
Tests an implementation from a set of inputs passed to it and the outputs it returned. These inputs are passed to the internal implementation and the results are compared to the outputs given.
- Format:
One line per operation.
Lines are separated by newlines (
\n
).Lines that start with # are counted as comments and ignored.
Arguments written in hexadecimal and separated by slashes.
Arguments in brackets are optional. If omitted, don’t include the trailing slash.
For ChaCha20, the order of the arguments is:
key/input/output/nonce[/init_counter]
For ChaCha20-Poly1305, the order of the arguments is:
key/input/output/nonce/mac[/aad]
- Where:
input
is the plaintext when encrypting (resp. the ciphertext when decrypting).output
is the ciphertext when encrypting (resp. the plaintext when decrypting).nonce
is the nonce used for that operation.init_counter
is the initial position in the keystream to seek before the operation.mac
is the MAC tag. When encrypting, it is compared to the MAC returned by the internal implementation. When decrypting it is used to authenticate the ciphertext.aad
is the associated data. Can be empty.
- Parameters:
- Returns:
The results of running the inputs of each line with the internal implementation, and comparing both outputs to see if they match.
Parsing errors are considered as test failures.
- Return type:
Example
Let’s generate 10 random tuples of (key, plaintext, nonce), encrypt the plaintexts using PyCryptodome, and write everything to a file.
>>> import random >>> from crypto_condor.primitives import ChaCha20 >>> from Crypto.Cipher import ChaCha20 as pyChaCha20 >>> filename = "/tmp/crypto-condor-test/chacha20-verify.txt" >>> with open(filename, "w") as file: ... for _ in range(10): ... # Pick random values ... key = random.randbytes(32) ... plaintext = random.randbytes(64) ... nonce = random.randbytes(12) ... # Encrypt ... cipher = pyChaCha20.new(key=key, nonce=nonce) ... ciphertext = cipher.encrypt(plaintext) ... # Convert to hexadecimal ... kh, nh = key.hex(), nonce.hex() ... ph, ch = plaintext.hex(), ciphertext.hex() ... # Create the line to write, note the absent init_counter ... line = f"{kh}/{ph}/{ch}/{nh}\n" ... _ = file.write(line)
Now we can test the file.
>>> mode = ChaCha20.Mode.CHACHA20 >>> operation = ChaCha20.Operation.ENCRYPT >>> results = ChaCha20.verify_file(filename, mode, operation) Testing ... >>> assert results.check()
Run a wrapper#
Note
Available wrappers are defined by the Wrapper
enum.
- crypto_condor.primitives.ChaCha20.run_wrapper(language, mode, *, resilience=True, encrypt=True, decrypt=True)#
Runs a wrapper.
- Parameters:
- Keyword Arguments:
resilience – Whether to run resilience test vectors.
encrypt – Whether to test the encryption.
decrypt – Whether to test the decryption.
- Returns:
The results from
test()
.- Return type:
Protocols#
- protocol crypto_condor.primitives.ChaCha20.Encrypt#
Represents a function that encrypts with ChaCha20.
Encryption functions must behave like one of the
__call__()
overloads below to be tested with this module. The first corresponds to ChaCha20, the second to ChaCha20-Poly1305.Classes that implement this protocol must have the following methods / attributes:
- __call__(key: bytes, plaintext: bytes, nonce: bytes, *, init_counter: int = 0) bytes #
- __call__(key: bytes, plaintext: bytes, nonce: bytes, *, aad: bytes | None) CiphertextAndTag
Encrypts with ChaCha20(-Poly1305).
- Parameters:
key – The symmetric key.
plaintext – The message to encrypt.
nonce – The nonce to use for this message.
- Keyword Arguments:
init_counter – (ChaCha20 only) A position to seek in the keystream before encrypting, in bytes.
aad – (ChaCha20-Poly1305 only) The associated data, can be empty or None.
- Returns:
(ChaCha20) The ciphertext.
(ChaCha20-Poly1305) A (ciphertext, MAC) tuple.
- protocol crypto_condor.primitives.ChaCha20.Decrypt#
Represents a function that decrypts with ChaCha20.
Decryption functions must behave like one of the
__call__()
overloads below to be tested with this module. The first corresponds to ChaCha20, the second to ChaCha20-Poly1305.Classes that implement this protocol must have the following methods / attributes:
- __call__(key: bytes, ciphertext: bytes, nonce: bytes, *, init_counter: int = 0) bytes #
- __call__(key: bytes, ciphertext: bytes, nonce: bytes, *, mac: bytes, aad: bytes | None) PlaintextAndBool
Decrypts with ChaCha20(-Poly1305).
- Parameters:
key – The symmetric key.
ciphertext – The message to decrypt.
nonce – The nonce to use for this message.
- Keyword Arguments:
init_counter – (ChaCha20 only) A position to seek in the keystream before encrypting, in bytes.
mac – (ChaCha20-Poly1305 only) The MAC tag to use for authenticating the ciphertext.
aad – (ChaCha20-Poly1305 only) The associated data, can be empty or None.
- Returns:
(ChaCha20) The plaintext.
(ChaCha20-Poly1305) If the MAC is valid, a (plaintext, True) tuple. Otherwise the plaintext should not be released, so return a (None, False) tuple.