Boxes - noiseprotocol/noise_spec GitHub Wiki
Noise assumes the following variables and functions are defined by a ciphersuite:
SUITE_NAME = ? # 24-byte string uniquely naming the ciphersuite DH_LEN = ? # Length in bytes of DH private, public keys, and DH outputs CC_LEN = ? # Length in bytes of cipher context MAC_LEN = ? # Length in bytes that ciphertext is enlarged due to MAC DH(privkey, pubkey): # Calculates a DH result. # Returns a DH secret of length DH_LEN. ENCRYPT(cc, plaintext, authtext): # Takes a cipher context, some additional authenticated data, and plaintext. # Returns an "authenticated encryption" ciphertext of length equal to # plaintext plus MAC_LEN. # Modifies the value of 'cc'.
All Noise ciphersuites use the following HMAC-SHA2-512 based key derivation functions:
CV_LEN = 48
H_LEN = 64 # output length of hash, >= 32
KDF(secret, extra_secret, info, output_len):
# Outputs a byte sequence that the caller typically splits into multiple variables
# such as a chain variable and cipher context, or two cipher contexts.
#
# The 'extra_secret' is used to pass a chaining variable to mix into the KDF.
# The 'info' ensures that applying the KDF to the same secret values will
# produce independent output, provided 'info' is different.
output = []
t = zeros[H_LEN]
for c = 0...(ceil(output_len / H_LEN) - 1)
t = HMAC-SHA2-512(secret, info || (byte)c || t[0:32] || extra_secret)
output = output || t
return outputstruct {
bytes encrypted_plaintext[plaintext_len];
bytes encrypted_padding[padding_len];
bytes encrypted_padding_len[4];
bytes mac[MAC_LEN];
} NoiseBody;
struct {
bytes ephemeral_pubkey[DH_LEN];
bytes encrypted_sender_pubkey[DH_LEN];
bytes mac[MAC_LEN];
NoiseBody body;
} NoiseBox;noise_body(cc, pad_len, app_data, authtext=""):
plaintext = app_data || random(pad_len) || (uint32_little_endian)pad_len
body = ENCRYPT(cc, plaintext, authtext)
return body
noise_box(eph_key, sender_key, target_pubkey, pad_len, app_data,
kdf_num=0x00, cv=zeros[CV_LEN]):
dh1 = DH(eph_key.priv, target_pubkey)
dh2 = DH(sender_key.priv, target_pubkey)
cv1 || cc1 = KDF(dh1, cv, SUITE_NAME || (byte)kdf_num, CV_LEN + CC_LEN)
cv2 || cc2 = KDF(dh2, cv1, SUITE_NAME || (byte)(kdf_num + 1), CV_LEN + CC_LEN)
header = eph_key.pub || ENCRYPT(cc1, sender_key.pub, target_pubkey || eph_key.pub)
body = noise_body(cc2, pad_len, app_data, target_pubkey || header)
return (header || body), cv2- Box size: The box size is not encoded, so must be determined by other means (e.g. file length). Boxes add a minimum of (2*DH_LEN + 2*MAC_LEN + 4) bytes of overhead to the payload.
- Ephemeral key reuse: Ephemeral keys SHALL NOT be reused for multiple Noise boxes, since the ephemeral key functions as a "nonce".
- Anonymous boxes: If the sender doesn't have a sender key, the ephemeral key MAY be reused as a sender key. This allows the sender and recipient to skip the second ECDH calculation.