This commit is contained in:
UrloMythus
2026-02-19 20:15:03 +01:00
parent 7785e8c604
commit cfc6bbabc9
181 changed files with 32141 additions and 4629 deletions

View File

@@ -1,22 +1,27 @@
# Authors:
# Authors:
# Trevor Perrin
# Martin von Loewis - python 3 port
# Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
#
# See the LICENSE file for legal information regarding use of this file.
# See the LICENSE file for legal information regarding use of this file
"""cryptomath module
This module has basic math/crypto code."""
from __future__ import print_function
import os
import math
import base64
import binascii
from .compat import compat26Str, compatHMAC, compatLong, \
bytes_to_int, int_to_bytes, bit_length, byte_length
import math
import os
import zlib
from .codec import Writer
from .compat import (
bit_length,
byte_length,
bytes_to_int,
compat26Str,
compatHMAC,
int_to_bytes,
)
from . import tlshashlib as hashlib
from . import tlshmac as hmac
@@ -33,27 +38,31 @@ pycryptoLoaded = False
# **************************************************************************
# Check that os.urandom works
import zlib
assert len(zlib.compress(os.urandom(1000))) > 900
def getRandomBytes(howMany):
b = bytearray(os.urandom(howMany))
assert(len(b) == howMany)
assert len(b) == howMany
return b
prngName = "os.urandom"
# **************************************************************************
# Simple hash functions
# **************************************************************************
def MD5(b):
"""Return a MD5 digest of data"""
return secureHash(b, 'md5')
return secureHash(b, "md5")
def SHA1(b):
"""Return a SHA1 digest of data"""
return secureHash(b, 'sha1')
return secureHash(b, "sha1")
def secureHash(data, algorithm):
"""Return a digest of `data` using `algorithm`"""
@@ -61,33 +70,40 @@ def secureHash(data, algorithm):
hashInstance.update(compat26Str(data))
return bytearray(hashInstance.digest())
def secureHMAC(k, b, algorithm):
"""Return a HMAC using `b` and `k` using `algorithm`"""
k = compatHMAC(k)
b = compatHMAC(b)
return bytearray(hmac.new(k, b, getattr(hashlib, algorithm)).digest())
def HMAC_MD5(k, b):
return secureHMAC(k, b, 'md5')
return secureHMAC(k, b, "md5")
def HMAC_SHA1(k, b):
return secureHMAC(k, b, 'sha1')
return secureHMAC(k, b, "sha1")
def HMAC_SHA256(k, b):
return secureHMAC(k, b, 'sha256')
return secureHMAC(k, b, "sha256")
def HMAC_SHA384(k, b):
return secureHMAC(k, b, 'sha384')
return secureHMAC(k, b, "sha384")
def HKDF_expand(PRK, info, L, algorithm):
N = divceil(L, getattr(hashlib, algorithm)().digest_size)
T = bytearray()
Titer = bytearray()
for x in range(1, N+2):
for x in range(1, N + 2):
T += Titer
Titer = secureHMAC(PRK, Titer + info + bytearray([x]), algorithm)
return T[:L]
def HKDF_expand_label(secret, label, hashValue, length, algorithm):
"""
TLS1.3 key derivation function (HKDF-Expand-Label).
@@ -108,6 +124,7 @@ def HKDF_expand_label(secret, label, hashValue, length, algorithm):
return HKDF_expand(secret, hkdfLabel.bytes, length, algorithm)
def derive_secret(secret, label, handshake_hashes, algorithm):
"""
TLS1.3 key derivation function (Derive-Secret).
@@ -123,17 +140,17 @@ def derive_secret(secret, label, handshake_hashes, algorithm):
:rtype: bytearray
"""
if handshake_hashes is None:
hs_hash = secureHash(bytearray(b''), algorithm)
hs_hash = secureHash(bytearray(b""), algorithm)
else:
hs_hash = handshake_hashes.digest(algorithm)
return HKDF_expand_label(secret, label, hs_hash,
getattr(hashlib, algorithm)().digest_size,
algorithm)
return HKDF_expand_label(secret, label, hs_hash, getattr(hashlib, algorithm)().digest_size, algorithm)
# **************************************************************************
# Converter Functions
# **************************************************************************
def bytesToNumber(b, endian="big"):
"""
Convert a number stored in bytearray to an integer.
@@ -156,7 +173,7 @@ def numberToByteArray(n, howManyBytes=None, endian="big"):
if howManyBytes < length:
ret = int_to_bytes(n, length, endian)
if endian == "big":
return ret[length-howManyBytes:length]
return ret[length - howManyBytes : length]
return ret[:howManyBytes]
return int_to_bytes(n, howManyBytes, endian)
@@ -172,12 +189,12 @@ def mpiToNumber(mpi):
def numberToMPI(n):
b = numberToByteArray(n)
ext = 0
#If the high-order bit is going to be set,
#add an extra byte of zeros
if (numBits(n) & 0x7)==0:
# If the high-order bit is going to be set,
# add an extra byte of zeros
if (numBits(n) & 0x7) == 0:
ext = 1
length = numBytes(n) + ext
b = bytearray(4+ext) + b
b = bytearray(4 + ext) + b
b[0] = (length >> 24) & 0xFF
b[1] = (length >> 16) & 0xFF
b[2] = (length >> 8) & 0xFF
@@ -190,75 +207,57 @@ def numberToMPI(n):
# **************************************************************************
# pylint: disable=invalid-name
# pylint recognises them as constants, not function names, also
# we can't change their names without API change
numBits = bit_length
numBytes = byte_length
# pylint: enable=invalid-name
# **************************************************************************
# Big Number Math
# **************************************************************************
def getRandomNumber(low, high):
assert low < high
howManyBits = numBits(high)
howManyBytes = numBytes(high)
lastBits = howManyBits % 8
while 1:
bytes = getRandomBytes(howManyBytes)
while True:
random_bytes = getRandomBytes(howManyBytes)
if lastBits:
bytes[0] = bytes[0] % (1 << lastBits)
n = bytesToNumber(bytes)
if n >= low and n < high:
random_bytes[0] = random_bytes[0] % (1 << lastBits)
n = bytesToNumber(random_bytes)
if low <= n < high:
return n
def gcd(a,b):
a, b = max(a,b), min(a,b)
def gcd(a, b):
a, b = max(a, b), min(a, b)
while b:
a, b = b, a % b
return a
def lcm(a, b):
return (a * b) // gcd(a, b)
# pylint: disable=invalid-name
# disable pylint check as the (a, b) are part of the API
if GMPY2_LOADED:
def invMod(a, b):
"""Return inverse of a mod b, zero if none."""
if a == 0:
return 0
return powmod(a, -1, b)
else:
# Use Extended Euclidean Algorithm
def invMod(a, b):
"""Return inverse of a mod b, zero if none."""
c, d = a, b
uc, ud = 1, 0
while c != 0:
q = d // c
c, d = d-(q*c), c
uc, ud = ud - (q * uc), uc
if d == 1:
return ud % b
return 0
# pylint: enable=invalid-name
def invMod(a, b):
"""Return inverse of a mod b, zero if none."""
c, d = a, b
uc, ud = 1, 0
while c != 0:
q = d // c
c, d = d - (q * c), c
uc, ud = ud - (q * uc), uc
if d == 1:
return ud % b
return 0
if gmpyLoaded or GMPY2_LOADED:
def powMod(base, power, modulus):
base = mpz(base)
power = mpz(power)
modulus = mpz(modulus)
result = pow(base, power, modulus)
return compatLong(result)
else:
powMod = pow
# Use built-in pow for modular exponentiation (Python 3 handles this efficiently)
powMod = pow
def divceil(divident, divisor):
@@ -267,10 +266,10 @@ def divceil(divident, divisor):
return quot + int(bool(r))
#Pre-calculate a sieve of the ~100 primes < 1000:
# Pre-calculate a sieve of the ~100 primes < 1000:
def makeSieve(n):
sieve = list(range(n))
for count in range(2, int(math.sqrt(n))+1):
for count in range(2, int(math.sqrt(n)) + 1):
if sieve[count] == 0:
continue
x = sieve[count] * 2
@@ -280,30 +279,34 @@ def makeSieve(n):
sieve = [x for x in sieve[2:] if x]
return sieve
def isPrime(n, iterations=5, display=False, sieve=makeSieve(1000)):
#Trial division with sieve
# Trial division with sieve
for x in sieve:
if x >= n: return True
if n % x == 0: return False
#Passed trial division, proceed to Rabin-Miller
#Rabin-Miller implemented per Ferguson & Schneier
#Compute s, t for Rabin-Miller
if display: print("*", end=' ')
s, t = n-1, 0
if x >= n:
return True
if n % x == 0:
return False
# Passed trial division, proceed to Rabin-Miller
# Rabin-Miller implemented per Ferguson & Schneier
# Compute s, t for Rabin-Miller
if display:
print("*", end=" ")
s, t = n - 1, 0
while s % 2 == 0:
s, t = s//2, t+1
#Repeat Rabin-Miller x times
a = 2 #Use 2 as a base for first iteration speedup, per HAC
for count in range(iterations):
s, t = s // 2, t + 1
# Repeat Rabin-Miller x times
a = 2 # Use 2 as a base for first iteration speedup, per HAC
for _ in range(iterations):
v = powMod(a, s, n)
if v==1:
if v == 1:
continue
i = 0
while v != n-1:
if i == t-1:
while v != n - 1:
if i == t - 1:
return False
else:
v, i = powMod(v, 2, n), i+1
v, i = powMod(v, 2, n), i + 1
a = getRandomNumber(2, n)
return True
@@ -316,16 +319,16 @@ def getRandomPrime(bits, display=False):
larger than `(2^(bits-1) * 3 ) / 2` but smaller than 2^bits.
"""
assert bits >= 10
#The 1.5 ensures the 2 MSBs are set
#Thus, when used for p,q in RSA, n will have its MSB set
# The 1.5 ensures the 2 MSBs are set
# Thus, when used for p,q in RSA, n will have its MSB set
#
#Since 30 is lcm(2,3,5), we'll set our test numbers to
#29 % 30 and keep them there
low = ((2 ** (bits-1)) * 3) // 2
high = 2 ** bits - 30
# Since 30 is lcm(2,3,5), we'll set our test numbers to
# 29 % 30 and keep them there
low = ((2 ** (bits - 1)) * 3) // 2
high = 2**bits - 30
while True:
if display:
print(".", end=' ')
print(".", end=" ")
cand_p = getRandomNumber(low, high)
# make odd
if cand_p % 2 == 0:
@@ -334,7 +337,7 @@ def getRandomPrime(bits, display=False):
return cand_p
#Unused at the moment...
# Unused at the moment...
def getRandomSafePrime(bits, display=False):
"""Generate a random safe prime.
@@ -342,23 +345,24 @@ def getRandomSafePrime(bits, display=False):
the (p-1)/2 will also be prime.
"""
assert bits >= 10
#The 1.5 ensures the 2 MSBs are set
#Thus, when used for p,q in RSA, n will have its MSB set
# The 1.5 ensures the 2 MSBs are set
# Thus, when used for p,q in RSA, n will have its MSB set
#
#Since 30 is lcm(2,3,5), we'll set our test numbers to
#29 % 30 and keep them there
low = (2 ** (bits-2)) * 3//2
high = (2 ** (bits-1)) - 30
# Since 30 is lcm(2,3,5), we'll set our test numbers to
# 29 % 30 and keep them there
low = (2 ** (bits - 2)) * 3 // 2
high = (2 ** (bits - 1)) - 30
q = getRandomNumber(low, high)
q += 29 - (q % 30)
while 1:
if display: print(".", end=' ')
while True:
if display:
print(".", end=" ")
q += 30
if (q >= high):
if q >= high:
q = getRandomNumber(low, high)
q += 29 - (q % 30)
#Ideas from Tom Wu's SRP code
#Do trial division on p and q before Rabin-Miller
# Ideas from Tom Wu's SRP code
# Do trial division on p and q before Rabin-Miller
if isPrime(q, 0, display=display):
p = (2 * q) + 1
if isPrime(p, display=display):