53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
package voiceprovider
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
HeaderCallbackTimestamp = "X-Provider-Timestamp"
|
|
HeaderCallbackSignature = "X-Provider-Signature"
|
|
)
|
|
|
|
var ErrInvalidCallbackSignature = errors.New("invalid provider callback signature")
|
|
|
|
func SignCallback(secret []byte, timestamp string, body []byte) string {
|
|
digest := sha256.Sum256(body)
|
|
canonical := timestamp + "\n" + hex.EncodeToString(digest[:])
|
|
mac := hmac.New(sha256.New, secret)
|
|
_, _ = mac.Write([]byte(canonical))
|
|
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
|
}
|
|
|
|
func VerifyCallback(secret []byte, timestamp, signature string, body []byte, now time.Time, maxSkew time.Duration) error {
|
|
if len(secret) < 32 || strings.TrimSpace(timestamp) == "" || strings.TrimSpace(signature) == "" || maxSkew <= 0 {
|
|
return ErrInvalidCallbackSignature
|
|
}
|
|
parsed, err := time.Parse(time.RFC3339, timestamp)
|
|
if err != nil || absoluteDuration(now.Sub(parsed)) > maxSkew {
|
|
return ErrInvalidCallbackSignature
|
|
}
|
|
provided, err := base64.RawURLEncoding.DecodeString(signature)
|
|
if err != nil {
|
|
return ErrInvalidCallbackSignature
|
|
}
|
|
expected, _ := base64.RawURLEncoding.DecodeString(SignCallback(secret, timestamp, body))
|
|
if !hmac.Equal(provided, expected) {
|
|
return ErrInvalidCallbackSignature
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func absoluteDuration(value time.Duration) time.Duration {
|
|
if value < 0 {
|
|
return -value
|
|
}
|
|
return value
|
|
}
|