| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- package state
- import (
- "bytes"
- "crypto/hmac"
- "crypto/rand"
- "crypto/sha256"
- "errors"
- "fmt"
- "io"
- "time"
- "github.com/mk6i/open-oscar-server/wire"
- )
- // authCookieLen is the fixed auth cookie length.
- const authCookieLen = 256
- // ServerCookie represents a token containing client metadata passed to the BOS
- // service upon connection.
- type ServerCookie struct {
- Service uint16
- ScreenName DisplayScreenName `oscar:"len_prefix=uint8"`
- ClientID string `oscar:"len_prefix=uint8"`
- ChatCookie string `oscar:"len_prefix=uint8"`
- MultiConnFlag uint8
- // KerberosAuth indicates whether the client used Kerberos for authentication.
- KerberosAuth uint8
- SessionNum uint8
- }
- func NewHMACCookieBaker() (HMACCookieBaker, error) {
- cb := HMACCookieBaker{}
- cb.key = make([]byte, 32)
- if _, err := io.ReadFull(rand.Reader, cb.key); err != nil {
- return cb, fmt.Errorf("cannot generate random HMAC key: %w", err)
- }
- return cb, nil
- }
- type HMACCookieBaker struct {
- key []byte
- }
- func (c HMACCookieBaker) Issue(data []byte) ([]byte, error) {
- payload := hmacTokenPayload{
- Expiry: uint32(time.Now().Add(1 * time.Minute).Unix()),
- Data: data,
- }
- buf := &bytes.Buffer{}
- if err := wire.MarshalBE(payload, buf); err != nil {
- return nil, fmt.Errorf("unable to marshal auth authCookie: %w", err)
- }
- hmacTok := hmacToken{
- Data: buf.Bytes(),
- }
- hmacTok.hash(c.key)
- buf.Reset()
- if err := wire.MarshalBE(hmacTok, buf); err != nil {
- return nil, fmt.Errorf("unable to marshal auth authCookie: %w", err)
- }
- // Some clients (such as perl NET::OSCAR) expect the auth cookie to be
- // exactly 256 bytes, even though the cookie is stored in a
- // variable-length TLV. Pad the auth cookie to make sure it's exactly
- // 256 bytes.
- if buf.Len() > authCookieLen {
- return nil, fmt.Errorf("sess is too long, expect 256 bytes, got %d", buf.Len())
- }
- buf.Write(make([]byte, authCookieLen-buf.Len()))
- return buf.Bytes(), nil
- }
- func (c HMACCookieBaker) Crack(data []byte) ([]byte, error) {
- hmacTok := hmacToken{}
- if err := wire.UnmarshalBE(&hmacTok, bytes.NewBuffer(data)); err != nil {
- return nil, fmt.Errorf("unable to unmarshal HMAC cookie: %w", err)
- }
- if !hmacTok.validate(c.key) {
- return nil, errors.New("invalid HMAC cookie")
- }
- payload := hmacTokenPayload{}
- if err := wire.UnmarshalBE(&payload, bytes.NewBuffer(hmacTok.Data)); err != nil {
- return nil, fmt.Errorf("unable to unmarshal HMAC cookie payload: %w", err)
- }
- expiry := time.Unix(int64(payload.Expiry), 0)
- if expiry.Before(time.Now()) {
- return nil, errors.New("HMAC cookie expired")
- }
- return payload.Data, nil
- }
- type hmacTokenPayload struct {
- Expiry uint32
- Data []byte `oscar:"len_prefix=uint16"`
- }
- type hmacToken struct {
- Data []byte `oscar:"len_prefix=uint16"`
- Sig []byte `oscar:"len_prefix=uint16"`
- }
- func (h *hmacToken) hash(key []byte) {
- hs := hmac.New(sha256.New, key)
- if _, err := hs.Write(h.Data); err != nil {
- // according to Hash interface, Write() should never return an error
- panic("unable to compute hmac token")
- }
- h.Sig = hs.Sum(nil)
- }
- func (h *hmacToken) validate(key []byte) bool {
- cp := *h
- cp.hash(key)
- return hmac.Equal(h.Sig, cp.Sig)
- }
|