diff options
Diffstat (limited to 'vendor/github.com/SherClockHolmes/webpush-go')
6 files changed, 0 insertions, 518 deletions
diff --git a/vendor/github.com/SherClockHolmes/webpush-go/.gitignore b/vendor/github.com/SherClockHolmes/webpush-go/.gitignore deleted file mode 100644 index 13b7c32ac..000000000 --- a/vendor/github.com/SherClockHolmes/webpush-go/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -vendor/** - -.DS_Store -*.out diff --git a/vendor/github.com/SherClockHolmes/webpush-go/LICENSE b/vendor/github.com/SherClockHolmes/webpush-go/LICENSE deleted file mode 100644 index 161eac777..000000000 --- a/vendor/github.com/SherClockHolmes/webpush-go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Ethan Holmes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/SherClockHolmes/webpush-go/README.md b/vendor/github.com/SherClockHolmes/webpush-go/README.md deleted file mode 100644 index c313fc6b1..000000000 --- a/vendor/github.com/SherClockHolmes/webpush-go/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# webpush-go - -[](https://goreportcard.com/report/github.com/SherClockHolmes/webpush-go) -[](https://godoc.org/github.com/SherClockHolmes/webpush-go) - -Web Push API Encryption with VAPID support. - -```bash -go get -u github.com/SherClockHolmes/webpush-go -``` - -## Example - -For a full example, refer to the code in the [example](example/) directory. - -```go -package main - -import ( - "encoding/json" - - webpush "github.com/SherClockHolmes/webpush-go" -) - -func main() { - // Decode subscription - s := &webpush.Subscription{} - json.Unmarshal([]byte("<YOUR_SUBSCRIPTION>"), s) - - // Send Notification - resp, err := webpush.SendNotification([]byte("Test"), s, &webpush.Options{ - Subscriber: "example@example.com", - VAPIDPublicKey: "<YOUR_VAPID_PUBLIC_KEY>", - VAPIDPrivateKey: "<YOUR_VAPID_PRIVATE_KEY>", - TTL: 30, - }) - if err != nil { - // TODO: Handle error - } - defer resp.Body.Close() -} -``` - -### Generating VAPID Keys - -Use the helper method `GenerateVAPIDKeys` to generate the VAPID key pair. - -```golang -privateKey, publicKey, err := webpush.GenerateVAPIDKeys() -if err != nil { - // TODO: Handle error -} -``` - -## Development - -1. Install [Go 1.11+](https://golang.org/) -2. `go mod vendor` -3. `go test` - -#### For other language implementations visit: - -[WebPush Libs](https://github.com/web-push-libs) diff --git a/vendor/github.com/SherClockHolmes/webpush-go/urgency.go b/vendor/github.com/SherClockHolmes/webpush-go/urgency.go deleted file mode 100644 index 97c4a32b4..000000000 --- a/vendor/github.com/SherClockHolmes/webpush-go/urgency.go +++ /dev/null @@ -1,26 +0,0 @@ -package webpush - -// Urgency indicates to the push service how important a message is to the user. -// This can be used by the push service to help conserve the battery life of a user's device -// by only waking up for important messages when battery is low. -type Urgency string - -const ( - // UrgencyVeryLow requires device state: on power and Wi-Fi - UrgencyVeryLow Urgency = "very-low" - // UrgencyLow requires device state: on either power or Wi-Fi - UrgencyLow Urgency = "low" - // UrgencyNormal excludes device state: low battery - UrgencyNormal Urgency = "normal" - // UrgencyHigh admits device state: low battery - UrgencyHigh Urgency = "high" -) - -// Checking allowable values for the urgency header -func isValidUrgency(urgency Urgency) bool { - switch urgency { - case UrgencyVeryLow, UrgencyLow, UrgencyNormal, UrgencyHigh: - return true - } - return false -} diff --git a/vendor/github.com/SherClockHolmes/webpush-go/vapid.go b/vendor/github.com/SherClockHolmes/webpush-go/vapid.go deleted file mode 100644 index d1c2a9154..000000000 --- a/vendor/github.com/SherClockHolmes/webpush-go/vapid.go +++ /dev/null @@ -1,118 +0,0 @@ -package webpush - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "encoding/base64" - "math/big" - "net/url" - "strings" - "time" - - "github.com/golang-jwt/jwt/v5" -) - -// GenerateVAPIDKeys will create a private and public VAPID key pair -func GenerateVAPIDKeys() (privateKey, publicKey string, err error) { - // Get the private key from the P256 curve - curve := elliptic.P256() - - private, x, y, err := elliptic.GenerateKey(curve, rand.Reader) - if err != nil { - return - } - - public := elliptic.Marshal(curve, x, y) - - // Convert to base64 - publicKey = base64.RawURLEncoding.EncodeToString(public) - privateKey = base64.RawURLEncoding.EncodeToString(private) - - return -} - -// Generates the ECDSA public and private keys for the JWT encryption -func generateVAPIDHeaderKeys(privateKey []byte) *ecdsa.PrivateKey { - // Public key - curve := elliptic.P256() - px, py := curve.ScalarMult( - curve.Params().Gx, - curve.Params().Gy, - privateKey, - ) - - pubKey := ecdsa.PublicKey{ - Curve: curve, - X: px, - Y: py, - } - - // Private key - d := &big.Int{} - d.SetBytes(privateKey) - - return &ecdsa.PrivateKey{ - PublicKey: pubKey, - D: d, - } -} - -// getVAPIDAuthorizationHeader -func getVAPIDAuthorizationHeader( - endpoint, - subscriber, - vapidPublicKey, - vapidPrivateKey string, - expiration time.Time, -) (string, error) { - // Create the JWT token - subURL, err := url.Parse(endpoint) - if err != nil { - return "", err - } - - // Unless subscriber is an HTTPS URL, assume an e-mail address - if !strings.HasPrefix(subscriber, "https:") { - subscriber = "mailto:" + subscriber - } - - token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{ - "aud": subURL.Scheme + "://" + subURL.Host, - "exp": time.Now().Add(time.Hour * 12).Unix(), - "sub": subscriber, - }) - - // Decode the VAPID private key - decodedVapidPrivateKey, err := decodeVapidKey(vapidPrivateKey) - if err != nil { - return "", err - } - - privKey := generateVAPIDHeaderKeys(decodedVapidPrivateKey) - - // Sign token with private key - jwtString, err := token.SignedString(privKey) - if err != nil { - return "", err - } - - // Decode the VAPID public key - pubKey, err := decodeVapidKey(vapidPublicKey) - if err != nil { - return "", err - } - - return "vapid t=" + jwtString + ", k=" + base64.RawURLEncoding.EncodeToString(pubKey), nil -} - -// Need to decode the vapid private key in multiple base64 formats -// Solution from: https://github.com/SherClockHolmes/webpush-go/issues/29 -func decodeVapidKey(key string) ([]byte, error) { - bytes, err := base64.URLEncoding.DecodeString(key) - if err == nil { - return bytes, nil - } - - return base64.RawURLEncoding.DecodeString(key) -} diff --git a/vendor/github.com/SherClockHolmes/webpush-go/webpush.go b/vendor/github.com/SherClockHolmes/webpush-go/webpush.go deleted file mode 100644 index a6ee7cd0c..000000000 --- a/vendor/github.com/SherClockHolmes/webpush-go/webpush.go +++ /dev/null @@ -1,286 +0,0 @@ -package webpush - -import ( - "bytes" - "context" - "crypto/aes" - "crypto/cipher" - "crypto/elliptic" - "crypto/rand" - "crypto/sha256" - "encoding/base64" - "encoding/binary" - "errors" - "io" - "net/http" - "strconv" - "strings" - "time" - - "golang.org/x/crypto/hkdf" -) - -const MaxRecordSize uint32 = 4096 - -var ErrMaxPadExceeded = errors.New("payload has exceeded the maximum length") - -// saltFunc generates a salt of 16 bytes -var saltFunc = func() ([]byte, error) { - salt := make([]byte, 16) - _, err := io.ReadFull(rand.Reader, salt) - if err != nil { - return salt, err - } - - return salt, nil -} - -// HTTPClient is an interface for sending the notification HTTP request / testing -type HTTPClient interface { - Do(*http.Request) (*http.Response, error) -} - -// Options are config and extra params needed to send a notification -type Options struct { - HTTPClient HTTPClient // Will replace with *http.Client by default if not included - RecordSize uint32 // Limit the record size - Subscriber string // Sub in VAPID JWT token - Topic string // Set the Topic header to collapse a pending messages (Optional) - TTL int // Set the TTL on the endpoint POST request - Urgency Urgency // Set the Urgency header to change a message priority (Optional) - VAPIDPublicKey string // VAPID public key, passed in VAPID Authorization header - VAPIDPrivateKey string // VAPID private key, used to sign VAPID JWT token - VapidExpiration time.Time // optional expiration for VAPID JWT token (defaults to now + 12 hours) -} - -// Keys are the base64 encoded values from PushSubscription.getKey() -type Keys struct { - Auth string `json:"auth"` - P256dh string `json:"p256dh"` -} - -// Subscription represents a PushSubscription object from the Push API -type Subscription struct { - Endpoint string `json:"endpoint"` - Keys Keys `json:"keys"` -} - -// SendNotification calls SendNotificationWithContext with default context for backwards-compatibility -func SendNotification(message []byte, s *Subscription, options *Options) (*http.Response, error) { - return SendNotificationWithContext(context.Background(), message, s, options) -} - -// SendNotificationWithContext sends a push notification to a subscription's endpoint -// Message Encryption for Web Push, and VAPID protocols. -// FOR MORE INFORMATION SEE RFC8291: https://datatracker.ietf.org/doc/rfc8291 -func SendNotificationWithContext(ctx context.Context, message []byte, s *Subscription, options *Options) (*http.Response, error) { - // Authentication secret (auth_secret) - authSecret, err := decodeSubscriptionKey(s.Keys.Auth) - if err != nil { - return nil, err - } - - // dh (Diffie Hellman) - dh, err := decodeSubscriptionKey(s.Keys.P256dh) - if err != nil { - return nil, err - } - - // Generate 16 byte salt - salt, err := saltFunc() - if err != nil { - return nil, err - } - - // Create the ecdh_secret shared key pair - curve := elliptic.P256() - - // Application server key pairs (single use) - localPrivateKey, x, y, err := elliptic.GenerateKey(curve, rand.Reader) - if err != nil { - return nil, err - } - - localPublicKey := elliptic.Marshal(curve, x, y) - - // Combine application keys with receiver's EC public key - sharedX, sharedY := elliptic.Unmarshal(curve, dh) - if sharedX == nil { - return nil, errors.New("Unmarshal Error: Public key is not a valid point on the curve") - } - - // Derive ECDH shared secret - sx, sy := curve.ScalarMult(sharedX, sharedY, localPrivateKey) - if !curve.IsOnCurve(sx, sy) { - return nil, errors.New("Encryption error: ECDH shared secret isn't on curve") - } - mlen := curve.Params().BitSize / 8 - sharedECDHSecret := make([]byte, mlen) - sx.FillBytes(sharedECDHSecret) - - hash := sha256.New - - // ikm - prkInfoBuf := bytes.NewBuffer([]byte("WebPush: info\x00")) - prkInfoBuf.Write(dh) - prkInfoBuf.Write(localPublicKey) - - prkHKDF := hkdf.New(hash, sharedECDHSecret, authSecret, prkInfoBuf.Bytes()) - ikm, err := getHKDFKey(prkHKDF, 32) - if err != nil { - return nil, err - } - - // Derive Content Encryption Key - contentEncryptionKeyInfo := []byte("Content-Encoding: aes128gcm\x00") - contentHKDF := hkdf.New(hash, ikm, salt, contentEncryptionKeyInfo) - contentEncryptionKey, err := getHKDFKey(contentHKDF, 16) - if err != nil { - return nil, err - } - - // Derive the Nonce - nonceInfo := []byte("Content-Encoding: nonce\x00") - nonceHKDF := hkdf.New(hash, ikm, salt, nonceInfo) - nonce, err := getHKDFKey(nonceHKDF, 12) - if err != nil { - return nil, err - } - - // Cipher - c, err := aes.NewCipher(contentEncryptionKey) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - - // Get the record size - recordSize := options.RecordSize - if recordSize == 0 { - recordSize = MaxRecordSize - } - - recordLength := int(recordSize) - 16 - - // Encryption Content-Coding Header - recordBuf := bytes.NewBuffer(salt) - - rs := make([]byte, 4) - binary.BigEndian.PutUint32(rs, recordSize) - - recordBuf.Write(rs) - recordBuf.Write([]byte{byte(len(localPublicKey))}) - recordBuf.Write(localPublicKey) - - // Data - dataBuf := bytes.NewBuffer(message) - - // Pad content to max record size - 16 - header - // Padding ending delimeter - dataBuf.Write([]byte("\x02")) - if err := pad(dataBuf, recordLength-recordBuf.Len()); err != nil { - return nil, err - } - - // Compose the ciphertext - ciphertext := gcm.Seal([]byte{}, nonce, dataBuf.Bytes(), nil) - recordBuf.Write(ciphertext) - - // POST request - req, err := http.NewRequest("POST", s.Endpoint, recordBuf) - if err != nil { - return nil, err - } - - if ctx != nil { - req = req.WithContext(ctx) - } - - req.Header.Set("Content-Encoding", "aes128gcm") - req.Header.Set("Content-Type", "application/octet-stream") - req.Header.Set("TTL", strconv.Itoa(options.TTL)) - - // Сheck the optional headers - if len(options.Topic) > 0 { - req.Header.Set("Topic", options.Topic) - } - - if isValidUrgency(options.Urgency) { - req.Header.Set("Urgency", string(options.Urgency)) - } - - expiration := options.VapidExpiration - if expiration.IsZero() { - expiration = time.Now().Add(time.Hour * 12) - } - - // Get VAPID Authorization header - vapidAuthHeader, err := getVAPIDAuthorizationHeader( - s.Endpoint, - options.Subscriber, - options.VAPIDPublicKey, - options.VAPIDPrivateKey, - expiration, - ) - if err != nil { - return nil, err - } - - req.Header.Set("Authorization", vapidAuthHeader) - - // Send the request - var client HTTPClient - if options.HTTPClient != nil { - client = options.HTTPClient - } else { - client = &http.Client{} - } - - return client.Do(req) -} - -// decodeSubscriptionKey decodes a base64 subscription key. -// if necessary, add "=" padding to the key for URL decode -func decodeSubscriptionKey(key string) ([]byte, error) { - // "=" padding - buf := bytes.NewBufferString(key) - if rem := len(key) % 4; rem != 0 { - buf.WriteString(strings.Repeat("=", 4-rem)) - } - - bytes, err := base64.StdEncoding.DecodeString(buf.String()) - if err == nil { - return bytes, nil - } - - return base64.URLEncoding.DecodeString(buf.String()) -} - -// Returns a key of length "length" given an hkdf function -func getHKDFKey(hkdf io.Reader, length int) ([]byte, error) { - key := make([]byte, length) - n, err := io.ReadFull(hkdf, key) - if n != len(key) || err != nil { - return key, err - } - - return key, nil -} - -func pad(payload *bytes.Buffer, maxPadLen int) error { - payloadLen := payload.Len() - if payloadLen > maxPadLen { - return ErrMaxPadExceeded - } - - padLen := maxPadLen - payloadLen - - padding := make([]byte, padLen) - payload.Write(padding) - - return nil -} |