Browse Source

添加jwt,jwe,mwt等token生成

dcsunny 4 years ago
parent
commit
23c3e856bd
6 changed files with 401 additions and 1 deletions
  1. 123 0
      common/jwe.go
  2. 125 0
      common/jwt.go
  3. 85 0
      common/mwt.go
  4. 1 1
      common/reply.go
  5. 5 0
      go.mod
  6. 62 0
      go.sum

+ 123 - 0
common/jwe.go

@@ -0,0 +1,123 @@
+package common
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/json"
+	"encoding/pem"
+	"strings"
+	"time"
+
+	"github.com/dcsunny/gocrypt"
+	"github.com/lestrrat-go/jwx/jwa"
+	"github.com/lestrrat-go/jwx/jwe"
+)
+
+type JWE struct {
+	publicKey  *rsa.PublicKey
+	privateKey *rsa.PrivateKey
+	expire     int64
+}
+
+var (
+	DefaultJWE *JWE
+)
+
+const (
+	jweHeader = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0"
+)
+
+//返回值 第一个private key,第二个是public key
+func GenerateRsaKey() ([]byte, []byte, error) {
+	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return nil, nil, err
+	}
+	pk, err := x509.MarshalPKCS8PrivateKey(privateKey)
+	if err != nil {
+		return nil, nil, err
+	}
+	block := &pem.Block{
+		Type:  "RSA PRIVATE KEY",
+		Bytes: pk,
+	}
+	_pk := pem.EncodeToMemory(block)
+	pb, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
+	if err != nil {
+		return nil, nil, err
+	}
+	publicBlock := &pem.Block{Type: "RSA Public Key", Bytes: pb}
+	_pb := pem.EncodeToMemory(publicBlock)
+	return _pk, _pb, nil
+}
+
+func InitJWE(privateKey, publicKey string, expire int64) error {
+	var err error
+	DefaultJWE, err = NewJWE(privateKey, publicKey, expire)
+	return err
+}
+
+func NewJWE(privateKey string, publicKey string, expire int64) (*JWE, error) {
+	privateKey = strings.Replace(privateKey, "-----BEGIN RSA PRIVATE KEY-----", "", 1)
+	privateKey = strings.Replace(privateKey, "-----END RSA PRIVATE KEY-----", "", 1)
+	privateKey = strings.Replace(privateKey, "\n", "", -1)
+	privateKeyDecoded, err := gocrypt.DecodeString(privateKey, gocrypt.Base64)
+	pk, err := gocrypt.ParsePrivateKey(privateKeyDecoded, gocrypt.PKCS8)
+	if err != nil {
+		return nil, err
+	}
+	publicKey = strings.Replace(publicKey, "-----BEGIN RSA Public Key-----", "", 1)
+	publicKey = strings.Replace(publicKey, "-----END RSA Public Key-----", "", 1)
+	publicKey = strings.Replace(publicKey, "\n", "", -1)
+	publicKeyDecoded, err := gocrypt.DecodeString(publicKey, gocrypt.Base64)
+	pb, err := x509.ParsePKIXPublicKey(publicKeyDecoded)
+	if err != nil {
+		return nil, err
+	}
+	var j = new(JWE)
+	j.publicKey = pb.(*rsa.PublicKey)
+	j.privateKey = pk
+	j.expire = expire
+	return j, nil
+}
+
+type JWEClaims struct {
+	AccountId string          `json:"id"`
+	Expire    int64           `json:"exp"`
+	Claims    json.RawMessage `json:"claims,omitempty"`
+}
+
+func (j *JWE) NewToken(accountID string, claims json.RawMessage) (string, error) {
+	jweClaim := JWEClaims{
+		AccountId: accountID,
+		Claims:    claims,
+		Expire:    time.Now().Unix() + j.expire,
+	}
+
+	payload, _ := json.Marshal(jweClaim)
+	token, err := jwe.Encrypt(payload, jwa.RSA1_5, j.publicKey, jwa.A128CBC_HS256, jwa.NoCompress)
+	if err != nil {
+		return "", err
+	}
+	_token := string(token)
+	_token = _token[52:]
+	return _token, err
+}
+
+func (j *JWE) Parse(token string) (string, json.RawMessage, error) {
+	token = jweHeader + "." + token
+	decrypted, err := jwe.Decrypt([]byte(token), jwa.RSA1_5, j.privateKey)
+	if err != nil {
+		return "", nil, err
+	}
+	var jweClaim JWEClaims
+	err = json.Unmarshal(decrypted, &jweClaim)
+	if err != nil {
+		return "", nil, err
+	}
+	if time.Now().Unix() > jweClaim.Expire {
+		return "", nil, ErrTokenExpired
+	}
+	return jweClaim.AccountId, jweClaim.Claims, nil
+}

+ 125 - 0
common/jwt.go

@@ -0,0 +1,125 @@
+package common
+
+import (
+	"encoding/json"
+	"errors"
+	"time"
+
+	jwtgo "github.com/dgrijalva/jwt-go"
+)
+
+// secret:加密秘钥
+// expire:过期时间,单位为秒
+type JWT struct {
+	secret []byte
+	expire int64
+}
+
+type CustomClaims struct {
+	AccountId string          `json:"id"`
+	Claims    json.RawMessage `json:"claims,omitempty"`
+	jwtgo.StandardClaims
+}
+
+var (
+	DefaultJWT      *JWT
+	ErrTokenExpired = errors.New("token expired")
+	ErrTokenInvalid = errors.New("token invalid")
+)
+
+const (
+	jwtHeader = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
+)
+
+func InitJWT(secret string, expire int64) {
+	DefaultJWT = NewJWT(secret, expire)
+}
+
+func NewJWT(secret string, expire int64) *JWT {
+	var j = new(JWT)
+	j.secret = []byte(secret)
+	j.expire = expire
+	return j
+}
+
+func (j *JWT) NewToken(accountID string, claimMap json.RawMessage) (token string, tokenExpire int64, err error) {
+	expireAt := time.Now().Add(time.Duration(j.expire) * time.Second).Unix()
+	claims := CustomClaims{
+		AccountId: accountID,
+		Claims:    claimMap,
+		StandardClaims: jwtgo.StandardClaims{
+			ExpiresAt: expireAt,
+			//Id:        utils.GetUUIDNoDash(),
+		},
+	}
+	t := jwtgo.NewWithClaims(jwtgo.SigningMethodHS256, claims)
+	token, err = t.SignedString(j.secret)
+	if err != nil {
+		return
+	}
+	token = token[37:]
+	tokenExpire = claims.ExpiresAt
+	return
+}
+
+func (j *JWT) Parse(jwtString string) (userId string, expiresAt int64, claimMap json.RawMessage, err error) {
+	jwtString = jwtHeader + "." + jwtString
+	token, err := jwtgo.ParseWithClaims(jwtString, &CustomClaims{}, func(token *jwtgo.Token) (interface{}, error) {
+		return j.secret, nil
+	})
+	if err != nil {
+		if ve, ok := err.(*jwtgo.ValidationError); ok {
+			if ve.Errors == jwtgo.ValidationErrorExpired {
+				err = ErrTokenExpired
+			}
+		}
+		return
+	}
+	if claims, ok := token.Claims.(*CustomClaims); ok {
+		userId = claims.AccountId
+		expiresAt = claims.StandardClaims.ExpiresAt
+		claimMap = claims.Claims
+		if token.Valid {
+			return
+		}
+	}
+	err = ErrTokenInvalid
+	return
+}
+
+//func (j *JWT) RevokeToken(sess string) error {
+//	return j.adapter.Revoke(sess)
+//}
+
+//type JWTSessionAdapter interface {
+//	//Validate(sessID, accountID string) bool
+//	//Store(sessID, accountID, refreshToken string) error
+//	//Revoke(sessID string) error
+//	//GenerateSess() (string, error)
+//}
+//
+//var ErrSessionInvalid = errors.New("session invalid")
+//
+//type DefaultJWTSessionAdatper struct {
+//}
+//
+//func NewDefaultAdapter() *DefaultJWTSessionAdatper {
+//	return &DefaultJWTSessionAdatper{}
+//}
+//
+//func (a *DefaultJWTSessionAdatper) Store(sess, accountID, refreshToken string) error {
+//	return nil
+//}
+//
+//func (a *DefaultJWTSessionAdatper) Validate(sess, accountID string) bool {
+//	return true
+//
+//}
+//
+//func (a *DefaultJWTSessionAdatper) Revoke(sess string) error {
+//	return nil
+//}
+//
+//func (a *DefaultJWTSessionAdatper) GenerateSess() (string, error) {
+//	return utils.GetUUID(), nil
+//}

+ 85 - 0
common/mwt.go

@@ -0,0 +1,85 @@
+package common
+
+import (
+	"time"
+
+	"github.com/vmihailenco/msgpack/v5"
+
+	"github.com/dcsunny/mwt"
+)
+
+// secret:加密秘钥
+// expire:过期时间,单位为秒
+type MWT struct {
+	secret []byte
+	expire int64
+}
+
+type MWTClaims struct {
+	AccountId string             `msgpack:"id"`
+	Claims    msgpack.RawMessage `msgpack:"claims,omitempty"`
+	mwt.StandardClaims
+}
+
+var (
+	DefaultMWT *MWT
+)
+
+const (
+	mwtHeader = "gqN0eXCjTVdUo2FsZ6VIUzI1Ng"
+)
+
+func InitMWT(secret string, expire int64) {
+	DefaultMWT = NewMWT(secret, expire)
+}
+
+func NewMWT(secret string, expire int64) *MWT {
+	var j = new(MWT)
+	j.secret = []byte(secret)
+	j.expire = expire
+	return j
+}
+
+func (j *MWT) NewToken(accountID string, claimMap msgpack.RawMessage) (token string, tokenExpire int64, err error) {
+	expireAt := time.Now().Add(time.Duration(j.expire) * time.Second).Unix()
+	claims := MWTClaims{
+		AccountId: accountID,
+		Claims:    claimMap,
+		StandardClaims: mwt.StandardClaims{
+			ExpiresAt: expireAt,
+		},
+	}
+	t := mwt.NewWithClaims(mwt.SigningMethodHS256, claims)
+	token, err = t.SignedString(j.secret)
+	if err != nil {
+		return
+	}
+	token = token[27:]
+	tokenExpire = claims.ExpiresAt
+	return
+}
+
+func (j *MWT) Parse(text string) (userId string, expiresAt int64, claimMap msgpack.RawMessage, err error) {
+	text = mwtHeader + "." + text
+	token, err := mwt.ParseWithClaims(text, &MWTClaims{}, func(token *mwt.Token) (interface{}, error) {
+		return j.secret, nil
+	})
+	if err != nil {
+		if ve, ok := err.(*mwt.ValidationError); ok {
+			if ve.Errors == mwt.ValidationErrorExpired {
+				err = ErrTokenExpired
+			}
+		}
+		return
+	}
+	if claims, ok := token.Claims.(*MWTClaims); ok {
+		userId = claims.AccountId
+		expiresAt = claims.StandardClaims.ExpiresAt
+		claimMap = claims.Claims
+		if token.Valid {
+			return claims.AccountId, claims.StandardClaims.ExpiresAt, claims.Claims, nil
+		}
+	}
+	err = ErrTokenInvalid
+	return
+}

+ 1 - 1
reply.go → common/reply.go

@@ -1,4 +1,4 @@
-package utils
+package common
 
 type SuccessReply struct {
 	Code    int         `json:"code"`

+ 5 - 0
go.mod

@@ -3,5 +3,10 @@ module git.ikuban.com/server/kratos-utils
 go 1.16
 
 require (
+	github.com/dcsunny/gocrypt v0.0.0-20200828060317-4dec5212cc15
+	github.com/dcsunny/mwt v0.0.0-20210128034911-2f50006077f5
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-kratos/kratos/v2 v2.0.0-beta2
+	github.com/lestrrat-go/jwx v1.1.5
+	github.com/vmihailenco/msgpack/v5 v5.3.0
 )

+ 62 - 0
go.sum

@@ -10,6 +10,16 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLje
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dcsunny/gocrypt v0.0.0-20200828060317-4dec5212cc15 h1:8RHfHCrI9cKZXG5TCqU+lMU7lfIOuU0ZEJ5XuH2VCXo=
+github.com/dcsunny/gocrypt v0.0.0-20200828060317-4dec5212cc15/go.mod h1:2mp0gjRCsi62xtFKIzPNphEtDrrmJndj+aqiz6baODU=
+github.com/dcsunny/mwt v0.0.0-20210128034911-2f50006077f5 h1:jtktV8hkvoAX2Q2Ja4FHm/zQvQdhw5V+KXJ9R4PQ3xs=
+github.com/dcsunny/mwt v0.0.0-20210128034911-2f50006077f5/go.mod h1:APsyTDg02bfsIwT2+nX9wgsuVxIRMzBOBcXAuALFtKk=
+github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw=
+github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE=
@@ -20,6 +30,8 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/go-kratos/kratos/v2 v2.0.0-beta2 h1:KfYlxfXsjRiccGS1TVRKvXVuEnPJDwrrTSx5HtaOBlc=
 github.com/go-kratos/kratos/v2 v2.0.0-beta2/go.mod h1:hwEYWw8GFuJ8IoNt3T/3k+7kUfYt+h2fHDcyFlR1jBA=
+github.com/goccy/go-json v0.4.7 h1:xGUjaNfhpqhKAV2LoyNXihFLZ8ABSST8B+W+duHqkPI=
+github.com/goccy/go-json v0.4.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
@@ -48,6 +60,22 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE=
+github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
+github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
+github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
+github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
+github.com/lestrrat-go/iter v1.0.0 h1:QD+hHQPDSHC4rCJkZYY/yXChYr/vjfBopKekTc+7l4Q=
+github.com/lestrrat-go/iter v1.0.0/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
+github.com/lestrrat-go/jwx v1.1.5 h1:TZ0Vly07R/duLIRS11CennIovzBvzF46ofWGj1vB26A=
+github.com/lestrrat-go/jwx v1.1.5/go.mod h1:VE4Y8PnxQ1hWQ34Nbx1EbIAgs+IzsEhANW4zvkFQZW0=
+github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
+github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/lestrrat-go/pdebug/v3 v3.0.1 h1:3G5sX/aw/TbMTtVc9U7IHBWRZtMvwvBziF1e4HoQtv8=
+github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
@@ -55,8 +83,17 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI=
+github.com/vmihailenco/msgpack/v5 v5.3.0 h1:8G3at/kelmBKeHY6d6cKnGsYO3BLn+uubitdOtOhyNI=
+github.com/vmihailenco/msgpack/v5 v5.3.0/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
+github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
+github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8=
 go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78=
 go.opentelemetry.io/otel/metric v0.18.0 h1:yuZCmY9e1ZTaMlZXLrrbAPmYW6tW1A5ozOZeOYGaTaY=
@@ -67,36 +104,61 @@ go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g
 go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
+golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=