Example #1
0
 def test_rsa_raw_verify(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature_raw'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw')
Example #2
0
 def test_rsa_raw_verify(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature_raw'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw')
Example #3
0
    def validate(self, msg, sig):
        if self._cert is None:
            raise CryptoException("Cannot validate: no certificate")

        try:
            oscrypto_asymmetric.rsa_pkcs1v15_verify(oscrypto_asymmetric.load_certificate(self._cert), sig, msg, "sha1")
        except oscrypto_errors.SignatureError as err:
            raise CryptoException("Cannot validate: %s" % (err))
Example #4
0
 def test_rsa_verify_key_size_mismatch(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-4096.crt'))
     with self.assertRaises(errors.SignatureError):
         asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1')
Example #5
0
 def test_rsa_raw_verify_fail(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature_raw'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     with self.assertRaises(errors.SignatureError):
         asymmetric.rsa_pkcs1v15_verify(public, signature, original_data + b'1', 'raw')
Example #6
0
 def test_rsa_raw_verify_fail(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature_raw'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     with self.assertRaises(errors.SignatureError):
         asymmetric.rsa_pkcs1v15_verify(public, signature, original_data + b'1', 'raw')
Example #7
0
    def test_rsa_raw_sign(self):
        original_data = b'This is data to sign!'
        private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key'))
        public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))

        signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'raw')
        self.assertIsInstance(signature, byte_cls)

        asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw')
Example #8
0
    def test_rsa_raw_sign(self):
        original_data = b'This is data to sign!'
        private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key'))
        public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))

        signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'raw')
        self.assertIsInstance(signature, byte_cls)

        asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw')
Example #9
0
    def validate(self, msg, sig):
        if self._cert is None:
            raise CryptoException('Cannot validate: no certificate')

        try:
            oscrypto_asymmetric.rsa_pkcs1v15_verify(
                oscrypto_asymmetric.load_certificate(self._cert),
                sig, msg, 'sha1')
        except oscrypto_errors.SignatureError as err:
            raise CryptoException('Cannot validate: {}'.format(err))
Example #10
0
    def test_rsa_generate(self):
        public, private = asymmetric.generate_pair('rsa', bit_size=2048)

        self.assertEqual('rsa', public.algorithm)
        self.assertEqual(2048, public.bit_size)

        original_data = b'This is data to sign'
        signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'sha1')
        self.assertIsInstance(signature, byte_cls)
        asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1')
Example #11
0
 def test_rsa_verify_key_size_mismatch(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f:
         signature = f.read()
     public = asymmetric.load_public_key(
         os.path.join(fixtures_dir, 'keys/test-4096.crt'))
     with self.assertRaises(errors.SignatureError):
         asymmetric.rsa_pkcs1v15_verify(public, signature, original_data,
                                        'sha1')
Example #12
0
    def test_rsa_generate(self):
        public, private = asymmetric.generate_pair('rsa', bit_size=2048)

        self.assertEqual('rsa', public.algorithm)
        self.assertEqual(2048, public.bit_size)

        original_data = b'This is data to sign'
        signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'sha1')
        self.assertIsInstance(signature, byte_cls)
        asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1')
Example #13
0
 def test_rsa_verify_fail_each_byte(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f:
         original_signature = f.read()
     public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt'))
     for i in range(0, len(original_signature)):
         if i == 0:
             signature = b'\xab' + original_signature[1:]
         elif i == len(original_signature) - 1:
             signature = original_signature[0:-1] + b'\xab'
         else:
             signature = original_signature[0:i] + b'\xab' + original_signature[i+1:]
         with self.assertRaises(errors.SignatureError):
             asymmetric.rsa_pkcs1v15_verify(public, signature, original_data+ b'1', 'sha1')
Example #14
0
def _validate_cms_signature(sig_blob: SignatureBlob, cd_hash: bytes):
    assert sig_blob.cms
    signed_data = sig_blob.cms["content"]
    assert isinstance(signed_data, SignedData)
    assert len(signed_data["signer_infos"]) == 1

    # Get certificates
    cert_chain = []
    for cert in signed_data["certificates"]:
        c = cert.chosen
        assert isinstance(c, Certificate)
        cert_chain.append(c)

    # Get algorithms used
    signer_info = signed_data["signer_infos"][0]
    digest_alg = signer_info["digest_algorithm"]["algorithm"].native
    sig_alg = signer_info["signature_algorithm"]["algorithm"].native

    # Get message and signature
    signed_attrs = signer_info["signed_attrs"]
    sig = signer_info["signature"].contents

    # Check the hash of CodeDirectory matches what is in the signature
    message_digest = None
    for attr in sig_blob.cms["content"]["signer_infos"][0]["signed_attrs"]:
        if attr["type"].native == "message_digest":
            message_digest = attr["values"][0].native
            if message_digest != cd_hash:
                raise Exception(
                    f"CodeDirectory Hash mismatch. Expected {message_digest.hex()}, Calculated {cd_hash.hex()}"
                )
    if message_digest is None:
        raise Exception("message_digest not found in signature")

    # Validate the certificate chain
    validation_context = ValidationContext(
        trust_roots=APPLE_ROOTS,
        allow_fetching=False,
        additional_critical_extensions=APPLE_CERT_CRIT_EXTS,
    )
    validator = CertificateValidator(cert_chain[-1], cert_chain[0:-1],
                                     validation_context)
    validator.validate_usage({"digital_signature"}, {"code_signing"})

    # Check the signature
    pubkey = asymmetric.load_public_key(cert_chain[-1].public_key)
    signed_msg = _sort_attributes(signed_attrs).dump()
    asymmetric.rsa_pkcs1v15_verify(pubkey, sig, signed_msg, digest_alg)
Example #15
0
 def test_rsa_verify_fail_each_byte(self):
     with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f:
         original_data = f.read()
     with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f:
         original_signature = f.read()
     public = asymmetric.load_public_key(
         os.path.join(fixtures_dir, 'keys/test.crt'))
     for i in range(0, len(original_signature)):
         if i == 0:
             signature = b'\xab' + original_signature[1:]
         elif i == len(original_signature) - 1:
             signature = original_signature[0:-1] + b'\xab'
         else:
             signature = original_signature[
                 0:i] + b'\xab' + original_signature[i + 1:]
         with self.assertRaises(errors.SignatureError):
             asymmetric.rsa_pkcs1v15_verify(public, signature,
                                            original_data + b'1', 'sha1')
Example #16
0
    def verify_signature(self, signature, data, algorithm, allow_legacy=False):
        """Verifies whether the signature bytes match the data using the hashing algorithm. Supports RSA and EC keys.
        Note that not all hashing algorithms are supported.

        :param bytes signature: The signature to verify
        :param bytes data: The data that must be verified
        :type algorithm: a hashlib function
        :param algorithm: The hashing algorithm to use
        :param bool allow_legacy: If True, allows a legacy signature verification. This method is intended for the case
            where the encryptedDigest does not contain an ASN.1 structure, but a raw hash value instead. It is attempted
            automatically when verification of the RSA signature fails.

            This case is described in more detail on
            https://mta.openssl.org/pipermail/openssl-users/2015-September/002053.html
        """

        public_key = asymmetric.load_public_key(self.to_asn1crypto.public_key)
        if public_key.algorithm == 'rsa':
            verify_func = asymmetric.rsa_pkcs1v15_verify
        elif public_key.algorithm == 'dsa':
            verify_func = asymmetric.dsa_verify
        elif public_key.algorithm == 'ec':
            verify_func = asymmetric.ecdsa_verify
        else:
            raise CertificateVerificationError(
                "Signature algorithm %s is unsupported for %s" %
                (public_key.algorithm, self))

        try:
            verify_func(public_key, signature, data, algorithm().name)
        except Exception as e:
            if not allow_legacy or public_key.algorithm != 'rsa':
                raise CertificateVerificationError(
                    "Invalid signature for %s: %s" % (self, e))
        else:
            return

        try:
            asymmetric.rsa_pkcs1v15_verify(public_key, signature,
                                           algorithm(data).digest(), 'raw')
        except Exception as e:
            raise CertificateVerificationError(
                "Invalid signature for %s (legacy attempted): %s" % (self, e))
Example #17
0
    def verify(self, public_key_modulus: int, public_key_exponent: int) -> bool:
        """Use given public key to verify the certificate is signed.

        :param public_key_modulus: modulus of the public key to be verified
        :param public_key_exponent: exponent of the public key to be verified
        :return: True if verification pass; False otherwise
        """
        public_key = internal_backend.rsa_public_key(public_key_modulus, public_key_exponent)
        key_object = load_public_key(public_key.export_key())
        try:
            rsa_pkcs1v15_verify(
                key_object,
                self._cert["signature_value"].native,
                self._cert["tbs_certificate"].dump(),
                self.hash_algo,
            )
        except SignatureError:
            return False
        return True
Example #18
0
    def test_rsa_generate(self):
        public, private = asymmetric.generate_pair('rsa', bit_size=2048)

        self.assertEqual('rsa', public.algorithm)
        self.assertEqual(2048, public.bit_size)

        original_data = b'This is data to sign'
        signature = asymmetric.rsa_pkcs1v15_sign(private, original_data,
                                                 'sha1')
        self.assertIsInstance(signature, byte_cls)
        asymmetric.rsa_pkcs1v15_verify(public, signature, original_data,
                                       'sha1')

        raw_public = asymmetric.dump_public_key(public)
        asymmetric.load_public_key(raw_public)
        raw_private = asymmetric.dump_private_key(private, None)
        asymmetric.load_private_key(raw_private, None)

        self.assertIsInstance(private.fingerprint, byte_cls)
        self.assertIsInstance(public.fingerprint, byte_cls)
        self.assertEqual(private.fingerprint, public.fingerprint)
Example #19
0
 def verify(self, pubkey):
     self.check_valid()
     with open(self.filepath, 'rb') as zipfile:
         zipfile.seek(0, os.SEEK_SET)
         message = zipfile.read(self.signed_len)
         zipfile.seek(-self.signature_start, os.SEEK_END)
         signature_size = self.signature_start - FOOTER_SIZE
         signature_raw = zipfile.read(signature_size)
     sig = ContentInfo.load(signature_raw)['content']['signer_infos'][0]
     sig_contents = sig['signature'].contents
     sig_type = DigestAlgorithmId.map(
         sig['digest_algorithm']['algorithm'].dotted)
     with open(pubkey, 'rb') as keyfile:
         keydata = load_public_key(keyfile.read())
     return rsa_pkcs1v15_verify(keydata, sig_contents, message, sig_type)
Example #20
0
def test_2k_signverify():
    k1, k2 = setup_keys_2k()
    ha = 'sha256'
    s = crypto.pkcs1v15_sign(k1, pytest.tbs_str)
    print('[{}]'.format(', '.join(hex(x) for x in list(s.signature))))

    pubkey_info = keys.PublicKeyInfo.load(pytest.twok)

    # Load a public key into the oscrypto engine to using it in the verify function
    public = load_public_key(pubkey_info)

    rsa_pkcs1v15_verify(public, s.signature, pytest.tbs_str, ha)

    # Assert wrong text
    with pytest.raises(SignatureError):
        rsa_pkcs1v15_verify(public, s.signature, pytest.tbs_str_fail, ha)

    # Assert wrong key
    with pytest.raises(SignatureError):
        pubkey_info = keys.PublicKeyInfo.load(pytest.twok_fail)
        public = load_public_key(pubkey_info)
        rsa_pkcs1v15_verify(public, s.signature, pytest.tbs_str, ha)
Example #21
0
def test_1k_signverify():
	LOGGER.info('Sign data with newly generated RSA1k key and verify result')
	setup_keys_1k()
	ha = 'sha256'
	s = rsassa.sign(pytest.onek, pytest.tbs_str)
	print('[{}]'.format(', '.join(hex(x) for x in list(s.signature))))

	pubkey_info = keys.PublicKeyInfo.load(pytest.onek.pkey)

	# Load a public key into the oscrypto engine to using it in the verify function
	public = load_public_key(pubkey_info)

	rsa_pkcs1v15_verify(public, s.signature, pytest.tbs_str, ha)

	# Assert wrong text
	with pytest.raises(SignatureError):
		rsa_pkcs1v15_verify(public, s.signature, pytest.tbs_str_fail, ha)

	# Assert wrong key
	with pytest.raises(SignatureError):
		pubkey_info = keys.PublicKeyInfo.load(pytest.onek_fail.pkey)
		public = load_public_key(pubkey_info)
		rsa_pkcs1v15_verify(public, s.signature, pytest.tbs_str, ha)
Example #22
0
    def on_get(self, req, resp):
        operation = req.get_param("operation", required=True)
        if operation.lower() == "getcacert":
            resp.body = keys.parse_certificate(
                self.authority.certificate_buf).dump()
            resp.append_header("Content-Type", "application/x-x509-ca-cert")
            return

        # If we bump into exceptions later
        encrypted_container = b""
        attr_list = [
            cms.CMSAttribute({
                'type': "message_type",
                'values': ["3"]
            }),
            cms.CMSAttribute({
                'type': "pki_status",
                'values': ["2"]  # rejected
            })
        ]

        try:
            info = cms.ContentInfo.load(
                b64decode(req.get_param("message", required=True)))

            ###############################################
            ### Verify signature of the outer container ###
            ###############################################

            signed_envelope = info['content']
            encap_content_info = signed_envelope['encap_content_info']
            encap_content = encap_content_info['content']

            # TODO: try except
            current_certificate, = signed_envelope["certificates"]
            signer, = signed_envelope["signer_infos"]

            # TODO: compare cert to current one if we are renewing

            assert signer["digest_algorithm"]["algorithm"].native == "md5"
            assert signer["signature_algorithm"][
                "algorithm"].native == "rsassa_pkcs1v15"
            message_digest = None
            transaction_id = None
            sender_nonce = None

            for attr in signer["signed_attrs"]:
                if attr["type"].native == "sender_nonce":
                    sender_nonce, = attr["values"]
                elif attr["type"].native == "trans_id":
                    transaction_id, = attr["values"]
                elif attr["type"].native == "message_digest":
                    message_digest, = attr["values"]
                    if hashlib.md5(encap_content.native).digest(
                    ) != message_digest.native:
                        raise SCEPBadMessageCheck()

            assert message_digest
            msg = signer["signed_attrs"].dump(force=True)
            assert msg[0] == 160

            # Verify signature
            try:
                asymmetric.rsa_pkcs1v15_verify(
                    asymmetric.load_certificate(current_certificate.dump()),
                    signer["signature"].native,
                    b"\x31" + msg[1:],  # wtf?!
                    "md5")
            except SignatureError:
                raise SCEPBadMessageCheck()

            ###############################
            ### Decrypt inner container ###
            ###############################

            info = cms.ContentInfo.load(encap_content.native)
            encrypted_envelope = info['content']
            encrypted_content_info = encrypted_envelope[
                'encrypted_content_info']
            iv = encrypted_content_info['content_encryption_algorithm'][
                'parameters'].native

            if encrypted_content_info['content_encryption_algorithm'][
                    "algorithm"].native != "des":
                raise SCEPBadAlgo()

            encrypted_content = encrypted_content_info[
                'encrypted_content'].native
            recipient, = encrypted_envelope['recipient_infos']

            if recipient.native["rid"][
                    "serial_number"] != self.authority.certificate.serial_number:
                raise SCEPBadCertId()

            # Since CA private key is not directly readable here, we'll redirect it to signer socket
            key = asymmetric.rsa_pkcs1v15_decrypt(
                self.authority.private_key, recipient.native["encrypted_key"])
            if len(key) == 8: key = key * 3  # Convert DES to 3DES
            buf = symmetric.tripledes_cbc_pkcs5_decrypt(
                key, encrypted_content, iv)
            _, _, common_name = self.authority.store_request(buf,
                                                             overwrite=True)
            cert, buf = self.authority.sign(common_name, overwrite=True)
            signed_certificate = asymmetric.load_certificate(buf)
            content = signed_certificate.asn1.dump()

        except SCEPError as e:
            attr_list.append(
                cms.CMSAttribute({
                    'type': "fail_info",
                    'values': ["%d" % e.code]
                }))
        else:

            ##################################
            ### Degenerate inner container ###
            ##################################

            degenerate = cms.ContentInfo({
                'content_type':
                "signed_data",
                'content':
                cms.SignedData({
                    'version':
                    "v1",
                    'certificates': [signed_certificate.asn1],
                    'digest_algorithms':
                    [cms.DigestAlgorithm({'algorithm': "md5"})],
                    'encap_content_info': {
                        'content_type':
                        "data",
                        'content':
                        cms.ContentInfo({
                            'content_type': "signed_data",
                            'content': None
                        }).dump()
                    },
                    'signer_infos': []
                })
            })

            ################################
            ### Encrypt middle container ###
            ################################

            key = os.urandom(8)
            iv, encrypted_content = symmetric.des_cbc_pkcs5_encrypt(
                key, degenerate.dump(), os.urandom(8))
            assert degenerate.dump() == symmetric.tripledes_cbc_pkcs5_decrypt(
                key * 3, encrypted_content, iv)

            ri = cms.RecipientInfo({
                'ktri':
                cms.KeyTransRecipientInfo({
                    'version':
                    "v0",
                    'rid':
                    cms.RecipientIdentifier({
                        'issuer_and_serial_number':
                        cms.IssuerAndSerialNumber({
                            'issuer':
                            current_certificate.chosen["tbs_certificate"]
                            ["issuer"],
                            'serial_number':
                            current_certificate.chosen["tbs_certificate"]
                            ["serial_number"],
                        }),
                    }),
                    'key_encryption_algorithm': {
                        'algorithm': "rsa"
                    },
                    'encrypted_key':
                    asymmetric.rsa_pkcs1v15_encrypt(
                        asymmetric.load_certificate(
                            current_certificate.chosen.dump()), key)
                })
            })

            encrypted_container = cms.ContentInfo({
                'content_type':
                "enveloped_data",
                'content':
                cms.EnvelopedData({
                    'version': "v1",
                    'recipient_infos': [ri],
                    'encrypted_content_info': {
                        'content_type': "data",
                        'content_encryption_algorithm': {
                            'algorithm': "des",
                            'parameters': iv
                        },
                        'encrypted_content': encrypted_content
                    }
                })
            }).dump()

            attr_list = [
                cms.CMSAttribute({
                    'type':
                    "message_digest",
                    'values': [hashlib.sha1(encrypted_container).digest()]
                }),
                cms.CMSAttribute({
                    'type': "message_type",
                    'values': ["3"]
                }),
                cms.CMSAttribute({
                    'type': "pki_status",
                    'values': ["0"]  # ok
                })
            ]
        finally:

            ##############################
            ### Signed outer container ###
            ##############################

            attrs = cms.CMSAttributes(attr_list + [
                cms.CMSAttribute({
                    'type': "recipient_nonce",
                    'values': [sender_nonce]
                }),
                cms.CMSAttribute({
                    'type': "trans_id",
                    'values': [transaction_id]
                })
            ])

            signer = cms.SignerInfo({
                "signed_attrs":
                attrs,
                'version':
                "v1",
                'sid':
                cms.SignerIdentifier({
                    'issuer_and_serial_number':
                    cms.IssuerAndSerialNumber({
                        'issuer':
                        self.authority.certificate.issuer,
                        'serial_number':
                        self.authority.certificate.serial_number,
                    }),
                }),
                'digest_algorithm':
                algos.DigestAlgorithm({'algorithm': "sha1"}),
                'signature_algorithm':
                algos.SignedDigestAlgorithm({'algorithm': "rsassa_pkcs1v15"}),
                'signature':
                asymmetric.rsa_pkcs1v15_sign(self.authority.private_key,
                                             b"\x31" + attrs.dump()[1:],
                                             "sha1")
            })

            resp.append_header("Content-Type", "application/x-pki-message")
            resp.body = cms.ContentInfo({
                'content_type':
                "signed_data",
                'content':
                cms.SignedData({
                    'version':
                    "v1",
                    'certificates': [self.authority.certificate],
                    'digest_algorithms':
                    [cms.DigestAlgorithm({'algorithm': "sha1"})],
                    'encap_content_info': {
                        'content_type': "data",
                        'content': encrypted_container
                    },
                    'signer_infos': [signer]
                })
            }).dump()
Example #23
0
def verify_message(data_to_verify, signature, verify_cert):
    """Function parses an ASN.1 encrypted message and extracts/decrypts 
            the original message.

    :param data_to_verify: 
        A byte string of the data to be verified against the signature. 

    :param signature: A CMS ASN.1 byte string containing the signature.
    
    :param verify_cert: The certificate to be used for verifying the signature.

    :return: The digest algorithm that was used in the signature.    
    """

    cms_content = cms.ContentInfo.load(signature)
    digest_alg = None
    if cms_content['content_type'].native == 'signed_data':

        for signer in cms_content['content']['signer_infos']:

            digest_alg = signer['digest_algorithm']['algorithm'].native
            if digest_alg not in DIGEST_ALGORITHMS:
                raise Exception('Unsupported Digest Algorithm')

            sig_alg = signer['signature_algorithm']['algorithm'].native
            sig = signer['signature'].native
            signed_data = data_to_verify

            if signer['signed_attrs']:
                attr_dict = {}
                for attr in signer['signed_attrs']:
                    try:
                        attr_dict[attr.native['type']] = attr.native['values']
                    except (ValueError, KeyError):
                        continue

                message_digest = bytes()
                for d in attr_dict['message_digest']:
                    message_digest += d

                digest_func = hashlib.new(digest_alg)
                digest_func.update(data_to_verify)
                calc_message_digest = digest_func.digest()
                if message_digest != calc_message_digest:
                    raise IntegrityError(
                        'Failed to verify message signature: Message Digest does not match.'
                    )

                signed_data = signer['signed_attrs'].untag().dump()

            try:
                if sig_alg == 'rsassa_pkcs1v15':
                    asymmetric.rsa_pkcs1v15_verify(verify_cert, sig,
                                                   signed_data, digest_alg)
                elif sig_alg == 'rsassa_pss':
                    asymmetric.rsa_pss_verify(verify_cert, sig, signed_data,
                                              digest_alg)
                else:
                    raise AS2Exception('Unsupported Signature Algorithm')
            except Exception as e:
                raise IntegrityError(
                    'Failed to verify message signature: {}'.format(e))
    else:
        raise IntegrityError('Signed data not found in ASN.1 ')

    return digest_alg
Example #24
0
    def verify(self, certificate, message, signature):
        """
        :param expected_measurement:
            Hex-String of the expected measurement, 
            e.g. "4ff505f350698c78e8b3b49b8e479146ce3896a06cd9e5109dfec8f393f14025"

        :param certificate:
            A byte string of the certificate
 
        :param message:
            A byte string of the data the signature is for
 
        :param signature:
            A byte string of the data the signature is for
 
        :raises:
            CertificateValidationError
            SignatureValidationError
            MessageParsingError
            ISVEnclaveQuoteStatusError
            EnclaveQuoteFlagsError
            MeasurementMismatchError
 
        :return:
            the QuoteBody inside the message
        """

        # Step 1: validate certificate with Intel SGX attestation root CA
        try:
            validator = CertificateValidator(certificate,
                                             validation_context=self.context)
            validator.validate_usage(set(["digital_signature"]))
        except:
            raise CertificateValidationError

        # Step 2: verify message signature
        try:
            pubk = asymmetric.load_public_key(certificate)
            asymmetric.rsa_pkcs1v15_verify(pubk, signature, message, "sha256")
        except:
            raise SignatureValidationError

        # Step 3: parse message and get quote body
        try:
            message_dic = json.loads(message)
            quote_body_encoded = message_dic["isvEnclaveQuoteBody"]
            isv_enclave_quote_status = message_dic["isvEnclaveQuoteStatus"]
            quote_body = _QuoteBody.from_base64_string(quote_body_encoded)
        except:
            raise MessageParsingError

        # Step 4: check isv enclave quote status
        if isv_enclave_quote_status == "OK":
            pass
        elif isv_enclave_quote_status == "CONFIGURATION_NEEDED":
            if not self.accept_configuration_needed:
                raise ISVEnclaveQuoteStatusError
        elif isv_enclave_quote_status == "GROUP_OUT_OF_DATE":
            if not self.accept_group_out_of_date:
                raise ISVEnclaveQuoteStatusError
        else:
            raise ISVEnclaveQuoteStatusError

        # Step 5: check enclave quote flags
        flags = quote_body.flags
        if not (flags & Verification.IASAttributeFlags.INIT):
            raise EnclaveQuoteFlagsError
        if not (flags & Verification.IASAttributeFlags.MODE64BIT):
            raise EnclaveQuoteFlagsError
        if flags & Verification.IASAttributeFlags.DEBUG:
            if not self.accept_debug:
                raise EnclaveQuoteFlagsError

        # Step 6: assert expected measurement
        if self.expected_measurement:
            measurement = bytes(quote_body.mrenclave).hex()
            if not (measurement == self.expected_measurement):
                raise MeasurementMismatchError(
                    f"Expected: {self.expected_measurement}. Actual: {measurement}."
                )

        return QuoteBody(quote_body)
Example #25
0
def main():
    if len(sys.argv) < 2:
        sys.exit("Not enough arguments")

    path = sys.argv[1]

    # TODO Add flag to override detection based on filetype

    # Try to determine type based on extension
    file, ext = os.path.splitext(path)
    ext = ext.lower()

    if ext == '.roa':
        ext_class = ContentInfo
    elif ext == '.mft':
        ext_class = ContentInfo
    elif ext == '.crl':
        ext_class = CertificateList
    elif ext == '.cer':
        ext_class = RPKICertificate
    else:
        sys.exit("Unknown filetype: " + ext)

    # Read file
    try:
        file = open(path, "rb")
        der_byte_string = file.read()
    except Exception as e:
        sys.exit("Could not read file.\n" + str(e))

    # Parse ASN.1 data using previously picked type
    try:
        parsed = ext_class.load(der_byte_string)
    except Exception as e:
        sys.exit("Could not parse file.\n" + str(e))

    # TODO Sanity check of resulting data

    try:
        # Convert to readable JSON output
        data = parsed.native

        if not type(parsed) is ContentInfo:
            raise Exception("Invalid content type (not ROA or RPKI manifest)")

        # Verification according to RFC 5652 - Cryptographic Message Syntax (CMS)

        # Compute message digest
        hash_data = bytes(parsed['content']['encap_content_info']['content'])
        h = hashlib.sha256()
        h.update(hash_data)
        computed_digest = h.digest()

        # Get digest and content type from message
        message_digest = None
        message_content_type = None
        for attr in data['content']['signer_infos'][0]['signed_attrs']:
            if attr['type'] == 'message_digest':
                message_digest = attr['values'][0]
            elif attr['type'] == 'content_type':
                message_content_type = attr['values'][0]

        # Compare message content types
        if data['content']['encap_content_info']['content_type'] == message_content_type:
            print('Content types equal (' + message_content_type + ')')
        else:
            print('Content types not equal')

        # Compare computed digest with digest from message
        if computed_digest == message_digest:
            print('Digests equal')
        else:
            print('Digests not equal')

        # Get certificate and algorithm to be used for verification
        cert = asymmetric.load_certificate(parsed['content']['certificates'][0].chosen)
        hash_algo = 'sha256'

        # Rewrite signed attributes to EXPLICIT SET OF
        signed_data = SetOf(spec=CMSAttribute)
        for child in parsed['content']['signer_infos'][0]['signed_attrs'].children:
            signed_data.append(child)

        # Verify signature
        result = asymmetric.rsa_pkcs1v15_verify(cert, data['content']['signer_infos'][0]['signature'], signed_data.dump(), hash_algo)
        if result is None:
            print("Signature is valid")

        # TODO Verify included certificate is part of a valid chain

    except Exception as e:
        sys.exit("Something went wrong:\n" + str(e))
Example #26
0
    def on_get(self, req, resp):
        operation = req.get_param("operation", required=True)
        if operation == "GetCACert":
            resp.body = keys.parse_certificate(
                self.authority.certificate_buf).dump()
            resp.append_header("Content-Type", "application/x-x509-ca-cert")
            return
        elif operation == "GetCACaps":
            # TODO: return renewal flag based on renewal subnets config option
            resp.body = "Renewal\nMD5\nSHA-1\nSHA-256\nSHA-512\nDES3\n"
            return
        elif operation == "PKIOperation":
            pass
        else:
            raise falcon.HTTPBadRequest("Bad request",
                                        "Unknown operation %s" % operation)

        # If we bump into exceptions later
        encrypted_container = b""
        attr_list = [
            cms.CMSAttribute({
                'type': "message_type",
                'values': ["3"]
            }),
            cms.CMSAttribute({
                'type': "pki_status",
                'values': ["2"]  # rejected
            })
        ]

        try:
            info = cms.ContentInfo.load(
                b64decode(req.get_param("message", required=True)))

            ###############################################
            ### Verify signature of the outer container ###
            ###############################################

            signed_envelope = info['content']
            encap_content_info = signed_envelope['encap_content_info']
            encap_content = encap_content_info['content']

            # TODO: try except
            current_certificate, = signed_envelope["certificates"]
            signer, = signed_envelope["signer_infos"]

            # TODO: compare cert to current one if we are renewing

            digest_algorithm = signer["digest_algorithm"]["algorithm"].native
            signature_algorithm = signer["signature_algorithm"][
                "algorithm"].native

            if digest_algorithm not in ("md5", "sha1", "sha256", "sha512"):
                raise SCEPBadAlgo()
            if signature_algorithm != "rsassa_pkcs1v15":
                raise SCEPBadAlgo()

            message_digest = None
            transaction_id = None
            sender_nonce = None

            for attr in signer["signed_attrs"]:
                if attr["type"].native == "sender_nonce":
                    sender_nonce, = attr["values"]
                elif attr["type"].native == "trans_id":
                    transaction_id, = attr["values"]
                elif attr["type"].native == "message_digest":
                    message_digest, = attr["values"]
                    if getattr(hashlib,
                               digest_algorithm)(encap_content.native).digest(
                               ) != message_digest.native:
                        raise SCEPDigestMismatch()

            if not sender_nonce:
                raise SCEPBadRequest()
            if not transaction_id:
                raise SCEPBadRequest()

            assert message_digest
            msg = signer["signed_attrs"].dump(force=True)
            assert msg[0] == 160

            # Verify signature
            try:
                asymmetric.rsa_pkcs1v15_verify(
                    asymmetric.load_certificate(current_certificate.dump()),
                    signer["signature"].native,
                    b"\x31" + msg[1:],  # wtf?!
                    "md5")
            except SignatureError:
                raise SCEPSignatureMismatch()

            ###############################
            ### Decrypt inner container ###
            ###############################

            info = cms.ContentInfo.load(encap_content.native)
            encrypted_envelope = info['content']
            encrypted_content_info = encrypted_envelope[
                'encrypted_content_info']
            iv = encrypted_content_info['content_encryption_algorithm'][
                'parameters'].native

            if encrypted_content_info['content_encryption_algorithm'][
                    "algorithm"].native != "des":
                raise SCEPBadAlgo()

            encrypted_content = encrypted_content_info[
                'encrypted_content'].native
            recipient, = encrypted_envelope['recipient_infos']

            if recipient.native["rid"][
                    "serial_number"] != self.authority.certificate.serial_number:
                raise SCEPBadCertId()

            key = asymmetric.rsa_pkcs1v15_decrypt(
                self.authority.private_key, recipient.native["encrypted_key"])
            if len(key) == 8: key = key * 3  # Convert DES to 3DES
            buf = symmetric.tripledes_cbc_pkcs5_decrypt(
                key, encrypted_content, iv)
            _, _, common_name = self.authority.store_request(buf,
                                                             overwrite=True)
            logger.info(
                "SCEP client from %s requested with %s digest algorithm, %s signature",
                req.context["remote_addr"], digest_algorithm,
                signature_algorithm)
            cert, buf = self.authority.sign(common_name,
                                            profile=config.PROFILES["gw"],
                                            overwrite=True)
            signed_certificate = asymmetric.load_certificate(buf)
            content = signed_certificate.asn1.dump()

        except SCEPError as e:
            attr_list.append(
                cms.CMSAttribute({
                    'type': "fail_info",
                    'values': ["%d" % e.code]
                }))
            logger.info("Failed to sign SCEP request due to: %s" %
                        e.explaination)
        else:

            ##################################
            ### Degenerate inner container ###
            ##################################

            degenerate = cms.ContentInfo({
                'content_type':
                "signed_data",
                'content':
                cms.SignedData({
                    'version':
                    "v1",
                    'certificates': [signed_certificate.asn1],
                    'digest_algorithms':
                    [cms.DigestAlgorithm({'algorithm': digest_algorithm})],
                    'encap_content_info': {
                        'content_type':
                        "data",
                        'content':
                        cms.ContentInfo({
                            'content_type': "signed_data",
                            'content': None
                        }).dump()
                    },
                    'signer_infos': []
                })
            })

            ################################
            ### Encrypt middle container ###
            ################################

            key = os.urandom(8)
            iv, encrypted_content = symmetric.des_cbc_pkcs5_encrypt(
                key, degenerate.dump(), os.urandom(8))
            assert degenerate.dump() == symmetric.tripledes_cbc_pkcs5_decrypt(
                key * 3, encrypted_content, iv)

            ri = cms.RecipientInfo({
                'ktri':
                cms.KeyTransRecipientInfo({
                    'version':
                    "v0",
                    'rid':
                    cms.RecipientIdentifier({
                        'issuer_and_serial_number':
                        cms.IssuerAndSerialNumber({
                            'issuer':
                            current_certificate.chosen["tbs_certificate"]
                            ["issuer"],
                            'serial_number':
                            current_certificate.chosen["tbs_certificate"]
                            ["serial_number"],
                        }),
                    }),
                    'key_encryption_algorithm': {
                        'algorithm': "rsa"
                    },
                    'encrypted_key':
                    asymmetric.rsa_pkcs1v15_encrypt(
                        asymmetric.load_certificate(
                            current_certificate.chosen.dump()), key)
                })
            })

            encrypted_container = cms.ContentInfo({
                'content_type':
                "enveloped_data",
                'content':
                cms.EnvelopedData({
                    'version': "v1",
                    'recipient_infos': [ri],
                    'encrypted_content_info': {
                        'content_type': "data",
                        'content_encryption_algorithm': {
                            'algorithm': "des",
                            'parameters': iv
                        },
                        'encrypted_content': encrypted_content
                    }
                })
            }).dump()

            attr_list = [
                cms.CMSAttribute({
                    'type':
                    "message_digest",
                    'values': [
                        getattr(
                            hashlib,
                            digest_algorithm)(encrypted_container).digest()
                    ]
                }),
                cms.CMSAttribute({
                    'type': "message_type",
                    'values': ["3"]
                }),
                cms.CMSAttribute({
                    'type': "pki_status",
                    'values': ["0"]  # ok
                })
            ]
        finally:

            ##############################
            ### Signed outer container ###
            ##############################

            attrs = cms.CMSAttributes(attr_list + [
                cms.CMSAttribute({
                    'type': "recipient_nonce",
                    'values': [sender_nonce]
                }),
                cms.CMSAttribute({
                    'type': "trans_id",
                    'values': [transaction_id]
                })
            ])

            signer = cms.SignerInfo({
                "signed_attrs":
                attrs,
                'version':
                "v1",
                'sid':
                cms.SignerIdentifier({
                    'issuer_and_serial_number':
                    cms.IssuerAndSerialNumber({
                        'issuer':
                        self.authority.certificate.issuer,
                        'serial_number':
                        self.authority.certificate.serial_number,
                    }),
                }),
                'digest_algorithm':
                algos.DigestAlgorithm({'algorithm': digest_algorithm}),
                'signature_algorithm':
                algos.SignedDigestAlgorithm({'algorithm': "rsassa_pkcs1v15"}),
                'signature':
                asymmetric.rsa_pkcs1v15_sign(self.authority.private_key,
                                             b"\x31" + attrs.dump()[1:],
                                             digest_algorithm)
            })

            resp.append_header("Content-Type", "application/x-pki-message")
            resp.body = cms.ContentInfo({
                'content_type':
                "signed_data",
                'content':
                cms.SignedData({
                    'version':
                    "v1",
                    'certificates': [self.authority.certificate],
                    'digest_algorithms':
                    [cms.DigestAlgorithm({'algorithm': digest_algorithm})],
                    'encap_content_info': {
                        'content_type': "data",
                        'content': encrypted_container
                    },
                    'signer_infos': [signer]
                })
            }).dump()
def validate_ocsp_response(cert, issuer, ocsp_request_obj, ocsp_response_objs,
                           current_time):
    #print(cert.ocsp_urls) # Just to see how many urls there are
    subject = cert['tbs_certificate']['subject'].native
    errors = {}
    warnings = {}
    lints_list = []
    count = 0
    lints = {}

    for (ocsp_url, ocsp_response_obj) in ocsp_response_objs:
        count += 1
        lints['domain'] = subject['common_name']
        lints['ocsp_url'] = ocsp_url
        lints['response_count'] = count
        #print(ocsp_response_obj.native)
        errors = {}
        warnings = {}
        if isinstance(ocsp_response_obj, urllib.error.HTTPError):
            errors['HTTPError'] = str(ocsp_response_obj)
            lints['errors'] = errors
            lints['warnings'] = warnings
            lints_list.append(lints)
            return lints_list

        if isinstance(ocsp_response_obj, ValueError):
            errors['ValueError'] = str(ocsp_response_obj)
            lints['errors'] = errors
            lints['warnings'] = warnings
            lints_list.append(lints)
            return lints_list

        if (ocsp_response_obj['response_status'].native == 'unauthorized'):
            errors['Unauthorized'] = 'Responder returned unauthorized'
            lints['errors'] = errors
            lints['warnings'] = warnings
            lints_list.append(lints)
            continue

        if (ocsp_response_obj['response_status'].native == 'malformed_request'
            ):
            errors['ResponseFailure'] = 'Responder returned malformed request'
            lints['errors'] = errors
            lints['warnings'] = warnings
            lints_list.append(lints)
            continue

        request_nonce = ocsp_request_obj.nonce_value
        #print (ocsp_response_obj.native)
        response_nonce = ocsp_response_obj.nonce_value
        if request_nonce and response_nonce and request_nonce.native != response_nonce.native:
            errors[
                'NonceVerificationFailure'] = 'Unable to verify OCSP response since the request and response nonces do not match'

        if ocsp_response_obj['response_status'].native != 'successful':
            errors['OCSPCheckFailure'] = 'OCSP check returned as failed'

        response_bytes = ocsp_response_obj['response_bytes']
        if response_bytes['response_type'].native != 'basic_ocsp_response':
            errors[
                'ResponseTypeFailure'] = 'OCSP response is not Basic OCSP Response'

        parsed_response = response_bytes['response'].parsed
        tbs_response = parsed_response['tbs_response_data']
        certificate_response = tbs_response['responses'][0]

        certificate_id = certificate_response['cert_id']
        algo = certificate_id['hash_algorithm']['algorithm'].native

        certificate_issuer_name_hash = certificate_id[
            'issuer_name_hash'].native
        certificate_issuer_key_hash = certificate_id['issuer_key_hash'].native
        certificate_serial_number = certificate_id['serial_number'].native

        certificate_issuer_name_hash_from_file = getattr(cert.issuer, algo)
        certificate_issuer_key_hash_from_file = getattr(
            issuer.public_key, algo)
        certificate_serial_number_from_file = cert.serial_number

        if certificate_serial_number != certificate_serial_number_from_file:
            errors['CertificateSerialMismatchFailure'] = \
            'OCSP response certificate serial number does not match request certificate serial number'

        if certificate_issuer_key_hash != certificate_issuer_key_hash_from_file:
            errors[
                'IssuerKeyMismatchFailure'] = 'OCSP response issuer key hash does not match request certificate issuer key hash'

        if certificate_issuer_name_hash != certificate_issuer_name_hash_from_file:
            errors['IssuerNameHashMismatchFailure'] = \
                'OCSP response issuer name hash does not match request certificate issuer name hash'

        this_update_time = certificate_response['this_update'].native
        if current_time < this_update_time:
            errors[
                'ThisUpdateTimeError'] = 'OCSP reponse update time is from the future'

        if "next_update" not in certificate_response or certificate_response[
                'next_update'].native is None:
            warnings[
                'NextUpdateTimeMissing'] = 'OCSP response does not contain next update time'
        else:
            next_update_time = certificate_response['next_update'].native
            if current_time > next_update_time:
                errors[
                    'NextUpdateTimeFailure'] = 'OCSP reponse next update time is in the past'

        registry = CertificateRegistry(trust_roots=[issuer])

        if tbs_response['responder_id'].name == 'by_key':
            key_identifier = tbs_response['responder_id'].native
            signing_cert = registry.retrieve_by_key_identifier(key_identifier)

        elif tbs_response['responder_id'].name == 'by_name':
            signing_certs = registry.retrieve_by_name(
                tbs_response['responder_id'].chosen, None)
            if signing_certs is not None and len(signing_certs) > 0:
                signing_cert = signing_certs[0]
            else:
                signing_cert = None

        if signing_cert is None:
            errors[
                'SigningCetificateNotFoundFailure'] = 'OCSP response signing certificate not found'
            lints['errors'] = errors
            lints['warnings'] = warnings
            lints_list.append(lints)
            continue

        if issuer.issuer_serial != signing_cert.issuer_serial:
            if signing_cert_issuer.issuer_serial != issuer.issuer_serial:
                errors[
                    'UnauthorizedSigningCertificateFailure'] = 'OCSP response signed by unauthorized certificate'

            extended_key_usage = signing_cert.extended_key_usage_value
            if 'ocsp_signing' not in extended_key_usage.native:
                errors['ExtendedKeyUsageExtensionValueFailure'] = \
                    'OCSP response signing certificate is not the issuing certificate and it does not have value "ocsp_signing"\
                    for the extended key usage extension'

        sig_algo = parsed_response['signature_algorithm'].signature_algo
        hash_algo = parsed_response['signature_algorithm'].hash_algo
        try:
            check_cert = asymmetric.load_certificate(signing_cert)
            if sig_algo == 'rsassa_pkcs1v15':
                asymmetric.rsa_pkcs1v15_verify(
                    check_cert, parsed_response['signature'].native,
                    tbs_response.dump(), hash_algo)
            elif sig_algo == 'dsa':
                asymmetric.dsa_verify(check_cert,
                                      parsed_response['signature'].native,
                                      tbs_response.dump(), hash_algo)
            elif sig_algo == 'ecdsa':
                asymmetric.ecdsa_verify(check_cert,
                                        parsed_response['signature'].native,
                                        tbs_response.dump(), hash_algo)
            else:
                errors[
                    'UnsupportedAlgorithmFailure'] = 'OCSP response signature uses unsupported algorithm'

        except (oscrypto.errors.SignatureError):
            errors[
                'SignatureVerificationFailure'] = 'OCSP response signature could not be verified'

        if certificate_response['cert_status'].name == 'revoked':
            revocation_data = certificate_response['cert_status'].chosen
            if revocation_data['revocation_reason'].native is None:
                errors[
                    'CertificateValidityFailure'] = 'Certificate revoked due to unknown reason'
            else:
                errors[
                    'CertificateValidityFailure'] = 'Certicate revoked due to ' + revocation_data[
                        'revocation_reason'].human_friendly

        if 'certs' in parsed_response and parsed_response[
                'certs'].native != None:
            #TODO Check for legit certs
            pass


#            print(parsed_response['certs'].native)

        if len(errors) == 0:
            errors['NoFailure'] = 'No errors in OCSP response'
        if len(warnings) == 0:
            warnings['NoWarning'] = 'No warnings in OCSP response'

        lints['errors'] = errors
        lints['warnings'] = warnings
        lints_list.append(lints)

    return lints_list