1
2
3
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
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
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
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
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
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
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
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
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:
123 return False
124
125
126 result = 0x00
127
128
129
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):
136
137
138
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
145 mask = ct_lsb_prop_u8(ct_le_u32(pad_start, i))
146
147 result |= (data[i] ^ pad_length) & mask
148
149
150
151
152
153
154 mac_start = pad_start - mac.digest_size
155 mac_start = max(0, mac_start)
156
157
158 start_pos = max(0, data_len - (256 + mac.digest_size)) // mac.block_size
159 start_pos *= mac.block_size
160
161
162 data_mac = mac.copy()
163 data_mac.update(compatHMAC(seqnumBytes))
164 data_mac.update(compatHMAC(bytearray([contentType])))
165 if version != (3, 0):
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
173 end_pos = data_len - 1 - mac.digest_size
174
175
176 for i in range(start_pos, end_pos):
177 cur_mac = data_mac.copy()
178 cur_mac.update(compatHMAC(data[start_pos:i]))
179 mac_compare = bytearray(cur_mac.digest())
180
181
182 mask = ct_lsb_prop_u8(ct_eq_u32(i, mac_start))
183 for j in range(0, mac.digest_size):
184 result |= (data[i+j] ^ mac_compare[j]) & mask
185
186
187 return result == 0
188
189 if hasattr(hmac, 'compare_digest'):
190 ct_compare_digest = hmac.compare_digest
191 else:
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