cli: print maa token in verify

Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
This commit is contained in:
Paul Meyer 2023-08-04 17:48:14 +02:00
parent 8dbe79500f
commit e97b2afc14
8 changed files with 225 additions and 23 deletions

View File

@ -2277,6 +2277,15 @@ def go_dependencies():
sum = "h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=",
version = "v4.5.0",
)
go_repository(
name = "com_github_golang_jwt_jwt_v5",
build_file_generation = "on",
build_file_proto_mode = "disable_global",
importpath = "github.com/golang-jwt/jwt/v5",
sum = "h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=",
version = "v5.0.0",
)
go_repository(
name = "com_github_golang_mock",
build_file_generation = "on",

View File

@ -84,6 +84,7 @@ go_library(
"//internal/versions",
"//operators/constellation-node-operator/api/v1alpha1",
"//verify/verifyproto",
"@com_github_golang_jwt_jwt_v5//:jwt",
"@com_github_google_go_sev_guest//abi",
"@com_github_google_go_sev_guest//kds",
"@com_github_google_uuid//:uuid",

View File

@ -15,7 +15,10 @@ import (
"encoding/pem"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strconv"
"strings"
@ -30,6 +33,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
"github.com/edgelesssys/constellation/v2/verify/verifyproto"
"github.com/golang-jwt/jwt/v5"
"github.com/google/go-sev-guest/abi"
"github.com/google/go-sev-guest/kds"
"github.com/spf13/afero"
@ -127,7 +131,14 @@ func (c *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
}
// certificates are only available for Azure
attDocOutput, err := formatter.format(rawAttestationDoc, conf.Provider.Azure == nil, flags.rawOutput, attConfig.GetMeasurements())
attDocOutput, err := formatter.format(
cmd.Context(),
rawAttestationDoc,
conf.Provider.Azure == nil,
flags.rawOutput,
attConfig.GetMeasurements(),
flags.maaURL,
)
if err != nil {
return fmt.Errorf("printing attestation document: %w", err)
}
@ -240,7 +251,8 @@ func addPortIfMissing(endpoint string, defaultPort int) (string, error) {
// an attestationDocFormatter formats the attestation document.
type attestationDocFormatter interface {
// format returns the raw or formatted attestation doc depending on the rawOutput argument.
format(docString string, PCRsOnly bool, rawOutput bool, expectedPCRs measurements.M) (string, error)
format(ctx context.Context, docString string, PCRsOnly bool, rawOutput bool, expectedPCRs measurements.M,
attestationServiceURL string) (string, error)
}
type attestationDocFormatterImpl struct {
@ -248,7 +260,9 @@ type attestationDocFormatterImpl struct {
}
// format returns the raw or formatted attestation doc depending on the rawOutput argument.
func (f *attestationDocFormatterImpl) format(docString string, PCRsOnly bool, rawOutput bool, expectedPCRs measurements.M) (string, error) {
func (f *attestationDocFormatterImpl) format(ctx context.Context, docString string, PCRsOnly bool,
rawOutput bool, expectedPCRs measurements.M, attestationServiceURL string,
) (string, error) {
b := &strings.Builder{}
b.WriteString("Attestation Document:\n")
if rawOutput {
@ -287,6 +301,9 @@ func (f *attestationDocFormatterImpl) format(docString string, PCRsOnly bool, ra
if err := f.parseSNPReport(b, instanceInfo.AttestationReport); err != nil {
return "", fmt.Errorf("print SNP report: %w", err)
}
if err := parseMAAToken(ctx, b, instanceInfo.MAAToken, attestationServiceURL); err != nil {
return "", fmt.Errorf("print MAA token: %w", err)
}
return b.String(), nil
}
@ -447,6 +464,158 @@ func (f *attestationDocFormatterImpl) parseSNPReport(b *strings.Builder, reportB
return nil
}
func parseMAAToken(ctx context.Context, b *strings.Builder, rawToken, attestationServiceURL string) error {
var claims maaTokenClaims
_, err := jwt.ParseWithClaims(rawToken, &claims, keyFromJKUFunc(ctx, attestationServiceURL), jwt.WithIssuedAt())
if err != nil {
return fmt.Errorf("parsing token: %w", err)
}
out, err := json.MarshalIndent(claims, "\t\t", " ")
if err != nil {
return fmt.Errorf("marshaling claims: %w", err)
}
b.WriteString("\tMicrosoft Azure Attestation Token:\n\t")
b.WriteString(string(out))
return nil
}
// keyFromJKUFunc returns a function that gets the JSON Web Key URI from the token
// and fetches the key from that URI. The keys are then parsed, and the key with
// the kid that matches the token header is returned.
func keyFromJKUFunc(ctx context.Context, webKeysURLBase string) func(token *jwt.Token) (any, error) {
return func(token *jwt.Token) (any, error) {
webKeysURL, err := url.JoinPath(webKeysURLBase, "certs")
if err != nil {
return nil, fmt.Errorf("joining web keys base URL with path: %w", err)
}
if token.Header["alg"] != "RS256" {
return nil, fmt.Errorf("invalid signing algorithm: %s", token.Header["alg"])
}
kid, ok := token.Header["kid"].(string)
if !ok {
return nil, fmt.Errorf("invalid kid: %v", token.Header["kid"])
}
jku, ok := token.Header["jku"].(string)
if !ok {
return nil, fmt.Errorf("invalid jku: %v", token.Header["jku"])
}
if jku != webKeysURL {
return nil, fmt.Errorf("jku from token (%s) does not match configured attestation service (%s)", jku, webKeysURL)
}
keySetBytes, err := httpGet(ctx, jku)
if err != nil {
return nil, fmt.Errorf("getting signing keys from jku %s: %w", jku, err)
}
var rawKeySet struct {
Keys []struct {
X5c [][]byte
Kid string
}
}
if err := json.Unmarshal(keySetBytes, &rawKeySet); err != nil {
return nil, err
}
for _, key := range rawKeySet.Keys {
if key.Kid != kid {
continue
}
cert, err := x509.ParseCertificate(key.X5c[0])
if err != nil {
return nil, fmt.Errorf("parsing certificate: %w", err)
}
return cert.PublicKey, nil
}
return nil, fmt.Errorf("no key found for kid %s", kid)
}
}
type maaTokenClaims struct {
jwt.RegisteredClaims
Secureboot bool `json:"secureboot,omitempty"`
XMsAttestationType string `json:"x-ms-attestation-type,omitempty"`
XMsAzurevmAttestationProtocolVer string `json:"x-ms-azurevm-attestation-protocol-ver,omitempty"`
XMsAzurevmAttestedPcrs []int `json:"x-ms-azurevm-attested-pcrs,omitempty"`
XMsAzurevmBootdebugEnabled bool `json:"x-ms-azurevm-bootdebug-enabled,omitempty"`
XMsAzurevmDbvalidated bool `json:"x-ms-azurevm-dbvalidated,omitempty"`
XMsAzurevmDbxvalidated bool `json:"x-ms-azurevm-dbxvalidated,omitempty"`
XMsAzurevmDebuggersdisabled bool `json:"x-ms-azurevm-debuggersdisabled,omitempty"`
XMsAzurevmDefaultSecurebootkeysvalidated bool `json:"x-ms-azurevm-default-securebootkeysvalidated,omitempty"`
XMsAzurevmElamEnabled bool `json:"x-ms-azurevm-elam-enabled,omitempty"`
XMsAzurevmFlightsigningEnabled bool `json:"x-ms-azurevm-flightsigning-enabled,omitempty"`
XMsAzurevmHvciPolicy int `json:"x-ms-azurevm-hvci-policy,omitempty"`
XMsAzurevmHypervisordebugEnabled bool `json:"x-ms-azurevm-hypervisordebug-enabled,omitempty"`
XMsAzurevmIsWindows bool `json:"x-ms-azurevm-is-windows,omitempty"`
XMsAzurevmKerneldebugEnabled bool `json:"x-ms-azurevm-kerneldebug-enabled,omitempty"`
XMsAzurevmOsbuild string `json:"x-ms-azurevm-osbuild,omitempty"`
XMsAzurevmOsdistro string `json:"x-ms-azurevm-osdistro,omitempty"`
XMsAzurevmOstype string `json:"x-ms-azurevm-ostype,omitempty"`
XMsAzurevmOsversionMajor int `json:"x-ms-azurevm-osversion-major,omitempty"`
XMsAzurevmOsversionMinor int `json:"x-ms-azurevm-osversion-minor,omitempty"`
XMsAzurevmSigningdisabled bool `json:"x-ms-azurevm-signingdisabled,omitempty"`
XMsAzurevmTestsigningEnabled bool `json:"x-ms-azurevm-testsigning-enabled,omitempty"`
XMsAzurevmVmid string `json:"x-ms-azurevm-vmid,omitempty"`
XMsIsolationTee struct {
XMsAttestationType string `json:"x-ms-attestation-type,omitempty"`
XMsComplianceStatus string `json:"x-ms-compliance-status,omitempty"`
XMsRuntime struct {
Keys []struct {
E string `json:"e,omitempty"`
KeyOps []string `json:"key_ops,omitempty"`
Kid string `json:"kid,omitempty"`
Kty string `json:"kty,omitempty"`
N string `json:"n,omitempty"`
} `json:"keys,omitempty"`
VMConfiguration struct {
ConsoleEnabled bool `json:"console-enabled,omitempty"`
CurrentTime int `json:"current-time,omitempty"`
SecureBoot bool `json:"secure-boot,omitempty"`
TpmEnabled bool `json:"tpm-enabled,omitempty"`
VMUniqueID string `json:"vmUniqueId,omitempty"`
} `json:"vm-configuration,omitempty"`
} `json:"x-ms-runtime,omitempty"`
XMsSevsnpvmAuthorkeydigest string `json:"x-ms-sevsnpvm-authorkeydigest,omitempty"`
XMsSevsnpvmBootloaderSvn int `json:"x-ms-sevsnpvm-bootloader-svn,omitempty"`
XMsSevsnpvmFamilyID string `json:"x-ms-sevsnpvm-familyId,omitempty"`
XMsSevsnpvmGuestsvn int `json:"x-ms-sevsnpvm-guestsvn,omitempty"`
XMsSevsnpvmHostdata string `json:"x-ms-sevsnpvm-hostdata,omitempty"`
XMsSevsnpvmIdkeydigest string `json:"x-ms-sevsnpvm-idkeydigest,omitempty"`
XMsSevsnpvmImageID string `json:"x-ms-sevsnpvm-imageId,omitempty"`
XMsSevsnpvmIsDebuggable bool `json:"x-ms-sevsnpvm-is-debuggable,omitempty"`
XMsSevsnpvmLaunchmeasurement string `json:"x-ms-sevsnpvm-launchmeasurement,omitempty"`
XMsSevsnpvmMicrocodeSvn int `json:"x-ms-sevsnpvm-microcode-svn,omitempty"`
XMsSevsnpvmMigrationAllowed bool `json:"x-ms-sevsnpvm-migration-allowed,omitempty"`
XMsSevsnpvmReportdata string `json:"x-ms-sevsnpvm-reportdata,omitempty"`
XMsSevsnpvmReportid string `json:"x-ms-sevsnpvm-reportid,omitempty"`
XMsSevsnpvmSmtAllowed bool `json:"x-ms-sevsnpvm-smt-allowed,omitempty"`
XMsSevsnpvmSnpfwSvn int `json:"x-ms-sevsnpvm-snpfw-svn,omitempty"`
XMsSevsnpvmTeeSvn int `json:"x-ms-sevsnpvm-tee-svn,omitempty"`
XMsSevsnpvmVmpl int `json:"x-ms-sevsnpvm-vmpl,omitempty"`
} `json:"x-ms-isolation-tee,omitempty"`
XMsPolicyHash string `json:"x-ms-policy-hash,omitempty"`
XMsRuntime struct {
ClientPayload struct {
Nonce string `json:"nonce,omitempty"`
} `json:"client-payload,omitempty"`
Keys []struct {
E string `json:"e,omitempty"`
KeyOps []string `json:"key_ops,omitempty"`
Kid string `json:"kid,omitempty"`
Kty string `json:"kty,omitempty"`
N string `json:"n,omitempty"`
} `json:"keys,omitempty"`
} `json:"x-ms-runtime,omitempty"`
XMsVer string `json:"x-ms-ver,omitempty"`
}
// attestationDoc is the attestation document returned by the verifier.
type attestationDoc struct {
Attestation struct {
@ -531,3 +700,23 @@ func writeIndentfln(b *strings.Builder, indentLvl int, format string, args ...an
}
b.WriteString(fmt.Sprintf(format+"\n", args...))
}
func httpGet(ctx context.Context, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New(resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}

View File

@ -201,7 +201,7 @@ type stubAttDocFormatter struct {
formatErr error
}
func (f *stubAttDocFormatter) format(_ string, _ bool, _ bool, _ measurements.M) (string, error) {
func (f *stubAttDocFormatter) format(_ context.Context, _ string, _ bool, _ bool, _ measurements.M, _ string) (string, error) {
return "", f.formatErr
}
@ -226,7 +226,7 @@ func TestFormat(t *testing.T) {
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
_, err := tc.formatter.format(tc.doc, false, false, nil)
_, err := tc.formatter.format(context.Background(), tc.doc, false, false, nil, "")
if tc.wantErr {
assert.Error(t, err)
} else {

34
go.mod
View File

@ -75,6 +75,7 @@ require (
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.14.1
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/google/go-tpm v0.9.0
github.com/google/go-tpm-tools v0.4.0
github.com/googleapis/gax-go/v2 v2.12.0
@ -123,29 +124,12 @@ require (
sigs.k8s.io/yaml v1.3.0
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/stretchr/objx v0.5.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
k8s.io/cli-runtime v0.27.2 // indirect
)
require (
cloud.google.com/go v0.110.2 // indirect
cloud.google.com/go/iam v1.1.0 // indirect
cloud.google.com/go/longrunning v0.4.2 // indirect
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 // indirect
@ -167,6 +151,8 @@ require (
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.44.297 // indirect
@ -189,6 +175,7 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
@ -204,11 +191,13 @@ require (
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.0.5 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
@ -219,6 +208,7 @@ require (
github.com/go-openapi/strfmt v0.21.7 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/validate v0.22.1 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
@ -236,6 +226,8 @@ require (
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/logger v1.1.1 // indirect
github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
@ -308,6 +300,7 @@ require (
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/theupdateframework/go-tuf v0.5.2
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
@ -319,6 +312,8 @@ require (
github.com/zclconf/go-cty v1.13.2 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.starlark.net v0.0.0-20220223235035-243c74974e97 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
@ -332,9 +327,12 @@ require (
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/cli-runtime v0.27.2 // indirect
k8s.io/component-base v0.27.3 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect

2
go.sum
View File

@ -471,6 +471,8 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@ -159,6 +159,7 @@ require (
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect

View File

@ -431,6 +431,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=