Exemplo n.º 1
0
    def FindDependency(self, ledger_config, contractid, statehash):
        logger.debug('find dependency for %s, %s', contractid, statehash)

        txnid = self.__get(contractid, statehash)
        if txnid:
            return txnid

        # no information about this update locally, so go to the
        # ledger to retrieve it
        client = PdoRegistryHelper(ledger_config['LedgerURL'])

        try:
            # this is not very efficient since it pulls all of the state
            # down with the txnid
            contract_state_info = client.get_ccl_state_dict(
                contractid, statehash)
            txnid = contract_state_info['transaction_id']
            self.__set(contractid, statehash, txnid)
            return txnid
        except Exception as e:
            logger.info('failed to retrieve the transaction: %s', str(e))

        logger.info('unable to find dependency for %s:%s', contractid,
                    statehash)
        return None
Exemplo n.º 2
0
    def __init__(self, config):
        self.__registry_helper = PdoRegistryHelper(
            config['Sawtooth']['LedgerURL'])

        self.SigningKey = pcrypto.SIG_PrivateKey(config['SigningKeyPrivate'])
        self.PSPK = pcrypto.SIG_PublicKey(config['SigningKeyPublic'])

        self.secrets_file_path = config['SecretsFilePath']

        self.RequestMap = {
            'secretRequest': self._secretreq,
            'dataRequest': self._datareq,
        }
Exemplo n.º 3
0
    def __init__(self, config, enclave) :

        self.Enclave = enclave
        self.SealedData = enclave.sealed_data
        self.PSPK = enclave.verifying_key # Enclave public signing key
        self.EncryptionKey = enclave.encryption_key # Enclave public encryption key
        self.EnclaveID = enclave.enclave_id

        self.__registry_helper = PdoRegistryHelper(config['Sawtooth']['LedgerURL'])

        self.secrets_file_path = config['SecretsFilePath']
        self.secret_length = 16

        self.RequestMap = {
            'secretRequest' : self._secretreq,
            'dataRequest' : self._datareq,
        }
Exemplo n.º 4
0
class ProvisioningServer(resource.Resource):
    isLeaf = True

    ## -----------------------------------------------------------------
    def __init__(self, config, enclave) :

        self.Enclave = enclave
        self.SealedData = enclave.sealed_data
        self.PSPK = enclave.verifying_key # Enclave public signing key
        self.EncryptionKey = enclave.encryption_key # Enclave public encryption key
        self.EnclaveID = enclave.enclave_id

        self.__registry_helper = PdoRegistryHelper(config['Sawtooth']['LedgerURL'])

        self.secrets_file_path = config['SecretsFilePath']
        self.secret_length = 16

        self.RequestMap = {
            'secretRequest' : self._secretreq,
            'dataRequest' : self._datareq,
        }


    ## -----------------------------------------------------------------
    def _GetContractSecret(self, contracttxnid) :
        """
        Retrieve or create the secret for a particular contract, returned in
        raw format
        """

        file_secrets = dict()
        with open(self.secrets_file_path, "r") as f:
            for line in f:
                key, val = line.partition(":")[::2]
                file_secrets[key.strip()] = val.strip()

        contracttxnid = hashlib.sha256(contracttxnid.encode()).hexdigest()

        # If secret already exists, return it, otherwise create, store, and return a new secret
        if contracttxnid in file_secrets:
            sealed_secret = file_secrets[contracttxnid]
            logger.debug('Secret for contract %s found', contracttxnid)
        else:
            sealed_secret = self.Enclave.create_secret(self.secret_length)["sealed_secret"]
            file_secrets[contracttxnid] = sealed_secret
            logger.debug('Creating new Secret for contract %s', contracttxnid)
            with open(self.secrets_file_path, "w") as f:
                for key in file_secrets:
                    f.write(key + ' : ' + file_secrets[key] + "\n")

        return sealed_secret

    ## -----------------------------------------------------------------
    def ErrorResponse(self, request, response, *msgargs) :
        """
        Generate a common error response for broken requests
        """
        request.setResponseCode(response)

        msg = msgargs[0].format(*msgargs[1:])
        if response > 400 :
            logger.warn(msg)
        elif response > 300 :
            logger.debug(msg)

        return "" if request.method == 'HEAD' else (msg + '\n')

    ## -----------------------------------------------------------------
    def _datareq(self, minfo) :
        logger.debug('Got request for public key data')

        # create the response
        response = dict()
        response['pspk'] = self.PSPK

        return response

    ## -----------------------------------------------------------------
    def _secretreq(self, minfo) :
        # unpack the request
        try:
            enclave_id = minfo['enclave_id']
            contract_id = minfo['contract_id']
            opk = minfo['opk']
            signature = minfo['signature']

        except KeyError as ke:
            raise Error(http.BAD_REQUEST, 'missing required field {0}'.format(ke))

        logger.debug('request for key for contract %s, enclave %s', contract_id, enclave_id)

        # verify the signature, that is, make sure that the request was really signed by opk
        try:
            opkkey = pcrypto.SIG_PublicKey(opk)
            opkkey.VerifySignature(pcrypto.string_to_byte_array(enclave_id + contract_id), pcrypto.hex_to_byte_array(signature))
        except:
            logger.warn("Signature verification failed")
            raise Error(http.BAD_REQUEST, 'Signature Mismatch')

        # Get enclave state
        try:
            logger.debug('retrieve information for enclave %s', enclave_id)
            enclave_info = self.__registry_helper.get_enclave_dict(enclave_id)
            logger.debug("enclave information retrieved: %s", enclave_info)
        except BaseException as err:
            logger.warn('exception occurred when getting ledger information for enclave %s; %s', enclave_id, str(err))
            raise Error(http.BAD_REQUEST, 'could not retrieve enclave state; {0}'.format(err))
        except ClientConnectException as err:
            logger.warn('client exception occurred when getting ledger information for enclave %s; %s', enclave_id, str(err))
            raise Error(http.BAD_REQUEST, 'could not retrieve enclave state; {0}'.format(err))

        # Get contract state
        try:
            logger.debug('retrieve information for contract <%s>', contract_id)
            contract_info = self.__registry_helper.get_contract_dict(contract_id)
            logger.debug("contract_info from ledger: %s", contract_info)
        except BaseException as err:
            logger.warn('exception occurred when getting ledger information for contract %s; %s', contract_id, str(err))
            raise Error(http.BAD_REQUEST, 'could not retrieve contract state; {0}'.format(err))
        except ClientConnectException as err:
            logger.warn('client exception occurred when getting ledger information for contract %s; %s', contract_id, str(err))
            raise Error(http.BAD_REQUEST, 'could not retrieve contract state; {0}'.format(err))

        # make sure that the signer of this request is really the owner of the contract
        try :
            # make sure that the signer of this request is really the owner of the contract
            # PdoContractInfo.pdo_contract_creator_pem_key is the VerifyingKey
            logger.debug("Contract creator's public key: %s", contract_info['pdo_contract_creator_pem_key'])
            logger.debug("Expected public key: %s", opk)
            assert contract_info['pdo_contract_creator_pem_key'] == opk
        except :
            logger.error('request to create secret did not come from the contract owner; %s != %s', contracttxn.OriginatorID, opk)
            raise Error(http.NOT_ALLOWED, 'operation not allowed for {0}'.format(opk))

        # make sure the provisioning service is allowed to access contract by the checking the list of allowed provisioning services
        try :
            logger.debug("Contract allowed service ids: %s", contract_info['provisioning_service_ids'])
            logger.debug("Expected provisioning service id: %s", self.PSPK)
            assert self.PSPK in contract_info['provisioning_service_ids']
        except :
            logger.error('This Pservice is not the list of allowed provisioning services, PSerivce ID: %s', self.PSPK)
            raise Error(http.NOT_ALLOWED, 'operation not allowed for {0}'.format(self.PSPK))

        # retrieve the sealed secret
        sealed_secret = self._GetContractSecret(contract_id)

        logger.debug("Enclave Info: %s", str(enclave_info))

        # Generate Secret for Contract Enclave, signs unsealed secret with contract enclave encryption key
        esecret = self.Enclave.generate_enclave_secret(
            self.SealedData,
            sealed_secret,
            contract_id,
            opk,
            json.dumps(enclave_info),
            )["enclave_secret"]

        logger.debug("Encrypted secret for contract %s: %s", contract_id, esecret)

        # create the response
        response = dict()
        response['pspk'] = self.PSPK
        response['encrypted_secret'] = esecret

        logger.info('created secret for contract %s and enclave %s', contract_id, enclave_id)
        return response

    ## -----------------------------------------------------------------
    def render_GET(self, request) :
        logger.warn('GET REQUEST: %s', request.uri)
        if request.uri == b'/shutdown' :
            logger.warn('shutdown request received')
            reactor.callLater(1, reactor.stop)
            return ""

        return self.ErrorResponse(request, http.BAD_REQUEST, 'unsupported')

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

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

        try :
            if encoding == 'application/json' :
                minfo = json.loads(data)
            else :
                logger.warn('unknown message encoding')
                return self.ErrorResponse(request, http.BAD_REQUEST, 'unknown message encoding, {0}', encoding)

            reqtype = minfo.get('reqType', '**UNSPECIFIED**')
            if reqtype not in self.RequestMap :
                logger.warn('unknown message type')
                return self.ErrorResponse(request, http.BAD_REQUEST, 'received request for unknown message type')

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

        # and finally execute the associated method and send back the results
        try :
            response = json.dumps(self.RequestMap[reqtype](minfo))

            request.responseHeaders.addRawHeader("content-type", encoding)
            logger.debug('Return Response: %s', response)
            return response.encode('utf-8')

        except Error as e :
            logger.warn('exception while processing request; %s', str(e))
            # return self.ErrorResponse(request, int(e.status), 'exception while processing request {0}; {1}', request.path, str(e))
            return self.ErrorResponse(request, int(e.status), 'exception while processing request')

        except :
            logger.warn('exception while processing http request %s; %s', request.path, traceback.format_exc(20))
            return self.ErrorResponse(request, http.BAD_REQUEST, 'error processing http request {0}', request.path)