1
2
3
4 """Pure Python implementation of ChaCha cipher
5
6 Implementation that follows RFC 7539 closely.
7 """
8
9 from __future__ import division
10 from .compat import compat26Str
11 import copy
12 import struct
13 try:
14
15 from itertools import izip
16 except ImportError:
17 izip = zip
20
21 """Pure python implementation of ChaCha cipher"""
22
23 constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
24
25 @staticmethod
27 """Rotate left a 32 bit integer v by c bits"""
28 return ((v << c) & 0xffffffff) | (v >> (32 - c))
29
30 @staticmethod
32 """Perform a ChaCha quarter round"""
33 xa = x[a]
34 xb = x[b]
35 xc = x[c]
36 xd = x[d]
37
38 xa = (xa + xb) & 0xffffffff
39 xd = xd ^ xa
40 xd = ((xd << 16) & 0xffffffff | (xd >> 16))
41
42 xc = (xc + xd) & 0xffffffff
43 xb = xb ^ xc
44 xb = ((xb << 12) & 0xffffffff | (xb >> 20))
45
46 xa = (xa + xb) & 0xffffffff
47 xd = xd ^ xa
48 xd = ((xd << 8) & 0xffffffff | (xd >> 24))
49
50 xc = (xc + xd) & 0xffffffff
51 xb = xb ^ xc
52 xb = ((xb << 7) & 0xffffffff | (xb >> 25))
53
54 x[a] = xa
55 x[b] = xb
56 x[c] = xc
57 x[d] = xd
58
59 _round_mixup_box = [(0, 4, 8, 12),
60 (1, 5, 9, 13),
61 (2, 6, 10, 14),
62 (3, 7, 11, 15),
63 (0, 5, 10, 15),
64 (1, 6, 11, 12),
65 (2, 7, 8, 13),
66 (3, 4, 9, 14)]
67
68 @classmethod
70 """Perform two rounds of ChaCha cipher"""
71 for a, b, c, d in cls._round_mixup_box:
72 xa = x[a]
73 xb = x[b]
74 xc = x[c]
75 xd = x[d]
76
77 xa = (xa + xb) & 0xffffffff
78 xd = xd ^ xa
79 xd = ((xd << 16) & 0xffffffff | (xd >> 16))
80
81 xc = (xc + xd) & 0xffffffff
82 xb = xb ^ xc
83 xb = ((xb << 12) & 0xffffffff | (xb >> 20))
84
85 xa = (xa + xb) & 0xffffffff
86 xd = xd ^ xa
87 xd = ((xd << 8) & 0xffffffff | (xd >> 24))
88
89 xc = (xc + xd) & 0xffffffff
90 xb = xb ^ xc
91 xb = ((xb << 7) & 0xffffffff | (xb >> 25))
92
93 x[a] = xa
94 x[b] = xb
95 x[c] = xc
96 x[d] = xd
97
98 @staticmethod
100 """Generate a state of a single block"""
101 state = ChaCha.constants + key + [counter] + nonce
102
103 working_state = state[:]
104 dbl_round = ChaCha.double_round
105 for _ in range(0, rounds // 2):
106 dbl_round(working_state)
107
108 return [(st + wrkSt) & 0xffffffff for st, wrkSt
109 in izip(state, working_state)]
110
111 @staticmethod
113 """Convert state to little endian bytestream"""
114 return bytearray(struct.pack('<LLLLLLLLLLLLLLLL', *state))
115
116 @staticmethod
118 """Convert a bytearray to array of word sized ints"""
119 ret = []
120 for i in range(0, len(data)//4):
121 ret.extend(struct.unpack('<L',
122 compat26Str(data[i*4:(i+1)*4])))
123 return ret
124
125 - def __init__(self, key, nonce, counter=0, rounds=20):
126 """Set the initial state for the ChaCha cipher"""
127 if len(key) != 32:
128 raise ValueError("Key must be 256 bit long")
129 if len(nonce) != 12:
130 raise ValueError("Nonce must be 96 bit long")
131 self.key = []
132 self.nonce = []
133 self.counter = counter
134 self.rounds = rounds
135
136
137 self.key = ChaCha._bytearray_to_words(key)
138 self.nonce = ChaCha._bytearray_to_words(nonce)
139
141 """Encrypt the data"""
142 encrypted_message = bytearray()
143 for i, block in enumerate(plaintext[i:i+64] for i
144 in range(0, len(plaintext), 64)):
145 key_stream = ChaCha.chacha_block(self.key,
146 self.counter + i,
147 self.nonce,
148 self.rounds)
149 key_stream = ChaCha.word_to_bytearray(key_stream)
150 encrypted_message += bytearray(x ^ y for x, y
151 in izip(key_stream, block))
152
153 return encrypted_message
154
156 """Decrypt the data"""
157 return self.encrypt(ciphertext)
158