[noise] New branch: "simpler"

Jason A. Donenfeld Jason at zx2c4.com
Thu Oct 1 05:56:02 PDT 2015


Hi Trevor,

I've implemented Noise_IS according to this branch. Below is a condensed
representation of this, if you're curious to check it for correctness. But
before that, I'll also paste a diff here to the previous version, before
the recent round of substantive changes.


diff --git a/doc/protocol.md b/doc/protocol.md
index 9f1d0de..7f817ae 100644
--- a/doc/protocol.md
<http://git.zx2c4.com/WireGuard/tree/doc/protocol.md?id=7d647b293345fe60decc16197c2e40be8f1a4a01>
+++ b/doc/protocol.md
<http://git.zx2c4.com/WireGuard/tree/doc/protocol.md?id=dfc3f8c0a143732d75b4a58092a1439dd2b6d5db>
@@ -61,19 +61,19 @@ The initiator sends this message:
The fields are populated as follows:
- initiator.key = "Noise WireGuard zx2c4 2015-08-31"
- initiator.hash = HASH(responder.static_public)
+ initiator.key = 32 bytes of zeros
+ initiator.hash = HASH("Noise WireGuard zx2c4 2015-09-30" ||
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)
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.hash = HASH(initiator.hash || msg.encrypted_static)
initiator.key = KDF(GETKEY(initiator.key, 1), 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)
+ msg.encrypted_timestamp = AEAD(initiator.key, 0, TAI64N(), initiator.hash)
+ initiator.hash = HASH(initiator.hash || msg.encrypted_timestamp)
When the responder receives this message, he decrypts and does all the
above operations in reverse, so that the state is identical.
@@ -85,7 +85,7 @@ The responder sends this message, after processing the
first message above and a
u8 message_type
u16 sender_index
u16 receiver_index
- u8 unencrypted_ephemeral[32]
+ u8 encrypted_ephemeral[AEAD_LEN(32)]
u8 encrypted_nothing[AEAD_LEN(0)]
}
@@ -95,8 +95,9 @@ The fields are populated as follows:
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))
+ msg.encrypted_ephemeral = AEAD(responder.key, 1,
DH_PUBKEY(responder.ephemeral_private), responder.hash)
+ responder.hash = HASH(responder.hash || msg.encrypted_ephemeral)
+ responder.key = KDF(GETKEY(responder.key, 2),
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)


Jason



First Message: Initiator to Responder

The initiator sends this message:

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)]
}

The fields are populated as follows:

initiator.key = 32 bytes of zeros
initiator.hash = HASH("Noise WireGuard zx2c4 2015-09-30" ||
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)
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 || msg.encrypted_static)
initiator.key = KDF(GETKEY(initiator.key, 1),
DH(initiator.static_private, responder.static_public))
msg.encrypted_timestamp = AEAD(initiator.key, 0, TAI64N(), initiator.hash)
initiator.hash = HASH(initiator.hash || msg.encrypted_timestamp)

When the responder receives this message, he decrypts and does all the
above operations in reverse, so that the state is identical.
Second Message: Responder to Initiator

The responder sends this message, after processing the first message above
and applying the same operations to arrive at an identical state:

msg = handshake_response {
    u8 message_type
    u16 sender_index
    u16 receiver_index
    u8 encrypted_ephemeral[AEAD_LEN(32)]
    u8 encrypted_nothing[AEAD_LEN(0)]
}

The fields are populated as follows:

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)
responder.key = KDF(GETKEY(responder.key, 2),
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)

When the initiator receives this message, he decrypts and does all the
above operations in reverse, so that the state is identical.
Data Keys Derivation

After the above two messages have been exchanged, keys are calculated by
the initiator for sending and receiving data:

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

And then all previous keys, ephemeral keys, and hashes are zeroed out.
Subsequent Messages: Exchange of Data Packets

The initiator and the responder exchange this packet for sharing
encapsulated packet data:

msg = packet_data {
    u8 message_type
    u16 receiver_index
    u64 counter
    u8 encrypted_encapsulated_packet[]
}

The fields are populated as follows:

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])

The responder uses his responder.receiving_key to read the message.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://moderncrypto.org/mail-archive/noise/attachments/20151001/e360449e/attachment.html>


More information about the Noise mailing list