diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6273c13e..7470ed3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,4 +23,5 @@ jobs: os-dependencies: "libpcsclite-dev" run-gitleaks: true run-codeql: true + make-test: true # run `make test` instead of the default test workflow secrets: inherit diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 00000000..471eedab --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,22 @@ +name: Dependabot auto-merge +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1.1.1 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f66ad67b..621f6a91 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,7 @@ jobs: version: ${{ steps.extract-tag.outputs.VERSION }} is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }} docker_tags: ${{ env.DOCKER_TAGS }} + docker_tags_hsm: ${{ env.DOCKER_TAGS_HSM }} steps: - name: Is Pre-release id: is_prerelease @@ -36,10 +37,12 @@ jobs: VERSION=${GITHUB_REF#refs/tags/v} echo "VERSION=${VERSION}" >> ${GITHUB_OUTPUT} echo "DOCKER_TAGS=${{ env.DOCKER_IMAGE }}:${VERSION}" >> ${GITHUB_ENV} + echo "DOCKER_TAGS_HSM=${{ env.DOCKER_IMAGE }}:${VERSION}-hsm" >> ${GITHUB_ENV} - name: Add Latest Tag if: steps.is_prerelease.outputs.IS_PRERELEASE == 'false' run: | echo "DOCKER_TAGS=${{ env.DOCKER_TAGS }},${{ env.DOCKER_IMAGE }}:latest" >> ${GITHUB_ENV} + echo "DOCKER_TAGS_HSM=${{ env.DOCKER_TAGS_HSM }},${{ env.DOCKER_IMAGE }}:hsm" >> ${GITHUB_ENV} - name: Create Release id: create_release uses: actions/create-release@v1 @@ -79,7 +82,7 @@ jobs: uses: goreleaser/goreleaser-action@v3 with: version: 'latest' - args: release --rm-dist + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GORELEASER_PAT }} RELEASE_DATE: ${{ env.RELEASE_DATE }} @@ -96,5 +99,19 @@ jobs: platforms: linux/amd64,linux/386,linux/arm,linux/arm64 tags: ${{ needs.create_release.outputs.docker_tags }} docker_image: smallstep/step-ca - docker_file: docker/Dockerfile.step-ca + docker_file: docker/Dockerfile + secrets: inherit + + build_upload_docker_hsm: + name: Build & Upload HSM Enabled Docker Images + needs: create_release + permissions: + id-token: write + contents: write + uses: smallstep/workflows/.github/workflows/docker-buildx-push.yml@main + with: + platforms: linux/amd64,linux/386,linux/arm,linux/arm64 + tags: ${{ needs.create_release.outputs.docker_tags_hsm }} + docker_image: smallstep/step-ca + docker_file: docker/Dockerfile.hsm secrets: inherit diff --git a/.goreleaser.yml b/.goreleaser.yml index 9b5398a9..c296092d 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -36,6 +36,7 @@ archives: # Most common use case is to archive as zip on Windows. # Default is empty. name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Version }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}" + rlcp: true format_overrides: - goos: windows format: zip @@ -78,6 +79,7 @@ nfpms: source: enabled: true + rlcp: true name_template: '{{ .ProjectName }}_{{ .Version }}' checksum: @@ -140,7 +142,7 @@ release: #### Windows - - 📦 [step-ca_windows_{{ .Version }}_arm64.zip](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) + - 📦 [step-ca_windows_{{ .Version }}_amd64.zip](https://dl.step.sm/gh-release/certificates/gh-release-header/{{ .Tag }}/step-ca_windows_{{ .Version }}_amd64.zip) For more builds across platforms and architectures, see the `Assets` section below. And for packaged versions (Docker, k8s, Homebrew), see our [installation docs](https://smallstep.com/docs/step-ca/installation). @@ -154,9 +156,11 @@ release: Below is an example using `cosign` to verify a release artifact: ``` - COSIGN_EXPERIMENTAL=1 cosign verify-blob \ + cosign verify-blob \ --certificate ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz.sig.pem \ --signature ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz.sig \ + --certificate-identity-regexp "https://github\.com/smallstep/certificates/.*" \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz ``` @@ -185,3 +189,40 @@ release: # - glob: ./path/to/file.txt # - glob: ./glob/**/to/**/file/**/* # - glob: ./glob/foo/to/bar/file/foobar/override_from_previous + +scoop: + # Template for the url which is determined by the given Token (github or gitlab) + # Default for github is "https://github.com///releases/download/{{ .Tag }}/{{ .ArtifactName }}" + # Default for gitlab is "https://gitlab.com///uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}" + # Default for gitea is "https://gitea.com///releases/download/{{ .Tag }}/{{ .ArtifactName }}" + url_template: "http://github.com/smallstep/certificates/releases/download/{{ .Tag }}/{{ .ArtifactName }}" + + # Repository to push the app manifest to. + bucket: + owner: smallstep + name: scoop-bucket + + # Git author used to commit to the repository. + # Defaults are shown. + commit_author: + name: goreleaserbot + email: goreleaser@smallstep.com + + # The project name and current git tag are used in the format string. + commit_msg_template: "Scoop update for {{ .ProjectName }} version {{ .Tag }}" + + # Your app's homepage. + # Default is empty. + homepage: "https://smallstep.com/docs/step-ca" + + # Skip uploads for prerelease. + skip_upload: auto + + # Your app's description. + # Default is empty. + description: "A private certificate authority (X.509 & SSH) & ACME server for secure automated certificate management, so you can use TLS everywhere & SSO for SSH." + + # Your app's license + # Default is empty. + license: "Apache-2.0" + diff --git a/CHANGELOG.md b/CHANGELOG.md index ada640fc..a7c85936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,12 +25,85 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. --- -## [Unreleased] +## [v0.24.0] - 2023-04-12 + +### Added + +- Add ACME `device-attest-01` support with TPM 2.0 + (smallstep/certificates#1063). +- Add support for new Azure SDK, sovereign clouds, and HSM keys on Azure KMS + (smallstep/crypto#192, smallstep/crypto#197, smallstep/crypto#198, + smallstep/certificates#1323, smallstep/certificates#1309). +- Add support for ASN.1 functions on certificate templates + (smallstep/crypto#208, smallstep/certificates#1345) +- Add `DOCKER_STEPCA_INIT_ADDRESS` to configure the address to use in a docker + container (smallstep/certificates#1262). +- Make sure that the CSR used matches the attested key when using AME + `device-attest-01` challenge (smallstep/certificates#1265). +- Add support for compacting the Badger DB (smallstep/certificates#1298). +- Build and release cleanups (smallstep/certificates#1322, + smallstep/certificates#1329, smallstep/certificates#1340). + +### Fixed + +- Fix support for PKCS #7 RSA-OAEP decryption through + [smallstep/pkcs7#4](https://github.com/smallstep/pkcs7/pull/4), as used in + SCEP. +- Fix RA installation using `scripts/install-step-ra.sh` + (smallstep/certificates#1255). +- Clarify error messages on policy errors (smallstep/certificates#1287, + smallstep/certificates#1278). +- Clarify error message on OIDC email validation (smallstep/certificates#1290). +- Mark the IDP critical in the generated CRL data (smallstep/certificates#1293). +- Disable database if CA is initialized with the `--no-db` flag + (smallstep/certificates#1294). + +## [v0.23.2] - 2023-02-02 + +### Added + +- Added [`step-kms-plugin`](https://github.com/smallstep/step-kms-plugin) to + docker images, and a new image, `smallstep/step-ca-hsm`, compiled with cgo + (smallstep/certificates#1243). +- Added [`scoop`](https://scoop.sh) packages back to the release + (smallstep/certificates#1250). +- Added optional flag `--pidfile` which allows passing a filename where step-ca + will write its process id (smallstep/certificates#1251). +- Added helpful message on CA startup when config can't be opened + (smallstep/certificates#1252). +- Improved validation and error messages on `device-attest-01` orders + (smallstep/certificates#1235). + +### Removed + +- The deprecated CLI utils `step-awskms-init`, `step-cloudkms-init`, + `step-pkcs11-init`, `step-yubikey-init` have been removed. + [`step`](https://github.com/smallstep/cli) and + [`step-kms-plugin`](https://github.com/smallstep/step-kms-plugin) should be + used instead (smallstep/certificates#1240). + +### Fixed + +- Fixed remote management flags in docker images (smallstep/certificates#1228). + +## [v0.23.1] - 2023-01-10 ### Added - Added configuration property `.crl.idpURL` to be able to set a custom Issuing - Distribution Point in the CRL. + Distribution Point in the CRL (smallstep/certificates#1178). +- Added WithContext methods to the CA client (smallstep/certificates#1211). +- Docker: Added environment variables for enabling Remote Management and ACME + provisioner (smallstep/certificates#1201). +- Docker: The entrypoint script now generates and displays an initial JWK + provisioner password by default when the CA is being initialized + (smallstep/certificates#1223). + +### Changed + +- Ignore SSH principals validation when using an OIDC provisioner. The + provisioner will ignore the principals passed and set the defaults or the ones + including using WebHooks or templates (smallstep/certificates#1206). ## [v0.23.0] - 2022-11-11 diff --git a/Makefile b/Makefile index 90e96993..5d7995f4 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,11 @@ PKG?=github.com/smallstep/certificates/cmd/step-ca BINNAME?=step-ca -CLOUDKMS_BINNAME?=step-cloudkms-init -CLOUDKMS_PKG?=github.com/smallstep/certificates/cmd/step-cloudkms-init -AWSKMS_BINNAME?=step-awskms-init -AWSKMS_PKG?=github.com/smallstep/certificates/cmd/step-awskms-init -YUBIKEY_BINNAME?=step-yubikey-init -YUBIKEY_PKG?=github.com/smallstep/certificates/cmd/step-yubikey-init -PKCS11_BINNAME?=step-pkcs11-init -PKCS11_PKG?=github.com/smallstep/certificates/cmd/step-pkcs11-init # Set V to 1 for verbose output from the Makefile Q=$(if $V,,@) PREFIX?= SRC=$(shell find . -type f -name '*.go' -not -path "./vendor/*") GOOS_OVERRIDE ?= -OUTPUT_ROOT=output/ -RELEASE=./.releases all: lint test build @@ -31,6 +21,8 @@ bootstra%: $Q curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin latest $Q go install golang.org/x/vuln/cmd/govulncheck@latest $Q go install gotest.tools/gotestsum@latest + $Q go install github.com/goreleaser/goreleaser@latest + $Q go install github.com/sigstore/cosign/v2/cmd/cosign@latest .PHONY: bootstra% @@ -38,17 +30,8 @@ bootstra%: # Determine the type of `push` and `version` ################################################# -# If TRAVIS_TAG is set then we know this ref has been tagged. -ifdef TRAVIS_TAG -VERSION ?= $(TRAVIS_TAG) -NOT_RC := $(shell echo $(VERSION) | grep -v -e -rc) - ifeq ($(NOT_RC),) -PUSHTYPE := release-candidate - else -PUSHTYPE := release - endif # GITHUB Actions -else ifdef GITHUB_REF +ifdef GITHUB_REF VERSION ?= $(shell echo $(GITHUB_REF) | sed 's/^refs\/tags\///') NOT_RC := $(shell echo $(VERSION) | grep -v -e -rc) ifeq ($(NOT_RC),) @@ -61,21 +44,14 @@ VERSION ?= $(shell [ -d .git ] && git describe --tags --always --dirty="-dev") # If we are not in an active git dir then try reading the version from .VERSION. # .VERSION contains a slug populated by `git archive`. VERSION := $(or $(VERSION),$(shell ./.version.sh .VERSION)) - ifeq ($(TRAVIS_BRANCH),master) -PUSHTYPE := master - else PUSHTYPE := branch - endif endif VERSION := $(shell echo $(VERSION) | sed 's/^v//') -DEB_VERSION := $(shell echo $(VERSION) | sed 's/-/./g') ifdef V -$(info TRAVIS_TAG is $(TRAVIS_TAG)) $(info GITHUB_REF is $(GITHUB_REF)) $(info VERSION is $(VERSION)) -$(info DEB_VERSION is $(DEB_VERSION)) $(info PUSHTYPE is $(PUSHTYPE)) endif @@ -90,29 +66,13 @@ GOFLAGS := CGO_ENABLED=0 download: $Q go mod download -build: $(PREFIX)bin/$(BINNAME) $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(PREFIX)bin/$(AWSKMS_BINNAME) $(PREFIX)bin/$(YUBIKEY_BINNAME) $(PREFIX)bin/$(PKCS11_BINNAME) +build: $(PREFIX)bin/$(BINNAME) @echo "Build Complete!" $(PREFIX)bin/$(BINNAME): download $(call rwildcard,*.go) $Q mkdir -p $(@D) $Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(BINNAME) $(LDFLAGS) $(PKG) -$(PREFIX)bin/$(CLOUDKMS_BINNAME): download $(call rwildcard,*.go) - $Q mkdir -p $(@D) - $Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(LDFLAGS) $(CLOUDKMS_PKG) - -$(PREFIX)bin/$(AWSKMS_BINNAME): download $(call rwildcard,*.go) - $Q mkdir -p $(@D) - $Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(AWSKMS_BINNAME) $(LDFLAGS) $(AWSKMS_PKG) - -$(PREFIX)bin/$(YUBIKEY_BINNAME): download $(call rwildcard,*.go) - $Q mkdir -p $(@D) - $Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(YUBIKEY_BINNAME) $(LDFLAGS) $(YUBIKEY_PKG) - -$(PREFIX)bin/$(PKCS11_BINNAME): download $(call rwildcard,*.go) - $Q mkdir -p $(@D) - $Q $(GOOS_OVERRIDE) $(GOFLAGS) go build -v -o $(PREFIX)bin/$(PKCS11_BINNAME) $(LDFLAGS) $(PKCS11_PKG) - # Target to force a build of step-ca without running tests simple: build @@ -130,14 +90,21 @@ generate: ######################################### # Test ######################################### -test: - $Q $(GOFLAGS) gotestsum -- -coverprofile=coverage.out -short -covermode=atomic ./... +test: testdefault testtpmsimulator combinecoverage +testdefault: + $Q $(GOFLAGS) gotestsum -- -coverprofile=defaultcoverage.out -short -covermode=atomic ./... + +testtpmsimulator: + $Q CGO_ENALBED=1 gotestsum -- -coverprofile=tpmsimulatorcoverage.out -short -covermode=atomic -tags tpmsimulator ./acme testcgo: $Q gotestsum -- -coverprofile=coverage.out -short -covermode=atomic ./... -.PHONY: test testcgo +combinecoverage: + cat defaultcoverage.out tpmsimulatorcoverage.out > coverage.out + +.PHONY: test testdefault testtpmsimulator testcgo combinecoverage integrate: integration @@ -166,15 +133,11 @@ lint: INSTALL_PREFIX?=/usr/ -install: $(PREFIX)bin/$(BINNAME) $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(PREFIX)bin/$(AWSKMS_BINNAME) +install: $(PREFIX)bin/$(BINNAME) $Q install -D $(PREFIX)bin/$(BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(BINNAME) - $Q install -D $(PREFIX)bin/$(CLOUDKMS_BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(CLOUDKMS_BINNAME) - $Q install -D $(PREFIX)bin/$(AWSKMS_BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(AWSKMS_BINNAME) uninstall: $Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BINNAME) - $Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(CLOUDKMS_BINNAME) - $Q rm -f $(DESTDIR)$(INSTALL_PREFIX)/bin/$(AWSKMS_BINNAME) .PHONY: install uninstall @@ -186,18 +149,6 @@ clean: ifneq ($(BINNAME),"") $Q rm -f bin/$(BINNAME) endif -ifneq ($(CLOUDKMS_BINNAME),"") - $Q rm -f bin/$(CLOUDKMS_BINNAME) -endif -ifneq ($(AWSKMS_BINNAME),"") - $Q rm -f bin/$(AWSKMS_BINNAME) -endif -ifneq ($(YUBIKEY_BINNAME),"") - $Q rm -f bin/$(YUBIKEY_BINNAME) -endif -ifneq ($(PKCS11_BINNAME),"") - $Q rm -f bin/$(PKCS11_BINNAME) -endif .PHONY: clean @@ -210,23 +161,3 @@ run: .PHONY: run -######################################### -# Debian -######################################### - -changelog: - $Q echo "step-ca ($(DEB_VERSION)) unstable; urgency=medium" > debian/changelog - $Q echo >> debian/changelog - $Q echo " * See https://github.com/smallstep/certificates/releases" >> debian/changelog - $Q echo >> debian/changelog - $Q echo " -- Smallstep Labs, Inc. $(shell date -uR)" >> debian/changelog - -debian: changelog - $Q mkdir -p $(RELEASE); \ - OUTPUT=../step-ca*.deb; \ - rm $$OUTPUT; \ - dpkg-buildpackage -b -rfakeroot -us -uc && cp $$OUTPUT $(RELEASE)/ - -distclean: clean - -.PHONY: changelog debian distclean diff --git a/acme/account_test.go b/acme/account_test.go index 88718a9a..b8ce7276 100644 --- a/acme/account_test.go +++ b/acme/account_test.go @@ -135,7 +135,6 @@ func TestExternalAccountKey_BindTo(t *testing.T) { if assert.True(t, errors.As(err, &ae)) { assert.Equals(t, ae.Type, tt.err.Type) assert.Equals(t, ae.Detail, tt.err.Detail) - assert.Equals(t, ae.Identifier, tt.err.Identifier) assert.Equals(t, ae.Subproblems, tt.err.Subproblems) } } else { diff --git a/acme/api/account_test.go b/acme/api/account_test.go index 3f8641b8..d46c9eed 100644 --- a/acme/api/account_test.go +++ b/acme/api/account_test.go @@ -388,7 +388,6 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -828,7 +827,6 @@ func TestHandler_NewAccount(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1032,7 +1030,6 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/eab_test.go b/acme/api/eab_test.go index c923a2f6..14dbdad1 100644 --- a/acme/api/eab_test.go +++ b/acme/api/eab_test.go @@ -866,7 +866,6 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) { assert.Equals(t, ae.Status, tc.err.Status) assert.HasPrefix(t, ae.Err.Error(), tc.err.Err.Error()) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) } } else { @@ -1145,7 +1144,6 @@ func Test_validateEABJWS(t *testing.T) { assert.Equals(t, tc.err.Status, err.Status) assert.HasPrefix(t, err.Err.Error(), tc.err.Err.Error()) assert.Equals(t, tc.err.Detail, err.Detail) - assert.Equals(t, tc.err.Identifier, err.Identifier) assert.Equals(t, tc.err.Subproblems, err.Subproblems) } else { assert.Nil(t, err) diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index e814aaba..7ef7cd68 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -193,7 +193,6 @@ func TestHandler_GetDirectory(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -366,7 +365,6 @@ func TestHandler_GetAuthorization(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -509,7 +507,6 @@ func TestHandler_GetCertificate(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.HasPrefix(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -768,7 +765,6 @@ func TestHandler_GetChallenge(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/middleware_test.go b/acme/api/middleware_test.go index faff0616..3db3773c 100644 --- a/acme/api/middleware_test.go +++ b/acme/api/middleware_test.go @@ -93,7 +93,6 @@ func TestHandler_addNonce(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -147,7 +146,6 @@ func TestHandler_addDirLink(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -252,7 +250,6 @@ func TestHandler_verifyContentType(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -320,7 +317,6 @@ func TestHandler_isPostAsGet(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -410,7 +406,6 @@ func TestHandler_parseJWS(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -606,7 +601,6 @@ func TestHandler_verifyAndExtractJWSPayload(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -808,7 +802,6 @@ func TestHandler_lookupJWK(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1008,7 +1001,6 @@ func TestHandler_extractJWK(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1384,7 +1376,6 @@ func TestHandler_validateJWS(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1567,7 +1558,6 @@ func TestHandler_extractOrLookupJWK(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1652,7 +1642,6 @@ func TestHandler_checkPrerequisites(t *testing.T) { assert.FatalError(t, json.Unmarshal(bytes.TrimSpace(body), &ae)) assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/order_test.go b/acme/api/order_test.go index b7b58b7f..9f03c547 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -486,7 +486,6 @@ func TestHandler_GetOrder(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1846,7 +1845,6 @@ func TestHandler_NewOrder(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -2144,7 +2142,6 @@ func TestHandler_FinalizeOrder(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { diff --git a/acme/api/revoke_test.go b/acme/api/revoke_test.go index 240ac748..c4182400 100644 --- a/acme/api/revoke_test.go +++ b/acme/api/revoke_test.go @@ -1090,7 +1090,6 @@ func TestHandler_RevokeCert(t *testing.T) { assert.Equals(t, ae.Type, tc.err.Type) assert.Equals(t, ae.Detail, tc.err.Detail) - assert.Equals(t, ae.Identifier, tc.err.Identifier) assert.Equals(t, ae.Subproblems, tc.err.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) } else { @@ -1230,7 +1229,6 @@ func TestHandler_isAccountAuthorized(t *testing.T) { assert.Equals(t, acmeErr.Type, tc.err.Type) assert.Equals(t, acmeErr.Status, tc.err.Status) assert.Equals(t, acmeErr.Detail, tc.err.Detail) - assert.Equals(t, acmeErr.Identifier, tc.err.Identifier) assert.Equals(t, acmeErr.Subproblems, tc.err.Subproblems) }) @@ -1323,7 +1321,6 @@ func Test_wrapUnauthorizedError(t *testing.T) { assert.Equals(t, acmeErr.Type, tc.want.Type) assert.Equals(t, acmeErr.Status, tc.want.Status) assert.Equals(t, acmeErr.Detail, tc.want.Detail) - assert.Equals(t, acmeErr.Identifier, tc.want.Identifier) assert.Equals(t, acmeErr.Subproblems, tc.want.Subproblems) }) } diff --git a/acme/authorization.go b/acme/authorization.go index d2df5ea5..cb629073 100644 --- a/acme/authorization.go +++ b/acme/authorization.go @@ -8,15 +8,16 @@ import ( // Authorization representst an ACME Authorization. type Authorization struct { - ID string `json:"-"` - AccountID string `json:"-"` - Token string `json:"-"` - Identifier Identifier `json:"identifier"` - Status Status `json:"status"` - Challenges []*Challenge `json:"challenges"` - Wildcard bool `json:"wildcard"` - ExpiresAt time.Time `json:"expires"` - Error *Error `json:"error,omitempty"` + ID string `json:"-"` + AccountID string `json:"-"` + Token string `json:"-"` + Fingerprint string `json:"-"` + Identifier Identifier `json:"identifier"` + Status Status `json:"status"` + Challenges []*Challenge `json:"challenges"` + Wildcard bool `json:"wildcard"` + ExpiresAt time.Time `json:"expires"` + Error *Error `json:"error,omitempty"` } // ToLog enables response logging. diff --git a/acme/challenge.go b/acme/challenge.go index 1a45a252..a1d4067f 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -26,9 +26,16 @@ import ( "time" "github.com/fxamacker/cbor/v2" - "github.com/smallstep/certificates/authority/provisioner" + "github.com/google/go-attestation/attest" + "github.com/google/go-tpm/tpm2" + "golang.org/x/exp/slices" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/keyutil" "go.step.sm/crypto/pemutil" + "go.step.sm/crypto/x509util" + + "github.com/smallstep/certificates/authority/provisioner" ) type ChallengeType string @@ -79,10 +86,9 @@ func (ch *Challenge) ToLog() (interface{}, error) { return string(b), nil } -// Validate attempts to validate the challenge. Stores changes to the Challenge -// type using the DB interface. -// satisfactorily validated, the 'status' and 'validated' attributes are -// updated. +// Validate attempts to validate the Challenge. Stores changes to the Challenge +// type using the DB interface. If the Challenge is validated, the 'status' and +// 'validated' attributes are updated. func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, payload []byte) error { // If already valid or invalid then return without performing validation. if ch.Status != StatusPending { @@ -335,20 +341,26 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK return nil } -type Payload struct { +type payloadType struct { AttObj string `json:"attObj"` Error string `json:"error"` } -type AttestationObject struct { +type attestationObject struct { Format string `json:"fmt"` AttStatement map[string]interface{} `json:"attStmt,omitempty"` } // TODO(bweeks): move attestation verification to a shared package. -// TODO(bweeks): define new error type for failed attestation validation. func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { - var p Payload + // Load authorization to store the key fingerprint. + az, err := db.GetAuthorization(ctx, ch.AuthorizationID) + if err != nil { + return WrapErrorISE(err, "error loading authorization") + } + + // Parse payload. + var p payloadType if err := json.Unmarshal(payload, &p); err != nil { return WrapErrorISE(err, "error unmarshalling JSON") } @@ -362,7 +374,7 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose return WrapErrorISE(err, "error base64 decoding attObj") } - att := AttestationObject{} + att := attestationObject{} if err := cbor.Unmarshal(attObj, &att); err != nil { return WrapErrorISE(err, "error unmarshalling CBOR") } @@ -386,7 +398,6 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose } return WrapErrorISE(err, "error validating attestation") } - // Validate nonce with SHA-256 of the token. if len(data.Nonce) != 0 { sum := sha256.Sum256([]byte(ch.Token)) @@ -402,6 +413,9 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose if data.UDID != ch.Value && data.SerialNumber != ch.Value { return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match")) } + + // Update attestation key fingerprint to compare against the CSR + az.Fingerprint = data.Fingerprint case "step": data, err := doStepAttestationFormat(ctx, prov, ch, jwk, &att) if err != nil { @@ -415,13 +429,53 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose return WrapErrorISE(err, "error validating attestation") } - // Validate Apple's ClientIdentifier (Identifier.Value) with device - // identifiers. + // Validate the YubiKey serial number from the attestation + // certificate with the challenged Order value. // // Note: We might want to use an external service for this. if data.SerialNumber != ch.Value { - return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match")) + subproblem := NewSubproblemWithIdentifier( + ErrorMalformedType, + Identifier{Type: "permanent-identifier", Value: ch.Value}, + "challenge identifier %q doesn't match the attested hardware identifier %q", ch.Value, data.SerialNumber, + ) + return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "permanent identifier does not match").AddSubproblems(subproblem)) } + + // Update attestation key fingerprint to compare against the CSR + az.Fingerprint = data.Fingerprint + + case "tpm": + data, err := doTPMAttestationFormat(ctx, prov, ch, jwk, &att) + if err != nil { + // TODO(hs): we should provide more details in the error reported to the client; + // "Attestation statement cannot be verified" is VERY generic. Also holds true for the other formats. + var acmeError *Error + if errors.As(err, &acmeError) { + if acmeError.Status == 500 { + return acmeError + } + return storeError(ctx, db, ch, true, acmeError) + } + return WrapErrorISE(err, "error validating attestation") + } + + // TODO(hs): currently this will allow a request for which no PermanentIdentifiers have been + // extracted from the AK certificate. This is currently the case for AK certs from the CLI, as we + // haven't implemented a way for AK certs requested by the CLI to always contain the requested + // PermanentIdentifier. Omitting the check below doesn't allow just any request, as the Order can + // still fail if the challenge value isn't equal to the CSR subject. + if len(data.PermanentIdentifiers) > 0 && !slices.Contains(data.PermanentIdentifiers, ch.Value) { // TODO(hs): add support for HardwareModuleName + subproblem := NewSubproblemWithIdentifier( + ErrorMalformedType, + Identifier{Type: "permanent-identifier", Value: ch.Value}, + "challenge identifier %q doesn't match any of the attested hardware identifiers %q", ch.Value, data.PermanentIdentifiers, + ) + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "permanent identifier does not match").AddSubproblems(subproblem)) + } + + // Update attestation key fingerprint to compare against the CSR + az.Fingerprint = data.Fingerprint default: return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatementType, "unexpected attestation object format")) } @@ -431,12 +485,316 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose ch.Error = nil ch.ValidatedAt = clock.Now().Format(time.RFC3339) + // Store the fingerprint in the authorization. + // + // TODO: add method to update authorization and challenge atomically. + if az.Fingerprint != "" { + if err := db.UpdateAuthorization(ctx, az); err != nil { + return WrapErrorISE(err, "error updating authorization") + } + } + if err := db.UpdateChallenge(ctx, ch); err != nil { return WrapErrorISE(err, "error updating challenge") } return nil } +var ( + oidSubjectAlternativeName = asn1.ObjectIdentifier{2, 5, 29, 17} +) + +type tpmAttestationData struct { + Certificate *x509.Certificate + VerifiedChains [][]*x509.Certificate + PermanentIdentifiers []string + Fingerprint string +} + +// coseAlgorithmIdentifier models a COSEAlgorithmIdentifier. +// Also see https://www.w3.org/TR/webauthn-2/#sctn-alg-identifier. +type coseAlgorithmIdentifier int32 + +const ( + coseAlgES256 coseAlgorithmIdentifier = -7 + coseAlgRS256 coseAlgorithmIdentifier = -257 +) + +func doTPMAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, jwk *jose.JSONWebKey, att *attestationObject) (*tpmAttestationData, error) { + ver, ok := att.AttStatement["ver"].(string) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "ver not present") + } + if ver != "2.0" { + return nil, NewError(ErrorBadAttestationStatementType, "version %q is not supported", ver) + } + + x5c, ok := att.AttStatement["x5c"].([]interface{}) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "x5c not present") + } + if len(x5c) == 0 { + return nil, NewError(ErrorBadAttestationStatementType, "x5c is empty") + } + + akCertBytes, ok := x5c[0].([]byte) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "x5c is malformed") + } + akCert, err := x509.ParseCertificate(akCertBytes) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "x5c is malformed") + } + + intermediates := x509.NewCertPool() + for _, v := range x5c[1:] { + intCertBytes, vok := v.([]byte) + if !vok { + return nil, NewError(ErrorBadAttestationStatementType, "x5c is malformed") + } + intCert, err := x509.ParseCertificate(intCertBytes) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "x5c is malformed") + } + intermediates.AddCert(intCert) + } + + // TODO(hs): this can be removed when permanent-identifier/hardware-module-name are handled correctly in + // the stdlib in https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/crypto/x509/parser.go;drc=b5b2cf519fe332891c165077f3723ee74932a647;l=362, + // but I doubt that will happen. + if len(akCert.UnhandledCriticalExtensions) > 0 { + unhandledCriticalExtensions := akCert.UnhandledCriticalExtensions[:0] + for _, extOID := range akCert.UnhandledCriticalExtensions { + if !extOID.Equal(oidSubjectAlternativeName) { + // critical extensions other than the Subject Alternative Name remain unhandled + unhandledCriticalExtensions = append(unhandledCriticalExtensions, extOID) + } + } + akCert.UnhandledCriticalExtensions = unhandledCriticalExtensions + } + + roots, ok := prov.GetAttestationRoots() + if !ok { + return nil, NewErrorISE("no root CA bundle available to verify the attestation certificate") + } + + // verify that the AK certificate was signed by a trusted root, + // chained to by the intermediates provided by the client. As part + // of building the verified certificate chain, the signature over the + // AK certificate is checked to be a valid signature of one of the + // provided intermediates. Signatures over the intermediates are in + // turn also verified to be valid signatures from one of the trusted + // roots. + verifiedChains, err := akCert.Verify(x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + CurrentTime: time.Now().Truncate(time.Second), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + }) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "x5c is not valid") + } + + // validate additional AK certificate requirements + if err := validateAKCertificate(akCert); err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "AK certificate is not valid") + } + + // TODO(hs): implement revocation check; Verify() doesn't perform CRL check nor OCSP lookup. + + sans, err := x509util.ParseSubjectAlternativeNames(akCert) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "failed parsing AK certificate Subject Alternative Names") + } + + permanentIdentifiers := make([]string, len(sans.PermanentIdentifiers)) + for i, pi := range sans.PermanentIdentifiers { + permanentIdentifiers[i] = pi.Identifier + } + + // extract and validate pubArea, sig, certInfo and alg properties from the request body + pubArea, ok := att.AttStatement["pubArea"].([]byte) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "invalid pubArea in attestation statement") + } + if len(pubArea) == 0 { + return nil, NewError(ErrorBadAttestationStatementType, "pubArea is empty") + } + + sig, ok := att.AttStatement["sig"].([]byte) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "invalid sig in attestation statement") + } + if len(sig) == 0 { + return nil, NewError(ErrorBadAttestationStatementType, "sig is empty") + } + + certInfo, ok := att.AttStatement["certInfo"].([]byte) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "invalid certInfo in attestation statement") + } + if len(certInfo) == 0 { + return nil, NewError(ErrorBadAttestationStatementType, "certInfo is empty") + } + + alg, ok := att.AttStatement["alg"].(int64) + if !ok { + return nil, NewError(ErrorBadAttestationStatementType, "invalid alg in attestation statement") + } + + // only RS256 and ES256 are allowed + coseAlg := coseAlgorithmIdentifier(alg) + if coseAlg != coseAlgRS256 && coseAlg != coseAlgES256 { + return nil, NewError(ErrorBadAttestationStatementType, "invalid alg %d in attestation statement", alg) + } + + // set the hash algorithm to use to SHA256 + hash := crypto.SHA256 + + // recreate the generated key certification parameter values and verify + // the attested key using the public key of the AK. + certificationParameters := &attest.CertificationParameters{ + Public: pubArea, // the public key that was attested + CreateAttestation: certInfo, // the attested properties of the key + CreateSignature: sig, // signature over the attested properties + } + verifyOpts := attest.VerifyOpts{ + Public: akCert.PublicKey, // public key of the AK that attested the key + Hash: hash, + } + if err = certificationParameters.Verify(verifyOpts); err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "invalid certification parameters") + } + + // decode the "certInfo" data. This won't fail, as it's also done as part of Verify(). + tpmCertInfo, err := tpm2.DecodeAttestationData(certInfo) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "failed decoding attestation data") + } + + keyAuth, err := KeyAuthorization(ch.Token, jwk) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "failed creating key auth digest") + } + hashedKeyAuth := sha256.Sum256([]byte(keyAuth)) + + // verify the WebAuthn object contains the expect key authorization digest, which is carried + // within the encoded `certInfo` property of the attestation statement. + if subtle.ConstantTimeCompare(hashedKeyAuth[:], []byte(tpmCertInfo.ExtraData)) == 0 { + return nil, NewError(ErrorBadAttestationStatementType, "key authorization does not match") + } + + // decode the (attested) public key and determine its fingerprint. This won't fail, as it's also done as part of Verify(). + pub, err := tpm2.DecodePublic(pubArea) + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "failed decoding pubArea") + } + + publicKey, err := pub.Key() + if err != nil { + return nil, WrapError(ErrorBadAttestationStatementType, err, "failed getting public key") + } + + data := &tpmAttestationData{ + Certificate: akCert, + VerifiedChains: verifiedChains, + PermanentIdentifiers: permanentIdentifiers, + } + + if data.Fingerprint, err = keyutil.Fingerprint(publicKey); err != nil { + return nil, WrapErrorISE(err, "error calculating key fingerprint") + } + + // TODO(hs): pass more attestation data, so that that can be used/recorded too? + return data, nil +} + +var ( + oidExtensionExtendedKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37} + oidTCGKpAIKCertificate = asn1.ObjectIdentifier{2, 23, 133, 8, 3} +) + +// validateAKCertifiate validates the X.509 AK certificate to be +// in accordance with the required properties. The requirements come from: +// https://www.w3.org/TR/webauthn-2/#sctn-tpm-cert-requirements. +// +// - Version MUST be set to 3. +// - Subject field MUST be set to empty. +// - The Subject Alternative Name extension MUST be set as defined +// in [TPMv2-EK-Profile] section 3.2.9. +// - The Extended Key Usage extension MUST contain the OID 2.23.133.8.3 +// ("joint-iso-itu-t(2) internationalorganizations(23) 133 tcg-kp(8) tcg-kp-AIKCertificate(3)"). +// - The Basic Constraints extension MUST have the CA component set to false. +// - An Authority Information Access (AIA) extension with entry id-ad-ocsp +// and a CRL Distribution Point extension [RFC5280] are both OPTIONAL as +// the status of many attestation certificates is available through metadata +// services. See, for example, the FIDO Metadata Service. +func validateAKCertificate(c *x509.Certificate) error { + if c.Version != 3 { + return fmt.Errorf("AK certificate has invalid version %d; only version 3 is allowed", c.Version) + } + if c.Subject.String() != "" { + return fmt.Errorf("AK certificate subject must be empty; got %q", c.Subject) + } + if c.IsCA { + return errors.New("AK certificate must not be a CA") + } + if err := validateAKCertificateExtendedKeyUsage(c); err != nil { + return err + } + if err := validateAKCertificateSubjectAlternativeNames(c); err != nil { + return err + } + + return nil +} + +// validateAKCertificateSubjectAlternativeNames checks if the AK certificate +// has TPM hardware details set. +func validateAKCertificateSubjectAlternativeNames(c *x509.Certificate) error { + sans, err := x509util.ParseSubjectAlternativeNames(c) + if err != nil { + return fmt.Errorf("failed parsing AK certificate Subject Alternative Names: %w", err) + } + + details := sans.TPMHardwareDetails + manufacturer, model, version := details.Manufacturer, details.Model, details.Version + + switch { + case manufacturer == "": + return errors.New("missing TPM manufacturer") + case model == "": + return errors.New("missing TPM model") + case version == "": + return errors.New("missing TPM version") + } + + return nil +} + +// validateAKCertificateExtendedKeyUsage checks if the AK certificate +// has the "tcg-kp-AIKCertificate" Extended Key Usage set. +func validateAKCertificateExtendedKeyUsage(c *x509.Certificate) error { + var ( + valid = false + ekus []asn1.ObjectIdentifier + ) + for _, ext := range c.Extensions { + if ext.Id.Equal(oidExtensionExtendedKeyUsage) { + if _, err := asn1.Unmarshal(ext.Value, &ekus); err != nil || !ekus[0].Equal(oidTCGKpAIKCertificate) { + return errors.New("AK certificate is missing Extended Key Usage value tcg-kp-AIKCertificate (2.23.133.8.3)") + } + valid = true + } + } + + if !valid { + return errors.New("AK certificate is missing Extended Key Usage extension") + } + + return nil +} + // Apple Enterprise Attestation Root CA from // https://www.apple.com/certificateauthority/private/ const appleEnterpriseAttestationRootCA = `-----BEGIN CERTIFICATE----- @@ -467,9 +825,10 @@ type appleAttestationData struct { UDID string SEPVersion string Certificate *x509.Certificate + Fingerprint string } -func doAppleAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, att *AttestationObject) (*appleAttestationData, error) { +func doAppleAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, att *attestationObject) (*appleAttestationData, error) { // Use configured or default attestation roots if none is configured. roots, ok := prov.GetAttestationRoots() if !ok { @@ -523,6 +882,9 @@ func doAppleAttestationFormat(ctx context.Context, prov Provisioner, ch *Challen data := &appleAttestationData{ Certificate: leaf, } + if data.Fingerprint, err = keyutil.Fingerprint(leaf.PublicKey); err != nil { + return nil, WrapErrorISE(err, "error calculating key fingerprint") + } for _, ext := range leaf.Extensions { switch { case ext.Id.Equal(oidAppleSerialNumber): @@ -568,9 +930,10 @@ var oidYubicoSerialNumber = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 41482, 3, 7} type stepAttestationData struct { Certificate *x509.Certificate SerialNumber string + Fingerprint string } -func doStepAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, jwk *jose.JSONWebKey, att *AttestationObject) (*stepAttestationData, error) { +func doStepAttestationFormat(ctx context.Context, prov Provisioner, ch *Challenge, jwk *jose.JSONWebKey, att *attestationObject) (*stepAttestationData, error) { // Use configured or default attestation roots if none is configured. roots, ok := prov.GetAttestationRoots() if !ok { @@ -663,6 +1026,9 @@ func doStepAttestationFormat(ctx context.Context, prov Provisioner, ch *Challeng data := &stepAttestationData{ Certificate: leaf, } + if data.Fingerprint, err = keyutil.Fingerprint(leaf.PublicKey); err != nil { + return nil, WrapErrorISE(err, "error calculating key fingerprint") + } for _, ext := range leaf.Extensions { if !ext.Id.Equal(oidYubicoSerialNumber) { continue @@ -726,10 +1092,10 @@ func uitoa(val uint) string { var buf [20]byte // big enough for 64bit value base 10 i := len(buf) - 1 for val >= 10 { - q := val / 10 - buf[i] = byte('0' + val - q*10) + v := val / 10 + buf[i] = byte('0' + val - v*10) i-- - val = q + val = v } // val < 10 buf[i] = byte('0' + val) diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 1aa9f6ab..ff93bea3 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -15,6 +15,7 @@ import ( "encoding/asn1" "encoding/base64" "encoding/hex" + "encoding/json" "encoding/pem" "errors" "fmt" @@ -30,12 +31,14 @@ import ( "time" "github.com/fxamacker/cbor/v2" - "github.com/smallstep/assert" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/minica" + "go.step.sm/crypto/x509util" ) type mockClient struct { @@ -50,6 +53,30 @@ func (m *mockClient) TLSDial(network, addr string, tlsConfig *tls.Config) (*tls. return m.tlsDial(network, addr, tlsConfig) } +func fatalError(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + +func mustNonAttestationProvisioner(t *testing.T) Provisioner { + t.Helper() + + prov := &provisioner.ACME{ + Type: "ACME", + Name: "acme", + Challenges: []provisioner.ACMEChallenge{provisioner.HTTP_01}, + } + if err := prov.Init(provisioner.Config{ + Claims: config.GlobalProvisionerClaims, + }); err != nil { + t.Fatal(err) + } + prov.AttestationFormats = []provisioner.ACMEAttestationFormat{"bogus-format"} // results in no attestation formats enabled + return prov +} + func mustAttestationProvisioner(t *testing.T, roots []byte) Provisioner { t.Helper() @@ -67,6 +94,108 @@ func mustAttestationProvisioner(t *testing.T, roots []byte) Provisioner { return prov } +func mustAccountAndKeyAuthorization(t *testing.T, token string) (*jose.JSONWebKey, string) { + t.Helper() + + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + fatalError(t, err) + + keyAuth, err := KeyAuthorization(token, jwk) + fatalError(t, err) + return jwk, keyAuth +} + +func mustAttestApple(t *testing.T, nonce string) ([]byte, *x509.Certificate, *x509.Certificate) { + t.Helper() + + ca, err := minica.New() + fatalError(t, err) + + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + fatalError(t, err) + + nonceSum := sha256.Sum256([]byte(nonce)) + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidAppleSerialNumber, Value: []byte("serial-number")}, + {Id: oidAppleUniqueDeviceIdentifier, Value: []byte("udid")}, + {Id: oidAppleSecureEnclaveProcessorOSVersion, Value: []byte("16.0")}, + {Id: oidAppleNonce, Value: nonceSum[:]}, + }, + }) + fatalError(t, err) + + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "apple", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + }, + }) + fatalError(t, err) + + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + fatalError(t, err) + + return payload, leaf, ca.Root +} + +func mustAttestYubikey(t *testing.T, nonce, keyAuthorization string, serial int) ([]byte, *x509.Certificate, *x509.Certificate) { + ca, err := minica.New() + fatalError(t, err) + + keyAuthSum := sha256.Sum256([]byte(keyAuthorization)) + + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + fatalError(t, err) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + fatalError(t, err) + cborSig, err := cbor.Marshal(sig) + fatalError(t, err) + + serialNumber, err := asn1.Marshal(serial) + fatalError(t, err) + + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + fatalError(t, err) + + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + fatalError(t, err) + + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + fatalError(t, err) + + return payload, leaf, ca.Root +} + func Test_storeError(t *testing.T) { type test struct { ch *Challenge @@ -87,16 +216,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) return errors.New("force") }, }, @@ -114,16 +244,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) return NewError(ErrorMalformedType, "bar") }, }, @@ -141,16 +272,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) return nil }, }, @@ -167,16 +299,17 @@ func Test_storeError(t *testing.T) { ch: ch, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusInvalid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusInvalid, updch.Status) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) return nil }, }, @@ -188,16 +321,15 @@ func Test_storeError(t *testing.T) { t.Run(name, func(t *testing.T) { tc := run(t) if err := storeError(context.Background(), tc.db, tc.ch, tc.markInvalid, err); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -217,7 +349,7 @@ func TestKeyAuthorization(t *testing.T) { tests := map[string]func(t *testing.T) test{ "fail/jwk-thumbprint-error": func(t *testing.T) test { jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) jwk.Key = "foo" return test{ token: "1234", @@ -228,9 +360,9 @@ func TestKeyAuthorization(t *testing.T) { "ok": func(t *testing.T) test { token := "1234" jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) thumbprint, err := jwk.Thumbprint(crypto.SHA256) - assert.FatalError(t, err) + require.NoError(t, err) encPrint := base64.RawURLEncoding.EncodeToString(thumbprint) return test{ token: token, @@ -243,21 +375,20 @@ func TestKeyAuthorization(t *testing.T) { t.Run(name, func(t *testing.T) { tc := run(t) if ka, err := KeyAuthorization(tc.token, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { if assert.Nil(t, tc.err) { - assert.Equals(t, tc.exp, ka) + assert.Equal(t, tc.exp, ka) } } }) @@ -266,12 +397,14 @@ func TestKeyAuthorization(t *testing.T) { func TestChallenge_Validate(t *testing.T) { type test struct { - ch *Challenge - vc Client - jwk *jose.JSONWebKey - db DB - srv *httptest.Server - err *Error + ch *Challenge + vc Client + jwk *jose.JSONWebKey + db DB + srv *httptest.Server + payload []byte + ctx context.Context + err *Error } tests := map[string]func(t *testing.T) test{ "ok/already-valid": func(t *testing.T) test { @@ -318,18 +451,19 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("http-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -354,18 +488,19 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("http-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -395,18 +530,19 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("http-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal:8080/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -430,19 +566,20 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("dns-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -467,19 +604,20 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("dns-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -502,19 +640,20 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -531,14 +670,14 @@ func TestChallenge_Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -550,12 +689,12 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) return nil }, }, @@ -577,14 +716,14 @@ func TestChallenge_Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { @@ -616,12 +755,12 @@ func TestChallenge_Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) return nil }, }, @@ -629,6 +768,91 @@ func TestChallenge_Validate(t *testing.T) { jwk: jwk, } }, + "fail/device-attest-01": func(t *testing.T) test { + payload, err := json.Marshal(struct { + Error string `json:"error"` + }{ + Error: "an error", + }) + assert.NoError(t, err) + return test{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + + return errors.New("force") + }, + }, + err: NewError(ErrorServerInternalType, "failure saving error to acme challenge: force"), + } + }, + "ok/device-attest-01": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, leaf, root := mustAttestYubikey(t, "nonce", keyAuth, 1234) + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + return test{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "1234", + }, + payload: payload, + ctx: ctx, + jwk: jwk, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(leaf.PublicKey) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "1234", updch.Value) + + return nil + }, + }, + } + }, } for name, run := range tests { t.Run(name, func(t *testing.T) { @@ -638,18 +862,21 @@ func TestChallenge_Validate(t *testing.T) { defer tc.srv.Close() } - ctx := NewClientContext(context.Background(), tc.vc) - if err := tc.ch.Validate(ctx, tc.db, tc.jwk, nil); err != nil { - if assert.NotNil(t, tc.err) { + ctx := tc.ctx + if ctx == nil { + ctx = context.Background() + } + ctx = NewClientContext(ctx, tc.vc) + if err := tc.ch.Validate(ctx, tc.db, tc.jwk, tc.payload); err != nil { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -694,17 +921,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -728,17 +956,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -764,17 +993,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -801,17 +1031,18 @@ func TestHTTP01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -846,7 +1077,7 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) jwk.Key = "foo" return test{ ch: ch, @@ -870,10 +1101,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -886,18 +1117,19 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusInvalid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusInvalid, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got foo", expKeyAuth) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -912,10 +1144,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -928,18 +1160,19 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusInvalid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusInvalid, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got foo", expKeyAuth) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -955,10 +1188,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -971,13 +1204,14 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) + va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -997,10 +1231,10 @@ func TestHTTP01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, vc: &mockClient{ @@ -1013,14 +1247,14 @@ func TestHTTP01Validate(t *testing.T) { jwk: jwk, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, "zap.internal", updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1035,16 +1269,15 @@ func TestHTTP01Validate(t *testing.T) { tc := run(t) ctx := NewClientContext(context.Background(), tc.vc) if err := http01Validate(ctx, tc.ch, tc.db, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -1082,18 +1315,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", domain) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1117,18 +1351,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", domain) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1143,7 +1378,7 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) jwk.Key = "foo" return test{ @@ -1166,10 +1401,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, @@ -1180,18 +1415,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1208,10 +1444,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) return test{ ch: ch, @@ -1222,18 +1458,19 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusPending) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusPending, updch.Status) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1249,10 +1486,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) h := sha256.Sum256([]byte(expKeyAuth)) expected := base64.RawURLEncoding.EncodeToString(h[:]) @@ -1265,15 +1502,14 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, ch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1294,10 +1530,10 @@ func TestDNS01Validate(t *testing.T) { } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) h := sha256.Sum256([]byte(expKeyAuth)) expected := base64.RawURLEncoding.EncodeToString(h[:]) @@ -1310,15 +1546,14 @@ func TestDNS01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Status, StatusValid) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, fulldomain, updch.Value) + assert.Equal(t, StatusValid, updch.Status) + assert.Nil(t, updch.Error) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Error, nil) va, err := time.Parse(time.RFC3339, updch.ValidatedAt) - assert.FatalError(t, err) + require.NoError(t, err) now := clock.Now() assert.True(t, va.Add(-time.Minute).Before(now)) assert.True(t, va.Add(time.Minute).After(now)) @@ -1335,16 +1570,15 @@ func TestDNS01Validate(t *testing.T) { tc := run(t) ctx := NewClientContext(context.Background(), tc.vc) if err := dns01Validate(ctx, tc.ch, tc.db, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -1483,19 +1717,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusPending, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1513,19 +1748,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusPending, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1544,19 +1780,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusPending, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) - err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443:", ch.Value) + err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: context deadline exceeded", ch.Value) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) return nil }, }, @@ -1575,19 +1812,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "tls-alpn-01 challenge for %v resulted in no certificates", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1605,19 +1843,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "tls-alpn-01 challenge for %v resulted in no certificates", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1628,7 +1867,7 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) srv := httptest.NewTLSServer(nil) @@ -1641,19 +1880,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1665,7 +1905,7 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) srv := httptest.NewTLSServer(nil) @@ -1678,19 +1918,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1703,14 +1944,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1722,19 +1963,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1746,14 +1988,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1765,19 +2007,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1790,14 +2033,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value, "other.internal") - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1809,19 +2052,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1833,14 +2077,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, "other.internal") - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1852,19 +2096,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address or DNS name, %v", ch.Value) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1876,15 +2121,15 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) jwk.Key = "foo" cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1903,10 +2148,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert(nil, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1918,19 +2163,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -1942,10 +2188,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert(nil, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -1957,19 +2203,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -1982,14 +2229,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, false, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2001,19 +2248,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2025,14 +2273,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, false, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2044,19 +2292,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2069,10 +2318,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert([]byte{1, 2, 3}, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2084,19 +2333,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2108,10 +2358,10 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) cert, err := newTLSALPNValidationCert([]byte{1, 2, 3}, false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2123,19 +2373,20 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2148,15 +2399,15 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) incorrectTokenHash := sha256.Sum256([]byte("mismatched")) cert, err := newTLSALPNValidationCert(incorrectTokenHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2168,21 +2419,22 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "expected acmeValidationV1 extension value %s for this challenge but got %s", hex.EncodeToString(expKeyAuthHash[:]), hex.EncodeToString(incorrectTokenHash[:])) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2194,15 +2446,15 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) incorrectTokenHash := sha256.Sum256([]byte("mismatched")) cert, err := newTLSALPNValidationCert(incorrectTokenHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2214,21 +2466,22 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "expected acmeValidationV1 extension value %s for this challenge but got %s", hex.EncodeToString(expKeyAuthHash[:]), hex.EncodeToString(incorrectTokenHash[:])) - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2241,14 +2494,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], true, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2260,20 +2513,21 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "obsolete id-pe-acmeIdentifier in acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return nil }, }, @@ -2285,14 +2539,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], true, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2304,20 +2558,21 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusInvalid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) err := NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "obsolete id-pe-acmeIdentifier in acmeValidationV1 extension") - assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) - assert.Equals(t, updch.Error.Type, err.Type) - assert.Equals(t, updch.Error.Detail, err.Detail) - assert.Equals(t, updch.Error.Status, err.Status) - assert.Equals(t, updch.Error.Detail, err.Detail) + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + return errors.New("force") }, }, @@ -2330,14 +2585,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch := makeTLSCh() jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2349,12 +2604,13 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "zap.internal", updch.Value) + assert.Nil(t, updch.Error) + return nil }, }, @@ -2367,14 +2623,14 @@ func TestTLSALPN01Validate(t *testing.T) { ch.Value = "127.0.0.1" jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuth, err := KeyAuthorization(ch.Token, jwk) - assert.FatalError(t, err) + require.NoError(t, err) expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth)) cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value) - assert.FatalError(t, err) + require.NoError(t, err) srv, tlsDial := newTestTLSALPNServer(cert) srv.Start() @@ -2386,12 +2642,13 @@ func TestTLSALPN01Validate(t *testing.T) { }, db: &MockDB{ MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { - assert.Equals(t, updch.ID, ch.ID) - assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, StatusValid) - assert.Equals(t, updch.Type, ch.Type) - assert.Equals(t, updch.Value, ch.Value) - assert.Equals(t, updch.Error, nil) + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type) + assert.Equal(t, "127.0.0.1", updch.Value) + assert.Nil(t, updch.Error) + return nil }, }, @@ -2410,16 +2667,16 @@ func TestTLSALPN01Validate(t *testing.T) { ctx := NewClientContext(context.Background(), tc.vc) if err := tlsalpn01Validate(ctx, tc.ch, tc.db, tc.jwk); err != nil { - if assert.NotNil(t, tc.err) { + if assert.Error(t, tc.err) { var k *Error if errors.As(err, &k) { - assert.Equals(t, k.Type, tc.err.Type) - assert.Equals(t, k.Detail, tc.err.Detail) - assert.Equals(t, k.Status, tc.err.Status) - assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) - assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equal(t, tc.err.Type, k.Type) + assert.Equal(t, tc.err.Detail, k.Detail) + assert.Equal(t, tc.err.Status, k.Status) + assert.Equal(t, tc.err.Err.Error(), k.Err.Error()) + assert.Equal(t, tc.err.Subproblems, k.Subproblems) } else { - assert.FatalError(t, errors.New("unexpected error type")) + assert.Fail(t, "unexpected error type") } } } else { @@ -2563,12 +2820,16 @@ func Test_doAppleAttestationFormat(t *testing.T) { if err != nil { t.Fatal(err) } + fingerprint, err := keyutil.Fingerprint(signer.Public()) + if err != nil { + t.Fatal(err) + } type args struct { ctx context.Context prov Provisioner ch *Challenge - att *AttestationObject + att *attestationObject } tests := []struct { name string @@ -2576,7 +2837,7 @@ func Test_doAppleAttestationFormat(t *testing.T) { want *appleAttestationData wantErr bool }{ - {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2587,50 +2848,51 @@ func Test_doAppleAttestationFormat(t *testing.T) { UDID: "udid", SEPVersion: "16.0", Certificate: leaf, + Fingerprint: fingerprint, }, false}, - {"fail apple issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{}, &AttestationObject{ + {"fail apple issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, }, }}, nil, true}, - {"fail missing x5c", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail missing x5c", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "foo": "bar", }, }}, nil, true}, - {"fail empty issuer", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail empty issuer", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{}, }, }}, nil, true}, - {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{"leaf", ca.Intermediate.Raw}, }, }}, nil, true}, - {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw[:100], ca.Intermediate.Raw}, }, }}, nil, true}, - {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, "intermediate"}, }, }}, nil, true}, - {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw[:100]}, }, }}, nil, true}, - {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &AttestationObject{ + {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{}, &attestationObject{ Format: "apple", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw}, @@ -2689,6 +2951,10 @@ func Test_doStepAttestationFormat(t *testing.T) { t.Fatal(err) } leaf := makeLeaf(signer, serialNumber) + fingerprint, err := keyutil.Fingerprint(signer.Public()) + if err != nil { + t.Fatal(err) + } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) if err != nil { @@ -2726,7 +2992,7 @@ func Test_doStepAttestationFormat(t *testing.T) { prov Provisioner ch *Challenge jwk *jose.JSONWebKey - att *AttestationObject + att *attestationObject } tests := []struct { name string @@ -2734,7 +3000,7 @@ func Test_doStepAttestationFormat(t *testing.T) { want *stepAttestationData wantErr bool }{ - {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"ok", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2744,8 +3010,9 @@ func Test_doStepAttestationFormat(t *testing.T) { }}, &stepAttestationData{ SerialNumber: "1234", Certificate: leaf, + Fingerprint: fingerprint, }, false}, - {"fail yubico issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail yubico issuer", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2753,7 +3020,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail x5c type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail x5c type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": [][]byte{leaf.Raw, ca.Intermediate.Raw}, @@ -2761,7 +3028,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail x5c empty", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail x5c empty", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{}, @@ -2769,7 +3036,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail leaf type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{"leaf", ca.Intermediate.Raw}, @@ -2777,7 +3044,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw[:100], ca.Intermediate.Raw}, @@ -2785,7 +3052,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, "intermediate"}, @@ -2793,7 +3060,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw[:100]}, @@ -2801,7 +3068,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail verify", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw}, @@ -2809,7 +3076,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig type", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2817,7 +3084,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": string(cborSig), }, }}, nil, true}, - {"fail sig unmarshal", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig unmarshal", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2825,7 +3092,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": []byte("bad-sig"), }, }}, nil, true}, - {"fail keyAuthorization", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, &jose.JSONWebKey{Key: []byte("not an asymmetric key")}, &AttestationObject{ + {"fail keyAuthorization", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, &jose.JSONWebKey{Key: []byte("not an asymmetric key")}, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2833,7 +3100,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig verify P-256", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify P-256", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2841,7 +3108,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": otherCBORSig, }, }}, nil, true}, - {"fail sig verify P-384", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify P-384", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(mustSigner("EC", "P-384", 0), serialNumber).Raw, ca.Intermediate.Raw}, @@ -2849,7 +3116,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig verify RSA", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify RSA", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(mustSigner("RSA", "", 2048), serialNumber).Raw, ca.Intermediate.Raw}, @@ -2857,7 +3124,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail sig verify Ed25519", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail sig verify Ed25519", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(mustSigner("OKP", "Ed25519", 0), serialNumber).Raw, ca.Intermediate.Raw}, @@ -2865,7 +3132,7 @@ func Test_doStepAttestationFormat(t *testing.T) { "sig": cborSig, }, }}, nil, true}, - {"fail unmarshal serial number", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail unmarshal serial number", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{makeLeaf(signer, []byte("bad-serial")).Raw, ca.Intermediate.Raw}, @@ -2951,7 +3218,7 @@ func Test_doStepAttestationFormat_noCAIntermediate(t *testing.T) { prov Provisioner ch *Challenge jwk *jose.JSONWebKey - att *AttestationObject + att *attestationObject } tests := []struct { name string @@ -2959,7 +3226,7 @@ func Test_doStepAttestationFormat_noCAIntermediate(t *testing.T) { want *stepAttestationData wantErr bool }{ - {"fail no intermediate", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &AttestationObject{ + {"fail no intermediate", args{ctx, mustAttestationProvisioner(t, caRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ Format: "step", AttStatement: map[string]interface{}{ "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, @@ -2981,3 +3248,1050 @@ func Test_doStepAttestationFormat_noCAIntermediate(t *testing.T) { }) } } + +func Test_deviceAttest01Validate(t *testing.T) { + invalidPayload := "!?" + errorPayload, err := json.Marshal(struct { + Error string `json:"error"` + }{ + Error: "an error", + }) + require.NoError(t, err) + errorBase64Payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: "?!", + }) + require.NoError(t, err) + errorCBORPayload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: "AAAA", + }) + require.NoError(t, err) + type args struct { + ctx context.Context + ch *Challenge + db DB + jwk *jose.JSONWebKey + payload []byte + } + type test struct { + args args + wantErr *Error + } + tests := map[string]func(t *testing.T) test{ + "fail/getAuthorization": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return nil, errors.New("not found") + }, + }, + payload: []byte(invalidPayload), + }, + wantErr: NewErrorISE("error loading authorization: not found"), + } + }, + "fail/json.Unmarshal": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + }, + payload: []byte(invalidPayload), + }, + wantErr: NewErrorISE("error unmarshalling JSON: invalid character '!' looking for beginning of value"), + } + + }, + "fail/storeError": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: errorPayload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return errors.New("force") + }, + }, + }, + wantErr: NewErrorISE("failure saving error to acme challenge: force"), + } + }, + "ok/storeError-return-nil": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: errorPayload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, "payload contained error: an error") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "fail/base64-decode": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + }, + payload: errorBase64Payload, + }, + wantErr: NewErrorISE("error base64 decoding attObj: illegal base64 data at input byte 0"), + } + }, + "fail/cbor.Unmarshal": func(t *testing.T) test { + return test{ + args: args{ + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + }, + payload: errorCBORPayload, + }, + wantErr: NewErrorISE("error unmarshalling CBOR: cbor: cannot unmarshal positive integer into Go value of type acme.attestationObject"), + } + }, + "ok/prov.IsAttestationFormatEnabled": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, _, _ := mustAttestYubikey(t, "nonce", keyAuth, 12345678) + ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t)) + + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "attestation format %q is not enabled", "step") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doAppleAttestationFormat-storeError": func(t *testing.T) test { + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, nil)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "apple", + AttStatement: map[string]interface{}{}, + }) + require.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + require.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "x5c not present") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doAppleAttestationFormat-non-matching-nonce": func(t *testing.T) test { + jwk, _ := mustAccountAndKeyAuthorization(t, "token") + payload, _, root := mustAttestApple(t, "bad-nonce") + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "serial-number", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "serial-number", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "challenge token does not match") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doAppleAttestationFormat-non-matching-challenge-value": func(t *testing.T) test { + jwk, _ := mustAccountAndKeyAuthorization(t, "token") + payload, _, root := mustAttestApple(t, "nonce") + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "nonce", + Type: "device-attest-01", + Status: StatusPending, + Value: "non-matching-value", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "nonce", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "non-matching-value", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doStepAttestationFormat-storeError": func(t *testing.T) test { + ca, err := minica.New() + require.NoError(t, err) + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.Root.Raw}) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + require.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + require.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + require.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + require.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "step", + AttStatement: map[string]interface{}{ + "alg": -7, + "sig": cborSig, + }, + }) + require.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + require.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "x5c not present") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/doStepAttestationFormat-non-matching-identifier": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, leaf, root := mustAttestYubikey(t, "nonce", keyAuth, 87654321) + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(leaf.PublicKey) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "permanent identifier does not match"). + AddSubproblems(NewSubproblemWithIdentifier( + ErrorMalformedType, + Identifier{Type: "permanent-identifier", Value: "12345678"}, + "challenge identifier \"12345678\" doesn't match the attested hardware identifier \"87654321\"", + )) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok/unknown-attestation-format": func(t *testing.T) test { + ca, err := minica.New() + require.NoError(t, err) + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + require.NoError(t, err) + token := "token" + keyAuth, err := KeyAuthorization(token, jwk) + require.NoError(t, err) + keyAuthSum := sha256.Sum256([]byte(keyAuth)) + sig, err := signer.Sign(rand.Reader, keyAuthSum[:], crypto.SHA256) + require.NoError(t, err) + cborSig, err := cbor.Marshal(sig) + require.NoError(t, err) + ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t)) + makeLeaf := func(signer crypto.Signer, serialNumber []byte) *x509.Certificate { + leaf, err := ca.Sign(&x509.Certificate{ + Subject: pkix.Name{CommonName: "attestation cert"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + {Id: oidYubicoSerialNumber, Value: serialNumber}, + }, + }) + if err != nil { + t.Fatal(err) + } + return leaf + } + require.NoError(t, err) + serialNumber, err := asn1.Marshal(87654321) + require.NoError(t, err) + leaf := makeLeaf(signer, serialNumber) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "bogus-format", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{leaf.Raw, ca.Intermediate.Raw}, + "alg": -7, + "sig": cborSig, + }, + }) + require.NoError(t, err) + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + require.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, "unexpected attestation object format") + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + jwk: jwk, + }, + wantErr: nil, + } + }, + "fail/db.UpdateAuthorization": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, leaf, root := mustAttestYubikey(t, "nonce", keyAuth, 12345678) + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(leaf.PublicKey) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return errors.New("force") + }, + }, + }, + wantErr: NewError(ErrorServerInternalType, "error updating authorization: force"), + } + }, + "fail/db.UpdateChallenge": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, leaf, root := mustAttestYubikey(t, "nonce", keyAuth, 12345678) + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(leaf.PublicKey) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + return errors.New("force") + }, + }, + }, + wantErr: NewError(ErrorServerInternalType, "error updating challenge: force"), + } + }, + "ok": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, leaf, root := mustAttestYubikey(t, "nonce", keyAuth, 12345678) + + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(leaf.PublicKey) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "12345678", updch.Value) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + } + for name, run := range tests { + t.Run(name, func(t *testing.T) { + tc := run(t) + + if err := deviceAttest01Validate(tc.args.ctx, tc.args.ch, tc.args.db, tc.args.jwk, tc.args.payload); err != nil { + assert.Error(t, tc.wantErr) + assert.EqualError(t, err, tc.wantErr.Error()) + return + } + + assert.Nil(t, tc.wantErr) + }) + } +} + +var ( + oidTPMManufacturer = asn1.ObjectIdentifier{2, 23, 133, 2, 1} + oidTPMModel = asn1.ObjectIdentifier{2, 23, 133, 2, 2} + oidTPMVersion = asn1.ObjectIdentifier{2, 23, 133, 2, 3} +) + +func generateValidAKCertificate(t *testing.T) *x509.Certificate { + t.Helper() + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + template := &x509.Certificate{ + PublicKey: signer.Public(), + Version: 3, + IsCA: false, + UnknownExtKeyUsage: []asn1.ObjectIdentifier{oidTCGKpAIKCertificate}, + } + asn1Value := []byte(fmt.Sprintf(`{"extraNames":[{"type": %q, "value": %q},{"type": %q, "value": %q},{"type": %q, "value": %q}]}`, oidTPMManufacturer, "1414747215", oidTPMModel, "SLB 9670 TPM2.0", oidTPMVersion, "7.55")) + sans := []x509util.SubjectAlternativeName{ + {Type: x509util.DirectoryNameType, + ASN1Value: asn1Value}, + } + ext, err := createSubjectAltNameExtension(nil, nil, nil, nil, sans, true) + require.NoError(t, err) + ext.Set(template) + ca, err := minica.New() + require.NoError(t, err) + cert, err := ca.Sign(template) + require.NoError(t, err) + + return cert +} + +func Test_validateAKCertificate(t *testing.T) { + cert := generateValidAKCertificate(t) + tests := []struct { + name string + c *x509.Certificate + expErr error + }{ + { + name: "ok", + c: cert, + expErr: nil, + }, + { + name: "fail/version", + c: &x509.Certificate{ + Version: 1, + }, + expErr: errors.New("AK certificate has invalid version 1; only version 3 is allowed"), + }, + { + name: "fail/subject", + c: &x509.Certificate{ + Version: 3, + Subject: pkix.Name{CommonName: "fail!"}, + }, + expErr: errors.New(`AK certificate subject must be empty; got "CN=fail!"`), + }, + { + name: "fail/isCA", + c: &x509.Certificate{ + Version: 3, + IsCA: true, + }, + expErr: errors.New("AK certificate must not be a CA"), + }, + { + name: "fail/extendedKeyUsage", + c: &x509.Certificate{ + Version: 3, + }, + expErr: errors.New("AK certificate is missing Extended Key Usage extension"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateAKCertificate(tt.c) + if tt.expErr != nil { + if assert.Error(t, err) { + assert.EqualError(t, err, tt.expErr.Error()) + } + return + } + + assert.NoError(t, err) + }) + } +} + +func Test_validateAKCertificateSubjectAlternativeNames(t *testing.T) { + ok := generateValidAKCertificate(t) + t.Helper() + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + getBase := func() *x509.Certificate { + return &x509.Certificate{ + PublicKey: signer.Public(), + Version: 3, + IsCA: false, + UnknownExtKeyUsage: []asn1.ObjectIdentifier{oidTCGKpAIKCertificate}, + } + } + + ca, err := minica.New() + require.NoError(t, err) + missingManufacturerASN1 := []byte(fmt.Sprintf(`{"extraNames":[{"type": %q, "value": %q},{"type": %q, "value": %q}]}`, oidTPMModel, "SLB 9670 TPM2.0", oidTPMVersion, "7.55")) + sans := []x509util.SubjectAlternativeName{ + {Type: x509util.DirectoryNameType, + ASN1Value: missingManufacturerASN1}, + } + ext, err := createSubjectAltNameExtension(nil, nil, nil, nil, sans, true) + require.NoError(t, err) + missingManufacturer := getBase() + ext.Set(missingManufacturer) + + missingManufacturer, err = ca.Sign(missingManufacturer) + require.NoError(t, err) + + missingModelASN1 := []byte(fmt.Sprintf(`{"extraNames":[{"type": %q, "value": %q},{"type": %q, "value": %q}]}`, oidTPMManufacturer, "1414747215", oidTPMVersion, "7.55")) + sans = []x509util.SubjectAlternativeName{ + {Type: x509util.DirectoryNameType, + ASN1Value: missingModelASN1}, + } + ext, err = createSubjectAltNameExtension(nil, nil, nil, nil, sans, true) + require.NoError(t, err) + missingModel := getBase() + ext.Set(missingModel) + + missingModel, err = ca.Sign(missingModel) + require.NoError(t, err) + + missingFirmwareVersionASN1 := []byte(fmt.Sprintf(`{"extraNames":[{"type": %q, "value": %q},{"type": %q, "value": %q}]}`, oidTPMManufacturer, "1414747215", oidTPMModel, "SLB 9670 TPM2.0")) + sans = []x509util.SubjectAlternativeName{ + {Type: x509util.DirectoryNameType, + ASN1Value: missingFirmwareVersionASN1}, + } + ext, err = createSubjectAltNameExtension(nil, nil, nil, nil, sans, true) + require.NoError(t, err) + missingFirmwareVersion := getBase() + ext.Set(missingFirmwareVersion) + + missingFirmwareVersion, err = ca.Sign(missingFirmwareVersion) + require.NoError(t, err) + + tests := []struct { + name string + c *x509.Certificate + expErr error + }{ + {"ok", ok, nil}, + {"fail/missing-manufacturer", missingManufacturer, errors.New("missing TPM manufacturer")}, + {"fail/missing-model", missingModel, errors.New("missing TPM model")}, + {"fail/missing-firmware-version", missingFirmwareVersion, errors.New("missing TPM version")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateAKCertificateSubjectAlternativeNames(tt.c) + if tt.expErr != nil { + if assert.Error(t, err) { + assert.EqualError(t, err, tt.expErr.Error()) + } + return + } + + assert.NoError(t, err) + }) + } +} + +func Test_validateAKCertificateExtendedKeyUsage(t *testing.T) { + ok := generateValidAKCertificate(t) + missingEKU := &x509.Certificate{} + t.Helper() + signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + template := &x509.Certificate{ + PublicKey: signer.Public(), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + ca, err := minica.New() + require.NoError(t, err) + wrongEKU, err := ca.Sign(template) + require.NoError(t, err) + tests := []struct { + name string + c *x509.Certificate + expErr error + }{ + {"ok", ok, nil}, + {"fail/wrong-eku", wrongEKU, errors.New("AK certificate is missing Extended Key Usage value tcg-kp-AIKCertificate (2.23.133.8.3)")}, + {"fail/missing-eku", missingEKU, errors.New("AK certificate is missing Extended Key Usage extension")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateAKCertificateExtendedKeyUsage(tt.c) + if tt.expErr != nil { + if assert.Error(t, err) { + assert.EqualError(t, err, tt.expErr.Error()) + } + return + } + + assert.NoError(t, err) + }) + } +} + +// createSubjectAltNameExtension will construct an Extension containing all +// SubjectAlternativeNames held in a Certificate. It implements more types than +// the golang x509 library, so it is used whenever OtherName or RegisteredID +// type SANs are present in the certificate. +// +// See also https://datatracker.ietf.org/doc/html/rfc5280.html#section-4.2.1.6 +// +// TODO(hs): this was copied from go.step.sm/crypto/x509util to make it easier +// to create the SAN extension for testing purposes. Should it be exposed instead? +func createSubjectAltNameExtension(dnsNames, emailAddresses x509util.MultiString, ipAddresses x509util.MultiIP, uris x509util.MultiURL, sans []x509util.SubjectAlternativeName, subjectIsEmpty bool) (x509util.Extension, error) { + var zero x509util.Extension + + var rawValues []asn1.RawValue + for _, dnsName := range dnsNames { + rawValue, err := x509util.SubjectAlternativeName{ + Type: x509util.DNSType, Value: dnsName, + }.RawValue() + if err != nil { + return zero, err + } + + rawValues = append(rawValues, rawValue) + } + + for _, emailAddress := range emailAddresses { + rawValue, err := x509util.SubjectAlternativeName{ + Type: x509util.EmailType, Value: emailAddress, + }.RawValue() + if err != nil { + return zero, err + } + + rawValues = append(rawValues, rawValue) + } + + for _, ip := range ipAddresses { + rawValue, err := x509util.SubjectAlternativeName{ + Type: x509util.IPType, Value: ip.String(), + }.RawValue() + if err != nil { + return zero, err + } + + rawValues = append(rawValues, rawValue) + } + + for _, uri := range uris { + rawValue, err := x509util.SubjectAlternativeName{ + Type: x509util.URIType, Value: uri.String(), + }.RawValue() + if err != nil { + return zero, err + } + + rawValues = append(rawValues, rawValue) + } + + for _, san := range sans { + rawValue, err := san.RawValue() + if err != nil { + return zero, err + } + + rawValues = append(rawValues, rawValue) + } + + // Now marshal the rawValues into the ASN1 sequence, and create an Extension object to hold the extension + rawBytes, err := asn1.Marshal(rawValues) + if err != nil { + return zero, fmt.Errorf("error marshaling SubjectAlternativeName extension to ASN1: %w", err) + } + + return x509util.Extension{ + ID: x509util.ObjectIdentifier(oidSubjectAlternativeName), + Critical: subjectIsEmpty, + Value: rawBytes, + }, nil +} diff --git a/acme/challenge_tpmsimulator_test.go b/acme/challenge_tpmsimulator_test.go new file mode 100644 index 00000000..dbd63226 --- /dev/null +++ b/acme/challenge_tpmsimulator_test.go @@ -0,0 +1,859 @@ +//go:build tpmsimulator +// +build tpmsimulator + +package acme + +import ( + "context" + "crypto" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "net/url" + "testing" + + "github.com/fxamacker/cbor/v2" + "github.com/google/go-attestation/attest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.step.sm/crypto/jose" + "go.step.sm/crypto/keyutil" + "go.step.sm/crypto/minica" + "go.step.sm/crypto/tpm" + "go.step.sm/crypto/tpm/simulator" + tpmstorage "go.step.sm/crypto/tpm/storage" + "go.step.sm/crypto/x509util" +) + +func newSimulatedTPM(t *testing.T) *tpm.TPM { + t.Helper() + tmpDir := t.TempDir() + tpm, err := tpm.New(withSimulator(t), tpm.WithStore(tpmstorage.NewDirstore(tmpDir))) // TODO: provide in-memory storage implementation instead + require.NoError(t, err) + return tpm +} + +func withSimulator(t *testing.T) tpm.NewTPMOption { + t.Helper() + var sim simulator.Simulator + t.Cleanup(func() { + if sim == nil { + return + } + err := sim.Close() + require.NoError(t, err) + }) + sim = simulator.New() + err := sim.Open() + require.NoError(t, err) + return tpm.WithSimulator(sim) +} + +func generateKeyID(t *testing.T, pub crypto.PublicKey) []byte { + t.Helper() + b, err := x509.MarshalPKIXPublicKey(pub) + require.NoError(t, err) + hash := sha256.Sum256(b) + return hash[:] +} + +func mustAttestTPM(t *testing.T, keyAuthorization string, permanentIdentifiers []string) ([]byte, crypto.Signer, *x509.Certificate) { + t.Helper() + aca, err := minica.New( + minica.WithName("TPM Testing"), + minica.WithGetSignerFunc( + func() (crypto.Signer, error) { + return keyutil.GenerateSigner("RSA", "", 2048) + }, + ), + ) + require.NoError(t, err) + + // prepare simulated TPM and create an AK + stpm := newSimulatedTPM(t) + eks, err := stpm.GetEKs(context.Background()) + require.NoError(t, err) + ak, err := stpm.CreateAK(context.Background(), "first-ak") + require.NoError(t, err) + require.NotNil(t, ak) + + // extract the AK public key // TODO(hs): replace this when there's a simpler method to get the AK public key (e.g. ak.Public()) + ap, err := ak.AttestationParameters(context.Background()) + require.NoError(t, err) + akp, err := attest.ParseAKPublic(attest.TPMVersion20, ap.Public) + require.NoError(t, err) + + // create template and sign certificate for the AK public key + keyID := generateKeyID(t, eks[0].Public()) + template := &x509.Certificate{ + PublicKey: akp.Public, + IsCA: false, + UnknownExtKeyUsage: []asn1.ObjectIdentifier{oidTCGKpAIKCertificate}, + } + sans := []x509util.SubjectAlternativeName{} + uris := []*url.URL{{Scheme: "urn", Opaque: "ek:sha256:" + base64.StdEncoding.EncodeToString(keyID)}} + for _, pi := range permanentIdentifiers { + sans = append(sans, x509util.SubjectAlternativeName{ + Type: x509util.PermanentIdentifierType, + Value: pi, + }) + } + asn1Value := []byte(fmt.Sprintf(`{"extraNames":[{"type": %q, "value": %q},{"type": %q, "value": %q},{"type": %q, "value": %q}]}`, oidTPMManufacturer, "1414747215", oidTPMModel, "SLB 9670 TPM2.0", oidTPMVersion, "7.55")) + sans = append(sans, x509util.SubjectAlternativeName{ + Type: x509util.DirectoryNameType, + ASN1Value: asn1Value, + }) + ext, err := createSubjectAltNameExtension(nil, nil, nil, uris, sans, true) + require.NoError(t, err) + ext.Set(template) + akCert, err := aca.Sign(template) + require.NoError(t, err) + require.NotNil(t, akCert) + + // create a new key attested by the AK, while including + // the key authorization bytes as qualifying data. + keyAuthSum := sha256.Sum256([]byte(keyAuthorization)) + config := tpm.AttestKeyConfig{ + Algorithm: "RSA", + Size: 2048, + QualifyingData: keyAuthSum[:], + } + key, err := stpm.AttestKey(context.Background(), "first-ak", "first-key", config) + require.NoError(t, err) + require.NotNil(t, key) + require.Equal(t, "first-key", key.Name()) + require.NotEqual(t, 0, len(key.Data())) + require.Equal(t, "first-ak", key.AttestedBy()) + require.True(t, key.WasAttested()) + require.True(t, key.WasAttestedBy(ak)) + + signer, err := key.Signer(context.Background()) + require.NoError(t, err) + + // prepare the attestation object with the AK certificate chain, + // the attested key, its metadata and the signature signed by the + // AK. + params, err := key.CertificationParameters(context.Background()) + require.NoError(t, err) + attObj, err := cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }) + require.NoError(t, err) + + // marshal the ACME payload + payload, err := json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + require.NoError(t, err) + + return payload, signer, aca.Root +} + +func Test_deviceAttest01ValidateWithTPMSimulator(t *testing.T) { + type args struct { + ctx context.Context + ch *Challenge + db DB + jwk *jose.JSONWebKey + payload []byte + } + type test struct { + args args + wantErr *Error + } + tests := map[string]func(t *testing.T) test{ + "ok/doTPMAttestationFormat-storeError": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, _, root := mustAttestTPM(t, keyAuth, nil) // TODO: value(s) for AK cert? + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + + // parse payload, set invalid "ver", remarshal + var p payloadType + err := json.Unmarshal(payload, &p) + require.NoError(t, err) + attObj, err := base64.RawURLEncoding.DecodeString(p.AttObj) + require.NoError(t, err) + att := attestationObject{} + err = cbor.Unmarshal(attObj, &att) + require.NoError(t, err) + att.AttStatement["ver"] = "bogus" + attObj, err = cbor.Marshal(struct { + Format string `json:"fmt"` + AttStatement map[string]interface{} `json:"attStmt,omitempty"` + }{ + Format: "tpm", + AttStatement: att.AttStatement, + }) + require.NoError(t, err) + payload, err = json.Marshal(struct { + AttObj string `json:"attObj"` + }{ + AttObj: base64.RawURLEncoding.EncodeToString(attObj), + }) + require.NoError(t, err) + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "device.id.12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "device.id.12345678", updch.Value) + + err := NewError(ErrorBadAttestationStatementType, `version "bogus" is not supported`) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok with invalid PermanentIdentifier SAN": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, _, root := mustAttestTPM(t, keyAuth, []string{"device.id.12345678"}) // TODO: value(s) for AK cert? + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "device.id.99999999", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusInvalid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "device.id.99999999", updch.Value) + + err := NewError(ErrorRejectedIdentifierType, `permanent identifier does not match`). + AddSubproblems(NewSubproblemWithIdentifier( + ErrorMalformedType, + Identifier{Type: "permanent-identifier", Value: "device.id.99999999"}, + `challenge identifier "device.id.99999999" doesn't match any of the attested hardware identifiers ["device.id.12345678"]`, + )) + + assert.EqualError(t, updch.Error.Err, err.Err.Error()) + assert.Equal(t, err.Type, updch.Error.Type) + assert.Equal(t, err.Detail, updch.Error.Detail) + assert.Equal(t, err.Status, updch.Error.Status) + assert.Equal(t, err.Subproblems, updch.Error.Subproblems) + + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, signer, root := mustAttestTPM(t, keyAuth, nil) // TODO: value(s) for AK cert? + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "device.id.12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(signer.Public()) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "device.id.12345678", updch.Value) + return nil + }, + }, + }, + wantErr: nil, + } + }, + "ok with PermanentIdentifier SAN": func(t *testing.T) test { + jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") + payload, signer, root := mustAttestTPM(t, keyAuth, []string{"device.id.12345678"}) // TODO: value(s) for AK cert? + caRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: root.Raw}) + ctx := NewProvisionerContext(context.Background(), mustAttestationProvisioner(t, caRoot)) + return test{ + args: args{ + ctx: ctx, + jwk: jwk, + ch: &Challenge{ + ID: "chID", + AuthorizationID: "azID", + Token: "token", + Type: "device-attest-01", + Status: StatusPending, + Value: "device.id.12345678", + }, + payload: payload, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + assert.Equal(t, "azID", id) + return &Authorization{ID: "azID"}, nil + }, + MockUpdateAuthorization: func(ctx context.Context, az *Authorization) error { + fingerprint, err := keyutil.Fingerprint(signer.Public()) + assert.NoError(t, err) + assert.Equal(t, "azID", az.ID) + assert.Equal(t, fingerprint, az.Fingerprint) + return nil + }, + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equal(t, "chID", updch.ID) + assert.Equal(t, "token", updch.Token) + assert.Equal(t, StatusValid, updch.Status) + assert.Equal(t, ChallengeType("device-attest-01"), updch.Type) + assert.Equal(t, "device.id.12345678", updch.Value) + return nil + }, + }, + }, + wantErr: nil, + } + }, + } + for name, run := range tests { + t.Run(name, func(t *testing.T) { + tc := run(t) + + if err := deviceAttest01Validate(tc.args.ctx, tc.args.ch, tc.args.db, tc.args.jwk, tc.args.payload); err != nil { + assert.Error(t, tc.wantErr) + assert.EqualError(t, err, tc.wantErr.Error()) + return + } + + assert.Nil(t, tc.wantErr) + }) + } +} + +func newBadAttestationStatementError(msg string) *Error { + return &Error{ + Type: "urn:ietf:params:acme:error:badAttestationStatement", + Status: 400, + Err: errors.New(msg), + } +} + +func newInternalServerError(msg string) *Error { + return &Error{ + Type: "urn:ietf:params:acme:error:serverInternal", + Status: 500, + Err: errors.New(msg), + } +} + +var ( + oidPermanentIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3} + oidHardwareModuleNameIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 4} +) + +func Test_doTPMAttestationFormat(t *testing.T) { + ctx := context.Background() + aca, err := minica.New( + minica.WithName("TPM Testing"), + minica.WithGetSignerFunc( + func() (crypto.Signer, error) { + return keyutil.GenerateSigner("RSA", "", 2048) + }, + ), + ) + require.NoError(t, err) + acaRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: aca.Root.Raw}) + + // prepare simulated TPM and create an AK + stpm := newSimulatedTPM(t) + eks, err := stpm.GetEKs(context.Background()) + require.NoError(t, err) + ak, err := stpm.CreateAK(context.Background(), "first-ak") + require.NoError(t, err) + require.NotNil(t, ak) + + // extract the AK public key // TODO(hs): replace this when there's a simpler method to get the AK public key (e.g. ak.Public()) + ap, err := ak.AttestationParameters(context.Background()) + require.NoError(t, err) + akp, err := attest.ParseAKPublic(attest.TPMVersion20, ap.Public) + require.NoError(t, err) + + // create template and sign certificate for the AK public key + keyID := generateKeyID(t, eks[0].Public()) + template := &x509.Certificate{ + PublicKey: akp.Public, + IsCA: false, + UnknownExtKeyUsage: []asn1.ObjectIdentifier{oidTCGKpAIKCertificate}, + } + sans := []x509util.SubjectAlternativeName{} + uris := []*url.URL{{Scheme: "urn", Opaque: "ek:sha256:" + base64.StdEncoding.EncodeToString(keyID)}} + asn1Value := []byte(fmt.Sprintf(`{"extraNames":[{"type": %q, "value": %q},{"type": %q, "value": %q},{"type": %q, "value": %q}]}`, oidTPMManufacturer, "1414747215", oidTPMModel, "SLB 9670 TPM2.0", oidTPMVersion, "7.55")) + sans = append(sans, x509util.SubjectAlternativeName{ + Type: x509util.DirectoryNameType, + ASN1Value: asn1Value, + }) + ext, err := createSubjectAltNameExtension(nil, nil, nil, uris, sans, true) + require.NoError(t, err) + ext.Set(template) + akCert, err := aca.Sign(template) + require.NoError(t, err) + require.NotNil(t, akCert) + + invalidTemplate := &x509.Certificate{ + PublicKey: akp.Public, + IsCA: false, + UnknownExtKeyUsage: []asn1.ObjectIdentifier{oidTCGKpAIKCertificate}, + } + invalidAKCert, err := aca.Sign(invalidTemplate) + require.NoError(t, err) + require.NotNil(t, invalidAKCert) + + // generate a JWK and the key authorization value + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + require.NoError(t, err) + keyAuthorization, err := KeyAuthorization("token", jwk) + require.NoError(t, err) + + // create a new key attested by the AK, while including + // the key authorization bytes as qualifying data. + keyAuthSum := sha256.Sum256([]byte(keyAuthorization)) + config := tpm.AttestKeyConfig{ + Algorithm: "RSA", + Size: 2048, + QualifyingData: keyAuthSum[:], + } + key, err := stpm.AttestKey(context.Background(), "first-ak", "first-key", config) + require.NoError(t, err) + require.NotNil(t, key) + params, err := key.CertificationParameters(context.Background()) + require.NoError(t, err) + + signer, err := key.Signer(context.Background()) + require.NoError(t, err) + fingerprint, err := keyutil.Fingerprint(signer.Public()) + require.NoError(t, err) + + // attest another key and get its certification parameters + anotherKey, err := stpm.AttestKey(context.Background(), "first-ak", "another-key", config) + require.NoError(t, err) + require.NotNil(t, key) + anotherKeyParams, err := anotherKey.CertificationParameters(context.Background()) + require.NoError(t, err) + + type args struct { + ctx context.Context + prov Provisioner + ch *Challenge + jwk *jose.JSONWebKey + att *attestationObject + } + tests := []struct { + name string + args args + want *tpmAttestationData + expErr *Error + }{ + {"ok", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, nil}, + {"fail ver not present", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("ver not present")}, + {"fail ver type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": []interface{}{}, + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("ver not present")}, + {"fail bogus ver", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "bogus", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError(`version "bogus" is not supported`)}, + {"fail x5c not present", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c not present")}, + {"fail x5c type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": [][]byte{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c not present")}, + {"fail x5c empty", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c is empty")}, + {"fail leaf type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "step", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{"leaf", aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c is malformed")}, + {"fail leaf parse", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "step", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw[:100], aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c is malformed: x509: malformed certificate")}, + {"fail intermediate type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "step", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, "intermediate"}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c is malformed")}, + {"fail intermediate parse", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "step", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw[:100]}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c is malformed: x509: malformed certificate")}, + {"fail roots", args{ctx, mustAttestationProvisioner(t, nil), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newInternalServerError("no root CA bundle available to verify the attestation certificate")}, + {"fail verify", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "step", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("x5c is not valid: x509: certificate signed by unknown authority")}, + {"fail validateAKCertificate", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{invalidAKCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("AK certificate is not valid: missing TPM manufacturer")}, + {"fail pubArea not present", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + }, + }}, nil, newBadAttestationStatementError("invalid pubArea in attestation statement")}, + {"fail pubArea type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": []interface{}{}, + }, + }}, nil, newBadAttestationStatementError("invalid pubArea in attestation statement")}, + {"fail pubArea empty", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": []byte{}, + }, + }}, nil, newBadAttestationStatementError("pubArea is empty")}, + {"fail sig not present", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("invalid sig in attestation statement")}, + {"fail sig type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": []interface{}{}, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("invalid sig in attestation statement")}, + {"fail sig empty", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": []byte{}, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("sig is empty")}, + {"fail certInfo not present", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("invalid certInfo in attestation statement")}, + {"fail certInfo type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": []interface{}{}, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("invalid certInfo in attestation statement")}, + {"fail certInfo empty", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": []byte{}, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("certInfo is empty")}, + {"fail alg not present", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("invalid alg in attestation statement")}, + {"fail alg type", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(0), // invalid alg + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("invalid alg 0 in attestation statement")}, + {"fail attestation verification", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": anotherKeyParams.Public, + }, + }}, nil, newBadAttestationStatementError("invalid certification parameters: certification refers to a different key")}, + {"fail keyAuthorization", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "token"}, &jose.JSONWebKey{Key: []byte("not an asymmetric key")}, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // RS256 + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newInternalServerError("failed creating key auth digest: error generating JWK thumbprint: square/go-jose: unknown key type '[]uint8'")}, + {"fail different keyAuthorization", args{ctx, mustAttestationProvisioner(t, acaRoot), &Challenge{Token: "aDifferentToken"}, jwk, &attestationObject{ + Format: "tpm", + AttStatement: map[string]interface{}{ + "ver": "2.0", + "x5c": []interface{}{akCert.Raw, aca.Intermediate.Raw}, + "alg": int64(-257), // + "sig": params.CreateSignature, + "certInfo": params.CreateAttestation, + "pubArea": params.Public, + }, + }}, nil, newBadAttestationStatementError("key authorization does not match")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := doTPMAttestationFormat(tt.args.ctx, tt.args.prov, tt.args.ch, tt.args.jwk, tt.args.att) + if tt.expErr != nil { + var ae *Error + if assert.True(t, errors.As(err, &ae)) { + assert.EqualError(t, err, tt.expErr.Error()) + assert.Equal(t, ae.StatusCode(), tt.expErr.StatusCode()) + assert.Equal(t, ae.Type, tt.expErr.Type) + } + assert.Nil(t, got) + return + } + + assert.NoError(t, err) + if assert.NotNil(t, got) { + assert.Equal(t, akCert, got.Certificate) + assert.Equal(t, [][]*x509.Certificate{ + { + akCert, aca.Intermediate, aca.Root, + }, + }, got.VerifiedChains) + assert.Equal(t, fingerprint, got.Fingerprint) + assert.Empty(t, got.PermanentIdentifiers) // currently expected to be always empty + } + }) + } +} diff --git a/acme/db/nosql/authz.go b/acme/db/nosql/authz.go index 01cb7fed..d63aa89e 100644 --- a/acme/db/nosql/authz.go +++ b/acme/db/nosql/authz.go @@ -17,6 +17,7 @@ type dbAuthz struct { Identifier acme.Identifier `json:"identifier"` Status acme.Status `json:"status"` Token string `json:"token"` + Fingerprint string `json:"fingerprint,omitempty"` ChallengeIDs []string `json:"challengeIDs"` Wildcard bool `json:"wildcard"` CreatedAt time.Time `json:"createdAt"` @@ -61,15 +62,16 @@ func (db *DB) GetAuthorization(ctx context.Context, id string) (*acme.Authorizat } } return &acme.Authorization{ - ID: dbaz.ID, - AccountID: dbaz.AccountID, - Identifier: dbaz.Identifier, - Status: dbaz.Status, - Challenges: chs, - Wildcard: dbaz.Wildcard, - ExpiresAt: dbaz.ExpiresAt, - Token: dbaz.Token, - Error: dbaz.Error, + ID: dbaz.ID, + AccountID: dbaz.AccountID, + Identifier: dbaz.Identifier, + Status: dbaz.Status, + Challenges: chs, + Wildcard: dbaz.Wildcard, + ExpiresAt: dbaz.ExpiresAt, + Token: dbaz.Token, + Fingerprint: dbaz.Fingerprint, + Error: dbaz.Error, }, nil } @@ -97,6 +99,7 @@ func (db *DB) CreateAuthorization(ctx context.Context, az *acme.Authorization) e Identifier: az.Identifier, ChallengeIDs: chIDs, Token: az.Token, + Fingerprint: az.Fingerprint, Wildcard: az.Wildcard, } @@ -111,8 +114,8 @@ func (db *DB) UpdateAuthorization(ctx context.Context, az *acme.Authorization) e } nu := old.clone() - nu.Status = az.Status + nu.Fingerprint = az.Fingerprint nu.Error = az.Error return db.save(ctx, old.ID, nu, old, "authz", authzTable) } @@ -136,15 +139,16 @@ func (db *DB) GetAuthorizationsByAccountID(ctx context.Context, accountID string continue } authzs = append(authzs, &acme.Authorization{ - ID: dbaz.ID, - AccountID: dbaz.AccountID, - Identifier: dbaz.Identifier, - Status: dbaz.Status, - Challenges: nil, // challenges not required for current use case - Wildcard: dbaz.Wildcard, - ExpiresAt: dbaz.ExpiresAt, - Token: dbaz.Token, - Error: dbaz.Error, + ID: dbaz.ID, + AccountID: dbaz.AccountID, + Identifier: dbaz.Identifier, + Status: dbaz.Status, + Challenges: nil, // challenges not required for current use case + Wildcard: dbaz.Wildcard, + ExpiresAt: dbaz.ExpiresAt, + Token: dbaz.Token, + Fingerprint: dbaz.Fingerprint, + Error: dbaz.Error, }) } diff --git a/acme/db/nosql/authz_test.go b/acme/db/nosql/authz_test.go index c7d47eda..6bc2b94d 100644 --- a/acme/db/nosql/authz_test.go +++ b/acme/db/nosql/authz_test.go @@ -473,6 +473,7 @@ func TestDB_UpdateAuthorization(t *testing.T) { ExpiresAt: now.Add(5 * time.Minute), ChallengeIDs: []string{"foo", "bar"}, Wildcard: true, + Fingerprint: "fingerprint", } b, err := json.Marshal(dbaz) assert.FatalError(t, err) @@ -549,10 +550,11 @@ func TestDB_UpdateAuthorization(t *testing.T) { {ID: "foo"}, {ID: "bar"}, }, - Token: dbaz.Token, - Wildcard: dbaz.Wildcard, - ExpiresAt: dbaz.ExpiresAt, - Error: acme.NewError(acme.ErrorMalformedType, "malformed"), + Token: dbaz.Token, + Wildcard: dbaz.Wildcard, + ExpiresAt: dbaz.ExpiresAt, + Fingerprint: "fingerprint", + Error: acme.NewError(acme.ErrorMalformedType, "malformed"), } return test{ az: updAz, @@ -582,6 +584,7 @@ func TestDB_UpdateAuthorization(t *testing.T) { assert.Equals(t, dbNew.Wildcard, dbaz.Wildcard) assert.Equals(t, dbNew.CreatedAt, dbaz.CreatedAt) assert.Equals(t, dbNew.ExpiresAt, dbaz.ExpiresAt) + assert.Equals(t, dbNew.Fingerprint, dbaz.Fingerprint) assert.Equals(t, dbNew.Error.Error(), acme.NewError(acme.ErrorMalformedType, "The request message was malformed").Error()) return nu, true, nil }, diff --git a/acme/db/nosql/challenge.go b/acme/db/nosql/challenge.go index f84a6f4e..c9224574 100644 --- a/acme/db/nosql/challenge.go +++ b/acme/db/nosql/challenge.go @@ -6,8 +6,10 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/acme" + "github.com/smallstep/nosql" + + "github.com/smallstep/certificates/acme" ) type dbChallenge struct { @@ -19,7 +21,7 @@ type dbChallenge struct { Value string `json:"value"` ValidatedAt string `json:"validatedAt"` CreatedAt time.Time `json:"createdAt"` - Error *acme.Error `json:"error"` + Error *acme.Error `json:"error"` // TODO(hs): a bit dangerous; should become db-specific type } func (dbc *dbChallenge) clone() *dbChallenge { diff --git a/acme/errors.go b/acme/errors.go index a969bd96..44f367a0 100644 --- a/acme/errors.go +++ b/acme/errors.go @@ -270,14 +270,34 @@ var ( } ) -// Error represents an ACME +// Error represents an ACME Error type Error struct { - Type string `json:"type"` - Detail string `json:"detail"` - Subproblems []interface{} `json:"subproblems,omitempty"` - Identifier interface{} `json:"identifier,omitempty"` - Err error `json:"-"` - Status int `json:"-"` + Type string `json:"type"` + Detail string `json:"detail"` + Subproblems []Subproblem `json:"subproblems,omitempty"` + Err error `json:"-"` + Status int `json:"-"` +} + +// Subproblem represents an ACME subproblem. It's fairly +// similar to an ACME error, but differs in that it can't +// include subproblems itself, the error is reflected +// in the Detail property and doesn't have a Status. +type Subproblem struct { + Type string `json:"type"` + Detail string `json:"detail"` + // The "identifier" field MUST NOT be present at the top level in ACME + // problem documents. It can only be present in subproblems. + // Subproblems need not all have the same type, and they do not need to + // match the top level type. + Identifier *Identifier `json:"identifier,omitempty"` +} + +// AddSubproblems adds the Subproblems to Error. It +// returns the Error, allowing for fluent addition. +func (e *Error) AddSubproblems(subproblems ...Subproblem) *Error { + e.Subproblems = append(e.Subproblems, subproblems...) + return e } // NewError creates a new Error type. @@ -285,6 +305,26 @@ func NewError(pt ProblemType, msg string, args ...interface{}) *Error { return newError(pt, errors.Errorf(msg, args...)) } +// NewSubproblem creates a new Subproblem. The msg and args +// are used to create a new error, which is set as the Detail, allowing +// for more detailed error messages to be returned to the ACME client. +func NewSubproblem(pt ProblemType, msg string, args ...interface{}) Subproblem { + e := newError(pt, fmt.Errorf(msg, args...)) + s := Subproblem{ + Type: e.Type, + Detail: e.Err.Error(), + } + return s +} + +// NewSubproblemWithIdentifier creates a new Subproblem with a specific ACME +// Identifier. It calls NewSubproblem and sets the Identifier. +func NewSubproblemWithIdentifier(pt ProblemType, identifier Identifier, msg string, args ...interface{}) Subproblem { + s := NewSubproblem(pt, msg, args...) + s.Identifier = &identifier + return s +} + func newError(pt ProblemType, err error) *Error { meta, ok := errorMap[pt] if !ok { diff --git a/acme/order.go b/acme/order.go index 7748df22..8dfcf97a 100644 --- a/acme/order.go +++ b/acme/order.go @@ -3,6 +3,7 @@ package acme import ( "bytes" "context" + "crypto/subtle" "crypto/x509" "encoding/json" "net" @@ -11,6 +12,7 @@ import ( "time" "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/crypto/keyutil" "go.step.sm/crypto/x509util" ) @@ -125,6 +127,27 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error { return nil } +// getKeyFingerprint returns a fingerprint from the list of authorizations. This +// fingerprint is used on the device-attest-01 flow to verify the attestation +// certificate public key with the CSR public key. +// +// There's no point on reading all the authorizations as there will be only one +// for a permanent identifier. +func (o *Order) getAuthorizationFingerprint(ctx context.Context, db DB) (string, error) { + for _, azID := range o.AuthorizationIDs { + az, err := db.GetAuthorization(ctx, azID) + if err != nil { + return "", WrapErrorISE(err, "error getting authorization %q", azID) + } + // There's no point on reading all the authorizations as there will + // be only one for a permanent identifier. + if az.Fingerprint != "" { + return az.Fingerprint, nil + } + } + return "", nil +} + // Finalize signs a certificate if the necessary conditions for Order completion // have been met. // @@ -150,6 +173,24 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques return NewErrorISE("unexpected status %s for order %s", o.Status, o.ID) } + // Get key fingerprint if any. And then compare it with the CSR fingerprint. + // + // In device-attest-01 challenges we should check that the keys in the CSR + // and the attestation certificate are the same. + fingerprint, err := o.getAuthorizationFingerprint(ctx, db) + if err != nil { + return err + } + if fingerprint != "" { + fp, err := keyutil.Fingerprint(csr.PublicKey) + if err != nil { + return WrapErrorISE(err, "error calculating key fingerprint") + } + if subtle.ConstantTimeCompare([]byte(fingerprint), []byte(fp)) == 0 { + return NewError(ErrorUnauthorizedType, "order %s csr does not match the attested key", o.ID) + } + } + // canonicalize the CSR to allow for comparison csr = canonicalize(csr) @@ -165,6 +206,15 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques for i := range o.Identifiers { if o.Identifiers[i].Type == PermanentIdentifier { permanentIdentifier = o.Identifiers[i].Value + // the first (and only) Permanent Identifier that gets added to the certificate + // should be equal to the Subject Common Name if it's set. If not equal, the CSR + // is rejected, because the Common Name hasn't been challenged in that case. This + // could result in unauthorized access if a relying system relies on the Common + // Name in its authorization logic. + if csr.Subject.CommonName != "" && csr.Subject.CommonName != permanentIdentifier { + return NewError(ErrorBadCSRType, "CSR Subject Common Name does not match identifiers exactly: "+ + "CSR Subject Common Name = %s, Order Permanent Identifier = %s", csr.Subject.CommonName, permanentIdentifier) + } break } } diff --git a/acme/order_test.go b/acme/order_test.go index 606e9f71..b8018c7b 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -2,9 +2,12 @@ package acme import ( "context" + "crypto" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/json" + "fmt" "net" "net/url" "reflect" @@ -16,6 +19,7 @@ import ( "github.com/smallstep/assert" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/crypto/keyutil" "go.step.sm/crypto/x509util" ) @@ -306,6 +310,14 @@ func (m *mockSignAuth) Revoke(context.Context, *authority.RevokeOptions) error { } func TestOrder_Finalize(t *testing.T) { + mustSigner := func(kty, crv string, size int) crypto.Signer { + s, err := keyutil.GenerateSigner(kty, crv, size) + if err != nil { + t.Fatal(err) + } + return s + } + type test struct { o *Order err *Error @@ -386,6 +398,72 @@ func TestOrder_Finalize(t *testing.T) { err: NewErrorISE("unrecognized order status: %s", o.Status), } }, + "fail/non-matching-permanent-identifier-common-name": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + + signer := mustSigner("EC", "P-256", 0) + fingerprint, err := keyutil.Fingerprint(signer.Public()) + if err != nil { + t.Fatal(err) + } + + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-different-identifier", + }, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + return test{ + o: o, + csr: csr, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + switch id { + case "a": + return &Authorization{ + ID: id, + Status: StatusValid, + }, nil + case "b": + return &Authorization{ + ID: id, + Fingerprint: fingerprint, + Status: StatusValid, + }, nil + default: + assert.FatalError(t, errors.Errorf("unexpected authorization %s", id)) + return nil, errors.New("force") + } + }, + MockUpdateOrder: func(ctx context.Context, o *Order) error { + return nil + }, + }, + err: &Error{ + Type: "urn:ietf:params:acme:error:badCSR", + Detail: "The CSR is unacceptable", + Status: 400, + Err: fmt.Errorf("CSR Subject Common Name does not match identifiers exactly: "+ + "CSR Subject Common Name = %s, Order Permanent Identifier = %s", csr.Subject.CommonName, "a-permanent-identifier"), + }, + } + }, "fail/error-provisioner-auth": func(t *testing.T) test { now := clock.Now() o := &Order{ @@ -415,6 +493,11 @@ func TestOrder_Finalize(t *testing.T) { return nil, errors.New("force") }, }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, + }, err: NewErrorISE("error retrieving authorization options from ACME provisioner: force"), } }, @@ -454,6 +537,11 @@ func TestOrder_Finalize(t *testing.T) { } }, }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, + }, err: NewErrorISE("error creating template options from ACME provisioner: error unmarshaling template data: invalid character 'o' in literal false (expecting 'a')"), } }, @@ -495,6 +583,11 @@ func TestOrder_Finalize(t *testing.T) { return nil, errors.New("force") }, }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, + }, err: NewErrorISE("error signing certificate for order oID: force"), } }, @@ -541,6 +634,9 @@ func TestOrder_Finalize(t *testing.T) { }, }, db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { assert.Equals(t, cert.AccountID, o.AccountID) assert.Equals(t, cert.OrderID, o.ID) @@ -595,6 +691,9 @@ func TestOrder_Finalize(t *testing.T) { }, }, db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { cert.ID = "certID" assert.Equals(t, cert.AccountID, o.AccountID) @@ -617,6 +716,297 @@ func TestOrder_Finalize(t *testing.T) { err: NewErrorISE("error updating order oID: force"), } }, + "fail/csr-fingerprint": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + + signer := mustSigner("EC", "P-256", 0) + + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-permanent-identifier", + }, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + + leaf := &x509.Certificate{ + Subject: pkix.Name{CommonName: "a-permanent-identifier"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + inter := &x509.Certificate{Subject: pkix.Name{CommonName: "inter"}} + root := &x509.Certificate{Subject: pkix.Name{CommonName: "root"}} + + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{leaf, inter, root}, nil + }, + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ + ID: id, + Fingerprint: "other-fingerprint", + Status: StatusValid, + }, nil + }, + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, leaf) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + err: NewError(ErrorUnauthorizedType, "order oID csr does not match the attested key"), + } + }, + "ok/permanent-identifier": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + + signer := mustSigner("EC", "P-256", 0) + fingerprint, err := keyutil.Fingerprint(signer.Public()) + if err != nil { + t.Fatal(err) + } + + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-permanent-identifier", + }, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + + leaf := &x509.Certificate{ + Subject: pkix.Name{CommonName: "a-permanent-identifier"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + inter := &x509.Certificate{Subject: pkix.Name{CommonName: "inter"}} + root := &x509.Certificate{Subject: pkix.Name{CommonName: "root"}} + + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{leaf, inter, root}, nil + }, + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + switch id { + case "a": + return &Authorization{ + ID: id, + Status: StatusValid, + }, nil + case "b": + return &Authorization{ + ID: id, + Fingerprint: fingerprint, + Status: StatusValid, + }, nil + default: + assert.FatalError(t, errors.Errorf("unexpected authorization %s", id)) + return nil, errors.New("force") + } + }, + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, leaf) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + } + }, + "ok/permanent-identifier-only": func(t *testing.T) test { + now := clock.Now() + o := &Order{ + ID: "oID", + AccountID: "accID", + Status: StatusReady, + ExpiresAt: now.Add(5 * time.Minute), + AuthorizationIDs: []string{"a", "b"}, + Identifiers: []Identifier{ + {Type: "dns", Value: "foo.internal"}, + {Type: "permanent-identifier", Value: "a-permanent-identifier"}, + }, + } + + signer := mustSigner("EC", "P-256", 0) + fingerprint, err := keyutil.Fingerprint(signer.Public()) + if err != nil { + t.Fatal(err) + } + + csr := &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "a-permanent-identifier", + }, + DNSNames: []string{"foo.internal"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + + leaf := &x509.Certificate{ + Subject: pkix.Name{CommonName: "a-permanent-identifier"}, + PublicKey: signer.Public(), + ExtraExtensions: []pkix.Extension{ + { + Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 8, 3}, + Value: []byte("a-permanent-identifier"), + }, + }, + } + inter := &x509.Certificate{Subject: pkix.Name{CommonName: "inter"}} + root := &x509.Certificate{Subject: pkix.Name{CommonName: "root"}} + + return test{ + o: o, + csr: csr, + prov: &MockProvisioner{ + MauthorizeSign: func(ctx context.Context, token string) ([]provisioner.SignOption, error) { + assert.Equals(t, token, "") + return nil, nil + }, + MgetOptions: func() *provisioner.Options { + return nil + }, + }, + // TODO(hs): we should work on making the mocks more realistic. Ideally, we should get rid of + // the mock entirely, relying on an instances of provisioner, authority and DB (possibly hardest), so + // that behavior of the tests is what an actual CA would do. We could gradually phase them out by + // using the mocking functions as a wrapper for actual test helpers generated per test case or per + // function that's tested. + ca: &mockSignAuth{ + sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + assert.Equals(t, _csr, csr) + return []*x509.Certificate{leaf, inter, root}, nil + }, + }, + db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ + ID: id, + Fingerprint: fingerprint, + Status: StatusValid, + }, nil + }, + MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { + cert.ID = "certID" + assert.Equals(t, cert.AccountID, o.AccountID) + assert.Equals(t, cert.OrderID, o.ID) + assert.Equals(t, cert.Leaf, leaf) + assert.Equals(t, cert.Intermediates, []*x509.Certificate{inter, root}) + return nil + }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "certID") + assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, + }, + } + }, "ok/new-cert-dns": func(t *testing.T) test { now := clock.Now() o := &Order{ @@ -660,6 +1050,9 @@ func TestOrder_Finalize(t *testing.T) { }, }, db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { cert.ID = "certID" assert.Equals(t, cert.AccountID, o.AccountID) @@ -721,6 +1114,9 @@ func TestOrder_Finalize(t *testing.T) { }, }, db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { cert.ID = "certID" assert.Equals(t, cert.AccountID, o.AccountID) @@ -785,6 +1181,9 @@ func TestOrder_Finalize(t *testing.T) { }, }, db: &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, MockCreateCertificate: func(ctx context.Context, cert *Certificate) error { cert.ID = "certID" assert.Equals(t, cert.AccountID, o.AccountID) @@ -1492,3 +1891,55 @@ func TestOrder_sans(t *testing.T) { }) } } + +func TestOrder_getAuthorizationFingerprint(t *testing.T) { + ctx := context.Background() + type fields struct { + AuthorizationIDs []string + } + type args struct { + ctx context.Context + db DB + } + tests := []struct { + name string + fields fields + args args + want string + wantErr bool + }{ + {"ok", fields{[]string{"az1", "az2"}}, args{ctx, &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return &Authorization{ID: id, Status: StatusValid}, nil + }, + }}, "", false}, + {"ok fingerprint", fields{[]string{"az1", "az2"}}, args{ctx, &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + if id == "az1" { + return &Authorization{ID: id, Status: StatusValid}, nil + } + return &Authorization{ID: id, Fingerprint: "fingerprint", Status: StatusValid}, nil + }, + }}, "fingerprint", false}, + {"fail", fields{[]string{"az1", "az2"}}, args{ctx, &MockDB{ + MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { + return nil, errors.New("force") + }, + }}, "", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &Order{ + AuthorizationIDs: tt.fields.AuthorizationIDs, + } + got, err := o.getAuthorizationFingerprint(tt.args.ctx, tt.args.db) + if (err != nil) != tt.wantErr { + t.Errorf("Order.getAuthorizationFingerprint() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Order.getAuthorizationFingerprint() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/crl.go b/api/crl.go index 1a4d309a..6386f34a 100644 --- a/api/crl.go +++ b/api/crl.go @@ -17,13 +17,13 @@ func CRL(w http.ResponseWriter, r *http.Request) { _, formatAsPEM := r.URL.Query()["pem"] if formatAsPEM { - pemBytes := pem.EncodeToMemory(&pem.Block{ + w.Header().Add("Content-Type", "application/x-pem-file") + w.Header().Add("Content-Disposition", "attachment; filename=\"crl.pem\"") + + _ = pem.Encode(w, &pem.Block{ Type: "X509 CRL", Bytes: crlBytes, }) - w.Header().Add("Content-Type", "application/x-pem-file") - w.Header().Add("Content-Disposition", "attachment; filename=\"crl.pem\"") - w.Write(pemBytes) } else { w.Header().Add("Content-Type", "application/pkix-crl") w.Header().Add("Content-Disposition", "attachment; filename=\"crl.der\"") diff --git a/authority/admin/api/webhook.go b/authority/admin/api/webhook.go index f73f6806..3939d55e 100644 --- a/authority/admin/api/webhook.go +++ b/authority/admin/api/webhook.go @@ -57,9 +57,9 @@ func validateWebhook(webhook *linkedca.Webhook) error { // kind switch webhook.Kind { - case linkedca.Webhook_ENRICHING, linkedca.Webhook_AUTHORIZING: + case linkedca.Webhook_ENRICHING, linkedca.Webhook_AUTHORIZING, linkedca.Webhook_SCEPCHALLENGE: default: - return admin.NewError(admin.ErrorBadRequestType, "webhook kind is invalid") + return admin.NewError(admin.ErrorBadRequestType, "webhook kind %q is invalid", webhook.Kind) } return nil diff --git a/authority/admin/api/webhook_test.go b/authority/admin/api/webhook_test.go index baac2c11..0fb199f0 100644 --- a/authority/admin/api/webhook_test.go +++ b/authority/admin/api/webhook_test.go @@ -180,6 +180,26 @@ func TestWebhookAdminResponder_CreateProvisionerWebhook(t *testing.T) { statusCode: 400, } }, + "fail/unsupported-webhook-kind": func(t *testing.T) test { + prov := &linkedca.Provisioner{ + Name: "provName", + } + ctx := linkedca.NewContextWithProvisioner(context.Background(), prov) + adminErr := admin.NewError(admin.ErrorBadRequestType, `(line 5:13): invalid value for enum type: "UNSUPPORTED"`) + adminErr.Message = `(line 5:13): invalid value for enum type: "UNSUPPORTED"` + body := []byte(` + { + "name": "metadata", + "url": "https://example.com", + "kind": "UNSUPPORTED", + }`) + return test{ + ctx: ctx, + body: body, + err: adminErr, + statusCode: 400, + } + }, "fail/auth.UpdateProvisioner-error": func(t *testing.T) test { adm := &linkedca.Admin{ Subject: "step", diff --git a/authority/authority.go b/authority/authority.go index 7904a7ea..ae85c018 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -545,50 +545,6 @@ func (a *Authority) init() error { tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts...) } - // Check if a KMS with decryption capability is required and available - if a.requiresDecrypter() { - if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { - return errors.New("keymanager doesn't provide crypto.Decrypter") - } - } - - // TODO: decide if this is a good approach for providing the SCEP functionality - // It currently mirrors the logic for the x509CAService - if a.requiresSCEPService() && a.scepService == nil { - var options scep.Options - - // Read intermediate and create X509 signer and decrypter for default CAS. - options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) - if err != nil { - return err - } - options.CertificateChain = append(options.CertificateChain, a.rootX509Certs...) - options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ - SigningKey: a.config.IntermediateKey, - Password: a.password, - }) - if err != nil { - return err - } - - if km, ok := a.keyManager.(kmsapi.Decrypter); ok { - options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ - DecryptionKey: a.config.IntermediateKey, - Password: a.password, - }) - if err != nil { - return err - } - } - - a.scepService, err = scep.NewService(ctx, options) - if err != nil { - return err - } - - // TODO: mimick the x509CAService GetCertificateAuthority here too? - } - if a.config.AuthorityConfig.EnableAdmin { // Initialize step-ca Admin Database if it's not already initialized using // WithAdminDB. @@ -684,6 +640,50 @@ func (a *Authority) init() error { return err } + // Check if a KMS with decryption capability is required and available + if a.requiresDecrypter() { + if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { + return errors.New("keymanager doesn't provide crypto.Decrypter") + } + } + + // TODO: decide if this is a good approach for providing the SCEP functionality + // It currently mirrors the logic for the x509CAService + if a.requiresSCEPService() && a.scepService == nil { + var options scep.Options + + // Read intermediate and create X509 signer and decrypter for default CAS. + options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert) + if err != nil { + return err + } + options.CertificateChain = append(options.CertificateChain, a.rootX509Certs...) + options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.IntermediateKey, + Password: a.password, + }) + if err != nil { + return err + } + + if km, ok := a.keyManager.(kmsapi.Decrypter); ok { + options.Decrypter, err = km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{ + DecryptionKey: a.config.IntermediateKey, + Password: a.password, + }) + if err != nil { + return err + } + } + + a.scepService, err = scep.NewService(ctx, options) + if err != nil { + return err + } + + // TODO: mimick the x509CAService GetCertificateAuthority here too? + } + // Load X509 constraints engine. // // This is currently only available in CA mode. diff --git a/authority/policy.go b/authority/policy.go index d3078e10..38a57bec 100644 --- a/authority/policy.go +++ b/authority/policy.go @@ -248,7 +248,7 @@ func isAllowed(engine authPolicy.X509Policy, sans []string) error { if isNamePolicyError && policyErr.Reason == policy.NotAllowed { return &PolicyError{ Typ: AdminLockOut, - Err: fmt.Errorf("the provided policy would lock out %s from the CA. Please update your policy to include %s as an allowed name", sans, sans), + Err: fmt.Errorf("the provided policy would lock out %s from the CA. Please create an x509 policy to include %s as an allowed DNS name", sans, sans), } } return &PolicyError{ diff --git a/authority/policy_test.go b/authority/policy_test.go index 8e2e0df4..672ca489 100644 --- a/authority/policy_test.go +++ b/authority/policy_test.go @@ -80,7 +80,7 @@ func TestAuthority_checkPolicy(t *testing.T) { }, err: &PolicyError{ Typ: AdminLockOut, - Err: errors.New("the provided policy would lock out [step] from the CA. Please update your policy to include [step] as an allowed name"), + Err: errors.New("the provided policy would lock out [step] from the CA. Please create an x509 policy to include [step] as an allowed DNS name"), }, } }, @@ -127,7 +127,7 @@ func TestAuthority_checkPolicy(t *testing.T) { }, err: &PolicyError{ Typ: AdminLockOut, - Err: errors.New("the provided policy would lock out [otherAdmin] from the CA. Please update your policy to include [otherAdmin] as an allowed name"), + Err: errors.New("the provided policy would lock out [otherAdmin] from the CA. Please create an x509 policy to include [otherAdmin] as an allowed DNS name"), }, } }, diff --git a/authority/provisioner/acme.go b/authority/provisioner/acme.go index 688a3532..38510af7 100644 --- a/authority/provisioner/acme.go +++ b/authority/provisioner/acme.go @@ -48,7 +48,7 @@ func (c ACMEChallenge) Validate() error { type ACMEAttestationFormat string const ( - // APPLE is the format used to enable device-attest-01 on apple devices. + // APPLE is the format used to enable device-attest-01 on Apple devices. APPLE ACMEAttestationFormat = "apple" // STEP is the format used to enable device-attest-01 on devices that @@ -57,7 +57,7 @@ const ( // TODO(mariano): should we rename this to something else. STEP ACMEAttestationFormat = "step" - // TPM is the format used to enable device-attest-01 on TPMs. + // TPM is the format used to enable device-attest-01 with TPMs. TPM ACMEAttestationFormat = "tpm" ) @@ -184,7 +184,7 @@ func (p *ACME) Init(config Config) (err error) { } // Parse attestation roots. - // The pool will be nil if the there are not roots. + // The pool will be nil if there are no roots. if rest := p.AttestationRoots; len(rest) > 0 { var block *pem.Block var hasCert bool diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index 4b161d9c..fcfbab27 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -26,7 +26,12 @@ import ( const azureOIDCBaseURL = "https://login.microsoftonline.com" //nolint:gosec // azureIdentityTokenURL is the URL to get the identity token for an instance. -const azureIdentityTokenURL = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F" +const azureIdentityTokenURL = "http://169.254.169.254/metadata/identity/oauth2/token" + +const azureIdentityTokenAPIVersion = "2018-02-01" + +// azureInstanceComputeURL is the URL to get the instance compute metadata. +const azureInstanceComputeURL = "http://169.254.169.254/metadata/instance/compute/azEnvironment" // azureDefaultAudience is the default audience used. const azureDefaultAudience = "https://management.azure.com/" @@ -35,15 +40,27 @@ const azureDefaultAudience = "https://management.azure.com/" // Using case insensitive as resourceGroups appears as resourcegroups. var azureXMSMirIDRegExp = regexp.MustCompile(`(?i)^/subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.(Compute/virtualMachines|ManagedIdentity/userAssignedIdentities)/([^/]+)$`) +// azureEnvironments is the list of all Azure environments. +var azureEnvironments = map[string]string{ + "AzurePublicCloud": "https://management.azure.com/", + "AzureCloud": "https://management.azure.com/", + "AzureUSGovernmentCloud": "https://management.usgovcloudapi.net/", + "AzureUSGovernment": "https://management.usgovcloudapi.net/", + "AzureChinaCloud": "https://management.chinacloudapi.cn/", + "AzureGermanCloud": "https://management.microsoftazure.de/", +} + type azureConfig struct { - oidcDiscoveryURL string - identityTokenURL string + oidcDiscoveryURL string + identityTokenURL string + instanceComputeURL string } func newAzureConfig(tenantID string) *azureConfig { return &azureConfig{ - oidcDiscoveryURL: azureOIDCBaseURL + "/" + tenantID + "/.well-known/openid-configuration", - identityTokenURL: azureIdentityTokenURL, + oidcDiscoveryURL: azureOIDCBaseURL + "/" + tenantID + "/.well-known/openid-configuration", + identityTokenURL: azureIdentityTokenURL, + instanceComputeURL: azureInstanceComputeURL, } } @@ -103,6 +120,7 @@ type Azure struct { oidcConfig openIDConfiguration keyStore *keyStore ctl *Controller + environment string } // GetID returns the provisioner unique identifier. @@ -167,11 +185,30 @@ func (p *Azure) GetIdentityToken(subject, caURL string) (string, error) { // Initialize the config if this method is used from the cli. p.assertConfig() + // default to AzurePublicCloud to keep existing behavior + identityTokenResource := azureEnvironments["AzurePublicCloud"] + + var err error + p.environment, err = p.getAzureEnvironment() + if err != nil { + return "", errors.Wrap(err, "error getting azure environment") + } + + if resource, ok := azureEnvironments[p.environment]; ok { + identityTokenResource = resource + } + req, err := http.NewRequest("GET", p.config.identityTokenURL, http.NoBody) if err != nil { return "", errors.Wrap(err, "error creating request") } req.Header.Set("Metadata", "true") + + query := req.URL.Query() + query.Add("resource", identityTokenResource) + query.Add("api-version", azureIdentityTokenAPIVersion) + req.URL.RawQuery = query.Encode() + resp, err := http.DefaultClient.Do(req) if err != nil { return "", errors.Wrap(err, "error getting identity token, are you in a Azure VM?") @@ -444,3 +481,37 @@ func (p *Azure) assertConfig() { p.config = newAzureConfig(p.TenantID) } } + +// getAzureEnvironment returns the Azure environment for the current instance +func (p *Azure) getAzureEnvironment() (string, error) { + if p.environment != "" { + return p.environment, nil + } + + req, err := http.NewRequest("GET", p.config.instanceComputeURL, http.NoBody) + if err != nil { + return "", errors.Wrap(err, "error creating request") + } + req.Header.Add("Metadata", "True") + + query := req.URL.Query() + query.Add("format", "text") + query.Add("api-version", "2021-02-01") + req.URL.RawQuery = query.Encode() + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", errors.Wrap(err, "error getting azure instance environment, are you in a Azure VM?") + } + defer resp.Body.Close() + + b, err := io.ReadAll(resp.Body) + if err != nil { + return "", errors.Wrap(err, "error reading azure environment response") + } + if resp.StatusCode >= 400 { + return "", errors.Errorf("error getting azure environment: status=%d, response=%s", resp.StatusCode, b) + } + + return string(b), nil +} diff --git a/authority/provisioner/azure_test.go b/authority/provisioner/azure_test.go index 84f2ebbf..51d46c5a 100644 --- a/authority/provisioner/azure_test.go +++ b/authority/provisioner/azure_test.go @@ -100,7 +100,14 @@ func TestAzure_GetIdentityToken(t *testing.T) { time.Now(), &p1.keyStore.keySet.Keys[0]) assert.FatalError(t, err) - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + srvIdentity := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wantResource := r.URL.Query().Get("want_resource") + resource := r.URL.Query().Get("resource") + if wantResource == "" || resource != wantResource { + http.Error(w, fmt.Sprintf("Azure query param resource = %s, wantResource %s", resource, wantResource), http.StatusBadRequest) + return + } + switch r.URL.Path { case "/bad-request": http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) @@ -111,29 +118,58 @@ func TestAzure_GetIdentityToken(t *testing.T) { fmt.Fprintf(w, `{"access_token":"%s"}`, t1) } })) - defer srv.Close() + defer srvIdentity.Close() + + srvInstance := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/bad-request": + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + case "/AzureChinaCloud": + w.Header().Add("Content-Type", "text/plain") + w.Write([]byte("AzureChinaCloud")) + case "/AzureGermanCloud": + w.Header().Add("Content-Type", "text/plain") + w.Write([]byte("AzureGermanCloud")) + case "/AzureUSGovernmentCloud": + w.Header().Add("Content-Type", "text/plain") + w.Write([]byte("AzureUSGovernmentCloud")) + default: + w.Header().Add("Content-Type", "text/plain") + w.Write([]byte("AzurePublicCloud")) + } + })) + defer srvInstance.Close() type args struct { subject string caURL string } tests := []struct { - name string - azure *Azure - args args - identityTokenURL string - want string - wantErr bool + name string + azure *Azure + args args + identityTokenURL string + instanceComputeURL string + wantEnvironment string + want string + wantErr bool }{ - {"ok", p1, args{"subject", "caURL"}, srv.URL, t1, false}, - {"fail request", p1, args{"subject", "caURL"}, srv.URL + "/bad-request", "", true}, - {"fail unmarshal", p1, args{"subject", "caURL"}, srv.URL + "/bad-json", "", true}, - {"fail url", p1, args{"subject", "caURL"}, "://ca.smallstep.com", "", true}, - {"fail connect", p1, args{"subject", "caURL"}, "foobarzar", "", true}, + {"ok", p1, args{"subject", "caURL"}, srvIdentity.URL, srvInstance.URL, "AzurePublicCloud", t1, false}, + {"ok azure china", p1, args{"subject", "caURL"}, srvIdentity.URL, srvInstance.URL, "AzurePublicCloud", t1, false}, + {"ok azure germany", p1, args{"subject", "caURL"}, srvIdentity.URL, srvInstance.URL, "AzureGermanCloud", t1, false}, + {"ok azure us gov", p1, args{"subject", "caURL"}, srvIdentity.URL, srvInstance.URL, "AzureUSGovernmentCloud", t1, false}, + {"fail instance request", p1, args{"subject", "caURL"}, srvIdentity.URL + "/bad-request", srvInstance.URL + "/bad-request", "AzurePublicCloud", "", true}, + {"fail request", p1, args{"subject", "caURL"}, srvIdentity.URL + "/bad-request", srvInstance.URL, "AzurePublicCloud", "", true}, + {"fail unmarshal", p1, args{"subject", "caURL"}, srvIdentity.URL + "/bad-json", srvInstance.URL, "AzurePublicCloud", "", true}, + {"fail url", p1, args{"subject", "caURL"}, "://ca.smallstep.com", srvInstance.URL, "AzurePublicCloud", "", true}, + {"fail connect", p1, args{"subject", "caURL"}, "foobarzar", srvInstance.URL, "AzurePublicCloud", "", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.azure.config.identityTokenURL = tt.identityTokenURL + // reset environment between tests to avoid caching issues + p1.environment = "" + tt.azure.config.identityTokenURL = tt.identityTokenURL + "?want_resource=" + azureEnvironments[tt.wantEnvironment] + tt.azure.config.instanceComputeURL = tt.instanceComputeURL + "/" + tt.wantEnvironment got, err := tt.azure.GetIdentityToken(tt.args.subject, tt.args.caURL) if (err != nil) != tt.wantErr { t.Errorf("Azure.GetIdentityToken() error = %v, wantErr %v", err, tt.wantErr) diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index 3840a4a8..01881de6 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -230,7 +230,7 @@ func (o *OIDC) ValidatePayload(p openIDPayload) error { } } if !found { - return errs.Unauthorized("validatePayload: failed to validate oidc token payload: email is not allowed") + return errs.Unauthorized("validatePayload: failed to validate oidc token payload: email %q is not allowed", p.Email) } } @@ -385,16 +385,13 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption } var data sshutil.TemplateData - var principals []string - if claims.Email == "" { - // If email is empty, use the Subject claim instead to create minimal data for the template to use + // If email is empty, use the Subject claim instead to create minimal + // data for the template to use. data = sshutil.CreateTemplateData(sshutil.UserCert, claims.Subject, nil) if v, err := unsafeParseSigned(token); err == nil { data.SetToken(v) } - - principals = nil } else { // Get the identity using either the default identityFunc or one injected // externally. Note that the PreferredUsername might be empty. @@ -417,8 +414,6 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption for k, v := range iden.Permissions.CriticalOptions { data.AddCriticalOption(k, v) } - - principals = iden.Usernames } // Use the default template unless no-templates are configured and email is @@ -446,8 +441,7 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption }) } else { signOptions = append(signOptions, sshCertOptionsValidator(SignSSHOptions{ - CertType: SSHUserCert, - Principals: principals, + CertType: SSHUserCert, })) } diff --git a/authority/provisioner/oidc_test.go b/authority/provisioner/oidc_test.go index 083799f6..9972dc2c 100644 --- a/authority/provisioner/oidc_test.go +++ b/authority/provisioner/oidc_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "go.step.sm/crypto/jose" "github.com/smallstep/assert" @@ -221,39 +222,37 @@ func TestOIDC_authorizeToken(t *testing.T) { args args code int wantIssuer string - wantErr bool + expErr error }{ - {"ok1", p1, args{t1}, http.StatusOK, issuer, false}, - {"ok tenantid", p2, args{t2}, http.StatusOK, tenantIssuer, false}, - {"ok admin", p3, args{t3}, http.StatusOK, issuer, false}, - {"ok domain", p3, args{t4}, http.StatusOK, issuer, false}, - {"ok no email", p3, args{t5}, http.StatusOK, issuer, false}, - {"fail-domain", p3, args{failDomain}, http.StatusUnauthorized, "", true}, - {"fail-key", p1, args{failKey}, http.StatusUnauthorized, "", true}, - {"fail-token", p1, args{failTok}, http.StatusUnauthorized, "", true}, - {"fail-claims", p1, args{failClaims}, http.StatusUnauthorized, "", true}, - {"fail-issuer", p1, args{failIss}, http.StatusUnauthorized, "", true}, - {"fail-audience", p1, args{failAud}, http.StatusUnauthorized, "", true}, - {"fail-signature", p1, args{failSig}, http.StatusUnauthorized, "", true}, - {"fail-expired", p1, args{failExp}, http.StatusUnauthorized, "", true}, - {"fail-not-before", p1, args{failNbf}, http.StatusUnauthorized, "", true}, + {"ok1", p1, args{t1}, http.StatusOK, issuer, nil}, + {"ok tenantid", p2, args{t2}, http.StatusOK, tenantIssuer, nil}, + {"ok admin", p3, args{t3}, http.StatusOK, issuer, nil}, + {"ok domain", p3, args{t4}, http.StatusOK, issuer, nil}, + {"ok no email", p3, args{t5}, http.StatusOK, issuer, nil}, + {"fail-domain", p3, args{failDomain}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken: validatePayload: failed to validate oidc token payload: email "name@example.com" is not allowed`)}, + {"fail-key", p1, args{failKey}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken; cannot validate oidc token`)}, + {"fail-token", p1, args{failTok}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken; error parsing oidc token: invalid character '~' looking for beginning of value`)}, + {"fail-claims", p1, args{failClaims}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken; error parsing oidc token claims: invalid character '~' looking for beginning of value`)}, + {"fail-issuer", p1, args{failIss}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken: validatePayload: failed to validate oidc token payload: square/go-jose/jwt: validation failed, invalid issuer claim (iss)`)}, + {"fail-audience", p1, args{failAud}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken: validatePayload: failed to validate oidc token payload: square/go-jose/jwt: validation failed, invalid audience claim (aud)`)}, + {"fail-signature", p1, args{failSig}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken; cannot validate oidc token`)}, + {"fail-expired", p1, args{failExp}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken: validatePayload: failed to validate oidc token payload: square/go-jose/jwt: validation failed, token is expired (exp)`)}, + {"fail-not-before", p1, args{failNbf}, http.StatusUnauthorized, "", errors.New(`oidc.AuthorizeToken: validatePayload: failed to validate oidc token payload: square/go-jose/jwt: validation failed, token not valid yet (nbf)`)}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tt.prov.authorizeToken(tt.args.token) - if (err != nil) != tt.wantErr { - fmt.Println(tt) - t.Errorf("OIDC.Authorize() error = %v, wantErr %v", err, tt.wantErr) - return - } - if err != nil { + if tt.expErr != nil { + require.Error(t, err) + require.EqualError(t, err, tt.expErr.Error()) + var sc render.StatusCodedError - assert.Fatal(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") - assert.Equals(t, sc.StatusCode(), tt.code) - assert.Nil(t, got) + require.ErrorAs(t, err, &sc, "error does not implement StatusCodedError interface") + require.Equal(t, tt.code, sc.StatusCode()) + require.Nil(t, got) } else { - assert.NotNil(t, got) - assert.Equals(t, got.Issuer, tt.wantIssuer) + require.NotNil(t, got) + require.Equal(t, tt.wantIssuer, got.Issuer) } }) } @@ -339,8 +338,6 @@ func TestOIDC_AuthorizeSign(t *testing.T) { case *validityValidator: assert.Equals(t, v.min, tt.prov.ctl.Claimer.MinTLSCertDuration()) assert.Equals(t, v.max, tt.prov.ctl.Claimer.MaxTLSCertDuration()) - case emailOnlyIdentity: - assert.Equals(t, string(v), "name@smallstep.com") case *x509NamePolicyValidator: assert.Equals(t, nil, v.policyEngine) case *WebhookController: @@ -582,6 +579,9 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) { {"ok-principals", p1, args{t1, SignSSHOptions{Principals: []string{"name"}}, pub}, &SignSSHOptions{CertType: "user", Principals: []string{"name", "name@smallstep.com"}, ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false}, + {"ok-principals-ignore-passed", p1, args{t1, SignSSHOptions{Principals: []string{"root"}}, pub}, + &SignSSHOptions{CertType: "user", Principals: []string{"name", "name@smallstep.com"}, + ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false}, {"ok-principals-getIdentity", p4, args{okGetIdentityToken, SignSSHOptions{Principals: []string{"mariano"}}, pub}, &SignSSHOptions{CertType: "user", Principals: []string{"max", "mariano"}, ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false}, @@ -600,7 +600,6 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) { ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false}, {"fail-rsa1024", p1, args{t1, SignSSHOptions{}, rsa1024.Public()}, expectedUserOptions, http.StatusOK, false, true}, {"fail-user-host", p1, args{t1, SignSSHOptions{CertType: "host"}, pub}, nil, http.StatusOK, false, true}, - {"fail-user-principals", p1, args{t1, SignSSHOptions{Principals: []string{"root"}}, pub}, nil, http.StatusOK, false, true}, {"fail-getIdentity", p5, args{failGetIdentityToken, SignSSHOptions{}, pub}, nil, http.StatusInternalServerError, true, false}, {"fail-sshCA-disabled", p6, args{"foo", SignSSHOptions{}, pub}, nil, http.StatusUnauthorized, true, false}, // Missing parametrs diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 3f8fb5a2..0db40864 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -2,10 +2,16 @@ package provisioner import ( "context" + "crypto/subtle" + "fmt" + "net/http" "time" "github.com/pkg/errors" + "go.step.sm/linkedca" + + "github.com/smallstep/certificates/webhook" ) // SCEP is the SCEP provisioner type, an entity that can authorize the @@ -34,6 +40,7 @@ type SCEP struct { Claims *Claims `json:"claims,omitempty"` ctl *Controller encryptionAlgorithm int + challengeValidationController *challengeValidationController } // GetID returns the provisioner unique identifier. @@ -81,6 +88,67 @@ func (s *SCEP) DefaultTLSCertDuration() time.Duration { return s.ctl.Claimer.DefaultTLSCertDuration() } +type challengeValidationController struct { + client *http.Client + webhooks []*Webhook +} + +// newChallengeValidationController creates a new challengeValidationController +// that performs challenge validation through webhooks. +func newChallengeValidationController(client *http.Client, webhooks []*Webhook) *challengeValidationController { + scepHooks := []*Webhook{} + for _, wh := range webhooks { + if wh.Kind != linkedca.Webhook_SCEPCHALLENGE.String() { + continue + } + if !isCertTypeOK(wh) { + continue + } + scepHooks = append(scepHooks, wh) + } + return &challengeValidationController{ + client: client, + webhooks: scepHooks, + } +} + +var ( + ErrSCEPChallengeInvalid = errors.New("webhook server did not allow request") +) + +// Validate executes zero or more configured webhooks to +// validate the SCEP challenge. If at least one of them indicates +// the challenge value is accepted, validation succeeds. In +// that case, the other webhooks will be skipped. If none of +// the webhooks indicates the value of the challenge was accepted, +// an error is returned. +func (c *challengeValidationController) Validate(ctx context.Context, challenge, transactionID string) error { + for _, wh := range c.webhooks { + req := &webhook.RequestBody{ + SCEPChallenge: challenge, + SCEPTransactionID: transactionID, + } + resp, err := wh.DoWithContext(ctx, c.client, req, nil) // TODO(hs): support templated URL? Requires some refactoring + if err != nil { + return fmt.Errorf("failed executing webhook request: %w", err) + } + if resp.Allow { + return nil // return early when response is positive + } + } + + return ErrSCEPChallengeInvalid +} + +// isCertTypeOK returns whether or not the webhook can be used +// with the SCEP challenge validation webhook controller. +func isCertTypeOK(wh *Webhook) bool { + if wh.CertType == linkedca.Webhook_ALL.String() || wh.CertType == "" { + return true + } + return linkedca.Webhook_X509.String() == wh.CertType +} + // Init initializes and validates the fields of a SCEP type. func (s *SCEP) Init(config Config) (err error) { switch { @@ -104,6 +172,11 @@ func (s *SCEP) Init(config Config) (err error) { return errors.New("only encryption algorithm identifiers from 0 to 4 are valid") } + s.challengeValidationController = newChallengeValidationController( + config.WebhookClient, + s.GetOptions().GetWebhooks(), + ) + // TODO: add other, SCEP specific, options? s.ctl, err = NewController(s, s.Claims, config, s.Options) @@ -151,3 +224,43 @@ func (s *SCEP) ShouldIncludeRootInChain() bool { func (s *SCEP) GetContentEncryptionAlgorithm() int { return s.encryptionAlgorithm } + +// ValidateChallenge validates the provided challenge. It starts by +// selecting the validation method to use, then performs validation +// according to that method. +func (s *SCEP) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { + if s.challengeValidationController == nil { + return fmt.Errorf("provisioner %q wasn't initialized", s.Name) + } + switch s.selectValidationMethod() { + case validationMethodWebhook: + return s.challengeValidationController.Validate(ctx, challenge, transactionID) + default: + if subtle.ConstantTimeCompare([]byte(s.secretChallengePassword), []byte(challenge)) == 0 { + return errors.New("invalid challenge password provided") + } + return nil + } +} + +type validationMethod string + +const ( + validationMethodNone validationMethod = "none" + validationMethodStatic validationMethod = "static" + validationMethodWebhook validationMethod = "webhook" +) + +// selectValidationMethod returns the method to validate SCEP +// challenges. If a webhook is configured with kind `SCEPCHALLENGE`, +// the webhook method will be used. If a challenge password is set, +// the static method is used. It will default to the `none` method. +func (s *SCEP) selectValidationMethod() validationMethod { + if len(s.challengeValidationController.webhooks) > 0 { + return validationMethodWebhook + } + if s.secretChallengePassword != "" { + return validationMethodStatic + } + return validationMethodNone +} diff --git a/authority/provisioner/scep_test.go b/authority/provisioner/scep_test.go new file mode 100644 index 00000000..acf047fb --- /dev/null +++ b/authority/provisioner/scep_test.go @@ -0,0 +1,342 @@ +package provisioner + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.step.sm/linkedca" +) + +func Test_challengeValidationController_Validate(t *testing.T) { + type request struct { + Challenge string `json:"scepChallenge"` + TransactionID string `json:"scepTransactionID"` + } + type response struct { + Allow bool `json:"allow"` + } + nokServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + req := &request{} + err := json.NewDecoder(r.Body).Decode(req) + require.NoError(t, err) + assert.Equal(t, "not-allowed", req.Challenge) + assert.Equal(t, "transaction-1", req.TransactionID) + b, err := json.Marshal(response{Allow: false}) + require.NoError(t, err) + w.WriteHeader(200) + w.Write(b) + })) + okServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + req := &request{} + err := json.NewDecoder(r.Body).Decode(req) + require.NoError(t, err) + assert.Equal(t, "challenge", req.Challenge) + assert.Equal(t, "transaction-1", req.TransactionID) + b, err := json.Marshal(response{Allow: true}) + require.NoError(t, err) + w.WriteHeader(200) + w.Write(b) + })) + type fields struct { + client *http.Client + webhooks []*Webhook + } + type args struct { + challenge string + transactionID string + } + tests := []struct { + name string + fields fields + args args + server *httptest.Server + expErr error + }{ + { + name: "fail/no-webhook", + fields: fields{http.DefaultClient, nil}, + args: args{"no-webhook", "transaction-1"}, + expErr: errors.New("webhook server did not allow request"), + }, + { + name: "fail/wrong-cert-type", + fields: fields{http.DefaultClient, []*Webhook{ + { + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + CertType: linkedca.Webhook_SSH.String(), + }, + }}, + args: args{"wrong-cert-type", "transaction-1"}, + expErr: errors.New("webhook server did not allow request"), + }, + { + name: "fail/wrong-secret-value", + fields: fields{http.DefaultClient, []*Webhook{ + { + ID: "webhook-id-1", + Name: "webhook-name-1", + Secret: "{{}}", + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + CertType: linkedca.Webhook_X509.String(), + URL: okServer.URL, + }, + }}, + args: args{ + challenge: "wrong-secret-value", + transactionID: "transaction-1", + }, + expErr: errors.New("failed executing webhook request: illegal base64 data at input byte 0"), + }, + { + name: "fail/not-allowed", + fields: fields{http.DefaultClient, []*Webhook{ + { + ID: "webhook-id-1", + Name: "webhook-name-1", + Secret: "MTIzNAo=", + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + CertType: linkedca.Webhook_X509.String(), + URL: nokServer.URL, + }, + }}, + args: args{ + challenge: "not-allowed", + transactionID: "transaction-1", + }, + server: nokServer, + expErr: errors.New("webhook server did not allow request"), + }, + { + name: "ok", + fields: fields{http.DefaultClient, []*Webhook{ + { + ID: "webhook-id-1", + Name: "webhook-name-1", + Secret: "MTIzNAo=", + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + CertType: linkedca.Webhook_X509.String(), + URL: okServer.URL, + }, + }}, + args: args{ + challenge: "challenge", + transactionID: "transaction-1", + }, + server: okServer, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := newChallengeValidationController(tt.fields.client, tt.fields.webhooks) + + if tt.server != nil { + defer tt.server.Close() + } + + ctx := context.Background() + err := c.Validate(ctx, tt.args.challenge, tt.args.transactionID) + + if tt.expErr != nil { + assert.EqualError(t, err, tt.expErr.Error()) + return + } + + assert.NoError(t, err) + }) + } +} + +func TestController_isCertTypeOK(t *testing.T) { + assert.True(t, isCertTypeOK(&Webhook{CertType: linkedca.Webhook_X509.String()})) + assert.True(t, isCertTypeOK(&Webhook{CertType: linkedca.Webhook_ALL.String()})) + assert.True(t, isCertTypeOK(&Webhook{CertType: ""})) + assert.False(t, isCertTypeOK(&Webhook{CertType: linkedca.Webhook_SSH.String()})) +} + +func Test_selectValidationMethod(t *testing.T) { + tests := []struct { + name string + p *SCEP + want validationMethod + }{ + {"webhooks", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{ + Webhooks: []*Webhook{ + { + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + }, + }, + }, + }, "webhook"}, + {"challenge", &SCEP{ + Name: "SCEP", + Type: "SCEP", + ChallengePassword: "pass", + }, "static"}, + {"challenge-with-different-webhook", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{ + Webhooks: []*Webhook{ + { + Kind: linkedca.Webhook_AUTHORIZING.String(), + }, + }, + }, + ChallengePassword: "pass", + }, "static"}, + {"none", &SCEP{ + Name: "SCEP", + Type: "SCEP", + }, "none"}, + {"none-with-different-webhook", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{ + Webhooks: []*Webhook{ + { + Kind: linkedca.Webhook_AUTHORIZING.String(), + }, + }, + }, + }, "none"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.p.Init(Config{Claims: globalProvisionerClaims}) + require.NoError(t, err) + got := tt.p.selectValidationMethod() + assert.Equal(t, tt.want, got) + }) + } +} + +func TestSCEP_ValidateChallenge(t *testing.T) { + type request struct { + Challenge string `json:"scepChallenge"` + TransactionID string `json:"scepTransactionID"` + } + type response struct { + Allow bool `json:"allow"` + } + okServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + req := &request{} + err := json.NewDecoder(r.Body).Decode(req) + require.NoError(t, err) + assert.Equal(t, "webhook-challenge", req.Challenge) + assert.Equal(t, "webhook-transaction-1", req.TransactionID) + b, err := json.Marshal(response{Allow: true}) + require.NoError(t, err) + w.WriteHeader(200) + w.Write(b) + })) + type args struct { + challenge string + transactionID string + } + tests := []struct { + name string + p *SCEP + server *httptest.Server + args args + expErr error + }{ + {"ok/webhooks", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{ + Webhooks: []*Webhook{ + { + ID: "webhook-id-1", + Name: "webhook-name-1", + Secret: "MTIzNAo=", + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + CertType: linkedca.Webhook_X509.String(), + URL: okServer.URL, + }, + }, + }, + }, okServer, args{"webhook-challenge", "webhook-transaction-1"}, + nil, + }, + {"fail/webhooks-secret-configuration", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{ + Webhooks: []*Webhook{ + { + ID: "webhook-id-1", + Name: "webhook-name-1", + Secret: "{{}}", + Kind: linkedca.Webhook_SCEPCHALLENGE.String(), + CertType: linkedca.Webhook_X509.String(), + URL: okServer.URL, + }, + }, + }, + }, nil, args{"webhook-challenge", "webhook-transaction-1"}, + errors.New("failed executing webhook request: illegal base64 data at input byte 0"), + }, + {"ok/static-challenge", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{}, + ChallengePassword: "secret-static-challenge", + }, nil, args{"secret-static-challenge", "static-transaction-1"}, + nil, + }, + {"fail/wrong-static-challenge", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{}, + ChallengePassword: "secret-static-challenge", + }, nil, args{"the-wrong-challenge-secret", "static-transaction-1"}, + errors.New("invalid challenge password provided"), + }, + {"ok/no-challenge", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{}, + ChallengePassword: "", + }, nil, args{"", "static-transaction-1"}, + nil, + }, + {"fail/no-challenge-but-provided", &SCEP{ + Name: "SCEP", + Type: "SCEP", + Options: &Options{}, + ChallengePassword: "", + }, nil, args{"a-challenge-value", "static-transaction-1"}, + errors.New("invalid challenge password provided"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if tt.server != nil { + defer tt.server.Close() + } + + err := tt.p.Init(Config{Claims: globalProvisionerClaims, WebhookClient: http.DefaultClient}) + require.NoError(t, err) + ctx := context.Background() + + err = tt.p.ValidateChallenge(ctx, tt.args.challenge, tt.args.transactionID) + if tt.expErr != nil { + assert.EqualError(t, err, tt.expErr.Error()) + return + } + + assert.NoError(t, err) + }) + } +} diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index bc0d88ff..c3db239a 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -83,31 +83,6 @@ type AttestationData struct { PermanentIdentifier string } -// emailOnlyIdentity is a CertificateRequestValidator that checks that the only -// SAN provided is the given email address. -type emailOnlyIdentity string - -func (e emailOnlyIdentity) Valid(req *x509.CertificateRequest) error { - switch { - case len(req.DNSNames) > 0: - return errs.Forbidden("certificate request cannot contain DNS names") - case len(req.IPAddresses) > 0: - return errs.Forbidden("certificate request cannot contain IP addresses") - case len(req.URIs) > 0: - return errs.Forbidden("certificate request cannot contain URIs") - case len(req.EmailAddresses) == 0: - return errs.Forbidden("certificate request does not contain any email address") - case len(req.EmailAddresses) > 1: - return errs.Forbidden("certificate request contains too many email addresses") - case req.EmailAddresses[0] == "": - return errs.Forbidden("certificate request cannot contain an empty email address") - case req.EmailAddresses[0] != string(e): - return errs.Forbidden("certificate request does not contain the valid email address - got %s, want %s", req.EmailAddresses[0], e) - default: - return nil - } -} - // defaultPublicKeyValidator validates the public key of a certificate request. type defaultPublicKeyValidator struct{} diff --git a/authority/provisioner/sign_options_test.go b/authority/provisioner/sign_options_test.go index 198462c7..01d2a0cd 100644 --- a/authority/provisioner/sign_options_test.go +++ b/authority/provisioner/sign_options_test.go @@ -16,38 +16,6 @@ import ( "go.step.sm/crypto/pemutil" ) -func Test_emailOnlyIdentity_Valid(t *testing.T) { - uri, err := url.Parse("https://example.com/1.0/getUser") - if err != nil { - t.Fatal(err) - } - - type args struct { - req *x509.CertificateRequest - } - tests := []struct { - name string - e emailOnlyIdentity - args args - wantErr bool - }{ - {"ok", "name@smallstep.com", args{&x509.CertificateRequest{EmailAddresses: []string{"name@smallstep.com"}}}, false}, - {"DNSNames", "name@smallstep.com", args{&x509.CertificateRequest{DNSNames: []string{"foo.bar.zar"}}}, true}, - {"IPAddresses", "name@smallstep.com", args{&x509.CertificateRequest{IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}}}, true}, - {"URIs", "name@smallstep.com", args{&x509.CertificateRequest{URIs: []*url.URL{uri}}}, true}, - {"no-emails", "name@smallstep.com", args{&x509.CertificateRequest{EmailAddresses: []string{}}}, true}, - {"empty-email", "", args{&x509.CertificateRequest{EmailAddresses: []string{""}}}, true}, - {"multiple-emails", "name@smallstep.com", args{&x509.CertificateRequest{EmailAddresses: []string{"name@smallstep.com", "foo@smallstep.com"}}}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.e.Valid(tt.args.req); (err != nil) != tt.wantErr { - t.Errorf("emailOnlyIdentity.Valid() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - func Test_defaultPublicKeyValidator_Valid(t *testing.T) { _shortRSA, err := pemutil.Read("./testdata/certs/short-rsa.csr") assert.FatalError(t, err) diff --git a/authority/provisioner/sign_ssh_options.go b/authority/provisioner/sign_ssh_options.go index 70dffba2..f027c3a6 100644 --- a/authority/provisioner/sign_ssh_options.go +++ b/authority/provisioner/sign_ssh_options.go @@ -125,35 +125,6 @@ func (o SignSSHOptions) match(got SignSSHOptions) error { return nil } -// sshCertPrincipalsModifier is an SSHCertModifier that sets the -// principals to the SSH certificate. -type sshCertPrincipalsModifier []string - -// Modify the ValidPrincipals value of the cert. -func (o sshCertPrincipalsModifier) Modify(cert *ssh.Certificate, _ SignSSHOptions) error { - cert.ValidPrincipals = []string(o) - return nil -} - -// sshCertKeyIDModifier is an SSHCertModifier that sets the given -// Key ID in the SSH certificate. -type sshCertKeyIDModifier string - -func (m sshCertKeyIDModifier) Modify(cert *ssh.Certificate, _ SignSSHOptions) error { - cert.KeyId = string(m) - return nil -} - -// sshCertTypeModifier is an SSHCertModifier that sets the -// certificate type. -type sshCertTypeModifier string - -// Modify sets the CertType for the ssh certificate. -func (m sshCertTypeModifier) Modify(cert *ssh.Certificate, _ SignSSHOptions) error { - cert.CertType = sshCertTypeUInt32(string(m)) - return nil -} - // sshCertValidAfterModifier is an SSHCertModifier that sets the // ValidAfter in the SSH certificate. type sshCertValidAfterModifier uint64 @@ -172,51 +143,6 @@ func (m sshCertValidBeforeModifier) Modify(cert *ssh.Certificate, _ SignSSHOptio return nil } -// sshCertDefaultsModifier implements a SSHCertModifier that -// modifies the certificate with the given options if they are not set. -type sshCertDefaultsModifier SignSSHOptions - -// Modify implements the SSHCertModifier interface. -func (m sshCertDefaultsModifier) Modify(cert *ssh.Certificate, _ SignSSHOptions) error { - if cert.CertType == 0 { - cert.CertType = sshCertTypeUInt32(m.CertType) - } - if len(cert.ValidPrincipals) == 0 { - cert.ValidPrincipals = m.Principals - } - if cert.ValidAfter == 0 && !m.ValidAfter.IsZero() { - cert.ValidAfter = uint64(m.ValidAfter.Unix()) - } - if cert.ValidBefore == 0 && !m.ValidBefore.IsZero() { - cert.ValidBefore = uint64(m.ValidBefore.Unix()) - } - return nil -} - -// sshDefaultExtensionModifier implements an SSHCertModifier that sets -// the default extensions in an SSH certificate. -type sshDefaultExtensionModifier struct{} - -func (m *sshDefaultExtensionModifier) Modify(cert *ssh.Certificate, _ SignSSHOptions) error { - switch cert.CertType { - // Default to no extensions for HostCert. - case ssh.HostCert: - return nil - case ssh.UserCert: - if cert.Extensions == nil { - cert.Extensions = make(map[string]string) - } - cert.Extensions["permit-X11-forwarding"] = "" - cert.Extensions["permit-agent-forwarding"] = "" - cert.Extensions["permit-port-forwarding"] = "" - cert.Extensions["permit-pty"] = "" - cert.Extensions["permit-user-rc"] = "" - return nil - default: - return errs.BadRequest("ssh certificate has an unknown type '%d'", cert.CertType) - } -} - // sshDefaultDuration is an SSHCertModifier that sets the certificate // ValidAfter and ValidBefore if they have not been set. It will fail if a // CertType has not been set or is not valid. diff --git a/authority/provisioner/sign_ssh_options_test.go b/authority/provisioner/sign_ssh_options_test.go index 1993295b..550a9f13 100644 --- a/authority/provisioner/sign_ssh_options_test.go +++ b/authority/provisioner/sign_ssh_options_test.go @@ -202,97 +202,6 @@ func TestSSHOptions_Match(t *testing.T) { } } -func Test_sshCertPrincipalsModifier_Modify(t *testing.T) { - type test struct { - modifier sshCertPrincipalsModifier - cert *ssh.Certificate - expected []string - } - tests := map[string]func() test{ - "ok": func() test { - a := []string{"foo", "bar"} - return test{ - modifier: sshCertPrincipalsModifier(a), - cert: new(ssh.Certificate), - expected: a, - } - }, - } - for name, run := range tests { - t.Run(name, func(t *testing.T) { - tc := run() - if assert.Nil(t, tc.modifier.Modify(tc.cert, SignSSHOptions{})) { - assert.Equals(t, tc.cert.ValidPrincipals, tc.expected) - } - }) - } -} - -func Test_sshCertKeyIDModifier_Modify(t *testing.T) { - type test struct { - modifier sshCertKeyIDModifier - cert *ssh.Certificate - expected string - } - tests := map[string]func() test{ - "ok": func() test { - a := "foo" - return test{ - modifier: sshCertKeyIDModifier(a), - cert: new(ssh.Certificate), - expected: a, - } - }, - } - for name, run := range tests { - t.Run(name, func(t *testing.T) { - tc := run() - if assert.Nil(t, tc.modifier.Modify(tc.cert, SignSSHOptions{})) { - assert.Equals(t, tc.cert.KeyId, tc.expected) - } - }) - } -} - -func Test_sshCertTypeModifier_Modify(t *testing.T) { - type test struct { - modifier sshCertTypeModifier - cert *ssh.Certificate - expected uint32 - } - tests := map[string]func() test{ - "ok/user": func() test { - return test{ - modifier: sshCertTypeModifier("user"), - cert: new(ssh.Certificate), - expected: ssh.UserCert, - } - }, - "ok/host": func() test { - return test{ - modifier: sshCertTypeModifier("host"), - cert: new(ssh.Certificate), - expected: ssh.HostCert, - } - }, - "ok/default": func() test { - return test{ - modifier: sshCertTypeModifier("foo"), - cert: new(ssh.Certificate), - expected: 0, - } - }, - } - for name, run := range tests { - t.Run(name, func(t *testing.T) { - tc := run() - if assert.Nil(t, tc.modifier.Modify(tc.cert, SignSSHOptions{})) { - assert.Equals(t, tc.cert.CertType, tc.expected) - } - }) - } -} - func Test_sshCertValidAfterModifier_Modify(t *testing.T) { type test struct { modifier sshCertValidAfterModifier @@ -318,176 +227,6 @@ func Test_sshCertValidAfterModifier_Modify(t *testing.T) { } } -func Test_sshCertDefaultsModifier_Modify(t *testing.T) { - type test struct { - modifier sshCertDefaultsModifier - cert *ssh.Certificate - valid func(*ssh.Certificate) - } - tests := map[string]func() test{ - "ok/changes": func() test { - n := time.Now() - va := NewTimeDuration(n.Add(1 * time.Minute)) - vb := NewTimeDuration(n.Add(5 * time.Minute)) - so := SignSSHOptions{ - Principals: []string{"foo", "bar"}, - CertType: "host", - ValidAfter: va, - ValidBefore: vb, - } - return test{ - modifier: sshCertDefaultsModifier(so), - cert: new(ssh.Certificate), - valid: func(cert *ssh.Certificate) { - assert.Equals(t, cert.ValidPrincipals, so.Principals) - assert.Equals(t, cert.CertType, uint32(ssh.HostCert)) - assert.Equals(t, cert.ValidAfter, uint64(so.ValidAfter.RelativeTime(time.Now()).Unix())) - assert.Equals(t, cert.ValidBefore, uint64(so.ValidBefore.RelativeTime(time.Now()).Unix())) - }, - } - }, - "ok/no-changes": func() test { - n := time.Now() - so := SignSSHOptions{ - Principals: []string{"foo", "bar"}, - CertType: "host", - ValidAfter: NewTimeDuration(n.Add(15 * time.Minute)), - ValidBefore: NewTimeDuration(n.Add(25 * time.Minute)), - } - return test{ - modifier: sshCertDefaultsModifier(so), - cert: &ssh.Certificate{ - CertType: uint32(ssh.UserCert), - ValidPrincipals: []string{"zap", "zoop"}, - ValidAfter: 15, - ValidBefore: 25, - }, - valid: func(cert *ssh.Certificate) { - assert.Equals(t, cert.ValidPrincipals, []string{"zap", "zoop"}) - assert.Equals(t, cert.CertType, uint32(ssh.UserCert)) - assert.Equals(t, cert.ValidAfter, uint64(15)) - assert.Equals(t, cert.ValidBefore, uint64(25)) - }, - } - }, - } - for name, run := range tests { - t.Run(name, func(t *testing.T) { - tc := run() - if assert.Nil(t, tc.modifier.Modify(tc.cert, SignSSHOptions{})) { - tc.valid(tc.cert) - } - }) - } -} - -func Test_sshDefaultExtensionModifier_Modify(t *testing.T) { - type test struct { - modifier sshDefaultExtensionModifier - cert *ssh.Certificate - valid func(*ssh.Certificate) - err error - } - tests := map[string]func() test{ - "fail/unexpected-cert-type": func() test { - cert := &ssh.Certificate{CertType: 3} - return test{ - modifier: sshDefaultExtensionModifier{}, - cert: cert, - err: errors.New("ssh certificate has an unknown type '3'"), - } - }, - "ok/host": func() test { - cert := &ssh.Certificate{CertType: ssh.HostCert} - return test{ - modifier: sshDefaultExtensionModifier{}, - cert: cert, - valid: func(cert *ssh.Certificate) { - assert.Len(t, 0, cert.Extensions) - }, - } - }, - "ok/user/extensions-exists": func() test { - cert := &ssh.Certificate{CertType: ssh.UserCert, Permissions: ssh.Permissions{Extensions: map[string]string{ - "foo": "bar", - }}} - return test{ - modifier: sshDefaultExtensionModifier{}, - cert: cert, - valid: func(cert *ssh.Certificate) { - val, ok := cert.Extensions["foo"] - assert.True(t, ok) - assert.Equals(t, val, "bar") - - val, ok = cert.Extensions["permit-X11-forwarding"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-agent-forwarding"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-port-forwarding"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-pty"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-user-rc"] - assert.True(t, ok) - assert.Equals(t, val, "") - }, - } - }, - "ok/user/no-extensions": func() test { - return test{ - modifier: sshDefaultExtensionModifier{}, - cert: &ssh.Certificate{CertType: ssh.UserCert}, - valid: func(cert *ssh.Certificate) { - _, ok := cert.Extensions["foo"] - assert.False(t, ok) - - val, ok := cert.Extensions["permit-X11-forwarding"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-agent-forwarding"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-port-forwarding"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-pty"] - assert.True(t, ok) - assert.Equals(t, val, "") - - val, ok = cert.Extensions["permit-user-rc"] - assert.True(t, ok) - assert.Equals(t, val, "") - }, - } - }, - } - for name, run := range tests { - t.Run(name, func(t *testing.T) { - tc := run() - if err := tc.modifier.Modify(tc.cert, SignSSHOptions{}); err != nil { - if assert.NotNil(t, tc.err) { - assert.HasPrefix(t, err.Error(), tc.err.Error()) - } - } else { - if assert.Nil(t, tc.err) { - tc.valid(tc.cert) - } - } - }) - } -} - func Test_sshCertDefaultValidator_Valid(t *testing.T) { pub, _, err := keyutil.GenerateDefaultKeyPair() assert.FatalError(t, err) diff --git a/authority/provisioner/utils_test.go b/authority/provisioner/utils_test.go index f0e6949f..55fdfe6f 100644 --- a/authority/provisioner/utils_test.go +++ b/authority/provisioner/utils_test.go @@ -665,6 +665,9 @@ func generateAzureWithServer() (*Azure, *httptest.Server, error) { AccessToken: tok, }) } + case "/metadata/instance/compute/azEnvironment": + w.Header().Add("Content-Type", "text/plain") + w.Write([]byte("AzurePublicCloud")) default: http.NotFound(w, r) } @@ -672,6 +675,7 @@ func generateAzureWithServer() (*Azure, *httptest.Server, error) { srv.Start() az.config.oidcDiscoveryURL = srv.URL + "/" + az.TenantID + "/.well-known/openid-configuration" az.config.identityTokenURL = srv.URL + "/metadata/identity/oauth2/token" + az.config.instanceComputeURL = srv.URL + "/metadata/instance/compute/azEnvironment" return az, srv, nil } diff --git a/authority/provisioner/webhook.go b/authority/provisioner/webhook.go index ea02da35..cb15547d 100644 --- a/authority/provisioner/webhook.go +++ b/authority/provisioner/webhook.go @@ -107,6 +107,13 @@ type Webhook struct { } func (w *Webhook) Do(client *http.Client, reqBody *webhook.RequestBody, data any) (*webhook.ResponseBody, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + return w.DoWithContext(ctx, client, reqBody, data) +} + +func (w *Webhook) DoWithContext(ctx context.Context, client *http.Client, reqBody *webhook.RequestBody, data any) (*webhook.ResponseBody, error) { tmpl, err := template.New("url").Funcs(templates.StepFuncMap()).Parse(w.URL) if err != nil { return nil, err @@ -129,8 +136,6 @@ func (w *Webhook) Do(client *http.Client, reqBody *webhook.RequestBody, data any reqBody.Token = tmpl[sshutil.TokenKey] } */ - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - defer cancel() reqBody.Timestamp = time.Now() diff --git a/authority/provisioner/x5c_test.go b/authority/provisioner/x5c_test.go index 437b7661..72f9f947 100644 --- a/authority/provisioner/x5c_test.go +++ b/authority/provisioner/x5c_test.go @@ -790,8 +790,6 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) { assert.Equals(t, int64(v), tc.claims.Step.SSH.ValidAfter.RelativeTime(nw).Unix()) case sshCertValidBeforeModifier: assert.Equals(t, int64(v), tc.claims.Step.SSH.ValidBefore.RelativeTime(nw).Unix()) - case sshCertDefaultsModifier: - assert.Equals(t, SignSSHOptions(v), SignSSHOptions{CertType: SSHUserCert}) case *sshLimitDuration: assert.Equals(t, v.Claimer, tc.p.ctl.Claimer) assert.Equals(t, v.NotAfter, x5cCerts[0].NotAfter) diff --git a/authority/tls.go b/authority/tls.go index e64bb5fa..b7531ce3 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -786,7 +786,7 @@ func (a *Authority) GenerateCertificateRevocationList() error { // Note that this is currently using the port 443 by default. if b, err := marshalDistributionPoint(fullName, false); err == nil { revocationList.ExtraExtensions = []pkix.Extension{ - {Id: oidExtensionIssuingDistributionPoint, Value: b}, + {Id: oidExtensionIssuingDistributionPoint, Critical: true, Value: b}, } } diff --git a/ca/adminClient.go b/ca/adminClient.go index 713668df..5cfaaf15 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -44,6 +44,9 @@ type AdminClient struct { x5cSubject string } +var ErrAdminAPINotImplemented = errors.New("admin API not implemented") +var ErrAdminAPINotAuthorized = errors.New("admin API not authorized") + // AdminClientError is the client side representation of an // AdminError returned by the CA. type AdminClientError struct { @@ -137,6 +140,28 @@ func (c *AdminClient) retryOnError(r *http.Response) bool { return false } +// IsEnabled checks if the admin API is enabled. +func (c *AdminClient) IsEnabled() error { + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins")}) + resp, err := c.client.Get(u.String()) + if err != nil { + return clientError(err) + } + defer resp.Body.Close() + + if resp.StatusCode < http.StatusBadRequest { + return nil + } + switch resp.StatusCode { + case http.StatusNotFound, http.StatusNotImplemented: + return ErrAdminAPINotImplemented + case http.StatusUnauthorized: + return ErrAdminAPINotAuthorized + default: + return errors.Errorf("unexpected status code when performing is-enabled check for Admin API: %d", resp.StatusCode) + } +} + // GetAdmin performs the GET /admin/admin/{id} request to the CA. func (c *AdminClient) GetAdmin(id string) (*linkedca.Admin, error) { var retried bool diff --git a/ca/bootstrap.go b/ca/bootstrap.go index 430f2e31..78b94ec9 100644 --- a/ca/bootstrap.go +++ b/ca/bootstrap.go @@ -61,7 +61,7 @@ func Bootstrap(token string) (*Client, error) { // } // resp, err := client.Get("https://internal.smallstep.com") func BootstrapClient(ctx context.Context, token string, options ...TLSOption) (*http.Client, error) { - b, err := createBootstrap(token) + b, err := createBootstrap(token) //nolint:contextcheck // deeply nested context; temporary if err != nil { return nil, err } @@ -120,7 +120,7 @@ func BootstrapServer(ctx context.Context, token string, base *http.Server, optio return nil, errors.New("server TLSConfig is already set") } - b, err := createBootstrap(token) + b, err := createBootstrap(token) //nolint:contextcheck // deeply nested context; temporary if err != nil { return nil, err } @@ -169,7 +169,7 @@ func BootstrapServer(ctx context.Context, token string, base *http.Server, optio // ... // register services // srv.Serve(lis) func BootstrapListener(ctx context.Context, token string, inner net.Listener, options ...TLSOption) (net.Listener, error) { - b, err := createBootstrap(token) + b, err := createBootstrap(token) //nolint:contextcheck // deeply nested context; temporary if err != nil { return nil, err } diff --git a/ca/ca.go b/ca/ca.go index 880f7e46..b8f65332 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -13,6 +13,7 @@ import ( "reflect" "strings" "sync" + "time" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" @@ -126,13 +127,15 @@ type CA struct { insecureSrv *server.Server opts *options renewer *TLSRenewer + compactStop chan struct{} } // New creates and initializes the CA with the given configuration and options. func New(cfg *config.Config, opts ...Option) (*CA, error) { ca := &CA{ - config: cfg, - opts: new(options), + config: cfg, + opts: new(options), + compactStop: make(chan struct{}), } ca.opts.apply(opts) return ca.Init(cfg) @@ -193,7 +196,11 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { api.Route(r) }) - //Add ACME api endpoints in /acme and /1.0/acme + // Mount the CRL to the insecure mux + insecureMux.Get("/crl", api.CRL) + insecureMux.Get("/1.0/crl", api.CRL) + + // Add ACME api endpoints in /acme and /1.0/acme dns := cfg.DNSNames[0] u, err := url.Parse("https://" + cfg.Address) if err != nil { @@ -273,6 +280,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { // helpful routine for logging all routes //dumpRoutes(mux) + //dumpRoutes(insecureMux) // Add monitoring if configured if len(cfg.Monitoring) > 0 { @@ -304,7 +312,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { // only start the insecure server if the insecure address is configured // and, currently, also only when it should serve SCEP endpoints. - if ca.shouldServeSCEPEndpoints() && cfg.InsecureAddress != "" { + if ca.shouldServeInsecureServer() { // TODO: instead opt for having a single server.Server but two // http.Servers handling the HTTP and HTTPS handler? The latter // will probably introduce more complexity in terms of graceful @@ -318,6 +326,23 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { return ca, nil } +// shouldServeInsecureServer returns whether or not the insecure +// server should also be started. This is (currently) only the case +// if the insecure address has been configured AND when a SCEP +// provisioner is configured or when a CRL is configured. +func (ca *CA) shouldServeInsecureServer() bool { + switch { + case ca.config.InsecureAddress == "": + return false + case ca.shouldServeSCEPEndpoints(): + return true + case ca.config.CRL.IsEnabled(): + return true + default: + return false + } +} + // buildContext builds the server base context. func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB acme.DB, acmeLinker acme.Linker) context.Context { ctx := authority.NewContext(context.Background(), a) @@ -370,6 +395,12 @@ func (ca *CA) Run() error { } } + wg.Add(1) + go func() { + defer wg.Done() + ca.runCompactJob() + }() + if ca.insecureSrv != nil { wg.Add(1) go func() { @@ -394,6 +425,7 @@ func (ca *CA) Run() error { // Stop stops the CA calling to the server Shutdown method. func (ca *CA) Stop() error { + close(ca.compactStop) ca.renewer.Stop() if err := ca.auth.Shutdown(); err != nil { log.Printf("error stopping ca.Authority: %+v\n", err) @@ -576,3 +608,39 @@ func (ca *CA) getConfigFileOutput() string { } return "loaded from token" } + +// runCompactJob will run the value log garbage collector if the nosql database +// supports it. +func (ca *CA) runCompactJob() { + caDB, ok := ca.auth.GetDatabase().(*db.DB) + if !ok { + return + } + compactor, ok := caDB.DB.(nosql.Compactor) + if !ok { + return + } + + // Compact database at start. + runCompact(compactor) + + // Compact database every minute. + ticker := time.NewTicker(time.Minute) + defer ticker.Stop() + + for { + select { + case <-ca.compactStop: + return + case <-ticker.C: + runCompact(compactor) + } + } +} + +// runCompact executes the compact job until it returns an error. +func runCompact(c nosql.Compactor) { + for err := error(nil); err == nil; { + err = c.Compact(0.7) + } +} diff --git a/ca/client.go b/ca/client.go index bbafcfee..7321f82f 100644 --- a/ca/client.go +++ b/ca/client.go @@ -2,6 +2,7 @@ package ca import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -75,7 +76,11 @@ func (c *uaClient) SetTransport(tr http.RoundTripper) { } func (c *uaClient) Get(u string) (*http.Response, error) { - req, err := http.NewRequest("GET", u, http.NoBody) + return c.GetWithContext(context.Background(), u) +} + +func (c *uaClient) GetWithContext(ctx context.Context, u string) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, "GET", u, http.NoBody) if err != nil { return nil, errors.Wrapf(err, "create GET %s request failed", u) } @@ -84,7 +89,11 @@ func (c *uaClient) Get(u string) (*http.Response, error) { } func (c *uaClient) Post(u, contentType string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest("POST", u, body) + return c.PostWithContext(context.Background(), u, contentType, body) +} + +func (c *uaClient) PostWithContext(ctx context.Context, u, contentType string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, "POST", u, body) if err != nil { return nil, errors.Wrapf(err, "create POST %s request failed", u) } @@ -581,18 +590,24 @@ func (c *Client) SetTransport(tr http.RoundTripper) { c.client.SetTransport(tr) } -// Version performs the version request to the CA and returns the +// Version performs the version request to the CA with an empty context and returns the // api.VersionResponse struct. func (c *Client) Version() (*api.VersionResponse, error) { + return c.VersionWithContext(context.Background()) +} + +// VersionWithContext performs the version request to the CA with the provided context +// and returns the api.VersionResponse struct. +func (c *Client) VersionWithContext(ctx context.Context) (*api.VersionResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/version"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -605,18 +620,24 @@ retry: return &version, nil } -// Health performs the health request to the CA and returns the -// api.HealthResponse struct. +// Health performs the health request to the CA with an empty context +// and returns the api.HealthResponse struct. func (c *Client) Health() (*api.HealthResponse, error) { + return c.HealthWithContext(context.Background()) +} + +// HealthWithContext performs the health request to the CA with the provided context +// and returns the api.HealthResponse struct. +func (c *Client) HealthWithContext(ctx context.Context) (*api.HealthResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/health"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -629,21 +650,29 @@ retry: return &health, nil } -// Root performs the root request to the CA with the given SHA256 and returns -// the api.RootResponse struct. It uses an insecure client, but it checks the -// resulting root certificate with the given SHA256, returning an error if they -// do not match. +// Root performs the root request to the CA with an empty context and the provided +// SHA256 and returns the api.RootResponse struct. It uses an insecure client, but +// it checks the resulting root certificate with the given SHA256, returning an error +// if they do not match. func (c *Client) Root(sha256Sum string) (*api.RootResponse, error) { + return c.RootWithContext(context.Background(), sha256Sum) +} + +// RootWithContext performs the root request to the CA with an empty context and the provided +// SHA256 and returns the api.RootResponse struct. It uses an insecure client, but +// it checks the resulting root certificate with the given SHA256, returning an error +// if they do not match. +func (c *Client) RootWithContext(ctx context.Context, sha256Sum string) (*api.RootResponse, error) { var retried bool sha256Sum = strings.ToLower(strings.ReplaceAll(sha256Sum, "-", "")) u := c.endpoint.ResolveReference(&url.URL{Path: "/root/" + sha256Sum}) retry: - resp, err := newInsecureClient().Get(u.String()) + resp, err := newInsecureClient().GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -661,9 +690,15 @@ retry: return &root, nil } -// Sign performs the sign request to the CA and returns the api.SignResponse -// struct. +// Sign performs the sign request to the CA with an empty context and returns +// the api.SignResponse struct. func (c *Client) Sign(req *api.SignRequest) (*api.SignResponse, error) { + return c.SignWithContext(context.Background(), req) +} + +// SignWithContext performs the sign request to the CA with the provided context +// and returns the api.SignResponse struct. +func (c *Client) SignWithContext(ctx context.Context, req *api.SignRequest) (*api.SignResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -671,12 +706,12 @@ func (c *Client) Sign(req *api.SignRequest) (*api.SignResponse, error) { } u := c.endpoint.ResolveReference(&url.URL{Path: "/sign"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -692,19 +727,30 @@ retry: return &sign, nil } -// Renew performs the renew request to the CA and returns the api.SignResponse -// struct. +// Renew performs the renew request to the CA with an empty context and +// returns the api.SignResponse struct. func (c *Client) Renew(tr http.RoundTripper) (*api.SignResponse, error) { + return c.RenewWithContext(context.Background(), tr) +} + +// RenewWithContext performs the renew request to the CA with the provided context +// and returns the api.SignResponse struct. +func (c *Client) RenewWithContext(ctx context.Context, tr http.RoundTripper) (*api.SignResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/renew"}) client := &http.Client{Transport: tr} retry: - resp, err := client.Post(u.String(), "application/json", http.NoBody) + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), http.NoBody) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -718,12 +764,19 @@ retry: } // RenewWithToken performs the renew request to the CA with the given -// authorization token and returns the api.SignResponse struct. This method is -// generally used to renew an expired certificate. +// authorization token and and empty context and returns the api.SignResponse struct. +// This method is generally used to renew an expired certificate. func (c *Client) RenewWithToken(token string) (*api.SignResponse, error) { + return c.RenewWithTokenAndContext(context.Background(), token) +} + +// RenewWithTokenAndContext performs the renew request to the CA with the given +// authorization token and context and returns the api.SignResponse struct. +// This method is generally used to renew an expired certificate. +func (c *Client) RenewWithTokenAndContext(ctx context.Context, token string) (*api.SignResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/renew"}) - req, err := http.NewRequest("POST", u.String(), http.NoBody) + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), http.NoBody) if err != nil { return nil, errors.Wrapf(err, "create POST %s request failed", u) } @@ -734,7 +787,7 @@ retry: return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -747,24 +800,34 @@ retry: return &sign, nil } -// Rekey performs the rekey request to the CA and returns the api.SignResponse -// struct. +// Rekey performs the rekey request to the CA with an empty context and +// returns the api.SignResponse struct. func (c *Client) Rekey(req *api.RekeyRequest, tr http.RoundTripper) (*api.SignResponse, error) { + return c.RekeyWithContext(context.Background(), req, tr) +} + +// RekeyWithContext performs the rekey request to the CA with the provided context +// and returns the api.SignResponse struct. +func (c *Client) RekeyWithContext(ctx context.Context, req *api.RekeyRequest, tr http.RoundTripper) (*api.SignResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { return nil, errors.Wrap(err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/rekey"}) client := &http.Client{Transport: tr} retry: - resp, err := client.Post(u.String(), "application/json", bytes.NewReader(body)) + httpReq, err := http.NewRequestWithContext(ctx, "POST", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/json") + resp, err := client.Do(httpReq) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -777,9 +840,15 @@ retry: return &sign, nil } -// Revoke performs the revoke request to the CA and returns the api.RevokeResponse -// struct. +// Revoke performs the revoke request to the CA with an empty context and returns +// the api.RevokeResponse struct. func (c *Client) Revoke(req *api.RevokeRequest, tr http.RoundTripper) (*api.RevokeResponse, error) { + return c.RevokeWithContext(context.Background(), req, tr) +} + +// RevokeWithContext performs the revoke request to the CA with the provided context and +// returns the api.RevokeResponse struct. +func (c *Client) RevokeWithContext(ctx context.Context, req *api.RevokeRequest, tr http.RoundTripper) (*api.RevokeResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -794,12 +863,12 @@ retry: } u := c.endpoint.ResolveReference(&url.URL{Path: "/revoke"}) - resp, err := client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -812,12 +881,21 @@ retry: return &revoke, nil } -// Provisioners performs the provisioners request to the CA and returns the -// api.ProvisionersResponse struct with a map of provisioners. +// Provisioners performs the provisioners request to the CA with an empty context +// and returns the api.ProvisionersResponse struct with a map of provisioners. // // ProvisionerOption WithProvisionerCursor and WithProvisionLimit can be used to // paginate the provisioners. func (c *Client) Provisioners(opts ...ProvisionerOption) (*api.ProvisionersResponse, error) { + return c.ProvisionersWithContext(context.Background(), opts...) +} + +// ProvisionersWithContext performs the provisioners request to the CA with the provided context +// and returns the api.ProvisionersResponse struct with a map of provisioners. +// +// ProvisionerOption WithProvisionerCursor and WithProvisionLimit can be used to +// paginate the provisioners. +func (c *Client) ProvisionersWithContext(ctx context.Context, opts ...ProvisionerOption) (*api.ProvisionersResponse, error) { var retried bool o := new(ProvisionerOptions) if err := o.Apply(opts); err != nil { @@ -828,12 +906,12 @@ func (c *Client) Provisioners(opts ...ProvisionerOption) (*api.ProvisionersRespo RawQuery: o.rawQuery(), }) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -846,19 +924,26 @@ retry: return &provisioners, nil } -// ProvisionerKey performs the request to the CA to get the encrypted key for -// the given provisioner kid and returns the api.ProvisionerKeyResponse struct -// with the encrypted key. +// ProvisionerKey performs the request to the CA with an empty context to get +// the encrypted key for the given provisioner kid and returns the api.ProvisionerKeyResponse +// struct with the encrypted key. func (c *Client) ProvisionerKey(kid string) (*api.ProvisionerKeyResponse, error) { + return c.ProvisionerKeyWithContext(context.Background(), kid) +} + +// ProvisionerKeyWithContext performs the request to the CA with the provided context to get +// the encrypted key for the given provisioner kid and returns the api.ProvisionerKeyResponse +// struct with the encrypted key. +func (c *Client) ProvisionerKeyWithContext(ctx context.Context, kid string) (*api.ProvisionerKeyResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/provisioners/" + kid + "/encrypted-key"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -871,18 +956,24 @@ retry: return &key, nil } -// Roots performs the get roots request to the CA and returns the -// api.RootsResponse struct. +// Roots performs the get roots request to the CA with an empty context +// and returns the api.RootsResponse struct. func (c *Client) Roots() (*api.RootsResponse, error) { + return c.RootsWithContext(context.Background()) +} + +// RootsWithContext performs the get roots request to the CA with the provided context +// and returns the api.RootsResponse struct. +func (c *Client) RootsWithContext(ctx context.Context) (*api.RootsResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/roots"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -895,18 +986,24 @@ retry: return &roots, nil } -// Federation performs the get federation request to the CA and returns the -// api.FederationResponse struct. +// Federation performs the get federation request to the CA with an empty context +// and returns the api.FederationResponse struct. func (c *Client) Federation() (*api.FederationResponse, error) { + return c.FederationWithContext(context.Background()) +} + +// FederationWithContext performs the get federation request to the CA with the provided context +// and returns the api.FederationResponse struct. +func (c *Client) FederationWithContext(ctx context.Context) (*api.FederationResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/federation"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -919,9 +1016,15 @@ retry: return &federation, nil } -// SSHSign performs the POST /ssh/sign request to the CA and returns the -// api.SSHSignResponse struct. +// SSHSign performs the POST /ssh/sign request to the CA with an empty context +// and returns the api.SSHSignResponse struct. func (c *Client) SSHSign(req *api.SSHSignRequest) (*api.SSHSignResponse, error) { + return c.SSHSignWithContext(context.Background(), req) +} + +// SSHSignWithContext performs the POST /ssh/sign request to the CA with the provided context +// and returns the api.SSHSignResponse struct. +func (c *Client) SSHSignWithContext(ctx context.Context, req *api.SSHSignRequest) (*api.SSHSignResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -929,12 +1032,12 @@ func (c *Client) SSHSign(req *api.SSHSignRequest) (*api.SSHSignResponse, error) } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/sign"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -947,9 +1050,15 @@ retry: return &sign, nil } -// SSHRenew performs the POST /ssh/renew request to the CA and returns the -// api.SSHRenewResponse struct. +// SSHRenew performs the POST /ssh/renew request to the CA with an empty context +// and returns the api.SSHRenewResponse struct. func (c *Client) SSHRenew(req *api.SSHRenewRequest) (*api.SSHRenewResponse, error) { + return c.SSHRenewWithContext(context.Background(), req) +} + +// SSHRenewWithContext performs the POST /ssh/renew request to the CA with the provided context +// and returns the api.SSHRenewResponse struct. +func (c *Client) SSHRenewWithContext(ctx context.Context, req *api.SSHRenewRequest) (*api.SSHRenewResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -957,12 +1066,12 @@ func (c *Client) SSHRenew(req *api.SSHRenewRequest) (*api.SSHRenewResponse, erro } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/renew"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -975,9 +1084,15 @@ retry: return &renew, nil } -// SSHRekey performs the POST /ssh/rekey request to the CA and returns the -// api.SSHRekeyResponse struct. +// SSHRekey performs the POST /ssh/rekey request to the CA with an empty context +// and returns the api.SSHRekeyResponse struct. func (c *Client) SSHRekey(req *api.SSHRekeyRequest) (*api.SSHRekeyResponse, error) { + return c.SSHRekeyWithContext(context.Background(), req) +} + +// SSHRekeyWithContext performs the POST /ssh/rekey request to the CA with the provided context +// and returns the api.SSHRekeyResponse struct. +func (c *Client) SSHRekeyWithContext(ctx context.Context, req *api.SSHRekeyRequest) (*api.SSHRekeyResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -985,12 +1100,12 @@ func (c *Client) SSHRekey(req *api.SSHRekeyRequest) (*api.SSHRekeyResponse, erro } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/rekey"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1003,9 +1118,15 @@ retry: return &rekey, nil } -// SSHRevoke performs the POST /ssh/revoke request to the CA and returns the -// api.SSHRevokeResponse struct. +// SSHRevoke performs the POST /ssh/revoke request to the CA with an empty context +// and returns the api.SSHRevokeResponse struct. func (c *Client) SSHRevoke(req *api.SSHRevokeRequest) (*api.SSHRevokeResponse, error) { + return c.SSHRevokeWithContext(context.Background(), req) +} + +// SSHRevokeWithContext performs the POST /ssh/revoke request to the CA with the provided context +// and returns the api.SSHRevokeResponse struct. +func (c *Client) SSHRevokeWithContext(ctx context.Context, req *api.SSHRevokeRequest) (*api.SSHRevokeResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -1013,12 +1134,12 @@ func (c *Client) SSHRevoke(req *api.SSHRevokeRequest) (*api.SSHRevokeResponse, e } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/revoke"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1031,18 +1152,24 @@ retry: return &revoke, nil } -// SSHRoots performs the GET /ssh/roots request to the CA and returns the -// api.SSHRootsResponse struct. +// SSHRoots performs the GET /ssh/roots request to the CA with an empty context +// and returns the api.SSHRootsResponse struct. func (c *Client) SSHRoots() (*api.SSHRootsResponse, error) { + return c.SSHRootsWithContext(context.Background()) +} + +// SSHRootsWithContext performs the GET /ssh/roots request to the CA with the provided context +// and returns the api.SSHRootsResponse struct. +func (c *Client) SSHRootsWithContext(ctx context.Context) (*api.SSHRootsResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/roots"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1055,18 +1182,24 @@ retry: return &keys, nil } -// SSHFederation performs the get /ssh/federation request to the CA and returns -// the api.SSHRootsResponse struct. +// SSHFederation performs the get /ssh/federation request to the CA with an empty context +// and returns the api.SSHRootsResponse struct. func (c *Client) SSHFederation() (*api.SSHRootsResponse, error) { + return c.SSHFederationWithContext(context.Background()) +} + +// SSHFederationWithContext performs the get /ssh/federation request to the CA with the provided context +// and returns the api.SSHRootsResponse struct. +func (c *Client) SSHFederationWithContext(ctx context.Context) (*api.SSHRootsResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/federation"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1079,9 +1212,15 @@ retry: return &keys, nil } -// SSHConfig performs the POST /ssh/config request to the CA to get the ssh -// configuration templates. +// SSHConfig performs the POST /ssh/config request to the CA with an empty context +// to get the ssh configuration templates. func (c *Client) SSHConfig(req *api.SSHConfigRequest) (*api.SSHConfigResponse, error) { + return c.SSHConfigWithContext(context.Background(), req) +} + +// SSHConfigWithContext performs the POST /ssh/config request to the CA with the provided context +// to get the ssh configuration templates. +func (c *Client) SSHConfigWithContext(ctx context.Context, req *api.SSHConfigRequest) (*api.SSHConfigResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -1089,12 +1228,12 @@ func (c *Client) SSHConfig(req *api.SSHConfigRequest) (*api.SSHConfigResponse, e } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/config"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1107,9 +1246,15 @@ retry: return &cfg, nil } -// SSHCheckHost performs the POST /ssh/check-host request to the CA with the -// given principal. +// SSHCheckHost performs the POST /ssh/check-host request to the CA with an empty context, +// the principal and a token and returns the api.SSHCheckPrincipalResponse. func (c *Client) SSHCheckHost(principal, token string) (*api.SSHCheckPrincipalResponse, error) { + return c.SSHCheckHostWithContext(context.Background(), principal, token) +} + +// SSHCheckHostWithContext performs the POST /ssh/check-host request to the CA with the provided context, +// principal and token and returns the api.SSHCheckPrincipalResponse. +func (c *Client) SSHCheckHostWithContext(ctx context.Context, principal, token string) (*api.SSHCheckPrincipalResponse, error) { var retried bool body, err := json.Marshal(&api.SSHCheckPrincipalRequest{ Type: provisioner.SSHHostCert, @@ -1122,12 +1267,12 @@ func (c *Client) SSHCheckHost(principal, token string) (*api.SSHCheckPrincipalRe } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/check-host"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1141,17 +1286,22 @@ retry: return &check, nil } -// SSHGetHosts performs the GET /ssh/get-hosts request to the CA. +// SSHGetHosts performs the GET /ssh/get-hosts request to the CA with an empty context. func (c *Client) SSHGetHosts() (*api.SSHGetHostsResponse, error) { + return c.SSHGetHostsWithContext(context.Background()) +} + +// SSHGetHostsWithContext performs the GET /ssh/get-hosts request to the CA with the provided context. +func (c *Client) SSHGetHostsWithContext(ctx context.Context) (*api.SSHGetHostsResponse, error) { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/hosts"}) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1164,8 +1314,13 @@ retry: return &hosts, nil } -// SSHBastion performs the POST /ssh/bastion request to the CA. +// SSHBastion performs the POST /ssh/bastion request to the CA with an empty context. func (c *Client) SSHBastion(req *api.SSHBastionRequest) (*api.SSHBastionResponse, error) { + return c.SSHBastionWithContext(context.Background(), req) +} + +// SSHBastionWithContext performs the POST /ssh/bastion request to the CA with the provided context. +func (c *Client) SSHBastionWithContext(ctx context.Context, req *api.SSHBastionRequest) (*api.SSHBastionResponse, error) { var retried bool body, err := json.Marshal(req) if err != nil { @@ -1173,12 +1328,12 @@ func (c *Client) SSHBastion(req *api.SSHBastionRequest) (*api.SSHBastionResponse } u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/bastion"}) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.PostWithContext(ctx, u.String(), "application/json", bytes.NewReader(body)) if err != nil { return nil, clientError(err) } if resp.StatusCode >= 400 { - if !retried && c.retryOnError(resp) { + if !retried && c.retryOnError(resp) { //nolint:contextcheck // deeply nested context; retry using the same context retried = true goto retry } @@ -1192,11 +1347,16 @@ retry: } // RootFingerprint is a helper method that returns the current root fingerprint. -// It does an health connection and gets the fingerprint from the TLS verified -// chains. +// It does an health connection and gets the fingerprint from the TLS verified chains. func (c *Client) RootFingerprint() (string, error) { + return c.RootFingerprintWithContext(context.Background()) +} + +// RootFingerprintWithContext is a helper method that returns the current root fingerprint. +// It does an health connection and gets the fingerprint from the TLS verified chains. +func (c *Client) RootFingerprintWithContext(ctx context.Context) (string, error) { u := c.endpoint.ResolveReference(&url.URL{Path: "/health"}) - resp, err := c.client.Get(u.String()) + resp, err := c.client.GetWithContext(ctx, u.String()) if err != nil { return "", clientError(err) } diff --git a/ca/tls.go b/ca/tls.go index 7644b11f..d5d479f3 100644 --- a/ca/tls.go +++ b/ca/tls.go @@ -135,7 +135,7 @@ func (c *Client) getClientTLSConfig(ctx context.Context, sign *api.SignResponse, //nolint:staticcheck // Use mutable tls.Config on renew tr.DialTLS = c.buildDialTLS(tlsCtx) // tr.DialTLSContext = c.buildDialTLSContext(tlsCtx) - renewer.RenewCertificate = getRenewFunc(tlsCtx, c, tr, pk) + renewer.RenewCertificate = getRenewFunc(tlsCtx, c, tr, pk) //nolint:contextcheck // deeply nested context // Update client transport c.SetTransport(tr) @@ -183,7 +183,7 @@ func (c *Client) GetServerTLSConfig(ctx context.Context, sign *api.SignResponse, //nolint:staticcheck // Use mutable tls.Config on renew tr.DialTLS = c.buildDialTLS(tlsCtx) // tr.DialTLSContext = c.buildDialTLSContext(tlsCtx) - renewer.RenewCertificate = getRenewFunc(tlsCtx, c, tr, pk) + renewer.RenewCertificate = getRenewFunc(tlsCtx, c, tr, pk) //nolint:contextcheck // deeply nested context // Update client transport c.SetTransport(tr) diff --git a/cas/stepcas/issuer.go b/cas/stepcas/issuer.go index 07607caa..cf985974 100644 --- a/cas/stepcas/issuer.go +++ b/cas/stepcas/issuer.go @@ -1,6 +1,7 @@ package stepcas import ( + "context" "net/url" "strings" "time" @@ -37,7 +38,7 @@ type stepIssuer interface { } // newStepIssuer returns the configured step issuer. -func newStepIssuer(caURL *url.URL, client *ca.Client, iss *apiv1.CertificateIssuer) (stepIssuer, error) { +func newStepIssuer(ctx context.Context, caURL *url.URL, client *ca.Client, iss *apiv1.CertificateIssuer) (stepIssuer, error) { if err := validateCertificateIssuer(iss); err != nil { return nil, err } @@ -46,7 +47,7 @@ func newStepIssuer(caURL *url.URL, client *ca.Client, iss *apiv1.CertificateIssu case "x5c": return newX5CIssuer(caURL, iss) case "jwk": - return newJWKIssuer(caURL, client, iss) + return newJWKIssuer(ctx, caURL, client, iss) default: return nil, errors.Errorf("stepCAS `certificateIssuer.type` %s is not supported", iss.Type) } diff --git a/cas/stepcas/issuer_test.go b/cas/stepcas/issuer_test.go index 7d468e38..ff4f45f5 100644 --- a/cas/stepcas/issuer_test.go +++ b/cas/stepcas/issuer_test.go @@ -1,6 +1,7 @@ package stepcas import ( + "context" "net/url" "reflect" "testing" @@ -118,7 +119,7 @@ func Test_newStepIssuer(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := newStepIssuer(tt.args.caURL, tt.args.client, tt.args.iss) + got, err := newStepIssuer(context.TODO(), tt.args.caURL, tt.args.client, tt.args.iss) if (err != nil) != tt.wantErr { t.Errorf("newStepIssuer() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cas/stepcas/jwk_issuer.go b/cas/stepcas/jwk_issuer.go index 4ef4f541..5ef017a2 100644 --- a/cas/stepcas/jwk_issuer.go +++ b/cas/stepcas/jwk_issuer.go @@ -1,6 +1,7 @@ package stepcas import ( + "context" "crypto" "encoding/json" "net/url" @@ -21,13 +22,13 @@ type jwkIssuer struct { signer jose.Signer } -func newJWKIssuer(caURL *url.URL, client *ca.Client, cfg *apiv1.CertificateIssuer) (*jwkIssuer, error) { +func newJWKIssuer(ctx context.Context, caURL *url.URL, client *ca.Client, cfg *apiv1.CertificateIssuer) (*jwkIssuer, error) { var err error var signer jose.Signer // Read the key from the CA if not provided. // Or read it from a PEM file. if cfg.Key == "" { - p, err := findProvisioner(client, provisioner.TypeJWK, cfg.Provisioner) + p, err := findProvisioner(ctx, client, provisioner.TypeJWK, cfg.Provisioner) if err != nil { return nil, err } @@ -144,10 +145,10 @@ func newJWKSignerFromEncryptedKey(kid, key, password string) (jose.Signer, error return newJoseSigner(signer, so) } -func findProvisioner(client *ca.Client, typ provisioner.Type, name string) (provisioner.Interface, error) { +func findProvisioner(ctx context.Context, client *ca.Client, typ provisioner.Type, name string) (provisioner.Interface, error) { cursor := "" for { - ps, err := client.Provisioners(ca.WithProvisionerCursor(cursor)) + ps, err := client.ProvisionersWithContext(ctx, ca.WithProvisionerCursor(cursor)) if err != nil { return nil, err } diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index c64963e6..7c0dc86f 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -43,7 +43,7 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) { } // Create client. - client, err := ca.NewClient(opts.CertificateAuthority, ca.WithRootSHA256(opts.CertificateAuthorityFingerprint)) + client, err := ca.NewClient(opts.CertificateAuthority, ca.WithRootSHA256(opts.CertificateAuthorityFingerprint)) //nolint:contextcheck // deeply nested context if err != nil { return nil, err } @@ -52,7 +52,7 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) { // Create configured issuer unless we only want to use GetCertificateAuthority. // This avoid the request for the password if not provided. if !opts.IsCAGetter { - if iss, err = newStepIssuer(caURL, client, opts.CertificateIssuer); err != nil { + if iss, err = newStepIssuer(ctx, caURL, client, opts.CertificateIssuer); err != nil { return nil, err } } diff --git a/cas/stepcas/stepcas_test.go b/cas/stepcas/stepcas_test.go index 6691a4b4..b9dd9abd 100644 --- a/cas/stepcas/stepcas_test.go +++ b/cas/stepcas/stepcas_test.go @@ -245,7 +245,7 @@ func testJWKIssuer(t *testing.T, caURL *url.URL, password string) *jwkIssuer { key = testEncryptedKeyPath password = testPassword } - jwk, err := newJWKIssuer(caURL, client, &apiv1.CertificateIssuer{ + jwk, err := newJWKIssuer(context.TODO(), caURL, client, &apiv1.CertificateIssuer{ Type: "jwk", Provisioner: "ra@doe.org", Key: key, diff --git a/cmd/step-awskms-init/main.go b/cmd/step-awskms-init/main.go deleted file mode 100644 index 81a91067..00000000 --- a/cmd/step-awskms-init/main.go +++ /dev/null @@ -1,248 +0,0 @@ -package main - -import ( - "context" - "crypto" - "crypto/rand" - "crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "flag" - "fmt" - "math/big" - "os" - "time" - - "go.step.sm/cli-utils/fileutil" - "go.step.sm/cli-utils/ui" - "go.step.sm/crypto/kms/apiv1" - "go.step.sm/crypto/kms/awskms" - "go.step.sm/crypto/pemutil" - "golang.org/x/crypto/ssh" -) - -func main() { - var credentialsFile, region string - var enableSSH bool - flag.StringVar(&credentialsFile, "credentials-file", "", "Path to the `file` containing the AWS KMS credentials.") - flag.StringVar(®ion, "region", "", "AWS KMS region name.") - flag.BoolVar(&enableSSH, "ssh", false, "Create SSH keys.") - flag.Usage = usage - flag.Parse() - - // Initialize windows terminal - ui.Init() - - ui.Println("⚠️ This command is deprecated and will be removed in future releases.") - ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.") - - c, err := awskms.New(context.Background(), apiv1.Options{ - Type: apiv1.AmazonKMS, - Region: region, - CredentialsFile: credentialsFile, - }) - if err != nil { - fatal(err) - } - - if err := createX509(c); err != nil { - fatal(err) - } - - if enableSSH { - ui.Println() - if err := createSSH(c); err != nil { - fatal(err) - } - } - - // Reset windows terminal - ui.Reset() -} - -func fatal(err error) { - fmt.Fprintln(os.Stderr, err) - ui.Reset() - os.Exit(1) -} - -func usage() { - fmt.Fprintln(os.Stderr, "Usage: step-awskms-init") - fmt.Fprintln(os.Stderr, ` -The step-awskms-init command initializes a public key infrastructure (PKI) -to be used by step-ca. - -This tool is experimental and in the future it will be integrated in step cli. - -OPTIONS`) - fmt.Fprintln(os.Stderr) - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, ` -COPYRIGHT - - (c) 2018-%d Smallstep Labs, Inc. -`, time.Now().Year()) - os.Exit(1) -} - -func createX509(c *awskms.KMS) error { - ui.Println("Creating X.509 PKI ...") - - // Root Certificate - resp, err := c.CreateKey(&apiv1.CreateKeyRequest{ - Name: "root", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - - signer, err := c.CreateSigner(&resp.CreateSignerRequest) - if err != nil { - return err - } - - now := time.Now() - root := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 1, - MaxPathLenZero: false, - Issuer: pkix.Name{CommonName: "Smallstep Root"}, - Subject: pkix.Name{CommonName: "Smallstep Root"}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(resp.PublicKey), - AuthorityKeyId: mustSubjectKeyID(resp.PublicKey), - } - - b, err := x509.CreateCertificate(rand.Reader, root, root, resp.PublicKey, signer) - if err != nil { - return err - } - - if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - ui.PrintSelected("Root Key", resp.Name) - ui.PrintSelected("Root Certificate", "root_ca.crt") - - root, err = pemutil.ReadCertificate("root_ca.crt") - if err != nil { - return err - } - - // Intermediate Certificate - resp, err = c.CreateKey(&apiv1.CreateKeyRequest{ - Name: "intermediate", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - - intermediate := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 0, - MaxPathLenZero: true, - Issuer: root.Subject, - Subject: pkix.Name{CommonName: "Smallstep Intermediate"}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(resp.PublicKey), - } - - b, err = x509.CreateCertificate(rand.Reader, intermediate, root, resp.PublicKey, signer) - if err != nil { - return err - } - - if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - ui.PrintSelected("Intermediate Key", resp.Name) - ui.PrintSelected("Intermediate Certificate", "intermediate_ca.crt") - - return nil -} - -func createSSH(c *awskms.KMS) error { - ui.Println("Creating SSH Keys ...") - - // User Key - resp, err := c.CreateKey(&apiv1.CreateKeyRequest{ - Name: "ssh-user-key", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - - key, err := ssh.NewPublicKey(resp.PublicKey) - if err != nil { - return err - } - - if err := fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { - return err - } - - ui.PrintSelected("SSH User Public Key", "ssh_user_ca_key.pub") - ui.PrintSelected("SSH User Private Key", resp.Name) - - // Host Key - resp, err = c.CreateKey(&apiv1.CreateKeyRequest{ - Name: "ssh-host-key", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - - key, err = ssh.NewPublicKey(resp.PublicKey) - if err != nil { - return err - } - - if err := fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { - return err - } - - ui.PrintSelected("SSH Host Public Key", "ssh_host_ca_key.pub") - ui.PrintSelected("SSH Host Private Key", resp.Name) - - return nil -} - -func mustSerialNumber() *big.Int { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - sn, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - panic(err) - } - return sn -} - -func mustSubjectKeyID(key crypto.PublicKey) []byte { - b, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - panic(err) - } - //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - hash := sha1.Sum(b) - return hash[:] -} diff --git a/cmd/step-cloudkms-init/main.go b/cmd/step-cloudkms-init/main.go deleted file mode 100644 index 6cc36adf..00000000 --- a/cmd/step-cloudkms-init/main.go +++ /dev/null @@ -1,286 +0,0 @@ -package main - -import ( - "context" - "crypto" - "crypto/rand" - "crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "flag" - "fmt" - "math/big" - "os" - "strings" - "time" - - "go.step.sm/cli-utils/fileutil" - "go.step.sm/cli-utils/ui" - "go.step.sm/crypto/kms/apiv1" - "go.step.sm/crypto/kms/cloudkms" - "go.step.sm/crypto/pemutil" - "golang.org/x/crypto/ssh" -) - -func main() { - var credentialsFile string - var project, location, ring string - var protectionLevelName string - var enableSSH bool - flag.StringVar(&credentialsFile, "credentials-file", "", "Path to the `file` containing the Google's Cloud KMS credentials.") - flag.StringVar(&project, "project", "", "Google Cloud Project ID.") - flag.StringVar(&location, "location", "global", "Cloud KMS location name.") - flag.StringVar(&ring, "ring", "pki", "Cloud KMS ring name.") - flag.StringVar(&protectionLevelName, "protection-level", "SOFTWARE", "Protection level to use, SOFTWARE or HSM.") - flag.BoolVar(&enableSSH, "ssh", false, "Create SSH keys.") - flag.Usage = usage - flag.Parse() - - switch { - case project == "": - usage() - case location == "": - fmt.Fprintln(os.Stderr, "flag `--location` is required") - os.Exit(1) - case ring == "": - fmt.Fprintln(os.Stderr, "flag `--ring` is required") - os.Exit(1) - case protectionLevelName == "": - fmt.Fprintln(os.Stderr, "flag `--protection-level` is required") - os.Exit(1) - } - - var protectionLevel apiv1.ProtectionLevel - switch strings.ToUpper(protectionLevelName) { - case "SOFTWARE": - protectionLevel = apiv1.Software - case "HSM": - protectionLevel = apiv1.HSM - default: - fmt.Fprintf(os.Stderr, "invalid value `%s` for flag `--protection-level`; options are `SOFTWARE` or `HSM`\n", protectionLevelName) - os.Exit(1) - } - - // Initialize windows terminal - ui.Init() - - ui.Println("⚠️ This command is deprecated and will be removed in future releases.") - ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.") - - c, err := cloudkms.New(context.Background(), apiv1.Options{ - Type: apiv1.CloudKMS, - CredentialsFile: credentialsFile, - }) - if err != nil { - fatal(err) - } - - if err := createPKI(c, project, location, ring, protectionLevel); err != nil { - fatal(err) - } - - if enableSSH { - ui.Println() - if err := createSSH(c, project, location, ring, protectionLevel); err != nil { - fatal(err) - } - } - - // Reset windows terminal - ui.Reset() -} - -func fatal(err error) { - fmt.Fprintln(os.Stderr, err) - ui.Reset() - os.Exit(1) -} - -func usage() { - fmt.Fprintln(os.Stderr, "Usage: step-cloudkms-init --project ") - fmt.Fprintln(os.Stderr, ` -The step-cloudkms-init command initializes a public key infrastructure (PKI) -to be used by step-ca. - -This tool is experimental and in the future it will be integrated in step cli. - -OPTIONS`) - fmt.Fprintln(os.Stderr) - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, ` -COPYRIGHT - - (c) 2018-%d Smallstep Labs, Inc. -`, time.Now().Year()) - os.Exit(1) -} - -func createPKI(c *cloudkms.CloudKMS, project, location, keyRing string, protectionLevel apiv1.ProtectionLevel) error { - ui.Println("Creating PKI ...") - - parent := "projects/" + project + "/locations/" + location + "/keyRings/" + keyRing + "/cryptoKeys" - - // Root Certificate - resp, err := c.CreateKey(&apiv1.CreateKeyRequest{ - Name: parent + "/root", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - ProtectionLevel: protectionLevel, - }) - if err != nil { - return err - } - - signer, err := c.CreateSigner(&resp.CreateSignerRequest) - if err != nil { - return err - } - - now := time.Now() - root := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 1, - MaxPathLenZero: false, - Issuer: pkix.Name{CommonName: "Smallstep Root"}, - Subject: pkix.Name{CommonName: "Smallstep Root"}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(resp.PublicKey), - AuthorityKeyId: mustSubjectKeyID(resp.PublicKey), - } - - b, err := x509.CreateCertificate(rand.Reader, root, root, resp.PublicKey, signer) - if err != nil { - return err - } - - if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - ui.PrintSelected("Root Key", resp.Name) - ui.PrintSelected("Root Certificate", "root_ca.crt") - - root, err = pemutil.ReadCertificate("root_ca.crt") - if err != nil { - return err - } - - // Intermediate Certificate - resp, err = c.CreateKey(&apiv1.CreateKeyRequest{ - Name: parent + "/intermediate", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - ProtectionLevel: protectionLevel, - }) - if err != nil { - return err - } - - intermediate := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 0, - MaxPathLenZero: true, - Issuer: root.Subject, - Subject: pkix.Name{CommonName: "Smallstep Intermediate"}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(resp.PublicKey), - } - - b, err = x509.CreateCertificate(rand.Reader, intermediate, root, resp.PublicKey, signer) - if err != nil { - return err - } - - if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - ui.PrintSelected("Intermediate Key", resp.Name) - ui.PrintSelected("Intermediate Certificate", "intermediate_ca.crt") - - return nil -} - -func createSSH(c *cloudkms.CloudKMS, project, location, keyRing string, protectionLevel apiv1.ProtectionLevel) error { - ui.Println("Creating SSH Keys ...") - - parent := "projects/" + project + "/locations/" + location + "/keyRings/" + keyRing + "/cryptoKeys" - - // User Key - resp, err := c.CreateKey(&apiv1.CreateKeyRequest{ - Name: parent + "/ssh-user-key", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - ProtectionLevel: protectionLevel, - }) - if err != nil { - return err - } - - key, err := ssh.NewPublicKey(resp.PublicKey) - if err != nil { - return err - } - - if err := fileutil.WriteFile("ssh_user_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { - return err - } - - ui.PrintSelected("SSH User Public Key", "ssh_user_ca_key.pub") - ui.PrintSelected("SSH User Private Key", resp.Name) - - // Host Key - resp, err = c.CreateKey(&apiv1.CreateKeyRequest{ - Name: parent + "/ssh-host-key", - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - ProtectionLevel: protectionLevel, - }) - if err != nil { - return err - } - - key, err = ssh.NewPublicKey(resp.PublicKey) - if err != nil { - return err - } - - if err := fileutil.WriteFile("ssh_host_ca_key.pub", ssh.MarshalAuthorizedKey(key), 0600); err != nil { - return err - } - - ui.PrintSelected("SSH Host Public Key", "ssh_host_ca_key.pub") - ui.PrintSelected("SSH Host Private Key", resp.Name) - - return nil -} - -func mustSerialNumber() *big.Int { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - sn, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - panic(err) - } - return sn -} - -func mustSubjectKeyID(key crypto.PublicKey) []byte { - b, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - panic(err) - } - //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - hash := sha1.Sum(b) - return hash[:] -} diff --git a/cmd/step-pkcs11-init/main.go b/cmd/step-pkcs11-init/main.go deleted file mode 100644 index 30258cdd..00000000 --- a/cmd/step-pkcs11-init/main.go +++ /dev/null @@ -1,553 +0,0 @@ -package main - -import ( - "context" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "flag" - "fmt" - "math/big" - "os" - "runtime" - "time" - - "github.com/pkg/errors" - "go.step.sm/cli-utils/fileutil" - "go.step.sm/cli-utils/ui" - "go.step.sm/crypto/kms" - "go.step.sm/crypto/kms/apiv1" - "go.step.sm/crypto/kms/uri" - "go.step.sm/crypto/pemutil" - - // Enable pkcs11. - _ "go.step.sm/crypto/kms/pkcs11" -) - -// Config is a mapping of the cli flags. -type Config struct { - KMS string - GenerateRoot bool - RootObject string - RootKeyObject string - RootSubject string - RootPath string - CrtObject string - CrtPath string - CrtKeyObject string - CrtSubject string - CrtKeyPath string - SSHHostKeyObject string - SSHUserKeyObject string - RootFile string - KeyFile string - Pin string - PinFile string - NoCerts bool - EnableSSH bool - Force bool - Extractable bool -} - -// Validate checks the flags in the config. -func (c *Config) Validate() error { - switch { - case c.KMS == "": - return errors.New("flag `--kms` is required") - case c.CrtPath == "": - return errors.New("flag `--crt-cert-path` is required") - case c.RootFile != "" && c.KeyFile == "": - return errors.New("flag `--root-cert-file` requires flag `--root-key-file`") - case c.KeyFile != "" && c.RootFile == "": - return errors.New("flag `--root-key-file` requires flag `--root-cert-file`") - case c.RootFile == "" && c.RootObject == "": - return errors.New("one of flag `--root-cert-file` or `--root-cert-obj` is required") - case c.KeyFile == "" && c.RootKeyObject == "": - return errors.New("one of flag `--root-key-file` or `--root-key-obj` is required") - case c.CrtKeyPath == "" && c.CrtKeyObject == "": - return errors.New("one of flag `--crt-key-path` or `--crt-key-obj` is required") - case c.RootFile == "" && c.GenerateRoot && c.RootKeyObject == "": - return errors.New("flag `--root-gen` requires flag `--root-key-obj`") - case c.RootFile == "" && c.GenerateRoot && c.RootPath == "": - return errors.New("flag `--root-gen` requires `--root-cert-path`") - case c.Pin != "" && c.PinFile != "": - return errors.New("Only set one of pin and pin-file") - default: - if c.RootFile != "" { - c.GenerateRoot = false - c.RootObject = "" - c.RootKeyObject = "" - } - if c.CrtKeyPath != "" { - c.CrtObject = "" - c.CrtKeyObject = "" - } - if !c.EnableSSH { - c.SSHHostKeyObject = "" - c.SSHUserKeyObject = "" - } - return nil - } -} - -func main() { - var kmsuri string - switch runtime.GOOS { - case "darwin": - kmsuri = "pkcs11:module-path=/usr/local/lib/pkcs11/yubihsm_pkcs11.dylib;token=YubiHSM" - case "linux": - kmsuri = "pkcs11:module-path=/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so;token=YubiHSM" - case "windows": - if home, err := os.UserHomeDir(); err == nil { - kmsuri = "pkcs11:module-path=" + home + "\\yubihsm2-sdk\\bin\\yubihsm_pkcs11.dll" + ";token=YubiHSM" - } - } - - var c Config - flag.StringVar(&c.KMS, "kms", kmsuri, "PKCS #11 URI with the module-path and token to connect to the module.") - flag.StringVar(&c.Pin, "pin", "", "PKCS #11 PIN") - flag.StringVar(&c.PinFile, "pin-file", "", "PKCS #11 PIN File") - // Option 1: Generate new root - flag.BoolVar(&c.GenerateRoot, "root-gen", true, "Enable the generation of a root key.") - flag.StringVar(&c.RootSubject, "root-name", "PKCS #11 Smallstep Root", "Subject and Issuer of the root certificate.") - flag.StringVar(&c.RootObject, "root-cert-obj", "pkcs11:id=7330;object=root-cert", "PKCS #11 URI with object id and label to store the root certificate.") - flag.StringVar(&c.RootKeyObject, "root-key-obj", "pkcs11:id=7330;object=root-key", "PKCS #11 URI with object id and label to store the root key.") - // Option 2: Read root from disk and sign intermediate - flag.StringVar(&c.RootFile, "root-cert-file", "", "Path to the root certificate to use.") - flag.StringVar(&c.KeyFile, "root-key-file", "", "Path to the root key to use.") - // Option 3: Generate certificate signing request - flag.StringVar(&c.CrtSubject, "crt-name", "PKCS #11 Smallstep Intermediate", "Subject of the intermediate certificate.") - flag.StringVar(&c.CrtObject, "crt-cert-obj", "pkcs11:id=7331;object=intermediate-cert", "PKCS #11 URI with object id and label to store the intermediate certificate.") - flag.StringVar(&c.CrtKeyObject, "crt-key-obj", "pkcs11:id=7331;object=intermediate-key", "PKCS #11 URI with object id and label to store the intermediate certificate.") - // SSH certificates - flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.") - flag.StringVar(&c.SSHHostKeyObject, "ssh-host-key", "pkcs11:id=7332;object=ssh-host-key", "PKCS #11 URI with object id and label to store the key used to sign SSH host certificates.") - flag.StringVar(&c.SSHUserKeyObject, "ssh-user-key", "pkcs11:id=7333;object=ssh-user-key", "PKCS #11 URI with object id and label to store the key used to sign SSH user certificates.") - // Output files - flag.StringVar(&c.RootPath, "root-cert-path", "root_ca.crt", "Location to write the root certificate.") - flag.StringVar(&c.CrtPath, "crt-cert-path", "intermediate_ca.crt", "Location to write the intermediate certificate.") - flag.StringVar(&c.CrtKeyPath, "crt-key-path", "", "Location to write the intermediate private key.") - // Others - flag.BoolVar(&c.NoCerts, "no-certs", false, "Do not store certificates in the module.") - flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.") - flag.BoolVar(&c.Extractable, "extractable", false, "Allow export of private keys under wrap.") - flag.Usage = usage - flag.Parse() - - if err := c.Validate(); err != nil { - fatal(err) - } - - u, err := uri.ParseWithScheme("pkcs11", c.KMS) - if err != nil { - fatal(err) - } - - // Initialize windows terminal - ui.Init() - - ui.Println("⚠️ This command is deprecated and will be removed in future releases.") - ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.") - - switch { - case u.Get("pin-value") != "": - case u.Get("pin-source") != "": - case c.Pin != "": - case c.PinFile != "": - content, err := os.ReadFile(c.PinFile) - if err != nil { - fatal(err) - } - c.Pin = string(content) - - default: - pin, err := ui.PromptPassword("What is the PKCS#11 PIN?") - if err != nil { - fatal(err) - } - c.Pin = string(pin) - } - - k, err := kms.New(context.Background(), apiv1.Options{ - Type: apiv1.PKCS11, - URI: c.KMS, - Pin: c.Pin, - }) - if err != nil { - fatal(err) - } - - defer func() { - _ = k.Close() - }() - - // Check if the slots are empty, fail if they are not - certUris := []string{ - c.RootObject, c.CrtObject, - } - keyUris := []string{ - c.RootKeyObject, c.CrtKeyObject, - c.SSHHostKeyObject, c.SSHUserKeyObject, - } - if !c.Force { - for _, u := range certUris { - if u != "" && !c.NoCerts { - checkObject(k, u) - checkCertificate(k, u) - } - } - for _, u := range keyUris { - if u != "" { - checkObject(k, u) - } - } - } else { - deleter, ok := k.(interface { - DeleteKey(uri string) error - DeleteCertificate(uri string) error - }) - if ok { - for _, u := range certUris { - if u != "" && !c.NoCerts { - // Some HSMs like Nitrokey will overwrite the key with the - // certificate label. - if err := deleter.DeleteKey(u); err != nil { - fatalClose(err, k) - } - if err := deleter.DeleteCertificate(u); err != nil { - fatalClose(err, k) - } - } - } - for _, u := range keyUris { - if u != "" { - if err := deleter.DeleteKey(u); err != nil { - fatalClose(err, k) - } - } - } - } - } - - if err := createPKI(k, c); err != nil { - fatalClose(err, k) - } - - // Reset windows terminal - ui.Reset() -} - -func fatal(err error) { - if os.Getenv("STEPDEBUG") == "1" { - fmt.Fprintf(os.Stderr, "%+v\n", err) - } else { - fmt.Fprintln(os.Stderr, err) - } - ui.Reset() - os.Exit(1) -} - -func fatalClose(err error, k kms.KeyManager) { - _ = k.Close() - fatal(err) -} - -func usage() { - fmt.Fprintln(os.Stderr, "Usage: step-pkcs11-init") - fmt.Fprintln(os.Stderr, ` -The step-pkcs11-init command initializes a public key infrastructure (PKI) -to be used by step-ca. - -This tool is experimental and in the future it will be integrated in step cli. - -OPTIONS`) - fmt.Fprintln(os.Stderr) - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, ` -COPYRIGHT - - (c) 2018-%d Smallstep Labs, Inc. -`, time.Now().Year()) - os.Exit(1) -} - -func checkCertificate(k kms.KeyManager, rawuri string) { - if cm, ok := k.(kms.CertificateManager); ok { - if _, err := cm.LoadCertificate(&apiv1.LoadCertificateRequest{ - Name: rawuri, - }); err == nil { - fmt.Fprintf(os.Stderr, "⚠️ Your PKCS #11 module already has a certificate on %s.\n", rawuri) - fmt.Fprintln(os.Stderr, " If you want to delete it and start fresh, use `--force`.") - _ = k.Close() - os.Exit(1) - } - } -} - -func checkObject(k kms.KeyManager, rawuri string) { - if _, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{ - Name: rawuri, - }); err == nil { - fmt.Fprintf(os.Stderr, "⚠️ Your PKCS #11 module already has a key on %s.\n", rawuri) - fmt.Fprintln(os.Stderr, " If you want to delete it and start fresh, use `--force`.") - _ = k.Close() - os.Exit(1) - } -} - -func createPKI(k kms.KeyManager, c Config) error { - var err error - ui.Println("Creating PKI ...") - now := time.Now() - - // Root Certificate - var signer crypto.Signer - var root *x509.Certificate - switch { - case c.GenerateRoot: - resp, err := k.CreateKey(&apiv1.CreateKeyRequest{ - Name: c.RootKeyObject, - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - Extractable: c.Extractable, - }) - if err != nil { - return err - } - - signer, err = k.CreateSigner(&resp.CreateSignerRequest) - if err != nil { - return err - } - - template := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 1, - MaxPathLenZero: false, - Issuer: pkix.Name{CommonName: c.RootSubject}, - Subject: pkix.Name{CommonName: c.RootSubject}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(resp.PublicKey), - AuthorityKeyId: mustSubjectKeyID(resp.PublicKey), - } - - b, err := x509.CreateCertificate(rand.Reader, template, template, resp.PublicKey, signer) - if err != nil { - return err - } - - root, err = x509.ParseCertificate(b) - if err != nil { - return errors.Wrap(err, "error parsing root certificate") - } - - if cm, ok := k.(kms.CertificateManager); ok && c.RootObject != "" && !c.NoCerts { - if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ - Name: c.RootObject, - Certificate: root, - Extractable: c.Extractable, - }); err != nil { - return err - } - } else { - c.RootObject = "" - } - - if err := fileutil.WriteFile(c.RootPath, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - ui.PrintSelected("Root Key", resp.Name) - ui.PrintSelected("Root Certificate", c.RootPath) - if c.RootObject != "" { - ui.PrintSelected("Root Certificate Object", c.RootObject) - } - case c.RootFile != "" && c.KeyFile != "": // Read Root From File - root, err = pemutil.ReadCertificate(c.RootFile) - if err != nil { - return err - } - - key, err := pemutil.Read(c.KeyFile) - if err != nil { - return err - } - - var ok bool - if signer, ok = key.(crypto.Signer); !ok { - return errors.Errorf("key type '%T' does not implement a signer", key) - } - } - - // Intermediate Certificate - var keyName string - var publicKey crypto.PublicKey - var intSigner crypto.Signer - if c.CrtKeyPath != "" { - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return errors.Wrap(err, "error creating intermediate key") - } - - pass, err := ui.PromptPasswordGenerate("What do you want your password to be? [leave empty and we'll generate one]", - ui.WithRichPrompt()) - if err != nil { - return err - } - - _, err = pemutil.Serialize(priv, pemutil.WithPassword(pass), pemutil.ToFile(c.CrtKeyPath, 0600)) - if err != nil { - return err - } - - publicKey = priv.Public() - intSigner = priv - } else { - resp, err := k.CreateKey(&apiv1.CreateKeyRequest{ - Name: c.CrtKeyObject, - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - Extractable: c.Extractable, - }) - if err != nil { - return err - } - publicKey = resp.PublicKey - keyName = resp.Name - - intSigner, err = k.CreateSigner(&resp.CreateSignerRequest) - if err != nil { - return err - } - } - - if root != nil { - template := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 0, - MaxPathLenZero: true, - Issuer: root.Subject, - Subject: pkix.Name{CommonName: c.CrtSubject}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(publicKey), - } - - b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer) - if err != nil { - return err - } - - intermediate, err := x509.ParseCertificate(b) - if err != nil { - return errors.Wrap(err, "error parsing intermediate certificate") - } - - if cm, ok := k.(kms.CertificateManager); ok && c.CrtObject != "" && !c.NoCerts { - if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ - Name: c.CrtObject, - Certificate: intermediate, - Extractable: c.Extractable, - }); err != nil { - return err - } - } else { - c.CrtObject = "" - } - - if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - } else { - // No root available, generate CSR for external root. - csrTemplate := x509.CertificateRequest{ - Subject: pkix.Name{CommonName: c.CrtSubject}, - SignatureAlgorithm: x509.ECDSAWithSHA256, - } - // step: generate the csr request - csrCertificate, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, intSigner) - if err != nil { - return err - } - if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE REQUEST", - Bytes: csrCertificate, - }), 0600); err != nil { - return err - } - } - - if c.CrtKeyPath != "" { - ui.PrintSelected("Intermediate Key", c.CrtKeyPath) - } else { - ui.PrintSelected("Intermediate Key", keyName) - } - - if root != nil { - ui.PrintSelected("Intermediate Certificate", c.CrtPath) - if c.CrtObject != "" { - ui.PrintSelected("Intermediate Certificate Object", c.CrtObject) - } - } else { - ui.PrintSelected("Intermediate Certificate Request", c.CrtPath) - } - - if c.SSHHostKeyObject != "" { - resp, err := k.CreateKey(&apiv1.CreateKeyRequest{ - Name: c.SSHHostKeyObject, - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - ui.PrintSelected("SSH Host Key", resp.Name) - } - - if c.SSHUserKeyObject != "" { - resp, err := k.CreateKey(&apiv1.CreateKeyRequest{ - Name: c.SSHUserKeyObject, - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - ui.PrintSelected("SSH User Key", resp.Name) - } - - return nil -} - -func mustSerialNumber() *big.Int { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - sn, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - panic(err) - } - return sn -} - -func mustSubjectKeyID(key crypto.PublicKey) []byte { - b, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - panic(err) - } - //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - hash := sha1.Sum(b) - return hash[:] -} diff --git a/cmd/step-yubikey-init/main.go b/cmd/step-yubikey-init/main.go deleted file mode 100644 index cd6018cf..00000000 --- a/cmd/step-yubikey-init/main.go +++ /dev/null @@ -1,355 +0,0 @@ -package main - -import ( - "context" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha1" //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - "crypto/x509" - "crypto/x509/pkix" - "encoding/hex" - "encoding/pem" - "flag" - "fmt" - "math/big" - "os" - "time" - - "github.com/pkg/errors" - "go.step.sm/cli-utils/fileutil" - "go.step.sm/cli-utils/ui" - "go.step.sm/crypto/kms" - "go.step.sm/crypto/kms/apiv1" - "go.step.sm/crypto/pemutil" - - // Enable yubikey. - _ "go.step.sm/crypto/kms/yubikey" -) - -// Config is a mapping of the cli flags. -type Config struct { - RootOnly bool - RootSlot string - CrtSlot string - RootFile string - KeyFile string - Pin string - ManagementKey string - Force bool -} - -// Validate checks the flags in the config. -func (c *Config) Validate() error { - switch { - case c.ManagementKey != "" && len(c.ManagementKey) != 48: - return errors.New("flag `--management-key` must be 48 hexadecimal characters (24 bytes)") - case c.RootFile != "" && c.KeyFile == "": - return errors.New("flag `--root` requires flag `--key`") - case c.KeyFile != "" && c.RootFile == "": - return errors.New("flag `--key` requires flag `--root`") - case c.RootOnly && c.RootFile != "": - return errors.New("flag `--root-only` is incompatible with flag `--root`") - case c.RootSlot == c.CrtSlot: - return errors.New("flag `--root-slot` and flag `--crt-slot` cannot be the same") - case c.RootFile == "" && c.RootSlot == "": - return errors.New("one of flag `--root` or `--root-slot` is required") - default: - if c.RootFile != "" { - c.RootSlot = "" - } - if c.RootOnly { - c.CrtSlot = "" - } - if c.ManagementKey != "" { - if _, err := hex.DecodeString(c.ManagementKey); err != nil { - return errors.Wrap(err, "flag `--management-key` is not valid") - } - } - return nil - } -} - -func main() { - var c Config - flag.StringVar(&c.ManagementKey, "management-key", "", `Management key to use in hexadecimal format. (default "010203040506070801020304050607080102030405060708")`) - flag.BoolVar(&c.RootOnly, "root-only", false, "Slot only the root certificate and sign and intermediate.") - flag.StringVar(&c.RootSlot, "root-slot", "9a", "Slot to store the root certificate.") - flag.StringVar(&c.CrtSlot, "crt-slot", "9c", "Slot to store the intermediate certificate.") - flag.StringVar(&c.RootFile, "root", "", "Path to the root certificate to use.") - flag.StringVar(&c.KeyFile, "key", "", "Path to the root key to use.") - flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.") - flag.Usage = usage - flag.Parse() - - if err := c.Validate(); err != nil { - fatal(err) - } - - // Initialize windows terminal - ui.Init() - - ui.Println("⚠️ This command is deprecated and will be removed in future releases.") - ui.Println("⚠️ Please use https://github.com/smallstep/step-kms-plugin instead.") - - pin, err := ui.PromptPassword("What is the YubiKey PIN?") - if err != nil { - fatal(err) - } - c.Pin = string(pin) - - k, err := kms.New(context.Background(), apiv1.Options{ - Type: apiv1.YubiKey, - Pin: c.Pin, - ManagementKey: c.ManagementKey, - }) - if err != nil { - fatal(err) - } - - // Check if the slots are empty, fail if they are not - if !c.Force { - switch { - case c.RootSlot != "": - checkSlot(k, c.RootSlot) - case c.CrtSlot != "": - checkSlot(k, c.CrtSlot) - } - } - - if err := createPKI(k, c); err != nil { - fatal(err) - } - - defer func() { - _ = k.Close() - }() - - // Reset windows terminal - ui.Reset() -} - -func fatal(err error) { - if os.Getenv("STEPDEBUG") == "1" { - fmt.Fprintf(os.Stderr, "%+v\n", err) - } else { - fmt.Fprintln(os.Stderr, err) - } - ui.Reset() - os.Exit(1) -} - -func usage() { - fmt.Fprintln(os.Stderr, "Usage: step-yubikey-init") - fmt.Fprintln(os.Stderr, ` -The step-yubikey-init command initializes a public key infrastructure (PKI) -to be used by step-ca. - -This tool is experimental and in the future it will be integrated in step cli. - -OPTIONS`) - fmt.Fprintln(os.Stderr) - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, ` -COPYRIGHT - - (c) 2018-%d Smallstep Labs, Inc. -`, time.Now().Year()) - os.Exit(1) -} - -func checkSlot(k kms.KeyManager, slot string) { - if _, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{ - Name: slot, - }); err == nil { - fmt.Fprintf(os.Stderr, "⚠️ Your YubiKey already has a key in the slot %s.\n", slot) - fmt.Fprintln(os.Stderr, " If you want to delete it and start fresh, use `--force`.") - os.Exit(1) - } -} - -func createPKI(k kms.KeyManager, c Config) error { - var err error - ui.Println("Creating PKI ...") - now := time.Now() - - // Root Certificate - var signer crypto.Signer - var root *x509.Certificate - if c.RootFile != "" && c.KeyFile != "" { - root, err = pemutil.ReadCertificate(c.RootFile) - if err != nil { - return err - } - - key, err := pemutil.Read(c.KeyFile) - if err != nil { - return err - } - - var ok bool - if signer, ok = key.(crypto.Signer); !ok { - return errors.Errorf("key type '%T' does not implement a signer", key) - } - } else { - resp, err := k.CreateKey(&apiv1.CreateKeyRequest{ - Name: c.RootSlot, - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - - signer, err = k.CreateSigner(&resp.CreateSignerRequest) - if err != nil { - return err - } - - template := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 1, - MaxPathLenZero: false, - Issuer: pkix.Name{CommonName: "YubiKey Smallstep Root"}, - Subject: pkix.Name{CommonName: "YubiKey Smallstep Root"}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(resp.PublicKey), - AuthorityKeyId: mustSubjectKeyID(resp.PublicKey), - } - - b, err := x509.CreateCertificate(rand.Reader, template, template, resp.PublicKey, signer) - if err != nil { - return err - } - - root, err = x509.ParseCertificate(b) - if err != nil { - return errors.Wrap(err, "error parsing root certificate") - } - - if cm, ok := k.(kms.CertificateManager); ok { - if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ - Name: c.RootSlot, - Certificate: root, - }); err != nil { - return err - } - } - - if err := fileutil.WriteFile("root_ca.crt", pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - ui.PrintSelected("Root Key", resp.Name) - ui.PrintSelected("Root Certificate", "root_ca.crt") - } - - // Intermediate Certificate - var keyName string - var publicKey crypto.PublicKey - if c.RootOnly { - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return errors.Wrap(err, "error creating intermediate key") - } - - pass, err := ui.PromptPasswordGenerate("What do you want your password to be? [leave empty and we'll generate one]", - ui.WithRichPrompt()) - if err != nil { - return err - } - - _, err = pemutil.Serialize(priv, pemutil.WithPassword(pass), pemutil.ToFile("intermediate_ca_key", 0600)) - if err != nil { - return err - } - - publicKey = priv.Public() - } else { - resp, err := k.CreateKey(&apiv1.CreateKeyRequest{ - Name: c.CrtSlot, - SignatureAlgorithm: apiv1.ECDSAWithSHA256, - }) - if err != nil { - return err - } - publicKey = resp.PublicKey - keyName = resp.Name - } - - template := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour * 24 * 365 * 10), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 0, - MaxPathLenZero: true, - Issuer: root.Subject, - Subject: pkix.Name{CommonName: "YubiKey Smallstep Intermediate"}, - SerialNumber: mustSerialNumber(), - SubjectKeyId: mustSubjectKeyID(publicKey), - } - - b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer) - if err != nil { - return err - } - - intermediate, err := x509.ParseCertificate(b) - if err != nil { - return errors.Wrap(err, "error parsing intermediate certificate") - } - - if cm, ok := k.(kms.CertificateManager); ok { - if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{ - Name: c.CrtSlot, - Certificate: intermediate, - }); err != nil { - return err - } - } - - if err := fileutil.WriteFile("intermediate_ca.crt", pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: b, - }), 0600); err != nil { - return err - } - - if c.RootOnly { - ui.PrintSelected("Intermediate Key", "intermediate_ca_key") - } else { - ui.PrintSelected("Intermediate Key", keyName) - } - - ui.PrintSelected("Intermediate Certificate", "intermediate_ca.crt") - - return nil -} - -func mustSerialNumber() *big.Int { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - sn, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - panic(err) - } - return sn -} - -func mustSubjectKeyID(key crypto.PublicKey) []byte { - b, err := x509.MarshalPKIXPublicKey(key) - if err != nil { - panic(err) - } - //nolint:gosec // used to create the Subject Key Identifier by RFC 5280 - hash := sha1.Sum(b) - return hash[:] -} diff --git a/commands/app.go b/commands/app.go index 31d62f87..e5c6ea1e 100644 --- a/commands/app.go +++ b/commands/app.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "path/filepath" + "strconv" "strings" "unicode" @@ -29,7 +30,7 @@ var AppCommand = cli.Command{ Action: appAction, UsageText: `**step-ca** [**--password-file**=] [**--ssh-host-password-file**=] [**--ssh-user-password-file**=] -[**--issuer-password-file**=] [**--resolver**=]`, +[**--issuer-password-file**=] [**--pidfile**=] [**--resolver**=]`, Flags: []cli.Flag{ cli.StringFlag{ Name: "password-file", @@ -82,6 +83,10 @@ Requires **--insecure** flag.`, Usage: `the used on tls-alpn-01 challenges. It can be changed for testing purposes. Requires **--insecure** flag.`, }, + cli.StringFlag{ + Name: "pidfile", + Usage: "the path to the to write the process ID.", + }, cli.BoolFlag{ Name: "insecure", Usage: "enable insecure flags.", @@ -89,6 +94,8 @@ Requires **--insecure** flag.`, }, } +var pidfile string + // AppAction is the action used when the top command runs. func appAction(ctx *cli.Context) error { passFile := ctx.String("password-file") @@ -141,6 +148,13 @@ func appAction(ctx *cli.Context) error { cfg, err := config.LoadConfiguration(configFile) if err != nil && token == "" { + var pathErr *os.PathError + if errors.As(err, &pathErr) { + fmt.Println("step-ca can't find or open the configuration file for your CA.") + fmt.Println("You may need to create a CA first by running `step ca init`.") + fmt.Println("Documentation: https://u.step.sm/docs/ca") + os.Exit(1) + } fatal(err) } @@ -213,6 +227,15 @@ To get a linked authority token: issuerPassword = bytes.TrimRightFunc(issuerPassword, unicode.IsSpace) } + if filename := ctx.String("pidfile"); filename != "" { + pid := []byte(strconv.Itoa(os.Getpid()) + "\n") + //nolint:gosec // 0644 (-rw-r--r--) are common permissions for a pid file + if err := os.WriteFile(filename, pid, 0644); err != nil { + fatal(errors.Wrap(err, "error writing pidfile")) + } + pidfile = filename + } + // replace resolver if requested if resolver != "" { net.DefaultResolver.PreferGo = true @@ -237,6 +260,11 @@ To get a linked authority token: if err = srv.Run(); err != nil && !errors.Is(err, http.ErrServerClosed) { fatal(err) } + + if pidfile != "" { + os.Remove(pidfile) + } + return nil } @@ -269,5 +297,8 @@ func fatal(err error) { } else { fmt.Fprintln(os.Stderr, err) } + if pidfile != "" { + os.Remove(pidfile) + } os.Exit(2) } diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 8267efed..00000000 --- a/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -step-ca (0.8.4-14-ge72f087-dev) unstable; urgency=medium - - * See https://github.com/smallstep/certificates/releases - - -- Smallstep Labs, Inc. Wed, 20 Feb 2019 20:44:25 +0000 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index f599e28b..00000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/debian/control b/debian/control deleted file mode 100644 index 0a3f6680..00000000 --- a/debian/control +++ /dev/null @@ -1,15 +0,0 @@ -Source: step-ca -Section: utils -Priority: optional -Maintainer: Smallstep Labs, Inc. -Build-Depends: debhelper (>= 9), git, bash-completion -Standards-Version: 4.2.0 -Homepage: https://github.com/smallstep/certificates -Vcs-Browser: https://github.com/smallstep/certificates.git -Vcs-Git: https://github.com/smallstep/certificates.git - -Package: step-ca -Architecture: any -Depends: ${misc:Depends} -Description: Smallstep Certificate Authority - step-ca is the Smallstep Certificate Authority. diff --git a/debian/rules b/debian/rules deleted file mode 100755 index f5b70196..00000000 --- a/debian/rules +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/make -f - -override_dh_install-arch: - dh_install --arch - -build: - dh build - -override_dh_auto_build: - dh_auto_build -- build - -%: - dh $@ diff --git a/debian/source/format b/debian/source/format deleted file mode 100644 index 163aaf8d..00000000 --- a/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/docker/Dockerfile.step-ca b/docker/Dockerfile similarity index 56% rename from docker/Dockerfile.step-ca rename to docker/Dockerfile index ed6b5f56..553d831f 100644 --- a/docker/Dockerfile.step-ca +++ b/docker/Dockerfile @@ -3,19 +3,17 @@ FROM golang:alpine AS builder WORKDIR /src COPY . . -RUN apk add --no-cache curl git make -RUN make V=1 download -RUN make V=1 bin/step-ca bin/step-awskms-init bin/step-cloudkms-init +RUN apk add --no-cache curl git make libcap +RUN make V=1 bin/step-ca +RUN setcap CAP_NET_BIND_SERVICE=+eip bin/step-ca +FROM smallstep/step-kms-plugin:cloud AS kms FROM smallstep/step-cli:latest COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca -COPY --from=builder /src/bin/step-awskms-init /usr/local/bin/step-awskms-init -COPY --from=builder /src/bin/step-cloudkms-init /usr/local/bin/step-cloudkms-init +COPY --from=kms /usr/local/bin/step-kms-plugin /usr/local/bin/step-kms-plugin -USER root -RUN apk add --no-cache libcap && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca USER step ENV CONFIGPATH="/home/step/config/ca.json" diff --git a/docker/Dockerfile.hsm b/docker/Dockerfile.hsm new file mode 100644 index 00000000..8ae1e7c7 --- /dev/null +++ b/docker/Dockerfile.hsm @@ -0,0 +1,36 @@ +FROM golang AS builder + +WORKDIR /src +COPY . . + +RUN apt-get update +RUN apt-get install -y --no-install-recommends \ + gcc pkgconf libpcsclite-dev libcap2-bin +RUN make V=1 GOFLAGS="" bin/step-ca +RUN setcap CAP_NET_BIND_SERVICE=+eip bin/step-ca + +FROM smallstep/step-kms-plugin:bullseye AS kms + +FROM smallstep/step-cli:bullseye + +COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca +COPY --from=kms /usr/local/bin/step-kms-plugin /usr/local/bin/step-kms-plugin + +USER root +RUN apt-get update +RUN apt-get install -y --no-install-recommends pcscd libpcsclite1 +RUN mkdir -p /run/pcscd +RUN chown step:step /run/pcscd +USER step + +ENV CONFIGPATH="/home/step/config/ca.json" +ENV PWDPATH="/home/step/secrets/password" + +VOLUME ["/home/step"] +STOPSIGNAL SIGTERM +HEALTHCHECK CMD step ca health 2>/dev/null | grep "^ok" >/dev/null + +COPY docker/entrypoint.sh /entrypoint.sh + +ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] +CMD exec /usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH diff --git a/docker/Dockerfile.step-ca.hsm b/docker/Dockerfile.step-ca.hsm deleted file mode 100644 index 8f413cd7..00000000 --- a/docker/Dockerfile.step-ca.hsm +++ /dev/null @@ -1,35 +0,0 @@ -FROM golang:alpine AS builder - -WORKDIR /src -COPY . . - -RUN apk add --no-cache curl git make -RUN apk add --no-cache gcc musl-dev pkgconf pcsc-lite-dev -RUN make V=1 download -RUN make V=1 GOFLAGS="" build - - -FROM smallstep/step-cli:latest - -COPY --from=builder /src/bin/step-ca /usr/local/bin/step-ca -COPY --from=builder /src/bin/step-awskms-init /usr/local/bin/step-awskms-init -COPY --from=builder /src/bin/step-cloudkms-init /usr/local/bin/step-cloudkms-init -COPY --from=builder /src/bin/step-pkcs11-init /usr/local/bin/step-pkcs11-init -COPY --from=builder /src/bin/step-yubikey-init /usr/local/bin/step-yubikey-init - -USER root -RUN apk add --no-cache libcap && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca -RUN apk add --no-cache pcsc-lite pcsc-lite-libs -USER step - -ENV CONFIGPATH="/home/step/config/ca.json" -ENV PWDPATH="/home/step/secrets/password" - -VOLUME ["/home/step"] -STOPSIGNAL SIGTERM -HEALTHCHECK CMD step ca health 2>/dev/null | grep "^ok" >/dev/null - -COPY docker/entrypoint.sh /entrypoint.sh - -ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] -CMD exec /usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 49d6b10c..93312ca8 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -19,7 +19,7 @@ function init_if_possible () { fi done if [ ${missing_vars} = 1 ]; then - >&2 echo "there is no ca.json config file; please run step ca init, or provide config parameters via DOCKER_STEPCA_INIT_ vars" + >&2 echo "there is no ca.json config file; please run step ca init, or provide config parameters via DOCKER_STEPCA_INIT_ vars" else step_ca_init "${@}" fi @@ -34,31 +34,53 @@ function generate_password () { # Initialize a CA if not already initialized function step_ca_init () { + DOCKER_STEPCA_INIT_PROVISIONER_NAME="${DOCKER_STEPCA_INIT_PROVISIONER_NAME:-admin}" + DOCKER_STEPCA_INIT_ADMIN_SUBJECT="${DOCKER_STEPCA_INIT_ADMIN_SUBJECT:-step}" + DOCKER_STEPCA_INIT_ADDRESS="${DOCKER_STEPCA_INIT_ADDRESS:-:9000}" + local -a setup_args=( --name "${DOCKER_STEPCA_INIT_NAME}" - --dns "${DOCKER_STEPCA_INIT_DNS_NAMES}" - --provisioner "${DOCKER_STEPCA_INIT_PROVISIONER_NAME:-admin}" - --password-file "${STEPPATH}/password" - --address ":9000" + --dns "${DOCKER_STEPCA_INIT_DNS_NAMES}" + --provisioner "${DOCKER_STEPCA_INIT_PROVISIONER_NAME}" + --password-file "${STEPPATH}/password" + --provisioner-password-file "${STEPPATH}/provisioner_password" + --address "${DOCKER_STEPCA_INIT_ADDRESS}" ) if [ -n "${DOCKER_STEPCA_INIT_PASSWORD}" ]; then echo "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password" + echo "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/provisioner_password" else generate_password > "${STEPPATH}/password" + generate_password > "${STEPPATH}/provisioner_password" fi - if [ -n "${DOCKER_STEPCA_INIT_SSH}" ]; then + if [ "${DOCKER_STEPCA_INIT_SSH}" == "true" ]; then setup_args=("${setup_args[@]}" --ssh) fi + if [ "${DOCKER_STEPCA_INIT_ACME}" == "true" ]; then + setup_args=("${setup_args[@]}" --acme) + fi + if [ "${DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT}" == "true" ]; then + setup_args=("${setup_args[@]}" --remote-management + --admin-subject "${DOCKER_STEPCA_INIT_ADMIN_SUBJECT}" + ) + fi step ca init "${setup_args[@]}" + echo "" + if [ "${DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT}" == "true" ]; then + echo "👉 Your CA administrative username is: ${DOCKER_STEPCA_INIT_ADMIN_SUBJECT}" + fi + echo "👉 Your CA administrative password is: $(< $STEPPATH/provisioner_password )" + echo "🤫 This will only be displayed once." + shred -u $STEPPATH/provisioner_password mv $STEPPATH/password $PWDPATH } if [ -f /usr/sbin/pcscd ]; then - /usr/sbin/pcscd + /usr/sbin/pcscd fi if [ ! -f "${STEPPATH}/config/ca.json" ]; then - init_if_possible + init_if_possible fi exec "${@}" diff --git a/go.mod b/go.mod index ccbdd424..4bf5e5e2 100644 --- a/go.mod +++ b/go.mod @@ -3,150 +3,138 @@ module github.com/smallstep/certificates go 1.18 require ( - cloud.google.com/go v0.105.0 // indirect - cloud.google.com/go/longrunning v0.3.0 - cloud.google.com/go/security v1.10.0 - github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.28 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 - github.com/ThalesIgnite/crypto11 v1.2.5 // indirect - github.com/aws/aws-sdk-go v1.44.132 // indirect - github.com/dgraph-io/ristretto v0.1.0 // indirect - github.com/fatih/color v1.9.0 // indirect + cloud.google.com/go/longrunning v0.4.1 + cloud.google.com/go/security v1.14.0 + github.com/Masterminds/sprig/v3 v3.2.3 github.com/fxamacker/cbor/v2 v2.4.0 github.com/go-chi/chi v4.1.2+incompatible - github.com/go-kit/kit v0.10.0 // indirect - github.com/go-piv/piv-go v1.10.0 // indirect - github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/golang/mock v1.6.0 + github.com/google/go-attestation v0.4.4-0.20220404204839-8820d49b18d9 github.com/google/go-cmp v0.5.9 + github.com/google/go-tpm v0.3.3 github.com/google/uuid v1.3.0 - github.com/googleapis/gax-go/v2 v2.7.0 - github.com/hashicorp/vault/api v1.8.2 - github.com/hashicorp/vault/api/auth/approle v0.3.0 - github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 - github.com/jhump/protoreflect v1.9.0 // indirect - github.com/kr/pretty v0.3.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect + github.com/googleapis/gax-go/v2 v2.8.0 + github.com/hashicorp/vault/api v1.9.1 + github.com/hashicorp/vault/api/auth/approle v0.4.0 + github.com/hashicorp/vault/api/auth/kubernetes v0.4.0 github.com/micromdm/scep/v2 v2.1.0 - github.com/newrelic/go-agent/v3 v3.20.1 + github.com/newrelic/go-agent/v3 v3.21.1 github.com/pkg/errors v0.9.1 - github.com/rs/xid v1.4.0 + github.com/rs/xid v1.5.0 github.com/sirupsen/logrus v1.9.0 github.com/slackhq/nebula v1.6.1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 - github.com/smallstep/nosql v0.5.0 - github.com/stretchr/testify v1.8.1 - github.com/urfave/cli v1.22.10 + github.com/smallstep/nosql v0.6.0 + github.com/stretchr/testify v1.8.2 + github.com/urfave/cli v1.22.13 go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 - go.step.sm/cli-utils v0.7.5 - go.step.sm/crypto v0.23.1 - go.step.sm/linkedca v0.19.0 - golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b - golang.org/x/net v0.2.0 - golang.org/x/sys v0.2.0 // indirect - golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect - google.golang.org/api v0.103.0 - google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.51.0 - google.golang.org/protobuf v1.28.1 + go.step.sm/cli-utils v0.7.6 + go.step.sm/crypto v0.29.3 + go.step.sm/linkedca v0.19.1 + golang.org/x/crypto v0.8.0 + golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 + golang.org/x/net v0.9.0 + google.golang.org/api v0.120.0 + google.golang.org/grpc v1.54.0 + google.golang.org/protobuf v1.30.0 gopkg.in/square/go-jose.v2 v2.6.0 + ) require ( - cloud.google.com/go/compute v1.12.1 // indirect - cloud.google.com/go/compute/metadata v0.2.1 // indirect - cloud.google.com/go/iam v0.6.0 // indirect - cloud.google.com/go/kms v1.6.0 // indirect + cloud.google.com/go v0.110.0 // indirect + cloud.google.com/go/compute v1.19.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v0.13.0 // indirect + cloud.google.com/go/kms v1.10.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/armon/go-metrics v0.3.9 // indirect - github.com/armon/go-radix v1.0.0 // indirect + github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/ThalesIgnite/crypto11 v1.2.5 // indirect + github.com/aws/aws-sdk-go v1.44.240 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/badger v1.6.2 // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/go-kit/kit v0.10.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/go-piv/piv-go v1.11.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/certificate-transparency-go v1.1.4 // indirect + github.com/google/go-tpm-tools v0.3.11 // indirect + github.com/google/go-tspi v0.3.0 // indirect + github.com/google/s2a-go v0.1.2 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v0.16.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.5 // indirect github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/go-uuid v1.0.2 // indirect - github.com/hashicorp/go-version v1.2.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/vault/sdk v0.6.0 // indirect - github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect - github.com/huandu/xstrings v1.3.2 // indirect + github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.13.0 // indirect + github.com/jackc/pgconn v1.14.0 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.1 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.12.0 // indirect - github.com/jackc/pgx/v4 v4.17.2 // indirect + github.com/jackc/pgproto3/v2 v2.3.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.15.11 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/run v1.0.0 // indirect - github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/peterbourgon/diskv/v3 v3.0.1 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/schollz/jsonstore v1.1.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/bbolt v1.3.6 // indirect + go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.1.0 // indirect google.golang.org/appengine v1.6.7 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -157,4 +145,7 @@ require ( // replace go.step.sm/linkedca => ../linkedca // use github.com/smallstep/pkcs7 fork with patches applied -replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20221024180420-e1aab68dda05 +replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948 + +// use github.com/smallstep/go-attestation fork with patches for Windows AK support applied +replace github.com/google/go-attestation v0.4.4-0.20220404204839-8820d49b18d9 => github.com/smallstep/go-attestation v0.4.4-0.20230224121042-1bcb20a75add diff --git a/go.sum b/go.sum index 79aa559b..615d2765 100644 --- a/go.sum +++ b/go.sum @@ -1,61 +1,123 @@ +bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/iam v0.6.0 h1:nsqQC88kT5Iwlm4MeNGTpfMWddp6NB/UOLFTH6m1QfQ= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/security v1.10.0 h1:KSKzzJMyUoMRQzcz7azIgqAUqxo7rmQ5rYvimMhikqg= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/security v1.14.0 h1:ujoEatlM890TPMVv3EBcoVfVh0DibTTTwy+lkUDE+kE= +cloud.google.com/go/security v1.14.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs= +cloud.google.com/go/spanner v1.18.0/go.mod h1:LvAjUXPeJRGNuGpikMULjhLj/t9cRvdc+fxRoLiugXA= +cloud.google.com/go/spanner v1.25.0/go.mod h1:kQUft3x355hzzaeFbObjsvkzZDgpDkesp3v75WBnI8w= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= +code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= +contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= +contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= +contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= +contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= +contrib.go.opencensus.io/exporter/stackdriver v0.13.8/go.mod h1:huNtlWx75MwO7qMs0KrMxPZXzNNWebav1Sq/pm02JdQ= +contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= +contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo= -github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2htVQTBY8nOZpyajYztF0vUvSZTuM= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= +github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= +github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= +github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= +github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 h1:UE9n9rkJF62ArLb1F3DEjRt8O3jLwMWdSoypKV4f3MU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -64,75 +126,120 @@ github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= +github.com/apache/beam v2.28.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o= +github.com/apache/beam v2.32.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= +github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.44.132 h1:+IjL9VoR0OXScQ5gyme9xjcolwUkd3uaH144f4Ao+4s= -github.com/aws/aws-sdk-go v1.44.132/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.44.240 h1:38f1qBTuzotDC6bgSNLw1vrrYaoWL8MNNzwTsGjP6TY= +github.com/aws/aws-sdk-go v1.44.240/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= +github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= @@ -145,8 +252,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -154,72 +262,103 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 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.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= -github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= -github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fullstorydev/grpcurl v1.8.0/go.mod h1:Mn2jWbdMrQGJQ8UD62uNyMumT2acsZUCkZIqFxsQf1o= +github.com/fullstorydev/grpcurl v1.8.1/go.mod h1:3BWhvHZwNO7iLXaQlojdg5NA6SxUDePli4ecpK1N7gw= +github.com/fullstorydev/grpcurl v1.8.2/go.mod h1:YvWNT3xRp2KIRuvCphFodG0fKkMXwaxA9CJgKCcyzUQ= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-piv/piv-go v1.10.0 h1:P1Y1VjBI5DnXW0+YkKmTuh5opWnMIrKriUaIOblee9Q= -github.com/go-piv/piv-go v1.10.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= +github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= +github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -227,6 +366,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -236,127 +376,190 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= +github.com/google/certificate-transparency-go v1.1.2-0.20210422104406-9f33727a7a18/go.mod h1:6CKh9dscIRoqc2kC6YUFICHZMT9NrClyPrRVFrdw1QQ= +github.com/google/certificate-transparency-go v1.1.2-0.20210512142713-bed466244fa6/go.mod h1:aF2dp7Dh81mY8Y/zpzyXps4fQW5zQbDu2CxfpJB6NkI= +github.com/google/certificate-transparency-go v1.1.2/go.mod h1:3OL+HKDqHPUfdKrHVQxO6T8nDLO0HF7LRTlkIWXaWvQ= +github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= +github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= +github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= +github.com/google/go-sev-guest v0.5.2 h1:dlCehnxU9aJWEIcTb0j7oZ/yM4qeno7AO6zWokb4mu0= +github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= +github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= +github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= +github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= +github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= +github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= +github.com/google/go-tpm-tools v0.3.9/go.mod h1:22JvWmHcD5w55cs+nMeqDGDxgNS15/2pDq2cLqnc3rc= +github.com/google/go-tpm-tools v0.3.11 h1:imObhmECgDS+ua4aAVPkMfCzE9LTZjS/MmVMCrAG4VY= +github.com/google/go-tpm-tools v0.3.11/go.mod h1:5UcOsOyG5B2hWhKsqNI3TtOjTcZs5sh+3913uMN29Y8= +github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= +github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= +github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/licenseclassifier v0.0.0-20210325184830-bb04aff29e72/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= +github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= +github.com/google/s2a-go v0.1.2 h1:WVtYAYuYxKeYajAmThMRYWP6K3wXkcqbGHeUgeubUHY= +github.com/google/s2a-go v0.1.2/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= +github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/trillian v1.3.14-0.20210409160123-c5ea3abd4a41/go.mod h1:1dPv0CUjNQVFEDuAUFhZql16pw/VlPgaX8qj+g5pVzQ= +github.com/google/trillian v1.3.14-0.20210511103300-67b5f349eefa/go.mod h1:s4jO3Ai4NSvxucdvqUHON0bCqJyoya32eNw6XJwsmNc= +github.com/google/trillian v1.4.0/go.mod h1:1Bja2nEgMDlEJWWRXBUemSPG9qYw84ZYX2gHRVHlR+g= +github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ= +github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= -github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= -github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= -github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E= -github.com/hashicorp/vault/api v1.8.2 h1:C7OL9YtOtwQbTKI9ogB0A1wffRbCN+rH/LLCHO3d8HM= -github.com/hashicorp/vault/api v1.8.2/go.mod h1:ML8aYzBIhY5m1MD1B2Q0JV89cC85YVH4t5kBaZiyVaE= -github.com/hashicorp/vault/api/auth/approle v0.3.0 h1:Ib0oCNXsCq/QZhPYtXPzJEbGS5WR/KoZf8c84QoFdkU= -github.com/hashicorp/vault/api/auth/approle v0.3.0/go.mod h1:hm51TbjzUkPO0Y17wkrpwOpvyyMRpXJNueTHiG04t3k= -github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 h1:HkaCmTKzcgLa2tjdiAid1rbmyQNmQGHfnmvIIM2WorY= -github.com/hashicorp/vault/api/auth/kubernetes v0.3.0/go.mod h1:l1B4MGtLc+P37MabBQiIhP3qd9agj0vqhETmaQjjC/Y= -github.com/hashicorp/vault/sdk v0.6.0 h1:6Z+In5DXHiUfZvIZdMx7e2loL1PPyDjA4bVh9ZTIAhs= -github.com/hashicorp/vault/sdk v0.6.0/go.mod h1:+DRpzoXIdMvKc88R4qxr+edwy/RvH5QK8itmxLiDHLc= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/vault/api v1.9.0/go.mod h1:lloELQP4EyhjnCQhF8agKvWIVTmxbpEJj70b98959sM= +github.com/hashicorp/vault/api v1.9.1 h1:LtY/I16+5jVGU8rufyyAkwopgq/HpUnxFBg+QLOAV38= +github.com/hashicorp/vault/api v1.9.1/go.mod h1:78kktNcQYbBGSrOjQfHjXN32OhhxXnbYl3zxpd2uPUs= +github.com/hashicorp/vault/api/auth/approle v0.4.0 h1:tjJHoUkPx8zRoFlFy86uvgg/1gpTnDPp0t0BYWTKjjw= +github.com/hashicorp/vault/api/auth/approle v0.4.0/go.mod h1:D2gEpR0aS/F/MEcSjmhUlOsuK1RMVZojsnIQAEf0EV0= +github.com/hashicorp/vault/api/auth/kubernetes v0.4.0 h1:f6OIOF9012JIdqYvOeeewxhtQdJosnog2CHzh33j41s= +github.com/hashicorp/vault/api/auth/kubernetes v0.4.0/go.mod h1:tMewM2hPyFNKP1EXdWbc0dUHHoS5V/0qS04BEaxuy78= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -372,9 +575,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= -github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= +github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= +github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -390,66 +592,83 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= -github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= +github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= -github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8= -github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= -github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= +github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4= +github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= -github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= +github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= +github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -457,32 +676,43 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU= github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -492,8 +722,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -504,13 +732,18 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= +github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= @@ -518,16 +751,22 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/newrelic/go-agent/v3 v3.20.1 h1:xxhPjE/j4z7n82FQV4izRjIkd4E10q4flqgzMj+DlLM= -github.com/newrelic/go-agent/v3 v3.20.1/go.mod h1:rT6ZUxJc5rQbWLyCtjqQCOcfb01lKRFbc1yMQkcboWM= +github.com/newrelic/go-agent/v3 v3.21.1 h1:nSLaQK+w/BHPUEpkPB+fX3ikgaRR2qyQiTECrcY+AmQ= +github.com/newrelic/go-agent/v3 v3.21.1/go.mod h1:AGagR69YHzamnvfxq9aDHnImvZwxr7C+4w7UN0Bm3UM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -537,65 +776,98 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= +github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg= +github.com/pseudomuto/protoc-gen-doc v1.5.0/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg= +github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= +github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -604,20 +876,28 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM= github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI= -github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= -github.com/smallstep/nosql v0.5.0 h1:1BPyHy8bha8qSaxgULGEdqhXpNFXimAfudnauFVqmxw= -github.com/smallstep/nosql v0.5.0/go.mod h1:yKZT5h7cdIVm6wEKM9+jN5dgK80Hljpuy8HNsnI7Gzo= -github.com/smallstep/pkcs7 v0.0.0-20221024180420-e1aab68dda05 h1:nVZXaJTwrUcfPUSZknkOidfITqOXSO0wE8pkOUTOdSM= -github.com/smallstep/pkcs7 v0.0.0-20221024180420-e1aab68dda05/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +github.com/smallstep/go-attestation v0.4.4-0.20230224121042-1bcb20a75add h1:nKji/LnfyxNGP6JM5EM2jVTnGus9Fblz9IGxQHRUI6M= +github.com/smallstep/go-attestation v0.4.4-0.20230224121042-1bcb20a75add/go.mod h1:hYP3lyq8hO11DmeEBjZ28norJ2uCFhm/Jj5m8V+hmNE= +github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc= +github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= +github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948 h1:/80FqDt6pzL9clNW8G2IsRAzKGNAuzsEs7g1Y5oaM/Y= +github.com/smallstep/pkcs7 v0.0.0-20230302202335-4c094085c948/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -629,10 +909,17 @@ github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -640,105 +927,202 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= -github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.13 h1:wsLILXG8qCJNse/qAgLNf23737Cx05GflHg/PJGe1Ok= +github.com/urfave/cli v1.22.13/go.mod h1:VufqObjsMTF2BBwKawpx9R8eAneNEWhoO0yx8Vd+FkE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0/go.mod h1:YPwSaBciV5G6Gpt435AasAG3ROetZsKNUzibRa/++oo= +go.etcd.io/etcd/etcdctl/v3 v3.5.0/go.mod h1:vGTfKdsh87RI7kA2JHFBEGxjQEYx+pi299wqEOdi34M= +go.etcd.io/etcd/etcdutl/v3 v3.5.0/go.mod h1:o98rKMCibbFAG8QS9KmvlYDGDShmmIbmRE8vSofzYNg= +go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY= +go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= +go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w= +go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= +go.etcd.io/etcd/server/v3 v3.5.0-alpha.0/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ= +go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= +go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0/go.mod h1:HnrHxjyCuZ8YDt8PYVyQQ5d1ZQfzJVEtQWllr5Vp/30= +go.etcd.io/etcd/tests/v3 v3.5.0/go.mod h1:f+mtZ1bE1YPvgKdOJV2BKy4JQW0nAFnQehgOE7+WyJE= +go.etcd.io/etcd/v3 v3.5.0-alpha.0/go.mod h1:JZ79d3LV6NUfPjUxXrpiFAYcjhT+06qqw+i28snx8To= +go.etcd.io/etcd/v3 v3.5.0/go.mod h1:FldM0/VzcxYWLvWx1sdA7ghKw7C3L2DvUTzGrcEtsC4= +go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk= -go.step.sm/cli-utils v0.7.5/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I= -go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -go.step.sm/crypto v0.23.1 h1:Yr9vlzjGqIKVi88KcpZtEcNTcpDkt1nVR7tumW4h+CU= -go.step.sm/crypto v0.23.1/go.mod h1:djAhDYpNAuWF2LkzbCVcf0JDy1UWgrxR3eQ7pQ8EQ/w= -go.step.sm/linkedca v0.19.0 h1:xuagkR35wrJI2gnu6FAM+q3VmjwsHScvGcJsfZ0GdsI= -go.step.sm/linkedca v0.19.0/go.mod h1:b7vWPrHfYLEOTSUZitFEcztVCpTc+ileIN85CwEAluM= +go.step.sm/cli-utils v0.7.6 h1:YkpLVrepmy2c5+eaz/wduiGxlgrRx3YdAStE37if25g= +go.step.sm/cli-utils v0.7.6/go.mod h1:j+FxFZ2gbWkAJl0eded/rksuxmNqWpmyxbkXcukGJaY= +go.step.sm/crypto v0.29.3 h1:lFCsFQQGic1VZIa0B/87iMCDy67+LW8eEl119GTyeWI= +go.step.sm/crypto v0.29.3/go.mod h1:0lYeIyQMJbFJ27L4BOGaq2gnuTgOShf+Ju/cTsMULq4= +go.step.sm/linkedca v0.19.1 h1:uY0ByT/uB3FCQ8zIo9mU7MWG7HKf5sDXNEBeN94MuP8= +go.step.sm/linkedca v0.19.1/go.mod h1:vPV2ad3LFQJmV7XWt87VlnJSs6UOqgsbVGVWe3veEmI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= +golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0= -golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw= +golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -747,35 +1131,88 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -787,76 +1224,192 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191119060738-e882bf8e40c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/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 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.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 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191118222007-07fc4c7f2b98/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -865,29 +1418,123 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.37.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA= +google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.120.0 h1:TTmhTei0mkR+kiBSW2UzZmAbkTaBfUUzfchyXnzG9Hs= +google.golang.org/api v0.120.0/go.mod h1:CrSvlNEFCFLae9ZUtL1z+61+rEBD7J/aCYwVYKZoWFU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210331142528-b7513248f0ba/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210427215850-f767ed18ee4d/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -895,12 +1542,28 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -909,27 +1572,37 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -943,12 +1616,21 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/pki/pki.go b/pki/pki.go index d6c15c9e..971c189b 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -812,6 +812,11 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) { Templates: p.getTemplates(), } + // Disable the database when WithNoDB() option is passed. + if p.options.noDB { + cfg.DB = nil + } + // Add linked as a deployment type to detect it on start and provide a // message if the token is not given. if p.options.deploymentType == LinkedDeployment { diff --git a/scep/api/api.go b/scep/api/api.go index 346b9c75..98da818b 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -305,6 +305,8 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // NOTE: at this point we have sufficient information for returning nicely signed CertReps csr := msg.CSRReqMessage.CSR + transactionID := string(msg.TransactionID) + challengePassword := msg.CSRReqMessage.ChallengePassword // NOTE: we're blocking the RenewalReq if the challenge does not match, because otherwise we don't have any authentication. // The macOS SCEP client performs renewals using PKCSreq. The CertNanny SCEP client will use PKCSreq with challenge too, it seems, @@ -312,13 +314,11 @@ func PKIOperation(ctx context.Context, req request) (Response, error) { // a certificate exists; then it will use RenewalReq. Adding the challenge check here may be a small breaking change for clients. // We'll have to see how it works out. if msg.MessageType == microscep.PKCSReq || msg.MessageType == microscep.RenewalReq { - challengeMatches, err := auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword) - if err != nil { - return createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("error when checking password")) - } - if !challengeMatches { - // TODO: can this be returned safely to the client? In the end, if the password was correct, that gains a bit of info too. - return createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("wrong password provided")) + if err := auth.ValidateChallenge(ctx, challengePassword, transactionID); err != nil { + if errors.Is(err, provisioner.ErrSCEPChallengeInvalid) { + return createFailureResponse(ctx, csr, msg, microscep.BadRequest, err) + } + return createFailureResponse(ctx, csr, msg, microscep.BadRequest, errors.New("failed validating challenge password")) } } diff --git a/scep/authority.go b/scep/authority.go index 585b937e..8ba9c9c9 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -2,7 +2,6 @@ package scep import ( "context" - "crypto/subtle" "crypto/x509" "errors" "fmt" @@ -456,24 +455,6 @@ func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.Certifi return crepMsg, nil } -// MatchChallengePassword verifies a SCEP challenge password -func (a *Authority) MatchChallengePassword(ctx context.Context, password string) (bool, error) { - p, err := provisionerFromContext(ctx) - if err != nil { - return false, err - } - - if subtle.ConstantTimeCompare([]byte(p.GetChallengePassword()), []byte(password)) == 1 { - return true, nil - } - - // TODO: support dynamic challenges, i.e. a list of challenges instead of one? - // That's probably a bit harder to configure, though; likely requires some data store - // that can be interacted with more easily, via some internal API, for example. - - return false, nil -} - // GetCACaps returns the CA capabilities func (a *Authority) GetCACaps(ctx context.Context) []string { p, err := provisionerFromContext(ctx) @@ -494,3 +475,11 @@ func (a *Authority) GetCACaps(ctx context.Context) []string { return caps } + +func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactionID string) error { + p, err := provisionerFromContext(ctx) + if err != nil { + return err + } + return p.ValidateChallenge(ctx, challenge, transactionID) +} diff --git a/scep/provisioner.go b/scep/provisioner.go index 679c6353..8120057e 100644 --- a/scep/provisioner.go +++ b/scep/provisioner.go @@ -14,8 +14,8 @@ type Provisioner interface { GetName() string DefaultTLSCertDuration() time.Duration GetOptions() *provisioner.Options - GetChallengePassword() string GetCapabilities() []string ShouldIncludeRootInChain() bool GetContentEncryptionAlgorithm() int + ValidateChallenge(ctx context.Context, challenge, transactionID string) error } diff --git a/scripts/install-step-ra.sh b/scripts/install-step-ra.sh index 74aa1914..07875601 100644 --- a/scripts/install-step-ra.sh +++ b/scripts/install-step-ra.sh @@ -188,7 +188,7 @@ CA_VERSION=$(curl -s https://api.github.com/repos/smallstep/certificates/release curl -sLO https://github.com/smallstep/certificates/releases/download/$CA_VERSION/step-ca_linux_${CA_VERSION:1}_$arch.tar.gz tar -xf step-ca_linux_${CA_VERSION:1}_$arch.tar.gz -install -m 0755 -t /usr/bin step-ca_${CA_VERSION:1}/bin/step-ca +install -m 0755 -t /usr/bin step-ca_${CA_VERSION:1}/step-ca setcap CAP_NET_BIND_SERVICE=+eip $(which step-ca) rm step-ca_linux_${CA_VERSION:1}_$arch.tar.gz rm -rf step-ca_${CA_VERSION:1} diff --git a/templates/values.go b/templates/values.go index a760001e..aa158a92 100644 --- a/templates/values.go +++ b/templates/values.go @@ -108,10 +108,10 @@ var DefaultSSHTemplateData = map[string]string{ {{- end }} {{- if or .User.GOOS "none" | eq "windows" }} UserKnownHostsFile "{{.User.StepPath}}\ssh\known_hosts" - ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }} %r %h %p + ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }}{{- if .User.Provisioner }} --provisioner {{ .User.Provisioner }}{{- end }} %r %h %p {{- else }} UserKnownHostsFile "{{.User.StepPath}}/ssh/known_hosts" - ProxyCommand step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }} %r %h %p + ProxyCommand step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }}{{- if .User.Provisioner }} --provisioner {{ .User.Provisioner }}{{- end }} %r %h %p {{- end }} `, diff --git a/webhook/types.go b/webhook/types.go index 19624f5c..9605742a 100644 --- a/webhook/types.go +++ b/webhook/types.go @@ -68,4 +68,7 @@ type RequestBody struct { X509Certificate *X509Certificate `json:"x509Certificate,omitempty"` SSHCertificateRequest *SSHCertificateRequest `json:"sshCertificateRequest,omitempty"` SSHCertificate *SSHCertificate `json:"sshCertificate,omitempty"` + // Only set for SCEP challenge validation requests + SCEPChallenge string `json:"scepChallenge,omitempty"` + SCEPTransactionID string `json:"scepTransactionID,omitempty"` }