1
2
3
4 """Pure Python implementation of ChaCha20/Poly1305 AEAD cipher
5
6 Implementation that follows RFC 7539 and draft-ietf-tls-chacha20-poly1305-00
7 """
8
9 from __future__ import division
10 from .chacha import ChaCha
11 from .poly1305 import Poly1305
12 import struct
15
16 """Pure python implementation of ChaCha20/Poly1305 AEAD cipher"""
17
18 - def __init__(self, key, implementation):
19 """Set the initial state for the ChaCha20 AEAD"""
20 if len(key) != 32:
21 raise ValueError("Key must be 256 bit long")
22 if implementation != "python":
23 raise ValueError("Implementations other then python unsupported")
24
25 self.isBlockCipher = False
26 self.isAEAD = True
27 self.nonceLength = 12
28 self.tagLength = 16
29 self.implementation = implementation
30 self.name = "chacha20-poly1305"
31 self.key = key
32
33 @staticmethod
35 """Generate the key for the Poly1305 authenticator"""
36 poly = ChaCha(key, nonce)
37 return poly.encrypt(bytearray(32))
38
39 @staticmethod
41 """Return padding for the Associated Authenticated Data"""
42 if len(data) % 16 == 0:
43 return bytearray(0)
44 else:
45 return bytearray(16-(len(data)%16))
46
47 - def seal(self, nonce, plaintext, data):
48 """
49 Encrypts and authenticates plaintext using nonce and data. Returns the
50 ciphertext, consisting of the encrypted plaintext and tag concatenated.
51 """
52 if len(nonce) != 12:
53 raise ValueError("Nonce must be 96 bit large")
54
55 otk = self.poly1305_key_gen(self.key, nonce)
56
57 ciphertext = ChaCha(self.key, nonce, counter=1).encrypt(plaintext)
58
59 mac_data = data + self.pad16(data)
60 mac_data += ciphertext + self.pad16(ciphertext)
61 mac_data += struct.pack('<Q', len(data))
62 mac_data += struct.pack('<Q', len(ciphertext))
63 tag = Poly1305(otk).create_tag(mac_data)
64
65 return ciphertext + tag
66
67 - def open(self, nonce, ciphertext, data):
68 """
69 Decrypts and authenticates ciphertext using nonce and data. If the
70 tag is valid, the plaintext is returned. If the tag is invalid,
71 returns None.
72 """
73 if len(nonce) != 12:
74 raise ValueError("Nonce must be 96 bit long")
75
76 if len(ciphertext) < 16:
77 return None
78
79 expected_tag = ciphertext[-16:]
80 ciphertext = ciphertext[:-16]
81
82 otk = self.poly1305_key_gen(self.key, nonce)
83
84 mac_data = data + self.pad16(data)
85 mac_data += ciphertext + self.pad16(ciphertext)
86 mac_data += struct.pack('<Q', len(data))
87 mac_data += struct.pack('<Q', len(ciphertext))
88 tag = Poly1305(otk).create_tag(mac_data)
89
90 if tag != expected_tag:
91 return None
92
93 return ChaCha(self.key, nonce, counter=1).decrypt(ciphertext)
94