<div dir="ltr">Hi,<div><br></div><div>I'm using noise for a very specific purpose, which involves the HandshakeIS. I've hence boiled down the noise spec to only the parts that are relevant to me, while still trying to remain as close as possible to the spec. Below is what I have. Note that a few days I ago I flirted with being considerably divergent from noise-proper, and sent that spec. Today's spec is much closer, being about 99% in line with noise (I hope).</div><div><br></div><div>Here's where it differs:</div><div><ul><li>Little endian everywhere (except for timestamps, where djb's TAI64N's big endianness has the nice quality of being memcmp()able).<br></li><li>My prologue is a 1 byte message type, and then two 2byte sender/receiver indices. I think the noise spec allows for this kind of extension though.<br></li><li>Nonces for transport messages begin at 1, not at 0! This is because I take advantage of x86 primitives for "increment and return" as an atomic operation, so I don't need locks for preventing nonce-reuse. I realize that I could probably always just subtract one from all "increment and return" atomic operations, and at the higher end, the wrap around of unsigned integers would have the correct behavior, but right now I'm not that courageous. If somebody wants to convince me that such a subtraction <<really is okay to do>> I'm all ears to the encouragement.<br></li><li>I use blake2b instead of HMAC-SHA2-256.<br></li><li>My payload messages don't have a length param, because the Poly MAC prevents against tampering with the encrypted contents, and encrypted is an IP packet, which usually knows its own semantics, and also encapsulating all of this is a UDP packet anyway.</li><li>I don't have a length field in handshake messages, because they're fixed size.</li></ul></div><div><br></div><div>Let me know what you think, and if there are places where this actually does diverge from the noise spec that I did not anticipate.</div><div><br></div><div>Jason</div><div><br></div><div><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">For the following packet descriptions, refer to these functions:</p><ul style="margin:15px 0px;padding-left:30px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px"><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">DH(private key, public key) = Curve25519-point-multiplication(private key, public key)</code> returning 32 bytes of output</li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">DH_GENERATE() = generate Curve25519 private key</code> returning 32 bytes of output</li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">DH_PUBKEY(private key) = calculate Curve25519 public key from private key</code> returning 32 bytes of output</li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">AEAD(key, counter, plain text, auth text) = ChaCha20Poly1305-RFC7539(key, nonce, plain text, auth text)</code> with <code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">nonce</code>being composed of 32 bits of zeros followed by the 64-bit little-endian value of <code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">counter</code></li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">AEAD_LEN(plain len) = plain len + 16</code></li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">GETKEY(key, counter) = ChaCha20(32 bytes of zeros, key, nonce)</code> with <code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">nonce</code> being composed of 32 bits of zeros followed by the 64-bit little-endian <code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">counter</code></li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">KDF(key, input) = Blake2b(key, input)</code> returning 32 bytes of output</li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">HASH(input) = Blake2b(input)</code> returning 32 bytes of output</li><li><code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">TAI64N() = TAI64N timestamp of current time</code> which is 12 bytes</li></ul><h3 style="margin:20px 0px 10px;padding:0px;font-size:18px;color:rgb(51,51,51);font-family:sans-serif">First Message: Initiator to Responder</h3><p style="margin:0px 0px 15px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The initiator sends this message:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">msg = handshake_initiation {
    u8 message_type
    u16 sender_index
    u8 unencryped_ephemeral[32]
    u8 encrypted_static[AEAD_LEN(32)]
    u8 encrypted_timestamp[AEAD_LEN(12)]
}
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The fields are populated as follows:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">initiator.key = HASH("WireGuard-zx2c4-20150827") (or maybe some other fixed string; it doesn't matter)
initiator.hash = HASH(responder.static_public)
initiator.ephemeral_private = DH_GENERATE()
msg.message_type = 1
msg.sender_index = little_endian(initiator.sender_index)
msg.unencrypted_ephemeral = DH_PUBKEY(initiator.ephemeral_private)
initiator.key = KDF(GETKEY(initiator.key, 0), DH(initiator.ephemeral_private, responder.static_public))
msg.encrypted_static = AEAD(initiator.key, 0, initiator.static_public, initiator.hash)
initiator.hash = HASH(initiator.hash || initiator.static_public)
initiator.key = KDF(GETKEY(initiator.key, 0), DH(initiator.static_private, responder.static_public))
stamp = TAI64N()
msg.encrypted_timestamp = AEAD(initiator.key, 0, stamp, initiator.hash)
initiator.hash = HASH(initiator.hash || stamp)
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">When the responder receives this message, he decrypts and does all the above operations in reverse, so that the state is identical.</p><h3 style="margin:20px 0px 10px;padding:0px;font-size:18px;color:rgb(51,51,51);font-family:sans-serif">Second Message: Responder to Initiator</h3><p style="margin:0px 0px 15px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The responder sends this message, after processing the first message above and applying the same operations to arrive at an identical state:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">msg = handshake_initiation {
    u8 message_type
    u16 sender_index
    u16 receiver_index
    u8 unencrypted_ephemeral[32]
    u8 encrypted_nothing[AEAD_LEN(0)]
}
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The fields are populated as follows:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">responder.ephemeral_private = DH_GENERATE()
msg.message_type = 2
msg.sender_index = little_endian(responder.sender_index)
msg.receiver_index = little_endian(initiator.sender_index)
msg.unencrypted_ephemeral = DH_PUBKEY(responder.ephemeral_private)
responder.key = KDF(GETKEY(responder.key, 1), DH(responder.ephemeral_private, initiator.ephemeral_public))
responder.key = KDF(GETKEY(responder.key, 0), DH(responder.ephemeral_private, initiator.static_public))
msg.encrypted_nothing = AEAD(responder.key, 0, [empty], responder.hash)
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">When the initiator receives this message, he decrypts and does all the above operations in reverse, so that the state is identical.</p><h3 style="margin:20px 0px 10px;padding:0px;font-size:18px;color:rgb(51,51,51);font-family:sans-serif">Data Keys Derivation</h3><p style="margin:0px 0px 15px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">After the above two messages have been exchanged, keys are calculated by the initiator for sending and receiving data:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">initiator.sending_key = GETKEY(initiator.key, 1)
initiator.sending_key_counter = 0
initiator.receiving_key = GETKEY(initiator.key, 2)
initiator.receiving_key_counter = 0

responder.receiving_key = GETKEY(responder.key, 1)
responder.receiving_key_counter = 0
responder.sending_key = GETKEY(responder.key, 2)
responder.sending_key_counter = 0
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">And then all previous keys, ephemeral keys, and hashes are zeroed out.</p><h3 style="margin:20px 0px 10px;padding:0px;font-size:18px;color:rgb(51,51,51);font-family:sans-serif">Subsequent Messages: Exchange of Data Packets</h3><p style="margin:0px 0px 15px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The initiator and the responder exchange this packet for sharing encapsulated packet data:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">msg = packet_data {
    u8 message_type
    u16 receiver_index
    u64 counter
    u8 encrypted_encapsulated_packet[]
}
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The fields are populated as follows:</p><pre style="margin-top:15px;margin-bottom:15px;border:1px solid rgb(204,204,204);font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;color:rgb(51,51,51);background-color:rgb(248,248,248)"><code style="margin:0px;padding:0px;border:none;border-radius:3px;background:transparent">msg.message_type = 3
msg.receiver_index = little_endian(responder.sender_index)
encapsulated_packet = encapsulated_packet || random padding in order to make the length a multiple of 16
counter = ++initiator.sending_key_counter
msg.counter = little_endian(counter)
msg.encrypted_encapsulated_packet = AEAD(initiator.sending_key, counter, encapsulated_packet, [empty])
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:22.4px">The responder uses his <code style="margin:0px 2px;padding:0px 5px;border:1px solid rgb(234,234,234);border-radius:3px;white-space:nowrap;background-color:rgb(248,248,248)">responder.receiving_key</code> to read the message.</p></div></div>