]> git.ipfire.org Git - thirdparty/wireguard-go.git/commitdiff
Restructured MAC/cookie calculation
authorMathias Hall-Andersen <mathias@hall-andersen.dk>
Wed, 12 Jul 2017 21:11:49 +0000 (23:11 +0200)
committerMathias Hall-Andersen <mathias@hall-andersen.dk>
Wed, 12 Jul 2017 21:11:49 +0000 (23:11 +0200)
Added copy-right headers accidentally removed

src/helper_test.go
src/macs.go [moved from src/macs_device.go with 62% similarity]
src/macs_peer.go [deleted file]
src/ratelimiter.go
src/replay.go
src/xchacha20.go [new file with mode: 0644]
src/xchacha20_test.go [new file with mode: 0644]

index 6d85771c3975dffac1b4e48f070a1e82d9c5f0eb..3838a7cd0928e997a5266a5f422057b24cca8939 100644 (file)
@@ -18,8 +18,8 @@ func (tun *DummyTUN) Name() string {
        return tun.name
 }
 
-func (tun *DummyTUN) MTU() int {
-       return tun.mtu
+func (tun *DummyTUN) MTU() (int, error) {
+       return tun.mtu, nil
 }
 
 func (tun *DummyTUN) Write(d []byte) (int, error) {
similarity index 62%
rename from src/macs_device.go
rename to src/macs.go
index 68181e60076ced137100957e3f7a34611f2cdca1..841ef318f23ba4d216279398f8fa1a8ea01d6fc6 100644 (file)
@@ -1,10 +1,9 @@
 package main
 
 import (
-       "crypto/cipher"
        "crypto/hmac"
        "crypto/rand"
-       "github.com/aead/chacha20poly1305" // Needed for XChaCha20Poly1305, TODO:
+       "errors"
        "golang.org/x/crypto/blake2s"
        "net"
        "sync"
@@ -17,9 +16,22 @@ type MACStateDevice struct {
        secret    [blake2s.Size]byte
        keyMAC1   [blake2s.Size]byte
        keyMAC2   [blake2s.Size]byte
-       xaead     cipher.AEAD
 }
 
+type MACStatePeer struct {
+       mutex     sync.RWMutex
+       cookieSet time.Time
+       cookie    [blake2s.Size128]byte
+       lastMAC1  [blake2s.Size128]byte
+       keyMAC1   [blake2s.Size]byte
+       keyMAC2   [blake2s.Size]byte
+}
+
+/* Methods for verifing MAC fields
+ * and creating/consuming cookies replies
+ * (per device)
+ */
+
 func (state *MACStateDevice) Init(pk NoisePublicKey) {
        state.mutex.Lock()
        defer state.mutex.Unlock()
@@ -38,7 +50,6 @@ func (state *MACStateDevice) Init(pk NoisePublicKey) {
                hsh.Sum(state.keyMAC2[:0])
        }()
 
-       state.xaead, _ = chacha20poly1305.NewXCipher(state.keyMAC2[:])
        state.refreshed = time.Time{}
 }
 
@@ -90,7 +101,10 @@ func (state *MACStateDevice) CheckMAC2(msg []byte, addr *net.UDPAddr) bool {
        return hmac.Equal(mac2[:], msg[start:])
 }
 
-func (device *Device) CreateMessageCookieReply(msg []byte, receiver uint32, addr *net.UDPAddr) (*MessageCookieReply, error) {
+func (device *Device) CreateMessageCookieReply(
+       msg []byte, receiver uint32, addr *net.UDPAddr,
+) (*MessageCookieReply, error) {
+
        state := &device.mac
        state.mutex.RLock()
 
@@ -137,7 +151,15 @@ func (device *Device) CreateMessageCookieReply(msg []byte, receiver uint32, addr
                state.mutex.RUnlock()
                return nil, err
        }
-       state.xaead.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], mac1)
+
+       XChaCha20Poly1305Encrypt(
+               reply.Cookie[:0],
+               &reply.Nonce,
+               cookie[:],
+               mac1,
+               &state.keyMAC2,
+       )
+
        state.mutex.RUnlock()
        return reply, nil
 }
@@ -163,7 +185,14 @@ func (device *Device) ConsumeMessageCookieReply(msg *MessageCookieReply) bool {
        state.mutex.Lock()
        defer state.mutex.Unlock()
 
-       _, err := state.xaead.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], state.lastMAC1[:])
+       _, err := XChaCha20Poly1305Decrypt(
+               cookie[:0],
+               &msg.Nonce,
+               msg.Cookie[:],
+               state.lastMAC1[:],
+               &state.keyMAC2,
+       )
+
        if err != nil {
                return false
        }
@@ -171,3 +200,69 @@ func (device *Device) ConsumeMessageCookieReply(msg *MessageCookieReply) bool {
        state.cookie = cookie
        return true
 }
+
+/* Methods for generating the MAC fields
+ * (per peer)
+ */
+
+func (state *MACStatePeer) Init(pk NoisePublicKey) {
+       state.mutex.Lock()
+       defer state.mutex.Unlock()
+
+       func() {
+               hsh, _ := blake2s.New256(nil)
+               hsh.Write([]byte(WGLabelMAC1))
+               hsh.Write(pk[:])
+               hsh.Sum(state.keyMAC1[:0])
+       }()
+
+       func() {
+               hsh, _ := blake2s.New256(nil)
+               hsh.Write([]byte(WGLabelCookie))
+               hsh.Write(pk[:])
+               hsh.Sum(state.keyMAC2[:0])
+       }()
+
+       state.cookieSet = time.Time{} // never
+}
+
+func (state *MACStatePeer) AddMacs(msg []byte) {
+       size := len(msg)
+
+       if size < blake2s.Size128*2 {
+               panic(errors.New("bug: message too short"))
+       }
+
+       startMac1 := size - (blake2s.Size128 * 2)
+       startMac2 := size - blake2s.Size128
+
+       mac1 := msg[startMac1 : startMac1+blake2s.Size128]
+       mac2 := msg[startMac2 : startMac2+blake2s.Size128]
+
+       state.mutex.Lock()
+       defer state.mutex.Unlock()
+
+       // set mac1
+
+       func() {
+               mac, _ := blake2s.New128(state.keyMAC1[:])
+               mac.Write(msg[:startMac1])
+               mac.Sum(mac1[:0])
+       }()
+       copy(state.lastMAC1[:], mac1)
+
+       // set mac2
+
+       if state.cookieSet.IsZero() {
+               return
+       }
+       if time.Now().Sub(state.cookieSet) > CookieRefreshTime {
+               state.cookieSet = time.Time{}
+               return
+       }
+       func() {
+               mac, _ := blake2s.New128(state.cookie[:])
+               mac.Write(msg[:startMac2])
+               mac.Sum(mac2[:0])
+       }()
+}
diff --git a/src/macs_peer.go b/src/macs_peer.go
deleted file mode 100644 (file)
index 16a7a87..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-package main
-
-import (
-       "crypto/cipher"
-       "errors"
-       "github.com/aead/chacha20poly1305" // Needed for XChaCha20Poly1305, TODO:
-       "golang.org/x/crypto/blake2s"
-       "sync"
-       "time"
-)
-
-type MACStatePeer struct {
-       mutex     sync.RWMutex
-       cookieSet time.Time
-       cookie    [blake2s.Size128]byte
-       lastMAC1  [blake2s.Size128]byte
-       keyMAC1   [blake2s.Size]byte
-       keyMAC2   [blake2s.Size]byte
-       xaead     cipher.AEAD
-}
-
-func (state *MACStatePeer) Init(pk NoisePublicKey) {
-       state.mutex.Lock()
-       defer state.mutex.Unlock()
-
-       func() {
-               hsh, _ := blake2s.New256(nil)
-               hsh.Write([]byte(WGLabelMAC1))
-               hsh.Write(pk[:])
-               hsh.Sum(state.keyMAC1[:0])
-       }()
-
-       func() {
-               hsh, _ := blake2s.New256(nil)
-               hsh.Write([]byte(WGLabelCookie))
-               hsh.Write(pk[:])
-               hsh.Sum(state.keyMAC2[:0])
-       }()
-
-       state.xaead, _ = chacha20poly1305.NewXCipher(state.keyMAC2[:])
-       state.cookieSet = time.Time{} // never
-}
-
-func (state *MACStatePeer) AddMacs(msg []byte) {
-       size := len(msg)
-
-       if size < blake2s.Size128*2 {
-               panic(errors.New("bug: message too short"))
-       }
-
-       startMac1 := size - (blake2s.Size128 * 2)
-       startMac2 := size - blake2s.Size128
-
-       mac1 := msg[startMac1 : startMac1+blake2s.Size128]
-       mac2 := msg[startMac2 : startMac2+blake2s.Size128]
-
-       state.mutex.Lock()
-       defer state.mutex.Unlock()
-
-       // set mac1
-
-       func() {
-               mac, _ := blake2s.New128(state.keyMAC1[:])
-               mac.Write(msg[:startMac1])
-               mac.Sum(state.lastMAC1[:0])
-       }()
-       copy(mac1, state.lastMAC1[:])
-
-       // set mac2
-
-       if state.cookieSet.IsZero() {
-               return
-       }
-       if time.Now().Sub(state.cookieSet) > CookieRefreshTime {
-               state.cookieSet = time.Time{}
-               return
-       }
-       func() {
-               mac, _ := blake2s.New128(state.cookie[:])
-               mac.Write(msg[:startMac2])
-               mac.Sum(mac2[:0])
-       }()
-}
index a9f719b9d0b85a063142df75068fb67a11330eda..4f8227ecb3af9f4b635be1ffafb8308474289f3f 100644 (file)
@@ -1,9 +1,8 @@
 package main
 
-/* Implementation of the ratelimited form the linux kernel version
- *
- *
- *
+/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+/* This file contains a port of the ratelimited from the linux kernel version
  */
 
 import (
index 49c7e087d50fc535f0b89e26d773c8363c1127cd..5d4286003896ae16126e0959cbad108c11b3fe8a 100644 (file)
@@ -1,5 +1,7 @@
 package main
 
+/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
 /* Implementation of RFC6479
  * https://tools.ietf.org/html/rfc6479
  *
diff --git a/src/xchacha20.go b/src/xchacha20.go
new file mode 100644 (file)
index 0000000..5d963e0
--- /dev/null
@@ -0,0 +1,169 @@
+// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
+// Use of this source code is governed by a license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+       "encoding/binary"
+       "golang.org/x/crypto/chacha20poly1305"
+)
+
+func HChaCha20(out *[32]byte, nonce []byte, key *[32]byte) {
+
+       v00 := uint32(0x61707865)
+       v01 := uint32(0x3320646e)
+       v02 := uint32(0x79622d32)
+       v03 := uint32(0x6b206574)
+
+       v04 := binary.LittleEndian.Uint32(key[0:])
+       v05 := binary.LittleEndian.Uint32(key[4:])
+       v06 := binary.LittleEndian.Uint32(key[8:])
+       v07 := binary.LittleEndian.Uint32(key[12:])
+       v08 := binary.LittleEndian.Uint32(key[16:])
+       v09 := binary.LittleEndian.Uint32(key[20:])
+       v10 := binary.LittleEndian.Uint32(key[24:])
+       v11 := binary.LittleEndian.Uint32(key[28:])
+       v12 := binary.LittleEndian.Uint32(nonce[0:])
+       v13 := binary.LittleEndian.Uint32(nonce[4:])
+       v14 := binary.LittleEndian.Uint32(nonce[8:])
+       v15 := binary.LittleEndian.Uint32(nonce[12:])
+
+       for i := 0; i < 20; i += 2 {
+               v00 += v04
+               v12 ^= v00
+               v12 = (v12 << 16) | (v12 >> 16)
+               v08 += v12
+               v04 ^= v08
+               v04 = (v04 << 12) | (v04 >> 20)
+               v00 += v04
+               v12 ^= v00
+               v12 = (v12 << 8) | (v12 >> 24)
+               v08 += v12
+               v04 ^= v08
+               v04 = (v04 << 7) | (v04 >> 25)
+               v01 += v05
+               v13 ^= v01
+               v13 = (v13 << 16) | (v13 >> 16)
+               v09 += v13
+               v05 ^= v09
+               v05 = (v05 << 12) | (v05 >> 20)
+               v01 += v05
+               v13 ^= v01
+               v13 = (v13 << 8) | (v13 >> 24)
+               v09 += v13
+               v05 ^= v09
+               v05 = (v05 << 7) | (v05 >> 25)
+               v02 += v06
+               v14 ^= v02
+               v14 = (v14 << 16) | (v14 >> 16)
+               v10 += v14
+               v06 ^= v10
+               v06 = (v06 << 12) | (v06 >> 20)
+               v02 += v06
+               v14 ^= v02
+               v14 = (v14 << 8) | (v14 >> 24)
+               v10 += v14
+               v06 ^= v10
+               v06 = (v06 << 7) | (v06 >> 25)
+               v03 += v07
+               v15 ^= v03
+               v15 = (v15 << 16) | (v15 >> 16)
+               v11 += v15
+               v07 ^= v11
+               v07 = (v07 << 12) | (v07 >> 20)
+               v03 += v07
+               v15 ^= v03
+               v15 = (v15 << 8) | (v15 >> 24)
+               v11 += v15
+               v07 ^= v11
+               v07 = (v07 << 7) | (v07 >> 25)
+               v00 += v05
+               v15 ^= v00
+               v15 = (v15 << 16) | (v15 >> 16)
+               v10 += v15
+               v05 ^= v10
+               v05 = (v05 << 12) | (v05 >> 20)
+               v00 += v05
+               v15 ^= v00
+               v15 = (v15 << 8) | (v15 >> 24)
+               v10 += v15
+               v05 ^= v10
+               v05 = (v05 << 7) | (v05 >> 25)
+               v01 += v06
+               v12 ^= v01
+               v12 = (v12 << 16) | (v12 >> 16)
+               v11 += v12
+               v06 ^= v11
+               v06 = (v06 << 12) | (v06 >> 20)
+               v01 += v06
+               v12 ^= v01
+               v12 = (v12 << 8) | (v12 >> 24)
+               v11 += v12
+               v06 ^= v11
+               v06 = (v06 << 7) | (v06 >> 25)
+               v02 += v07
+               v13 ^= v02
+               v13 = (v13 << 16) | (v13 >> 16)
+               v08 += v13
+               v07 ^= v08
+               v07 = (v07 << 12) | (v07 >> 20)
+               v02 += v07
+               v13 ^= v02
+               v13 = (v13 << 8) | (v13 >> 24)
+               v08 += v13
+               v07 ^= v08
+               v07 = (v07 << 7) | (v07 >> 25)
+               v03 += v04
+               v14 ^= v03
+               v14 = (v14 << 16) | (v14 >> 16)
+               v09 += v14
+               v04 ^= v09
+               v04 = (v04 << 12) | (v04 >> 20)
+               v03 += v04
+               v14 ^= v03
+               v14 = (v14 << 8) | (v14 >> 24)
+               v09 += v14
+               v04 ^= v09
+               v04 = (v04 << 7) | (v04 >> 25)
+       }
+
+       binary.LittleEndian.PutUint32(out[0:], v00)
+       binary.LittleEndian.PutUint32(out[4:], v01)
+       binary.LittleEndian.PutUint32(out[8:], v02)
+       binary.LittleEndian.PutUint32(out[12:], v03)
+       binary.LittleEndian.PutUint32(out[16:], v12)
+       binary.LittleEndian.PutUint32(out[20:], v13)
+       binary.LittleEndian.PutUint32(out[24:], v14)
+       binary.LittleEndian.PutUint32(out[28:], v15)
+}
+
+func XChaCha20Poly1305Encrypt(
+       dst []byte,
+       nonceFull *[24]byte,
+       plaintext []byte,
+       additionalData []byte,
+       key *[chacha20poly1305.KeySize]byte,
+) []byte {
+       var nonce [chacha20poly1305.NonceSize]byte
+       var derivedKey [chacha20poly1305.KeySize]byte
+       HChaCha20(&derivedKey, nonceFull[:16], key)
+       aead, _ := chacha20poly1305.New(derivedKey[:])
+       copy(nonce[4:], nonceFull[16:])
+       return aead.Seal(dst, nonce[:], plaintext, additionalData)
+}
+
+func XChaCha20Poly1305Decrypt(
+       dst []byte,
+       nonceFull *[24]byte,
+       plaintext []byte,
+       additionalData []byte,
+       key *[chacha20poly1305.KeySize]byte,
+) ([]byte, error) {
+       var nonce [chacha20poly1305.NonceSize]byte
+       var derivedKey [chacha20poly1305.KeySize]byte
+       HChaCha20(&derivedKey, nonceFull[:16], key)
+       aead, _ := chacha20poly1305.New(derivedKey[:])
+       copy(nonce[4:], nonceFull[16:])
+       return aead.Open(dst, nonce[:], plaintext, additionalData)
+}
diff --git a/src/xchacha20_test.go b/src/xchacha20_test.go
new file mode 100644 (file)
index 0000000..0f41cf8
--- /dev/null
@@ -0,0 +1,96 @@
+package main
+
+import (
+       "encoding/hex"
+       "testing"
+)
+
+type XChaCha20Test struct {
+       Nonce string
+       Key   string
+       PT    string
+       CT    string
+}
+
+func TestXChaCha20(t *testing.T) {
+
+       tests := []XChaCha20Test{
+               {
+                       Nonce: "000000000000000000000000000000000000000000000000",
+                       Key:   "0000000000000000000000000000000000000000000000000000000000000000",
+                       PT:    "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+                       CT:    "789e9689e5208d7fd9e1f3c5b5341f48ef18a13e418998addadd97a3693a987f8e82ecd5c1433bfed1af49750c0f1ff29c4174a05b119aa3a9e8333812e0c0feb1299c5949d895ee01dbf50f8395dd84",
+               },
+               {
+                       Nonce: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
+                       Key:   "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
+                       PT:    "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
+                       CT:    "e1a046aa7f71e2af8b80b6408b2fd8d3a350278cde79c94d9efaa475e1339b3dd490127b",
+               },
+               {
+                       Nonce: "d9a8213e8a697508805c2c171ad54487ead9e3e02d82d5bc",
+                       Key:   "979196dbd78526f2f584f7534db3f5824d8ccfa858ca7e09bdd3656ecd36033c",
+                       PT:    "43cc6d624e451bbed952c3e071dc6c03392ce11eb14316a94b2fdc98b22fedea",
+                       CT:    "53c1e8bef2dbb8f2505ec010a7afe21d5a8e6dd8f987e4ea1a2ed5dfbc844ea400db34496fd2153526c6e87c36694200",
+               },
+       }
+
+       for _, test := range tests {
+
+               nonce, err := hex.DecodeString(test.Nonce)
+               if err != nil {
+                       panic(err)
+               }
+
+               key, err := hex.DecodeString(test.Key)
+               if err != nil {
+                       panic(err)
+               }
+
+               pt, err := hex.DecodeString(test.PT)
+               if err != nil {
+                       panic(err)
+               }
+
+               func() {
+                       var nonceArray [24]byte
+                       var keyArray [32]byte
+                       copy(nonceArray[:], nonce)
+                       copy(keyArray[:], key)
+
+                       // test encryption
+
+                       ct := XChaCha20Poly1305Encrypt(
+                               nil,
+                               &nonceArray,
+                               pt,
+                               nil,
+                               &keyArray,
+                       )
+                       ctHex := hex.EncodeToString(ct)
+                       if ctHex != test.CT {
+                               t.Fatal("encryption failed, expected:", test.CT, "got", ctHex)
+                       }
+
+                       // test decryption
+
+                       ptp, err := XChaCha20Poly1305Decrypt(
+                               nil,
+                               &nonceArray,
+                               ct,
+                               nil,
+                               &keyArray,
+                       )
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+
+                       ptHex := hex.EncodeToString(ptp)
+                       if ptHex != test.PT {
+                               t.Fatal("decryption failed, expected:", test.PT, "got", ptHex)
+                       }
+               }()
+
+       }
+
+}