87 lines
3.5 KiB
Python
87 lines
3.5 KiB
Python
"""Base class that represents any signed object"""
|
|
|
|
from .utils.cryptomath import numBytes
|
|
|
|
RSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
|
|
ALL_RSA_SIGNATURE_HASHES = RSA_SIGNATURE_HASHES + ["md5"]
|
|
RSA_SCHEMES = ["pss", "pkcs1"]
|
|
|
|
|
|
class SignatureSettings(object):
|
|
def __init__(self, min_key_size=None, max_key_size=None,
|
|
rsa_sig_hashes=None, rsa_schemes=None):
|
|
"""Create default variables for key-related settings."""
|
|
self.min_key_size = min_key_size or 1023
|
|
self.max_key_size = max_key_size or 8193
|
|
self.rsa_sig_hashes = rsa_sig_hashes or list(RSA_SIGNATURE_HASHES)
|
|
self.rsa_schemes = rsa_schemes or list(RSA_SCHEMES)
|
|
|
|
def _copy_settings(self, other):
|
|
other.min_key_size = self.min_key_size
|
|
other.max_key_size = self.max_key_size
|
|
other.rsa_sig_hashes = self.rsa_sig_hashes
|
|
other.rsa_schemes = self.rsa_schemes
|
|
|
|
@staticmethod
|
|
def _sanityCheckKeySizes(other):
|
|
if other.min_key_size < 512:
|
|
raise ValueError("min_key_size too small")
|
|
if other.min_key_size > 16384:
|
|
raise ValueError("min_key_size too large")
|
|
if other.max_key_size < 512:
|
|
raise ValueError("max_key_size too small")
|
|
if other.max_key_size > 16384:
|
|
raise ValueError("max_key_size too large")
|
|
if other.max_key_size < other.min_key_size:
|
|
raise ValueError("max_key_size smaller than min_key_size")
|
|
|
|
@staticmethod
|
|
def _sanityCheckSignatureAlgs(other):
|
|
not_allowed = [alg for alg in other.rsa_sig_hashes
|
|
if alg not in ALL_RSA_SIGNATURE_HASHES]
|
|
if len(not_allowed) > 0:
|
|
raise ValueError("Following signature algorithms are not allowed: "
|
|
"{0}".format(", ".join(not_allowed)))
|
|
|
|
def validate(self):
|
|
other = SignatureSettings()
|
|
self._copy_settings(other)
|
|
self._sanityCheckKeySizes(other)
|
|
self._sanityCheckSignatureAlgs(other)
|
|
return other
|
|
|
|
|
|
class SignedObject(object):
|
|
def __init__(self):
|
|
self.tbs_data = None
|
|
self.signature = None
|
|
self.signature_alg = None
|
|
|
|
_hash_algs_OIDs = {
|
|
tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x4]): 'md5',
|
|
tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x5]): 'sha1',
|
|
tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xe]): 'sha224',
|
|
tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xc]): 'sha384',
|
|
tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xb]): 'sha256',
|
|
tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xd]): 'sha512'
|
|
}
|
|
|
|
def verify_signature(self, publicKey, settings=None):
|
|
""" Verify signature in a reponse"""
|
|
offset = 0
|
|
settings = settings or SignatureSettings()
|
|
|
|
# workaround as some signature encodings could be zero left-padded
|
|
if (self.signature[0] == 0 and
|
|
numBytes(publicKey.n) + 1 == len(self.signature)):
|
|
offset = 1
|
|
|
|
alg = self._hash_algs_OIDs[tuple(self.signature_alg)]
|
|
if alg not in settings.rsa_sig_hashes:
|
|
raise ValueError("Invalid signature algorithm: {0}".format(alg))
|
|
verified = publicKey.hashAndVerify(self.signature[offset:],
|
|
self.tbs_data, hAlg=alg)
|
|
if not verified:
|
|
raise ValueError("Signature could not be verified for {0}"
|
|
.format(alg))
|
|
return True
|