"""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