diff --git a/autopush/client.go b/autopush/client.go index abfdce6..82e8c0e 100644 --- a/autopush/client.go +++ b/autopush/client.go @@ -72,7 +72,6 @@ func (c *AutoPushClient) Unregister(channelID string) (UnregisterResponse, error } func (c *AutoPushClient) Decrypt( - curve ecdh.Curve, authSecret []byte, useragentPrivateKey *ecdh.PrivateKey, notification Notification, @@ -82,25 +81,16 @@ func (c *AutoPushClient) Decrypt( return nil, fmt.Errorf("base64 decode error: %v", err) } - payload, err := rfc8291.Unmarshal(data) - if err != nil { - return nil, fmt.Errorf("rfc8291 decode error: %v", err) - } - - appserverPublicKey, err := curve.NewPublicKey(payload.KeyId) - if err != nil { - return nil, fmt.Errorf("ecdh public key load error: %v", err) - } - plaintext, err := c.ece.Decrypt( - payload.CipherText, - payload.Salt, + data, + rfc8291.Encoding(notification.Headers.Encoding), + notification.Headers.Encryption, + notification.Headers.CryptoKey, authSecret, useragentPrivateKey, - appserverPublicKey, ) if err != nil { - return nil, fmt.Errorf("rfc8291 decrypt error: %v", err) + return nil, fmt.Errorf("decrypt error: %v", err) } var result webpush.WebPushPayload diff --git a/examples/ece/main.go b/examples/ece/main.go index 3401671..2a20ca4 100644 --- a/examples/ece/main.go +++ b/examples/ece/main.go @@ -19,7 +19,7 @@ func appserver(authSecret []byte, useragentPublicKey *ecdh.PublicKey) []byte { _, salt, privateKey := rfc8291.NewSecrets(CURVE) ece := rfc8291.NewRFC8291(HASH) - encrypted, err := ece.Encrypt( + encrypted, err := ece.EncryptAes128gcm( []byte(PLAINTEXT), salt, authSecret, @@ -53,7 +53,7 @@ func main() { log.Panicln("Load PublicKey Error", err) } - plaintext, err := ece.Decrypt( + plaintext, err := ece.DecryptAes128gcm( payload.CipherText, payload.Salt, authSecret, diff --git a/rfc8291/aes128gcm.go b/rfc8291/aes128gcm.go new file mode 100644 index 0000000..87ccee5 --- /dev/null +++ b/rfc8291/aes128gcm.go @@ -0,0 +1,154 @@ +package rfc8291 + +import ( + "bytes" + "crypto/ecdh" + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + + "golang.org/x/crypto/hkdf" +) + +// Aes128gcmScheme implements EncodingScheme for the aes128gcm encoding. +type Aes128gcmScheme struct{} + +func (s Aes128gcmScheme) DeriveIKM(hash func() hash.Hash, authSecret, ecdhSecret []byte, uaKey, asKey *ecdh.PublicKey) ([]byte, error) { + prkKey := hkdf.Extract(hash, ecdhSecret, authSecret) + + // aes128gcm: "WebPush: info\0" + receiver key + sender key + keyInfo := bytes.Join([][]byte{ + []byte("WebPush: info\000"), + uaKey.Bytes(), + asKey.Bytes(), + }, nil) + + ikm := make([]byte, HKDF_IKM_LEN) + if _, err := io.ReadFull(hkdf.Expand(hash, prkKey, keyInfo), ikm); err != nil { + return nil, fmt.Errorf("derive IKM failed: %v", err) + } + + return ikm, nil +} + +func (s Aes128gcmScheme) DeriveCEKAndNonce(hash func() hash.Hash, ikm, salt []byte, uaKey, asKey *ecdh.PublicKey) (cek, nonce []byte, err error) { + prk := hkdf.Extract(hash, ikm, salt) + + // aes128gcm: simple info strings without keys + cekInfo := []byte("Content-Encoding: aes128gcm\000") + nonceInfo := []byte("Content-Encoding: nonce\000") + + cek = make([]byte, HKDF_CEK_LEN) + if _, err := io.ReadFull(hkdf.Expand(hash, prk, cekInfo), cek); err != nil { + return nil, nil, fmt.Errorf("derive CEK failed: %v", err) + } + + nonce = make([]byte, HKDF_NONCE_LEN) + if _, err := io.ReadFull(hkdf.Expand(hash, prk, nonceInfo), nonce); err != nil { + return nil, nil, fmt.Errorf("derive nonce failed: %v", err) + } + + return cek, nonce, nil +} + +func (s Aes128gcmScheme) Pad(plaintext []byte) []byte { + // aes128gcm: append 0x02 delimiter for final record + return append(plaintext, 0x02) +} + +func (s Aes128gcmScheme) Unpad(data []byte) ([]byte, error) { + if len(data) == 0 { + return nil, fmt.Errorf("data is empty") + } + // aes128gcm: remove trailing 0x01 or 0x02 delimiter + last := data[len(data)-1] + if last == 0x01 || last == 0x02 { + return data[:len(data)-1], nil + } + return data, nil +} + +// Payload represents the aes128gcm message format with embedded crypto parameters. +type Payload struct { + RS uint32 + Salt []byte + KeyId []byte + CipherText []byte +} + +const ( + baseHeaderLen = 21 +) + +// Marshal serializes a Payload into the aes128gcm binary format. +func Marshal(p Payload) (data []byte) { + rs := make([]byte, 4) + binary.BigEndian.PutUint32(rs, p.RS) + return bytes.Join([][]byte{ + p.Salt, + rs, + {uint8(len(p.KeyId))}, + p.KeyId, + p.CipherText, + }, nil) +} + +// Unmarshal parses the aes128gcm binary format into a Payload. +func Unmarshal(data []byte) (p Payload, err error) { + if len(data) < baseHeaderLen { + return p, errors.New("data is too short") + } + + p.Salt = data[:16] + p.RS = binary.BigEndian.Uint32(data[16:20]) + + idlen := int(data[20]) + if len(data) < baseHeaderLen+idlen { + return p, errors.New("data is too short") + } + + if idlen > 0 { + p.KeyId = data[baseHeaderLen : baseHeaderLen+idlen] + } + + p.CipherText = data[baseHeaderLen+idlen:] + + return p, nil +} + +// EncryptAes128gcm encrypts a message using the aes128gcm encoding scheme. +// Returns the complete payload with embedded crypto parameters. +func (c *RFC8291) EncryptAes128gcm( + plaintext []byte, + salt []byte, + authSecret []byte, + receiverPublicKey *ecdh.PublicKey, + senderPrivateKey *ecdh.PrivateKey, +) ([]byte, error) { + ciphertext, err := c.encrypt(Aes128gcmScheme{}, plaintext, salt, authSecret, receiverPublicKey, senderPrivateKey) + if err != nil { + return nil, err + } + + rs := uint32(len(plaintext) + 1 + AES_GCM_OVERHEAD) + + return Marshal(Payload{ + RS: rs, + Salt: salt, + KeyId: senderPrivateKey.PublicKey().Bytes(), + CipherText: ciphertext, + }), nil +} + +// DecryptAes128gcm decrypts a message encrypted with the aes128gcm encoding scheme. +func (c *RFC8291) DecryptAes128gcm( + ciphertext []byte, + salt []byte, + authSecret []byte, + receiverPrivateKey *ecdh.PrivateKey, + senderPublicKey *ecdh.PublicKey, +) ([]byte, error) { + return c.decrypt(Aes128gcmScheme{}, ciphertext, salt, authSecret, receiverPrivateKey, senderPublicKey) +} diff --git a/rfc8291/aesgcm.go b/rfc8291/aesgcm.go new file mode 100644 index 0000000..d7a6f87 --- /dev/null +++ b/rfc8291/aesgcm.go @@ -0,0 +1,184 @@ +package rfc8291 + +import ( + "bytes" + "crypto/ecdh" + "encoding/base64" + "fmt" + "hash" + "io" + "strings" + + "golang.org/x/crypto/hkdf" +) + +// AesgcmScheme implements EncodingScheme for the aesgcm encoding. +type AesgcmScheme struct{} + +func (s AesgcmScheme) DeriveIKM(hash func() hash.Hash, authSecret, ecdhSecret []byte, uaKey, asKey *ecdh.PublicKey) ([]byte, error) { + prkKey := hkdf.Extract(hash, ecdhSecret, authSecret) + + // aesgcm: just "Content-Encoding: auth\0" without keys + keyInfo := []byte("Content-Encoding: auth\000") + + ikm := make([]byte, HKDF_IKM_LEN) + if _, err := io.ReadFull(hkdf.Expand(hash, prkKey, keyInfo), ikm); err != nil { + return nil, fmt.Errorf("derive IKM failed: %v", err) + } + + return ikm, nil +} + +func (s AesgcmScheme) DeriveCEKAndNonce(hash func() hash.Hash, ikm, salt []byte, uaKey, asKey *ecdh.PublicKey) (cek, nonce []byte, err error) { + prk := hkdf.Extract(hash, ikm, salt) + + // aesgcm: info includes "P-256\0" and length-prefixed public keys + uaKeyBytes := uaKey.Bytes() + asKeyBytes := asKey.Bytes() + + context := bytes.Join([][]byte{ + []byte("P-256\000"), + {0, byte(len(uaKeyBytes))}, + uaKeyBytes, + {0, byte(len(asKeyBytes))}, + asKeyBytes, + }, nil) + + cekInfo := append([]byte("Content-Encoding: aesgcm\000"), context...) + nonceInfo := append([]byte("Content-Encoding: nonce\000"), context...) + + cek = make([]byte, HKDF_CEK_LEN) + if _, err := io.ReadFull(hkdf.Expand(hash, prk, cekInfo), cek); err != nil { + return nil, nil, fmt.Errorf("derive CEK failed: %v", err) + } + + nonce = make([]byte, HKDF_NONCE_LEN) + if _, err := io.ReadFull(hkdf.Expand(hash, prk, nonceInfo), nonce); err != nil { + return nil, nil, fmt.Errorf("derive nonce failed: %v", err) + } + + return cek, nonce, nil +} + +func (s AesgcmScheme) Pad(plaintext []byte) []byte { + // aesgcm: 2-byte big-endian padding length prefix (0 padding) + result := make([]byte, 2+len(plaintext)) + // First two bytes are 0 (no padding), already zero-initialized + copy(result[2:], plaintext) + return result +} + +func (s AesgcmScheme) Unpad(data []byte) ([]byte, error) { + if len(data) < 2 { + return nil, fmt.Errorf("data too short for aesgcm padding") + } + + padLen := int(data[0])<<8 | int(data[1]) + + if 2+padLen > len(data) { + return nil, fmt.Errorf("invalid padding length: %d (data length: %d)", padLen, len(data)) + } + + // Verify padding bytes are all zeros + for i := 2; i < 2+padLen; i++ { + if data[i] != 0 { + return nil, fmt.Errorf("invalid padding: non-zero byte at position %d", i) + } + } + + return data[2+padLen:], nil +} + +// EncryptResult holds the result of aesgcm encryption. +// Unlike aes128gcm which embeds crypto params in the payload, +// aesgcm requires these to be sent as HTTP headers. +type EncryptResult struct { + Ciphertext []byte // The encrypted data (for request body) + Salt []byte // For Encryption header: salt= + SenderPublicKey []byte // For Crypto-Key header: dh= +} + +// CryptoParams holds the extracted cryptographic parameters for decryption. +type CryptoParams struct { + Salt []byte + SenderPublicKey *ecdh.PublicKey +} + +// ParseAesgcmHeaders extracts salt and sender public key from aesgcm HTTP headers. +// encryptionHeader: e.g., "salt=FiyMDLvlVl678odI9AWL3A" +// cryptoKeyHeader: e.g., "dh=BMLYo...;p256ecdsa=BF5o..." +func ParseAesgcmHeaders(encryptionHeader, cryptoKeyHeader string, curve ecdh.Curve) (*CryptoParams, error) { + salt, err := parseHeaderParam(encryptionHeader, "salt") + if err != nil { + return nil, fmt.Errorf("failed to parse salt: %v", err) + } + if len(salt) != SALT_LEN { + return nil, fmt.Errorf("salt must be %d bytes, got %d", SALT_LEN, len(salt)) + } + + dhBytes, err := parseHeaderParam(cryptoKeyHeader, "dh") + if err != nil { + return nil, fmt.Errorf("failed to parse dh: %v", err) + } + + senderPublicKey, err := curve.NewPublicKey(dhBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse sender public key: %v", err) + } + + return &CryptoParams{ + Salt: salt, + SenderPublicKey: senderPublicKey, + }, nil +} + +// parseHeaderParam extracts a base64url-encoded parameter value from a header string. +func parseHeaderParam(header, paramName string) ([]byte, error) { + parts := strings.FieldsFunc(header, func(r rune) bool { + return r == ';' || r == ',' + }) + + prefix := paramName + "=" + for _, part := range parts { + part = strings.TrimSpace(part) + if strings.HasPrefix(part, prefix) { + value := strings.TrimPrefix(part, prefix) + value = strings.Trim(value, "\"") + return base64.RawURLEncoding.DecodeString(value) + } + } + + return nil, fmt.Errorf("parameter %q not found in header", paramName) +} + +// EncryptAesgcm encrypts a message using the aesgcm encoding scheme. +// Returns the ciphertext and crypto parameters needed for HTTP headers. +func (c *RFC8291) EncryptAesgcm( + plaintext []byte, + salt []byte, + authSecret []byte, + receiverPublicKey *ecdh.PublicKey, + senderPrivateKey *ecdh.PrivateKey, +) (*EncryptResult, error) { + ciphertext, err := c.encrypt(AesgcmScheme{}, plaintext, salt, authSecret, receiverPublicKey, senderPrivateKey) + if err != nil { + return nil, err + } + + return &EncryptResult{ + Ciphertext: ciphertext, + Salt: salt, + SenderPublicKey: senderPrivateKey.PublicKey().Bytes(), + }, nil +} + +// DecryptAesgcm decrypts a message encrypted with the aesgcm encoding scheme. +func (c *RFC8291) DecryptAesgcm( + ciphertext []byte, + salt []byte, + authSecret []byte, + receiverPrivateKey *ecdh.PrivateKey, + senderPublicKey *ecdh.PublicKey, +) ([]byte, error) { + return c.decrypt(AesgcmScheme{}, ciphertext, salt, authSecret, receiverPrivateKey, senderPublicKey) +} diff --git a/rfc8291/payload.go b/rfc8291/payload.go deleted file mode 100644 index f5f3355..0000000 --- a/rfc8291/payload.go +++ /dev/null @@ -1,54 +0,0 @@ -package rfc8291 - -import ( - "bytes" - "encoding/binary" - "errors" -) - -const ( - BASE_HEADER_LEN = 21 -) - -type Payload struct { - RS uint32 - Salt []byte - KeyId []byte - CipherText []byte -} - -func Marshal(p Payload) (data []byte) { - rs := make([]byte, 4) - binary.BigEndian.PutUint32(rs, p.RS) - return bytes.Join([][]byte{ - p.Salt, - rs, - {uint8(len(p.KeyId))}, - p.KeyId, - p.CipherText, - }, []byte{}) -} - -func Unmarshal(data []byte) (p Payload, err error) { - err = errors.New("data is too short") - - if len(data) < BASE_HEADER_LEN { - return p, err - } - - p.Salt = data[:16] - p.RS = binary.BigEndian.Uint32(data[16:20]) - - idlen := int(data[20]) - if len(data) < BASE_HEADER_LEN+idlen { - return p, err - } - - if idlen > 0 { - p.KeyId = data[BASE_HEADER_LEN : BASE_HEADER_LEN+idlen] - } - - p.CipherText = data[BASE_HEADER_LEN+idlen:] - - return p, nil -} diff --git a/rfc8291/rfc8291.go b/rfc8291/rfc8291.go index 042cfdb..a6aec8a 100644 --- a/rfc8291/rfc8291.go +++ b/rfc8291/rfc8291.go @@ -1,7 +1,6 @@ package rfc8291 import ( - "bytes" "crypto/aes" "crypto/cipher" "crypto/ecdh" @@ -11,8 +10,6 @@ import ( "hash" "io" "log" - - "golang.org/x/crypto/hkdf" ) const ( @@ -26,11 +23,40 @@ const ( HKDF_NONCE_LEN = 12 ) +// Encoding represents the Content-Encoding type for WebPush messages. +type Encoding string + +const ( + EncodingAes128gcm Encoding = "aes128gcm" + EncodingAesgcm Encoding = "aesgcm" +) + +// EncodingScheme defines the encoding-specific operations for WebPush encryption. +type EncodingScheme interface { + DeriveIKM(hash func() hash.Hash, authSecret, ecdhSecret []byte, uaKey, asKey *ecdh.PublicKey) ([]byte, error) + DeriveCEKAndNonce(hash func() hash.Hash, ikm, salt []byte, uaKey, asKey *ecdh.PublicKey) (cek, nonce []byte, err error) + Pad(plaintext []byte) []byte + Unpad(data []byte) ([]byte, error) +} + +// Scheme returns the EncodingScheme implementation for the given encoding type. +func Scheme(encoding Encoding) (EncodingScheme, error) { + switch encoding { + case EncodingAes128gcm: + return Aes128gcmScheme{}, nil + case EncodingAesgcm: + return AesgcmScheme{}, nil + default: + return nil, fmt.Errorf("unsupported encoding: %s", encoding) + } +} + +// RFC8291 implements WebPush message encryption and decryption. type RFC8291 struct { hash func() hash.Hash } -// Default Hash is SHA256 +// NewRFC8291 creates a new RFC8291 instance. Default hash is SHA256. func NewRFC8291(hash func() hash.Hash) *RFC8291 { if hash == nil { hash = sha256.New @@ -38,6 +64,7 @@ func NewRFC8291(hash func() hash.Hash) *RFC8291 { return &RFC8291{hash: hash} } +// NewSecrets generates new random auth secret, salt, and ECDH private key. func NewSecrets(curve ecdh.Curve) (auth, salt []byte, key *ecdh.PrivateKey) { auth = make([]byte, AUTH_SECRET_LEN) salt = make([]byte, SALT_LEN) @@ -56,12 +83,14 @@ func NewSecrets(curve ecdh.Curve) (auth, salt []byte, key *ecdh.PrivateKey) { return auth, salt, key } -func (c *RFC8291) Encrypt( +// encrypt performs encryption using the specified encoding scheme. +func (c *RFC8291) encrypt( + scheme EncodingScheme, plaintext []byte, salt []byte, authSecret []byte, - useragentPublicKey *ecdh.PublicKey, - appserverPrivateKey *ecdh.PrivateKey, + receiverPublicKey *ecdh.PublicKey, + senderPrivateKey *ecdh.PrivateKey, ) ([]byte, error) { if len(authSecret) != AUTH_SECRET_LEN { return nil, fmt.Errorf("auth_secret must be %d bytes", AUTH_SECRET_LEN) @@ -70,17 +99,17 @@ func (c *RFC8291) Encrypt( return nil, fmt.Errorf("salt must be %d bytes", SALT_LEN) } - ecdhSecret, err := appserverPrivateKey.ECDH(useragentPublicKey) + ecdhSecret, err := senderPrivateKey.ECDH(receiverPublicKey) if err != nil { return nil, fmt.Errorf("calculate ecdh_secret failed: %v", err) } - ikm, err := c.ikm(authSecret, ecdhSecret, useragentPublicKey, appserverPrivateKey.PublicKey()) + ikm, err := scheme.DeriveIKM(c.hash, authSecret, ecdhSecret, receiverPublicKey, senderPrivateKey.PublicKey()) if err != nil { return nil, err } - cek, nonce, err := c.cekAndNonce(ikm, salt) + cek, nonce, err := scheme.DeriveCEKAndNonce(c.hash, ikm, salt, receiverPublicKey, senderPrivateKey.PublicKey()) if err != nil { return nil, err } @@ -90,27 +119,20 @@ func (c *RFC8291) Encrypt( return nil, err } - ciphertext := gcm.Seal(nil, nonce, plaintext, nil) - rs := uint32(len(plaintext) + 1 + AES_GCM_OVERHEAD) + paddedPlaintext := scheme.Pad(plaintext) + ciphertext := gcm.Seal(nil, nonce, paddedPlaintext, nil) - // RFC8188: 0x01 or 0x02 in tail of plaintext data - // RFC8291: The push message plaintext has the padding delimiter octet (0x02) appended to produce - // ciphertext = bytes.Join([][]byte{ciphertext, {0x02}}, []byte{}) - - return Marshal(Payload{ - RS: rs, - Salt: salt, - KeyId: appserverPrivateKey.PublicKey().Bytes(), - CipherText: ciphertext, - }), nil + return ciphertext, nil } -func (c *RFC8291) Decrypt( +// decrypt performs decryption using the specified encoding scheme. +func (c *RFC8291) decrypt( + scheme EncodingScheme, ciphertext []byte, salt []byte, authSecret []byte, - useragentPrivateKey *ecdh.PrivateKey, - appserverPublicKey *ecdh.PublicKey, + receiverPrivateKey *ecdh.PrivateKey, + senderPublicKey *ecdh.PublicKey, ) ([]byte, error) { if len(authSecret) != AUTH_SECRET_LEN { return nil, fmt.Errorf("auth_secret must be %d bytes", AUTH_SECRET_LEN) @@ -119,17 +141,17 @@ func (c *RFC8291) Decrypt( return nil, fmt.Errorf("salt must be %d bytes", SALT_LEN) } - ecdhSecret, err := useragentPrivateKey.ECDH(appserverPublicKey) + ecdhSecret, err := receiverPrivateKey.ECDH(senderPublicKey) if err != nil { return nil, fmt.Errorf("calculate ecdh_secret failed: %v", err) } - ikm, err := c.ikm(authSecret, ecdhSecret, useragentPrivateKey.PublicKey(), appserverPublicKey) + ikm, err := scheme.DeriveIKM(c.hash, authSecret, ecdhSecret, receiverPrivateKey.PublicKey(), senderPublicKey) if err != nil { return nil, err } - cek, nonce, err := c.cekAndNonce(ikm, salt) + cek, nonce, err := scheme.DeriveCEKAndNonce(c.hash, ikm, salt, receiverPrivateKey.PublicKey(), senderPublicKey) if err != nil { return nil, err } @@ -144,13 +166,7 @@ func (c *RFC8291) Decrypt( return nil, err } - // RFC8188: 0x01 or 0x02 in tail of plaintext data - // RFC8291: The push message plaintext has the padding delimiter octet (0x02) appended to produce - if plaintext[len(plaintext)-1] == 0x01 || plaintext[len(plaintext)-1] == 0x02 { - plaintext = plaintext[:len(plaintext)-1] - } - - return plaintext, err + return scheme.Unpad(plaintext) } func (c *RFC8291) gcm(cek []byte) (cipher.AEAD, error) { @@ -167,43 +183,42 @@ func (c *RFC8291) gcm(cek []byte) (cipher.AEAD, error) { return gcm, nil } -func (c *RFC8291) ikm( +// Decrypt decrypts a push notification, automatically selecting the correct +// encoding scheme based on the encoding parameter. +// +// For aes128gcm: crypto params are extracted from the data payload. +// For aesgcm: crypto params are extracted from the HTTP headers. +func (c *RFC8291) Decrypt( + data []byte, + encoding Encoding, + encryptionHeader string, + cryptoKeyHeader string, authSecret []byte, - ecdhSecret []byte, - useragentPublicKey *ecdh.PublicKey, - appserverPublicKey *ecdh.PublicKey, -) (ikm []byte, err error) { - prkKey := hkdf.Extract(c.hash, ecdhSecret, authSecret) + receiverPrivateKey *ecdh.PrivateKey, +) ([]byte, error) { + switch encoding { + case EncodingAes128gcm: + payload, err := Unmarshal(data) + if err != nil { + return nil, fmt.Errorf("unmarshal aes128gcm payload: %v", err) + } - keyInfo := bytes.Join([][]byte{ - []byte("WebPush: info\000"), - useragentPublicKey.Bytes(), - appserverPublicKey.Bytes(), - }, []byte{}) + senderPublicKey, err := receiverPrivateKey.Curve().NewPublicKey(payload.KeyId) + if err != nil { + return nil, fmt.Errorf("parse sender public key: %v", err) + } - ikm = make([]byte, HKDF_IKM_LEN) - if _, err := io.ReadFull(hkdf.Expand(c.hash, prkKey, keyInfo), ikm); err != nil { - return nil, fmt.Errorf("read IKM failed: %v", err) + return c.DecryptAes128gcm(payload.CipherText, payload.Salt, authSecret, receiverPrivateKey, senderPublicKey) + + case EncodingAesgcm: + params, err := ParseAesgcmHeaders(encryptionHeader, cryptoKeyHeader, receiverPrivateKey.Curve()) + if err != nil { + return nil, fmt.Errorf("parse aesgcm headers: %v", err) + } + + return c.DecryptAesgcm(data, params.Salt, authSecret, receiverPrivateKey, params.SenderPublicKey) + + default: + return nil, fmt.Errorf("unsupported encoding: %s", encoding) } - - return ikm, nil -} - -func (c *RFC8291) cekAndNonce(ikm []byte, salt []byte) (cek, nonce []byte, err error) { - prk := hkdf.Extract(c.hash, ikm, salt) - - cekInfo := []byte("Content-Encoding: aes128gcm\000") - nonceInfo := []byte("Content-Encoding: nonce\000") - - cek = make([]byte, HKDF_CEK_LEN) - if _, err := io.ReadFull(hkdf.Expand(c.hash, prk, cekInfo), cek); err != nil { - return nil, nil, fmt.Errorf("read CEK failed: %v", err) - } - - nonce = make([]byte, HKDF_NONCE_LEN) - if _, err := io.ReadFull(hkdf.Expand(c.hash, prk, nonceInfo), nonce); err != nil { - return nil, nil, fmt.Errorf("read Nonce failed: %v", err) - } - - return cek, nonce, nil } diff --git a/sites/nicopush/client.go b/sites/nicopush/client.go index 374c00c..0236efd 100644 --- a/sites/nicopush/client.go +++ b/sites/nicopush/client.go @@ -99,7 +99,6 @@ func (c *NicoPushClient) Register() (channelID string, err error) { func (c *NicoPushClient) Decrypt(data autopush.Notification) (*webpush.WebPushPayload, error) { return c.autoPushClient.Decrypt( - ecdh.P256(), c.authSecret, c.privateKey, data,