aboutsummaryrefslogtreecommitdiffstats
path: root/device/cookie.go
diff options
context:
space:
mode:
Diffstat (limited to 'device/cookie.go')
-rw-r--r--device/cookie.go250
1 files changed, 250 insertions, 0 deletions
diff --git a/device/cookie.go b/device/cookie.go
new file mode 100644
index 0000000..2f21067
--- /dev/null
+++ b/device/cookie.go
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package device
+
+import (
+ "crypto/hmac"
+ "crypto/rand"
+ "golang.org/x/crypto/blake2s"
+ "golang.org/x/crypto/chacha20poly1305"
+ "sync"
+ "time"
+)
+
+type CookieChecker struct {
+ sync.RWMutex
+ mac1 struct {
+ key [blake2s.Size]byte
+ }
+ mac2 struct {
+ secret [blake2s.Size]byte
+ secretSet time.Time
+ encryptionKey [chacha20poly1305.KeySize]byte
+ }
+}
+
+type CookieGenerator struct {
+ sync.RWMutex
+ mac1 struct {
+ key [blake2s.Size]byte
+ }
+ mac2 struct {
+ cookie [blake2s.Size128]byte
+ cookieSet time.Time
+ hasLastMAC1 bool
+ lastMAC1 [blake2s.Size128]byte
+ encryptionKey [chacha20poly1305.KeySize]byte
+ }
+}
+
+func (st *CookieChecker) Init(pk NoisePublicKey) {
+ st.Lock()
+ defer st.Unlock()
+
+ // mac1 state
+
+ func() {
+ hash, _ := blake2s.New256(nil)
+ hash.Write([]byte(WGLabelMAC1))
+ hash.Write(pk[:])
+ hash.Sum(st.mac1.key[:0])
+ }()
+
+ // mac2 state
+
+ func() {
+ hash, _ := blake2s.New256(nil)
+ hash.Write([]byte(WGLabelCookie))
+ hash.Write(pk[:])
+ hash.Sum(st.mac2.encryptionKey[:0])
+ }()
+
+ st.mac2.secretSet = time.Time{}
+}
+
+func (st *CookieChecker) CheckMAC1(msg []byte) bool {
+ st.RLock()
+ defer st.RUnlock()
+
+ size := len(msg)
+ smac2 := size - blake2s.Size128
+ smac1 := smac2 - blake2s.Size128
+
+ var mac1 [blake2s.Size128]byte
+
+ mac, _ := blake2s.New128(st.mac1.key[:])
+ mac.Write(msg[:smac1])
+ mac.Sum(mac1[:0])
+
+ return hmac.Equal(mac1[:], msg[smac1:smac2])
+}
+
+func (st *CookieChecker) CheckMAC2(msg []byte, src []byte) bool {
+ st.RLock()
+ defer st.RUnlock()
+
+ if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
+ return false
+ }
+
+ // derive cookie key
+
+ var cookie [blake2s.Size128]byte
+ func() {
+ mac, _ := blake2s.New128(st.mac2.secret[:])
+ mac.Write(src)
+ mac.Sum(cookie[:0])
+ }()
+
+ // calculate mac of packet (including mac1)
+
+ smac2 := len(msg) - blake2s.Size128
+
+ var mac2 [blake2s.Size128]byte
+ func() {
+ mac, _ := blake2s.New128(cookie[:])
+ mac.Write(msg[:smac2])
+ mac.Sum(mac2[:0])
+ }()
+
+ return hmac.Equal(mac2[:], msg[smac2:])
+}
+
+func (st *CookieChecker) CreateReply(
+ msg []byte,
+ recv uint32,
+ src []byte,
+) (*MessageCookieReply, error) {
+
+ st.RLock()
+
+ // refresh cookie secret
+
+ if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
+ st.RUnlock()
+ st.Lock()
+ _, err := rand.Read(st.mac2.secret[:])
+ if err != nil {
+ st.Unlock()
+ return nil, err
+ }
+ st.mac2.secretSet = time.Now()
+ st.Unlock()
+ st.RLock()
+ }
+
+ // derive cookie
+
+ var cookie [blake2s.Size128]byte
+ func() {
+ mac, _ := blake2s.New128(st.mac2.secret[:])
+ mac.Write(src)
+ mac.Sum(cookie[:0])
+ }()
+
+ // encrypt cookie
+
+ size := len(msg)
+
+ smac2 := size - blake2s.Size128
+ smac1 := smac2 - blake2s.Size128
+
+ reply := new(MessageCookieReply)
+ reply.Type = MessageCookieReplyType
+ reply.Receiver = recv
+
+ _, err := rand.Read(reply.Nonce[:])
+ if err != nil {
+ st.RUnlock()
+ return nil, err
+ }
+
+ xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
+ xchapoly.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], msg[smac1:smac2])
+
+ st.RUnlock()
+
+ return reply, nil
+}
+
+func (st *CookieGenerator) Init(pk NoisePublicKey) {
+ st.Lock()
+ defer st.Unlock()
+
+ func() {
+ hash, _ := blake2s.New256(nil)
+ hash.Write([]byte(WGLabelMAC1))
+ hash.Write(pk[:])
+ hash.Sum(st.mac1.key[:0])
+ }()
+
+ func() {
+ hash, _ := blake2s.New256(nil)
+ hash.Write([]byte(WGLabelCookie))
+ hash.Write(pk[:])
+ hash.Sum(st.mac2.encryptionKey[:0])
+ }()
+
+ st.mac2.cookieSet = time.Time{}
+}
+
+func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
+ st.Lock()
+ defer st.Unlock()
+
+ if !st.mac2.hasLastMAC1 {
+ return false
+ }
+
+ var cookie [blake2s.Size128]byte
+
+ xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
+ _, err := xchapoly.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], st.mac2.lastMAC1[:])
+
+ if err != nil {
+ return false
+ }
+
+ st.mac2.cookieSet = time.Now()
+ st.mac2.cookie = cookie
+ return true
+}
+
+func (st *CookieGenerator) AddMacs(msg []byte) {
+
+ size := len(msg)
+
+ smac2 := size - blake2s.Size128
+ smac1 := smac2 - blake2s.Size128
+
+ mac1 := msg[smac1:smac2]
+ mac2 := msg[smac2:]
+
+ st.Lock()
+ defer st.Unlock()
+
+ // set mac1
+
+ func() {
+ mac, _ := blake2s.New128(st.mac1.key[:])
+ mac.Write(msg[:smac1])
+ mac.Sum(mac1[:0])
+ }()
+ copy(st.mac2.lastMAC1[:], mac1)
+ st.mac2.hasLastMAC1 = true
+
+ // set mac2
+
+ if time.Now().Sub(st.mac2.cookieSet) > CookieRefreshTime {
+ return
+ }
+
+ func() {
+ mac, _ := blake2s.New128(st.mac2.cookie[:])
+ mac.Write(msg[:smac2])
+ mac.Sum(mac2[:0])
+ }()
+}