Skip to content

Commit f223419

Browse files
parsing of DSA keys from X.509 certificates
1 parent 1c68f2e commit f223419

File tree

11 files changed

+573
-11
lines changed

11 files changed

+573
-11
lines changed

tlslite/constants.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ class SignatureScheme(TLSEnum):
237237
rsa_pss_sha384 = (8, 5)
238238
rsa_pss_sha512 = (8, 6)
239239

240+
dsa_sha1 = (2, 2)
241+
dsa_sha224 = (3, 2)
242+
dsa_sha256 = (4, 2)
243+
dsa_sha384 = (5, 2)
244+
dsa_sha512 = (6, 2)
245+
240246
@classmethod
241247
def toRepr(cls, value, blacklist=None):
242248
"""Convert numeric type to name representation"""
@@ -337,6 +343,16 @@ class AlgorithmOID(TLSEnum):
337343
SignatureScheme.rsa_pss_rsae_sha384
338344
oid[bytes(a2b_hex('300b0609608648016503040203'))] = \
339345
SignatureScheme.rsa_pss_rsae_sha512
346+
oid[bytes(a2b_hex('06072A8648CE380403'))] = \
347+
SignatureScheme.dsa_sha1
348+
oid[bytes(a2b_hex('0609608648016503040301'))] = \
349+
SignatureScheme.dsa_sha224
350+
oid[bytes(a2b_hex('0609608648016503040302'))] = \
351+
SignatureScheme.dsa_sha256
352+
oid[bytes(a2b_hex('0609608648016503040303'))] = \
353+
SignatureScheme.dsa_sha384
354+
oid[bytes(a2b_hex('0609608648016503040304'))] = \
355+
SignatureScheme.dsa_sha512
340356

341357

342358
class GroupName(TLSEnum):

tlslite/utils/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"python_aes",
2323
"python_rc4",
2424
"python_rsakey",
25+
"python_dsakey",
2526
"rc4",
2627
"rijndael",
2728
"rsakey",

tlslite/utils/dsakey.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""Abstract class for DSA."""
2+
3+
class DSAKey(object):
4+
"""This is an abstract base class for DSA keys.
5+
6+
Particular implementations of DSA keys, such as
7+
:py:class:`~.python_dsakey.Python_DSAKey`
8+
... more coming
9+
inherit from this.
10+
11+
To create or parse an DSA key, don't use one of these classes
12+
directly. Instead, use the factory functions in
13+
:py:class:`~tlslite.utils.keyfactory`.
14+
"""
15+
16+
def __init__(self, p, q, g, x, y):
17+
"""Create a new DSA key.
18+
:type p: int
19+
:param p: domain parameter, prime num defining Gaolis Field
20+
:type q: int
21+
:param q: domain parameter, prime factor of p-1
22+
:type g: int
23+
:param g: domain parameter, generator of q-order cyclic group GP(p)
24+
:type x: int
25+
:param x: private key
26+
:type y: int
27+
:param y: public key
28+
"""
29+
raise NotImplementedError()
30+
31+
def __len__(self):
32+
"""Return the size of the order of the curve of this key, in bits.
33+
34+
:rtype: int
35+
"""
36+
raise NotImplementedError()
37+
38+
def hasPrivateKey(self):
39+
"""Return whether or not this key has a private component.
40+
41+
:rtype: bool
42+
"""
43+
raise NotImplementedError()
44+
45+
def hashAndSign(self, data, hAlg):
46+
"""Hash and sign the passed-in bytes.
47+
48+
This requires the key to have a private component and
49+
global parameters. It performs a signature on the passed-in data
50+
with selected hash algorithm.
51+
52+
:type data: str
53+
:param data: The data which will be hashed and signed.
54+
55+
:type hAlg: str
56+
:param hAlg: The hash algorithm that will be used to hash data
57+
58+
:rtype: bytearray
59+
:returns: An DSA signature on the passed-in data.
60+
"""
61+
raise NotImplementedError()
62+
63+
def hashAndVerify(self, signature, data, hAlg="sha1"):
64+
"""Hash and verify the passed-in bytes with signature.
65+
66+
:type signature: ASN1 bytearray
67+
:param signature: the r, s dsa signature
68+
69+
:type data: str
70+
:param data: The data which will be hashed and verified.
71+
72+
:type hAlg: str
73+
:param hAlg: The hash algorithm that will be used to hash data
74+
75+
:rtype: bool
76+
:returns: return True if verification is OK.
77+
"""
78+
raise NotImplementedError()
79+
80+
@staticmethod
81+
def generate(L, N):
82+
"""Generate new key given by bit lengths L, N.
83+
84+
:type L: int
85+
:param L: length of parameter p in bits
86+
87+
:type N: int
88+
:param N: length of parameter q in bits
89+
90+
:rtype: DSAkey
91+
:returns: DSAkey(domain parameters, private key, public key)
92+
"""
93+
raise NotImplementedError()
94+
95+
@staticmethod
96+
def generate_qp(L, N):
97+
"""Generate new (p, q) given by bit lengths L, N.
98+
99+
:type L: int
100+
:param L: length of parameter p in bits
101+
102+
:type N: int
103+
:param N: length of parameter q in bits
104+
105+
:rtype: (int, int)
106+
:returns: new p and q key parameters
107+
"""
108+
raise NotImplementedError()

tlslite/utils/keyfactory.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .rsakey import RSAKey
99
from .python_rsakey import Python_RSAKey
1010
from .python_ecdsakey import Python_ECDSAKey
11+
from .python_dsakey import Python_DSAKey
1112
from tlslite.utils import cryptomath
1213

1314
if cryptomath.m2cryptoLoaded:
@@ -233,3 +234,28 @@ def _create_public_ecdsa_key(point_x, point_y, curve_name,
233234
if impl == "python":
234235
return Python_ECDSAKey(point_x, point_y, curve_name)
235236
raise ValueError("No acceptable implementation")
237+
238+
def _create_public_dsa_key(p, q, g, y,
239+
implementations=("python",)):
240+
"""
241+
Convert public key parameters into concrete implementation of verifier.
242+
243+
The public key in DSA consists of four integers.
244+
245+
:type p: int
246+
:param p: domain parameter, prime num defining Gaolis Field
247+
:type q: int
248+
:param q: domain parameter, prime factor of p-1
249+
:type g: int
250+
:param g: domain parameter, generator of q-order cyclic group GP(p)
251+
:type y: int
252+
:param y: public key
253+
:type implementations: iterable of str
254+
:param implementations: list of implementations that can be used as the
255+
concrete implementation of the verifying key (only 'python' is
256+
supported currently)
257+
"""
258+
for impl in implementations:
259+
if impl == "python":
260+
return Python_DSAKey(p=p, q=q, g=g, y=y)
261+
raise ValueError("No acceptable implementation")

tlslite/utils/python_dsakey.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Author: Frantisek Krenzelok
2+
3+
"""Pure-Python RSA implementation."""
4+
from ecdsa.der import encode_sequence, encode_integer, \
5+
remove_sequence, remove_integer
6+
7+
from .cryptomath import getRandomNumber, getRandomPrime, \
8+
powMod, numBits, bytesToNumber, invMod, secureHash, \
9+
GMPY2_LOADED, gmpyLoaded
10+
11+
if GMPY2_LOADED:
12+
from gmpy2 import mpz
13+
elif gmpyLoaded:
14+
from gmpy import mpz
15+
16+
from .dsakey import DSAKey
17+
18+
class Python_DSAKey(DSAKey):
19+
"""
20+
Concrete implementaion of DSA object.
21+
for func docstring see tlslite/dsakey.py
22+
"""
23+
def __init__(self, p=0, q=0, g=0, x=0, y=0):
24+
if gmpyLoaded or GMPY2_LOADED:
25+
p = mpz(p)
26+
q = mpz(q)
27+
g = mpz(g)
28+
x = mpz(x)
29+
y = mpz(y)
30+
self.p = p
31+
self.q = q
32+
self.g = g
33+
self.private_key = x
34+
self.public_key = y
35+
self.key_type = "dsa"
36+
37+
if p and q and p < q:
38+
raise ValueError("q is greater than p")
39+
40+
def __len__(self):
41+
return numBits(self.p)
42+
43+
def hasPrivateKey(self):
44+
return bool(self.private_key)
45+
46+
@staticmethod
47+
def generate(L, N):
48+
assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]
49+
key = Python_DSAKey()
50+
(q, p) = Python_DSAKey.generate_qp(L, N)
51+
52+
index = getRandomNumber(1, (p-1))
53+
g = powMod(index, int((p-1)/q), p)
54+
x = getRandomNumber(1, q-1)
55+
y = powMod(g, x, p)
56+
if gmpyLoaded or GMPY2_LOADED:
57+
p = mpz(p)
58+
q = mpz(q)
59+
g = mpz(g)
60+
x = mpz(x)
61+
y = mpz(y)
62+
key.q = q
63+
key.p = p
64+
key.g = g
65+
key.private_key = x
66+
key.public_key = y
67+
return key
68+
69+
@staticmethod
70+
def generate_qp(L, N):
71+
assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]
72+
73+
q = int(getRandomPrime(N))
74+
while True:
75+
p = int(getRandomPrime(L))
76+
if (p-1) % q:
77+
break
78+
return (q, p)
79+
80+
def hashAndSign(self, data, hAlg="sha1"):
81+
digest = bytesToNumber(secureHash(bytearray(data), hAlg))
82+
digest_size = numBits(digest)
83+
84+
# extract min(|hAlg|, N) left bits of digest
85+
N = numBits(self.q)
86+
if N < digest_size:
87+
digest &= ~(~0 << (digest_size - N))
88+
89+
k = getRandomNumber(1, (self.q-1))
90+
r = powMod(self.g, k, self.p) % self.q
91+
s = invMod(k, self.q) * (digest + self.private_key * r) % self.q
92+
93+
return encode_sequence(encode_integer(r), encode_integer(s))
94+
95+
def hashAndVerify(self, signature, data, hAlg="sha1"):
96+
# Get r, s components from signature
97+
digest = bytesToNumber(secureHash(bytearray(data), hAlg))
98+
digest_size = numBits(digest)
99+
100+
# extract min(|hAlg|, N) left bits of digest
101+
N = numBits(self.q)
102+
if N < digest_size:
103+
digest &= ~(~0 << (digest_size - N))
104+
105+
# get r, s keys
106+
if not signature:
107+
return False
108+
body, rest = remove_sequence(signature)
109+
if rest:
110+
return False
111+
r, rest = remove_integer(body)
112+
s, rest = remove_integer(rest)
113+
if rest:
114+
return False
115+
116+
# check the signature
117+
if 0 < r < self.q and 0 < s < self.q:
118+
w = invMod(s, self.q)
119+
u1 = (digest * w) % self.q
120+
u2 = (r * w) % self.q
121+
v = ((powMod(self.g, u1, self.p) * \
122+
powMod(self.public_key, u2, self.p)) % self.p) % self.q
123+
124+
return r == v
125+
return False

0 commit comments

Comments
 (0)