def create_signup_info(cls,
                           originator_public_key_hash,
                           nonce):
        with cls._lock:
            # First we need to create a public/private key pair for the PoET
            # enclave to use.
            # Counter ID is a placeholder for a hardware counter in a TEE.
            poet_private_key = Secp256k1PrivateKey.new_random()
            poet_public_key = \
                cls._context.get_public_key(poet_private_key)
            counter_id = None

            # Simulate sealing (encrypting) the signup data.
            signup_data = {
                'poet_private_key': poet_private_key.as_hex(),
                'poet_public_key': poet_public_key.as_hex(),
                'counter_id': counter_id
            }
            sealed_signup_data = \
                base64.b64encode(
                    dict2json(signup_data).encode()).decode('utf-8')

            # Build up a fake SGX quote containing:
            # 1. The basename
            # 2. The report body that contains:
            #    a. The enclave measurement
            #    b. The report data SHA256(SHA256(OPK)|PPK)
            sgx_basename = \
                sgx_structs.SgxBasename(name=cls.__VALID_BASENAME__)
            sgx_measurement = \
                sgx_structs.SgxMeasurement(
                    m=cls.__VALID_ENCLAVE_MEASUREMENT__)

            hash_input = \
                '{0}{1}'.format(
                    originator_public_key_hash.upper(),
                    poet_public_key.as_hex().upper()).encode()
            report_data = hashlib.sha256(hash_input).digest()
            sgx_report_data = sgx_structs.SgxReportData(d=report_data)
            sgx_report_body = \
                sgx_structs.SgxReportBody(
                    mr_enclave=sgx_measurement,
                    report_data=sgx_report_data)

            sgx_quote = \
                sgx_structs.SgxQuote(
                    basename=sgx_basename,
                    report_body=sgx_report_body)

            # Create a fake PSE manifest.  A base64 encoding of the
            # originator public key hash should suffice.
            pse_manifest = \
                base64.b64encode(originator_public_key_hash.encode())

            timestamp = datetime.datetime.now().isoformat()

            # Fake our "proof" data.
            verification_report = {
                'epidPseudonym': cls._anti_sybil_id,
                'id': base64.b64encode(
                    hashlib.sha256(
                        timestamp.encode()).hexdigest().encode()).decode(),
                'isvEnclaveQuoteStatus': 'OK',
                'isvEnclaveQuoteBody':
                    base64.b64encode(sgx_quote.serialize_to_bytes()).decode(),
                'pseManifestStatus': 'OK',
                'pseManifestHash':
                    hashlib.sha256(base64.b64decode(pse_manifest)).hexdigest(),
                'nonce': nonce,
                'timestamp': timestamp
            }

            # Serialize the verification report, sign it, and then put
            # in the proof data
            verification_report_json = dict2json(verification_report)
            signature = \
                cls._report_private_key.sign(
                    verification_report_json.encode(),
                    padding.PKCS1v15(),
                    hashes.SHA256())

            proof_data_dict = {
                'evidence_payload': {
                    'pse_manifest': pse_manifest.decode()
                },
                'verification_report': verification_report_json,
                'signature': base64.b64encode(signature).decode()
            }
            proof_data = dict2json(proof_data_dict)

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data,
                    anti_sybil_id=cls._anti_sybil_id,
                    sealed_signup_data=sealed_signup_data)
Example #2
0
    def _verify_signup_info(self, signup_info, originator_public_key_hash,
                            val_reg_payload, context):

        # Verify the attestation verification report signature
        proof_data_dict = json.loads(signup_info.proof_data)
        verification_report = proof_data_dict.get('verification_report')
        if verification_report is None:
            raise ValueError('Verification report is missing from proof data')

        signature = proof_data_dict.get('signature')
        if signature is None:
            raise ValueError('Signature is missing from proof data')

        # Try to get the report key from the configuration setting.  If it
        # is not there or we cannot parse it, fail verification.
        try:
            report_public_key_pem = \
                _get_config_setting(
                    context=context,
                    key='sawtooth.poet.report_public_key_pem')
            report_public_key = \
                serialization.load_pem_public_key(
                    report_public_key_pem.encode(),
                    backend=backends.default_backend())
        except KeyError:
            raise \
                ValueError(
                    'Report public key configuration setting '
                    '(sawtooth.poet.report_public_key_pem) not found.')
        except (TypeError, ValueError) as error:
            raise ValueError('Failed to parse public key: {}'.format(error))

        # Retrieve the valid enclave measurement values, converting the comma-
        # delimited list. If it is not there, or fails to parse correctly,
        # fail verification.
        try:
            valid_measurements = \
                _get_config_setting(
                    context=context,
                    key='sawtooth.poet.valid_enclave_measurements')
            valid_enclave_mesaurements = \
                [bytes.fromhex(m) for m in valid_measurements.split(',')]
        except KeyError:
            raise \
                ValueError(
                    'Valid enclave measurements configuration setting '
                    '(sawtooth.poet.valid_enclave_measurements) not found.')
        except ValueError as error:
            raise \
                ValueError(
                    'Failed to parse enclave measurement: {}'.format(
                        valid_measurements))

        # Retrieve the valid enclave basename value. If it is not there, or
        # fails to parse correctly, fail verification.
        try:
            valid_basenames = \
                _get_config_setting(
                    context=context,
                    key='sawtooth.poet.valid_enclave_basenames')
            valid_enclave_basenames = \
                [bytes.fromhex(b) for b in valid_basenames.split(',')]
        except KeyError:
            raise \
                ValueError(
                    'Valid enclave basenames configuration setting '
                    '(sawtooth.poet.valid_enclave_basenames) not found.')
        except ValueError:
            raise \
                ValueError(
                    'Failed to parse enclave basename: {}'.format(
                        valid_basenames))

        try:
            report_public_key.verify(base64.b64decode(signature.encode()),
                                     verification_report.encode(),
                                     padding.PKCS1v15(), hashes.SHA256())
        except InvalidSignature:
            raise ValueError('Verification report signature is invalid')

        verification_report_dict = json.loads(verification_report)

        # Verify that the verification report contains an ID field
        if 'id' not in verification_report_dict:
            raise ValueError('Verification report does not contain an ID')

        # Verify that the verification report contains an EPID pseudonym and
        # that it matches the anti-Sybil ID
        epid_pseudonym = verification_report_dict.get('epidPseudonym')
        if epid_pseudonym is None:
            raise \
                ValueError(
                    'Verification report does not contain an EPID pseudonym')

        if epid_pseudonym != signup_info.anti_sybil_id:
            raise \
                ValueError(
                    'The anti-Sybil ID in the verification report [{0}] does '
                    'not match the one contained in the signup information '
                    '[{1}]'.format(
                        epid_pseudonym,
                        signup_info.anti_sybil_id))

        # Verify that the verification report contains a PSE manifest status
        # and it is OK
        pse_manifest_status = \
            verification_report_dict.get('pseManifestStatus')
        if pse_manifest_status is None:
            raise \
                ValueError(
                    'Verification report does not contain a PSE manifest '
                    'status')
        if pse_manifest_status.upper() != 'OK':
            if pse_manifest_status.upper() == 'OUT_OF_DATE':
                LOGGER.warning(
                    'Peer has out of date (but not revoked)'
                    ' hardware, pseManifestStatus: %s', pse_manifest_status)
            else:
                raise \
                    ValueError(
                        'PSE manifest status is {} (i.e., not OK)'.format(
                            pse_manifest_status))

        # Verify that the verification report contains a PSE manifest hash
        pse_manifest_hash = \
            verification_report_dict.get('pseManifestHash')
        if pse_manifest_hash is None:
            raise \
                ValueError(
                    'Verification report does not contain a PSE manifest '
                    'hash')

        # Verify that the proof data contains evidence payload
        evidence_payload = proof_data_dict.get('evidence_payload')
        if evidence_payload is None:
            raise ValueError('Evidence payload is missing from proof data')

        # Verify that the evidence payload contains a PSE manifest and then
        # use it to make sure that the PSE manifest hash is what we expect
        pse_manifest = evidence_payload.get('pse_manifest')
        if pse_manifest is None:
            raise ValueError('Evidence payload does not include PSE manifest')

        expected_pse_manifest_hash = \
            hashlib.sha256(
                base64.b64decode(pse_manifest.encode())).hexdigest()
        if pse_manifest_hash.upper() != expected_pse_manifest_hash.upper():
            raise \
                ValueError(
                    'PSE manifest hash {0} does not match {1}'.format(
                        pse_manifest_hash,
                        expected_pse_manifest_hash))

        # Verify that the verification report contains an enclave quote and
        # that its status is OK
        enclave_quote_status = \
            verification_report_dict.get('isvEnclaveQuoteStatus')
        if enclave_quote_status is None:
            raise \
                ValueError(
                    'Verification report does not contain an enclave quote '
                    'status')
        if enclave_quote_status.upper() != 'OK':
            if enclave_quote_status.upper() == 'GROUP_OUT_OF_DATE':
                LOGGER.warning(
                    'Peer has out of date (but not revoked)'
                    ' hardware, isvEnclaveQuoteStatus: %s',
                    str(enclave_quote_status))
            else:
                raise \
                    ValueError(
                        'Enclave quote status is {} (i.e., not OK)'.format(
                            enclave_quote_status))

        # Verify that the verification report contains an enclave quote
        enclave_quote = verification_report_dict.get('isvEnclaveQuoteBody')
        if enclave_quote is None:
            raise \
                ValueError(
                    'Verification report does not contain an enclave quote')

        # The ISV enclave quote body is base 64 encoded, so decode it and then
        # create an SGX quote structure from it so we can inspect
        sgx_quote = sgx_structs.SgxQuote()
        sgx_quote.parse_from_bytes(base64.b64decode(enclave_quote))

        # The report body should be SHA256(SHA256(OPK)|PPK)
        #
        # NOTE - since the code that created the report data is in the enclave
        # code, this code needs to be kept in sync with it.  Any changes to how
        # the report data is created, needs to be reflected in how we re-create
        # the report data for verification.

        hash_input = \
            '{0}{1}'.format(
                originator_public_key_hash.upper(),
                signup_info.poet_public_key.upper()).encode()
        hash_value = hashlib.sha256(hash_input).digest()
        expected_report_data = \
            hash_value + \
            (b'\x00' *
             (sgx_structs.SgxReportData.STRUCT_SIZE - len(hash_value)))

        if sgx_quote.report_body.report_data.d != expected_report_data:
            raise \
                ValueError(
                    'AVR report data [{0}] not equal to [{1}]'.format(
                        sgx_quote.report_body.report_data.d.hex(),
                        expected_report_data.hex()))

        # Verify that the enclave measurement is in the list of valid
        # enclave measurements.
        if sgx_quote.report_body.mr_enclave.m not in \
                valid_enclave_mesaurements:
            raise \
                ValueError(
                    'AVR enclave measurement [{}] not in list of valid '
                    'enclave measurements [{}]'.format(
                        sgx_quote.report_body.mr_enclave.m.hex(),
                        valid_measurements))

        # Verify that the enclave basename is in the list of valid
        # enclave basenames
        if sgx_quote.basename.name not in valid_enclave_basenames:
            raise \
                ValueError(
                    'AVR enclave basename [{}] not in list of valid '
                    'enclave basenames [{}]'.format(
                        sgx_quote.basename.name.hex(),
                        valid_basenames))

        # Verify that the nonce in the verification report matches the nonce
        # in the transaction payload submitted
        nonce = verification_report_dict.get('nonce', '')
        if nonce != val_reg_payload.signup_info.nonce:
            raise \
                ValueError(
                    'AVR nonce [{0}] does not match signup info nonce '
                    '[{1}]'.format(
                        nonce,
                        val_reg_payload.signup_info.nonce))
    def test_invalid_enclave_body(self):
        """
        Test that a transaction whose enclave_body is invalid returns an
        invalid transaction.
        """
        signup_info = self.factory.create_signup_info(
            self.factory.pubkey_hash, "000")

        proof_data = signup_info.proof_data
        proof_data_dict = json.loads(proof_data)

        # ------------------------------------------------------
        # No isvEnclaveQuoteStatus
        verification_report = \
            json.loads(proof_data_dict["verification_report"])
        enclave_status = verification_report["isvEnclaveQuoteStatus"]
        verification_report["isvEnclaveQuoteStatus"] = None

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # Bad isvEnclaveQuoteStatus
        verification_report = \
            json.loads(proof_data_dict["verification_report"])
        verification_report["isvEnclaveQuoteStatus"] = "Bad"

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # No isvEnclaveQuoteBody
        verification_report = \
            json.loads(proof_data_dict["verification_report"])
        verification_report["isvEnclaveQuoteStatus"] = enclave_status
        verification_report['isvEnclaveQuoteBody'] = None

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # Malformed isvEnclaveQuoteBody (decode the enclave quote, chop off
        # the last byte, and re-encode)
        verification_report = \
            json.loads(proof_data_dict["verification_report"])

        verification_report['isvEnclaveQuoteBody'] = \
            base64.b64encode(
                base64.b64decode(
                    verification_report['isvEnclaveQuoteBody'].encode())[1:])\
            .decode()

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # Invalid basename
        verification_report = \
            json.loads(proof_data_dict["verification_report"])

        sgx_quote = sgx_structs.SgxQuote()
        sgx_quote.parse_from_bytes(
            base64.b64decode(
                verification_report['isvEnclaveQuoteBody'].encode()))
        sgx_quote.basename.name = \
            b'\xCC' * sgx_structs.SgxBasename.STRUCT_SIZE

        verification_report['isvEnclaveQuoteBody'] = \
            base64.b64encode(sgx_quote.serialize_to_bytes()).decode()

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # Report data is not valid (bad OPK hash)
        verification_report = \
            json.loads(proof_data_dict["verification_report"])

        sgx_quote = sgx_structs.SgxQuote()
        sgx_quote.parse_from_bytes(
            base64.b64decode(
                verification_report['isvEnclaveQuoteBody'].encode()))

        hash_input = \
            '{0}{1}'.format(
                'Not a valid OPK Hash',
                self.factory.poet_public_key).upper().encode()
        sgx_quote.report_body.report_data.d = \
            hashlib.sha256(hash_input).digest()

        verification_report['isvEnclaveQuoteBody'] = \
            base64.b64encode(sgx_quote.serialize_to_bytes()).decode()

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # Report data is not valid (bad PPK)
        verification_report = \
            json.loads(proof_data_dict["verification_report"])

        sgx_quote = sgx_structs.SgxQuote()
        sgx_quote.parse_from_bytes(
            base64.b64decode(
                verification_report['isvEnclaveQuoteBody'].encode()))

        hash_input = \
            '{0}{1}'.format(
                self.factory.pubkey_hash,
                "Not a valid PPK").encode()
        sgx_quote.report_body.report_data.d = \
            hashlib.sha256(hash_input).digest()

        verification_report['isvEnclaveQuoteBody'] = \
            base64.b64encode(sgx_quote.serialize_to_bytes()).decode()

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)

        # ------------------------------------------------------
        # Invalid enclave measurement
        verification_report = \
            json.loads(proof_data_dict["verification_report"])

        sgx_quote = sgx_structs.SgxQuote()
        sgx_quote.parse_from_bytes(
            base64.b64decode(
                verification_report['isvEnclaveQuoteBody'].encode()))
        sgx_quote.report_body.mr_enclave.m = \
            b'\xCC' * sgx_structs.SgxMeasurement.STRUCT_SIZE

        verification_report['isvEnclaveQuoteBody'] = \
            base64.b64encode(sgx_quote.serialize_to_bytes()).decode()

        signup_info.proof_data = \
            self.factory.create_proof_data(
                verification_report=verification_report,
                evidence_payload=proof_data_dict.get('evidence_payload'))

        self._test_bad_signup_info(signup_info)
Example #4
0
    def verify_signup_info(self, signup_info, originator_public_key_hash,
                           most_recent_wait_certificate_id):

        # Verify the attestation verification report signature
        proof_data_dict = json.loads(signup_info.proof_data)
        verification_report = proof_data_dict.get('verification_report')
        if verification_report is None:
            raise ValueError('Verification report is missing from proof data')

        signature = proof_data_dict.get('signature')
        if signature is None:
            raise ValueError('Signature is missing from proof data')

        try:
            self._report_public_key.verify(
                base64.b64decode(signature.encode()),
                verification_report.encode(), padding.PKCS1v15(),
                hashes.SHA256())
        except InvalidSignature:
            raise ValueError('Verification report signature is invalid')

        verification_report_dict = json.loads(verification_report)

        # Verify that the verification report contains an ID field
        if 'id' not in verification_report_dict:
            raise ValueError('Verification report does not contain an ID')

        # Verify that the verification report contains an EPID pseudonym and
        # that it matches the anti-Sybil ID
        epid_pseudonym = verification_report_dict.get('epidPseudonym')
        if epid_pseudonym is None:
            raise \
                ValueError(
                    'Verification report does not contain an EPID pseudonym')

        if epid_pseudonym != signup_info.anti_sybil_id:
            raise \
                ValueError(
                    'The anti-Sybil ID in the verification report [{0}] does '
                    'not match the one contained in the signup information '
                    '[{1}]'.format(
                        epid_pseudonym,
                        signup_info.anti_sybil_id))

        # Verify that the verification report contains a PSE manifest status
        # and it is OK
        pse_manifest_status = \
            verification_report_dict.get('pseManifestStatus')
        if pse_manifest_status is None:
            raise \
                ValueError(
                    'Verification report does not contain a PSE manifest '
                    'status')
        if pse_manifest_status.upper() != 'OK':
            raise \
                ValueError(
                    'PSE manifest status is {} (i.e., not OK)'.format(
                        pse_manifest_status))

        # Verify that the verification report contains a PSE manifest hash
        pse_manifest_hash = \
            verification_report_dict.get('pseManifestHash')
        if pse_manifest_hash is None:
            raise \
                ValueError(
                    'Verification report does not contain a PSE manifest '
                    'hash')

        # Verify that the proof data contains evidence payload
        evidence_payload = proof_data_dict.get('evidence_payload')
        if evidence_payload is None:
            raise ValueError('Evidence payload is missing from proof data')

        # Verify that the evidence payload contains a PSE manifest and then
        # use it to make sure that the PSE manifest hash is what we expect
        pse_manifest = evidence_payload.get('pse_manifest')
        if pse_manifest is None:
            raise ValueError('Evidence payload does not include PSE manifest')

        expected_pse_manifest_hash = \
            base64.b64encode(
                hashlib.sha256(
                    pse_manifest.encode()).hexdigest().encode()).decode()

        if pse_manifest_hash.upper() != expected_pse_manifest_hash.upper():
            raise \
                ValueError(
                    'PSE manifest hash {0} does not match {1}'.format(
                        pse_manifest_hash,
                        expected_pse_manifest_hash))

        # Verify that the verification report contains an enclave quote and
        # that its status is OK
        enclave_quote_status = \
            verification_report_dict.get('isvEnclaveQuoteStatus')
        if enclave_quote_status is None:
            raise \
                ValueError(
                    'Verification report does not contain an enclave quote '
                    'status')
        if enclave_quote_status.upper() != 'OK':
            raise \
                ValueError(
                    'Enclave quote status is {} (i.e., not OK)'.format(
                        enclave_quote_status))

        # Verify that the verification report contains an enclave quote
        enclave_quote = verification_report_dict.get('isvEnclaveQuoteBody')
        if enclave_quote is None:
            raise \
                ValueError(
                    'Verification report does not contain an enclave quote')

        # The ISV enclave quote body is base 64 encoded, so decode it and then
        # create an SGX quote structure from it so we can inspect
        sgx_quote = sgx_structs.SgxQuote()
        sgx_quote.parse_from_bytes(base64.b64decode(enclave_quote))

        # The report body should be SHA256(SHA256(OPK)|PPK)
        #
        # NOTE - since the code that created the report data is in the enclave
        # code, this code needs to be kept in sync with it.  Any changes to how
        # the report data is created, needs to be reflected in how we re-create
        # the report data for verification.

        hash_input = \
            '{0}{1}'.format(
                originator_public_key_hash.upper(),
                signup_info.poet_public_key.upper().upper()).encode()
        hash_value = hashlib.sha256(hash_input).digest()
        expected_report_data = \
            hash_value + \
            (b'\x00' *
             (sgx_structs.SgxReportData.STRUCT_SIZE - len(hash_value)))

        if sgx_quote.report_body.report_data.d != expected_report_data:
            raise \
                ValueError(
                    'AVR report data [{0}] not equal to [{1}]'.format(
                        sgx_quote.report_body.report_data.d.hex(),
                        expected_report_data.hex()))

        # Compare the enclave measurement against the expected valid enclave
        # measurement.
        #
        # NOTE - this is only a temporary check.  Instead of checking against
        # a predefined enclave measurement value, we should be configured with
        # a set of one or more enclave measurement values that we will
        # consider as valid.

        if sgx_quote.report_body.mr_enclave.m != \
                self.__VALID_ENCLAVE_MEASUREMENT__:
            raise \
                ValueError(
                    'AVR enclave measurement [{0}] not equal to [{1}]'.format(
                        sgx_quote.report_body.mr_enclave.m.hex(),
                        self.__VALID_ENCLAVE_MEASUREMENT__.hex()))

        # Compare the enclave basename in the verification report against the
        # expected enclave basename.
        #
        # NOTE - this is only a temporary check.  Instead of checking against
        # a predefined enclave basenme value, we should be configured with a
        # set of one or more enclave basenames that we will consider as valid.

        if sgx_quote.basename.name != self.__VALID_BASENAME__:
            raise \
                ValueError(
                    'AVR enclave basename [{0}] not equal to [{1}]'.format(
                        sgx_quote.basename.name.hex(),
                        self.__VALID_BASENAME__.hex()))

        # Verify that the wait certificate ID in the verification report
        # matches the provided wait certificate ID.  The wait certificate ID
        # is stored in the nonce field.
        nonce = verification_report_dict.get('nonce')
        if nonce is None:
            raise \
                ValueError(
                    'Verification report does not have a nonce')
    def create_signup_info(cls, originator_public_key_hash,
                           most_recent_wait_certificate_id):
        with cls._lock:
            # First we need to create a public/private key pair for the PoET
            # enclave to use.
            cls._poet_private_key = signing.generate_privkey()
            cls._poet_public_key = \
                signing.generate_pubkey(cls._poet_private_key)
            cls._active_wait_timer = None

            # We are going to fake out sealing the signup data.  Note that
            # the signing module uses strings for both private (WIF encoded)
            # and public (hex encoded) key canonical formats.  Therefore, we
            # don't have to encode before putting in the signup data.  This
            # also means that on the flip side (unsealing signup data and
            # verifying signatures using public keys), we don't have to decode
            # before using.
            signup_data = {
                'poet_public_key': cls._poet_public_key,
                'poet_private_key': cls._poet_private_key
            }
            sealed_signup_data = \
                base64.b64encode(bytes(dict2json(signup_data).encode()))

            # Build up a fake SGX quote containing:
            # 1. The basename
            # 2. The report body that contains:
            #    a. The enclave measurement
            #    b. The report data SHA256(SHA256(OPK)|PPK)
            sgx_basename = \
                sgx_structs.SgxBasename(name=cls.__VALID_BASENAME__)
            sgx_measurement = \
                sgx_structs.SgxMeasurement(
                    m=cls.__VALID_ENCLAVE_MEASUREMENT__)

            hash_input = \
                '{0}{1}'.format(
                    originator_public_key_hash.upper(),
                    cls._poet_public_key.upper()).encode()
            report_data = hashlib.sha256(hash_input).digest()
            sgx_report_data = sgx_structs.SgxReportData(d=report_data)
            sgx_report_body = \
                sgx_structs.SgxReportBody(
                    mr_enclave=sgx_measurement,
                    report_data=sgx_report_data)

            sgx_quote = \
                sgx_structs.SgxQuote(
                    basename=sgx_basename,
                    report_body=sgx_report_body)

            # Create a fake PSE manifest.  A base64 encoding of the
            # originator public key hash should suffice.
            pse_manifest = \
                base64.b64encode(originator_public_key_hash.encode())

            timestamp = datetime.datetime.now().isoformat()

            # Fake our "proof" data.
            verification_report = {
                'epidPseudonym':
                originator_public_key_hash,
                'id':
                base64.b64encode(
                    hashlib.sha256(
                        timestamp.encode()).hexdigest().encode()).decode(),
                'isvEnclaveQuoteStatus':
                'OK',
                'isvEnclaveQuoteBody':
                base64.b64encode(sgx_quote.serialize_to_bytes()).decode(),
                'pseManifestStatus':
                'OK',
                'pseManifestHash':
                base64.b64encode(
                    hashlib.sha256(
                        pse_manifest).hexdigest().encode()).decode(),
                'nonce':
                most_recent_wait_certificate_id,
                'timestamp':
                timestamp
            }

            # Serialize the verification report, sign it, and then put
            # in the proof data
            verification_report_json = dict2json(verification_report)
            signature = \
                cls._report_private_key.sign(
                    verification_report_json.encode(),
                    padding.PKCS1v15(),
                    hashes.SHA256())

            proof_data_dict = {
                'evidence_payload': {
                    'pse_manifest': pse_manifest.decode()
                },
                'verification_report': verification_report_json,
                'signature': base64.b64encode(signature).decode()
            }
            proof_data = dict2json(proof_data_dict)

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data,
                    anti_sybil_id=originator_public_key_hash,
                    sealed_signup_data=sealed_signup_data)
    def do_sgx_quote():
        sgx_quote = sgx_structs.SgxQuote()

        # typedef uint8_t sgx_epid_group_id_t[4];
        # typedef uint16_t sgx_isv_svn_t;

        # uint16_t            version;                /* 0   */
        # uint16_t            sign_type;              /* 2   */
        # sgx_epid_group_id_t epid_group_id;          /* 4   */
        # sgx_isv_svn_t       qe_svn;                 /* 8   */
        # sgx_isv_svn_t       pce_svn;                /* 10  */
        # uint32_t            extended_epid_group_id; /* 12  */
        # sgx_basename_t      basename;               /* 16  */
        # sgx_report_body_t   report_body;            /* 48  */
        # uint32_t            signature_len;          /* 432 */
        # uint8_t             signature[];            /* 436 */

        # Verify sgx_quote unpacks properly
        version = 0x0102
        sign_type = 0x0304
        epid_group_id = create_random_buffer(4)
        qe_svn = 0x0506
        pce_svn = 0x0708
        extended_epid_group_id = 0x090a0b0c
        basename = \
            create_random_buffer(
                sgx_structs.SgxBasename.STRUCT_SIZE)
        svn = \
            create_random_buffer(
                sgx_structs.SgxCpuSvn.STRUCT_SIZE)
        misc_select = 0x0d0e0f10
        reserved1 = b'\x00' * 28
        flags = 0x1112131415161718
        xfrm = 0x191a1b1c1d1e1f20
        mr_enclave = \
            create_random_buffer(
                sgx_structs.SgxMeasurement.STRUCT_SIZE)
        reserved2 = b'\x00' * 32
        mr_signer = \
            create_random_buffer(
                sgx_structs.SgxMeasurement.STRUCT_SIZE)
        reserved3 = b'\x00' * 96
        isv_prod_id = 0x2122
        isv_svn = 0x2324
        reserved4 = b'\x00' * 60
        report_data = \
            create_random_buffer(
                sgx_structs.SgxReportData.STRUCT_SIZE)
        report_body = \
            struct.pack(
                '<{}sL{}sQQ{}s{}s{}s{}sHH{}s{}s'.format(
                    len(svn),
                    len(reserved1),
                    len(mr_enclave),
                    len(reserved2),
                    len(mr_signer),
                    len(reserved3),
                    len(reserved4),
                    len(report_data)
                ),
                svn,
                misc_select,
                reserved1,
                flags,
                xfrm,
                mr_enclave,
                reserved2,
                mr_signer,
                reserved3,
                isv_prod_id,
                isv_svn,
                reserved4,
                report_data)
        quote = \
            struct.pack(
                '<HH{}sHHL{}s{}s'.format(
                    len(epid_group_id),
                    len(basename),
                    len(report_body)),
                version,
                sign_type,
                epid_group_id,
                qe_svn,
                pce_svn,
                extended_epid_group_id,
                basename,
                report_body)

        sgx_quote.parse_from_bytes(quote)

        # Add a few bytes so that the signature length is partial.  Should
        # fail.
        bad_quote = quote[:]
        for _ in range(3):
            bad_quote += create_random_buffer(1)
            with pytest.raises(ValueError):
                sgx_quote.parse_from_bytes(bad_quote)

        # Add a non-zero signature length, but don't add a signature.  Should
        # fail.
        bad_quote = struct.pack('<{}sL'.format(len(quote)), quote, 4)
        with pytest.raises(ValueError):
            sgx_quote.parse_from_bytes(bad_quote)

        # Add a signature that is too short
        short_signature = create_random_buffer(31)
        bad_quote = \
            struct.pack(
                '<{}sL{}s'.format(
                    len(quote),
                    len(short_signature)),
                quote,
                len(short_signature) + 1,
                short_signature)
        with pytest.raises(ValueError):
            sgx_quote.parse_from_bytes(bad_quote)

        # Add a signature length of zero without a signature and verify
        unsigned_quote = struct.pack('<{}sL'.format(len(quote)), quote, 0)

        sgx_quote.parse_from_bytes(unsigned_quote)

        # Add a good signature and verify
        signature = create_random_buffer(32)
        signed_quote = \
            struct.pack(
                '<{}sL{}s'.format(
                    len(quote),
                    len(signature)),
                quote,
                len(signature),
                signature)

        sgx_quote.parse_from_bytes(signed_quote)

        # Reset the object using the field values and verify that we
        # get expected serialized buffer
        sgx_basename = sgx_structs.SgxBasename(name=basename)
        sgx_cpu_svn = sgx_structs.SgxCpuSvn(svn=svn)
        sgx_attributes = sgx_structs.SgxAttributes(flags=flags, xfrm=xfrm)
        sgx_measurement_mr_enclave = sgx_structs.SgxMeasurement(m=mr_enclave)
        sgx_measurement_mr_signer = sgx_structs.SgxMeasurement(m=mr_signer)
        sgx_report_data = sgx_structs.SgxReportData(d=report_data)
        sgx_report_body = \
            sgx_structs.SgxReportBody(
                cpu_svn=sgx_cpu_svn,
                misc_select=misc_select,
                attributes=sgx_attributes,
                mr_enclave=sgx_measurement_mr_enclave,
                mr_signer=sgx_measurement_mr_signer,
                isv_prod_id=isv_prod_id,
                isv_svn=isv_svn,
                report_data=sgx_report_data)
        sgx_quote = sgx_structs.SgxQuote(
            version=version,
            sign_type=sign_type,
            epid_group_id=epid_group_id,
            qe_svn=qe_svn,
            pce_svn=pce_svn,
            extended_epid_group_id=extended_epid_group_id,
            basename=sgx_basename,
            report_body=sgx_report_body)

        assert quote == sgx_quote.serialize_to_bytes()[:len(quote)]

        # Verify that zero signature serializes successfully
        zero_sign_quote = struct.pack('<{}sL'.format(len(quote)), quote, 0)

        assert zero_sign_quote == sgx_quote.serialize_to_bytes()

        # Verify that non-zero signature serializes successfully
        non_zero_sign_quote = \
            struct.pack(
                '<{}sL{}s'.format(
                    len(quote),
                    len(signature)),
                quote,
                len(signature),
                signature)
        sgx_quote.signature_len = len(signature)
        sgx_quote.signature = signature
        assert non_zero_sign_quote == sgx_quote.serialize_to_bytes()
Example #7
0
    def create_signup_info(self, originator_public_key_hash, nonce,
                           pse_manifest_status='OK'):
        # currently not used
        # _active_wait_timer = None

        # We are going to fake out the sealing the signup data.
        signup_data = {
            'poet_public_key': self.poet_public_key,
            'poet_private_key': self._poet_private_key
        }

        # Build up a fake SGX quote containing:
        # 1. The basename
        # 2. The report body that contains:
        #    a. The enclave measurement
        #    b. The report data SHA256(SHA256(OPK)|PPK)
        sgx_basename = \
            sgx_structs.SgxBasename(name=self.__VALID_BASENAME__)
        sgx_measurement = \
            sgx_structs.SgxMeasurement(
                m=self.__VALID_ENCLAVE_MEASUREMENT__)

        hash_input = \
            '{0}{1}'.format(
                originator_public_key_hash.upper(),
                self.poet_public_key.upper()).encode()
        report_data = hashlib.sha256(hash_input).digest()

        sgx_report_data = sgx_structs.SgxReportData(d=report_data)
        sgx_report_body = \
            sgx_structs.SgxReportBody(
                mr_enclave=sgx_measurement,
                report_data=sgx_report_data)

        sgx_quote = \
            sgx_structs.SgxQuote(
                basename=sgx_basename,
                report_body=sgx_report_body)

        # Create a fake PSE manifest.  A base64 encoding of the
        # originator public key hash should suffice.
        pse_manifest = \
            base64.b64encode(originator_public_key_hash.encode())

        timestamp = '2017-02-16T15:21:24.437048'

        # Fake our "proof" data.
        verification_report = OrderedDict([
            ('epidPseudonym', originator_public_key_hash),
            ('id', base64.b64encode(
                hashlib.sha256(
                    timestamp.encode()).hexdigest().encode()).decode()),
            ('isvEnclaveQuoteStatus', 'OK'),
            ('isvEnclaveQuoteBody',
                base64.b64encode(sgx_quote.serialize_to_bytes()).decode()),
            ('pseManifestStatus', pse_manifest_status),
            ('pseManifestHash',
                hashlib.sha256(base64.b64decode(pse_manifest)).hexdigest()),
            ('nonce', nonce),
            ('timestamp', timestamp)
        ])

        proof_data = \
            self.create_proof_data(
                verification_report=verification_report,
                evidence_payload={
                    'pse_manifest': pse_manifest.decode()
                })

        return \
            SignUpInfo(
                poet_public_key=signup_data['poet_public_key'],
                proof_data=proof_data,
                anti_sybil_id=originator_public_key_hash,
                nonce=nonce)