🤝Der Handshake – Noise_IKpsk2
Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s.Handshake-Muster: Initiator kennt den statischen Schlüssel des Responders (K), überträgt seinen eigenen sofort (I).
Pre-Shared Key wird nach der zweiten Nachricht eingemischt (Modifier „psk“ an Position 2).
Diffie-Hellman auf Curve25519 (X25519), 32-Byte-Schlüssel.
AEAD ChaCha20-Poly1305 (RFC 7539) mit 16-Byte-Tag; im Cookie Reply XChaCha20-Poly1305 mit 24-Byte-Nonce.
Hash (32 Byte), keyed MAC (16 Byte) und HKDF auf Basis von HMAC-BLAKE2s.
🐞Schritt für Schritt (Debugger)
Ausgangslage: statische Schlüssel sind bekannt
C ≔ HASH(CONSTRUCTION)H ≔ HASH(C ‖ IDENTIFIER)H ≔ HASH(H ‖ Sᵣ_pub)
Beide Seiten haben die öffentlichen Schlüssel der Gegenseite vorab erhalten (in der wg0.conf). Das ist das „K“ in IK: der Initiator kennt den Responder. Der Initiator startet den Chaining-Key C und den Hash H – H protokolliert das gesamte Gespräch (Transkript) und fließt als Associated Data in jede Verschlüsselung ein.
- Sᵢ (eigenes Paar)
- Sᵣ_pub (aus Konfig)
- PSK (optional, sonst 32 × 0)
- C, H
- Sᵣ (eigenes Paar)
- Sᵢ_pub (aus Konfig)
- PSK (optional, sonst 32 × 0)
🧱Alle vier Nachrichtentypen
| 0 | message_type | 1 | Nachrichtentyp = 1 |
| 1 | reserved_zero | 3 | drei Null-Bytes (Ausrichtung auf 4 Byte) |
| 4 | sender_index | 4 | zufälliger Index des Initiators (für spätere Zuordnung) |
| 8 | unencrypted_ephemeral | 32 | flüchtiger öffentlicher Schlüssel Eᵢ (unverschlüsselt) |
| 40 | encrypted_static | 48 | statischer öffentl. Schlüssel Sᵢ: 32 + 16 Tag = 48 |
| 88 | encrypted_timestamp | 28 | TAI64N-Zeitstempel: 12 + 16 Tag = 28 |
| 116 | mac1 | 16 | MAC mit HASH(LABEL_MAC1 ‖ Sᵣ_pub) über Bytes 0…115 |
| 132 | mac2 | 16 | Null – oder MAC mit Cookie über Bytes 0…131 (unter Last) |
| 0 | message_type | 1 | Nachrichtentyp = 2 |
| 1 | reserved_zero | 3 | drei Null-Bytes (Ausrichtung auf 4 Byte) |
| 4 | sender_index | 4 | zufälliger Index des Responders |
| 8 | receiver_index | 4 | = sender_index aus der Initiation |
| 12 | unencrypted_ephemeral | 32 | flüchtiger öffentlicher Schlüssel Eᵣ |
| 44 | encrypted_nothing | 16 | leerer Klartext: nur das 16-Byte-Tag (beweist Schlüsselbesitz) |
| 60 | mac1 | 16 | MAC mit HASH(LABEL_MAC1 ‖ Sᵢ_pub) über Bytes 0…59 |
| 76 | mac2 | 16 | Null – oder MAC mit Cookie über Bytes 0…75 |
| 0 | message_type | 1 | Nachrichtentyp = 3 |
| 1 | reserved_zero | 3 | drei Null-Bytes (Ausrichtung auf 4 Byte) |
| 4 | receiver_index | 4 | sender_index der abgewiesenen Nachricht |
| 8 | nonce | 24 | 24 zufällige Bytes für XChaCha20-Poly1305 |
| 32 | encrypted_cookie | 32 | 16-Byte-Cookie + 16 Tag = 32 |
| 0 | message_type | 1 | Nachrichtentyp = 4 |
| 1 | reserved_zero | 3 | drei Null-Bytes (Ausrichtung auf 4 Byte) |
| 4 | receiver_index | 4 | Index der Sitzung beim Empfänger |
| 8 | counter | 8 | 64-Bit-Zähler, zugleich Nonce für ChaCha20-Poly1305 |
| 16 | encrypted_encapsulated_packet | var. | inneres IP-Paket, auf Vielfache von 16 gepolstert, + 16 Byte Tag |
⏱️Timer und Grenzwerte
| Konstante | Wert | Bedeutung |
|---|---|---|
| Rekey-After-Messages | 2⁶⁰ Nachrichten | danach neuer Handshake |
| Reject-After-Messages | 2⁶⁴ − 2¹³ − 1 Nachrichten | Sitzungsschlüssel wird hart verworfen |
| Rekey-After-Time | 120 s | Initiator startet nach 2 Minuten einen neuen Handshake |
| Reject-After-Time | 180 s | Schlüssel älter als 3 Minuten werden nicht mehr benutzt |
| Rekey-Attempt-Time | 90 s | so lange werden Handshakes wiederholt |
| Rekey-Timeout | 5 s | Wartezeit bis zur Wiederholung einer Initiation (+ Jitter) |
| Keepalive-Timeout | 10 s | passives Keepalive, wenn nur empfangen wurde |
IDENTIFIER = "WireGuard v1 zx2c4 Jason@zx2c4.com"
LABEL_MAC1 = "mac1----" · LABEL_COOKIE = "cookie--"
mac1 (das die Kenntnis des öffentlichen Schlüssels beweist) antwortet WireGuard auf nichts. Ein Portscan sieht einen geschlossenen UDP-Port.