summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMathias Hall-Andersen <mathias@hall-andersen.dk>2017-07-12 23:11:49 +0200
committerMathias Hall-Andersen <mathias@hall-andersen.dk>2017-07-12 23:11:49 +0200
commit8393cbff521560caef5b1b468cbb2ad030e8eda4 (patch)
tree7fb701a023beb36780eebded65d4915ff196925d
parent86707cbc88fbad58481b400eaf12fa75c78f9855 (diff)
Restructured MAC/cookie calculation
Added copy-right headers accidentally removed
-rw-r--r--src/helper_test.go4
-rw-r--r--src/macs.go (renamed from src/macs_device.go)109
-rw-r--r--src/macs_peer.go83
-rw-r--r--src/ratelimiter.go7
-rw-r--r--src/replay.go2
-rw-r--r--src/xchacha20.go169
-rw-r--r--src/xchacha20_test.go96
7 files changed, 374 insertions, 96 deletions
diff --git a/src/helper_test.go b/src/helper_test.go
index 6d85771..3838a7c 100644
--- a/src/helper_test.go
+++ b/src/helper_test.go
@@ -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) {
diff --git a/src/macs_device.go b/src/macs.go
index 68181e6..841ef31 100644
--- a/src/macs_device.go
+++ b/src/macs.go
@@ -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
index 16a7a87..0000000
--- a/src/macs_peer.go
+++ /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])
- }()
-}
diff --git a/src/ratelimiter.go b/src/ratelimiter.go
index a9f719b..4f8227e 100644
--- a/src/ratelimiter.go
+++ b/src/ratelimiter.go
@@ -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 (
diff --git a/src/replay.go b/src/replay.go
index 49c7e08..5d42860 100644
--- a/src/replay.go
+++ b/src/replay.go
@@ -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
index 0000000..5d963e0
--- /dev/null
+++ b/src/xchacha20.go
@@ -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
index 0000000..0f41cf8
--- /dev/null
+++ b/src/xchacha20_test.go
@@ -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)
+ }
+ }()
+
+ }
+
+}