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  }