135 lines
3.7 KiB
Python
135 lines
3.7 KiB
Python
# Author: Frantisek Krenzelok
|
|
"""Pure-Python RSA implementation."""
|
|
from ecdsa.der import encode_sequence, encode_integer, \
|
|
remove_sequence, remove_integer
|
|
|
|
from .cryptomath import getRandomNumber, getRandomPrime, \
|
|
powMod, numBits, bytesToNumber, invMod, \
|
|
secureHash, GMPY2_LOADED, gmpyLoaded
|
|
|
|
from .compat import compatHMAC
|
|
|
|
if GMPY2_LOADED:
|
|
from gmpy2 import mpz
|
|
elif gmpyLoaded:
|
|
from gmpy import mpz
|
|
|
|
from .dsakey import DSAKey
|
|
|
|
class Python_DSAKey(DSAKey):
|
|
"""
|
|
Concrete implementaion of DSA object.
|
|
for func docstring see tlslite/dsakey.py
|
|
"""
|
|
def __init__(self, p=0, q=0, g=0, x=0, y=0):
|
|
if gmpyLoaded or GMPY2_LOADED:
|
|
p = mpz(p)
|
|
q = mpz(q)
|
|
g = mpz(g)
|
|
x = mpz(x)
|
|
y = mpz(y)
|
|
self.p = p
|
|
self.q = q
|
|
self.g = g
|
|
self.private_key = x
|
|
self.public_key = y
|
|
self.key_type = "dsa"
|
|
|
|
if p and q and p < q:
|
|
raise ValueError("q is greater than p")
|
|
|
|
def __len__(self):
|
|
return numBits(self.p)
|
|
|
|
def hasPrivateKey(self):
|
|
return bool(self.private_key)
|
|
|
|
@staticmethod
|
|
def generate(L, N):
|
|
assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]
|
|
key = Python_DSAKey()
|
|
(q, p) = Python_DSAKey.generate_qp(L, N)
|
|
|
|
index = getRandomNumber(1, (p-1))
|
|
g = powMod(index, int((p-1)/q), p)
|
|
x = getRandomNumber(1, q-1)
|
|
y = powMod(g, x, p)
|
|
if gmpyLoaded or GMPY2_LOADED:
|
|
p = mpz(p)
|
|
q = mpz(q)
|
|
g = mpz(g)
|
|
x = mpz(x)
|
|
y = mpz(y)
|
|
key.q = q
|
|
key.p = p
|
|
key.g = g
|
|
key.private_key = x
|
|
key.public_key = y
|
|
return key
|
|
|
|
@staticmethod
|
|
def generate_qp(L, N):
|
|
assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]
|
|
|
|
q = int(getRandomPrime(N))
|
|
while True:
|
|
p = int(getRandomPrime(L))
|
|
if (p-1) % q:
|
|
break
|
|
return (q, p)
|
|
|
|
def hashAndSign(self, data, hAlg="sha1"):
|
|
hashData = (secureHash(bytearray(data), hAlg))
|
|
return self.sign(hashData)
|
|
|
|
def sign(self, data):
|
|
N = numBits(self.q)
|
|
digest_len = len(data) * 8
|
|
digest = bytesToNumber(data)
|
|
if N < digest_len:
|
|
digest >>= digest_len - N
|
|
|
|
k = getRandomNumber(1, (self.q-1))
|
|
r = powMod(self.g, k, self.p) % self.q
|
|
s = invMod(k, self.q) * (digest + self.private_key * r) % self.q
|
|
|
|
return encode_sequence(encode_integer(r), encode_integer(s))
|
|
|
|
def verify(self, signature, hashData):
|
|
N = numBits(self.q)
|
|
digest_len = len(hashData) * 8
|
|
digest = bytesToNumber(hashData)
|
|
|
|
if N < digest_len:
|
|
digest >>= digest_len - N
|
|
|
|
signature = compatHMAC(signature)
|
|
|
|
# get r, s keys
|
|
if not signature:
|
|
return False
|
|
body, rest = remove_sequence(signature)
|
|
if rest:
|
|
return False
|
|
r, rest = remove_integer(body)
|
|
s, rest = remove_integer(rest)
|
|
if rest:
|
|
return False
|
|
|
|
if gmpyLoaded or GMPY2_LOADED:
|
|
r = mpz(r)
|
|
s = mpz(s)
|
|
|
|
# check the signature
|
|
if 0 < r < self.q and 0 < s < self.q:
|
|
w = invMod(s, self.q)
|
|
u1 = (digest * w) % self.q
|
|
u2 = (r * w) % self.q
|
|
v = ((powMod(self.g, u1, self.p) * \
|
|
powMod(self.public_key, u2, self.p)) % self.p) % self.q
|
|
return r == v
|
|
return False
|
|
|
|
def hashAndVerify(self, signature, data, hAlg="sha1"):
|
|
digest = secureHash(bytearray(data), hAlg)
|
|
return self.verify(signature, digest)
|