Skip to content

Commit a6cbf7e

Browse files
authored
Merge pull request #438 from tlsfuzzer/bleichenbacher-fixes
Attempt side-channel free RSA decryption
2 parents 0812ed6 + 44d6fd4 commit a6cbf7e

File tree

7 files changed

+1504
-55
lines changed

7 files changed

+1504
-55
lines changed

tlslite/utils/constanttime.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def ct_lt_u32(val_a, val_b):
2323

2424
return (val_a^((val_a^val_b)|(((val_a-val_b)&0xffffffff)^val_b)))>>31
2525

26+
2627
def ct_gt_u32(val_a, val_b):
2728
"""
2829
Return 1 if val_a > val_b, 0 otherwise. Constant time.
@@ -35,6 +36,7 @@ def ct_gt_u32(val_a, val_b):
3536
"""
3637
return ct_lt_u32(val_b, val_a)
3738

39+
3840
def ct_le_u32(val_a, val_b):
3941
"""
4042
Return 1 if val_a <= val_b, 0 otherwise. Constant time.
@@ -47,14 +49,26 @@ def ct_le_u32(val_a, val_b):
4749
"""
4850
return 1 ^ ct_gt_u32(val_a, val_b)
4951

52+
5053
def ct_lsb_prop_u8(val):
51-
"""Propagate LSB to all 8 bits of the returned byte. Constant time."""
54+
"""Propagate LSB to all 8 bits of the returned int. Constant time."""
55+
val &= 0x01
56+
val |= val << 1
57+
val |= val << 2
58+
val |= val << 4
59+
return val
60+
61+
62+
def ct_lsb_prop_u16(val):
63+
"""Propagate LSB to all 16 bits of the returned int. Constant time."""
5264
val &= 0x01
5365
val |= val << 1
5466
val |= val << 2
5567
val |= val << 4
68+
val |= val << 8
5669
return val
5770

71+
5872
def ct_isnonzero_u32(val):
5973
"""
6074
Returns 1 if val is != 0, 0 otherwise. Constant time.
@@ -66,6 +80,7 @@ def ct_isnonzero_u32(val):
6680
val &= 0xffffffff
6781
return (val|(-val&0xffffffff)) >> 31
6882

83+
6984
def ct_neq_u32(val_a, val_b):
7085
"""
7186
Return 1 if val_a != val_b, 0 otherwise. Constant time.

tlslite/utils/keyfactory.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,23 @@ def parsePEMKey(s, private=False, public=False, passwordCallback=None,
121121

122122

123123
def _parseKeyHelper(key, private, public):
124-
if private:
125-
if not key.hasPrivateKey():
126-
raise SyntaxError("Not a private key!")
124+
if private and not key.hasPrivateKey():
125+
raise SyntaxError("Not a private key!")
127126

128127
if public:
129128
return _createPublicKey(key)
130129

131130
if private:
132-
if hasattr(key, "d"):
133-
return _createPrivateKey(key)
134-
else:
131+
if cryptomath.m2cryptoLoaded:
132+
if type(key) == Python_RSAKey:
133+
return _createPrivateKey(key)
134+
assert type(key) in (OpenSSL_RSAKey, Python_ECDSAKey), type(key)
135135
return key
136-
136+
elif hasattr(key, "d"):
137+
return _createPrivateKey(key)
137138
return key
138139

140+
139141
def parseAsPublicKey(s):
140142
"""Parse a PEM-formatted public key.
141143

tlslite/utils/openssl_rsakey.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .rsakey import *
99
from .python_rsakey import Python_RSAKey
1010
from .compat import compatAscii2Bytes
11+
import sys
1112

1213
#copied from M2Crypto.util.py, so when we load the local copy of m2
1314
#we can still use it
@@ -67,13 +68,21 @@ def _rawPrivateKeyOp(self, message):
6768
ciphertext = bytesToNumber(bytearray(string))
6869
return ciphertext
6970

71+
def _raw_private_key_op_bytes(self, message):
72+
return bytearray(m2.rsa_private_encrypt(self.rsa, bytes(message),
73+
m2.no_padding))
74+
7075
def _rawPublicKeyOp(self, ciphertext):
7176
data = numberToByteArray(ciphertext, numBytes(self.n))
7277
string = m2.rsa_public_decrypt(self.rsa, bytes(data),
7378
m2.no_padding)
7479
message = bytesToNumber(bytearray(string))
7580
return message
7681

82+
def _raw_public_key_op_bytes(self, ciphertext):
83+
return bytearray(m2.rsa_public_decrypt(self.rsa, bytes(ciphertext),
84+
m2.no_padding))
85+
7786
def acceptsPassword(self): return True
7887

7988
def write(self, password=None):
@@ -151,6 +160,13 @@ def f():pass
151160
key._hasPrivateKey = False
152161
else:
153162
raise SyntaxError()
163+
if key._hasPrivateKey:
164+
if sys.version_info < (3, 0):
165+
b64_key = str(key.write())
166+
else:
167+
b64_key = str(key.write(), "ascii")
168+
py_key = Python_RSAKey.parsePEM(b64_key)
169+
key.d = py_key.d
154170
return key
155171
finally:
156172
m2.bio_free(bio)

tlslite/utils/python_key.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,39 @@ def parsePEM(s, passwordCallback=None):
3333
elif pemSniff(s, "EC PRIVATE KEY"):
3434
bytes = dePem(s, "EC PRIVATE KEY")
3535
return Python_Key._parse_ecc_ssleay(bytes)
36+
elif pemSniff(s, "PUBLIC KEY"):
37+
bytes = dePem(s, "PUBLIC KEY")
38+
return Python_Key._parse_public_key(bytes)
3639
else:
3740
raise SyntaxError("Not a PEM private key file")
3841

42+
@staticmethod
43+
def _parse_public_key(bytes):
44+
# public keys are encoded as the subject_public_key_info objects
45+
spk_info = ASN1Parser(bytes)
46+
47+
# first element of the SEQUENCE is the AlgorithmIdentifier
48+
alg_id = spk_info.getChild(0)
49+
50+
# AlgId has two elements, the OID of the algorithm and parameters
51+
# parameters generally have to be NULL, with exception of RSA-PSS
52+
53+
alg_oid = alg_id.getChild(0)
54+
55+
if list(alg_oid.value) != [42, 134, 72, 134, 247, 13, 1, 1, 1]:
56+
raise SyntaxError("Only RSA Public keys supported")
57+
58+
subject_public_key = ASN1Parser(
59+
ASN1Parser(spk_info.getChildBytes(1)).value[1:])
60+
61+
modulus = subject_public_key.getChild(0)
62+
exponent = subject_public_key.getChild(1)
63+
64+
n = bytesToNumber(modulus.value)
65+
e = bytesToNumber(exponent.value)
66+
67+
return Python_RSAKey(n, e, key_type="rsa")
68+
3969
@staticmethod
4070
def _parse_pkcs8(bytes):
4171
parser = ASN1Parser(bytes)

0 commit comments

Comments
 (0)