def e2etest(self, vector): initiator = NoiseConnection.from_name(vector['protocol_name']) responder = NoiseConnection.from_name(vector['protocol_name']) if 'init_psks' in vector and 'resp_psks' in vector: initiator.set_psks(psks=vector['init_psks']) responder.set_psks(psks=vector['resp_psks']) initiator.set_prologue(vector['init_prologue']) initiator.set_as_initiator() self._set_keypairs(vector, initiator, ephemeral=False) assert initiator.get_keypair(Keypair.STATIC).private.private_bytes( encoding=serialization.Encoding.Raw, format=serialization.PrivateFormat.Raw, encryption_algorithm=serialization.NoEncryption( )) == vector['init_static'] responder.set_prologue(vector['resp_prologue']) responder.set_as_responder() self._set_keypairs(vector, responder, ephemeral=False) initiator.start_handshake() responder.start_handshake() # set sender and received s, r = initiator, responder # loop until both sides think the handshake has finished while not initiator.handshake_finished and not responder.handshake_finished: # exchange a message r.read_message(s.write_message()) # Swap roles s, r = r, s assert initiator.get_keypair( Keypair.STATIC).public_bytes == responder.get_keypair( Keypair.REMOTE_STATIC).public_bytes pt = b'this is a test message' # make sure that the initiator can send a message ct = initiator.encrypt(pt) # and is decrypted by the responder assert pt == responder.decrypt(ct) # make sure that the responder can send a message ct = responder.encrypt(pt) # and is decrypted by the initiator assert pt == initiator.decrypt(ct)
def connectToNoise(self, ip, port): print('Trying to connect to', ip + ":" + str(port)) sock = socket.socket() sock.connect((ip, port)) # Create instance of NoiseConnection, set up to use NN handshake pattern, Curve25519 for # elliptic curve keypair, ChaCha20Poly1305 as cipher function and SHA256 for hashing. proto = NoiseConnection.from_name(b'Noise_NN_25519_ChaChaPoly_SHA256') # Set role in this connection as initiator proto.set_as_initiator() # Enter handshake mode proto.start_handshake() # Perform handshake - as we are the initiator, we need to generate first message. # We don't provide any payload (although we could, but it would be cleartext for this pattern). message = proto.write_message() # Send the message to the responder - you may simply use sockets or any other way # to exchange bytes between communicating parties. sock.sendall(message) # Receive the message from the responder received = sock.recv(2048) # Feed the received message into noise payload = proto.read_message(received) self.connection = sock self.noise = proto
def HandleNoiseConnection(self): s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('localhost', self.port)) s.listen(1) print("Waiting connections...") conn, addr = s.accept() print('Accepted connection from', addr) self.noise = NoiseConnection.from_name( b'Noise_NN_25519_ChaChaPoly_SHA256') self.noise.set_as_responder() self.noise.start_handshake() # Perform handshake. Break when finished for action in cycle(['receive', 'send']): if self.noise.handshake_finished: break elif action == 'send': ciphertext = self.noise.write_message() # print('cipherText',ciphertext) conn.sendall(ciphertext) elif action == 'receive': data = conn.recv(2048) plaintext = self.noise.read_message(data) # print('receive',plaintext) print("Noise handshake finished with", addr) self.connection = conn
async def accept_connection(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): """Responder side""" self.reader = reader self.writer = writer self.noise = NoiseConnection.from_name( b"Noise_XK_25519_ChaChaPoly_BLAKE2s") self.noise.set_as_responder() self.noise.set_keypair_from_private_bytes(Keypair.STATIC, self.private_key_bytes) self.noise.start_handshake() first_message = await self.reader.readexactly(0x30) logger.debug("NOISE: Resp -> e, es (%#x bytes)", len(first_message)) self.noise.read_message(first_message) second_message = self.noise.write_message() logger.debug("NOISE: Resp <- e, ee (%#x bytes)", len(second_message)) assert len(second_message) == 0x30 self.writer.write(second_message) third_message = await self.reader.readexactly(0x40) logger.debug("NOISE: Resp -> s, se (%#x bytes)", len(third_message)) self.noise.read_message(third_message) assert self.noise.handshake_finished # Exchange the "x" coordinate of the secret shares self.send_encrypted(self.my_secret_x.to_bytes(32, "big")) peer_secret = await self.recv_exactly_encrypted(32) self.peer_secret_x = int.from_bytes(peer_secret, "big") logger.debug("NOISE: Responded to encrypted connection %d <- %d", self.my_secret_x, self.peer_secret_x)
async def _perform_handshake(self, expected_name: Optional[str]) -> None: await self._write_frame(b"") # ClientHello prologue = b"NoiseAPIInit" + b"\x00\x00" server_hello = await self._read_frame() # ServerHello if not server_hello: raise HandshakeAPIError("ServerHello is empty") # First byte of server hello is the protocol the server chose # for this session. Currently only 0x01 (Noise_NNpsk0_25519_ChaChaPoly_SHA256) # exists. chosen_proto = server_hello[0] if chosen_proto != 0x01: raise HandshakeAPIError( f"Unknown protocol selected by client {chosen_proto}") # Check name matches expected name (for noise sessions, this is done # during hello phase before a connection is set up) # Server name is encoded as a string followed by a zero byte after the chosen proto byte server_name_i = server_hello.find(b"\0", 1) if server_name_i != -1: # server name found, this extension was added in 2022.2 server_name = server_hello[1:server_name_i].decode() if expected_name is not None and expected_name != server_name: raise BadNameAPIError( f"Server sent a different name '{server_name}'", server_name) self._proto = NoiseConnection.from_name( b"Noise_NNpsk0_25519_ChaChaPoly_SHA256") self._proto.set_as_initiator() self._proto.set_psks(_decode_noise_psk(self._noise_psk)) self._proto.set_prologue(prologue) self._proto.start_handshake() _LOGGER.debug("Starting handshake...") do_write = True while not self._proto.handshake_finished: if do_write: msg = self._proto.write_message() await self._write_frame(b"\x00" + msg) else: msg = await self._read_frame() if not msg: raise HandshakeAPIError("Handshake message too short") if msg[0] != 0: explanation = msg[1:].decode() if explanation == "Handshake MAC failure": raise InvalidEncryptionKeyAPIError( "Invalid encryption key") raise HandshakeAPIError( f"Handshake failure: {explanation}") self._proto.read_message(msg[1:]) do_write = not do_write _LOGGER.debug("Handshake complete!") self._ready_event.set()
def _create_noise_channel(self, noise_config: BitBoxNoiseConfig) -> NoiseConnection: if self._raw_query(OP_I_CAN_HAS_HANDSHAEK) != RESPONSE_SUCCESS: self.close() raise Exception("Couldn't kick off handshake") # init noise channel noise = NoiseConnection.from_name(b"Noise_XX_25519_ChaChaPoly_SHA256") noise.set_as_initiator() private_key = noise_config.get_app_static_privkey() if private_key is None: private_key = os.urandom(32) noise_config.set_app_static_privkey(private_key) noise.set_keypair_from_private_bytes(Keypair.STATIC, private_key) noise.set_prologue(b"Noise_XX_25519_ChaChaPoly_SHA256") noise.start_handshake() start_handshake_status, start_handshake_reply = self._handshake_query(noise.write_message()) if start_handshake_status != RESPONSE_SUCCESS: self.close() raise Exception("Handshake process request failed.") noise.read_message(start_handshake_reply) remote_static_key = noise.noise_protocol.handshake_state.rs.public_bytes assert not noise.handshake_finished send_msg = noise.write_message() assert noise.handshake_finished pairing_code = base64.b32encode(noise.get_handshake_hash()).decode("ascii") handshake_finish_status, response = self._handshake_query(send_msg) if handshake_finish_status != RESPONSE_SUCCESS: self.close() raise Exception("Handshake conclusion failed.") # Check if we recognize the device's public key pairing_verification_required_by_host = True if noise_config.contains_device_static_pubkey(remote_static_key): pairing_verification_required_by_host = False pairing_verification_required_by_device = response == b"\x01" if pairing_verification_required_by_host or pairing_verification_required_by_device: def get_device_response() -> bool: device_response = self._raw_query(OP_I_CAN_HAS_PAIRIN_VERIFICASHUN) if device_response == RESPONSE_SUCCESS: return True if device_response == RESPONSE_FAILURE: return False raise Exception(f"Unexpected pairing response: f{device_response}") client_response_success = noise_config.show_pairing( "{} {}\n{} {}".format( pairing_code[:5], pairing_code[5:10], pairing_code[10:15], pairing_code[15:20] ), get_device_response, ) if not client_response_success: self.close() raise Exception("pairing rejected by the user") noise_config.add_device_static_pubkey(remote_static_key) return noise
def __init__(self, device_info, show_pairing_callback, attestation_check_callback=None): self.debug = False serial_number = device_info["serial_number"] self.version = parse_device_version(serial_number) if self.version is None: raise ValueError(f"Could not parse version from {serial_number}") self.device = hid.device() self.device.open_path(device_info["path"]) if self.version > semver.VersionInfo(1, 0, 0): if attestation_check_callback is not None: # Perform attestation attestation_check_callback(self._perform_attestation()) # Invoke unlock workflow on the device. # In version <=1.0.0, the device did this automatically. self._query(OP_UNLOCK) if self._query(OP_I_CAN_HAS_HANDSHAEK) != RESPONSE_SUCCESS: raise Exception("Couldn't kick off handshake") # init noise channel noise = NoiseConnection.from_name(b"Noise_XX_25519_ChaChaPoly_SHA256") noise.set_as_initiator() dummy_private_key = os.urandom(32) noise.set_keypair_from_private_bytes(Keypair.STATIC, dummy_private_key) noise.set_prologue(b"Noise_XX_25519_ChaChaPoly_SHA256") noise.start_handshake() noise.read_message(self._query(noise.write_message())) assert not noise.handshake_finished send_msg = noise.write_message() assert noise.handshake_finished pairing_code = base64.b32encode( noise.get_handshake_hash()).decode("ascii") show_pairing_callback("{} {}\n{} {}".format(pairing_code[:5], pairing_code[5:10], pairing_code[10:15], pairing_code[15:20])) response = self._query(send_msg) # Can be set to False if the remote static pubkey was previously confirmed. pairing_verification_required_by_host = True pairing_verification_required_by_device = response == b"\x01" if pairing_verification_required_by_host or pairing_verification_required_by_device: pairing_response = self._query(OP_I_CAN_HAS_PAIRIN_VERIFICASHUN) if pairing_response == RESPONSE_SUCCESS: pass elif pairing_response == RESPONSE_FAILURE: raise Exception("pairing rejected by the user") else: raise Exception("unexpected response") self.noise = noise
def _do_test_for(key_type: str, keys: _Keys): noise_name = 'Noise_XX_{}_ChaChaPoly_SHA256'.format(key_type).encode() initiator = NoiseConnection.from_name(noise_name) responder = NoiseConnection.from_name(noise_name) initiator.set_as_initiator() responder.set_as_responder() initiator.set_keypair_from_private_bytes(Keypair.STATIC, keys.initiator_private) responder.set_keypair_from_private_bytes(Keypair.STATIC, keys.responder_private) initiator.start_handshake() responder.start_handshake() message = b'public-key-test' assert message == responder.read_message(initiator.write_message(message)) with pytest.raises(NoiseGetPublicKeyError): initiator.get_public_bytes(Keypair.REMOTE_STATIC) with pytest.raises(NoiseGetPublicKeyError): responder.get_public_bytes(Keypair.REMOTE_STATIC) assert message == initiator.read_message(responder.write_message(message)) with pytest.raises(NoiseGetPublicKeyError): responder.get_public_bytes(Keypair.REMOTE_STATIC) assert message == responder.read_message(initiator.write_message(message)) # Test ability to get remote static public key, # which is essential for application layer to validate peer identity assert keys.responder_public == initiator.get_public_bytes( Keypair.REMOTE_STATIC) assert keys.initiator_public == responder.get_public_bytes( Keypair.REMOTE_STATIC) # Other tests assert initiator.get_public_bytes( Keypair.REMOTE_EPHEMERAL) == responder.get_public_bytes( Keypair.EPHEMERAL) assert responder.get_public_bytes( Keypair.REMOTE_EPHEMERAL) == initiator.get_public_bytes( Keypair.EPHEMERAL)
def __init__(self, host_key, packetizer, debug=False): self.debug = debug self.packetizer = packetizer self.static_local = host_key self.proto = NoiseConnection.from_name(b'Noise_XX_25519_ChaChaPoly_BLAKE2s') self.proto.set_as_initiator() self.proto.set_keypair_from_private_bytes(Keypair.STATIC, self.static_local) self.proto.start_handshake() self.handshake = self.proto.noise_protocol.handshake_state # save for later because someone didn't think self.paired = False self.connected = False
def __init__(self, connection, security=b'Noise_NN_25519_ChaChaPoly_SHA256', debug=False): self.conn = connection super(SecureConnection, self).__init__('NOISE') self._debug = debug if self.conn.type == 6: self.type = 'SERVER' elif self.conn.type == 5: self.type = 'CLIENT' self.noise = NoiseConnection.from_name(security)
def do_test_connection(self, name): key = b"\x00" * 32 left = NoiseConnection.from_name(name) left.set_psks(key) left.set_as_initiator() left.start_handshake() right = NoiseConnection.from_name(name) right.set_psks(key) right.set_as_responder() right.start_handshake() h = left.write_message() _ = right.read_message(h) h2 = right.write_message() left.read_message(h2) assert left.handshake_finished assert right.handshake_finished enc = left.encrypt(b"hello") dec = right.decrypt(enc) assert dec == b"hello"
def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15): self._test = test self.itf = itf self.endpoint = endpoint self.port = port self.allowed_ips = allowed_ips self.persistent_keepalive = persistent_keepalive # remote peer's public self.private_key = X25519PrivateKey.generate() self.public_key = self.private_key.public_key() self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
async def connect_peer(self, peer_public_info): """Initiator side""" self.reader, self.writer = await asyncio.open_connection( host=peer_public_info["addr"] or "127.0.0.1", port=peer_public_info["port"]) peer_public_key = base64.b64decode(peer_public_info["public_key"]) self.noise = NoiseConnection.from_name( b"Noise_XK_25519_ChaChaPoly_BLAKE2s") self.noise.set_as_initiator() self.noise.set_keypair_from_private_bytes(Keypair.STATIC, self.private_key_bytes) self.noise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, peer_public_key) self.noise.start_handshake() # Send a SECU command to the peer first_message = b"SECU" + self.noise.write_message() logger.debug("NOISE: Init -> e, es (%#x bytes)", len(first_message) - 4) assert len(first_message) == 4 + 0x30 self.writer.write(first_message) second_message = await self.reader.readexactly(0x30) self.noise.read_message(second_message) logger.debug("NOISE: Init <- e, ee (%#x bytes)", len(second_message)) third_message = self.noise.write_message() logger.debug("NOISE: Init -> s, se (%#x bytes)", len(third_message)) self.writer.write(third_message) assert self.noise.handshake_finished # Exchange the "x" coordinate of the secret shares self.send_encrypted(self.my_secret_x.to_bytes(32, "big")) peer_secret = await self.recv_exactly_encrypted(32) self.peer_secret_x = int.from_bytes(peer_secret, "big") logger.debug("NOISE: Initiated encrypted connection %d -> %d", self.my_secret_x, self.peer_secret_x)
def __init__( self, device_info: DeviceInfo, show_pairing_callback: Callable[[str], None], attestation_check_callback: Optional[Callable[[bool], None]] = None, ): self.debug = False serial_number = device_info["serial_number"] self.version = parse_device_version(serial_number) if self.version is None: raise ValueError(f"Could not parse version from {serial_number}") # Delete the prelease part, as it messes with the comparison (e.g. 3.0.0-pre < 3.0.0 is # True, but the 3.0.0-pre has already the same API breaking changes like 3.0.0...). self.version = semver.VersionInfo( self.version.major, self.version.minor, self.version.patch, build=self.version.build ) self.device = hid.device() self.device.open_path(device_info["path"]) if self.version >= semver.VersionInfo(2, 0, 0): if attestation_check_callback is not None: # Perform attestation attestation_check_callback(self._perform_attestation()) # Invoke unlock workflow on the device. # In version <2.0.0, the device did this automatically. unlock_result = self._query(OP_UNLOCK) if self.version < semver.VersionInfo(3, 0, 0): assert unlock_result == b"" else: # since 3.0.0, unlock can fail if cancelled if unlock_result == RESPONSE_FAILURE: raise Exception("Unlock process aborted") if self._query(OP_I_CAN_HAS_HANDSHAEK) != RESPONSE_SUCCESS: raise Exception("Couldn't kick off handshake") # init noise channel noise = NoiseConnection.from_name(b"Noise_XX_25519_ChaChaPoly_SHA256") noise.set_as_initiator() dummy_private_key = os.urandom(32) noise.set_keypair_from_private_bytes(Keypair.STATIC, dummy_private_key) noise.set_prologue(b"Noise_XX_25519_ChaChaPoly_SHA256") noise.start_handshake() noise.read_message(self._query(noise.write_message())) assert not noise.handshake_finished send_msg = noise.write_message() assert noise.handshake_finished pairing_code = base64.b32encode(noise.get_handshake_hash()).decode("ascii") show_pairing_callback( "{} {}\n{} {}".format( pairing_code[:5], pairing_code[5:10], pairing_code[10:15], pairing_code[15:20] ) ) response = self._query(send_msg) # Can be set to False if the remote static pubkey was previously confirmed. pairing_verification_required_by_host = True pairing_verification_required_by_device = response == b"\x01" if pairing_verification_required_by_host or pairing_verification_required_by_device: pairing_response = self._query(OP_I_CAN_HAS_PAIRIN_VERIFICASHUN) if pairing_response == RESPONSE_SUCCESS: pass elif pairing_response == RESPONSE_FAILURE: raise Exception("pairing rejected by the user") else: raise Exception("unexpected response") self.noise = noise
from hashlib import blake2s import socket import struct from scapy.layers.inet import IP, ICMP from noise.connection import NoiseConnection, Keypair address = ('demo.wireguard.com', 12913) our_private = base64.b64decode('WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=') their_public = base64.b64decode('qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=') preshared = base64.b64decode('FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=') prologue = b'WireGuard v1 zx2c4 [email protected]' noise = NoiseConnection.from_name(b'Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s') noise.set_as_initiator() noise.set_keypair_from_private_bytes(Keypair.STATIC, our_private) noise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, their_public) noise.set_psks(psk=preshared) noise.set_prologue(prologue) noise.start_handshake() sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 1. Prepare and send handshake initiation packet now = datetime.datetime.now() tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()), int(now.microsecond * 1e3)) initiation_packet = b'\x01' # Type: initiation initiation_packet += b'\x00' * 3 # Reserved
def create_noise_state(self) -> NoiseState: noise_state = NoiseState.from_name(self.protocol_name) noise_state.set_keypair_from_private_bytes( NoiseKeypairEnum.STATIC, self.noise_static_key.to_bytes()) return noise_state
def test_vector(self, vector): if b'_XK_448_' in vector['protocol_name']: self.e2etest(vector) initiator = NoiseConnection.from_name(vector['protocol_name']) responder = NoiseConnection.from_name(vector['protocol_name']) if 'init_psks' in vector and 'resp_psks' in vector: initiator.set_psks(psks=vector['init_psks']) responder.set_psks(psks=vector['resp_psks']) initiator.set_prologue(vector['init_prologue']) initiator.set_as_initiator() self._set_keypairs(vector, initiator) responder.set_prologue(vector['resp_prologue']) responder.set_as_responder() self._set_keypairs(vector, responder) initiator.start_handshake() responder.start_handshake() initiator_to_responder = True handshake_finished = False for message in vector['messages']: if not handshake_finished: if initiator_to_responder: sender, receiver = initiator, responder else: sender, receiver = responder, initiator sender_result = sender.write_message(message['payload']) assert sender_result == message['ciphertext'] receiver_result = receiver.read_message(sender_result) assert receiver_result == message['payload'] if not (sender.handshake_finished and receiver.handshake_finished): # Not finished with handshake, fail if one would finish before other assert sender.handshake_finished == receiver.handshake_finished else: # Handshake done handshake_finished = True # Verify handshake hash if 'handshake_hash' in vector: assert initiator.noise_protocol.handshake_hash == responder.noise_protocol.handshake_hash == vector[ 'handshake_hash'] # Verify split cipherstates keys assert initiator.noise_protocol.cipher_state_encrypt.k == responder.noise_protocol.cipher_state_decrypt.k if not initiator.noise_protocol.pattern.one_way: assert initiator.noise_protocol.cipher_state_decrypt.k == responder.noise_protocol.cipher_state_encrypt.k else: assert initiator.noise_protocol.cipher_state_decrypt is responder.noise_protocol.cipher_state_encrypt is None else: if initiator.noise_protocol.pattern.one_way or initiator_to_responder: sender, receiver = initiator, responder else: sender, receiver = responder, initiator ciphertext = sender.encrypt(message['payload']) assert ciphertext == message['ciphertext'] plaintext = receiver.decrypt(message['ciphertext']) assert plaintext == message['payload'] initiator_to_responder = not initiator_to_responder
import socket from itertools import cycle from noise.connection import NoiseConnection if __name__ == '__main__': s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('localhost', 2000)) s.listen(1) conn, addr = s.accept() print('Accepted connection from', addr) noise = NoiseConnection.from_name(b'Noise_NN_25519_ChaChaPoly_SHA256') print(noise) noise.set_as_responder() noise.start_handshake() # Perform handshake. Break when finished for action in cycle(['receive', 'send']): if noise.handshake_finished: break elif action == 'send': ciphertext = noise.write_message() #print('cipherText',ciphertext) conn.sendall(ciphertext) elif action == 'receive': data = conn.recv(2048) plaintext = noise.read_message(data) #print('receive',plaintext)
import socket import struct from scapy.layers.inet import IP, ICMP from noise.connection import NoiseConnection, Keypair address = ('demo.wireguard.com', 12913) our_private = base64.b64decode('WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=') their_public = base64.b64decode('qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=') preshared = base64.b64decode('FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=') prologue = b'WireGuard v1 zx2c4 [email protected]' noise = NoiseConnection.from_name(b'Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s') noise.set_as_initiator() noise.set_keypair_from_private_bytes(Keypair.STATIC, our_private) noise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, their_public) noise.set_psks(psk=preshared) noise.set_prologue(prologue) noise.start_handshake() sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 1. Prepare and send handshake initiation packet now = datetime.datetime.now() tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()), int(now.microsecond * 1e3)) initiation_packet = b'\x01' # Type: initiation initiation_packet += b'\x00' * 3 # Reserved