def __add_enclave_secrets(ledger_config, contract_id, client_keys, enclaveclients, provclients) : """Create and provision the encrypted secrets for each of the enclaves that will be provisioned for this contract. """ secrets = {} encrypted_state_encryption_keys = {} for enclaveclient in enclaveclients: psecrets = [] for provclient in provclients: # Get a pspk:esecret pair from the provisioning service for each enclave sig_payload = pcrypto.string_to_byte_array(enclaveclient.enclave_id + contract_id) secretinfo = provclient.get_secret(enclaveclient.enclave_id, contract_id, client_keys.verifying_key, client_keys.sign(sig_payload)) logger.debug("pservice secretinfo: %s", secretinfo) # Add this pspk:esecret pair to the list psecrets.append(secretinfo) # Print all of the secret pairs generated for this particular enclave logger.debug('psecrets for enclave %s : %s', enclaveclient.enclave_id, psecrets) # Verify those secrets with the enclave esresponse = enclaveclient.verify_secrets(contract_id, client_keys.verifying_key, psecrets) logger.debug("verify_secrets response: %s", esresponse) # Store the ESEK mapping in a dictionary key'd by the enclave's public key (ID) encrypted_state_encryption_keys[enclaveclient.enclave_id] = esresponse['encrypted_state_encryption_key'] # Add this spefiic enclave to the contract add_enclave_to_contract(ledger_config, client_keys, contract_id, enclaveclient.enclave_id, psecrets, esresponse['encrypted_state_encryption_key'], esresponse['signature']) return encrypted_state_encryption_keys
def compute_pdo_ccl_signature(verb, private_key, enclave_id, enclave_signature, channel_id, contract_id, creator_public_key_pem, contract_code_hash, message_hash, contract_metadata_hash, current_state_hash, previous_state_hash, dependency_list): k = crypto.SIG_PrivateKey(private_key) message_byte_array = crypto.string_to_byte_array(enclave_id) message_byte_array += crypto.base64_to_byte_array(enclave_signature) message_byte_array += crypto.string_to_byte_array(channel_id) message_byte_array += crypto.string_to_byte_array(contract_id) message_byte_array += crypto.base64_to_byte_array(contract_code_hash) message_byte_array += crypto.base64_to_byte_array(message_hash) if verb == 'initialize': #in ccl initialize, previous state hash and dependencies are supposed to be empty message_byte_array += crypto.string_to_byte_array( creator_public_key_pem) message_byte_array += crypto.base64_to_byte_array( contract_metadata_hash) message_byte_array += crypto.base64_to_byte_array(current_state_hash) else: message_byte_array += crypto.base64_to_byte_array(previous_state_hash) message_byte_array += crypto.base64_to_byte_array(current_state_hash) for d in dependency_list: message_byte_array += crypto.string_to_byte_array(d.contract_id) message_byte_array += crypto.string_to_byte_array(d.state_hash) signature = k.SignMessage(message_byte_array) encoded_signature = crypto.byte_array_to_base64(signature) logger.debug("signed message string: " + crypto.byte_array_to_base64(message_byte_array)) logger.debug("signed message hash: " + crypto.byte_array_to_hex( crypto.compute_message_hash(message_byte_array))) logger.debug("signature: %s", encoded_signature) return encoded_signature
def hashed_identity(self): key_byte_array = crypto.string_to_byte_array(self.txn_public) hashed_txn_key = crypto.compute_message_hash(key_byte_array) encoded_hashed_key = crypto.byte_array_to_hex(hashed_txn_key) encoded_hashed_key = encoded_hashed_key.lower() return encoded_hashed_key
def __encrypt_request(self): serialized_byte_array = crypto.string_to_byte_array( self.__serialize_for_encryption()) encrypted_request = crypto.SKENC_EncryptMessage( self.session_key, self.session_iv, serialized_byte_array) return crypto.byte_array_to_base64(encrypted_request)
def CreateAndRegisterContract(config, contract_info, creator_keys): ledger_config = config.get('Sawtooth') contract_config = config.get('Contract') contract_creator_id = creator_keys.identity contract_name = contract_info['Name'] source_file = contract_info['Source'] search_path = contract_config['SourceSearchPath'] contract_code = ContractCode.create_from_scheme_file( contract_name, source_file, search_path=search_path) # -------------------------------------------------- logger.info('register the contract') # -------------------------------------------------- pservice_urls = contract_info.get("ProvisioningServices") provisioning_services = list( map(lambda url: ProvisioningServiceClient(url), pservice_urls)) provisioning_service_keys = list( map(lambda svc: svc.identity, provisioning_services)) contract_id = register_contract(ledger_config, creator_keys, contract_code, provisioning_service_keys) logger.info('registered the contract as %s', contract_id) contract_state = ContractState.create_new_state(contract_id) contract = Contract(contract_code, contract_state, contract_id, contract_creator_id) # -------------------------------------------------- logger.info('provision enclaves') # -------------------------------------------------- eservice_urls = contract_info.get("EnclaveServices") enclave_services = list( map(lambda url: GetEnclaveServiceByURL(url), eservice_urls)) for eservice in enclave_services: secret_list = [] for pservice in provisioning_services: message = pcrypto.string_to_byte_array(eservice.enclave_id + contract_id) signature = creator_keys.sign(message) secret = pservice.get_secret(eservice.enclave_id, contract_id, creator_keys.verifying_key, signature) secret_list.append(secret) secretinfo = eservice.verify_secrets(contract_id, contract_creator_id, secret_list) encrypted_state_encryption_key = secretinfo[ 'encrypted_state_encryption_key'] signature = secretinfo['signature'] txnid = add_enclave_to_contract(ledger_config, creator_keys, contract_id, eservice.enclave_id, secret_list, encrypted_state_encryption_key, signature) contract.set_state_encryption_key(eservice.enclave_id, encrypted_state_encryption_key) # -------------------------------------------------- logger.info('create the initial contract state') # -------------------------------------------------- eservice = random.choice(enclave_services) initialize_request = contract.create_initialize_request( creator_keys, eservice) initialize_response = initialize_request.evaluate() if initialize_response.status is False: emessage = initialize_response.result logger.warn('initialization for contract %s failed; %s', contract_name, emessage) raise Exception('initialization failed; {}'.format(emessage)) contract.set_state(initialize_response.encrypted_state) logger.info('initial state created') # -------------------------------------------------- logger.info('save the initial state in the ledger') # -------------------------------------------------- txnid = initialize_response.submit_initialize_transaction(ledger_config, wait=30) return contract
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_info(enclave_id) logger.debug("enclave information retrieved: %s", enclave_info) except Exception as err: logger.error( 'exception occurred when getting ledger information for enclave %s; %s', enclave_id, str(err)) raise Exception( '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_info( contract_id) logger.debug("contract_info from ledger: %s", contract_info) except Exception as err: logger.error( 'exception occurred when getting ledger information for contract %s; %s', contract_id, str(err)) raise Exception( '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 _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.Serialize()) assert self.PSPK.Serialize( ) in contract_info['provisioning_service_ids'] except: logger.error( 'This Pservice is not the list of allowed provisioning services, PSerivce ID: %s', self.PSPK.Serialize()) raise Error( http.NOT_ALLOWED, 'operation not allowed for {0}'.format(self.PSPK.Serialize())) # retrieve the secret secret = self._GetContractSecret(contract_id) # create the signature message = secret + enclave_id + contract_id + opk secretsig = pcrypto.byte_array_to_hex( self.SigningKey.SignMessage(pcrypto.string_to_byte_array(message))) # pad secret to required max size # TODO: Eventually this requirement needs to be fixed in the crypto library itself required_padding = 2 * pcrypto.MAX_SIG_SIZE - len(secretsig) secretsig = secretsig + ('0' * required_padding) enclavekey = pcrypto.PKENC_PublicKey(enclave_info['encryption_key']) esecret = pcrypto.byte_array_to_base64( enclavekey.EncryptMessage( pcrypto.string_to_byte_array(secret + secretsig))) logger.debug("Encrypted secret for contract %s: %s", contract_id, esecret) # create the response response = dict() response['pspk'] = self.PSPK.Serialize() response['encrypted_secret'] = esecret logger.info('created secret for contract %s and enclave %s', contract_id, enclave_id) return response
def compute_hash(self): return crypto.compute_message_hash( crypto.string_to_byte_array(self.serialize_for_hash()))