From 08afb177450c1e90a8ee342c5bbed671d2845996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Thu, 25 Jun 2020 12:02:19 +0300 Subject: [PATCH] Implement xchacha20 --- chacha20.py | 5 +++-- chapoly_aead.py | 9 ++++----- xchacha.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 xchacha.py diff --git a/chacha20.py b/chacha20.py index 2e657f1..42842dd 100644 --- a/chacha20.py +++ b/chacha20.py @@ -30,7 +30,7 @@ def u32(i): def unu32(n): return bytes((n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, n >> 24)) -def chacha20_block(key, counter, nonce): +def chacha20_block(key, counter, nonce, do_final_add = True): s = [ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, u32(key[:4]), u32(key[4:8]), u32(key[8:12]), u32(key[12:16]), @@ -51,7 +51,8 @@ def chacha20_block(key, counter, nonce): qr(2, 7, 8, 13) qr(3, 4, 9, 14) - s = [add(initial[i], s[i]) for i in range(len(s))] + if do_final_add: + s = [add(initial[i], s[i]) for i in range(len(s))] return b''.join(unu32(i) for i in s) def ceildiv(a, b): diff --git a/chapoly_aead.py b/chapoly_aead.py index 75a6b18..90f8479 100644 --- a/chapoly_aead.py +++ b/chapoly_aead.py @@ -26,14 +26,12 @@ def generate_tag(aad, key, nonce, ciphertext): )) return poly1305.poly1305(message, key) -def chapoly_aead_enc(aad, key, iv, constant, plaintext): - nonce = constant + iv +def chapoly_aead_enc(aad, key, nonce, plaintext): ciphertext = bytes(chacha20.chacha20(key, 1, nonce, plaintext)) tag = generate_tag(aad, key, nonce, ciphertext) return ciphertext + tag -def chapoly_aead_dec(aad, key, iv, constant, ciphertext): - nonce = constant + iv +def chapoly_aead_dec(aad, key, nonce, ciphertext): ciphertext, tag = ciphertext[:-16], ciphertext[-16:] expected_tag = generate_tag(aad, key, nonce, ciphertext) if not secrets.compare_digest(tag, expected_tag): @@ -46,4 +44,5 @@ if __name__ == '__main__': key = bytes.fromhex('80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f') iv = b'@ABCDEFG' constant = bytes.fromhex('07 00 00 00') - print(chapoly_aead_enc(aad, key, iv, constant, plaintext) == bytes.fromhex('d3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2 a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6 3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b 1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36 92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58 fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc 3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b 61 16 1a:e1:0b:59:4f:09:e2:6a:7e:90:2e:cb:d0:60:06:91'.replace(':',''))) + nonce = constant + iv + print(chapoly_aead_enc(aad, key, nonce, plaintext) == bytes.fromhex('d3 1a 8d 34 64 8e 60 db 7b 86 af bc 53 ef 7e c2 a4 ad ed 51 29 6e 08 fe a9 e2 b5 a7 36 ee 62 d6 3d be a4 5e 8c a9 67 12 82 fa fb 69 da 92 72 8b 1a 71 de 0a 9e 06 0b 29 05 d6 a5 b6 7e cd 3b 36 92 dd bd 7f 2d 77 8b 8c 98 03 ae e3 28 09 1b 58 fa b3 24 e4 fa d6 75 94 55 85 80 8b 48 31 d7 bc 3f f4 de f0 8e 4b 7a 9d e5 76 d2 65 86 ce c6 4b 61 16 1a:e1:0b:59:4f:09:e2:6a:7e:90:2e:cb:d0:60:06:91'.replace(':',''))) diff --git a/xchacha.py b/xchacha.py new file mode 100644 index 0000000..e87c62f --- /dev/null +++ b/xchacha.py @@ -0,0 +1,28 @@ +import chacha20 +import chapoly_aead +leu=lambda b:sum(b[i]<<(i*8)for i in range(len(b))) + +def hchacha20(key, nonce): + counter = leu(nonce[:4]) + nonce = nonce[4:] + state = chacha20.chacha20_block(key, counter, nonce, do_final_add = False) + return state[:16] + state[48:] + +def enc(aad, key, nonce, plaintext): + subkey = hchacha20(key, nonce[:16]) + chacha20_nonce = b'\x00'*4 + nonce[16:] + return chapoly_aead.chapoly_aead_enc(aad, subkey, chacha20_nonce, plaintext) + +def dec(aad, key, nonce, ciphertext): + subkey = hchacha20(key, nonce[:16]) + chacha20_nonce = b'\x00'*4 + nonce[16:] + return chapoly_aead.chapoly_aead_dec(aad, subkey, chacha20_nonce, ciphertext) + +if __name__ == '__main__': + plaintext = bytes.fromhex('4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c 65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73 73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63 6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f 6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20 74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73 63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69 74 2e') + aad = bytes.fromhex('50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7') + key = bytes.fromhex('80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f') + nonce = b'@ABCDEFGHIJKLMNOPQRSTUVW' + ciphertext = enc(aad, key, nonce, plaintext) + print(ciphertext == bytes.fromhex('bd 6d 17 9d 3e 83 d4 3b 95 76 57 94 93 c0 e9 39 57 2a 17 00 25 2b fa cc be d2 90 2c 21 39 6c bb 73 1c 7f 1b 0b 4a a6 44 0b f3 a8 2f 4e da 7e 39 ae 64 c6 70 8c 54 c2 16 cb 96 b7 2e 12 13 b4 52 2f 8c 9b a4 0d b5 d9 45 b1 1b 69 b9 82 c1 bb 9e 3f 3f ac 2b c3 69 48 8f 76 b2 38 35 65 d3 ff f9 21 f9 66 4c 97 63 7d a9 76 88 12 f6 15 c6 8b 13 b5 2e c0:87:59:24:c1:c7:98:79:47:de:af:d8:78:0a:cf:49'.replace(':', ''))) + print(plaintext == dec(aad, key, nonce, ciphertext))