diff options
| -rw-r--r-- | internal/cli/serve.go | 2 | ||||
| -rw-r--r-- | internal/notify/vapid.go | 16 | ||||
| -rw-r--r-- | internal/notify/vapid_test.go | 21 |
3 files changed, 37 insertions, 2 deletions
diff --git a/internal/cli/serve.go b/internal/cli/serve.go index efac719..5677562 100644 --- a/internal/cli/serve.go +++ b/internal/cli/serve.go @@ -54,7 +54,7 @@ func serve(addr string) error { if cfg.VAPIDPublicKey == "" || cfg.VAPIDPrivateKey == "" { pub, _ := store.GetSetting("vapid_public_key") priv, _ := store.GetSetting("vapid_private_key") - if pub == "" || priv == "" { + if pub == "" || priv == "" || !notify.ValidateVAPIDPublicKey(pub) { pub, priv, err = notify.GenerateVAPIDKeys() if err != nil { return fmt.Errorf("generating VAPID keys: %w", err) diff --git a/internal/notify/vapid.go b/internal/notify/vapid.go index d93a090..684bf4d 100644 --- a/internal/notify/vapid.go +++ b/internal/notify/vapid.go @@ -1,6 +1,10 @@ package notify -import webpush "github.com/SherClockHolmes/webpush-go" +import ( + "encoding/base64" + + webpush "github.com/SherClockHolmes/webpush-go" +) // GenerateVAPIDKeys generates a VAPID key pair for web push notifications. // Returns the base64url-encoded public and private keys. @@ -9,3 +13,13 @@ func GenerateVAPIDKeys() (publicKey, privateKey string, err error) { privateKey, publicKey, err = webpush.GenerateVAPIDKeys() return } + +// ValidateVAPIDPublicKey reports whether key is a valid VAPID public key: +// a base64url-encoded 65-byte uncompressed P-256 point (starts with 0x04). +func ValidateVAPIDPublicKey(key string) bool { + b, err := base64.RawURLEncoding.DecodeString(key) + if err != nil { + return false + } + return len(b) == 65 && b[0] == 0x04 +} diff --git a/internal/notify/vapid_test.go b/internal/notify/vapid_test.go index 6157854..a45047d 100644 --- a/internal/notify/vapid_test.go +++ b/internal/notify/vapid_test.go @@ -5,6 +5,27 @@ import ( "testing" ) +// TestValidateVAPIDPublicKey verifies that ValidateVAPIDPublicKey accepts valid +// public keys and rejects private keys, empty strings, and invalid base64. +func TestValidateVAPIDPublicKey(t *testing.T) { + pub, priv, err := GenerateVAPIDKeys() + if err != nil { + t.Fatalf("GenerateVAPIDKeys: %v", err) + } + if !ValidateVAPIDPublicKey(pub) { + t.Error("valid public key should pass validation") + } + if ValidateVAPIDPublicKey(priv) { + t.Error("private key (32 bytes) should fail public key validation") + } + if ValidateVAPIDPublicKey("") { + t.Error("empty string should fail validation") + } + if ValidateVAPIDPublicKey("notbase64!!!") { + t.Error("invalid base64 should fail validation") + } +} + // TestGenerateVAPIDKeys_PublicKeyIs65Bytes verifies that the public key returned // by GenerateVAPIDKeys is a 65-byte uncompressed P256 EC point (base64url, no padding = 87 chars) // and the private key is 32 bytes (43 chars). Previously the return values were swapped. |
