package main
import (
"crypto/ed25519"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"strings"
)
func verifyWebhook(publicKeyStr, webhookId, webhookTimestamp, webhookSignature, rawBody string) (bool, error) {
// 1. Strip the whpk_ prefix and decode the public key
rawKey := strings.TrimPrefix(publicKeyStr, "whpk_")
keyBytes, err := base64.StdEncoding.DecodeString(rawKey)
if err != nil {
return false, fmt.Errorf("failed to decode public key: %w", err)
}
parsed, err := x509.ParsePKIXPublicKey(keyBytes)
if err != nil {
return false, fmt.Errorf("failed to parse public key: %w", err)
}
publicKey, ok := parsed.(ed25519.PublicKey)
if !ok {
return false, errors.New("key is not Ed25519")
}
// 2. Strip the v1a, prefix and decode the signature
if !strings.HasPrefix(webhookSignature, "v1a,") {
return false, errors.New("unsupported signature version")
}
sigBytes, err := base64.StdEncoding.DecodeString(webhookSignature[4:])
if err != nil {
return false, fmt.Errorf("failed to decode signature: %w", err)
}
// 3. Reconstruct the signed message
message := []byte(webhookId + "." + webhookTimestamp + "." + rawBody)
// 4. Verify
return ed25519.Verify(publicKey, message, sigBytes), nil
}