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  try: 
 14      # in Python 3 the native zip returns iterator 
 15      from itertools import izip 
 16  except ImportError: 
 17      izip = zip 
18 19 -class ChaCha(object):
20 21 """Pure python implementation of ChaCha cipher""" 22 23 constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] 24 25 @staticmethod
26 - def rotl32(v, c):
27 """Rotate left a 32 bit integer v by c bits""" 28 return ((v << c) & 0xffffffff) | (v >> (32 - c))
29 30 @staticmethod
31 - def quarter_round(x, a, b, c, d):
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
69 - def double_round(cls, x):
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
99 - def chacha_block(key, counter, nonce, rounds):
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
112 - def word_to_bytearray(state):
113 """Convert state to little endian bytestream""" 114 return bytearray(struct.pack('<LLLLLLLLLLLLLLLL', *state))
115 116 @staticmethod
117 - def _bytearray_to_words(data):
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 # convert bytearray key and nonce to little endian 32 bit unsigned ints 137 self.key = ChaCha._bytearray_to_words(key) 138 self.nonce = ChaCha._bytearray_to_words(nonce)
139
140 - def encrypt(self, plaintext):
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
155 - def decrypt(self, ciphertext):
156 """Decrypt the data""" 157 return self.encrypt(ciphertext)
158