mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-12-04 17:42:46 +00:00
Add webpush-go dependency
This commit is contained in:
parent
2d0dcff754
commit
2566523a94
1
go.mod
1
go.mod
|
@ -65,6 +65,7 @@ require (
|
|||
github.com/ncruces/go-sqlite3 v0.20.3
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/SherClockHolmes/webpush-go v1.3.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
|
|
13
go.sum
generated
13
go.sum
generated
|
@ -88,6 +88,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0
|
|||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k=
|
||||
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
|
@ -675,6 +677,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -712,6 +715,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -746,6 +750,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -765,6 +771,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -807,11 +814,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -821,6 +831,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -868,6 +880,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
4
vendor/github.com/SherClockHolmes/webpush-go/.gitignore
generated
vendored
Normal file
4
vendor/github.com/SherClockHolmes/webpush-go/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
vendor/**
|
||||
|
||||
.DS_Store
|
||||
*.out
|
21
vendor/github.com/SherClockHolmes/webpush-go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/SherClockHolmes/webpush-go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
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.
|
63
vendor/github.com/SherClockHolmes/webpush-go/README.md
generated
vendored
Normal file
63
vendor/github.com/SherClockHolmes/webpush-go/README.md
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
# webpush-go
|
||||
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/SherClockHolmes/webpush-go)](https://goreportcard.com/report/github.com/SherClockHolmes/webpush-go)
|
||||
[![GoDoc](https://godoc.org/github.com/SherClockHolmes/webpush-go?status.svg)](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)
|
26
vendor/github.com/SherClockHolmes/webpush-go/urgency.go
generated
vendored
Normal file
26
vendor/github.com/SherClockHolmes/webpush-go/urgency.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
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
|
||||
}
|
117
vendor/github.com/SherClockHolmes/webpush-go/vapid.go
generated
vendored
Normal file
117
vendor/github.com/SherClockHolmes/webpush-go/vapid.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
package webpush
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{
|
||||
"aud": fmt.Sprintf("%s://%s", subURL.Scheme, subURL.Host),
|
||||
"exp": expiration.Unix(),
|
||||
"sub": fmt.Sprintf("mailto:%s", 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 fmt.Sprintf(
|
||||
"vapid t=%s, k=%s",
|
||||
jwtString,
|
||||
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)
|
||||
}
|
287
vendor/github.com/SherClockHolmes/webpush-go/webpush.go
generated
vendored
Normal file
287
vendor/github.com/SherClockHolmes/webpush-go/webpush.go
generated
vendored
Normal file
|
@ -0,0 +1,287 @@
|
|||
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-Length", strconv.Itoa(len(ciphertext)))
|
||||
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
|
||||
}
|
95
vendor/golang.org/x/crypto/hkdf/hkdf.go
generated
vendored
Normal file
95
vendor/golang.org/x/crypto/hkdf/hkdf.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package hkdf implements the HMAC-based Extract-and-Expand Key Derivation
|
||||
// Function (HKDF) as defined in RFC 5869.
|
||||
//
|
||||
// HKDF is a cryptographic key derivation function (KDF) with the goal of
|
||||
// expanding limited input keying material into one or more cryptographically
|
||||
// strong secret keys.
|
||||
package hkdf
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Extract generates a pseudorandom key for use with Expand from an input secret
|
||||
// and an optional independent salt.
|
||||
//
|
||||
// Only use this function if you need to reuse the extracted key with multiple
|
||||
// Expand invocations and different context values. Most common scenarios,
|
||||
// including the generation of multiple keys, should use New instead.
|
||||
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
||||
if salt == nil {
|
||||
salt = make([]byte, hash().Size())
|
||||
}
|
||||
extractor := hmac.New(hash, salt)
|
||||
extractor.Write(secret)
|
||||
return extractor.Sum(nil)
|
||||
}
|
||||
|
||||
type hkdf struct {
|
||||
expander hash.Hash
|
||||
size int
|
||||
|
||||
info []byte
|
||||
counter byte
|
||||
|
||||
prev []byte
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (f *hkdf) Read(p []byte) (int, error) {
|
||||
// Check whether enough data can be generated
|
||||
need := len(p)
|
||||
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
||||
if remains < need {
|
||||
return 0, errors.New("hkdf: entropy limit reached")
|
||||
}
|
||||
// Read any leftover from the buffer
|
||||
n := copy(p, f.buf)
|
||||
p = p[n:]
|
||||
|
||||
// Fill the rest of the buffer
|
||||
for len(p) > 0 {
|
||||
if f.counter > 1 {
|
||||
f.expander.Reset()
|
||||
}
|
||||
f.expander.Write(f.prev)
|
||||
f.expander.Write(f.info)
|
||||
f.expander.Write([]byte{f.counter})
|
||||
f.prev = f.expander.Sum(f.prev[:0])
|
||||
f.counter++
|
||||
|
||||
// Copy the new batch into p
|
||||
f.buf = f.prev
|
||||
n = copy(p, f.buf)
|
||||
p = p[n:]
|
||||
}
|
||||
// Save leftovers for next run
|
||||
f.buf = f.buf[n:]
|
||||
|
||||
return need, nil
|
||||
}
|
||||
|
||||
// Expand returns a Reader, from which keys can be read, using the given
|
||||
// pseudorandom key and optional context info, skipping the extraction step.
|
||||
//
|
||||
// The pseudorandomKey should have been generated by Extract, or be a uniformly
|
||||
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
|
||||
// 3.3. Most common scenarios will want to use New instead.
|
||||
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
||||
expander := hmac.New(hash, pseudorandomKey)
|
||||
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
||||
}
|
||||
|
||||
// New returns a Reader, from which keys can be read, using the given hash,
|
||||
// secret, salt and context info. Salt and info can be nil.
|
||||
func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader {
|
||||
prk := Extract(hash, secret, salt)
|
||||
return Expand(hash, prk, info)
|
||||
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
|
@ -84,6 +84,9 @@ github.com/Masterminds/semver/v3
|
|||
# github.com/Masterminds/sprig/v3 v3.2.3
|
||||
## explicit; go 1.13
|
||||
github.com/Masterminds/sprig/v3
|
||||
# github.com/SherClockHolmes/webpush-go v1.3.0
|
||||
## explicit; go 1.13
|
||||
github.com/SherClockHolmes/webpush-go
|
||||
# github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
## explicit; go 1.13
|
||||
github.com/asaskevich/govalidator
|
||||
|
@ -1081,6 +1084,7 @@ golang.org/x/crypto/blowfish
|
|||
golang.org/x/crypto/chacha20
|
||||
golang.org/x/crypto/curve25519
|
||||
golang.org/x/crypto/ed25519
|
||||
golang.org/x/crypto/hkdf
|
||||
golang.org/x/crypto/internal/alias
|
||||
golang.org/x/crypto/internal/poly1305
|
||||
golang.org/x/crypto/pbkdf2
|
||||
|
|
Loading…
Reference in a new issue