<div dir="ltr">After this is applied, the protocol looks like this:<div><br></div><div><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:20.3636px">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:20.3636px"><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)</code>: Curve25519 point multiplication 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)">private key</code> and <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)">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()</code>: generate a random Curve25519 private key, 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)</code>: calculate a Curve25519 public key from <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)">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)</code>: ChaCha20Poly1305 AEAD, as specified in RFC7539, with its<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)</code>: <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)">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)">KDF(key, input)</code>: <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)">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)</code>: <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)">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()</code>: TAI64N timestamp of current time 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:20.3636px">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:20.3636px">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.chaining_key = "Noise WireGuard zx2c4 2015-10-12"
initiator.hash = HASH("Noise WireGuard zx2c4 2015-10-12" || 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.hash = HASH(initiator.hash || msg.unencrypted_ephemeral)
temp = KDF(initiator.chaining_key, DH(initiator.ephemeral_private, responder.static_public))
initiator.chaining_key = KDF(temp, 0x1)
initiator.key = KDF(temp, initiator.chaining_key || 0x2)
msg.encrypted_static = AEAD(initiator.key, 0, initiator.static_public, initiator.hash)
initiator.hash = HASH(initiator.hash || msg.encrypted_static)
temp = KDF(initiator.chaining_key, DH(initiator.static_private, responder.static_public))
initiator.chaining_key = KDF(temp, 0x1)
initiator.key = KDF(temp, initiator.chaining_key || 0x2)
msg.encrypted_timestamp = AEAD(initiator.key, 0, TAI64N(), initiator.hash)
initiator.hash = HASH(initiator.hash || msg.encrypted_timestamp)
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:20.3636px">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:20.3636px">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_response {
u8 message_type
u16 sender_index
u16 receiver_index
u8 encrypted_ephemeral[AEAD_LEN(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:20.3636px">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.encrypted_ephemeral = AEAD(responder.key, 1, DH_PUBKEY(responder.ephemeral_private), responder.hash)
responder.hash = HASH(responder.hash || msg.encrypted_ephemeral)
temp = KDF(responder.chaining_key, DH(responder.ephemeral_private, initiator.ephemeral_public))
responder.chaining_key = KDF(temp, 0x1)
responder.key = KDF(temp, responder.chaining_key || 0x2)
temp = KDF(responder.chaining_key, DH(responder.ephemeral_private, initiator.static_public))
responder.chaining_key = KDF(temp, 0x1)
responder.key = KDF(temp, responder.chaining_key || 0x2)
msg.encrypted_nothing = AEAD(responder.key, 0, [empty], responder.hash)
responder.hash = HASH(responder.hash || msg.encrypted_nothing)
</code></pre><p style="margin:15px 0px;color:rgb(51,51,51);font-family:sans-serif;font-size:14px;line-height:20.3636px">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:20.3636px">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">temp = KDF(initiator.chaining_key, [32 empty bytes])
initiator.sending_key = KDF(temp, 0x1)
initiator.receiving_key = KDF(temp, initiator.sending_key || 0x2)
initiator.sending_key_counter = 0
initiator.receiving_key_counter = 0
temp = KDF(responder.chaining_key, [32 empty bytes])
responder.receiving_key = KDF(temp, 0x1)
responder.sending_key = KDF(temp, responder.receiving_key || 0x2)
responder.receiving_key_counter = 0
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:20.3636px">And then all previous keys, chaining 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:20.3636px">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:20.3636px">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:20.3636px">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>