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
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, }
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, }
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)