def test_go_sig(): """ go client started with: ethereum -port="40404" -loglevel=5 -nodekeyhex="9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658" -bootnodes="enode://2da47499d52d9161a778e4c711e22e8651cb90350ec066452f9516d1d11eb465d1ec42bb27ec6cd4488b8b6a1a411cb5ef83c16cbb8bee194624bb65fef0f7fd@127.0.0.1:30303" """ r_pubkey = "ab16b8c7fc1febb74ceedf1349944ffd4a04d11802451d02e808f08cb3b0c1c1a9c4e1efb7d309a762baa4c9c8da08890b3b712d1666b5b630d6c6a09cbba171".decode( 'hex') d = { 'signed_data': 'a061e5b799b5bb3a3a68a7eab6ee11207d90672e796510ac455e985bd206e240', 'cmd': 'find_node', 'body': '03f847b840ab16b8c7fc1febb74ceedf1349944ffd4a04d11802451d02e808f08cb3b0c1c1a9c4e1efb7d309a762baa4c9c8da08890b3b712d1666b5b630d6c6a09cbba1718454e869b1', 'signature': '0de032c62e30f4a9f9f07f25ac5377c5a531116147617a6c08f946c97991f351577e53ae138210bdb7447bab53f3398d746d42c64a9ce67a6248e59353f1bc6e01' } priv_seed = 'test' priv_key = mk_privkey(priv_seed) assert priv_key == "9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658".decode( 'hex') my_pubkey = privtopub(priv_key) assert my_pubkey == r_pubkey, (my_pubkey, r_pubkey) go_body = d['body'].decode('hex') # cmd_id, rlp.encoded import rlp target_node_id, expiry = rlp.decode(go_body[1:]) assert target_node_id == r_pubkey # lookup for itself go_signed_data = d['signed_data'].decode('hex') go_signature = d['signature'].decode('hex') b_signature = bitcoin.ecdsa_sign(go_signed_data, priv_key) # base64 encoded! # https://github.com/vbuterin/pybitcointools/blob/master/bitcoin/main.py#L500 my_signature = ecdsa_sign(go_signed_data, priv_key) assert my_signature == ecdsa_sign(go_signed_data, priv_key) # deterministic k assert len(go_signed_data) == 32 # sha3() assert len(go_signature) == 65 assert len(my_signature) == 65 # length is okay try: assert my_signature == go_signature failed = False except: "expected fail, go signatures are not generated with deterministic k" failed = True pass assert failed # decoding works when we signed it assert my_pubkey == ecdsa_recover(go_signed_data, my_signature) # problem we can not decode the pubkey from the go signature # and go can not decode ours ecdsa_recover(go_signed_data, go_signature)
def test_go_sig(): """ go client started with: ethereum -port="40404" -loglevel=5 -nodekeyhex="9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658" -bootnodes="enode://2da47499d52d9161a778e4c711e22e8651cb90350ec066452f9516d1d11eb465d1ec42bb27ec6cd4488b8b6a1a411cb5ef83c16cbb8bee194624bb65fef0f7fd@127.0.0.1:30303" """ r_pubkey = "ab16b8c7fc1febb74ceedf1349944ffd4a04d11802451d02e808f08cb3b0c1c1a9c4e1efb7d309a762baa4c9c8da08890b3b712d1666b5b630d6c6a09cbba171".decode( 'hex') d = {'signed_data': 'a061e5b799b5bb3a3a68a7eab6ee11207d90672e796510ac455e985bd206e240', 'cmd': 'find_node', 'body': '03f847b840ab16b8c7fc1febb74ceedf1349944ffd4a04d11802451d02e808f08cb3b0c1c1a9c4e1efb7d309a762baa4c9c8da08890b3b712d1666b5b630d6c6a09cbba1718454e869b1', 'signature': '0de032c62e30f4a9f9f07f25ac5377c5a531116147617a6c08f946c97991f351577e53ae138210bdb7447bab53f3398d746d42c64a9ce67a6248e59353f1bc6e01'} priv_seed = 'test' priv_key = mk_privkey(priv_seed) assert priv_key == "9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658".decode( 'hex') my_pubkey = privtopub(priv_key) assert my_pubkey == r_pubkey, (my_pubkey, r_pubkey) go_body = d['body'].decode('hex') # cmd_id, rlp.encoded import rlp target_node_id, expiry = rlp.decode(go_body[1:]) assert target_node_id == r_pubkey # lookup for itself go_signed_data = d['signed_data'].decode('hex') go_signature = d['signature'].decode('hex') my_signature = ecdsa_sign(go_signed_data, priv_key) assert my_signature == ecdsa_sign(go_signed_data, priv_key) # deterministic k assert len(go_signed_data) == 32 # sha3() assert len(go_signature) == 65 assert len(my_signature) == 65 # length is okay try: assert my_signature == go_signature failed = False except: "expected fail, go signatures are not generated with deterministic k" failed = True pass assert failed # decoding works when we signed it assert my_pubkey == ecdsa_recover(go_signed_data, my_signature) # problem we can not decode the pubkey from the go signature # and go can not decode ours ecdsa_recover(go_signed_data, go_signature)
def decode_authentication(self, ciphertext): """ 3. optionally, remote decrypts and verifies auth (checks that recovery of signature == H(ephemeral-pubk)) 4. remote generates authAck from remote-ephemeral-pubk and nonce (authAck = authRecipient handshake) optional: remote derives secrets and preemptively sends protocol-handshake (steps 9,11,8,10) """ assert not self.is_initiator if len(ciphertext) < 307: raise FormatError("Ciphertext too short") try: (size, sig, initiator_pubkey, nonce, version) = self.decode_auth_plain(ciphertext) except AuthenticationError: (size, sig, initiator_pubkey, nonce, version) = self.decode_auth_eip8(ciphertext) self.got_eip8_auth = True self.auth_init = ciphertext[:size] # recover initiator ephemeral pubkey from sig # S(ephemeral-privk, ecdh-shared-secret ^ nonce) token = self.ecc.get_ecdh_key(initiator_pubkey) self.remote_ephemeral_pubkey = ecdsa_recover(sxor(token, nonce), sig) if not self.ecc.is_valid_key(self.remote_ephemeral_pubkey): raise InvalidKeyError('invalid remote ephemeral pubkey') self.initiator_nonce = nonce self.remote_pubkey = initiator_pubkey self.remote_version = version return ciphertext[size:]
def unpack(self, message): """ macSize = 256 / 8 = 32 sigSize = 520 / 8 = 65 headSize = macSize + sigSize = 97 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Sha3(buf[macSize:]) """ mdc = message[:32] assert mdc == crypto.sha3(message[32:]) signature = message[32:97] assert len(signature) == 65 signed_data = crypto.sha3(message[97:]) remote_pubkey = crypto.ecdsa_recover(signed_data, signature) assert len(remote_pubkey) == 512 / 8 if not crypto.verify(remote_pubkey, signature, signed_data): raise InvalidSignature() cmd_id = self.decoders['cmd_id'](message[97]) assert cmd_id in self.cmd_id_map.values() payload = rlp.decode(message[98:]) assert isinstance(payload, list) expiration = self.decoders['expiration'](payload.pop()) if time.time() > expiration: raise PacketExpired() return remote_pubkey, cmd_id, payload, mdc
def unpack(self, message): """ macSize = 256 / 8 = 32 sigSize = 520 / 8 = 65 headSize = macSize + sigSize = 97 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Sha3(buf[macSize:]) """ mdc = message[:32] if mdc != crypto.sha3(message[32:]): log.warn('packet with wrong mcd') raise WrongMAC() signature = message[32:97] assert len(signature) == 65 signed_data = crypto.sha3(message[97:]) remote_pubkey = crypto.ecdsa_recover(signed_data, signature) assert len(remote_pubkey) == 512 / 8 if not crypto.verify(remote_pubkey, signature, signed_data): raise InvalidSignature() cmd_id = self.decoders['cmd_id'](message[97]) assert cmd_id in self.cmd_id_map.values() payload = rlp.decode(message[98:]) assert isinstance(payload, list) expiration = self.decoders['expiration'](payload.pop()) if time.time() > expiration: raise PacketExpired() return remote_pubkey, cmd_id, payload, mdc
def recover_1kb(times=1000): alice = get_ecc('secret1') message = ''.join(chr(random.randrange(0, 256)) for i in range(1024)) signature = alice.sign(message) for i in range(times): recovered_pubkey = crypto.ecdsa_recover(message, signature) assert recovered_pubkey == alice.raw_pubkey
def unpack(self, message): """ macSize = 256 / 8 = 32 sigSize = 520 / 8 = 65 headSize = macSize + sigSize = 97 hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] shouldhash := crypto.Sha3(buf[macSize:]) """ mdc = message[:32] if mdc != crypto.sha3(message[32:]): log.warn('packet with wrong mcd') raise WrongMAC() signature = message[32:97] assert len(signature) == 65 signed_data = crypto.sha3(message[97:]) remote_pubkey = crypto.ecdsa_recover(signed_data, signature) assert len(remote_pubkey) == 512 / 8 # if not crypto.verify(remote_pubkey, signature, signed_data): # raise InvalidSignature() cmd_id = self.decoders['cmd_id'](message[97]) cmd = self.rev_cmd_id_map[cmd_id] payload = rlp.decode(message[98:], strict=False) assert isinstance(payload, list) # ignore excessive list elements as required by EIP-8. payload = payload[:self.cmd_elem_count_map.get(cmd, len(payload))] return remote_pubkey, cmd_id, payload, mdc
def test_recover(): alice = get_ecc(b'secret1') message = crypto.sha3(b'hello bob') signature = alice.sign(message) assert len(signature) == 65 assert crypto.verify(alice.raw_pubkey, signature, message) is True recovered_pubkey = crypto.ecdsa_recover(message, signature) assert len(recovered_pubkey) == 64 assert alice.raw_pubkey == recovered_pubkey
def test_recover(): alice = get_ecc('secret1') message = 'hello bob' signature = alice.sign(message) assert len(signature) == 65 assert crypto.verify(alice.raw_pubkey, signature, message) is True recovered_pubkey = crypto.ecdsa_recover(message, signature) assert len(recovered_pubkey) == 64 assert alice.raw_pubkey == recovered_pubkey
def decode_authentication(self, ciphertext): """ 3. optionally, remote decrypts and verifies auth (checks that recovery of signature == H(ephemeral-pubk)) 4. remote generates authAck from remote-ephemeral-pubk and nonce (authAck = authRecipient handshake) optional: remote derives secrets and preemptively sends protocol-handshake (steps 9,11,8,10) """ assert not self.is_initiator self.auth_init = ciphertext try: auth_message = self.ecc.ecies_decrypt(ciphertext) except RuntimeError as e: raise AuthenticationError(e) # S || H(ephemeral-pubk) || pubk || nonce || 0x[0|1] assert len( auth_message ) == 65 + 32 + 64 + 32 + 1 == 194 == self.auth_message_length signature = auth_message[:65] H_initiator_ephemeral_pubkey = auth_message[65:65 + 32] initiator_pubkey = auth_message[65 + 32:65 + 32 + 64] if not self.ecc.is_valid_key(initiator_pubkey): raise InvalidKeyError('invalid initiator pubkey') self.remote_pubkey = initiator_pubkey self.initiator_nonce = auth_message[65 + 32 + 64:65 + 32 + 64 + 32] known_flag = bool(ord(auth_message[65 + 32 + 64 + 32:])) # token or new ecdh_shared_secret if known_flag: self.remote_token_found = True # what todo if remote has token, but local forgot it. # reply with token not found. FIXME!!! token = self.token_by_pubkey.get(initiator_pubkey) assert token # FIXME continue session with ecdh_key and send flag in auth_ack else: token = self.ecc.get_ecdh_key(initiator_pubkey) # verify auth # S(ephemeral-privk, ecdh-shared-secret ^ nonce) signed = sxor(token, self.initiator_nonce) # recover initiator ephemeral pubkey self.remote_ephemeral_pubkey = ecdsa_recover(signed, signature) if not self.ecc.is_valid_key(self.remote_ephemeral_pubkey): raise InvalidKeyError('invalid remote ephemeral pubkey') if not ecdsa_verify(self.remote_ephemeral_pubkey, signature, signed): raise AuthenticationError('could not verify signature') # checks that recovery of signature == H(ephemeral-pubk) assert H_initiator_ephemeral_pubkey == sha3( self.remote_ephemeral_pubkey)
def decode_authentication(self, ciphertext): """ 3. optionally, remote decrypts and verifies auth (checks that recovery of signature == H(ephemeral-pubk)) 4. remote generates authAck from remote-ephemeral-pubk and nonce (authAck = authRecipient handshake) optional: remote derives secrets and preemptively sends protocol-handshake (steps 9,11,8,10) """ assert not self.is_initiator self.auth_init = ciphertext try: auth_message = self.ecc.ecies_decrypt(ciphertext) except RuntimeError as e: raise AuthenticationError(e) # S || H(ephemeral-pubk) || pubk || nonce || 0x[0|1] assert len(auth_message) == 65 + 32 + 64 + 32 + 1 == 194 == self.auth_message_length signature = auth_message[:65] H_initiator_ephemeral_pubkey = auth_message[65:65 + 32] initiator_pubkey = auth_message[65 + 32:65 + 32 + 64] if not self.ecc.is_valid_key(initiator_pubkey): raise InvalidKeyError('invalid initiator pubkey') self.remote_pubkey = initiator_pubkey self.initiator_nonce = auth_message[65 + 32 + 64:65 + 32 + 64 + 32] known_flag = bool(ord(auth_message[65 + 32 + 64 + 32:])) # token or new ecdh_shared_secret if known_flag: self.remote_token_found = True # what todo if remote has token, but local forgot it. # reply with token not found. FIXME!!! token = self.token_by_pubkey.get(initiator_pubkey) assert token # FIXME continue session with ecdh_key and send flag in auth_ack else: token = self.ecc.get_ecdh_key(initiator_pubkey) # verify auth # S(ephemeral-privk, ecdh-shared-secret ^ nonce) signed = sxor(token, self.initiator_nonce) # recover initiator ephemeral pubkey self.remote_ephemeral_pubkey = ecdsa_recover(signed, signature) if not self.ecc.is_valid_key(self.remote_ephemeral_pubkey): raise InvalidKeyError('invalid remote ephemeral pubkey') if not ecdsa_verify(self.remote_ephemeral_pubkey, signature, signed): raise AuthenticationError('could not verify signature') # checks that recovery of signature == H(ephemeral-pubk) assert H_initiator_ephemeral_pubkey == sha3(self.remote_ephemeral_pubkey)
def receive_authentication(self, ciphertext): """ 3. optionally, remote decrypts and verifies auth (checks that recovery of signature == H(ephemeral-pubk)) 4. remote generates authAck from remote-ephemeral-pubk and nonce (authAck = authRecipient handshake) optional: remote derives secrets and preemptively sends protocol-handshake (steps 9,11,8,10) """ auth_message = self.node.ecies_decrypt(ciphertext) # S || H(ephemeral-pubk) || pubk || nonce || 0x[0|1] assert len(auth_message) == 65 + 32 + 64 + 32 + 1 == 194 signature = auth_message[:65] H_remote_ephemeral_pubkey = auth_message[65:65 + 32] remote_pubkey = auth_message[65 + 32:65 + 32 + 64] nonce = auth_message[65 + 32 + 64:65 + 32 + 64 + 32] known_flag = auth_message[65 + 32 + 64 + 32:] # token or new ecdh_shared_secret token_database = dict() # FIXME token_found = False if known_flag == 1: token = token_database.get(remote_pubkey) if token: token_found = True else: token = ecdh_shared_secret = self.node.get_ecdh_key(remote_pubkey) # verify auth # S(ephemeral-privk, ecdh-shared-secret ^ nonce) ecdh_shared_secret = self.node.get_ecdh_key(remote_pubkey) signed = sxor(ecdh_shared_secret, nonce) # recover remote ephemeral pubkey remote_ephemeral_pubkey = ecdsa_recover(signed, signature) assert ecdsa_verify(remote_ephemeral_pubkey, signature, signed) # checks that recovery of signature == H(ephemeral-pubk) assert H_remote_ephemeral_pubkey == sha3(remote_ephemeral_pubkey) return dict(remote_ephemeral_pubkey=remote_ephemeral_pubkey, token=token, token_found=token_found, ecdh_shared_secret=ecdh_shared_secret, remote_pubkey=remote_pubkey, nonce=nonce, known_flag=known_flag)