Package tlslite :: Package utils :: Module chacha
[hide private]
[frames] | no frames]

Source Code for Module tlslite.utils.chacha

  1  # Copyright (c) 2015, Hubert Kario 
  2  # 
  3  # See the LICENSE file for legal information regarding use of this file. 
  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 14 -class ChaCha(object):
15 16 """Pure python implementation of ChaCha cipher""" 17 18 constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] 19 20 @staticmethod
21 - def rotl32(v, c):
22 """Rotate left a 32 bit integer v by c bits""" 23 return ((v << c) & 0xffffffff) | (v >> (32 - c))
24 25 @staticmethod
26 - def quarter_round(x, a, b, c, d):
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
45 - def double_round(x):
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
57 - def chacha_block(key, counter, nonce, rounds):
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
75 - def word_to_bytearray(state):
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
83 - def _bytearray_to_words(data):
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 # convert bytearray key and nonce to little endian 32 bit unsigned ints 103 self.key = ChaCha._bytearray_to_words(key) 104 self.nonce = ChaCha._bytearray_to_words(nonce)
105
106 - def encrypt(self, plaintext):
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
125 - def decrypt(self, ciphertext):
126 """Decrypt the data""" 127 return self.encrypt(ciphertext)
128