api: implement fernet encryption of pagination tokens
This commit is contained in:
parent
b8c534cd0d
commit
d19a4348df
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -30,6 +30,10 @@
|
||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fernet/fernet-go",
|
||||
"Rev": "1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-sql-driver/mysql",
|
||||
"Comment": "v1.2-125-gd512f20",
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/coreos/clair/config"
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils"
|
||||
)
|
||||
@ -58,5 +59,6 @@ func HTTPHandler(handler Handler, ctx *RouteContext) httprouter.Handle {
|
||||
}
|
||||
|
||||
type RouteContext struct {
|
||||
Store database.Datastore
|
||||
Store database.Datastore
|
||||
Config *config.APIConfig
|
||||
}
|
||||
|
@ -15,13 +15,20 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
"github.com/fernet/fernet-go"
|
||||
)
|
||||
|
||||
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "v1")
|
||||
|
||||
type Error struct {
|
||||
Message string `json:"Layer`
|
||||
}
|
||||
@ -187,10 +194,9 @@ type Notification struct {
|
||||
NextPage string `json:"NextPage,omitempty"`
|
||||
Old *VulnerabilityWithLayers `json:"Old,omitempty"`
|
||||
New *VulnerabilityWithLayers `json:"New,omitempty"`
|
||||
Changed []string `json:"Changed,omitempty"`
|
||||
}
|
||||
|
||||
func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotification, limit int, page, nextPage database.VulnerabilityNotificationPageNumber) Notification {
|
||||
func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotification, limit int, page, nextPage database.VulnerabilityNotificationPageNumber, key string) Notification {
|
||||
var oldVuln *VulnerabilityWithLayers
|
||||
if dbNotification.OldVulnerability != nil {
|
||||
v := VulnerabilityWithLayersFromDatabaseModel(*dbNotification.OldVulnerability)
|
||||
@ -205,7 +211,7 @@ func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotifica
|
||||
|
||||
var nextPageStr string
|
||||
if nextPage != database.NoVulnerabilityNotificationPage {
|
||||
nextPageStr = DBPageNumberToString(nextPage)
|
||||
nextPageStr = pageNumberToToken(nextPage, key)
|
||||
}
|
||||
|
||||
var created, notified, deleted string
|
||||
@ -227,7 +233,7 @@ func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotifica
|
||||
Notified: notified,
|
||||
Deleted: deleted,
|
||||
Limit: limit,
|
||||
Page: DBPageNumberToString(page),
|
||||
Page: pageNumberToToken(page, key),
|
||||
NextPage: nextPageStr,
|
||||
Old: oldVuln,
|
||||
New: newVuln,
|
||||
@ -279,14 +285,30 @@ type FeatureEnvelope struct {
|
||||
Error *Error `json:"Error,omitempty"`
|
||||
}
|
||||
|
||||
func pageStringToDBPageNumber(pageStr string) (database.VulnerabilityNotificationPageNumber, error) {
|
||||
// TODO(jzelinskie): turn pagination into an encrypted token
|
||||
var old, new int
|
||||
_, err := fmt.Sscanf(pageStr, "%d-%d", &old, &new)
|
||||
return database.VulnerabilityNotificationPageNumber{old, new}, err
|
||||
func tokenToPageNumber(token, key string) (database.VulnerabilityNotificationPageNumber, error) {
|
||||
k, _ := fernet.DecodeKey(key)
|
||||
msg := fernet.VerifyAndDecrypt([]byte(token), time.Hour, []*fernet.Key{k})
|
||||
if msg == nil {
|
||||
return database.VulnerabilityNotificationPageNumber{}, errors.New("invalid or expired pagination token")
|
||||
}
|
||||
|
||||
page := database.VulnerabilityNotificationPageNumber{}
|
||||
err := json.NewDecoder(bytes.NewBuffer(msg)).Decode(&page)
|
||||
return page, err
|
||||
}
|
||||
|
||||
func DBPageNumberToString(page database.VulnerabilityNotificationPageNumber) string {
|
||||
// TODO(jzelinskie): turn pagination into an encrypted token
|
||||
return fmt.Sprintf("%d-%d", page.OldVulnerability, page.NewVulnerability)
|
||||
func pageNumberToToken(page database.VulnerabilityNotificationPageNumber, key string) string {
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(page)
|
||||
if err != nil {
|
||||
log.Fatal("failed to encode VulnerabilityNotificationPageNumber")
|
||||
}
|
||||
|
||||
k, _ := fernet.DecodeKey(key)
|
||||
tokenBytes, err := fernet.EncryptAndSign(buf.Bytes(), k)
|
||||
if err != nil {
|
||||
log.Fatal("failed to encrypt VulnerabilityNotificationpageNumber")
|
||||
}
|
||||
|
||||
return string(tokenBytes)
|
||||
}
|
||||
|
@ -321,7 +321,7 @@ func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params
|
||||
page := database.VulnerabilityNotificationFirstPage
|
||||
pageStrs, pageExists := query["page"]
|
||||
if pageExists {
|
||||
page, err = pageStringToDBPageNumber(pageStrs[0])
|
||||
page, err = tokenToPageNumber(pageStrs[0], ctx.Config.PaginationKey)
|
||||
if err != nil {
|
||||
writeResponse(w, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"invalid page format: " + err.Error()}})
|
||||
return getNotificationRoute, http.StatusBadRequest
|
||||
@ -337,7 +337,7 @@ func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params
|
||||
return getNotificationRoute, http.StatusInternalServerError
|
||||
}
|
||||
|
||||
notification := NotificationFromDatabaseModel(dbNotification, limit, page, nextPage)
|
||||
notification := NotificationFromDatabaseModel(dbNotification, limit, page, nextPage, ctx.Config.PaginationKey)
|
||||
|
||||
writeResponse(w, http.StatusOK, NotificationEnvelope{Notification: ¬ification})
|
||||
return getNotificationRoute, http.StatusOK
|
||||
|
4
clair.go
4
clair.go
@ -53,9 +53,9 @@ func Boot(config *config.Config) {
|
||||
|
||||
// Start API
|
||||
st.Begin()
|
||||
go api.Run(config.API, &context.RouteContext{db}, st)
|
||||
go api.Run(config.API, &context.RouteContext{db, config.API}, st)
|
||||
st.Begin()
|
||||
go api.RunHealth(config.API, &context.RouteContext{db}, st)
|
||||
go api.RunHealth(config.API, &context.RouteContext{db, config.API}, st)
|
||||
|
||||
// Start updater
|
||||
st.Begin()
|
||||
|
@ -13,6 +13,8 @@ api:
|
||||
healthport: 6061
|
||||
# Maximum time that API requests may take before they time-out with a HTTP 503 error.
|
||||
timeout: 900s
|
||||
# 32-bit key used to encrypt pagination tokens
|
||||
paginationKey: "2E9IgrgWLNb4gjuU0WbiBIudLH8xolz_qxFn--vxJP8="
|
||||
# Paths to certificates to secure the main API with TLS and client certificate auth.
|
||||
cafile:
|
||||
keyfile:
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fernet/fernet-go"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@ -44,9 +45,9 @@ type UpdaterConfig struct {
|
||||
|
||||
// NotifierConfig is the configuration for the Notifier service and its registered notifiers.
|
||||
type NotifierConfig struct {
|
||||
Attempts int
|
||||
RenotifyInterval time.Duration
|
||||
Params map[string]interface{} `yaml:",inline"`
|
||||
Attempts int
|
||||
RenotifyInterval time.Duration
|
||||
Params map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// APIConfig is the configuration for the API service.
|
||||
@ -54,6 +55,7 @@ type APIConfig struct {
|
||||
Port int
|
||||
HealthPort int
|
||||
Timeout time.Duration
|
||||
PaginationKey string
|
||||
CertFile, KeyFile, CAFile string
|
||||
}
|
||||
|
||||
@ -71,8 +73,8 @@ var DefaultConfig = Config{
|
||||
Timeout: 900 * time.Second,
|
||||
},
|
||||
Notifier: &NotifierConfig{
|
||||
Attempts: 5,
|
||||
RenotifyInterval: 2 * time.Hour,
|
||||
Attempts: 5,
|
||||
RenotifyInterval: 2 * time.Hour,
|
||||
},
|
||||
}
|
||||
|
||||
@ -96,5 +98,22 @@ func Load(path string) (config *Config, err error) {
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(d, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if config.API.PaginationKey == "" {
|
||||
var key fernet.Key
|
||||
if err = key.Generate(); err != nil {
|
||||
return
|
||||
}
|
||||
config.API.PaginationKey = key.Encode()
|
||||
} else {
|
||||
_, err = fernet.DecodeKey(config.API.PaginationKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
20
vendor/github.com/fernet/fernet-go/License
generated
vendored
Normal file
20
vendor/github.com/fernet/fernet-go/License
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright © 2013 Keith Rarick
|
||||
|
||||
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.
|
22
vendor/github.com/fernet/fernet-go/Readme
generated
vendored
Normal file
22
vendor/github.com/fernet/fernet-go/Readme
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Fernet takes a user-provided *message* (an arbitrary sequence of
|
||||
bytes), a *key* (256 bits), and the current time, and produces a
|
||||
*token*, which contains the message in a form that can't be read
|
||||
or altered without the key.
|
||||
|
||||
This package is compatible with the other implementations at
|
||||
https://github.com/fernet. They can exchange tokens freely among
|
||||
each other.
|
||||
|
||||
Documentation: http://godoc.org/github.com/fernet/fernet-go
|
||||
|
||||
|
||||
INSTALL
|
||||
|
||||
$ go get github.com/fernet/fernet-go
|
||||
|
||||
|
||||
For more information and background, see the Fernet spec at
|
||||
https://github.com/fernet/spec.
|
||||
|
||||
Fernet is distributed under the terms of the MIT license.
|
||||
See the License file for details.
|
19
vendor/github.com/fernet/fernet-go/cmd/fernet-keygen/main.go
generated
vendored
Normal file
19
vendor/github.com/fernet/fernet-go/cmd/fernet-keygen/main.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/fernet/fernet-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("fernet: ")
|
||||
|
||||
var key fernet.Key
|
||||
if err := key.Generate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(key.Encode())
|
||||
}
|
45
vendor/github.com/fernet/fernet-go/cmd/fernet-sign/main.go
generated
vendored
Normal file
45
vendor/github.com/fernet/fernet-go/cmd/fernet-sign/main.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/fernet/fernet-go"
|
||||
)
|
||||
|
||||
const Usage = `Usage: fernet-sign ENV
|
||||
|
||||
fernet-sign encrypts and signs its input and prints the resulting token.
|
||||
It uses the key in environment variable ENV.`
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("fernet: ")
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintln(os.Stderr, Usage)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
key, err := fernet.DecodeKey(os.Getenv(os.Args[1]))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
t, err := fernet.EncryptAndSign(b, key)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
_, err = os.Stdout.Write(append(t, '\n'))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
168
vendor/github.com/fernet/fernet-go/fernet.go
generated
vendored
Normal file
168
vendor/github.com/fernet/fernet-go/fernet.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
// Package fernet takes a user-provided message (an arbitrary
|
||||
// sequence of bytes), a key (256 bits), and the current time,
|
||||
// and produces a token, which contains the message in a form
|
||||
// that can't be read or altered without the key.
|
||||
//
|
||||
// For more information and background, see the Fernet spec
|
||||
// at https://github.com/fernet/spec.
|
||||
//
|
||||
// Subdirectories in this package provide command-line tools
|
||||
// for working with Fernet keys and tokens.
|
||||
package fernet
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
version byte = 0x80
|
||||
tsOffset = 1
|
||||
ivOffset = tsOffset + 8
|
||||
payOffset = ivOffset + aes.BlockSize
|
||||
overhead = 1 + 8 + aes.BlockSize + sha256.Size // ver + ts + iv + hmac
|
||||
maxClockSkew = 60 * time.Second
|
||||
)
|
||||
|
||||
var encoding = base64.URLEncoding
|
||||
|
||||
// generates a token from msg, writes it into tok, and returns the
|
||||
// number of bytes generated, which is encodedLen(msg).
|
||||
// len(tok) must be >= encodedLen(len(msg))
|
||||
func gen(tok, msg, iv []byte, ts time.Time, k *Key) int {
|
||||
tok[0] = version
|
||||
binary.BigEndian.PutUint64(tok[tsOffset:], uint64(ts.Unix()))
|
||||
copy(tok[ivOffset:], iv)
|
||||
p := tok[payOffset:]
|
||||
n := pad(p, msg, aes.BlockSize)
|
||||
bc, _ := aes.NewCipher(k.cryptBytes())
|
||||
cipher.NewCBCEncrypter(bc, iv).CryptBlocks(p[:n], p[:n])
|
||||
genhmac(p[n:n], tok[:payOffset+n], k.signBytes())
|
||||
return payOffset + n + sha256.Size
|
||||
}
|
||||
|
||||
// token length for input msg of length n, not including base64
|
||||
func encodedLen(n int) int {
|
||||
const k = aes.BlockSize
|
||||
return n/k*k + k + overhead
|
||||
}
|
||||
|
||||
// max msg length for tok of length n, for binary token (no base64)
|
||||
// upper bound; not exact
|
||||
func decodedLen(n int) int {
|
||||
return n - overhead
|
||||
}
|
||||
|
||||
// if msg is nil, decrypts in place and returns a slice of tok.
|
||||
func verify(msg, tok []byte, ttl time.Duration, now time.Time, k *Key) []byte {
|
||||
if len(tok) < 1 || tok[0] != version {
|
||||
return nil
|
||||
}
|
||||
ts := time.Unix(int64(binary.BigEndian.Uint64(tok[1:])), 0)
|
||||
if ttl >= 0 && (now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew))) {
|
||||
return nil
|
||||
}
|
||||
n := len(tok) - sha256.Size
|
||||
var hmac [sha256.Size]byte
|
||||
genhmac(hmac[:0], tok[:n], k.signBytes())
|
||||
if subtle.ConstantTimeCompare(tok[n:], hmac[:]) != 1 {
|
||||
return nil
|
||||
}
|
||||
pay := tok[payOffset : len(tok)-sha256.Size]
|
||||
if len(pay)%aes.BlockSize != 0 {
|
||||
return nil
|
||||
}
|
||||
if msg != nil {
|
||||
copy(msg, pay)
|
||||
pay = msg
|
||||
}
|
||||
bc, _ := aes.NewCipher(k.cryptBytes())
|
||||
iv := tok[9:][:aes.BlockSize]
|
||||
cipher.NewCBCDecrypter(bc, iv).CryptBlocks(pay, pay)
|
||||
return unpad(pay)
|
||||
}
|
||||
|
||||
// Pads p to a multiple of k using PKCS #7 standard block padding.
|
||||
// See http://tools.ietf.org/html/rfc5652#section-6.3.
|
||||
func pad(q, p []byte, k int) int {
|
||||
n := len(p)/k*k + k
|
||||
copy(q, p)
|
||||
c := byte(n - len(p))
|
||||
for i := len(p); i < n; i++ {
|
||||
q[i] = c
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Removes PKCS #7 standard block padding from p.
|
||||
// See http://tools.ietf.org/html/rfc5652#section-6.3.
|
||||
// This function is the inverse of pad.
|
||||
// If the padding is not well-formed, unpad returns nil.
|
||||
func unpad(p []byte) []byte {
|
||||
c := p[len(p)-1]
|
||||
for i := len(p) - int(c); i < len(p); i++ {
|
||||
if i < 0 || p[i] != c {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return p[:len(p)-int(c)]
|
||||
}
|
||||
|
||||
func b64enc(src []byte) []byte {
|
||||
dst := make([]byte, encoding.EncodedLen(len(src)))
|
||||
encoding.Encode(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func b64dec(src []byte) []byte {
|
||||
dst := make([]byte, encoding.DecodedLen(len(src)))
|
||||
n, err := encoding.Decode(dst, src)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return dst[:n]
|
||||
}
|
||||
|
||||
func genhmac(q, p, k []byte) {
|
||||
h := hmac.New(sha256.New, k)
|
||||
h.Write(p)
|
||||
h.Sum(q)
|
||||
}
|
||||
|
||||
// EncryptAndSign encrypts and signs msg with key k and returns the resulting
|
||||
// fernet token. If msg contains text, the text should be encoded
|
||||
// with UTF-8 to follow fernet convention.
|
||||
func EncryptAndSign(msg []byte, k *Key) (tok []byte, err error) {
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := make([]byte, encodedLen(len(msg)))
|
||||
n := gen(b, msg, iv, time.Now(), k)
|
||||
tok = make([]byte, encoding.EncodedLen(n))
|
||||
encoding.Encode(tok, b[:n])
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
// VerifyAndDecrypt verifies that tok is a valid fernet token that was signed
|
||||
// with a key in k at most ttl time ago only if ttl is greater than zero.
|
||||
// Returns the message contained in tok if tok is valid, otherwise nil.
|
||||
func VerifyAndDecrypt(tok []byte, ttl time.Duration, k []*Key) (msg []byte) {
|
||||
b := make([]byte, encoding.DecodedLen(len(tok)))
|
||||
n, _ := encoding.Decode(b, tok)
|
||||
for _, k1 := range k {
|
||||
msg = verify(nil, b[:n], ttl, time.Now(), k1)
|
||||
if msg != nil {
|
||||
return msg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
9
vendor/github.com/fernet/fernet-go/generate.json
generated
vendored
Normal file
9
vendor/github.com/fernet/fernet-go/generate.json
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"token": "gAAAAAAdwJ6wAAECAwQFBgcICQoLDA0ODy021cpGVWKZ_eEwCGM4BLLF_5CV9dOPmrhuVUPgJobwOz7JcbmrR64jVmpU4IwqDA==",
|
||||
"now": "1985-10-26T01:20:00-07:00",
|
||||
"iv": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
"src": "hello",
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
}
|
||||
]
|
58
vendor/github.com/fernet/fernet-go/invalid.json
generated
vendored
Normal file
58
vendor/github.com/fernet/fernet-go/invalid.json
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
[
|
||||
{
|
||||
"desc": "incorrect mac",
|
||||
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAl1-szkFVzXTuGb4hR8AKtwcaX1YdykQUFBQUFBQUFBQQ==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "too short",
|
||||
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPA==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "invalid base64",
|
||||
"token": "%%%%%%%%%%%%%AECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAl1-szkFVzXTuGb4hR8AKtwcaX1YdykRtfsH-p1YsUD2Q==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "payload size not multiple of block size",
|
||||
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPOm73QeoCk9uGib28Xe5vz6oxq5nmxbx_v7mrfyudzUm",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "payload padding error",
|
||||
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0ODz4LEpdELGQAad7aNEHbf-JkLPIpuiYRLQ3RtXatOYREu2FWke6CnJNYIbkuKNqOhw==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "far-future TS (unacceptable clock skew)",
|
||||
"token": "gAAAAAAdwStRAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAnja1xKYyhd-Y6mSkTOyTGJmw2Xc2a6kBd-iX9b_qXQcw==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "expired TTL",
|
||||
"token": "gAAAAAAdwJ6xAAECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAl1-szkFVzXTuGb4hR8AKtwcaX1YdykRtfsH-p1YsUD2Q==",
|
||||
"now": "1985-10-26T01:21:31-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"desc": "incorrect IV (causes padding error)",
|
||||
"token": "gAAAAAAdwJ6xBQECAwQFBgcICQoLDA0OD3HkMATM5lFqGaerZ-fWPAkLhFLHpGtDBRLRTZeUfWgHSv49TF2AUEZ1TIvcZjK1zQ==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
}
|
||||
]
|
91
vendor/github.com/fernet/fernet-go/key.go
generated
vendored
Normal file
91
vendor/github.com/fernet/fernet-go/key.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package fernet
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
errKeyLen = errors.New("fernet: key decodes to wrong size")
|
||||
errNoKeys = errors.New("fernet: no keys provided")
|
||||
)
|
||||
|
||||
// Key represents a key.
|
||||
type Key [32]byte
|
||||
|
||||
func (k *Key) cryptBytes() []byte {
|
||||
return k[len(k)/2:]
|
||||
}
|
||||
|
||||
func (k *Key) signBytes() []byte {
|
||||
return k[:len(k)/2]
|
||||
}
|
||||
|
||||
// Generate initializes k with pseudorandom data from package crypto/rand.
|
||||
func (k *Key) Generate() error {
|
||||
_, err := io.ReadFull(rand.Reader, k[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// Encode returns the URL-safe base64 encoding of k.
|
||||
func (k *Key) Encode() string {
|
||||
return encoding.EncodeToString(k[:])
|
||||
}
|
||||
|
||||
// DecodeKey decodes a key from s and returns it. The key can be in
|
||||
// hexadecimal, standard base64, or URL-safe base64.
|
||||
func DecodeKey(s string) (*Key, error) {
|
||||
var b []byte
|
||||
var err error
|
||||
if s == "" {
|
||||
return nil, errors.New("empty key")
|
||||
}
|
||||
if len(s) == hex.EncodedLen(len(Key{})) {
|
||||
b, err = hex.DecodeString(s)
|
||||
} else {
|
||||
b, err = base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
b, err = base64.URLEncoding.DecodeString(s)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) != len(Key{}) {
|
||||
return nil, errKeyLen
|
||||
}
|
||||
k := new(Key)
|
||||
copy(k[:], b)
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// DecodeKeys decodes each element of a using DecodeKey and returns the
|
||||
// resulting keys. Requires at least one key.
|
||||
func DecodeKeys(a ...string) ([]*Key, error) {
|
||||
if len(a) == 0 {
|
||||
return nil, errNoKeys
|
||||
}
|
||||
var err error
|
||||
ks := make([]*Key, len(a))
|
||||
for i, s := range a {
|
||||
ks[i], err = DecodeKey(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ks, nil
|
||||
}
|
||||
|
||||
// MustDecodeKeys is like DecodeKeys, but panics if an error occurs.
|
||||
// It simplifies safe initialization of global variables holding
|
||||
// keys.
|
||||
func MustDecodeKeys(a ...string) []*Key {
|
||||
k, err := DecodeKeys(a...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return k
|
||||
}
|
16
vendor/github.com/fernet/fernet-go/verify.json
generated
vendored
Normal file
16
vendor/github.com/fernet/fernet-go/verify.json
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
[
|
||||
{
|
||||
"token": "gAAAAAAdwJ6wAAECAwQFBgcICQoLDA0ODy021cpGVWKZ_eEwCGM4BLLF_5CV9dOPmrhuVUPgJobwOz7JcbmrR64jVmpU4IwqDA==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": 60,
|
||||
"src": "hello",
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
},
|
||||
{
|
||||
"token": "gAAAAAAdwJ6wAAECAwQFBgcICQoLDA0ODy021cpGVWKZ_eEwCGM4BLLF_5CV9dOPmrhuVUPgJobwOz7JcbmrR64jVmpU4IwqDA==",
|
||||
"now": "1985-10-26T01:20:01-07:00",
|
||||
"ttl_sec": -1,
|
||||
"src": "hello",
|
||||
"secret": "cw_0x689RpI-jtRR7oE8h_eQsKImvJapLeSbXpwF4e4="
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue
Block a user