github.com/ezoic/ws@v1.0.4-0.20220713205711-5c1d69e074c5/nonce.go (about) 1 package ws 2 3 import ( 4 "bufio" 5 "bytes" 6 "crypto/sha1" 7 "encoding/base64" 8 "fmt" 9 "math/rand" 10 ) 11 12 const ( 13 // RFC6455: The value of this header field MUST be a nonce consisting of a 14 // randomly selected 16-byte value that has been base64-encoded (see 15 // Section 4 of [RFC4648]). The nonce MUST be selected randomly for each 16 // connection. 17 nonceKeySize = 16 18 nonceSize = 24 // base64.StdEncoding.EncodedLen(nonceKeySize) 19 20 // RFC6455: The value of this header field is constructed by concatenating 21 // /key/, defined above in step 4 in Section 4.2.2, with the string 22 // "258EAFA5- E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this 23 // concatenated value to obtain a 20-byte value and base64- encoding (see 24 // Section 4 of [RFC4648]) this 20-byte hash. 25 acceptSize = 28 // base64.StdEncoding.EncodedLen(sha1.Size) 26 ) 27 28 // initNonce fills given slice with random base64-encoded nonce bytes. 29 func initNonce(dst []byte) { 30 // NOTE: bts does not escape. 31 bts := make([]byte, nonceKeySize) 32 if _, err := rand.Read(bts); err != nil { 33 panic(fmt.Sprintf("rand read error: %s", err)) 34 } 35 base64.StdEncoding.Encode(dst, bts) 36 } 37 38 // checkAcceptFromNonce reports whether given accept bytes are valid for given 39 // nonce bytes. 40 func checkAcceptFromNonce(accept, nonce []byte) bool { 41 if len(accept) != acceptSize { 42 return false 43 } 44 // NOTE: expect does not escape. 45 expect := make([]byte, acceptSize) 46 initAcceptFromNonce(expect, nonce) 47 return bytes.Equal(expect, accept) 48 } 49 50 // initAcceptFromNonce fills given slice with accept bytes generated from given 51 // nonce bytes. Given buffer should be exactly acceptSize bytes. 52 func initAcceptFromNonce(accept, nonce []byte) { 53 const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 54 55 if len(accept) != acceptSize { 56 panic("accept buffer is invalid") 57 } 58 if len(nonce) != nonceSize { 59 panic("nonce is invalid") 60 } 61 62 p := make([]byte, nonceSize+len(magic)) 63 copy(p[:nonceSize], nonce) 64 copy(p[nonceSize:], magic) 65 66 sum := sha1.Sum(p) 67 base64.StdEncoding.Encode(accept, sum[:]) 68 69 return 70 } 71 72 func writeAccept(bw *bufio.Writer, nonce []byte) (int, error) { 73 accept := make([]byte, acceptSize) 74 initAcceptFromNonce(accept, nonce) 75 // NOTE: write accept bytes as a string to prevent heap allocation – 76 // WriteString() copy given string into its inner buffer, unlike Write() 77 // which may write p directly to the underlying io.Writer – which in turn 78 // will lead to p escape. 79 return bw.WriteString(btsToString(accept)) 80 }