Skip to content

Commit c28d6d3

Browse files
authored
Merge pull request #439 from tlsfuzzer/bleichenbacher-fixes-0.7
Bleichenbacher fixes [0.7]
2 parents acdde31 + 2738f15 commit c28d6d3

9 files changed

+1621
-129
lines changed

tlslite/utils/compat.py

+14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import sys
77
import os
8+
import re
89
import platform
910
import math
1011
import binascii
@@ -68,6 +69,10 @@ def formatExceptionTrace(e):
6869
"""Return exception information formatted as string"""
6970
return str(e)
7071

72+
def remove_whitespace(text):
73+
"""Removes all whitespace from passed in string"""
74+
return re.sub(r"\s+", "", text, flags=re.UNICODE)
75+
7176
else:
7277
# Python 2.6 requires strings instead of bytearrays in a couple places,
7378
# so we define this function so it does the conversion if needed.
@@ -76,9 +81,18 @@ def formatExceptionTrace(e):
7681
if sys.version_info < (2, 7) or sys.version_info < (2, 7, 4) \
7782
or platform.system() == 'Java':
7883
def compat26Str(x): return str(x)
84+
85+
def remove_whitespace(text):
86+
"""Removes all whitespace from passed in string"""
87+
return re.sub(r"\s+", "", text)
88+
7989
else:
8090
def compat26Str(x): return x
8191

92+
def remove_whitespace(text):
93+
"""Removes all whitespace from passed in string"""
94+
return re.sub(r"\s+", "", text, flags=re.UNICODE)
95+
8296
def compatAscii2Bytes(val):
8397
"""Convert ASCII string to bytes."""
8498
return val

tlslite/utils/constanttime.py

+16-1
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

+9-7
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,23 @@ def parsePEMKey(s, private=False, public=False, passwordCallback=None,
107107

108108

109109
def _parseKeyHelper(key, private, public):
110-
if private:
111-
if not key.hasPrivateKey():
112-
raise SyntaxError("Not a private key!")
110+
if private and not key.hasPrivateKey():
111+
raise SyntaxError("Not a private key!")
113112

114113
if public:
115114
return _createPublicKey(key)
116115

117116
if private:
118-
if hasattr(key, "d"):
119-
return _createPrivateKey(key)
120-
else:
117+
if cryptomath.m2cryptoLoaded:
118+
if type(key) == Python_RSAKey:
119+
return _createPrivateKey(key)
120+
assert type(key) in (OpenSSL_RSAKey, ), type(key)
121121
return key
122-
122+
elif hasattr(key, "d"):
123+
return _createPrivateKey(key)
123124
return key
124125

126+
125127
def parseAsPublicKey(s):
126128
"""Parse a PEM-formatted public key.
127129

tlslite/utils/openssl_rsakey.py

+16
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
@@ -65,12 +66,20 @@ def _rawPrivateKeyOp(self, m):
6566
c = bytesToNumber(bytearray(s))
6667
return c
6768

69+
def _raw_private_key_op_bytes(self, message):
70+
return bytearray(m2.rsa_private_encrypt(self.rsa, bytes(message),
71+
m2.no_padding))
72+
6873
def _rawPublicKeyOp(self, c):
6974
b = numberToByteArray(c, numBytes(self.n))
7075
s = m2.rsa_public_decrypt(self.rsa, bytes(b), m2.no_padding)
7176
m = bytesToNumber(bytearray(s))
7277
return m
7378

79+
def _raw_public_key_op_bytes(self, ciphertext):
80+
return bytearray(m2.rsa_public_decrypt(self.rsa, bytes(ciphertext),
81+
m2.no_padding))
82+
7483
def acceptsPassword(self): return True
7584

7685
def write(self, password=None):
@@ -146,6 +155,13 @@ def f():pass
146155
key._hasPrivateKey = False
147156
else:
148157
raise SyntaxError()
158+
if key._hasPrivateKey:
159+
if sys.version_info < (3, 0):
160+
b64_key = str(key.write())
161+
else:
162+
b64_key = str(key.write(), "ascii")
163+
py_key = Python_RSAKey.parsePEM(b64_key)
164+
key.d = py_key.d
149165
return key
150166
finally:
151167
m2.bio_free(bio)

tlslite/utils/python_key.py

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
2+
3+
from .python_rsakey import Python_RSAKey
4+
from .pem import dePem, pemSniff
5+
from .asn1parser import ASN1Parser
6+
from .cryptomath import bytesToNumber
7+
8+
9+
class Python_Key(object):
10+
"""
11+
Generic methods for parsing private keys from files.
12+
13+
Handles both RSA and ECDSA keys, irrespective of file format.
14+
"""
15+
16+
@staticmethod
17+
def parsePEM(s, passwordCallback=None):
18+
"""Parse a string containing a PEM-encoded <privateKey>."""
19+
20+
if pemSniff(s, "PRIVATE KEY"):
21+
bytes = dePem(s, "PRIVATE KEY")
22+
return Python_Key._parse_pkcs8(bytes)
23+
elif pemSniff(s, "RSA PRIVATE KEY"):
24+
bytes = dePem(s, "RSA PRIVATE KEY")
25+
return Python_Key._parse_ssleay(bytes)
26+
elif pemSniff(s, "DSA PRIVATE KEY"):
27+
raise SyntaxError("DSA private key files unsupported")
28+
elif pemSniff(s, "EC PRIVATE KEY"):
29+
raise SyntaxError("ECDSA private key files unsupported")
30+
elif pemSniff(s, "PUBLIC KEY"):
31+
bytes = dePem(s, "PUBLIC KEY")
32+
return Python_Key._parse_public_key(bytes)
33+
else:
34+
raise SyntaxError("Not a PEM private key file")
35+
36+
@staticmethod
37+
def _parse_public_key(bytes):
38+
# public keys are encoded as the subject_public_key_info objects
39+
spk_info = ASN1Parser(bytes)
40+
41+
# first element of the SEQUENCE is the AlgorithmIdentifier
42+
alg_id = spk_info.getChild(0)
43+
44+
# AlgId has two elements, the OID of the algorithm and parameters
45+
# parameters generally have to be NULL, with exception of RSA-PSS
46+
47+
alg_oid = alg_id.getChild(0)
48+
49+
if list(alg_oid.value) != [42, 134, 72, 134, 247, 13, 1, 1, 1]:
50+
raise SyntaxError("Only RSA Public keys supported")
51+
52+
subject_public_key = ASN1Parser(
53+
ASN1Parser(spk_info.getChildBytes(1)).value[1:])
54+
55+
modulus = subject_public_key.getChild(0)
56+
exponent = subject_public_key.getChild(1)
57+
58+
n = bytesToNumber(modulus.value)
59+
e = bytesToNumber(exponent.value)
60+
61+
return Python_RSAKey(n, e)
62+
63+
@staticmethod
64+
def _parse_pkcs8(bytes):
65+
parser = ASN1Parser(bytes)
66+
67+
# first element in PrivateKeyInfo is an INTEGER
68+
version = parser.getChild(0).value
69+
if bytesToNumber(version) != 0:
70+
raise SyntaxError("Unrecognized PKCS8 version")
71+
72+
# second element in PrivateKeyInfo is a SEQUENCE of type
73+
# AlgorithmIdentifier
74+
alg_ident = parser.getChild(1)
75+
seq_len = alg_ident.getChildCount()
76+
# first item of AlgorithmIdentifier is an OBJECT (OID)
77+
oid = alg_ident.getChild(0)
78+
if list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 1]:
79+
key_type = "rsa"
80+
elif list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 10]:
81+
key_type = "rsa-pss"
82+
else:
83+
raise SyntaxError("Unrecognized AlgorithmIdentifier: {0}"
84+
.format(list(oid.value)))
85+
# second item of AlgorithmIdentifier are parameters (defined by
86+
# above algorithm)
87+
if key_type == "rsa":
88+
if seq_len != 2:
89+
raise SyntaxError("Missing parameters for RSA algorithm ID")
90+
parameters = alg_ident.getChild(1)
91+
if parameters.value != bytearray(0):
92+
raise SyntaxError("RSA parameters are not NULL")
93+
else: # rsa-pss
94+
pass # ignore parameters - don't apply restrictions
95+
96+
if seq_len > 2:
97+
raise SyntaxError("Invalid encoding of AlgorithmIdentifier")
98+
99+
#Get the privateKey
100+
private_key_parser = parser.getChild(2)
101+
102+
#Adjust for OCTET STRING encapsulation
103+
private_key_parser = ASN1Parser(private_key_parser.value)
104+
105+
return Python_Key._parse_asn1_private_key(private_key_parser)
106+
107+
@staticmethod
108+
def _parse_ssleay(data):
109+
"""
110+
Parse binary structure of the old SSLeay file format used by OpenSSL.
111+
112+
For RSA keys.
113+
"""
114+
private_key_parser = ASN1Parser(data)
115+
return Python_Key._parse_asn1_private_key(private_key_parser)
116+
117+
@staticmethod
118+
def _parse_asn1_private_key(private_key_parser):
119+
version = private_key_parser.getChild(0).value[0]
120+
if version != 0:
121+
raise SyntaxError("Unrecognized RSAPrivateKey version")
122+
n = bytesToNumber(private_key_parser.getChild(1).value)
123+
e = bytesToNumber(private_key_parser.getChild(2).value)
124+
d = bytesToNumber(private_key_parser.getChild(3).value)
125+
p = bytesToNumber(private_key_parser.getChild(4).value)
126+
q = bytesToNumber(private_key_parser.getChild(5).value)
127+
dP = bytesToNumber(private_key_parser.getChild(6).value)
128+
dQ = bytesToNumber(private_key_parser.getChild(7).value)
129+
qInv = bytesToNumber(private_key_parser.getChild(8).value)
130+
return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)

tlslite/utils/python_rsakey.py

+3-74
Original file line numberDiff line numberDiff line change
@@ -86,79 +86,8 @@ def generate(bits):
8686
return key
8787
generate = staticmethod(generate)
8888

89+
@staticmethod
8990
def parsePEM(s, passwordCallback=None):
9091
"""Parse a string containing a PEM-encoded <privateKey>."""
91-
92-
if pemSniff(s, "PRIVATE KEY"):
93-
bytes = dePem(s, "PRIVATE KEY")
94-
return Python_RSAKey._parsePKCS8(bytes)
95-
elif pemSniff(s, "RSA PRIVATE KEY"):
96-
bytes = dePem(s, "RSA PRIVATE KEY")
97-
return Python_RSAKey._parseSSLeay(bytes)
98-
else:
99-
raise SyntaxError("Not a PEM private key file")
100-
parsePEM = staticmethod(parsePEM)
101-
102-
def _parsePKCS8(bytes):
103-
p = ASN1Parser(bytes)
104-
105-
# first element in PrivateKeyInfo is an INTEGER
106-
version = p.getChild(0).value
107-
if bytesToNumber(version) != 0:
108-
raise SyntaxError("Unrecognized PKCS8 version")
109-
110-
# second element in PrivateKeyInfo is a SEQUENCE of type
111-
# AlgorithmIdentifier
112-
algIdent = p.getChild(1)
113-
seqLen = algIdent.getChildCount()
114-
# first item of AlgorithmIdentifier is an OBJECT (OID)
115-
oid = algIdent.getChild(0)
116-
if list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 1]:
117-
keyType = "rsa"
118-
elif list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 10]:
119-
keyType = "rsa-pss"
120-
else:
121-
raise SyntaxError("Unrecognized AlgorithmIdentifier: {0}"
122-
.format(list(oid.value)))
123-
# second item of AlgorithmIdentifier are parameters (defined by
124-
# above algorithm)
125-
if keyType == "rsa":
126-
if seqLen != 2:
127-
raise SyntaxError("Missing parameters for RSA algorithm ID")
128-
parameters = algIdent.getChild(1)
129-
if parameters.value != bytearray(0):
130-
raise SyntaxError("RSA parameters are not NULL")
131-
else: # rsa-pss
132-
pass # ignore parameters - don't apply restrictions
133-
134-
if seqLen > 2:
135-
raise SyntaxError("Invalid encoding of AlgorithmIdentifier")
136-
137-
#Get the privateKey
138-
privateKeyP = p.getChild(2)
139-
140-
#Adjust for OCTET STRING encapsulation
141-
privateKeyP = ASN1Parser(privateKeyP.value)
142-
143-
return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
144-
_parsePKCS8 = staticmethod(_parsePKCS8)
145-
146-
def _parseSSLeay(bytes):
147-
privateKeyP = ASN1Parser(bytes)
148-
return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
149-
_parseSSLeay = staticmethod(_parseSSLeay)
150-
151-
def _parseASN1PrivateKey(privateKeyP):
152-
version = privateKeyP.getChild(0).value[0]
153-
if version != 0:
154-
raise SyntaxError("Unrecognized RSAPrivateKey version")
155-
n = bytesToNumber(privateKeyP.getChild(1).value)
156-
e = bytesToNumber(privateKeyP.getChild(2).value)
157-
d = bytesToNumber(privateKeyP.getChild(3).value)
158-
p = bytesToNumber(privateKeyP.getChild(4).value)
159-
q = bytesToNumber(privateKeyP.getChild(5).value)
160-
dP = bytesToNumber(privateKeyP.getChild(6).value)
161-
dQ = bytesToNumber(privateKeyP.getChild(7).value)
162-
qInv = bytesToNumber(privateKeyP.getChild(8).value)
163-
return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
164-
_parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey)
92+
from .python_key import Python_Key
93+
return Python_Key.parsePEM(s, passwordCallback)

0 commit comments

Comments
 (0)