def get_aes_key_impl(cls, pin_pubkey, pin_secret, aes_pin_data_key): # Load the data from the pubkey pin_pubkey_hash = bytes(sha256(pin_pubkey)) saved_hps, saved_key, counter = cls._load_pin_fields( pin_pubkey_hash, pin_pubkey, aes_pin_data_key) # Check that the pin provided matches that saved hash_pin_secret = sha256(pin_secret) if compare_digest(saved_hps, hash_pin_secret): # pin-secret matches - correct pin if counter != 0: # Zero the 'bad guess counter' cls._save_pin_fields(pin_pubkey_hash, saved_hps, saved_key, pin_pubkey, aes_pin_data_key) # return the saved key return saved_key # user provided wrong pin if counter >= 2: # pin failed 3 times, overwrite and then remove secret cls._save_pin_fields(pin_pubkey_hash, saved_hps, bytearray(AES_KEY_LEN_256), pin_pubkey, aes_pin_data_key) cls.storage.remove(pin_pubkey_hash) raise Exception("Too many attempts") else: # increment counter cls._save_pin_fields(pin_pubkey_hash, saved_hps, saved_key, pin_pubkey, aes_pin_data_key, counter + 1) raise Exception("Invalid PIN")
def set_pin(cls, cke, payload, aes_pin_data_key): pin_secret, entropy, pin_pubkey = cls._extract_fields(cke, payload) # Make a new aes-key to persist from our and client entropy our_random = os.urandom(32) new_key = hmac_sha256(our_random, entropy) # Persist the pin fields pin_pubkey_hash = bytes(sha256(pin_pubkey)) hash_pin_secret = sha256(pin_secret) saved_key = cls._save_pin_fields(pin_pubkey_hash, hash_pin_secret, new_key, pin_pubkey, aes_pin_data_key) # Combine saved key with (not persisted) pin-secret return cls.make_client_aes_key(pin_secret, saved_key)
def new_static_client_keys(cls): private_key, public_key = PINClientECDH.generate_ec_key_pair() # Cache the pinfile for this client key so we can ensure it is removed pinfile = bytes(sha256(public_key)) cls.pinfiles.add(bytes(pinfile)) # Return the keys and the pin-filename return private_key, public_key, pinfile
def handshake(self, e_ecdh_server_public_key, static_server_signature): ec_sig_verify(self.static_server_public_key, sha256(e_ecdh_server_public_key), EC_FLAG_ECDSA, static_server_signature) # Store the ecdh server public key (ske) self.ecdh_server_public_key = e_ecdh_server_public_key # Cache the shared secrets self.generate_shared_secrets(e_ecdh_server_public_key)
def new_keys(cls): # USE ECDH class just because it's convenient way to make key pairs sig_priv, sig_pub = E_ECDH.generate_ec_key_pair() _, cke = E_ECDH.generate_ec_key_pair() # add the pin_pubkey_hash to the set pin_pubkey_hash = bytes(sha256(sig_pub)) cls.pinfiles.add(pin_pubkey_hash) return sig_priv, sig_pub, cke, pin_pubkey_hash
def _extract_fields(cls, cke, data): assert len(data) == (2 * SHA256_LEN) + EC_SIGNATURE_RECOVERABLE_LEN # secret + entropy + sig pin_secret = data[:SHA256_LEN] entropy = data[SHA256_LEN:SHA256_LEN + SHA256_LEN] sig = data[SHA256_LEN + SHA256_LEN:] # We know mesage the signature is for, so can recover the public key signed_msg = sha256(cke + pin_secret + entropy) client_public_key = ec_sig_to_public_key(signed_msg, sig) return pin_secret, entropy, client_public_key
def file_static_client_keys(cls): pubfile = open(client_public_key, 'rb') public_key = pubfile.read() pubfile.close() privfile = open(client_private_key, 'rb') private_key = privfile.read() privfile.close() # Cache the pinfile for this client key so we can ensure it is removed pinfile = bytes(sha256(public_key)) #cls.pinfiles.add(bytes(pinfile)) # Return the keys and the pin-filename return private_key, public_key, pinfile
def test_save_and_load_pin_fields(self): # Reinitialise keys and secret _, _, _, pinfile = self.new_keys() pin_secret, key_in = self.new_pin_secret(), self.new_entropy() hps_in = sha256(pin_secret) count_in = 5 # Trying to read non-existent file throws (and does not create file) self.assertFalse(PINDb.storage.exists(pinfile)) with self.assertRaises((FileNotFoundError, Exception)) as _: PINDb._load_pin_fields(pinfile, None, None) self.assertFalse(PINDb.storage.exists(pinfile)) user_id = os.urandom(32) aes_pin = bytes(os.urandom(32)) # Save some data - check new file created new_key = PINDb._save_pin_fields(pinfile, hps_in, key_in, user_id, aes_pin, count_in) self.assertTrue(PINDb.storage.exists(pinfile)) # Atm the 'new key' returned should be the one passed in self.assertEqual(new_key, key_in) # Read file back in - ensure fields the same hps_out, key_out, count_out = PINDb._load_pin_fields( pinfile, user_id, aes_pin) self.assertEqual(hps_out, hps_in) self.assertEqual(key_out, key_in) self.assertEqual(count_out, count_in) # Ensure we can set zero the count of an existing file count_in = 0 new_key = PINDb._save_pin_fields(pinfile, hps_in, key_in, user_id, aes_pin, count_in) hps_out, key_out, count_out = PINDb._load_pin_fields( pinfile, user_id, aes_pin) self.assertEqual(hps_out, hps_in) self.assertEqual(key_out, key_in) self.assertEqual(count_out, count_in) # Ensure we can't decrypt the pin with the wrong aes_key, hmac won't match bad_aes = os.urandom(32) with self.assertRaises(AssertionError) as _: PINDb._load_pin_fields(pinfile, user_id, bad_aes)
def server_call(self, private_key, client, endpoint, pin_secret, entropy): # Make and encrypt the payload (ie. pin secret) ske, cke = client.get_key_exchange() sig = ec_sig_from_bytes(private_key, sha256(cke + pin_secret + entropy), EC_FLAG_ECDSA | EC_FLAG_RECOVERABLE) payload = pin_secret + entropy + sig encrypted, hmac = client.encrypt_request_payload(payload) # Make call and parse response urldata = { 'ske': b2h(ske), 'cke': b2h(cke), 'encrypted_data': b2h(encrypted), 'hmac_encrypted_data': b2h(hmac) } response = self.post(endpoint, urldata) encrypted = h2b(response['encrypted_key']) hmac = h2b(response['hmac']) # Return decrypted payload return client.decrypt_response_payload(encrypted, hmac)
# Create the contact and calculate the asset id (Needed for asset registry!) contract = json.dumps( { 'name': name, 'ticker': ticker, 'precision': precision, 'entity': { 'domain': domain }, 'issuer_pubkey': issuer_pubkey, 'version': version }, separators=(',', ':'), sort_keys=True) contract_hash = wally.hex_from_bytes(wally.sha256(contract.encode('ascii'))) a = wally.hex_from_bytes( wally.hex_to_bytes(prev_tx)[::-1]) + wally.hex_from_bytes( struct.pack('<L', int(prev_vout))) a = wally.hex_from_bytes(wally.sha256d(wally.hex_to_bytes(a))) b = a + contract_hash merkle = wally.hex_from_bytes(wally.sha256_midstate(wally.hex_to_bytes(b))) c = merkle + '0000000000000000000000000000000000000000000000000000000000000000' merkle = wally.hex_from_bytes( wally.sha256_midstate(wally.hex_to_bytes(c))[::-1]) res['asset_id'] = merkle res['contract'] = contract res['contract_hash'] = contract_hash # Create the rawissuance transaction contract_hash_rev = wally.hex_from_bytes(
def witness(script_bin): # PUSH(OP_0 PUSH(sha256(script_bin))) return _b('220020') + sha256(script_bin)
entropy) def set_pin(self, private_key, pin_secret, entropy): # Create new ephemeral client, initiate handshake, and make call client = self.new_client_handshake() return self.server_call(private_key, client, 'set_pin', pin_secret, entropy) if __name__ == '__main__': test = PinServerClient() # Make ourselves a static key pair for this logical client priv_key, _, _ = test.file_static_client_keys() # The 'correct' client pin pin_secret = bytes(sha256(b'pippo')) # Make a new client and set the pin secret to get a new aes key aeskey_s = test.set_pin(priv_key, pin_secret, test.new_entropy()) if not len(aeskey_s) == AES_KEY_LEN_256: print('dimension!') iv = priv_key[0:16] encrypted = aes.AES(aeskey_s).encrypt_ctr(b'Attack at dawn', iv) iv = priv_key[0:16] print(aes.AES(aeskey_s).decrypt_ctr(encrypted, iv)) print('---') # Get key with a new client, with the correct pin secret (new entropy) for attempt in range(5): aeskey_g = test.get_pin(priv_key, pin_secret, test.new_entropy())
def _sign_with_static_key(cls, msg): cls._load_private_key() hashed = sha256(msg) return ec_sig_from_bytes(cls.STATIC_SERVER_PRIVATE_KEY, hashed, EC_FLAG_ECDSA)
def make_payload(signing_key, cke, secret_in, entropy_in): # Build the expected payload sig = ec_sig_from_bytes(signing_key, sha256(cke + secret_in + entropy_in), EC_FLAG_ECDSA | EC_FLAG_RECOVERABLE) return secret_in + entropy_in + sig
def get_shared_nonce(self, pubkey: bytes, script: bytes) -> bytes: our_privkey = self.get_private_blinding_key(script) nonce = wally.sha256(wally.ecdh(pubkey, our_privkey)) return nonce
def issuer(asset_amount, asset_address, token_amount, token_address, issuer_pubkey, name, ticker, precision, domain): data = {} version = 0 # don't change blind = False feerate = 0.00003000 asset_amount = int(asset_amount) / 10**(8 - int(precision)) token_amount = int(token_amount) / 10**(8 - int(precision)) # Create funded base tx base = host.call('createrawtransaction', [], [{'data': '00'}]) funded = host.call('fundrawtransaction', base, {'feeRate': feerate}) # Create the contact and calculate the asset id (Needed for asset registry!) contract = json.dumps( { 'name': name, 'ticker': ticker, 'precision': int(precision), 'entity': { 'domain': domain }, 'issuer_pubkey': issuer_pubkey, 'version': version }, separators=(',', ':'), sort_keys=True) contract_hash = wally.hex_from_bytes(wally.sha256( contract.encode('ascii'))) data['contract'] = contract # Create the rawissuance transaction contract_hash_rev = wally.hex_from_bytes( wally.hex_to_bytes(contract_hash)[::-1]) rawissue = host.call('rawissueasset', funded['hex'], [{ 'asset_amount': asset_amount, 'asset_address': asset_address, 'token_amount': token_amount, 'token_address': token_address, 'blind': blind, 'contract_hash': contract_hash_rev }]) # Blind the transaction blind = host.call('blindrawtransaction', rawissue[0]['hex'], True, [], False) # Sign transaction signed = host.call('signrawtransactionwithwallet', blind) decoded = host.call('decoderawtransaction', signed['hex']) data['asset_id'] = decoded['vin'][0]['issuance']['asset'] # Test transaction test = host.call('testmempoolaccept', [signed['hex']]) if test[0]['allowed'] is True: txid = host.call('sendrawtransaction', signed['hex']) data['txid'] = txid data['registry'] = json.dumps({ 'asset_id': data['asset_id'], 'contract': json.loads(data['contract']) }) return data
import sys b2h = wally.hex_from_bytes if (len(sys.argv) != 2): print("Use " + sys.argv[0] + " minikey") exit() minikey = sys.argv[1] PREFIX = int('80', 16) # wif prv VERSION = int('39', 16) # address CA_PREFIX = wally.WALLY_CA_PREFIX_LIQUID # 0c, confidential address # check minikey is valid minikey_checksum = b2h(wally.sha256((minikey + '?').encode())) assert minikey_checksum[: 2] == '00', 'Invalid minikey, incorrect checksum: {}'.format( minikey_checksum) # compute private key private_key = wally.sha256(minikey.encode()) # check that private_key is valid wally.ec_private_key_verify(private_key) # compute private key in WIF format private_key_wif = wally.wif_from_bytes(private_key, PREFIX, wally.WALLY_WIF_FLAG_COMPRESSED) # compute blinding key
def witness_program(self): return wally.sha256(self.witness_script)