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
15
16 """Pure python implementation of ChaCha cipher"""
17
18 constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
19
20 @staticmethod
22 """Rotate left a 32 bit integer v by c bits"""
23 return ((v << c) & 0xffffffff) | (v >> (32 - c))
24
25 @staticmethod
27 """Perform a ChaCha quarter round"""
28 x[a] = (x[a] + x[b]) & 0xffffffff
29 x[d] = x[d] ^ x[a]
30 x[d] = ChaCha.rotl32(x[d], 16)
31
32 x[c] = (x[c] + x[d]) & 0xffffffff
33 x[b] = x[b] ^ x[c]
34 x[b] = ChaCha.rotl32(x[b], 12)
35
36 x[a] = (x[a] + x[b]) & 0xffffffff
37 x[d] = x[d] ^ x[a]
38 x[d] = ChaCha.rotl32(x[d], 8)
39
40 x[c] = (x[c] + x[d]) & 0xffffffff
41 x[b] = x[b] ^ x[c]
42 x[b] = ChaCha.rotl32(x[b], 7)
43
44 @staticmethod
46 """Perform two rounds of ChaCha cipher"""
47 ChaCha.quarter_round(x, 0, 4, 8, 12)
48 ChaCha.quarter_round(x, 1, 5, 9, 13)
49 ChaCha.quarter_round(x, 2, 6, 10, 14)
50 ChaCha.quarter_round(x, 3, 7, 11, 15)
51 ChaCha.quarter_round(x, 0, 5, 10, 15)
52 ChaCha.quarter_round(x, 1, 6, 11, 12)
53 ChaCha.quarter_round(x, 2, 7, 8, 13)
54 ChaCha.quarter_round(x, 3, 4, 9, 14)
55
56 @staticmethod
58 """Generate a state of a single block"""
59 state = []
60 state.extend(ChaCha.constants)
61 state.extend(key)
62 state.append(counter)
63 state.extend(nonce)
64
65 working_state = copy.copy(state)
66 for i in range(0, rounds // 2):
67 ChaCha.double_round(working_state)
68
69 for i, _ in enumerate(working_state):
70 state[i] = (state[i] + working_state[i]) & 0xffffffff
71
72 return state
73
74 @staticmethod
76 """Convert state to little endian bytestream"""
77 ret = bytearray()
78 for i in state:
79 ret += struct.pack('<L', i)
80 return ret
81
82 @staticmethod
84 """Convert a bytearray to array of word sized ints"""
85 ret = []
86 for i in range(0, len(data)//4):
87 ret.extend(struct.unpack('<L',
88 compat26Str(data[i*4:(i+1)*4])))
89 return ret
90
91 - def __init__(self, key, nonce, counter=0, rounds=20):
92 """Set the initial state for the ChaCha cipher"""
93 if len(key) != 32:
94 raise ValueError("Key must be 256 bit long")
95 if len(nonce) != 12:
96 raise ValueError("Nonce must be 96 bit long")
97 self.key = []
98 self.nonce = []
99 self.counter = counter
100 self.rounds = rounds
101
102
103 self.key = ChaCha._bytearray_to_words(key)
104 self.nonce = ChaCha._bytearray_to_words(nonce)
105
107 """Encrypt the data"""
108 encrypted_message = bytearray()
109 if len(plaintext) % 64 != 0:
110 extra = 1
111 else:
112 extra = 0
113 for i in range(0, len(plaintext) // 64 + extra):
114 key_stream = ChaCha.chacha_block(self.key,
115 self.counter + i,
116 self.nonce,
117 self.rounds)
118 key_stream = ChaCha.word_to_bytearray(key_stream)
119 block = plaintext[i*64:(i+1)*64]
120 encrypted_message += bytearray((x ^ y for x, y \
121 in zip(key_stream, block)))
122
123 return encrypted_message
124
126 """Decrypt the data"""
127 return self.encrypt(ciphertext)
128