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

Source Code for Module tlslite.utils.constanttime

  1  # Copyright (c) 2015, Hubert Kario 
  2  # 
  3  # See the LICENSE file for legal information regarding use of this file. 
  4  """Various constant time functions for processing sensitive data""" 
  5   
  6  from __future__ import division 
  7   
  8  from .compat import compatHMAC 
  9  import hmac 
 10   
11 -def ct_lt_u32(val_a, val_b):
12 """ 13 Returns 1 if val_a < val_b, 0 otherwise. Constant time. 14 15 @type val_a: int 16 @type val_b: int 17 @param val_a: an unsigned integer representable as a 32 bit value 18 @param val_b: an unsigned integer representable as a 32 bit value 19 @rtype: int 20 """ 21 val_a &= 0xffffffff 22 val_b &= 0xffffffff 23 24 return (val_a^((val_a^val_b)|(((val_a-val_b)&0xffffffff)^val_b)))>>31
25
26 -def ct_gt_u32(val_a, val_b):
27 """ 28 Return 1 if val_a > val_b, 0 otherwise. Constant time. 29 30 @type val_a: int 31 @type val_b: int 32 @param val_a: an unsigned integer representable as a 32 bit value 33 @param val_b: an unsigned integer representable as a 32 bit value 34 @rtype: int 35 """ 36 return ct_lt_u32(val_b, val_a)
37
38 -def ct_le_u32(val_a, val_b):
39 """ 40 Return 1 if val_a <= val_b, 0 otherwise. Constant time. 41 42 @type val_a: int 43 @type val_b: int 44 @param val_a: an unsigned integer representable as a 32 bit value 45 @param val_b: an unsigned integer representable as a 32 bit value 46 @rtype: int 47 """ 48 return 1 ^ ct_gt_u32(val_a, val_b)
49
50 -def ct_lsb_prop_u8(val):
51 """Propagate LSB to all 8 bits of the returned byte. Constant time.""" 52 val &= 0x01 53 val |= val << 1 54 val |= val << 2 55 val |= val << 4 56 return val
57
58 -def ct_isnonzero_u32(val):
59 """ 60 Returns 1 if val is != 0, 0 otherwise. Constant time. 61 62 @type val: int 63 @param val: an unsigned integer representable as a 32 bit value 64 @rtype: int 65 """ 66 val &= 0xffffffff 67 return (val|(-val&0xffffffff)) >> 31
68
69 -def ct_neq_u32(val_a, val_b):
70 """ 71 Return 1 if val_a != val_b, 0 otherwise. Constant time. 72 73 @type val_a: int 74 @type val_b: int 75 @param val_a: an unsigned integer representable as a 32 bit value 76 @param val_b: an unsigned integer representable as a 32 bit value 77 @rtype: int 78 """ 79 val_a &= 0xffffffff 80 val_b &= 0xffffffff 81 82 return (((val_a-val_b)&0xffffffff) | ((val_b-val_a)&0xffffffff)) >> 31
83
84 -def ct_eq_u32(val_a, val_b):
85 """ 86 Return 1 if val_a == val_b, 0 otherwise. Constant time. 87 88 @type val_a: int 89 @type val_b: int 90 @param val_a: an unsigned integer representable as a 32 bit value 91 @param val_b: an unsigned integer representable as a 32 bit value 92 @rtype: int 93 """ 94 return 1 ^ ct_neq_u32(val_a, val_b)
95
96 -def ct_check_cbc_mac_and_pad(data, mac, seqnumBytes, contentType, version):
97 """ 98 Check CBC cipher HMAC and padding. Close to constant time. 99 100 @type data: bytearray 101 @param data: data with HMAC value to test and padding 102 103 @type mac: hashlib mac 104 @param mac: empty HMAC, initialised with a key 105 106 @type seqnumBytes: bytearray 107 @param seqnumBytes: TLS sequence number, used as input to HMAC 108 109 @type contentType: int 110 @param contentType: a single byte, used as input to HMAC 111 112 @type version: tuple of int 113 @param version: a tuple of two ints, used as input to HMAC and to guide 114 checking of padding 115 116 @rtype: boolean 117 @return: True if MAC and pad is ok, False otherwise 118 """ 119 assert version in ((3, 0), (3, 1), (3, 2), (3, 3)) 120 121 data_len = len(data) 122 if mac.digest_size + 1 > data_len: # data_len is public 123 return False 124 125 # 0 - OK 126 result = 0x00 127 128 # 129 # check padding 130 # 131 pad_length = data[data_len-1] 132 pad_start = data_len - pad_length - 1 133 pad_start = max(0, pad_start) 134 135 if version == (3, 0): # version is public 136 # in SSLv3 we can only check if pad is not longer than overall length 137 138 # subtract 1 for the pad length byte 139 mask = ct_lsb_prop_u8(ct_lt_u32(data_len-1, pad_length)) 140 result |= mask 141 else: 142 start_pos = max(0, data_len - 256) 143 for i in range(start_pos, data_len): 144 # if pad_start < i: mask = 0xff; else: mask = 0x00 145 mask = ct_lsb_prop_u8(ct_le_u32(pad_start, i)) 146 # if data[i] != pad_length and "inside_pad": result = False 147 result |= (data[i] ^ pad_length) & mask 148 149 # 150 # check MAC 151 # 152 153 # real place where mac starts and data ends 154 mac_start = pad_start - mac.digest_size 155 mac_start = max(0, mac_start) 156 157 # place to start processing 158 start_pos = max(0, data_len - (256 + mac.digest_size)) // mac.block_size 159 start_pos *= mac.block_size 160 161 # add start data 162 data_mac = mac.copy() 163 data_mac.update(compatHMAC(seqnumBytes)) 164 data_mac.update(compatHMAC(bytearray([contentType]))) 165 if version != (3, 0): # version is public 166 data_mac.update(compatHMAC(bytearray([version[0]]))) 167 data_mac.update(compatHMAC(bytearray([version[1]]))) 168 data_mac.update(compatHMAC(bytearray([mac_start >> 8]))) 169 data_mac.update(compatHMAC(bytearray([mac_start & 0xff]))) 170 data_mac.update(compatHMAC(data[:start_pos])) 171 172 # don't check past the array end (already checked to be >= zero) 173 end_pos = data_len - 1 - mac.digest_size 174 175 # calculate all possible 176 for i in range(start_pos, end_pos): # constant for given overall length 177 cur_mac = data_mac.copy() 178 cur_mac.update(compatHMAC(data[start_pos:i])) 179 mac_compare = bytearray(cur_mac.digest()) 180 # compare the hash for real only if it's the place where mac is 181 # supposed to be 182 mask = ct_lsb_prop_u8(ct_eq_u32(i, mac_start)) 183 for j in range(0, mac.digest_size): # digest_size is public 184 result |= (data[i+j] ^ mac_compare[j]) & mask 185 186 # return python boolean 187 return result == 0
188 189 if hasattr(hmac, 'compare_digest'): 190 ct_compare_digest = hmac.compare_digest 191 else:
192 - def ct_compare_digest(val_a, val_b):
193 """Compares if string like objects are equal. Constant time.""" 194 if len(val_a) != len(val_b): 195 return False 196 197 result = 0 198 for x, y in zip(val_a, val_b): 199 result |= x ^ y 200 201 return result == 0
202