def test_check_invalid_digit(self): priv_chars = list(KEY1_PRIV_HEX) priv_chars[3] = 'i' with self.assertRaises(ParseError): Secp256k1PrivateKey.from_hex(''.join(priv_chars)) pub_chars = list(KEY1_PUB_HEX) pub_chars[3] = 'i' with self.assertRaises(ParseError): Secp256k1PublicKey.from_hex(''.join(pub_chars))
def test_key_signature_validation(self): signature, message, pubkey = self.test_key_signing() public_key = Secp256k1PublicKey.from_hex(pubkey) context = sawtooth_signing.create_context("secp256k1") self.assertTrue(context.verify(signature, bytes(message, "utf8"), public_key)) self.assertFalse( context.verify(signature, bytes(message + "foo", "utf8"), public_key) ) other = Secp256k1PublicKey.from_hex(Key().public_key) self.assertFalse(context.verify(signature, bytes(message, "utf8"), other))
def is_valid_batch(batch): # validate batch signature header = BatchHeader() header.ParseFromString(batch.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(batch.header_signature, batch.header, public_key): LOGGER.debug("batch failed signature validation: %s", batch.header_signature) return False # validate all transactions in batch for txn in batch.transactions: if not is_valid_transaction(txn): return False txn_header = TransactionHeader() txn_header.ParseFromString(txn.header) if txn_header.batcher_public_key != header.signer_public_key: LOGGER.debug("txn batcher public_key does not match signer" "public_key for batch: %s txn: %s", batch.header_signature, txn.header_signature) return False return True
def verify(self, signature, message, public_key=None): """Verifies a message was signed by this Key""" if public_key is None: public_key = self._public_key elif isinstance(public_key, str): public_key = Secp256k1PublicKey.from_hex(public_key) return self._context.verify(signature, message, public_key)
def verify_nonce(nonce, checksum, message, hex_public_key): ##message is hex encoded message = binascii.unhexlify(message) public_key = Secp256k1PublicKey.from_hex(hex_public_key) unserialized = public_key.secp256k1_public_key.ecdsa_deserialize(message) result = public_key.secp256k1_public_key.ecdsa_verify(str(nonce).encode(), unserialized) return result
def is_valid_batch(batch): # validate batch signature header = BatchHeader() header.ParseFromString(batch.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(batch.header_signature, batch.header, public_key): LOGGER.debug("batch failed signature validation: %s", batch.header_signature) return False # validate all transactions in batch for txn in batch.transactions: if not is_valid_transaction(txn): return False txn_header = TransactionHeader() txn_header.ParseFromString(txn.header) if txn_header.batcher_public_key != header.signer_public_key: LOGGER.debug( "txn batcher public_key does not match signer" "public_key for batch: %s txn: %s", batch.header_signature, txn.header_signature) return False return True
def assertIsPublicKeyBytes(self, key): """Sanity checks a public key in bytes""" self.assertIsInstance(key, bytes) self.assertEqual(len(key), PUBLIC_KEY_LENGTH) key = Secp256k1PublicKey.from_hex(str(binascii.hexlify(key), "ascii")) self.assertIsPublicKeySecp256k1(key) return key
def assertIsPublicKeyHex(self, key): """Sanity checks a hexidecimal string public key""" self.assertIsInstance(key, str) self.assertTrue(PUBLIC_KEY_PATTERN.match(key)) key = Secp256k1PublicKey.from_hex(key) self.assertIsPublicKeySecp256k1(key) return key
def do_test_verification(): context = create_context("secp256k1") factory = CryptoFactory(context) pub_key1 = Secp256k1PublicKey.from_hex(KEY1_PUB_HEX) context.verify(signature=MSG1_KEY1_SIG, message=MSG1.encode(), public_key=pub_key1)
def verify_signature(message, signature, public_key): try: context = create_context("secp256k1") pubkey = Secp256k1PublicKey.from_hex(public_key) result = context.verify(signature,message.encode(),pubkey) return result except Exception: return False
def test_hex_key(self): priv_key = Secp256k1PrivateKey.from_hex(KEY1_PRIV_HEX) self.assertEqual(priv_key.get_algorithm_name(), "secp256k1") self.assertEqual(priv_key.as_hex(), KEY1_PRIV_HEX) pub_key = Secp256k1PublicKey.from_hex(KEY1_PUB_HEX) self.assertEqual(pub_key.get_algorithm_name(), "secp256k1") self.assertEqual(pub_key.as_hex(), KEY1_PUB_HEX)
def test_verification(self): context = create_context("secp256k1") self.assertEqual(context.get_algorithm_name(), "secp256k1") pub_key1 = Secp256k1PublicKey.from_hex(KEY1_PUB_HEX) self.assertEqual(pub_key1.get_algorithm_name(), "secp256k1") self.assertEqual(pub_key1.as_hex(), KEY1_PUB_HEX) result = context.verify(MSG1_KEY1_SIG, MSG1.encode(), pub_key1) self.assertEqual(result, True)
def test_verification_error(self): context = create_context("secp256k1") self.assertEqual(context.get_algorithm_name(), "secp256k1") pub_key1 = Secp256k1PublicKey.from_hex(KEY1_PUB_HEX) self.assertEqual(pub_key1.get_algorithm_name(), "secp256k1") self.assertEqual(pub_key1.as_hex(), KEY1_PUB_HEX) # This signature doesn't match for MSG1/KEY1 result = context.verify(MSG2_KEY2_SIG, MSG1.encode(), pub_key1) self.assertEqual(result, False)
def verify_wait_certificate(cls, certificate, poet_public_key): # Since the signing module uses a hex-encoded string as the canonical # format for public keys and we should be handed a public key that was # part of signup information created by us, don't bother decoding # the public key. try: poet_public_key = Secp256k1PublicKey.from_hex(poet_public_key) except ParseError: raise \ ValueError( 'Invalid signup data. Badly formatted poet key(s).') if not \ cls._context.verify( certificate.signature, certificate.serialize().encode(), poet_public_key): raise ValueError('Wait certificate signature does not match')
def verify_wait_certificate(cls, certificate, poet_public_key): # Since the signing module uses a hex-encoded string as the canonical # format for public keys and we should be handed a public key that was # part of signup information created by us, don't bother decoding # the public key. try: poet_public_key = Secp256k1PublicKey.from_hex(poet_public_key) except ParseError: raise \ ValueError( 'Invalid signup data. Badly formatted poet key(s).') if not \ cls._context.verify( certificate.signature, certificate.serialize().encode(), poet_public_key): raise ValueError('Wait certificate signature does not match')
def __init__(self, private_key=None, public_key=None): """ Key() -- generates a new key Key(private_key:str) -- Uses the private key passed """ self._context = create_context(ELLIPTIC_CURVE_ALGORITHM) if private_key is None and public_key is None: private_key = Secp256k1PrivateKey.new_random() if isinstance(private_key, str): private_key = Secp256k1PrivateKey.from_hex(private_key) if isinstance(public_key, str): public_key = Secp256k1PublicKey.from_hex(public_key) if public_key is None and private_key is not None: public_key = self._context.get_public_key(private_key) self._public_key = public_key self._private_key = private_key
def is_valid_block(block): # validate block signature header = BlockHeader() header.ParseFromString(block.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(block.header_signature, block.header, public_key): LOGGER.debug("block failed signature validation: %s", block.header_signature) return False # validate all batches in block. These are not all batches in the # batch_ids stored in the block header, only those sent with the block. if not all(map(is_valid_batch, block.batches)): return False return True
def is_valid_block(block): # validate block signature header = BlockHeader() header.ParseFromString(block.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(block.header_signature, block.header, public_key): LOGGER.debug("block failed signature validation: %s", block.header_signature) return False # validate all batches in block. These are not all batches in the # batch_ids stored in the block header, only those sent with the block. if not all(map(is_valid_batch, block.batches)): return False return True
def ecdsa_signature_verify(public_key, signature, raw_message): secp_public = Secp256k1PublicKey.from_hex(public_key) #unhexlify signature try: signature = binascii.unhexlify(signature) except Exception as e: logging.error("Signatures are not in valid hex format") raise ApiInternalError(e) unserialized = secp_public.secp256k1_public_key.ecdsa_deserialize(signature) if isinstance(raw_message, int): raw_message = str(raw_message) if isinstance(raw_message, str): raw_message = raw_message.encode() return secp_public.secp256k1_public_key.ecdsa_verify(raw_message, unserialized)
def is_valid_transaction(txn): # validate transactions signature header = TransactionHeader() header.ParseFromString(txn.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(txn.header_signature, txn.header, public_key): LOGGER.debug("transaction signature invalid for txn: %s", txn.header_signature) return False # verify the payload field matches the header txn_payload_sha512 = hashlib.sha512(txn.payload).hexdigest() if txn_payload_sha512 != header.payload_sha512: LOGGER.debug( "payload doesn't match payload_sha512 of the header" "for txn: %s", txn.header_signature) return False return True
def is_valid_transaction(txn): # validate transactions signature header = TransactionHeader() header.ParseFromString(txn.header) context = create_context('secp256k1') public_key = Secp256k1PublicKey.from_hex(header.signer_public_key) if not context.verify(txn.header_signature, txn.header, public_key): LOGGER.debug("transaction signature invalid for txn: %s", txn.header_signature) return False # verify the payload field matches the header txn_payload_sha512 = hashlib.sha512(txn.payload).hexdigest() if txn_payload_sha512 != header.payload_sha512: LOGGER.debug("payload doesn't match payload_sha512 of the header" "for txn: %s", txn.header_signature) return False return True
def _store_certificate(self, context, signer_pubkey, transaction_payload): address = self.make_address_from_data(transaction_payload.certificate_raw) data = get_data(context, CertificateStorage, address) if data: raise InvalidTransaction('This certificate is already registered.') certificate = x509.load_der_x509_certificate(bytes.fromhex(transaction_payload.certificate_raw), default_backend()) if transaction_payload.cert_signer_public_key: cert_signer_pubkey = load_pem_public_key(transaction_payload.cert_signer_public_key.encode('utf-8'), backend=default_backend()) else: cert_signer_pubkey = certificate.public_key() try: cert_signer_pubkey.verify(bytes.fromhex(transaction_payload.signature_crt), bytes.fromhex(transaction_payload.signature_rem), padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256()) except InvalidSignature: raise InvalidTransaction('signature_crt mismatch') crt_hash = hashlib.sha512(transaction_payload.certificate_raw.encode('utf-8')).hexdigest().encode('utf-8') sawtooth_signing_ctx = Secp256k1Context() sawtooth_signing_pubkey = Secp256k1PublicKey.from_hex(signer_pubkey) sawtooth_signing_check_res = \ sawtooth_signing_ctx.verify(transaction_payload.signature_rem, crt_hash, sawtooth_signing_pubkey) if not sawtooth_signing_check_res: raise InvalidTransaction('signature_rem mismatch with signer key {}'.format(signer_pubkey)) subject = certificate.subject issuer = certificate.issuer organization = issuer.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)[0].value uid = issuer.get_attributes_for_oid(NameOID.USER_ID)[0].value valid_from = certificate.not_valid_before valid_until = certificate.not_valid_after if organization != CERT_ORGANIZATION: raise InvalidTransaction('The organization name should be set to REMME. The actual name is {}' .format(organization)) if uid != signer_pubkey: raise InvalidTransaction('The certificate should be sent by its signer. Certificate signed by {}. ' 'Transaction sent by {}.'.format(uid, signer_pubkey)) if valid_until - valid_from > CERT_MAX_VALIDITY: raise InvalidTransaction('The certificate validity exceeds the maximum value.') fingerprint = certificate.fingerprint(hashes.SHA512()).hex()[:64] data = CertificateStorage() data.hash = fingerprint data.owner = signer_pubkey data.revoked = False account_address, account = TokenHandler.get_account_by_pub_key(context, signer_pubkey) if account.balance < CERT_STORE_PRICE: raise InvalidTransaction('Not enough tokens to register a new certificate. Current balance: {}' .format(account.balance)) account.balance -= CERT_STORE_PRICE LOGGER.info('Registered a new certificate on address {}. Fingerprint: {}'.format(address, fingerprint)) return {address: data, account_address: account}
def handle(self, connection_id, message_content): """ When the validator receives an AuthorizationChallengeSubmit message, it will verify the public key against the signature. If the public key is verified, the requested roles will be checked against the stored roles to see if the public key is included in the policy. If the node’s response is accepted, the node’s public key will be stored and the requester may start sending messages for the approved roles. If the requester wanted a role that is either not available on the endpoint, the requester does not have access to one of the roles requested, or the previous message was not an AuthorizationChallengeRequest, the challenge will be rejected and the connection will be closed. """ if self._network.get_connection_status(connection_id) != \ ConnectionStatus.AUTH_CHALLENGE_REQUEST: LOGGER.debug( "Connection's previous message was not a" " AuthorizationChallengeRequest, Remove connection to" "%s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() auth_challenge_submit = AuthorizationChallengeSubmit() auth_challenge_submit.ParseFromString(message_content) try: payload = self._challenge_payload_cache[connection_id] except KeyError: LOGGER.debug( "Connection's challenge payload expired before a" "response was received. %s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() context = create_context('secp256k1') try: public_key = Secp256k1PublicKey.from_hex( auth_challenge_submit.public_key) except ParseError: LOGGER.warning( 'Authorization Challenge Request cannot be ' 'verified. Invalid public key %s', auth_challenge_submit.public_key) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() if not context.verify(auth_challenge_submit.signature, payload, public_key): LOGGER.warning( "Signature was not able to be verifed. Remove " "connection to %s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() roles = self._network.roles for role in auth_challenge_submit.roles: if role == RoleType.Value("NETWORK") or role == \ RoleType.Value("ALL"): permitted = False if "network" in roles: permitted = self._permission_verifier.check_network_role( auth_challenge_submit.public_key) if not permitted: return AuthorizationChallengeSubmitHandler \ ._network_violation_result() self._network.update_connection_public_key( connection_id, auth_challenge_submit.public_key) if RoleType.Value("NETWORK") in auth_challenge_submit.roles: # Need to send ConnectionRequest to authorize ourself with the # connection if they initialized the connection try: is_outbound_connection = self._network.is_outbound_connection( connection_id) except KeyError: # Connection has gone away, drop message return HandlerResult(HandlerStatus.DROP) if not is_outbound_connection: self._network.send_connect_request(connection_id) else: # If this is an outbound connection, authorization is complete # for both connections and peering/topology build out can # begin. self._gossip.connect_success(connection_id) auth_challenge_result = AuthorizationChallengeResult( roles=[RoleType.Value("NETWORK")]) LOGGER.debug("Connection: %s is approved", connection_id) self._network.update_connection_status(connection_id, ConnectionStatus.CONNECTED) return HandlerResult( HandlerStatus.RETURN, message_out=auth_challenge_result, message_type=validator_pb2.Message.AUTHORIZATION_CHALLENGE_RESULT)
def _check_signature(obj, signature, sender_key_str): publicKey = Secp256k1PublicKey.from_hex(sender_key_str) token_serialized = str(cbor.dumps(obj, sort_keys=True)).encode('utf-8') if not create_context('secp256k1').verify(signature, token_serialized, publicKey): raise InvalidTransaction('Invalid signature.')
def _store_public_key_for_other(self, context, signer_pubkey, transaction_payload): """ Store public key for other account. The transaction for account which want to pay for other account public keys storing. A first account -> send payload -> A second account -> send transaction with first account's public key, but sign and pay for storing on own -> Remme-core. So Remme core charges tokens from a second account, but store a first account's public key. Public key owner here is a first account. Arguments: context (sawtooth_sdk.processor.context): context to store updated state (blockchain data). signer_pubkey: transaction sender public key. transaction_payload (pub_key_pb2.NewPubKeyStoreAndPayPayload): payload for storing public key for other. """ new_public_key_payload = transaction_payload.pub_key_payload owner_public_key_as_bytes = transaction_payload.owner_public_key owner_public_key_as_hex = owner_public_key_as_bytes.hex() owner_secp256k1_public_key = Secp256k1PublicKey.from_hex(owner_public_key_as_hex) is_owner_public_key_payload_signature_valid = Secp256k1Context().verify( signature=transaction_payload.signature_by_owner.hex(), message=new_public_key_payload.SerializeToString(), public_key=owner_secp256k1_public_key, ) if not is_owner_public_key_payload_signature_valid: raise InvalidTransaction('Public key owner\'s signature is invalid.') processor = self._get_public_key_processor(transaction_payload=transaction_payload.pub_key_payload) if not processor.verify(): raise InvalidTransaction('Payed public key has invalid signature.') public_key = processor.get_public_key() public_key_to_store_address = self.make_address_from_data(public_key) public_key_to_store_owner_address = AccountHandler().make_address_from_data(owner_public_key_as_hex) payer_for_storing_address = AccountHandler().make_address_from_data(signer_pubkey) public_key_information, public_key_to_store_owner_account, payer_for_storing_account = get_multiple_data(context, [ (public_key_to_store_address, PubKeyStorage), (public_key_to_store_owner_address, Account), (payer_for_storing_address, Account), ]) if public_key_information: raise InvalidTransaction('This public key is already registered.') if public_key_to_store_owner_account is None: public_key_to_store_owner_account = Account() if payer_for_storing_account is None: payer_for_storing_account = Account() if not self._is_public_key_validity_exceeded( valid_from=new_public_key_payload.valid_from, valid_to=new_public_key_payload.valid_to, ): raise InvalidTransaction('The public key validity exceeds the maximum value.') public_key_information = PubKeyStorage() public_key_information.owner = owner_public_key_as_hex public_key_information.payload.CopyFrom(new_public_key_payload) public_key_information.is_revoked = False state = { public_key_to_store_owner_address: public_key_to_store_owner_account, payer_for_storing_address: payer_for_storing_account, public_key_to_store_address: public_key_information, } charging_state = self._charge_for_storing(context=context, address_from=payer_for_storing_address) if charging_state is not None: state.update(charging_state) public_key_to_store_owner_account = self._store_public_key_to_account( public_key_to_store_address=public_key_to_store_address, public_key_to_store_owner_account=public_key_to_store_owner_account, ) state.update({ public_key_to_store_owner_address: public_key_to_store_owner_account, }) return state
def get_public_hex_secp256k1(public_hex_string): """Return and instance of a sawtooth public key""" return Secp256k1PublicKey.from_hex(public_hex_string)
def create_wait_certificate(cls, sealed_signup_data, wait_timer, block_hash): with cls._lock: # Extract keys from the 'sealed' signup data if sealed_signup_data is None: raise ValueError('Sealed Signup Data is None') signup_data = \ json2dict( base64.b64decode(sealed_signup_data.encode()).decode()) poet_private_key = signup_data['poet_private_key'] poet_public_key = signup_data['poet_public_key'] if poet_private_key is None or poet_public_key is None: raise \ ValueError( 'Invalid signup data. No poet key(s).') try: poet_public_key = Secp256k1PublicKey.from_hex(poet_public_key) poet_private_key = Secp256k1PrivateKey.from_hex( poet_private_key) except ParseError: raise \ ValueError( 'Invalid signup data. Badly formatted poet key(s).') # Several criteria need to be met before we can create a wait # certificate: # 1. This signup data was used to sign this timer. # i.e. the key sealed / unsealed by the TEE signed this # wait timer. # 2. This timer has expired # 3. This timer has not timed out # # In a TEE implementation we would check HW counter agreement. # We can't usefully simulate a HW counter though. # i.e. wait_timer.counter_value == signup_data.counter.value # # 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 wait_timer is None or \ not cls._context.verify( wait_timer.signature, wait_timer.serialize().encode(), poet_public_key): raise \ ValueError( 'Validator is not using the current wait timer') is_not_genesis_block = \ (wait_timer.previous_certificate_id != NULL_BLOCK_IDENTIFIER) now = time.time() expire_time = \ wait_timer.request_time + \ 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 = \ wait_timer.request_time + \ 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': 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=wait_timer, nonce=nonce, block_hash=block_hash) wait_certificate.signature = \ cls._context.sign( wait_certificate.serialize().encode(), poet_private_key) # In a TEE implementation we would increment the HW counter here # to prevent replay. # We can't usefully simulate a HW counter though. return wait_certificate
def validate_from_dict(self,token): _check_format(token,"access token",VALIDATION_FORMAT) # state retrival device = token['DE'] result = self._send_request( "state?address={}".format( self._get_address(device))) try: encoded_entries = yaml.safe_load(result)["data"] data_list = [ cbor.loads(base64.b64decode(entry["data"])) for entry in encoded_entries ] state = {x:y[x] for y in data_list for x in y} except BaseException: return None LOGGER.info('checking authorization') # check authorization capability = token['IC'] if capability not in state: return False LOGGER.info('checking delegation chain') # delegation chain check now = int(time.time()) resource = token['RE'] action = token['AC'] current_token = state[capability] parent = current_token['IC'] while parent != None: if parent not in state: raise BaseException parent_token = state[parent] # check time interval if now >= int(parent_token['NA']): return False if now < int(parent_token['NB']): return False # check access rights if resource not in parent_token["AR"]: return False if action not in parent_token["AR"][resource]: return False # next current_token = parent_token parent = current_token['IC'] LOGGER.info('checking signature') # check signature signature = token.pop('SI') if not create_context('secp256k1').verify( signature, str(cbor.dumps(token,sort_keys=True)).encode('utf-8'), Secp256k1PublicKey.from_hex(state[capability]['SU']) ): return False return True
def test_verification(benchmark): context = create_context("secp256k1") factory = CryptoFactory(context) pub_key1 = Secp256k1PublicKey.from_hex(KEY1_PUB_HEX) result = benchmark(context.verify, MSG1_KEY1_SIG, MSG1.encode(), pub_key1) assert result == True
def create_wait_certificate(cls, sealed_signup_data, wait_timer, block_hash): with cls._lock: # Extract keys from the 'sealed' signup data if sealed_signup_data is None: raise ValueError('Sealed Signup Data is None') signup_data = \ json2dict( base64.b64decode(sealed_signup_data.encode()).decode()) poet_private_key = signup_data['poet_private_key'] poet_public_key = signup_data['poet_public_key'] if poet_private_key is None or poet_public_key is None: raise \ ValueError( 'Invalid signup data. No poet key(s).') try: poet_public_key = Secp256k1PublicKey.from_hex(poet_public_key) poet_private_key = Secp256k1PrivateKey.from_hex( poet_private_key) except ParseError: raise \ ValueError( 'Invalid signup data. Badly formatted poet key(s).') # Several criteria need to be met before we can create a wait # certificate: # 1. This signup data was used to sign this timer. # i.e. the key sealed / unsealed by the TEE signed this # wait timer. # 2. This timer has expired # 3. This timer has not timed out # # In a TEE implementation we would check HW counter agreement. # We can't usefully simulate a HW counter though. # i.e. wait_timer.counter_value == signup_data.counter.value # # 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 wait_timer is None or \ not cls._context.verify( wait_timer.signature, wait_timer.serialize().encode(), poet_public_key): raise \ ValueError( 'Validator is not using the current wait timer') is_not_genesis_block = \ (wait_timer.previous_certificate_id != NULL_BLOCK_IDENTIFIER) now = time.time() expire_time = \ wait_timer.request_time + \ 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 = \ wait_timer.request_time + \ 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': 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=wait_timer, nonce=nonce, block_hash=block_hash) wait_certificate.signature = \ cls._context.sign( wait_certificate.serialize().encode(), poet_private_key) # In a TEE implementation we would increment the HW counter here # to prevent replay. # We can't usefully simulate a HW counter though. return wait_certificate
def handle(self, connection_id, message_content): """ When the validator receives an AuthorizationChallengeSubmit message, it will verify the public key against the signature. If the public key is verified, the requested roles will be checked against the stored roles to see if the public key is included in the policy. If the node’s response is accepted, the node’s public key will be stored and the requester may start sending messages for the approved roles. If the requester wanted a role that is either not available on the endpoint, the requester does not have access to one of the roles requested, or the previous message was not an AuthorizationChallengeRequest, the challenge will be rejected and the connection will be closed. """ if self._network.get_connection_status(connection_id) != \ ConnectionStatus.AUTH_CHALLENGE_REQUEST: LOGGER.debug("Connection's previous message was not a" " AuthorizationChallengeRequest, Remove connection to" "%s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() auth_challenge_submit = AuthorizationChallengeSubmit() auth_challenge_submit.ParseFromString(message_content) try: payload = self._challenge_payload_cache[connection_id] except KeyError: LOGGER.warning("Connection's challenge payload expired before a" "response was received. %s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() context = create_context('secp256k1') try: public_key = Secp256k1PublicKey.from_hex( auth_challenge_submit.public_key) except ParseError: LOGGER.warning('Authorization Challenge Request cannot be ' 'verified. Invalid public key %s', auth_challenge_submit.public_key) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() if not context.verify(auth_challenge_submit.signature, payload, public_key): LOGGER.warning("Signature was not able to be verified. Remove " "connection to %s", connection_id) return AuthorizationChallengeSubmitHandler \ ._network_violation_result() roles = self._network.roles for role in auth_challenge_submit.roles: if role == RoleType.Value("NETWORK") or role == \ RoleType.Value("ALL"): permitted = False if "network" in roles: permitted = self._permission_verifier.check_network_role( auth_challenge_submit.public_key) if not permitted: return AuthorizationChallengeSubmitHandler \ ._network_violation_result() self._network.update_connection_public_key( connection_id, auth_challenge_submit.public_key) if RoleType.Value("NETWORK") in auth_challenge_submit.roles: # Need to send ConnectionRequest to authorize ourself with the # connection if they initialized the connection try: is_outbound_connection = self._network.is_outbound_connection( connection_id) except KeyError: # Connection has gone away, drop message return HandlerResult(HandlerStatus.DROP) if not is_outbound_connection: self._network.send_connect_request(connection_id) else: # If this is an outbound connection, authorization is complete # for both connections and peering/topology build out can # begin. self._gossip.connect_success(connection_id) auth_challenge_result = AuthorizationChallengeResult( roles=[RoleType.Value("NETWORK")]) LOGGER.debug("Connection: %s is approved", connection_id) self._network.update_connection_status( connection_id, ConnectionStatus.CONNECTED) return HandlerResult( HandlerStatus.RETURN, message_out=auth_challenge_result, message_type=validator_pb2.Message.AUTHORIZATION_CHALLENGE_RESULT)