cookie.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package state
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/rand"
  6. "crypto/sha256"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "time"
  11. "github.com/mk6i/retro-aim-server/wire"
  12. )
  13. // authCookieLen is the fixed auth cookie length.
  14. const authCookieLen = 256
  15. // ServerCookie represents a token containing client metadata passed to the BOS
  16. // service upon connection.
  17. type ServerCookie struct {
  18. Service uint16
  19. ScreenName DisplayScreenName `oscar:"len_prefix=uint8"`
  20. ClientID string `oscar:"len_prefix=uint8"`
  21. ChatCookie string `oscar:"len_prefix=uint8"`
  22. MultiConnFlag uint8
  23. // KerberosAuth indicates whether the client used Kerberos for authentication.
  24. KerberosAuth uint8
  25. }
  26. func NewHMACCookieBaker() (HMACCookieBaker, error) {
  27. cb := HMACCookieBaker{}
  28. cb.key = make([]byte, 32)
  29. if _, err := io.ReadFull(rand.Reader, cb.key); err != nil {
  30. return cb, fmt.Errorf("cannot generate random HMAC key: %w", err)
  31. }
  32. return cb, nil
  33. }
  34. type HMACCookieBaker struct {
  35. key []byte
  36. }
  37. func (c HMACCookieBaker) Issue(data []byte) ([]byte, error) {
  38. payload := hmacTokenPayload{
  39. Expiry: uint32(time.Now().Add(1 * time.Minute).Unix()),
  40. Data: data,
  41. }
  42. buf := &bytes.Buffer{}
  43. if err := wire.MarshalBE(payload, buf); err != nil {
  44. return nil, fmt.Errorf("unable to marshal auth authCookie: %w", err)
  45. }
  46. hmacTok := hmacToken{
  47. Data: buf.Bytes(),
  48. }
  49. hmacTok.hash(c.key)
  50. buf.Reset()
  51. if err := wire.MarshalBE(hmacTok, buf); err != nil {
  52. return nil, fmt.Errorf("unable to marshal auth authCookie: %w", err)
  53. }
  54. // Some clients (such as perl NET::OSCAR) expect the auth cookie to be
  55. // exactly 256 bytes, even though the cookie is stored in a
  56. // variable-length TLV. Pad the auth cookie to make sure it's exactly
  57. // 256 bytes.
  58. if buf.Len() > authCookieLen {
  59. return nil, fmt.Errorf("sess is too long, expect 256 bytes, got %d", buf.Len())
  60. }
  61. buf.Write(make([]byte, authCookieLen-buf.Len()))
  62. return buf.Bytes(), nil
  63. }
  64. func (c HMACCookieBaker) Crack(data []byte) ([]byte, error) {
  65. hmacTok := hmacToken{}
  66. if err := wire.UnmarshalBE(&hmacTok, bytes.NewBuffer(data)); err != nil {
  67. return nil, fmt.Errorf("unable to unmarshal HMAC cooie: %w", err)
  68. }
  69. if !hmacTok.validate(c.key) {
  70. return nil, errors.New("invalid HMAC cookie")
  71. }
  72. payload := hmacTokenPayload{}
  73. if err := wire.UnmarshalBE(&payload, bytes.NewBuffer(hmacTok.Data)); err != nil {
  74. return nil, fmt.Errorf("unable to unmarshal HMAC cookie payload: %w", err)
  75. }
  76. expiry := time.Unix(int64(payload.Expiry), 0)
  77. if expiry.Before(time.Now()) {
  78. return nil, errors.New("HMAC cookie expired")
  79. }
  80. return payload.Data, nil
  81. }
  82. type hmacTokenPayload struct {
  83. Expiry uint32
  84. Data []byte `oscar:"len_prefix=uint16"`
  85. }
  86. type hmacToken struct {
  87. Data []byte `oscar:"len_prefix=uint16"`
  88. Sig []byte `oscar:"len_prefix=uint16"`
  89. }
  90. func (h *hmacToken) hash(key []byte) {
  91. hs := hmac.New(sha256.New, key)
  92. if _, err := hs.Write(h.Data); err != nil {
  93. // according to Hash interface, Write() should never return an error
  94. panic("unable to compute hmac token")
  95. }
  96. h.Sig = hs.Sum(nil)
  97. }
  98. func (h *hmacToken) validate(key []byte) bool {
  99. cp := *h
  100. cp.hash(key)
  101. return hmac.Equal(h.Sig, cp.Sig)
  102. }