github.com/noisysockets/noisysockets@v0.21.2-0.20240515114641-7f467e651c90/internal/transport/cookie.go (about)

     1  // SPDX-License-Identifier: MPL-2.0
     2  /*
     3   * Copyright (C) 2024 The Noisy Sockets Authors.
     4   *
     5   * This Source Code Form is subject to the terms of the Mozilla Public
     6   * License, v. 2.0. If a copy of the MPL was not distributed with this
     7   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     8   *
     9   * Portions of this file are based on code originally from wireguard-go,
    10   *
    11   * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
    12   *
    13   * Permission is hereby granted, free of charge, to any person obtaining a copy of
    14   * this software and associated documentation files (the "Software"), to deal in
    15   * the Software without restriction, including without limitation the rights to
    16   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
    17   * of the Software, and to permit persons to whom the Software is furnished to do
    18   * so, subject to the following conditions:
    19   *
    20   * The above copyright notice and this permission notice shall be included in all
    21   * copies or substantial portions of the Software.
    22   *
    23   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    26   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    29   * SOFTWARE.
    30   */
    31  
    32  package transport
    33  
    34  import (
    35  	"crypto/hmac"
    36  	"crypto/rand"
    37  	"sync"
    38  	"time"
    39  
    40  	"github.com/noisysockets/noisysockets/types"
    41  	"golang.org/x/crypto/blake2s"
    42  	"golang.org/x/crypto/chacha20poly1305"
    43  )
    44  
    45  type CookieChecker struct {
    46  	sync.RWMutex
    47  	mac1 struct {
    48  		key [blake2s.Size]byte
    49  	}
    50  	mac2 struct {
    51  		secret        [blake2s.Size]byte
    52  		secretSet     time.Time
    53  		encryptionKey [chacha20poly1305.KeySize]byte
    54  	}
    55  }
    56  
    57  type CookieGenerator struct {
    58  	sync.RWMutex
    59  	mac1 struct {
    60  		key [blake2s.Size]byte
    61  	}
    62  	mac2 struct {
    63  		cookie        [blake2s.Size128]byte
    64  		cookieSet     time.Time
    65  		hasLastMAC1   bool
    66  		lastMAC1      [blake2s.Size128]byte
    67  		encryptionKey [chacha20poly1305.KeySize]byte
    68  	}
    69  }
    70  
    71  func (st *CookieChecker) Init(pk types.NoisePublicKey) {
    72  	st.Lock()
    73  	defer st.Unlock()
    74  
    75  	// mac1 state
    76  
    77  	func() {
    78  		hash, _ := blake2s.New256(nil)
    79  		hash.Write([]byte(NoiseLabelMAC1))
    80  		hash.Write(pk[:])
    81  		hash.Sum(st.mac1.key[:0])
    82  	}()
    83  
    84  	// mac2 state
    85  
    86  	func() {
    87  		hash, _ := blake2s.New256(nil)
    88  		hash.Write([]byte(NoiseLabelCookie))
    89  		hash.Write(pk[:])
    90  		hash.Sum(st.mac2.encryptionKey[:0])
    91  	}()
    92  
    93  	st.mac2.secretSet = time.Time{}
    94  }
    95  
    96  func (st *CookieChecker) CheckMAC1(msg []byte) bool {
    97  	st.RLock()
    98  	defer st.RUnlock()
    99  
   100  	size := len(msg)
   101  	smac2 := size - blake2s.Size128
   102  	smac1 := smac2 - blake2s.Size128
   103  
   104  	var mac1 [blake2s.Size128]byte
   105  
   106  	mac, _ := blake2s.New128(st.mac1.key[:])
   107  	mac.Write(msg[:smac1])
   108  	mac.Sum(mac1[:0])
   109  
   110  	return hmac.Equal(mac1[:], msg[smac1:smac2])
   111  }
   112  
   113  func (st *CookieChecker) CheckMAC2(msg, src []byte) bool {
   114  	st.RLock()
   115  	defer st.RUnlock()
   116  
   117  	if time.Since(st.mac2.secretSet) > CookieRefreshTime {
   118  		return false
   119  	}
   120  
   121  	// derive cookie key
   122  
   123  	var cookie [blake2s.Size128]byte
   124  	func() {
   125  		mac, _ := blake2s.New128(st.mac2.secret[:])
   126  		mac.Write(src)
   127  		mac.Sum(cookie[:0])
   128  	}()
   129  
   130  	// calculate mac of packet (including mac1)
   131  
   132  	smac2 := len(msg) - blake2s.Size128
   133  
   134  	var mac2 [blake2s.Size128]byte
   135  	func() {
   136  		mac, _ := blake2s.New128(cookie[:])
   137  		mac.Write(msg[:smac2])
   138  		mac.Sum(mac2[:0])
   139  	}()
   140  
   141  	return hmac.Equal(mac2[:], msg[smac2:])
   142  }
   143  
   144  func (st *CookieChecker) CreateReply(
   145  	msg []byte,
   146  	recv uint32,
   147  	src []byte,
   148  ) (*MessageCookieReply, error) {
   149  	st.RLock()
   150  
   151  	// refresh cookie secret
   152  
   153  	if time.Since(st.mac2.secretSet) > CookieRefreshTime {
   154  		st.RUnlock()
   155  		st.Lock()
   156  		_, err := rand.Read(st.mac2.secret[:])
   157  		if err != nil {
   158  			st.Unlock()
   159  			return nil, err
   160  		}
   161  		st.mac2.secretSet = time.Now()
   162  		st.Unlock()
   163  		st.RLock()
   164  	}
   165  
   166  	// derive cookie
   167  
   168  	var cookie [blake2s.Size128]byte
   169  	func() {
   170  		mac, _ := blake2s.New128(st.mac2.secret[:])
   171  		mac.Write(src)
   172  		mac.Sum(cookie[:0])
   173  	}()
   174  
   175  	// encrypt cookie
   176  
   177  	size := len(msg)
   178  
   179  	smac2 := size - blake2s.Size128
   180  	smac1 := smac2 - blake2s.Size128
   181  
   182  	reply := new(MessageCookieReply)
   183  	reply.Type = MessageCookieReplyType
   184  	reply.Receiver = recv
   185  
   186  	_, err := rand.Read(reply.Nonce[:])
   187  	if err != nil {
   188  		st.RUnlock()
   189  		return nil, err
   190  	}
   191  
   192  	xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
   193  	xchapoly.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], msg[smac1:smac2])
   194  
   195  	st.RUnlock()
   196  
   197  	return reply, nil
   198  }
   199  
   200  func (st *CookieGenerator) Init(pk types.NoisePublicKey) {
   201  	st.Lock()
   202  	defer st.Unlock()
   203  
   204  	func() {
   205  		hash, _ := blake2s.New256(nil)
   206  		hash.Write([]byte(NoiseLabelMAC1))
   207  		hash.Write(pk[:])
   208  		hash.Sum(st.mac1.key[:0])
   209  	}()
   210  
   211  	func() {
   212  		hash, _ := blake2s.New256(nil)
   213  		hash.Write([]byte(NoiseLabelCookie))
   214  		hash.Write(pk[:])
   215  		hash.Sum(st.mac2.encryptionKey[:0])
   216  	}()
   217  
   218  	st.mac2.cookieSet = time.Time{}
   219  }
   220  
   221  func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
   222  	st.Lock()
   223  	defer st.Unlock()
   224  
   225  	if !st.mac2.hasLastMAC1 {
   226  		return false
   227  	}
   228  
   229  	var cookie [blake2s.Size128]byte
   230  
   231  	xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
   232  	_, err := xchapoly.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], st.mac2.lastMAC1[:])
   233  	if err != nil {
   234  		return false
   235  	}
   236  
   237  	st.mac2.cookieSet = time.Now()
   238  	st.mac2.cookie = cookie
   239  	return true
   240  }
   241  
   242  func (st *CookieGenerator) AddMacs(msg []byte) {
   243  	size := len(msg)
   244  
   245  	smac2 := size - blake2s.Size128
   246  	smac1 := smac2 - blake2s.Size128
   247  
   248  	mac1 := msg[smac1:smac2]
   249  	mac2 := msg[smac2:]
   250  
   251  	st.Lock()
   252  	defer st.Unlock()
   253  
   254  	// set mac1
   255  
   256  	func() {
   257  		mac, _ := blake2s.New128(st.mac1.key[:])
   258  		mac.Write(msg[:smac1])
   259  		mac.Sum(mac1[:0])
   260  	}()
   261  	copy(st.mac2.lastMAC1[:], mac1)
   262  	st.mac2.hasLastMAC1 = true
   263  
   264  	// set mac2
   265  
   266  	if time.Since(st.mac2.cookieSet) > CookieRefreshTime {
   267  		return
   268  	}
   269  
   270  	func() {
   271  		mac, _ := blake2s.New128(st.mac2.cookie[:])
   272  		mac.Write(msg[:smac2])
   273  		mac.Sum(mac2[:0])
   274  	}()
   275  }