/
hmac.go
125 lines (107 loc) · 2.91 KB
/
hmac.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package gauth
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"errors"
"encoding/hex"
"net/http"
"os"
"strings"
"log"
"sort"
"net/url"
)
const (
defaultMaxMemory = 32 << 20 // 32 MB
authHeader = "Authentication"
xHMACPrefix = "Hmac"
xHMACDate = "X-Hmac-Date"
xHMACNonce = "X-Hmac-Nonce"
)
func Authenticate(req *http.Request) (err error) {
authHeader := req.Header.Get(authHeader)
if len(authHeader) == 0 {
return errors.New("Missing Authentication header")
}
authTkns := strings.Split(authHeader, " ")
if len(authTkns) != 2 {
return errors.New("Malformed auth-tokens")
}
apiKey := []byte(authTkns[0])
signature := []byte(authTkns[1])
if !checkApiClient(apiKey) {
log.Println("ERROR unknown API client")
return errors.New("Error authenticating")
}
canonicalRep, err := canonicalRep(req)
if err != nil {
log.Printf("ERROR creating canonicalRep: %s", err)
return err
}
sharedSecret := []byte(os.Getenv("GAUTH_SHARED_SECRET"))
expectedMAC := computeSignature([]byte(canonicalRep), sharedSecret)
if !hmac.Equal(signature, expectedMAC){
log.Printf("ERROR signature and expectedMAC mismatch for:\n%s", canonicalRep)
log.Printf("*** expected signature: %s", string(expectedMAC))
log.Printf("*** computed signature: %s", string(signature))
return errors.New("Error authenticating")
}
return nil
}
func checkApiClient(apiKey []byte) bool {
// constant time byte comparison
return hmac.Equal(apiKey, []byte(os.Getenv("GAUTH_API_TOKEN")))
}
func computeSignature(message, sharedSecret []byte) []byte {
mac := hmac.New(sha256.New, sharedSecret)
mac.Write(message)
return []byte(hex.EncodeToString(mac.Sum(nil)))
}
/*
func computeSignature(message, sharedSecret []byte) []byte {
mac := hmac.New(sha256.New, sharedSecret)
mac.Write(message)
return mac.Sum(nil)
}
*/
func canonicalRep(req *http.Request) (rep string, err error) {
err = req.ParseMultipartForm(defaultMaxMemory)
if err != nil {
return rep, err
}
var repBuff bytes.Buffer
// HTTP verb (GET, POST,...) uppercased
repBuff.WriteString(strings.ToUpper(req.Method+"\n"))
// original URI
repBuff.WriteString(req.RequestURI+"\n")
// original headers
headers := req.Header
if hmacDate := headers[xHMACDate]; len(hmacDate) > 0 {
repBuff.WriteString("date:"+hmacDate[0]+"\n")
} else {
repBuff.WriteString("date:\n")
}
if hmacNonce := headers[xHMACNonce]; len(hmacNonce) > 0 {
repBuff.WriteString("nonce:"+hmacNonce[0]+"\n")
} else {
repBuff.WriteString("nonce:\n")
}
// request params
params := canonicalParams(req.Form)
for _, v := range params {
repBuff.WriteString(v+":"+req.Form[v][0]+"\n")
}
rep = repBuff.String()
return rep, nil
}
func canonicalParams(m url.Values) []string {
mk := make([]string, len(m))
i := 0
for k, _ := range m {
mk[i] = k
i++
}
sort.Strings(mk)
return mk
}