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 }