コード例 #1
0
ファイル: base_page.py プロジェクト: danmack/sawtooth-core
    def do_post(self, request):
        """
        Handle two types of HTTP POST requests:
         - gossip messages.  relayed to the gossip network as is
         - validator command and control (/command)
        """

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        try:
            response = self.render_post(request, components, request.args)

            encoding = request.getHeader('Content-Type')
            request.responseHeaders.addRawHeader("content-type", encoding)
            if encoding == 'application/json':
                result = dict2json(response)
            else:
                result = dict2cbor(response)

            return result
        except Exception as e:
            LOGGER.warn('error processing http request %s; %s; %s',
                        request.path,
                        str(e),
                        traceback.format_exc(20))
            return self._encode_error_response(
                request,
                http.INTERNAL_SERVER_ERROR,
                e)
コード例 #2
0
    def do_post(self, request):
        """
        Handle two types of HTTP POST requests:
         - gossip messages.  relayed to the gossip network as is
         - validator command and control (/command)
        """

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        try:
            response = self.render_post(request, components, request.args)

            encoding = request.getHeader('Content-Type')
            request.responseHeaders.addRawHeader("content-type", encoding)
            if encoding == 'application/json':
                result = dict2json(response)
            else:
                result = dict2cbor(response)

            return result
        except Exception as e:
            LOGGER.warn('error processing http request %s; %s; %s',
                        request.path, str(e), traceback.format_exc(20))
            return self._encode_error_response(request,
                                               http.INTERNAL_SERVER_ERROR, e)
コード例 #3
0
    def do_get(self, request):
        """
        Handle a GET request on the HTTP interface. Three paths are accepted:
            /store[/<storename>[/<key>|*]]
            /block[/<blockid>]
            /transaction[/<txnid>]
        """
        # pylint: disable=invalid-name

        # split the request path removing leading duplicate slashes
        LOGGER.info("%s.do_get %s", self.__class__.__name__, request.path)
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        if components[0] != self.page_name:
            return self.error_response(request, http.NOT_FOUND,
                                       "Invalid page name: {}", request.path)
        else:
            components.pop(0)

        test_only = (request.method == 'HEAD')
        try:
            response = self.render_get(request, components, request.args)
            if test_only:
                return ''

            cbor = (request.getHeader('Accept') == 'application/cbor')

            if cbor:
                request.responseHeaders.addRawHeader(b"content-type",
                                                     b"application/cbor")
                return dict2cbor(response)

            request.responseHeaders.addRawHeader(b"content-type",
                                                 b"application/json")
            pretty = False
            if 'p' in request.args:
                pretty = request.args['p'] == ['1']

            if pretty:
                result = pretty_print_dict(response) + '\n'
            else:
                result = dict2json(response)

            return result

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing http request {0}; {1}',
                request.path, str(e))

        except:
            LOGGER.warn('error processing http request %s; %s', request.path,
                        traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #4
0
    def render_GET(self, request):
        """
        Handle a GET request on the HTTP interface. Three paths are accepted:
            /store[/<storename>[/<key>|*]]
            /block[/<blockid>]
            /transaction[/<txnid>]
        """
        # pylint: disable=invalid-name

        # split the request path removing leading duplicate slashes
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        prefix = components.pop(0) if components else 'error'

        if prefix not in self.GetPageMap:
            return self.error_response(request, http.BAD_REQUEST,
                                       'unknown request {0}', request.path)

        testonly = (request.method == 'HEAD')

        try:
            response = self.GetPageMap[prefix](components, request.args,
                                               testonly)

            if testonly:
                return ''

            cbor = (request.getHeader('Accept') == 'application/cbor')

            if cbor:
                request.responseHeaders.addRawHeader(b"content-type",
                                                     b"application/cbor")
                return dict2cbor(response)

            request.responseHeaders.addRawHeader(b"content-type",
                                                 b"application/json")

            pretty = 'p' in request.args
            if pretty:
                result = pretty_print_dict(response) + '\n'
            else:
                result = dict2json(response)

            return result

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing http request {0}; {1}',
                request.path, str(e))

        except:
            logger.warn('error processing http request %s; %s', request.path,
                        traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #5
0
ファイル: web_api.py プロジェクト: trbs/sawtooth-validator
    def do_get(self, request):
        """
        Handle a GET request on the HTTP interface. Three paths are accepted:
            /store[/<storename>[/<key>|*]]
            /block[/<blockid>]
            /transaction[/<txnid>]
        """
        # pylint: disable=invalid-name

        # split the request path removing leading duplicate slashes
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        prefix = components.pop(0) if components else 'error'

        if prefix not in self.GetPageMap:
            # attempt to serve static content if present.
            resource = self.static_content.getChild(request.path[1:], request)
            return resource.render(request)

        test_only = (request.method == 'HEAD')

        try:
            response = self.GetPageMap[prefix](components, request.args,
                                               test_only)
            if test_only:
                return ''

            cbor = (request.getHeader('Accept') == 'application/cbor')

            if cbor:
                request.responseHeaders.addRawHeader(b"content-type",
                                                     b"application/cbor")
                return dict2cbor(response)

            request.responseHeaders.addRawHeader(b"content-type",
                                                 b"application/json")

            pretty = 'p' in request.args
            if pretty:
                result = pretty_print_dict(response) + '\n'
            else:
                result = dict2json(response)

            return result

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing http request {0}; {1}',
                request.path, str(e))

        except:
            logger.warn('error processing http request %s; %s', request.path,
                        traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #6
0
    def create_signup_info(cls, originator_public_key):
        # First we need to create a public/private key pair for the PoET
        # enclave to use.
        cls._poet_private_key = pybitcointools.random_key()
        cls._poet_public_key = pybitcointools.privtopub(cls._poet_private_key)
        cls._active_wait_timer = None

        # We are going to fake out the sealing the signup data.
        signup_data = {
            'poet_public_key':
            pybitcointools.encode_pubkey(cls._poet_public_key, 'hex'),
            'poet_private_key':
            pybitcointools.encode_privkey(cls._poet_private_key, 'hex')
        }
        sealed_signup_data = \
            pybitcointools.base64.b32encode(dict2json(signup_data))

        # Create a fake report
        report_data = {
            'originator_public_key_hash':
            pybitcointools.sha256(
                pybitcointools.encode_pubkey(originator_public_key, 'hex')),
            'poet_public_key':
            pybitcointools.encode_pubkey(cls._poet_public_key, 'hex')
        }
        report = {'report_data': pybitcointools.sha256(dict2json(report_data))}
        report = pybitcointools.base64.b32encode(dict2json(report))

        # Fake our "proof" data.
        proof_data = {
            'attestation_evidence_payload':
            pybitcointools.sha256(report),
            'attestation_verification_report':
            pybitcointools.sha256('Shave and a haircut...Two bits!')
        }

        return \
            EnclaveSignupInfo(
                anti_sybil_id='Sally Field',
                poet_public_key=signup_data['poet_public_key'],
                proof_data=proof_data,
                sealed_signup_data=sealed_signup_data)
コード例 #7
0
ファイル: base_page.py プロジェクト: danmack/sawtooth-core
    def do_get(self, request):
        """
        Handle a GET request on the HTTP interface. Three paths are accepted:
            /store[/<storename>[/<key>|*]]
            /block[/<blockid>]
            /transaction[/<txnid>]
        """
        # pylint: disable=invalid-name

        # split the request path removing leading duplicate slashes
        LOGGER.info("%s.do_get %s", self.__class__.__name__, request.path)
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        if components[0] != self.page_name:
            return self._error_response(
                request,
                http.NOT_FOUND,
                "Invalid page name: {}", request.path)
        else:
            components.pop(0)

        test_only = (request.method == 'HEAD')
        try:
            response = self.render_get(request, components, request.args)
            if test_only:
                return ''

            cbor = (request.getHeader('Accept') == 'application/cbor')
            if cbor:
                request.responseHeaders.addRawHeader(b"content-type",
                                                     b"application/cbor")
                result = dict2cbor(response)
            else:
                request.responseHeaders.addRawHeader(b"content-type",
                                                     b"application/json")
                pretty = False
                if 'p' in request.args:
                    pretty = request.args['p'] == ['1']

                if pretty:
                    result = pretty_print_dict(response) + '\n'
                else:
                    result = dict2json(response)

            return result
        except Exception as e:
            LOGGER.warn('error processing http request %s; %s', request.path,
                        traceback.format_exc(20))
            return self._encode_error_response(
                request,
                http.INTERNAL_SERVER_ERROR,
                e)
コード例 #8
0
    def is_valid(self, store):
        if not super(Register, self).is_valid(store):
            logger.debug('something weird happened; %s',
                         dict2json(self.dump()))
            return False

        if not self.is_permitted(store):
            logger.debug('failed permission check on offer %s', self.ObjectID)
            return False

        if not liability_update.LiabilityObject.is_valid_object(
                store, self.InputID):
            logger.debug('input liability %s is not valid in offer %s ',
                         self.InputID, self.ObjectID)
            return False

        obj = liability_update.LiabilityObject.get_valid_object(
            store, self.InputID)
        if not self.CreatorType.is_valid_creator(store, obj.get('creator'),
                                                 self.OriginatorID):
            logger.info('%s does not have permission to modify liability %s',
                        self.OriginatorID, self.InputID)
            return False

        if not liability_update.LiabilityObject.is_valid_object(
                store, self.OutputID):
            logger.debug('output liability %s is not valid in offer %s ',
                         self.OutputID, self.ObjectID)
            return False

        obj = liability_update.LiabilityObject.get_valid_object(
            store, self.OutputID)
        if not self.CreatorType.is_valid_creator(store, obj.get('creator'),
                                                 self.OriginatorID):
            logger.info('%s does not have permission to modify liability %s',
                        self.OriginatorID, self.OutputID)
            return False

        if self.Ratio <= 0:
            logger.debug('invalid ratio %s in offer %s', self.Ratio,
                         self.ObjectID)
            return False

        if self.Minimum < 0 or self.Maximum < 0 or self.Maximum < self.Minimum:
            logger.debug('inconsistent range %s < %s in offer %s',
                         self.Minimum, self.Maximum, self.ObjectID)
            return False

        if self.Execution not in ExchangeOfferObject.ExecutionStyle:
            logger.debug('invalid execution style %s in offer %s',
                         self.Execution, self.ObjectID)
            return False

        return True
コード例 #9
0
    def is_valid(self, store):
        if not super(Register, self).is_valid(store):
            logger.debug('something weird happened; %s',
                         dict2json(self.dump()))
            return False

        if not self.is_permitted(store):
            logger.debug('failed permission check on offer %s', self.ObjectID)
            return False

        if not liability_update.LiabilityObject.is_valid_object(store,
                                                                self.InputID):
            logger.debug('input liability %s is not valid in offer %s ',
                         self.InputID, self.ObjectID)
            return False

        obj = liability_update.LiabilityObject.get_valid_object(store,
                                                                self.InputID)
        if not self.CreatorType.is_valid_creator(store, obj.get('creator'),
                                                 self.OriginatorID):
            logger.info('%s does not have permission to modify liability %s',
                        self.OriginatorID, self.InputID)
            return False

        if not liability_update.LiabilityObject.is_valid_object(store,
                                                                self.OutputID):
            logger.debug('output liability %s is not valid in offer %s ',
                         self.OutputID, self.ObjectID)
            return False

        obj = liability_update.LiabilityObject.get_valid_object(store,
                                                                self.OutputID)
        if not self.CreatorType.is_valid_creator(store, obj.get('creator'),
                                                 self.OriginatorID):
            logger.info('%s does not have permission to modify liability %s',
                        self.OriginatorID, self.OutputID)
            return False

        if self.Ratio <= 0:
            logger.debug('invalid ratio %s in offer %s', self.Ratio,
                         self.ObjectID)
            return False

        if self.Minimum < 0 or self.Maximum < 0 or self.Maximum < self.Minimum:
            logger.debug('inconsistent range %s < %s in offer %s',
                         self.Minimum, self.Maximum, self.ObjectID)
            return False

        if self.Execution not in ExchangeOfferObject.ExecutionStyle:
            logger.debug('invalid execution style %s in offer %s',
                         self.Execution, self.ObjectID)
            return False

        return True
コード例 #10
0
    def postmsg(self, msgtype, info):
        """
        Post a transaction message to the validator, parse the returning CBOR
        and return the corresponding dictionary.
        """

        logger.debug(dict2json(info))

        data = dict2cbor(info)
        datalen = len(data)
        url = self.BaseURL + msgtype

        logger.debug(
            'post transaction to %s with DATALEN=%d, '
            'base64(DATA)=<%s>', url, datalen, base64.b64encode(data))

        try:
            request = urllib2.Request(url, data, {
                'Content-Type': 'application/cbor',
                'Content-Length': datalen
            })
            opener = urllib2.build_opener(self.ProxyHandler)
            response = opener.open(request, timeout=10)

        except urllib2.HTTPError as err:
            logger.warn('operation failed with response: %s', err.code)
            raise MessageException(
                'operation failed with response: {0}'.format(err.code))

        except urllib2.URLError as err:
            logger.warn('operation failed: %s', err.reason)
            raise MessageException('operation failed: {0}'.format(err.reason))

        except:
            logger.warn('no response from server')
            raise MessageException('no response from server')

        content = response.read()
        headers = response.info()
        response.close()

        encoding = headers.get('Content-Type')

        if encoding == 'application/json':
            value = json2dict(content)
        elif encoding == 'application/cbor':
            value = cbor2dict(content)
        else:
            logger.info('server responds with message %s of type %s', content,
                        encoding)
            return None

        logger.debug(pretty_print_dict(value))
        return value
コード例 #11
0
    def postmsg(self, msgtype, info):
        """
        Post a transaction message to the validator, parse the returning CBOR
        and return the corresponding dictionary.
        """

        logger.debug(dict2json(info))

        data = dict2cbor(info)
        datalen = len(data)
        url = self.BaseURL + msgtype

        logger.debug('post transaction to %s with DATALEN=%d, '
                     'base64(DATA)=<%s>', url, datalen, base64.b64encode(data))

        try:
            request = urllib2.Request(url, data,
                                      {'Content-Type': 'application/cbor',
                                       'Content-Length': datalen})
            opener = urllib2.build_opener(self.ProxyHandler)
            response = opener.open(request, timeout=10)

        except urllib2.HTTPError as err:
            logger.warn('operation failed with response: %s', err.code)
            raise MessageException(
                'operation failed with response: {0}'.format(err.code))

        except urllib2.URLError as err:
            logger.warn('operation failed: %s', err.reason)
            raise MessageException('operation failed: {0}'.format(err.reason))

        except:
            logger.warn('no response from server')
            raise MessageException('no response from server')

        content = response.read()
        headers = response.info()
        response.close()

        encoding = headers.get('Content-Type')

        if encoding == 'application/json':
            value = json2dict(content)
        elif encoding == 'application/cbor':
            value = cbor2dict(content)
        else:
            logger.info('server responds with message %s of type %s', content,
                        encoding)
            return None

        logger.debug(pretty_print_dict(value))
        return value
コード例 #12
0
    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 = pybitcointools.random_key()
            cls._poet_public_key = \
                pybitcointools.privtopub(cls._poet_private_key)
            cls._active_wait_timer = None

            # We are going to fake out the sealing the signup data.
            signup_data = {
                'poet_public_key':
                pybitcointools.encode_pubkey(cls._poet_public_key, 'hex'),
                'poet_private_key':
                pybitcointools.encode_privkey(cls._poet_private_key, 'hex')
            }
            sealed_signup_data = \
                pybitcointools.base64.b64encode(dict2json(signup_data))

            # Create a fake report
            report_data = {
                'originator_public_key_hash':
                originator_public_key_hash.upper(),
                'poet_public_key':
                pybitcointools.encode_pubkey(cls._poet_public_key,
                                             'hex').upper()
            }
            report = {
                'report_data': pybitcointools.sha256(dict2json(report_data))
            }

            # Fake our "proof" data.
            verification_report = {
                'enclave_quote':
                pybitcointools.base64.b64encode(dict2json(report)),
                'pse_manifest_hash':
                pybitcointools.base64.b64encode(
                    pybitcointools.sha256('manifest destiny')),
                'nonce':
                most_recent_wait_certificate_id
            }

            proof_data = {
                'verification_report':
                dict2json(verification_report),
                'signature':
                pybitcointools.ecdsa_sign(dict2json(verification_report),
                                          cls._report_private_key)
            }
            proof_data_dict = dict2json(proof_data)

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data_dict,
                    anti_sybil_id=originator_public_key_hash,
                    sealed_signup_data=sealed_signup_data)
コード例 #13
0
    def render_post(self, request, components, msg):
        """
        Process validator control commands
        """
        encoding = request.getHeader('Content-Type')
        data = request.content.getvalue()

        try:
            if encoding == 'application/json':
                minfo = json2dict(data)
            else:
                raise Error(
                    "", 'unknown message'
                    ' encoding: {0}'.format(encoding))
        except Error as e:
            LOGGER.info('exception while decoding http request %s; %s',
                        request.path, traceback.format_exc(20))
            raise Error(http.BAD_REQUEST,
                        'unable to decode incoming request {0}'.format(str(e)))

        # process /command
        try:
            if minfo['action'] == 'start':
                if self.Validator.delaystart is True:
                    self.Validator.delaystart = False
                    LOGGER.info("command received : %s", minfo['action'])
                    minfo['action'] = 'started'
                else:
                    LOGGER.warn("validator startup not delayed")
                    minfo['action'] = 'running'
            else:
                LOGGER.warn("unknown command received")
                minfo['action'] = 'startup failed'

            request.responseHeaders.addRawHeader("content-type", encoding)
            result = dict2json(minfo)
            return result
        except Error as e:
            raise Error(
                int(e.status), 'exception while processing'
                ' request {0}; {1}'.format(request.path, str(e)))

        except:
            LOGGER.info('exception while processing http request %s; %s',
                        request.path, traceback.format_exc(20))
            raise Error(
                http.BAD_REQUEST, 'error processing http request'
                ' {0}'.format(request.path))

        return msg
コード例 #14
0
    def render_post(self, request, components, msg):
        """
        Process validator control commands
        """
        encoding = request.getHeader('Content-Type')
        data = request.content.getvalue()

        try:
            if encoding == 'application/json':
                minfo = json2dict(data)
            else:
                raise Error("", 'unknown message'
                            ' encoding: {0}'.format(encoding))
        except Error as e:
            LOGGER.info('exception while decoding http request %s; %s',
                        request.path, traceback.format_exc(20))
            raise Error(http.BAD_REQUEST,
                        'unable to decode incoming request {0}'.format(str(e)))

        # process /command
        try:
            if minfo['action'] == 'start':
                if self.Validator.delaystart is True:
                    self.Validator.delaystart = False
                    LOGGER.info("command received : %s", minfo['action'])
                    minfo['action'] = 'started'
                else:
                    LOGGER.warn("validator startup not delayed")
                    minfo['action'] = 'running'
            else:
                LOGGER.warn("unknown command received")
                minfo['action'] = 'startup failed'

            request.responseHeaders.addRawHeader("content-type", encoding)
            result = dict2json(minfo)
            return result
        except Error as e:
            raise Error(int(e.status),
                        'exception while processing'
                        ' request {0}; {1}'.format(request.path, str(e)))

        except:
            LOGGER.info('exception while processing http request %s; %s',
                        request.path, traceback.format_exc(20))
            raise Error(http.BAD_REQUEST,
                        'error processing http request'
                        ' {0}'.format(request.path))

        return msg
コード例 #15
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveWaitTimer object

        Returns:
            A JSON string representing the serialized version of the object
        """
        timer_dict = {
            'request_time': self.request_time,
            'duration': self.duration,
            'previous_certificate_id': self.previous_certificate_id,
            'local_mean': self.local_mean
        }

        return dict2json(timer_dict)
コード例 #16
0
 def test_register_validator_key_mismatch(self):
     key = signed_object.generate_signing_key()
     key2 = signed_object.generate_signing_key()
     validator_id = signed_object.generate_identifier(key)
     name = 'DasValidator'
     signup_info = dict2json({'poet_public_key': 'fake_key',
                              'anti_sybil_id': 'some_token',
                              'proof_data': 'proof'})
     store = KeyValueStore()
     transaction = ValidatorRegistryTransaction.register_validator(
         name, validator_id, signup_info)
     transaction.sign_object(key2)
     with self.assertRaises(InvalidTransactionError):
         transaction.check_valid(store)
         transaction.apply(store)
         self.fail("Failure: Verified an invalid transaction")
コード例 #17
0
 def test_register_validator_key_mismatch(self):
     key = signed_object.generate_signing_key()
     key2 = signed_object.generate_signing_key()
     validator_id = signed_object.generate_identifier(key)
     name = 'DasValidator'
     signup_info = dict2json({'poet_pubkey': 'fake_key',
                              'anti_sybil_id': 'some_token',
                              'proof_data': 'proof'})
     store = KeyValueStore()
     transaction = ValidatorRegistryTransaction.register_validator(
         name, validator_id, signup_info)
     transaction.sign_object(key2)
     with self.assertRaises(InvalidTransactionError):
         transaction.check_valid(store)
         transaction.apply(store)
         self.fail("Failure: Verified an invalid transaction")
コード例 #18
0
 def test_register_validator_valid(self):
     key = signed_object.generate_signing_key()
     validator_id = signed_object.generate_identifier(key)
     name = 'DasValidator'
     signup_info = dict2json({'poet_public_key': 'fake_key',
                              'anti_sybil_id': 'some_token',
                              'proof_data': 'proof'})
     store = KeyValueStore()
     transaction = ValidatorRegistryTransaction.register_validator(
         name, validator_id, signup_info)
     transaction.sign_object(key)
     try:
         transaction.check_valid(store)
         transaction.apply(store)
     except InvalidTransactionError:
         self.fail("Bad: Failed valid transaction")
コード例 #19
0
 def test_register_validator_valid(self):
     key = signed_object.generate_signing_key()
     validator_id = signed_object.generate_identifier(key)
     name = 'DasValidator'
     signup_info = dict2json({'poet_pubkey': 'fake_key',
                              'anti_sybil_id': 'some_token',
                              'proof_data': 'proof'})
     store = KeyValueStore()
     transaction = ValidatorRegistryTransaction.register_validator(
         name, validator_id, signup_info)
     transaction.sign_object(key)
     try:
         transaction.check_valid(store)
         transaction.apply(store)
     except InvalidTransactionError:
         self.fail("Bad: Failed valid transaction")
コード例 #20
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveSignupInfo object

        Returns:
            A JSON string representing the serialized version of the object
        """
        signup_info_dict = {
            'anti_sybil_id': self.anti_sybil_id,
            'poet_public_key': self.poet_public_key,
            'proof_data': self.proof_data,
            'sealed_signup_data': self.sealed_signup_data
        }

        return dict2json(signup_info_dict)
コード例 #21
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveWaitCertificate object

        Returns:
            A JSON string representing the serialized version of the object
        """
        certificate_dict = {
            'duration': self.duration,
            'previous_certificate_id': self.previous_certificate_id,
            'local_mean': self.local_mean,
            'request_time': self.request_time,
            'nonce': self.nonce,
            'block_digest': self.block_digest
        }

        return dict2json(certificate_dict)
コード例 #22
0
    def verify_signup_info(cls, signup_info, originator_public_key_hash,
                           most_recent_wait_certificate_id):
        # Verify the attestation verification report signature
        proof_data = json2dict(signup_info.proof_data)
        verification_report = proof_data.get('verification_report')
        if verification_report is None:
            raise \
                SignupInfoError(
                    'Verification report is missing from proof data')

        if not pybitcointools.ecdsa_verify(verification_report,
                                           proof_data.get('signature'),
                                           cls._report_public_key):
            raise \
                SignupInfoError('Verification report signature is invalid')

        # Verify that the report data field in the report contains the SHA256
        # digest of the originator's public key SHA 256 digest and the PoET
        # public key.
        verification_report_dict = json2dict(verification_report)

        enclave_quote = verification_report_dict.get('enclave_quote')
        if enclave_quote is None:
            raise \
                SignupInfoError(
                    'Verification report does not contain an enclave quote')

        report = json2dict(pybitcointools.base64.b64decode(enclave_quote))
        report_data = report.get('report_data')
        if report_data is None:
            raise \
                SignupInfoError('Enclave quote does not contain report data')

        target_report_data = {
            'originator_public_key_hash': originator_public_key_hash.upper(),
            'poet_public_key': signup_info.poet_public_key.upper()
        }
        target_report_data_digest = \
            pybitcointools.sha256(dict2json(target_report_data))

        if report_data != target_report_data_digest:
            raise SignupInfoError('Enclave quote report data is invalid')
コード例 #23
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveSignupInfo object

        Returns:
            A JSON string representing the serialized version of the object.
            Note that the sealed signup data is not encluded in the serialized
            data.
        """

        # Note - we are not serializing the sealed signup data.  Sealed signup
        # data is meant to be used locally on the system and not serialized
        # and sent to anyone else.
        signup_info_dict = {
            'poet_public_key': self.poet_public_key,
            'proof_data': self.proof_data
        }

        return dict2json(signup_info_dict)
コード例 #24
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveSignupInfo object

        Returns:
            A JSON string representing the serialized version of the object.
            Note that the sealed signup data is not encluded in the serialized
            data.
        """

        # Note - we are not serializing the sealed signup data.  Sealed signup
        # data is meant to be used locally on the system and not serialized
        # and sent to anyone else.
        signup_info_dict = {
            'poet_public_key': self.poet_public_key,
            'proof_data': self.proof_data
        }

        return dict2json(signup_info_dict)
コード例 #25
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveWaitTimer object

        Returns:
            A JSON string representing the serialized version of the object
        """
        if self._serialized is None:
            timer_dict = {
                'request_time': self.request_time,
                'validator_address': self.validator_address,
                'duration': self.duration,
                'previous_certificate_id': self.previous_certificate_id,
                'local_mean': self.local_mean
            }

            self._serialized = dict2json(timer_dict)

        return self._serialized
コード例 #26
0
    def serialize(self):
        """
        Serializes to JSON that can later be reconstituted to an
        EnclaveWaitCertificate object

        Returns:
            A JSON string representing the serialized version of the object
        """
        if self._serialized is None:
            certificate_dict = {
                'duration': self.duration,
                'previous_certificate_id': self.previous_certificate_id,
                'local_mean': self.local_mean,
                'request_time': self.request_time,
                'nonce': self.nonce,
                'block_digest': self.block_digest
            }

            self._serialized = dict2json(certificate_dict)

        return self._serialized
コード例 #27
0
    def do_post(self, request):
        """
        Handle two types of HTTP POST requests:
         - gossip messages.  relayed to the gossip network as is
         - validator command and control (/command)
        """

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        try:
            response = self.render_post(request, components, request.args)

            encoding = request.getHeader('Content-Type')
            request.responseHeaders.addRawHeader("content-type", encoding)
            if encoding == 'application/json':
                result = dict2json(response.dump())
            else:
                result = dict2cbor(response.dump())

            return result

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing request {0}; {1}',
                request.path, str(e))

        except:
            LOGGER.info('exception while processing http request %s; %s',
                        request.path, traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #28
0
    def do_post(self, request):
        """
        Handle two types of HTTP POST requests:
         - gossip messages.  relayed to the gossip network as is
         - validator command and control (/command)
        """

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        try:
            response = self.render_post(request, components, request.args)

            encoding = request.getHeader('Content-Type')
            request.responseHeaders.addRawHeader("content-type", encoding)
            if encoding == 'application/json':
                result = dict2json(response.dump())
            else:
                result = dict2cbor(response.dump())

            return result

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing request {0}; {1}', request.path,
                str(e))

        except:
            LOGGER.info('exception while processing http request %s; %s',
                        request.path, traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #29
0
    def create_wait_certificate(cls, block_digest):
        with cls._lock:
            # If we don't have a PoET private key, then the enclave has not
            # been properly initialized (either by calling create_signup_info
            # or unseal_signup_data)
            if cls._poet_private_key is None:
                raise \
                    WaitCertificateError(
                        'Enclave must be initialized before attempting to '
                        'create a wait certificate')

            # Several criteria need to be met before we can create a wait
            # certificate:
            # 1. We have an active timer
            # 2. The active timer has expired
            # 3. The active timer has not timed out
            #
            # Note - we make a concession for the genesis block (i.e., a wait
            # timer for which the previous certificate ID is the Null
            # identifier) in that we don't require the timer to have expired
            # and we don't worry about the timer having timed out.
            if cls._active_wait_timer is None:
                raise \
                    WaitCertificateError(
                        'Enclave active wait timer has not been initialized')

            is_not_genesis_block = \
                (cls._active_wait_timer.previous_certificate_id !=
                 NullIdentifier)

            now = time.time()
            expire_time = \
                cls._active_wait_timer.request_time + \
                cls._active_wait_timer.duration

            if is_not_genesis_block and now < expire_time:
                raise \
                    WaitCertificateError(
                        'Cannot create wait certificate because timer has '
                        'not expired')

            time_out_time = \
                cls._active_wait_timer.request_time + \
                cls._active_wait_timer.duration + \
                TIMER_TIMEOUT_PERIOD

            if is_not_genesis_block and time_out_time < now:
                raise \
                    WaitCertificateError(
                        'Cannot create wait certificate because timer '
                        'has timed out')

            # Create a random nonce for the certificate.  For our "random"
            # nonce we will take the timer signature, concat that with the
            # current time, JSON-ize it and create a SHA-256 hash over it.
            # Probably not considered random by security professional
            # standards, but it is good enough for the simulator.
            random_string = \
                dict2json({
                    'wait_timer_signature': cls._active_wait_timer.signature,
                    'now': datetime.datetime.utcnow().isoformat()
                })
            nonce = hashlib.sha256(random_string).hexdigest()

            # First create a new enclave wait certificate using the data
            # provided and then sign the certificate with the PoET private key
            wait_certificate = \
                EnclaveWaitCertificate.wait_certificate_with_wait_timer(
                    wait_timer=cls._active_wait_timer,
                    nonce=nonce,
                    block_digest=block_digest)
            wait_certificate.signature = \
                signing.sign(
                    wait_certificate.serialize(),
                    cls._poet_private_key)

            # Now that we have created the certificate, we no longer have an
            # active timer
            cls._active_wait_timer = None

            return wait_certificate
コード例 #30
0
    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 the sealing the signup data.
            signup_data = {
                'poet_public_key':
                signing.encode_pubkey(cls._poet_public_key, 'hex'),
                'poet_private_key':
                signing.encode_privkey(cls._poet_private_key, 'hex')
            }
            sealed_signup_data = \
                base64.b64encode(dict2json(signup_data))

            # Create a fake report
            report_data = '{0}{1}'.format(
                originator_public_key_hash.upper(),
                signing.encode_pubkey(cls._poet_public_key, 'hex').upper())
            quote = {
                'report_body':
                hashlib.sha256(dict2json(report_data)).hexdigest()
            }

            # Fake our "proof" data.
            verification_report = {
                'id':
                base64.b64encode(
                    hashlib.sha256(
                        datetime.datetime.now().isoformat()).hexdigest()),
                'isvEnclaveQuoteStatus':
                'OK',
                'isvEnclaveQuoteBody':
                base64.b64encode(dict2json(quote)),
                'pseManifestStatus':
                'OK',
                'pseManifestHash':
                base64.b64encode(
                    hashlib.sha256(b'Do you believe in '
                                   'manifest destiny?').hexdigest()),
                'nonce':
                most_recent_wait_certificate_id
            }

            proof_data_dict = {
                'verification_report':
                dict2json(verification_report),
                'signature':
                signing.sign(dict2json(verification_report),
                             cls._report_private_key)
            }
            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)
コード例 #31
0
ファイル: client.py プロジェクト: lirm/sawtooth-core
    def _posturl(self, url, info, encoding='application/cbor'):
        """
        Post a transaction message to the validator, parse the returning CBOR
        and return the corresponding dictionary.
        """

        if encoding == 'application/json':
            data = dict2json(info)
        elif encoding == 'application/cbor':
            data = dict2cbor(info)
        else:
            LOGGER.error('unknown request encoding %s', encoding)
            return None

        datalen = len(data)

        LOGGER.debug('post transaction to %s with DATALEN=%d, DATA=<%s>', url,
                     datalen, data)

        try:
            request = urllib2.Request(url, data,
                                      {'Content-Type': 'application/cbor',
                                       'Content-Length': datalen})
            opener = urllib2.build_opener(self.proxy_handler)
            response = opener.open(request, timeout=10)

        except urllib2.HTTPError as err:
            LOGGER.error('peer operation on url %s failed with response: %d',
                         url, err.code)
            raise MessageException('operation failed with resonse: {0}'.format(
                err.code))

        except urllib2.URLError as err:
            LOGGER.error('peer operation on url %s failed: %s', url,
                         err.reason)
            raise MessageException('operation failed: {0}'.format(err.reason))

        except NameError as err:
            LOGGER.error('name error %s', err)
            raise MessageException('operation failed: {0}'.format(url))

        except:
            LOGGER.error('no response from peer server for url %s; %s', url,
                         sys.exc_info()[0])
            raise MessageException('no response from server')

        content = response.read()
        headers = response.info()
        response.close()

        encoding = headers.get('Content-Type')

        if encoding == 'application/json':
            value = json2dict(content)
        elif encoding == 'application/cbor':
            value = cbor2dict(content)
        else:
            LOGGER.info('server responds with message %s of unknown type %s',
                        content, encoding)
            value = OrderedDict()

        return value
コード例 #32
0
    def verify_signup_info(cls, signup_info, originator_public_key_hash,
                           most_recent_wait_certificate_id):
        # Verify the attestation verification report signature
        proof_data_dict = json2dict(signup_info.proof_data)
        verification_report = proof_data_dict.get('verification_report')
        if verification_report is None:
            raise \
                SignupInfoError(
                    'Verification report is missing from proof data')

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

        if not signing.verify(verification_report, signature,
                              cls._report_public_key):
            raise \
                SignupInfoError('Verification report signature is invalid')

        verification_report_dict = json2dict(verification_report)

        # 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 \
                SignupInfoError(
                    'Verification report does not contain a PSE manifest '
                    'status')
        if pse_manifest_status != 'OK':
            raise \
                SignupInfoError(
                    'PSE manifest status is {} (i.e., not OK)'.format(
                        pse_manifest_status))

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

        expected_pse_manifest_hash = \
            base64.b64encode(
                hashlib.sha256(
                    b'Do you believe in manifest destiny?').hexdigest())

        if pse_manifest_hash != expected_pse_manifest_hash:
            raise \
                SignupInfoError(
                    '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 \
                SignupInfoError(
                    'Verification report does not contain an enclave quote '
                    'status')
        if enclave_quote_status != 'OK':
            raise \
                SignupInfoError(
                    'Enclave quote status is {} (i.e., not OK)'.format(
                        enclave_quote_status))

        enclave_quote = verification_report_dict.get('isvEnclaveQuoteBody')
        if enclave_quote is None:
            raise \
                SignupInfoError(
                    'Verification report does not contain an enclave quote')

        # Verify that the enclave quote contains a report body with the value
        # we expect (i.e., SHA256(SHA256(OPK)|PPK)
        report_data = '{0}{1}'.format(originator_public_key_hash.upper(),
                                      signup_info.poet_public_key.upper())
        expected_report_body = hashlib.sha256(
            dict2json(report_data)).hexdigest()

        enclave_quote_dict = \
            json2dict(base64.b64decode(enclave_quote))
        report_body = enclave_quote_dict.get('report_body')
        if report_body is None:
            raise \
                SignupInfoError(
                    'Enclave quote does not contain a report body')

        if report_body != expected_report_body:
            raise \
                SignupInfoError(
                    'Enclave quote report body {0} does not match {1}'.format(
                        report_body,
                        expected_report_body))

        # 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 \
                SignupInfoError(
                    'Verification report does not have a nonce')
コード例 #33
0
    def render_POST(self, request):
        """
        Handle a POST request on the HTTP interface. All message on the POST
        interface are gossip messages that should be relayed into the gossip
        network as is.
        """
        # pylint: disable=invalid-name

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        prefix = components.pop(0) if components else 'error'
        if prefix not in self.PostPageMap:
            prefix = 'default'

        # process the message encoding
        encoding = request.getHeader('Content-Type')
        data = request.content.getvalue()

        try:
            if encoding == 'application/json':
                minfo = json2dict(data)
            elif encoding == 'application/cbor':
                minfo = cbor2dict(data)
            else:
                return self.error_response(request, http.BAD_REQUEST,
                                           'unknown message encoding, {0}',
                                           encoding)

            typename = minfo.get('__TYPE__', '**UNSPECIFIED**')
            if typename not in self.Ledger.MessageHandlerMap:
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'received request for unknown message type, {0}', typename)

            msg = self.Ledger.MessageHandlerMap[typename][0](minfo)

        except:
            logger.info('exception while decoding http request %s; %s',
                        request.path, traceback.format_exc(20))
            return self.error_response(
                request, http.BAD_REQUEST,
                'unabled to decode incoming request {0}',
                data)

        # and finally execute the associated method and send back the results
        try:
            response = self.PostPageMap[prefix](request, components, msg)

            request.responseHeaders.addRawHeader("content-type", encoding)
            if encoding == 'application/json':
                result = dict2json(response.dump())
            else:
                result = dict2cbor(response.dump())

            return result

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing request {0}; {1}', request.path,
                str(e))

        except:
            logger.info('exception while processing http request %s; %s',
                        request.path, traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #34
0
ファイル: client.py プロジェクト: archimy/sawtooth-core
    def _posturl(self, url, info, encoding='application/cbor'):
        """
        Post a transaction message to the validator, parse the returning CBOR
        and return the corresponding dictionary.
        """

        if encoding == 'application/json':
            data = dict2json(info)
        elif encoding == 'application/cbor':
            data = dict2cbor(info)
        else:
            LOGGER.error('unknown request encoding %s', encoding)
            return None

        datalen = len(data)

        LOGGER.debug('post transaction to %s with DATALEN=%d, DATA=<%s>', url,
                     datalen, data)

        try:
            request = urllib2.Request(url, data, {
                'Content-Type': 'application/cbor',
                'Content-Length': datalen
            })
            opener = urllib2.build_opener(self.proxy_handler)
            response = opener.open(request, timeout=10)

        except urllib2.HTTPError as err:
            LOGGER.error('peer operation on url %s failed with response: %d',
                         url, err.code)
            raise MessageException(
                'operation failed with response: {0}'.format(err.code))

        except urllib2.URLError as err:
            LOGGER.error('peer operation on url %s failed: %s', url,
                         err.reason)
            raise MessageException('operation failed: {0}'.format(err.reason))

        except NameError as err:
            LOGGER.error('name error %s', err)
            raise MessageException('operation failed: {0}'.format(url))

        except:
            LOGGER.error('no response from peer server for url %s; %s', url,
                         sys.exc_info()[0])
            raise MessageException('no response from server')

        content = response.read()
        headers = response.info()
        response.close()

        encoding = headers.get('Content-Type')

        if encoding == 'application/json':
            value = json2dict(content)
        elif encoding == 'application/cbor':
            value = cbor2dict(content)
        else:
            LOGGER.info('server responds with message %s of unknown type %s',
                        content, encoding)
            value = OrderedDict()

        return value
コード例 #35
0
    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 the sealing the signup data.
            signup_data = {
                'poet_public_key':
                    signing.encode_pubkey(cls._poet_public_key, 'hex'),
                'poet_private_key':
                    signing.encode_privkey(
                        cls._poet_private_key,
                        'hex')
            }
            sealed_signup_data = \
                base64.b64encode(bytes(dict2json(signup_data).encode()))

            # Create a fake report
            report_data = '{0}{1}'.format(
                originator_public_key_hash.upper(),
                signing.encode_pubkey(
                    cls._poet_public_key,
                    'hex').upper()
            )
            quote = {
                'report_body': hashlib.sha256(
                    dict2json(report_data).encode()).hexdigest()
            }

            # Fake our "proof" data.
            verification_report = {
                'id': base64.b64encode(
                    bytes(hashlib.sha256(
                        datetime.datetime.now().isoformat().encode())
                        .hexdigest().encode())).decode(),
                'isvEnclaveQuoteStatus': 'OK',
                'isvEnclaveQuoteBody':
                    base64.b64encode(
                        dict2json(quote).encode()).decode(),
                'pseManifestStatus': 'OK',
                'pseManifestHash':
                    base64.b64encode(
                        hashlib.sha256(
                            bytes(b'Do you believe in '
                                  b'manifest destiny?')).hexdigest()
                        .encode()).decode(),
                'nonce': most_recent_wait_certificate_id
            }

            proof_data_dict = {
                'verification_report': dict2json(verification_report),
                'signature':
                    signing.sign(
                        dict2json(verification_report),
                        cls._report_private_key)
            }
            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)
コード例 #36
0
    def verify_signup_info(cls,
                           signup_info,
                           originator_public_key,
                           validator_network_basename,
                           most_recent_wait_certificate_id):
        # Verify the attestation verification report signature
        attestation_verification_report = \
            signup_info.proof_data.get('attestation_verification_report')
        if attestation_verification_report is None:
            raise \
                SignupInfoError(
                    'Attestation verification report is missing from proof '
                    'data')

        if not pybitcointools.ecdsa_verify(
                dict2json(attestation_verification_report),
                signup_info.proof_data.get('signature'),
                cls._report_public_key):
            raise \
                SignupInfoError(
                    'Attestation verification report signature is invalid')

        # Verify the presence of the anti-Sybil ID
        anti_sybil_id = attestation_verification_report.get('anti_sybil_id')
        if anti_sybil_id is None:
            raise \
                SignupInfoError(
                    'Attestation verification report does not contain an '
                    'anti-Sybil ID')

        # Verify that the report data field in the report contains the SHA256
        # digest of the originator's public key SHA 256 digest and the PoET
        # public key.
        attestation_evidence_payload = \
            attestation_verification_report.get(
                'attestation_evidence_payload')
        if attestation_evidence_payload is None:
            raise \
                SignupInfoError(
                    'Attestation verification report does not contain '
                    'attestation evidence payload')

        enclave_quote = attestation_evidence_payload.get('enclave_quote')
        if enclave_quote is None:
            raise \
                SignupInfoError(
                    'Attestation evidence payload does not contain an '
                    'enclave quote')

        report = json2dict(pybitcointools.base64.b64decode(enclave_quote))
        report_data = report.get('report_data')
        if report_data is None:
            raise \
                SignupInfoError('Enclave quote does not contain report data')

        target_report_data = {
            'originator_public_key_hash':
                pybitcointools.sha256(
                    pybitcointools.encode_pubkey(
                        originator_public_key,
                        'hex')),
            'poet_public_key': signup_info.poet_public_key
        }
        target_report_data_digest = \
            pybitcointools.sha256(dict2json(target_report_data))

        if report_data != target_report_data_digest:
            raise SignupInfoError('Enclave quote report data is invalid')

        # Verify that the validator base name in the enclave quote report
        # matches the provided validator network basename
        validator_net_basename = report.get('validator_network_basename')
        if validator_net_basename is None:
            raise \
                SignupInfoError(
                    'Enclave quote report does not have a validator network '
                    'basename')

        if validator_net_basename != validator_network_basename:
            raise \
                SignupInfoError(
                    'Enclave quote report validator network basename [{0}] '
                    'does not match [{1}]'.format(
                        validator_net_basename,
                        validator_network_basename))
コード例 #37
0
    def verify_signup_info(cls,
                           signup_info,
                           originator_public_key_hash,
                           most_recent_wait_certificate_id):
        # Verify the attestation verification report signature
        proof_data_dict = json2dict(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')

        if not signing.verify(
                verification_report,
                signature,
                cls._report_public_key):
            raise ValueError('Verification report signature is invalid')

        verification_report_dict = json2dict(verification_report)

        # 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 != '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
        # and it is the value we expect
        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')

        expected_pse_manifest_hash = \
            base64.b64encode(
                hashlib.sha256(
                    bytes(b'Do you believe in '
                          b'manifest destiny?')).hexdigest()
                .encode()).decode()

        if pse_manifest_hash != expected_pse_manifest_hash:
            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 != 'OK':
            raise \
                ValueError(
                    'Enclave quote status is {} (i.e., not OK)'.format(
                        enclave_quote_status))

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

        # Verify that the enclave quote contains a report body with the value
        # we expect (i.e., SHA256(SHA256(OPK)|PPK)
        report_data = '{0}{1}'.format(
            originator_public_key_hash.upper(),
            signup_info.poet_public_key.upper()
        )
        expected_report_body = hashlib.sha256(
            dict2json(report_data).encode()).hexdigest()

        enclave_quote_dict = \
            json2dict(base64.b64decode(enclave_quote).decode())
        report_body = enclave_quote_dict.get('report_body')
        if report_body is None:
            raise ValueError('Enclave quote does not contain a report body')

        if report_body != expected_report_body:
            raise \
                ValueError(
                    'Enclave quote report body {0} does not match {1}'.format(
                        report_body,
                        expected_report_body))

        # 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')
コード例 #38
0
ファイル: web_api.py プロジェクト: trbs/sawtooth-validator
    def do_post(self, request):
        """
        Handle two types of HTTP POST requests:
         - gossip messages.  relayed to the gossip network as is
         - validator command and control (/command)
        """

        # break the path into its component parts

        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        prefix = components.pop(0) if components else 'error'
        if prefix not in self.PostPageMap:
            prefix = 'default'

        encoding = request.getHeader('Content-Type')
        data = request.content.getvalue()

        # process non-gossip API requests
        if prefix == 'command':

            try:
                if encoding == 'application/json':
                    minfo = json2dict(data)
                else:
                    return self.error_response(request, http.BAD_REQUEST,
                                               'bad message encoding, {0}',
                                               encoding)
            except:
                logger.info('exception while decoding http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'unable to decode incoming request {0}', data)

            # process /command
            try:
                response = self.PostPageMap[prefix](request, components, minfo)
                request.responseHeaders.addRawHeader("content-type", encoding)
                result = dict2json(response)
                return result

            except Error as e:
                return self.error_response(
                    request, int(e.status),
                    'exception while processing request {0}; {1}',
                    request.path, str(e))

            except:
                logger.info('exception while processing http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'error processing http request {0}', request.path)
        else:
            try:
                if encoding == 'application/json':
                    minfo = json2dict(data)
                elif encoding == 'application/cbor':
                    minfo = cbor2dict(data)
                else:
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'unknown message encoding, {0}', encoding)
                typename = minfo.get('__TYPE__', '**UNSPECIFIED**')
                if typename not in self.Ledger.MessageHandlerMap:
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'received request for unknown message type, {0}',
                        typename)

                msg = self.Ledger.MessageHandlerMap[typename][0](minfo)

            except:
                logger.info('exception while decoding http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'unabled to decode incoming request {0}', data)

            # determine if the message contains a valid transaction before
            # we send the message to the network

            # we need to start with a copy of the message due to cases
            # where side effects of the validity check may impact objects
            # related to msg
            mymsg = copy.deepcopy(msg)

            if hasattr(mymsg, 'Transaction') and mymsg.Transaction is not None:
                mytxn = mymsg.Transaction
                logger.info(
                    'starting local validation '
                    'for txn id: %s type: %s', mytxn.Identifier,
                    mytxn.TransactionTypeName)
                block_id = self.Ledger.MostRecentCommittedBlockID

                real_store_map = self.Ledger.GlobalStoreMap.get_block_store(
                    block_id)
                temp_store_map = \
                    global_store_manager.BlockStore(real_store_map)
                if not temp_store_map:
                    logger.info('no store map for block %s', block_id)
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'unable to validate enclosed transaction {0}', data)

                transaction_type = mytxn.TransactionTypeName
                if transaction_type not in temp_store_map.TransactionStores:
                    logger.info('transaction type %s not in global store map',
                                transaction_type)
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'unable to validate enclosed transaction {0}', data)

                # clone a copy of the ledger's message queue so we can
                # temporarily play forward all locally submitted yet
                # uncommitted transactions
                my_queue = copy.deepcopy(self.Ledger.MessageQueue)

                # apply any enqueued messages
                while len(my_queue) > 0:
                    qmsg = my_queue.pop()
                    if qmsg and \
                       qmsg.MessageType in self.Ledger.MessageHandlerMap:
                        if (hasattr(qmsg, 'Transaction')
                                and qmsg.Transaction is not None):
                            my_store = temp_store_map.get_transaction_store(
                                qmsg.Transaction.TransactionTypeName)
                            if qmsg.Transaction.is_valid(my_store):
                                myqtxn = copy.copy(qmsg.Transaction)
                                myqtxn.apply(my_store)

                # apply any local pending transactions
                for txn_id in self.Ledger.PendingTransactions.iterkeys():
                    pend_txn = self.Ledger.TransactionStore[txn_id]
                    my_store = temp_store_map.get_transaction_store(
                        pend_txn.TransactionTypeName)
                    if pend_txn and pend_txn.is_valid(my_store):
                        my_pend_txn = copy.copy(pend_txn)
                        logger.debug(
                            'applying pending transaction '
                            '%s to temp store', txn_id)
                        my_pend_txn.apply(my_store)

                # determine validity of the POSTed transaction against our
                # new temporary state
                my_store = temp_store_map.get_transaction_store(
                    mytxn.TransactionTypeName)
                try:
                    mytxn.check_valid(my_store)
                except InvalidTransactionError as e:
                    logger.info(
                        'submitted transaction fails transaction '
                        'family validation check: %s; %s', request.path,
                        mymsg.dump())
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        "enclosed transaction failed transaction "
                        "family validation check: {}".format(str(e)), data)
                except:
                    logger.info(
                        'submitted transaction is '
                        'not valid %s; %s; %s', request.path, mymsg.dump(),
                        traceback.format_exc(20))
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        "enclosed transaction is not valid", data)

                logger.info('transaction %s is valid',
                            msg.Transaction.Identifier)

            # and finally execute the associated method
            # and send back the results
            try:
                response = self.PostPageMap[prefix](request, components, msg)

                request.responseHeaders.addRawHeader("content-type", encoding)
                if encoding == 'application/json':
                    result = dict2json(response.dump())
                else:
                    result = dict2cbor(response.dump())

                return result

            except Error as e:
                return self.error_response(
                    request, int(e.status),
                    'exception while processing request {0}; {1}',
                    request.path, str(e))

            except:
                logger.info('exception while processing http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'error processing http request {0}', request.path)
コード例 #39
0
 def serialize(self):
     return dict2json({
         'anti_sybil_id': self.anti_sybil_id,
         'poet_public_key': self.poet_public_key,
         'proof_data': self.proof_data
     })
コード例 #40
0
    def render_POST(self, request):
        """
        Handle two types of HTTP POST requests:
         - gossip messages.  relayed to the gossip network as is
         - validator command and control (/command)
        """
        # pylint: disable=invalid-name

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        prefix = components.pop(0) if components else 'error'
        if prefix not in self.PostPageMap:
            prefix = 'default'

        encoding = request.getHeader('Content-Type')
        data = request.content.getvalue()

        # process non-gossip API requests
        if prefix == 'command':

            try:
                if encoding == 'application/json':
                    minfo = json2dict(data)
                else:
                    return self.error_response(request, http.BAD_REQUEST,
                                               'bad message encoding, {0}',
                                               encoding)
            except:
                logger.info('exception while decoding http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'unable to decode incoming request {0}',
                    data)

            # process /command
            try:
                response = self.PostPageMap[prefix](request, components, minfo)
                request.responseHeaders.addRawHeader("content-type", encoding)
                result = dict2json(response)
                return result

            except Error as e:
                return self.error_response(
                    request, int(e.status),
                    'exception while processing request {0}; {1}',
                    request.path, str(e))

            except:
                logger.info('exception while processing http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(request, http.BAD_REQUEST,
                                           'error processing http request {0}',
                                           request.path)
        else:
            try:
                if encoding == 'application/json':
                    minfo = json2dict(data)
                elif encoding == 'application/cbor':
                    minfo = cbor2dict(data)
                else:
                    return self.error_response(request, http.BAD_REQUEST,
                                               'unknown message encoding, {0}',
                                               encoding)
                typename = minfo.get('__TYPE__', '**UNSPECIFIED**')
                if typename not in self.Ledger.MessageHandlerMap:
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'received request for unknown message type, {0}',
                        typename)

                msg = self.Ledger.MessageHandlerMap[typename][0](minfo)

            except:
                logger.info('exception while decoding http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'unabled to decode incoming request {0}',
                    data)

            # determine if the message contains a valid transaction before
            # we send the message to the network

            # we need to start with a copy of the message due to cases
            # where side effects of the validity check may impact objects
            # related to msg
            mymsg = copy.deepcopy(msg)

            if hasattr(mymsg, 'Transaction') and mymsg.Transaction is not None:
                mytxn = mymsg.Transaction
                logger.info('starting local validation '
                            'for txn id: %s type: %s',
                            mytxn.Identifier,
                            mytxn.TransactionTypeName)
                blockid = self.Ledger.MostRecentCommittedBlockID

                realstoremap = self.Ledger.GlobalStoreMap.get_block_store(
                    blockid)
                tempstoremap = global_store_manager.BlockStore(realstoremap)
                if not tempstoremap:
                    logger.info('no store map for block %s', blockid)
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'unable to validate enclosed transaction {0}',
                        data)

                transtype = mytxn.TransactionTypeName
                if transtype not in tempstoremap.TransactionStores:
                    logger.info('transaction type %s not in global store map',
                                transtype)
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        'unable to validate enclosed transaction {0}',
                        data)

                # clone a copy of the ledger's message queue so we can
                # temporarily play forward all locally submitted yet
                # uncommitted transactions
                myqueue = copy.deepcopy(self.Ledger.MessageQueue)

                # apply any enqueued messages
                while len(myqueue) > 0:
                    qmsg = myqueue.pop()
                    if qmsg and \
                       qmsg.MessageType in self.Ledger.MessageHandlerMap:
                        if (hasattr(qmsg, 'Transaction') and
                                qmsg.Transaction is not None):
                            mystore = tempstoremap.get_transaction_store(
                                qmsg.Transaction.TransactionTypeName)
                            if qmsg.Transaction.is_valid(mystore):
                                myqtxn = copy.copy(qmsg.Transaction)
                                myqtxn.apply(mystore)

                # apply any local pending transactions
                for txnid in self.Ledger.PendingTransactions.iterkeys():
                    pendtxn = self.Ledger.TransactionStore[txnid]
                    mystore = tempstoremap.get_transaction_store(
                        pendtxn.TransactionTypeName)
                    if pendtxn and pendtxn.is_valid(mystore):
                        mypendtxn = copy.copy(pendtxn)
                        logger.debug('applying pending transaction '
                                     '%s to temp store', txnid)
                        mypendtxn.apply(mystore)

                # determine validity of the POSTed transaction against our
                # new temporary state
                mystore = tempstoremap.get_transaction_store(
                    mytxn.TransactionTypeName)
                try:
                    mytxn.check_valid(mystore)
                except InvalidTransactionError as e:
                    logger.info('submitted transaction fails transaction '
                                'family validation check: %s; %s',
                                request.path, mymsg.dump())
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        "enclosed transaction failed transaction "
                        "family validation check: {}".format(str(e)),
                        data)
                except:
                    logger.info('submitted transaction is '
                                'not valid %s; %s; %s',
                                request.path, mymsg.dump(),
                                traceback.format_exc(20))
                    return self.error_response(
                        request, http.BAD_REQUEST,
                        "enclosed transaction is not valid",
                        data)

                logger.info('transaction %s is valid',
                            msg.Transaction.Identifier)

            # and finally execute the associated method
            # and send back the results
            try:
                response = self.PostPageMap[prefix](request, components, msg)

                request.responseHeaders.addRawHeader("content-type", encoding)
                if encoding == 'application/json':
                    result = dict2json(response.dump())
                else:
                    result = dict2cbor(response.dump())

                return result

            except Error as e:
                return self.error_response(
                    request, int(e.status),
                    'exception while processing request {0}; {1}',
                    request.path, str(e))

            except:
                logger.info('exception while processing http request %s; %s',
                            request.path, traceback.format_exc(20))
                return self.error_response(request, http.BAD_REQUEST,
                                           'error processing http request {0}',
                                           request.path)
コード例 #41
0
    def render_POST(self, request):
        """
        Handle a POST request on the HTTP interface. All message on the POST
        interface are gossip messages that should be relayed into the gossip
        network as is.
        """
        # pylint: disable=invalid-name

        # break the path into its component parts
        components = request.path.split('/')
        while components and components[0] == '':
            components.pop(0)

        prefix = components.pop(0) if components else 'error'
        if prefix not in self.PostPageMap:
            prefix = 'default'

        # process the message encoding
        encoding = request.getHeader('Content-Type')
        data = request.content.getvalue()

        try:
            if encoding == 'application/json':
                minfo = json2dict(data)
            elif encoding == 'application/cbor':
                minfo = cbor2dict(data)
            else:
                return self.error_response(request, http.BAD_REQUEST,
                                           'unknown message encoding, {0}',
                                           encoding)

            typename = minfo.get('__TYPE__', '**UNSPECIFIED**')
            if typename not in self.Ledger.MessageHandlerMap:
                return self.error_response(
                    request, http.BAD_REQUEST,
                    'received request for unknown message type, {0}', typename)

            msg = self.Ledger.MessageHandlerMap[typename][0](minfo)

        except:
            logger.info('exception while decoding http request %s; %s',
                        request.path, traceback.format_exc(20))
            return self.error_response(
                request, http.BAD_REQUEST,
                'unabled to decode incoming request {0}', data)

        # and finally execute the associated method and send back the results
        try:
            response = self.PostPageMap[prefix](request, components, msg)

            request.responseHeaders.addRawHeader("content-type", encoding)
            if encoding == 'application/json':
                return dict2json(response.dump())
            else:
                return dict2cbor(response.dump())

        except Error as e:
            return self.error_response(
                request, int(e.status),
                'exception while processing request {0}; {1}', request.path,
                str(e))

        except:
            logger.info('exception while processing http request %s; %s',
                        request.path, traceback.format_exc(20))
            return self.error_response(request, http.BAD_REQUEST,
                                       'error processing http request {0}',
                                       request.path)
コード例 #42
0
    def create_wait_certificate(cls,
                                wait_timer,
                                block_digest):
        with cls._lock:
            # If we don't have a PoET private key, then the enclave has not
            # been properly initialized (either by calling create_signup_info
            # or unseal_signup_data)
            if cls._poet_private_key is None:
                raise \
                    ValueError(
                        'Enclave must be initialized before attempting to '
                        'create a wait certificate')

            # Several criteria need to be met before we can create a wait
            # certificate:
            # 1. We have an active timer
            # 2. The caller's wait timer is the active wait timer.  We are not
            #    going to rely the objects, being the same, but will compute
            #    a signature over the object and verify that the signatures
            #    are the same.
            # 3. The active timer has expired
            # 4. The active timer has not timed out
            #
            # Note - we make a concession for the genesis block (i.e., a wait
            # timer for which the previous certificate ID is the Null
            # identifier) in that we don't require the timer to have expired
            # and we don't worry about the timer having timed out.
            if cls._active_wait_timer is None:
                raise \
                    ValueError(
                        'There is not a current enclave active wait timer')

            if wait_timer is None or \
                    cls._active_wait_timer.signature != \
                    signing.sign(
                        wait_timer.serialize(),
                        cls._poet_private_key):
                raise \
                    ValueError(
                        'Validator is not using the current wait timer')

            is_not_genesis_block = \
                (cls._active_wait_timer.previous_certificate_id !=
                 NullIdentifier)

            now = time.time()
            expire_time = \
                cls._active_wait_timer.request_time + \
                cls._active_wait_timer.duration

            if is_not_genesis_block and now < expire_time:
                raise \
                    ValueError(
                        'Cannot create wait certificate because timer has '
                        'not expired')

            time_out_time = \
                cls._active_wait_timer.request_time + \
                cls._active_wait_timer.duration + \
                TIMER_TIMEOUT_PERIOD

            if is_not_genesis_block and time_out_time < now:
                raise \
                    ValueError(
                        'Cannot create wait certificate because timer '
                        'has timed out')

            # Create a random nonce for the certificate.  For our "random"
            # nonce we will take the timer signature, concat that with the
            # current time, JSON-ize it and create a SHA-256 hash over it.
            # Probably not considered random by security professional
            # standards, but it is good enough for the simulator.
            random_string = \
                dict2json({
                    'wait_timer_signature': cls._active_wait_timer.signature,
                    'now': datetime.datetime.utcnow().isoformat()
                })
            nonce = hashlib.sha256(random_string.encode()).hexdigest()

            # First create a new enclave wait certificate using the data
            # provided and then sign the certificate with the PoET private key
            wait_certificate = \
                EnclaveWaitCertificate.wait_certificate_with_wait_timer(
                    wait_timer=cls._active_wait_timer,
                    nonce=nonce,
                    block_digest=block_digest)
            wait_certificate.signature = \
                signing.sign(
                    wait_certificate.serialize(),
                    cls._poet_private_key)

            # Now that we have created the certificate, we no longer have an
            # active timer
            cls._active_wait_timer = None

            return wait_certificate
コード例 #43
0
ファイル: signup_info.py プロジェクト: danmack/sawtooth-core
 def serialize(self):
     serialized = dict2json({'anti_sybil_id': self.anti_sybil_id,
                             'poet_pubkey': self.poet_pubkey,
                             'proof_data': self.proof_data})
     return serialized
コード例 #44
0
    def create_wait_certificate(cls, block_digest):
        with cls._lock:
            # If we don't have a PoET private key, then the enclave has not
            # been properly initialized (either by calling create_signup_info
            # or unseal_signup_data)
            if cls._poet_private_key is None:
                raise \
                    WaitCertificateError(
                        'Enclave must be initialized before attempting to '
                        'create a wait certificate')

            # Several criteria we need to be met before we can create a wait
            # certificate:
            # 1. We have an active timer
            # 2. The active timer has expired
            # 3. The active timer has not timed out
            if cls._active_wait_timer is None:
                raise \
                    WaitCertificateError(
                        'Enclave active wait timer has not been initialized')

            # HACK ALERT!!  HACK ALERT!!  HACK ALERT!!  HACK ALERT!!
            #
            # Today, without the genesis utility we cannot make these checks.
            # Once we have the genesis utility, this code needs to change to
            # Depend upon the timer not being expired or timed out.  The
            # Original specification requires this check.
            #
            # HACK ALERT!!  HACK ALERT!!  HACK ALERT!!  HACK ALERT!!
            #
            # if not cls._active_wait_timer.has_expired():
            #     raise \
            #         WaitCertificateError(
            #             'Cannot create wait certificate because timer has '
            #             'not expired')
            # if wait_timer.has_timed_out():
            #     raise \
            #         WaitCertificateError(
            #             'Cannot create wait certificate because timer '
            #             'has timed out')

            # Create a random nonce for the certificate.  For our "random"
            # nonce we will take the timer signature, concat that with the
            # current time, JSON-ize it and create a SHA-256 hash over it.
            # Probably not considered random by security professional
            # standards, but it is good enough for the simulator.
            random_string = \
                dict2json({
                    'wait_timer_signature': cls._active_wait_timer.signature,
                    'now': datetime.datetime.utcnow().isoformat()
                })
            nonce = pybitcointools.sha256(random_string)

            # First create a new enclave wait certificate using the data
            # provided and then sign the certificate with the PoET private key
            wait_certificate = \
                EnclaveWaitCertificate.wait_certificate_with_wait_timer(
                    wait_timer=cls._active_wait_timer,
                    nonce=nonce,
                    block_digest=block_digest)
            wait_certificate.signature = \
                pybitcointools.ecdsa_sign(
                    wait_certificate.serialize(),
                    cls._poet_private_key)

            # Now that we have created the certificate, we no longer have an
            # active timer
            cls._active_wait_timer = None

            return wait_certificate
コード例 #45
0
    def create_signup_info(cls,
                           originator_public_key,
                           validator_network_basename,
                           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 = pybitcointools.random_key()
            cls._poet_public_key = \
                pybitcointools.privtopub(cls._poet_private_key)
            cls._active_wait_timer = None

            # We are going to fake out the sealing the signup data.
            signup_data = {
                'poet_public_key':
                    pybitcointools.encode_pubkey(cls._poet_public_key, 'hex'),
                'poet_private_key':
                    pybitcointools.encode_privkey(
                        cls._poet_private_key,
                        'hex')
            }
            sealed_signup_data = \
                pybitcointools.base64.b32encode(dict2json(signup_data))

            # Create a fake report
            report_data = {
                'originator_public_key_hash':
                    pybitcointools.sha256(
                        pybitcointools.encode_pubkey(
                            originator_public_key,
                            'hex')),
                'poet_public_key':
                    pybitcointools.encode_pubkey(cls._poet_public_key, 'hex')
            }
            report = {
                'report_data': pybitcointools.sha256(dict2json(report_data)),
                'validator_network_basename': validator_network_basename
            }

            # Fake our "proof" data.
            attestation_evidence_payload = {
                'enclave_quote':
                    pybitcointools.base64.b64encode(dict2json(report)),
                'pse_manifest':
                    pybitcointools.base64.b64encode(
                        pybitcointools.sha256(
                            'manifest destiny')),
                'nonce': most_recent_wait_certificate_id
            }

            attestation_verification_report = {
                'attestation_evidence_payload': attestation_evidence_payload,
                'anti_sybil_id': cls._anti_sybil_id
            }

            proof_data = {
                'attestation_verification_report':
                    attestation_verification_report,
                'signature':
                    pybitcointools.ecdsa_sign(
                        dict2json(attestation_verification_report),
                        cls._report_private_key)
            }

            return \
                EnclaveSignupInfo(
                    poet_public_key=signup_data['poet_public_key'],
                    proof_data=proof_data,
                    sealed_signup_data=sealed_signup_data)