3
3
# Distributed under the MIT software license, see the accompanying
4
4
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
6
- """Test-only implementation of ChaCha20 Poly1305 AEAD Construction in RFC 8439
6
+ """Test-only implementation of ChaCha20 Poly1305 AEAD Construction in RFC 8439 and FSChaCha20Poly1305 for BIP 324
7
7
8
8
It is designed for ease of understanding, not performance.
9
9
13
13
14
14
import unittest
15
15
16
- from .chacha20 import chacha20_block
16
+ from .chacha20 import chacha20_block , REKEY_INTERVAL
17
17
from .poly1305 import Poly1305
18
18
19
19
@@ -60,6 +60,32 @@ def aead_chacha20_poly1305_decrypt(key, nonce, aad, ciphertext):
60
60
return bytes (ret )
61
61
62
62
63
+ class FSChaCha20Poly1305 :
64
+ """Rekeying wrapper AEAD around ChaCha20Poly1305."""
65
+ def __init__ (self , initial_key ):
66
+ self ._key = initial_key
67
+ self ._packet_counter = 0
68
+
69
+ def _crypt (self , aad , text , is_decrypt ):
70
+ nonce = ((self ._packet_counter % REKEY_INTERVAL ).to_bytes (4 , 'little' ) +
71
+ (self ._packet_counter // REKEY_INTERVAL ).to_bytes (8 , 'little' ))
72
+ if is_decrypt :
73
+ ret = aead_chacha20_poly1305_decrypt (self ._key , nonce , aad , text )
74
+ else :
75
+ ret = aead_chacha20_poly1305_encrypt (self ._key , nonce , aad , text )
76
+ if (self ._packet_counter + 1 ) % REKEY_INTERVAL == 0 :
77
+ rekey_nonce = b"\xFF \xFF \xFF \xFF " + nonce [4 :]
78
+ self ._key = aead_chacha20_poly1305_encrypt (self ._key , rekey_nonce , b"" , b"\x00 " * 32 )[:32 ]
79
+ self ._packet_counter += 1
80
+ return ret
81
+
82
+ def decrypt (self , aad , ciphertext ):
83
+ return self ._crypt (aad , ciphertext , True )
84
+
85
+ def encrypt (self , aad , plaintext ):
86
+ return self ._crypt (aad , plaintext , False )
87
+
88
+
63
89
# Test vectors from RFC8439 consisting of plaintext, aad, 32 byte key, 12 byte nonce and ciphertext
64
90
AEAD_TESTS = [
65
91
# RFC 8439 Example from section 2.8.2
@@ -113,6 +139,30 @@ def aead_chacha20_poly1305_decrypt(key, nonce, aad, ciphertext):
113
139
"aaae5bb81e8407c94b2ae86ae0c7efbe" ],
114
140
]
115
141
142
+ FSAEAD_TESTS = [
143
+ ["d6a4cb04ef0f7c09c1866ed29dc24d820e75b0491032a51b4c3366f9ca35c19e"
144
+ "a3047ec6be9d45f9637b63e1cf9eb4c2523a5aab7b851ebeba87199db0e839cf"
145
+ "0d5c25e50168306377aedbe9089fd2463ded88b83211cf51b73b150608cc7a60"
146
+ "0d0f11b9a742948482e1b109d8faf15b450aa7322e892fa2208c6691e3fecf4c"
147
+ "711191b14d75a72147" ,
148
+ "786cb9b6ebf44288974cf0" ,
149
+ "5c9e1c3951a74fba66708bf9d2c217571684556b6a6a3573bff2847d38612654" ,
150
+ 500 ,
151
+ "9dcebbd3281ea3dd8e9a1ef7d55a97abd6743e56ebc0c190cb2c4e14160b385e"
152
+ "0bf508dddf754bd02c7c208447c131ce23e47a4a14dfaf5dd8bc601323950f75"
153
+ "4e05d46e9232f83fc5120fbbef6f5347a826ec79a93820718d4ec7a2b7cfaaa4"
154
+ "4b21e16d726448b62f803811aff4f6d827ed78e738ce8a507b81a8ae13131192"
155
+ "8039213de18a5120dc9b7370baca878f50ff254418de3da50c" ],
156
+ ["8349b7a2690b63d01204800c288ff1138a1d473c832c90ea8b3fc102d0bb3adc"
157
+ "44261b247c7c3d6760bfbe979d061c305f46d94c0582ac3099f0bf249f8cb234" ,
158
+ "" ,
159
+ "3bd2093fcbcb0d034d8c569583c5425c1a53171ea299f8cc3bbf9ae3530adfce" ,
160
+ 60000 ,
161
+ "30a6757ff8439b975363f166a0fa0e36722ab35936abd704297948f45083f4d4"
162
+ "99433137ce931f7fca28a0acd3bc30f57b550acbc21cbd45bbef0739d9caf30c"
163
+ "14b94829deb27f0b1923a2af704ae5d6" ],
164
+ ]
165
+
116
166
117
167
class TestFrameworkAEAD (unittest .TestCase ):
118
168
def test_aead (self ):
@@ -128,3 +178,24 @@ def test_aead(self):
128
178
self .assertEqual (hex_cipher , ciphertext .hex ())
129
179
plaintext = aead_chacha20_poly1305_decrypt (key , nonce , aad , ciphertext )
130
180
self .assertEqual (plain , plaintext )
181
+
182
+ def test_fschacha20poly1305aead (self ):
183
+ "FSChaCha20Poly1305 AEAD test vectors."
184
+ for test_vector in FSAEAD_TESTS :
185
+ hex_plain , hex_aad , hex_key , msg_idx , hex_cipher = test_vector
186
+ plain = bytes .fromhex (hex_plain )
187
+ aad = bytes .fromhex (hex_aad )
188
+ key = bytes .fromhex (hex_key )
189
+
190
+ enc_aead = FSChaCha20Poly1305 (key )
191
+ dec_aead = FSChaCha20Poly1305 (key )
192
+
193
+ for _ in range (msg_idx ):
194
+ enc_aead .encrypt (b"" , b"" )
195
+ ciphertext = enc_aead .encrypt (aad , plain )
196
+ self .assertEqual (hex_cipher , ciphertext .hex ())
197
+
198
+ for _ in range (msg_idx ):
199
+ dec_aead .decrypt (b"" , bytes (16 ))
200
+ plaintext = dec_aead .decrypt (aad , ciphertext )
201
+ self .assertEqual (plain , plaintext )
0 commit comments