This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Validator struct {
|
||||
conf SignatureConf
|
||||
nonceStore NonceStore
|
||||
}
|
||||
|
||||
func NewValidator(conf SignatureConf, store NonceStore) *Validator {
|
||||
return &Validator{
|
||||
conf: conf,
|
||||
nonceStore: store,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Validator) windowSeconds() int64 {
|
||||
if v.conf.ValidWindowSeconds <= 0 {
|
||||
return 300
|
||||
}
|
||||
return v.conf.ValidWindowSeconds
|
||||
}
|
||||
|
||||
func (v *Validator) Validate(ctx context.Context, appId, timestamp, nonce, signature, stringToSign string) error {
|
||||
secret, ok := v.conf.AppSecrets[appId]
|
||||
if !ok {
|
||||
return ErrSignatureMissing
|
||||
}
|
||||
|
||||
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return ErrSignatureExpired
|
||||
}
|
||||
diff := time.Now().Unix() - ts
|
||||
if diff < 0 {
|
||||
diff = -diff
|
||||
}
|
||||
if diff > v.windowSeconds() {
|
||||
return ErrSignatureExpired
|
||||
}
|
||||
|
||||
replayed, err := v.nonceStore.SetIfNotExists(ctx, appId, nonce, v.windowSeconds()+60)
|
||||
if err == nil && replayed {
|
||||
return ErrSignatureReplay
|
||||
}
|
||||
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
mac.Write([]byte(stringToSign))
|
||||
expected := hex.EncodeToString(mac.Sum(nil))
|
||||
if !hmac.Equal([]byte(expected), []byte(signature)) {
|
||||
return ErrSignatureInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user