cookie.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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/open-oscar-server/wire"
  12. )
  13. // authCookieLen is the fixed auth cookie length.
  14. const authCookieLen = 256
  15. // DefaultCookieTTL is the auth cookie lifetime granted to a client that does not
  16. // ask for one.
  17. const DefaultCookieTTL = time.Minute
  18. // ServerCookie represents a token containing client metadata passed to the BOS
  19. // service upon connection.
  20. type ServerCookie struct {
  21. Service uint16
  22. ScreenName DisplayScreenName `oscar:"len_prefix=uint8"`
  23. ClientID string `oscar:"len_prefix=uint8"`
  24. ChatCookie string `oscar:"len_prefix=uint8"`
  25. MultiConnFlag uint8
  26. // KerberosAuth indicates whether the client used Kerberos for authentication.
  27. KerberosAuth uint8
  28. SessionNum uint8
  29. // TokenTTL is the lifetime in seconds this cookie was granted. Subtracted from
  30. // the expiry, it gives the instant the cookie was issued — for a login cookie,
  31. // when its owner authenticated. Zero means unspecified.
  32. TokenTTL uint32
  33. }
  34. func NewHMACCookieBaker() (HMACCookieBaker, error) {
  35. cb := HMACCookieBaker{}
  36. cb.key = make([]byte, 32)
  37. if _, err := io.ReadFull(rand.Reader, cb.key); err != nil {
  38. return cb, fmt.Errorf("cannot generate random HMAC key: %w", err)
  39. }
  40. return cb, nil
  41. }
  42. type HMACCookieBaker struct {
  43. key []byte
  44. }
  45. // Issue mints a token carrying data that Crack honors until ttl has elapsed.
  46. func (c HMACCookieBaker) Issue(data []byte, ttl time.Duration) ([]byte, error) {
  47. payload := hmacTokenPayload{
  48. Expiry: uint32(time.Now().Add(ttl).Unix()),
  49. Data: data,
  50. }
  51. buf := &bytes.Buffer{}
  52. if err := wire.MarshalBE(payload, buf); err != nil {
  53. return nil, fmt.Errorf("unable to marshal auth authCookie: %w", err)
  54. }
  55. hmacTok := hmacToken{
  56. Data: buf.Bytes(),
  57. }
  58. hmacTok.hash(c.key)
  59. buf.Reset()
  60. if err := wire.MarshalBE(hmacTok, buf); err != nil {
  61. return nil, fmt.Errorf("unable to marshal auth authCookie: %w", err)
  62. }
  63. // Some clients (such as perl NET::OSCAR) expect the auth cookie to be
  64. // exactly 256 bytes, even though the cookie is stored in a
  65. // variable-length TLV. Pad the auth cookie to make sure it's exactly
  66. // 256 bytes.
  67. if buf.Len() > authCookieLen {
  68. return nil, fmt.Errorf("sess is too long, expect 256 bytes, got %d", buf.Len())
  69. }
  70. buf.Write(make([]byte, authCookieLen-buf.Len()))
  71. return buf.Bytes(), nil
  72. }
  73. // Crack verifies a token and returns its payload along with the instant it
  74. // stops being valid, so callers can report how much life it has left.
  75. func (c HMACCookieBaker) Crack(data []byte) ([]byte, time.Time, error) {
  76. hmacTok := hmacToken{}
  77. if err := wire.UnmarshalBE(&hmacTok, bytes.NewBuffer(data)); err != nil {
  78. return nil, time.Time{}, fmt.Errorf("unable to unmarshal HMAC cookie: %w", err)
  79. }
  80. if !hmacTok.validate(c.key) {
  81. return nil, time.Time{}, errors.New("invalid HMAC cookie")
  82. }
  83. payload := hmacTokenPayload{}
  84. if err := wire.UnmarshalBE(&payload, bytes.NewBuffer(hmacTok.Data)); err != nil {
  85. return nil, time.Time{}, fmt.Errorf("unable to unmarshal HMAC cookie payload: %w", err)
  86. }
  87. expiry := time.Unix(int64(payload.Expiry), 0)
  88. if expiry.Before(time.Now()) {
  89. return nil, time.Time{}, errors.New("HMAC cookie expired")
  90. }
  91. return payload.Data, expiry, nil
  92. }
  93. type hmacTokenPayload struct {
  94. Expiry uint32
  95. Data []byte `oscar:"len_prefix=uint16"`
  96. }
  97. type hmacToken struct {
  98. Data []byte `oscar:"len_prefix=uint16"`
  99. Sig []byte `oscar:"len_prefix=uint16"`
  100. }
  101. func (h *hmacToken) hash(key []byte) {
  102. hs := hmac.New(sha256.New, key)
  103. if _, err := hs.Write(h.Data); err != nil {
  104. // according to Hash interface, Write() should never return an error
  105. panic("unable to compute hmac token")
  106. }
  107. h.Sig = hs.Sum(nil)
  108. }
  109. func (h *hmacToken) validate(key []byte) bool {
  110. cp := *h
  111. cp.hash(key)
  112. return hmac.Equal(h.Sig, cp.Sig)
  113. }