|
1 | 1 | package hmac |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "bytes" |
5 | 4 | "crypto/hmac" |
6 | 5 | "crypto/sha256" |
7 | 6 | "encoding/base64" |
8 | 7 | "encoding/binary" |
| 8 | + "hash" |
9 | 9 | "time" |
10 | 10 | ) |
11 | 11 |
|
12 | 12 | const ( |
13 | 13 | HMACSignaturePrefix = "dumbproxy grant token v1" |
14 | | - HMACSignatureSize = 32 |
| 14 | + HMACExpireSize = 8 |
| 15 | + passwordBufferSize = HMACExpireSize + 64 // for worst case if 512-bit hash is used for some reason |
15 | 16 | ) |
16 | 17 |
|
17 | 18 | var hmacSignaturePrefix = []byte(HMACSignaturePrefix) |
18 | 19 |
|
19 | | -type HMACToken struct { |
20 | | - Expire int64 |
21 | | - Signature [HMACSignatureSize]byte |
| 20 | +func NewHasher(secret []byte) hash.Hash { |
| 21 | + return hmac.New(sha256.New, secret) |
22 | 22 | } |
23 | 23 |
|
24 | | -func VerifyHMACLoginAndPassword(secret, login, password []byte) bool { |
25 | | - rd := base64.NewDecoder(base64.RawURLEncoding, bytes.NewReader(password)) |
| 24 | +type Verifier struct { |
| 25 | + mac hash.Hash |
| 26 | + buf []byte |
| 27 | +} |
| 28 | + |
| 29 | +func NewVerifier(secret []byte) *Verifier { |
| 30 | + return &Verifier{ |
| 31 | + mac: hmac.New(sha256.New, secret), |
| 32 | + } |
| 33 | +} |
26 | 34 |
|
27 | | - var token HMACToken |
28 | | - if err := binary.Read(rd, binary.BigEndian, &token); err != nil { |
| 35 | +func (v *Verifier) ensureBufferSize(size int) { |
| 36 | + if len(v.buf) < size { |
| 37 | + v.buf = make([]byte, size) |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +func (v *Verifier) VerifyLoginAndPassword(login, password []byte) bool { |
| 42 | + v.ensureBufferSize(base64.RawURLEncoding.DecodedLen(len(password))) |
| 43 | + buf := v.buf |
| 44 | + n, err := base64.RawURLEncoding.Decode(buf, password) |
| 45 | + if err != nil { |
29 | 46 | return false |
30 | 47 | } |
| 48 | + buf = buf[:n] |
31 | 49 |
|
32 | | - if time.Unix(token.Expire, 0).Before(time.Now()) { |
| 50 | + var expire int64 |
| 51 | + if len(buf) < HMACExpireSize { |
33 | 52 | return false |
34 | 53 | } |
| 54 | + expire = int64(binary.BigEndian.Uint64(buf[:HMACExpireSize])) |
| 55 | + buf = buf[HMACExpireSize:] |
35 | 56 |
|
36 | | - expectedMAC := CalculateHMACSignature(secret, login, token.Expire) |
37 | | - return hmac.Equal(token.Signature[:], expectedMAC) |
| 57 | + if time.Unix(expire, 0).Before(time.Now()) { |
| 58 | + return false |
| 59 | + } |
| 60 | + |
| 61 | + if len(buf) < v.mac.Size() { |
| 62 | + return false |
| 63 | + } |
| 64 | + |
| 65 | + expectedMAC := v.calculateHMACSignature(login, expire) |
| 66 | + return hmac.Equal(buf[:v.mac.Size()], expectedMAC) |
38 | 67 | } |
39 | 68 |
|
40 | | -func CalculateHMACSignature(secret, username []byte, expire int64) []byte { |
41 | | - mac := hmac.New(sha256.New, secret) |
42 | | - mac.Write(hmacSignaturePrefix) |
43 | | - mac.Write(username) |
44 | | - binary.Write(mac, binary.BigEndian, expire) |
45 | | - return mac.Sum(nil) |
| 69 | +func (v *Verifier) calculateHMACSignature(username []byte, expire int64) []byte { |
| 70 | + var buf [HMACExpireSize]byte |
| 71 | + binary.BigEndian.PutUint64(buf[:], uint64(expire)) |
| 72 | + |
| 73 | + v.mac.Reset() |
| 74 | + v.mac.Write(hmacSignaturePrefix) |
| 75 | + v.mac.Write(username) |
| 76 | + v.mac.Write(buf[:]) |
| 77 | + |
| 78 | + return v.mac.Sum(nil) |
46 | 79 | } |
0 commit comments