/
auth.go
199 lines (157 loc) · 4.61 KB
/
auth.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// +build go1.12
package state
import (
"bytes"
"crypto/hmac"
"encoding/hex"
"fmt"
"time"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
)
type Verifier struct {
p []byte // password
s []byte // secret
}
func NewVerifier(clientId, clientSecret string) (*Verifier, error) {
var buf bytes.Buffer
// state auth is build from the client secret & client id.
// derive 2 keys from clientSecret
// pass is blake2 ( hkdf( sha3(clientSecret + clientId)) )
// secret is blake2 ( hkdf( sha3(clientSecret + clientId + UrlRedirect)) )
sha3ClientSecret := sha3.Sum512([]byte(clientSecret))
sha3ClientId := sha3.Sum512([]byte(clientId))
_, err := buf.Write(sha3ClientSecret[:])
if err != nil {
return nil, err
}
_, err = buf.Write(sha3ClientId[:])
if err != nil {
return nil, err
}
sha3KdfSalt := sha3.Sum512(buf.Bytes())
// XXX ok this needs to move in the ProviderAuth part to avoid someone
// using the auth without derivation.. users hey!
// if for some reason there is a crypto biais or side channel, at least
// the secret is derived and it does not leak the clientSecret directly
// XXX key material handling is shit here :)
hkdfReader := hkdf.New(sha3.New512, sha3ClientSecret[:], sha3KdfSalt[:], []byte(clientId))
oidcpass := make([]byte, 1024)
oidcsecret := make([]byte, 1024)
// first 64 bytes of that reader -> pass (state encryption key)
_, err = hkdfReader.Read(oidcpass)
if err != nil {
return nil, err
}
// second 64 bytes of that reader -> secret (hmac state key)
_, err = hkdfReader.Read(oidcsecret)
if err != nil {
return nil, err
}
pTmp := sha3.Sum512([]byte(oidcpass))
sTmp := sha3.Sum512([]byte(oidcsecret))
return &Verifier{
p: pTmp[:], // this is to encrypt the state
s: sTmp[:], // this is for the hmac part that goes in the URL
}, nil
}
func hmac256(key, data []byte) ([]byte, error) {
hm := hmac.New(sha3.New256, key)
_, err := hm.Write(data)
if err != nil {
return nil, err
}
mac := hm.Sum(nil)
return mac, nil
}
func (pa *Verifier) stateHmac(data []byte) (string, error) {
var nilstr string
mac, err := hmac256(pa.s, data)
if err != nil {
return nilstr, err
}
return hex.EncodeToString(mac), nil
}
func (pa *Verifier) stateHmacEqual(data []byte, stateHmac string) bool {
mac, err := hmac256(pa.s, data)
if err != nil {
return false
}
machex, err := hex.DecodeString(stateHmac)
if err != nil {
return false
}
return hmac.Equal(mac, machex)
}
func (pa *Verifier) New(provider, oidcNonce string) (string, string, error) {
return pa.NewWithData(provider, oidcNonce, nil)
}
// duration would be 30 minutes -> NOW()
//func (oa *ProviderAuth) State(password, secret []byte, oidcNonce string) (*OidcStateValue, error) {
// let's limit that state otherwise...
// let's limit nonceSize also
func (pa *Verifier) NewWithData(provider, nonce string, userData []byte) (string, string, error) {
var nilstr string
if len(userData) > MaxUserDataSize || len(nonce) > MaxUserDataSize {
return nilstr, nilstr, ErrInvalid
}
d := NewData(nonce, userData)
data, err := d.Pack()
if err != nil {
return nilstr, nilstr, ErrInvalid
}
e, err := NewEnvelope(provider)
if err != nil {
return nilstr, nilstr, ErrInvalid
}
err = e.Seal(pa.p, data)
if err != nil {
return nilstr, nilstr, ErrInvalid
}
// envelope.Pack()
cookie, err := e.Pack()
if err != nil {
return nilstr, nilstr, ErrInvalid
}
// hmac
state, err := pa.stateHmac([]byte(cookie))
if err != nil {
return nilstr, nilstr, ErrInvalid
}
// return
return cookie, state, nil
}
func (pa *Verifier) Validate(cookie, state string, t time.Duration) (nonce string, err error) {
n, _, err := pa.ValidateWithData(cookie, state, t)
return n, err
}
func (pa *Verifier) ValidateWithData(cookie, stateparam string, t time.Duration) (nonce string, userData []byte, err error) {
var nilstr string
fmt.Printf("verification starts: ok\n")
if !pa.stateHmacEqual([]byte(cookie), stateparam) {
return nilstr, nil, ErrInvalidState
}
fmt.Printf("verification state equality: ok\n")
e, err := ParseEnvelope(cookie)
if err != nil {
return nilstr, nil, err
}
fmt.Printf("verification envelope parsing: ok\n")
data, err := e.Open(pa.p)
if err != nil {
return nilstr, nil, err
}
fmt.Printf("verification crypto open: ok\n")
d, err := ParseData(data)
if err != nil {
return nilstr, nil, err
}
fmt.Printf("verification data parsing: ok\n")
// is the state expired?
stateCreationTime := time.Unix(d.Timestamp, 0)
fmt.Printf("verification time expiration: %v vs %v\n", time.Now(), stateCreationTime)
if time.Since(stateCreationTime) > t {
return nilstr, nil, ErrInvalidState
}
return d.Nonce, d.Userdata, nil
}