Files

53 lines
1.4 KiB
Go
Raw Permalink Normal View History

2026-08-26 06:41:07 +08:00
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
}