package main import ( "crypto/ecdh" "testing" ) // The worked example from RFC 8291 §5. Reproducing it exactly pins every step // of the derivation — the ECDH, both HKDF extractions, the record padding and // the RFC 8188 header layout — against the spec rather than against ourselves. func TestEncryptPayloadRFC8291Vector(t *testing.T) { const ( plaintext = "When I grow up, I want to be a watermelon" authSecret = "BTBZMqHH6r4Tts7J_aSIgg" receiverPub = "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4" senderPriv = "yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw" saltB64 = "DGv6ra1nlYgDCS1FRnbzlw" wantCiphered = "DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27ml" + "mlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPT" + "pK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN" ) var sub Subscription sub.Endpoint = "https://push.example.net/push/JzLQ3raZJfFBR0aqvOMsLrt54w4rJUsV" sub.Keys.P256dh = receiverPub sub.Keys.Auth = authSecret salt, err := b64.DecodeString(saltB64) if err != nil { t.Fatalf("decode salt: %v", err) } seed, err := b64.DecodeString(senderPriv) if err != nil { t.Fatalf("decode sender key: %v", err) } eph, err := ecdh.P256().NewPrivateKey(seed) if err != nil { t.Fatalf("sender key: %v", err) } got, err := encryptPayload(sub, []byte(plaintext), salt, eph) if err != nil { t.Fatalf("encryptPayload: %v", err) } if b64.EncodeToString(got) != wantCiphered { t.Errorf("ciphertext mismatch\n got: %s\nwant: %s", b64.EncodeToString(got), wantCiphered) } } // A VAPID key must survive the round trip through its on-disk form, since the // public half is pinned by every subscription made while it was in use. func TestVAPIDKeyRoundTrip(t *testing.T) { key, err := newVAPIDKey() if err != nil { t.Fatalf("generate: %v", err) } raw, err := key.marshal() if err != nil { t.Fatalf("marshal: %v", err) } back, err := parseVAPIDKey(raw) if err != nil { t.Fatalf("parse: %v", err) } if back.Public != key.Public { t.Errorf("public key changed across round trip: %s != %s", back.Public, key.Public) } if _, err := back.authHeader("https://push.example.net/push/abc", vapidSubject); err != nil { t.Errorf("authHeader: %v", err) } }