Noise Protocol Framework Implementation
The `noiseprotocol` library provides a Python implementation of the Noise Protocol Framework (Revision 5), a widely used framework for building secure cryptographic protocols. It focuses on offering a robust and flexible API for performing handshakes and secure transport of data, supporting various handshake patterns and cipher suites. The current version is 0.3.1, and releases are infrequent, reflecting the stability of the underlying protocol.
Common errors
-
ModuleNotFoundError: No module named 'noiseprotocol'
cause The `noiseprotocol` package is not installed in your Python environment.fixRun `pip install noiseprotocol` to install the package. -
ValueError: Mismatched handshake pattern
cause The initiator and responder have been configured with different `HandshakePattern` enums, which is a protocol violation.fixEnsure that `HandshakePattern` (e.g., `HandshakePattern.IK`) is identical for both the initiator's and responder's `HandshakeState` initialization. -
TypeError: expected bytes-like object, not str
cause You are attempting to pass a Python string to a method that expects raw bytes (e.g., `write_message` or key material).fixEncode your string to bytes using `.encode()` (e.g., `my_string.encode('utf-8')`) before passing it to `noiseprotocol` functions. -
noiseprotocol.exceptions.NoiseProtocolException: MAC mismatch
cause This error indicates that a message's Message Authentication Code (MAC) failed validation, usually due to data tampering in transit or incorrect keying/state management between peers.fixVerify that no data was altered during transmission. Check that both peers are using the correct and synchronized `HandshakeState` or transport state, and that static keys are correctly configured and shared (if applicable).
Warnings
- gotcha Mismatch in HandshakePattern or CipherSuite between Initiator and Responder will prevent a successful handshake. Both sides must agree on these parameters.
- gotcha Keypair management is crucial. Static private keys must be kept secret and ideally persisted for consistent peer identification. Ephemeral keys are generated per handshake.
- gotcha The library expects byte-like objects for all message payloads and key material. Passing strings directly without encoding will result in `TypeError`.
- gotcha The `read_message` and `write_message` methods return a tuple of `(payload, next_state)`. It's critical to use the `next_state` for subsequent message processing or transport, as the handshake state evolves.
Install
-
pip install noiseprotocol
Imports
- HandshakeState
from noiseprotocol.handshake import HandshakeState
- Keypair
from noiseprotocol.handshake import Keypair
- HandshakePattern
from noiseprotocol.constants import HandshakePattern
- CipherSuite
from noiseprotocol.constants import CipherSuite
- NoiseConnectionRole
from noiseprotocol.constants import NoiseConnectionRole
Quickstart
import secrets
from noiseprotocol.constants import HandshakePattern, CipherSuite, NoiseConnectionRole
from noiseprotocol.handshake import HandshakeState, Keypair
# 1. Define static keys (can be pre-shared or generated once)
initiator_static_key = Keypair(private=secrets.token_bytes(32))
responder_static_key = Keypair(private=secrets.token_bytes(32))
# 2. Setup Initiator
initiator_hs = HandshakeState(
role=NoiseConnectionRole.INITIATOR,
pattern=HandshakePattern.IK,
static_key=initiator_static_key,
remote_static_key=responder_static_key.public, # Initiator knows Responder's public key
cipher_suite=CipherSuite.AESGCM256_SHA256_X25519
)
initiator_hs.initialize()
# 3. Setup Responder
responder_hs = HandshakeState(
role=NoiseConnectionRole.RESPONDER,
pattern=HandshakePattern.IK,
static_key=responder_static_key,
cipher_suite=CipherSuite.AESGCM256_SHA256_X25519
)
responder_hs.initialize()
# 4. Perform Handshake (simplified for example, messages would be sent over network)
# Initiator sends first message
message_initiator_to_responder, _ = initiator_hs.write_message(b"")
# Responder receives message
_, post_handshake_state_responder = responder_hs.read_message(message_initiator_to_responder)
# Responder sends second message
message_responder_to_initiator, _ = responder_hs.write_message(b"")
# Initiator receives message
_, post_handshake_state_initiator = initiator_hs.read_message(message_responder_to_initiator)
# Handshake complete, now use post_handshake_state for transport
if post_handshake_state_initiator and post_handshake_state_responder:
plaintext = b"Hello, secure world!"
ciphertext = post_handshake_state_initiator.write_message(plaintext)
decrypted = post_handshake_state_responder.read_message(ciphertext)
assert plaintext == decrypted
print("Handshake and transport successful!")
else:
print("Handshake failed.")