API Documentation

API Reference

Complete reference for all public APIs in NoiseFramework

NoiseHandshake

Main class for orchestrating Noise Protocol handshakes.

NoiseHandshake(pattern_string)
Initialize a Noise handshake with a pattern string

Parameters:

pattern_string (str): Noise pattern in format Noise_PATTERN_DH_CIPHER_HASH

Example: "Noise_XX_25519_ChaChaPoly_SHA256"

Example:

from noiseframework import NoiseHandshake

handshake = NoiseHandshake("Noise_XX_25519_ChaChaPoly_SHA256")

Methods

set_as_initiator()
Configure this handshake instance as the initiator (client)
handshake.set_as_initiator()

NoiseTransport

Transport layer for encrypted communication after handshake completion.

Import
from noiseframework import NoiseTransport
send(plaintext, ad)
Encrypt and send a message

Parameters:

plaintext (bytes): Data to encrypt

ad (bytes): Associated data (optional)

ciphertext = transport.send(b"Hello!")
receive(ciphertext, ad)
Receive and decrypt a message

Parameters:

ciphertext (bytes): Encrypted data

ad (bytes): Associated data (optional)

plaintext = transport.receive(ciphertext)

NoiseConnection (High-Level API)

Simplified API that combines handshake, transport, and framing in one interface.

NoiseConnection(pattern, role)
Create a high-level connection that handles everything automatically

Parameters:

pattern (str): Noise pattern string

role (str): "initiator" or "responder"

Example (Client):

from noiseframework import NoiseConnection

with NoiseConnection("Noise_XX_25519_ChaChaPoly_SHA256", "initiator") as conn:
conn.connect(("example.com", 9999))
conn.send(b"Hello!")
response = conn.receive()

Message Framing

Built-in length-prefixed framing for TCP/stream transports. Handles message boundaries automatically.

FramedWriter
Write length-prefixed messages
from noiseframework import FramedWriter

writer = FramedWriter(socket)
writer.write_message(ciphertext)
FramedReader
Read length-prefixed messages
from noiseframework import FramedReader

reader = FramedReader(socket)
ciphertext = reader.read_message()

Key Features: 4-byte big-endian length prefix, 16 MB default max (configurable), automatic partial read handling, works with sockets, files, and pipes.

Async/Await Support

Full asyncio support with async wrappers for modern non-blocking Python applications.

Async Example
Using AsyncNoiseConnection for non-blocking operations
import asyncio
from noiseframework import AsyncNoiseConnection

async def client():
async with AsyncNoiseConnection(
"Noise_XX_25519_ChaChaPoly_SHA256", "initiator"
) as conn:
await conn.connect(("localhost", 9999))
await conn.send(b"Hello async!")
response = await conn.receive()

Pre-Shared Key (PSK) Support

PSK patterns provide quantum-resistant authentication, immune to quantum computer attacks on DH key exchange.

Quantum Resistance
PSKs are immune to quantum attacks, providing long-term security (25+ years)

✓ Protection against future quantum computers

✓ Additional authentication beyond public keys

✓ Suitable for government and defense use cases

PSK Example (XXpsk3 Pattern)
Most common PSK pattern for mutual authentication
import os
from noiseframework import NoiseHandshake

# Generate or load 32-byte PSK
psk = os.urandom(32)

# Initiator with PSK
initiator = NoiseHandshake("Noise_XXpsk3_25519_ChaChaPoly_SHA256")
initiator.set_as_initiator()
initiator.generate_static_keypair()
initiator.set_psk(psk) # Set PSK before initialize
initiator.initialize()

# Responder with same PSK
responder = NoiseHandshake("Noise_XXpsk3_25519_ChaChaPoly_SHA256")
responder.set_as_responder()
responder.generate_static_keypair()
responder.set_psk(psk) # Must use same PSK
responder.initialize()

Common Patterns

  • XXpsk3
  • NNpsk0
  • IKpsk2

PSK Modifiers

  • psk0 - Early mix
  • psk2 - Mid handshake
  • psk3 - Late mix

Use Cases

  • IoT devices
  • Defense systems
  • Enterprise VPNs

Fallback Pattern Support

Graceful handshake degradation when initiator's first message fails (Noise Pipes protocol).

Fallback Example (IK → XXfallback)
Recover from IK failure by falling back to XX pattern
# Responder detects decryption failure in IK
alice_ephemeral = ik_msg1[:32] # Extract ephemeral key

# Initiate fallback to XXfallback
bob.start_fallback(alice_ephemeral)

# Continue with XXfallback pattern
fallback_msg1 = bob.write_message(b"Fallback initiated")

Use Cases: Key rotation scenarios, outdated PSKs, server identity updates, graceful degradation preferred over connection failure.

Pattern System

Supported Patterns
NoiseFramework supports all 12 fundamental interactive patterns

One-way patterns

  • NN
  • NK
  • NX

Initiator auth

  • KN
  • KK
  • KX

Interactive (most common)

  • XX (recommended)
  • XK
  • XN
Pattern Selection Guide
XX

Mutual authentication, no pre-shared keys

Best for most use cases

IK

Client knows server's public key

Faster handshake, requires pre-shared keys

NN

Anonymous communication

No authentication - not recommended for production

Cryptographic Primitives

DH Functions
  • 25519

    Curve25519 (X25519) - 32 bytes

  • 448

    Curve448 (X448) - 56 bytes

Cipher Functions
  • ChaChaPoly

    ChaCha20-Poly1305 (recommended)

  • AESGCM

    AES-256-GCM

Hash Functions
  • SHA256

    SHA-256 - 32 bytes

  • SHA512

    SHA-512 - 64 bytes

  • BLAKE2s

    BLAKE2s - 32 bytes

  • BLAKE2b

    BLAKE2b - 64 bytes

Complete Example

XX Pattern (Mutual Authentication)
Full handshake and transport setup
from noiseframework import NoiseHandshake, NoiseTransport

# Setup
initiator = NoiseHandshake("Noise_XX_25519_ChaChaPoly_SHA256")
initiator.set_as_initiator()
initiator.generate_static_keypair()
initiator.initialize()

responder = NoiseHandshake("Noise_XX_25519_ChaChaPoly_SHA256")
responder.set_as_responder()
responder.generate_static_keypair()
responder.initialize()

# Handshake
msg1 = initiator.write_message(b"")
responder.read_message(msg1)

msg2 = responder.write_message(b"")
initiator.read_message(msg2)

msg3 = initiator.write_message(b"")
responder.read_message(msg3)

# Transport
i_send, i_recv = initiator.to_transport()
r_send, r_recv = responder.to_transport()

i_transport = NoiseTransport(i_send, i_recv)
r_transport = NoiseTransport(r_send, r_recv)

# Encrypt/Decrypt
ciphertext = i_transport.send(b"Hello!")
plaintext = r_transport.receive(ciphertext)
print(plaintext.decode()) # "Hello!"

Error Handling

ValueError Exceptions
All functions raise ValueError for invalid inputs or cryptographic failures
try:
  handshake = NoiseHandshake("Invalid_Pattern")
except ValueError as e:
  print(f"Error: {e}")