nonce.go 2.4 KB

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