forked from TrueCloudLab/certificates
Merge branch 'master' into crl-support
# Conflicts: # authority/config/config.go
This commit is contained in:
commit
d2483f3a70
143 changed files with 1371 additions and 9479 deletions
72
.github/workflows/codeql-analysis.yml
vendored
Normal file
72
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ "master" ]
|
||||||
|
schedule:
|
||||||
|
- cron: '30 3 * * 3'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'go' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
||||||
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
|
# - run: |
|
||||||
|
# echo "Run, Build Application using script"
|
||||||
|
# ./location_of_script_within_repo/buildscript.sh
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
12
.github/workflows/labeler.yml
vendored
12
.github/workflows/labeler.yml
vendored
|
@ -1,12 +0,0 @@
|
||||||
name: Pull Request Labeler
|
|
||||||
on:
|
|
||||||
pull_request_target
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
label:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/labeler@v3.0.2
|
|
||||||
with:
|
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
|
|
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go: [ '1.17', '1.18' ]
|
go: [ '1.18', '1.19' ]
|
||||||
outputs:
|
outputs:
|
||||||
is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }}
|
is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }}
|
||||||
steps:
|
steps:
|
||||||
|
@ -32,26 +32,8 @@ jobs:
|
||||||
name: golangci-lint
|
name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
version: ${{ secrets.GOLANGCI_LINT_VERSION }}
|
||||||
version: 'v1.45.2'
|
|
||||||
|
|
||||||
# Optional: working directory, useful for monorepos
|
|
||||||
# working-directory: somedir
|
|
||||||
|
|
||||||
# Optional: golangci-lint command line arguments.
|
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
|
|
||||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
|
||||||
# only-new-issues: true
|
|
||||||
|
|
||||||
# Optional: if set to true then the action will use pre-installed Go.
|
|
||||||
# skip-go-installation: true
|
|
||||||
|
|
||||||
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
|
||||||
# skip-pkg-cache: true
|
|
||||||
|
|
||||||
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
|
||||||
# skip-build-cache: true
|
|
||||||
-
|
-
|
||||||
name: Test, Build
|
name: Test, Build
|
||||||
id: lint_test_build
|
id: lint_test_build
|
||||||
|
@ -106,7 +88,7 @@ jobs:
|
||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: 1.19
|
||||||
-
|
-
|
||||||
name: APT Install
|
name: APT Install
|
||||||
id: aptInstall
|
id: aptInstall
|
||||||
|
@ -159,7 +141,7 @@ jobs:
|
||||||
name: Setup Go
|
name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.18'
|
go-version: '1.19'
|
||||||
-
|
-
|
||||||
name: Install cosign
|
name: Install cosign
|
||||||
uses: sigstore/cosign-installer@v1.1.0
|
uses: sigstore/cosign-installer@v1.1.0
|
||||||
|
|
24
.github/workflows/test.yml
vendored
24
.github/workflows/test.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go: [ '1.17', '1.18' ]
|
go: [ '1.18', '1.19' ]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
|
@ -32,33 +32,15 @@ jobs:
|
||||||
name: golangci-lint
|
name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
version: ${{ secrets.GOLANGCI_LINT_VERSION }}
|
||||||
version: 'v1.45.2'
|
|
||||||
|
|
||||||
# Optional: working directory, useful for monorepos
|
|
||||||
# working-directory: somedir
|
|
||||||
|
|
||||||
# Optional: golangci-lint command line arguments.
|
|
||||||
args: --timeout=30m
|
args: --timeout=30m
|
||||||
|
|
||||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
|
||||||
# only-new-issues: true
|
|
||||||
|
|
||||||
# Optional: if set to true then the action will use pre-installed Go.
|
|
||||||
# skip-go-installation: true
|
|
||||||
|
|
||||||
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
|
||||||
# skip-pkg-cache: true
|
|
||||||
|
|
||||||
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
|
||||||
# skip-build-cache: true
|
|
||||||
-
|
-
|
||||||
name: Test, Build
|
name: Test, Build
|
||||||
id: lint_test_build
|
id: lint_test_build
|
||||||
run: V=1 make ci
|
run: V=1 make ci
|
||||||
-
|
-
|
||||||
name: Codecov
|
name: Codecov
|
||||||
if: matrix.go == '1.18'
|
if: matrix.go == '1.19'
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
29
.github/workflows/triage.yml
vendored
Normal file
29
.github/workflows/triage.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
name: Add Issues and PRs to Triage
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
label:
|
||||||
|
name: Label PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
steps:
|
||||||
|
- uses: actions/labeler@v3.0.2
|
||||||
|
with:
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|
||||||
|
add-to-project:
|
||||||
|
name: Add to Triage Project
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/add-to-project@v0.3.0
|
||||||
|
with:
|
||||||
|
project-url: https://github.com/orgs/smallstep/projects/94
|
||||||
|
github-token: ${{ secrets.TRIAGE_PAT }}
|
|
@ -50,7 +50,6 @@ linters-settings:
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- deadcode
|
|
||||||
- gocritic
|
- gocritic
|
||||||
- gofmt
|
- gofmt
|
||||||
- gosimple
|
- gosimple
|
||||||
|
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -16,8 +16,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
---
|
---
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.22.1] - 2022-08-31
|
||||||
|
### Fixed
|
||||||
|
- Fixed signature algorithm on EC (root) + RSA (intermediate) PKIs.
|
||||||
|
|
||||||
|
## [0.22.0] - 2022-08-26
|
||||||
|
### Added
|
||||||
|
- Added automatic configuration of Linked RAs.
|
||||||
|
- Send provisioner configuration on Linked RAs.
|
||||||
### Changed
|
### Changed
|
||||||
- Certificates signed by an issuer using an RSA key will be signed using the same algorithm as the issuer certificate was signed with. The signature will no longer default to PKCS #1. For example, if the issuer certificate was signed using RSA-PSS with SHA-256, a new certificate will also be signed using RSA-PSS with SHA-256.
|
- Certificates signed by an issuer using an RSA key will be signed using the
|
||||||
|
same algorithm used to sign the issuer certificate. The signature will no
|
||||||
|
longer default to PKCS #1. For example, if the issuer certificate was signed
|
||||||
|
using RSA-PSS with SHA-256, a new certificate will also be signed using
|
||||||
|
RSA-PSS with SHA-256.
|
||||||
|
- Support two latest versions of Go (1.18, 1.19).
|
||||||
|
- Validate revocation serial number (either base 10 or prefixed with an
|
||||||
|
appropriate base).
|
||||||
|
- Sanitize TLS options.
|
||||||
|
|
||||||
## [0.20.0] - 2022-05-26
|
## [0.20.0] - 2022-05-26
|
||||||
### Added
|
### Added
|
||||||
|
|
8
SECURITY.md
Normal file
8
SECURITY.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
We appreciate any effort to discover and disclose security vulnerabilities responsibly.
|
||||||
|
|
||||||
|
If you would like to report a vulnerability in one of our projects, or have security concerns regarding Smallstep software, please email security@smallstep.com.
|
||||||
|
|
||||||
|
In order for us to best respond to your report, please include any of the following:
|
||||||
|
* Steps to reproduce or proof-of-concept
|
||||||
|
* Any relevant tools, including versions used
|
||||||
|
* Tool output
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/nosql/database"
|
"github.com/smallstep/nosql/database"
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
|
"go.step.sm/crypto/keyutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testBody = []byte("foo")
|
var testBody = []byte("foo")
|
||||||
|
@ -1136,6 +1137,8 @@ func TestHandler_validateJWS(t *testing.T) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fail/rsa-key-too-small": func(t *testing.T) test {
|
"fail/rsa-key-too-small": func(t *testing.T) test {
|
||||||
|
revert := keyutil.Insecure()
|
||||||
|
defer revert()
|
||||||
jwk, err := jose.GenerateJWK("RSA", "", "", "sig", "", 1024)
|
jwk, err := jose.GenerateJWK("RSA", "", "", "sig", "", 1024)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
pub := jwk.Public()
|
pub := jwk.Public()
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
|
|
||||||
"go.step.sm/crypto/randutil"
|
"go.step.sm/crypto/randutil"
|
||||||
|
"go.step.sm/crypto/x509util"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/certificates/api/render"
|
"github.com/smallstep/certificates/api/render"
|
||||||
|
@ -33,12 +34,20 @@ func (n *NewOrderRequest) Validate() error {
|
||||||
return acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty")
|
return acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty")
|
||||||
}
|
}
|
||||||
for _, id := range n.Identifiers {
|
for _, id := range n.Identifiers {
|
||||||
if !(id.Type == acme.DNS || id.Type == acme.IP) {
|
switch id.Type {
|
||||||
|
case acme.IP:
|
||||||
|
if net.ParseIP(id.Value) == nil {
|
||||||
|
return acme.NewError(acme.ErrorMalformedType, "invalid IP address: %s", id.Value)
|
||||||
|
}
|
||||||
|
case acme.DNS:
|
||||||
|
value, _ := trimIfWildcard(id.Value)
|
||||||
|
if _, err := x509util.SanitizeName(value); err != nil {
|
||||||
|
return acme.NewError(acme.ErrorMalformedType, "invalid DNS name: %s", id.Value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type)
|
return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type)
|
||||||
}
|
}
|
||||||
if id.Type == acme.IP && net.ParseIP(id.Value) == nil {
|
|
||||||
return acme.NewError(acme.ErrorMalformedType, "invalid IP address: %s", id.Value)
|
|
||||||
}
|
|
||||||
// TODO(hs): add some validations for DNS domains?
|
// TODO(hs): add some validations for DNS domains?
|
||||||
// TODO(hs): combine the errors from this with allow/deny policy, like example error in https://datatracker.ietf.org/doc/html/rfc8555#section-6.7.1
|
// TODO(hs): combine the errors from this with allow/deny policy, like example error in https://datatracker.ietf.org/doc/html/rfc8555#section-6.7.1
|
||||||
}
|
}
|
||||||
|
@ -218,13 +227,19 @@ func newACMEPolicyEngine(eak *acme.ExternalAccountKey) (policy.X509Policy, error
|
||||||
return policy.NewX509PolicyEngine(eak.Policy)
|
return policy.NewX509PolicyEngine(eak.Policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trimIfWildcard(value string) (string, bool) {
|
||||||
|
if strings.HasPrefix(value, "*.") {
|
||||||
|
return strings.TrimPrefix(value, "*."), true
|
||||||
|
}
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
|
||||||
func newAuthorization(ctx context.Context, az *acme.Authorization) error {
|
func newAuthorization(ctx context.Context, az *acme.Authorization) error {
|
||||||
if strings.HasPrefix(az.Identifier.Value, "*.") {
|
value, isWildcard := trimIfWildcard(az.Identifier.Value)
|
||||||
az.Wildcard = true
|
az.Wildcard = isWildcard
|
||||||
az.Identifier = acme.Identifier{
|
az.Identifier = acme.Identifier{
|
||||||
Value: strings.TrimPrefix(az.Identifier.Value, "*."),
|
Value: value,
|
||||||
Type: az.Identifier.Type,
|
Type: az.Identifier.Type,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chTypes := challengeTypes(az)
|
chTypes := challengeTypes(az)
|
||||||
|
|
|
@ -49,6 +49,36 @@ func TestNewOrderRequest_Validate(t *testing.T) {
|
||||||
err: acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: foo"),
|
err: acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: foo"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fail/bad-identifier/bad-dns": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
nor: &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "xn--bücher.example.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewError(acme.ErrorMalformedType, "invalid DNS name: xn--bücher.example.com"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/bad-identifier/dns-port": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
nor: &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "example.com:8080"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewError(acme.ErrorMalformedType, "invalid DNS name: example.com:8080"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/bad-identifier/dns-wildcard-port": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
nor: &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "*.example.com:8080"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewError(acme.ErrorMalformedType, "invalid DNS name: *.example.com:8080"),
|
||||||
|
}
|
||||||
|
},
|
||||||
"fail/bad-ip": func(t *testing.T) test {
|
"fail/bad-ip": func(t *testing.T) test {
|
||||||
nbf := time.Now().UTC().Add(time.Minute)
|
nbf := time.Now().UTC().Add(time.Minute)
|
||||||
naf := time.Now().UTC().Add(5 * time.Minute)
|
naf := time.Now().UTC().Add(5 * time.Minute)
|
||||||
|
@ -72,7 +102,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
|
||||||
nor: &NewOrderRequest{
|
nor: &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{Type: "dns", Value: "example.com"},
|
{Type: "dns", Value: "example.com"},
|
||||||
{Type: "dns", Value: "bar.com"},
|
{Type: "dns", Value: "*.bar.com"},
|
||||||
},
|
},
|
||||||
NotAfter: naf,
|
NotAfter: naf,
|
||||||
NotBefore: nbf,
|
NotBefore: nbf,
|
||||||
|
@ -2097,3 +2127,32 @@ func TestHandler_challengeTypes(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTrimIfWildcard(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
arg string
|
||||||
|
wantValue string
|
||||||
|
wantBool bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no trim",
|
||||||
|
arg: "smallstep.com",
|
||||||
|
wantValue: "smallstep.com",
|
||||||
|
wantBool: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim",
|
||||||
|
arg: "*.smallstep.com",
|
||||||
|
wantValue: "smallstep.com",
|
||||||
|
wantBool: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
v, ok := trimIfWildcard(tt.arg)
|
||||||
|
assert.Equals(t, v, tt.wantValue)
|
||||||
|
assert.Equals(t, ok, tt.wantBool)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||||
// [RFC5246] or higher when connecting to clients for validation.
|
// [RFC5246] or higher when connecting to clients for validation.
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
ServerName: serverName(ch),
|
ServerName: serverName(ch),
|
||||||
InsecureSkipVerify: true, // we expect a self-signed challenge certificate
|
InsecureSkipVerify: true, // nolint:gosec // we expect a self-signed challenge certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
hostPort := net.JoinHostPort(ch.Value, "443")
|
hostPort := net.JoinHostPort(ch.Value, "443")
|
||||||
|
|
|
@ -56,7 +56,8 @@ func NewClient() Client {
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
// nolint:gosec // used on tls-alpn-01 challenge
|
||||||
|
InsecureSkipVerify: true, // lgtm[go/disabled-certificate-check]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1485,7 +1485,7 @@ func Test_fmtPublicKey(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1511,7 +1511,7 @@ func Test_fmtPublicKey(t *testing.T) {
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"p256", args{p256.Public(), p256, nil}, "ECDSA P-256"},
|
{"p256", args{p256.Public(), p256, nil}, "ECDSA P-256"},
|
||||||
{"rsa1024", args{rsa1024.Public(), rsa1024, nil}, "RSA 1024"},
|
{"rsa2048", args{rsa2048.Public(), rsa2048, nil}, "RSA 2048"},
|
||||||
{"ed25519", args{edPub, edPriv, nil}, "Ed25519"},
|
{"ed25519", args{edPub, edPriv, nil}, "Ed25519"},
|
||||||
{"dsa2048", args{cert: &x509.Certificate{PublicKeyAlgorithm: x509.DSA, PublicKey: &dsa2048.PublicKey}}, "DSA 2048"},
|
{"dsa2048", args{cert: &x509.Certificate{PublicKeyAlgorithm: x509.DSA, PublicKey: &dsa2048.PublicKey}}, "DSA 2048"},
|
||||||
{"unknown", args{cert: &x509.Certificate{PublicKeyAlgorithm: x509.ECDSA, PublicKey: []byte("12345678")}}, "ECDSA unknown"},
|
{"unknown", args{cert: &x509.Certificate{PublicKeyAlgorithm: x509.ECDSA, PublicKey: []byte("12345678")}}, "ECDSA unknown"},
|
||||||
|
|
|
@ -3,7 +3,6 @@ package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -28,8 +27,6 @@ type StackTracedError interface {
|
||||||
func Error(rw http.ResponseWriter, err error) {
|
func Error(rw http.ResponseWriter, err error) {
|
||||||
rl, ok := rw.(logging.ResponseLogger)
|
rl, ok := rw.(logging.ResponseLogger)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Println(err)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +69,6 @@ func EnabledResponse(rw http.ResponseWriter, v interface{}) {
|
||||||
rl.WithFields(map[string]interface{}{
|
rl.WithFields(map[string]interface{}{
|
||||||
"response": out,
|
"response": out,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
log.Println(out)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"golang.org/x/crypto/ocsp"
|
"golang.org/x/crypto/ocsp"
|
||||||
|
@ -33,6 +34,11 @@ func (r *RevokeRequest) Validate() (err error) {
|
||||||
if r.Serial == "" {
|
if r.Serial == "" {
|
||||||
return errs.BadRequest("missing serial")
|
return errs.BadRequest("missing serial")
|
||||||
}
|
}
|
||||||
|
sn, ok := new(big.Int).SetString(r.Serial, 0)
|
||||||
|
if !ok {
|
||||||
|
return errs.BadRequest("'%s' is not a valid serial number - use a base 10 representation or a base 16 representation with '0x' prefix", r.Serial)
|
||||||
|
}
|
||||||
|
r.Serial = sn.String()
|
||||||
if r.ReasonCode < ocsp.Unspecified || r.ReasonCode > ocsp.AACompromise {
|
if r.ReasonCode < ocsp.Unspecified || r.ReasonCode > ocsp.AACompromise {
|
||||||
return errs.BadRequest("reasonCode out of bounds")
|
return errs.BadRequest("reasonCode out of bounds")
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,13 @@ func TestRevokeRequestValidate(t *testing.T) {
|
||||||
rr: &RevokeRequest{},
|
rr: &RevokeRequest{},
|
||||||
err: &errs.Error{Err: errors.New("missing serial"), Status: http.StatusBadRequest},
|
err: &errs.Error{Err: errors.New("missing serial"), Status: http.StatusBadRequest},
|
||||||
},
|
},
|
||||||
|
"error/bad sn": {
|
||||||
|
rr: &RevokeRequest{Serial: "sn"},
|
||||||
|
err: &errs.Error{Err: errors.New("'sn' is not a valid serial number - use a base 10 representation or a base 16 representation with '0x' prefix"), Status: http.StatusBadRequest},
|
||||||
|
},
|
||||||
"error/bad reasonCode": {
|
"error/bad reasonCode": {
|
||||||
rr: &RevokeRequest{
|
rr: &RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 15,
|
ReasonCode: 15,
|
||||||
Passive: true,
|
Passive: true,
|
||||||
},
|
},
|
||||||
|
@ -41,7 +45,7 @@ func TestRevokeRequestValidate(t *testing.T) {
|
||||||
},
|
},
|
||||||
"error/non-passive not implemented": {
|
"error/non-passive not implemented": {
|
||||||
rr: &RevokeRequest{
|
rr: &RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 8,
|
ReasonCode: 8,
|
||||||
Passive: false,
|
Passive: false,
|
||||||
},
|
},
|
||||||
|
@ -49,7 +53,7 @@ func TestRevokeRequestValidate(t *testing.T) {
|
||||||
},
|
},
|
||||||
"ok": {
|
"ok": {
|
||||||
rr: &RevokeRequest{
|
rr: &RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 9,
|
ReasonCode: 9,
|
||||||
Passive: true,
|
Passive: true,
|
||||||
},
|
},
|
||||||
|
@ -97,7 +101,7 @@ func Test_caHandler_Revoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"200/ott": func(t *testing.T) test {
|
"200/ott": func(t *testing.T) test {
|
||||||
input, err := json.Marshal(RevokeRequest{
|
input, err := json.Marshal(RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 4,
|
ReasonCode: 4,
|
||||||
Reason: "foo",
|
Reason: "foo",
|
||||||
OTT: "valid",
|
OTT: "valid",
|
||||||
|
@ -114,7 +118,7 @@ func Test_caHandler_Revoke(t *testing.T) {
|
||||||
revoke: func(ctx context.Context, opts *authority.RevokeOptions) error {
|
revoke: func(ctx context.Context, opts *authority.RevokeOptions) error {
|
||||||
assert.True(t, opts.PassiveOnly)
|
assert.True(t, opts.PassiveOnly)
|
||||||
assert.False(t, opts.MTLS)
|
assert.False(t, opts.MTLS)
|
||||||
assert.Equals(t, opts.Serial, "sn")
|
assert.Equals(t, opts.Serial, "10")
|
||||||
assert.Equals(t, opts.ReasonCode, 4)
|
assert.Equals(t, opts.ReasonCode, 4)
|
||||||
assert.Equals(t, opts.Reason, "foo")
|
assert.Equals(t, opts.Reason, "foo")
|
||||||
return nil
|
return nil
|
||||||
|
@ -125,7 +129,7 @@ func Test_caHandler_Revoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"400/no OTT and no peer certificate": func(t *testing.T) test {
|
"400/no OTT and no peer certificate": func(t *testing.T) test {
|
||||||
input, err := json.Marshal(RevokeRequest{
|
input, err := json.Marshal(RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 4,
|
ReasonCode: 4,
|
||||||
Passive: true,
|
Passive: true,
|
||||||
})
|
})
|
||||||
|
@ -176,7 +180,7 @@ func Test_caHandler_Revoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"500/ott authority.Revoke": func(t *testing.T) test {
|
"500/ott authority.Revoke": func(t *testing.T) test {
|
||||||
input, err := json.Marshal(RevokeRequest{
|
input, err := json.Marshal(RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 4,
|
ReasonCode: 4,
|
||||||
Reason: "foo",
|
Reason: "foo",
|
||||||
OTT: "valid",
|
OTT: "valid",
|
||||||
|
@ -198,7 +202,7 @@ func Test_caHandler_Revoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"403/ott authority.Revoke": func(t *testing.T) test {
|
"403/ott authority.Revoke": func(t *testing.T) test {
|
||||||
input, err := json.Marshal(RevokeRequest{
|
input, err := json.Marshal(RevokeRequest{
|
||||||
Serial: "sn",
|
Serial: "10",
|
||||||
ReasonCode: 4,
|
ReasonCode: 4,
|
||||||
Reason: "foo",
|
Reason: "foo",
|
||||||
OTT: "valid",
|
OTT: "valid",
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/sshutil"
|
||||||
|
"go.step.sm/crypto/x509util"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
|
@ -89,6 +92,12 @@ func CreateProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate the templates and template data
|
||||||
|
if err := validateTemplates(prov.X509Template, prov.SshTemplate); err != nil {
|
||||||
|
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "invalid template"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := mustAuthority(r.Context()).StoreProvisioner(r.Context(), prov); err != nil {
|
if err := mustAuthority(r.Context()).StoreProvisioner(r.Context(), prov); err != nil {
|
||||||
render.Error(w, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name))
|
render.Error(w, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name))
|
||||||
return
|
return
|
||||||
|
@ -179,9 +188,47 @@ func UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate the templates and template data
|
||||||
|
if err := validateTemplates(nu.X509Template, nu.SshTemplate); err != nil {
|
||||||
|
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "invalid template"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := auth.UpdateProvisioner(r.Context(), nu); err != nil {
|
if err := auth.UpdateProvisioner(r.Context(), nu); err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
render.ProtoJSON(w, nu)
|
render.ProtoJSON(w, nu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateTemplates validates the X.509 and SSH templates and template data if set.
|
||||||
|
func validateTemplates(x509, ssh *linkedca.Template) error {
|
||||||
|
if x509 != nil {
|
||||||
|
if len(x509.Template) > 0 {
|
||||||
|
if err := x509util.ValidateTemplate(x509.Template); err != nil {
|
||||||
|
return fmt.Errorf("invalid X.509 template: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(x509.Data) > 0 {
|
||||||
|
if err := x509util.ValidateTemplateData(x509.Data); err != nil {
|
||||||
|
return fmt.Errorf("invalid X.509 template data: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ssh != nil {
|
||||||
|
if len(ssh.Template) > 0 {
|
||||||
|
if err := sshutil.ValidateTemplate(ssh.Template); err != nil {
|
||||||
|
return fmt.Errorf("invalid SSH template: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ssh.Data) > 0 {
|
||||||
|
if err := sshutil.ValidateTemplateData(ssh.Data); err != nil {
|
||||||
|
return fmt.Errorf("invalid SSH template data: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ import (
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
|
"github.com/smallstep/assert"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
|
||||||
"github.com/smallstep/certificates/authority/admin"
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
)
|
)
|
||||||
|
@ -349,6 +349,29 @@ func TestHandler_CreateProvisioner(t *testing.T) {
|
||||||
// "fail/authority.ValidateClaims": func(t *testing.T) test {
|
// "fail/authority.ValidateClaims": func(t *testing.T) test {
|
||||||
// return test{}
|
// return test{}
|
||||||
// },
|
// },
|
||||||
|
"fail/validateTemplates": func(t *testing.T) test {
|
||||||
|
prov := &linkedca.Provisioner{
|
||||||
|
Id: "provID",
|
||||||
|
Type: linkedca.Provisioner_OIDC,
|
||||||
|
Name: "provName",
|
||||||
|
X509Template: &linkedca.Template{
|
||||||
|
Template: []byte(`{ {{missingFunction "foo"}} }`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
body, err := protojson.Marshal(prov)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
return test{
|
||||||
|
ctx: context.Background(),
|
||||||
|
body: body,
|
||||||
|
statusCode: 400,
|
||||||
|
err: &admin.Error{
|
||||||
|
Type: "badRequest",
|
||||||
|
Status: 400,
|
||||||
|
Detail: "bad request",
|
||||||
|
Message: "invalid template: invalid X.509 template: error parsing template: template: template:1: function \"missingFunction\" not defined",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
"fail/auth.StoreProvisioner": func(t *testing.T) test {
|
"fail/auth.StoreProvisioner": func(t *testing.T) test {
|
||||||
prov := &linkedca.Provisioner{
|
prov := &linkedca.Provisioner{
|
||||||
Id: "provID",
|
Id: "provID",
|
||||||
|
@ -936,6 +959,61 @@ func TestHandler_UpdateProvisioner(t *testing.T) {
|
||||||
},
|
},
|
||||||
// TODO(hs): ValidateClaims can't be mocked atm
|
// TODO(hs): ValidateClaims can't be mocked atm
|
||||||
//"fail/ValidateClaims": func(t *testing.T) test { return test{} },
|
//"fail/ValidateClaims": func(t *testing.T) test { return test{} },
|
||||||
|
"fail/validateTemplates": func(t *testing.T) test {
|
||||||
|
chiCtx := chi.NewRouteContext()
|
||||||
|
chiCtx.URLParams.Add("name", "provName")
|
||||||
|
ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
|
||||||
|
createdAt := time.Now()
|
||||||
|
var deletedAt time.Time
|
||||||
|
prov := &linkedca.Provisioner{
|
||||||
|
Id: "provID",
|
||||||
|
Type: linkedca.Provisioner_OIDC,
|
||||||
|
Name: "provName",
|
||||||
|
AuthorityId: "authorityID",
|
||||||
|
CreatedAt: timestamppb.New(createdAt),
|
||||||
|
DeletedAt: timestamppb.New(deletedAt),
|
||||||
|
X509Template: &linkedca.Template{
|
||||||
|
Template: []byte("{ {{ missingFunction }} }"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
body, err := protojson.Marshal(prov)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
auth := &mockAdminAuthority{
|
||||||
|
MockLoadProvisionerByName: func(name string) (provisioner.Interface, error) {
|
||||||
|
assert.Equals(t, "provName", name)
|
||||||
|
return &provisioner.OIDC{
|
||||||
|
ID: "provID",
|
||||||
|
Name: "provName",
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
db := &admin.MockDB{
|
||||||
|
MockGetProvisioner: func(ctx context.Context, id string) (*linkedca.Provisioner, error) {
|
||||||
|
assert.Equals(t, "provID", id)
|
||||||
|
return &linkedca.Provisioner{
|
||||||
|
Id: "provID",
|
||||||
|
Name: "provName",
|
||||||
|
Type: linkedca.Provisioner_OIDC,
|
||||||
|
AuthorityId: "authorityID",
|
||||||
|
CreatedAt: timestamppb.New(createdAt),
|
||||||
|
DeletedAt: timestamppb.New(deletedAt),
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
body: body,
|
||||||
|
auth: auth,
|
||||||
|
adminDB: db,
|
||||||
|
statusCode: 400,
|
||||||
|
err: &admin.Error{
|
||||||
|
Type: "badRequest",
|
||||||
|
Status: 400,
|
||||||
|
Detail: "bad request",
|
||||||
|
Message: "invalid template: invalid X.509 template: error parsing template: template: template:1: function \"missingFunction\" not defined",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
"fail/auth.UpdateProvisioner": func(t *testing.T) test {
|
"fail/auth.UpdateProvisioner": func(t *testing.T) test {
|
||||||
chiCtx := chi.NewRouteContext()
|
chiCtx := chi.NewRouteContext()
|
||||||
chiCtx.URLParams.Add("name", "provName")
|
chiCtx.URLParams.Add("name", "provName")
|
||||||
|
@ -1107,3 +1185,87 @@ func TestHandler_UpdateProvisioner(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_validateTemplates(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
x509 *linkedca.Template
|
||||||
|
ssh *linkedca.Template
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ok",
|
||||||
|
args: args{},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ok/x509",
|
||||||
|
args: args{
|
||||||
|
x509: &linkedca.Template{
|
||||||
|
Template: []byte(`{"x": 1}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ok/ssh",
|
||||||
|
args: args{
|
||||||
|
ssh: &linkedca.Template{
|
||||||
|
Template: []byte(`{"x": 1}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fail/x509-template-missing-quote",
|
||||||
|
args: args{
|
||||||
|
x509: &linkedca.Template{
|
||||||
|
Template: []byte(`{ {{printf "%q" "quoted}} }`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("invalid X.509 template: error parsing template: template: template:1: unterminated quoted string"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fail/x509-template-data",
|
||||||
|
args: args{
|
||||||
|
x509: &linkedca.Template{
|
||||||
|
Data: []byte(`{!?}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("invalid X.509 template data: error validating json template data"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fail/ssh-template-unknown-function",
|
||||||
|
args: args{
|
||||||
|
ssh: &linkedca.Template{
|
||||||
|
Template: []byte(`{ {{unknownFunction "foo"}} }`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("invalid SSH template: error parsing template: template: template:1: function \"unknownFunction\" not defined"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fail/ssh-template-data",
|
||||||
|
args: args{
|
||||||
|
ssh: &linkedca.Template{
|
||||||
|
Data: []byte(`{!?}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: errors.New("invalid SSH template data: error validating json template data"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := validateTemplates(tt.args.x509, tt.args.ssh)
|
||||||
|
if tt.err != nil {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equals(t, tt.err.Error(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/kms"
|
||||||
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
|
"go.step.sm/crypto/kms/sshagentkms"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
|
@ -27,9 +30,6 @@ import (
|
||||||
"github.com/smallstep/certificates/cas"
|
"github.com/smallstep/certificates/cas"
|
||||||
casapi "github.com/smallstep/certificates/cas/apiv1"
|
casapi "github.com/smallstep/certificates/cas/apiv1"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/certificates/kms"
|
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/sshagentkms"
|
|
||||||
"github.com/smallstep/certificates/scep"
|
"github.com/smallstep/certificates/scep"
|
||||||
"github.com/smallstep/certificates/templates"
|
"github.com/smallstep/certificates/templates"
|
||||||
"github.com/smallstep/nosql"
|
"github.com/smallstep/nosql"
|
||||||
|
@ -316,6 +316,7 @@ func (a *Authority) init() error {
|
||||||
if id := a.config.AuthorityConfig.AuthorityID; id != "" && !strings.EqualFold(id, linkedcaClient.authorityID) {
|
if id := a.config.AuthorityConfig.AuthorityID; id != "" && !strings.EqualFold(id, linkedcaClient.authorityID) {
|
||||||
return errors.New("error initializing linkedca: token authority and configured authority do not match")
|
return errors.New("error initializing linkedca: token authority and configured authority do not match")
|
||||||
}
|
}
|
||||||
|
a.config.AuthorityConfig.AuthorityID = linkedcaClient.authorityID
|
||||||
linkedcaClient.Run()
|
linkedcaClient.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,6 +327,9 @@ func (a *Authority) init() error {
|
||||||
options = *a.config.AuthorityConfig.Options
|
options = *a.config.AuthorityConfig.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthorityID might be empty. It's always available linked CAs/RAs.
|
||||||
|
options.AuthorityID = a.config.AuthorityConfig.AuthorityID
|
||||||
|
|
||||||
// Configure linked RA
|
// Configure linked RA
|
||||||
if linkedcaClient != nil && options.CertificateAuthority == "" {
|
if linkedcaClient != nil && options.CertificateAuthority == "" {
|
||||||
conf, err := linkedcaClient.GetConfiguration(ctx)
|
conf, err := linkedcaClient.GetConfiguration(ctx)
|
||||||
|
@ -339,6 +343,19 @@ func (a *Authority) init() error {
|
||||||
Type: conf.RaConfig.Provisioner.Type.String(),
|
Type: conf.RaConfig.Provisioner.Type.String(),
|
||||||
Provisioner: conf.RaConfig.Provisioner.Name,
|
Provisioner: conf.RaConfig.Provisioner.Name,
|
||||||
}
|
}
|
||||||
|
// Configure the RA authority type if needed
|
||||||
|
if options.Type == "" {
|
||||||
|
options.Type = casapi.StepCAS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remote configuration is currently only supported on a linked RA
|
||||||
|
if sc := conf.ServerConfig; sc != nil {
|
||||||
|
if a.config.Address == "" {
|
||||||
|
a.config.Address = sc.Address
|
||||||
|
}
|
||||||
|
if len(a.config.DNSNames) == 0 {
|
||||||
|
a.config.DNSNames = sc.DnsNames
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +378,6 @@ func (a *Authority) init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.x509CAService, err = cas.New(ctx, options)
|
a.x509CAService, err = cas.New(ctx, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -432,7 +448,7 @@ func (a *Authority) init() error {
|
||||||
// erroring out with: ssh: unsupported key type *agent.Key
|
// erroring out with: ssh: unsupported key type *agent.Key
|
||||||
switch s := signer.(type) {
|
switch s := signer.(type) {
|
||||||
case *sshagentkms.WrappedSSHSigner:
|
case *sshagentkms.WrappedSSHSigner:
|
||||||
a.sshCAHostCertSignKey = s.Sshsigner
|
a.sshCAHostCertSignKey = s.Signer
|
||||||
case crypto.Signer:
|
case crypto.Signer:
|
||||||
a.sshCAHostCertSignKey, err = ssh.NewSignerFromSigner(s)
|
a.sshCAHostCertSignKey, err = ssh.NewSignerFromSigner(s)
|
||||||
default:
|
default:
|
||||||
|
@ -458,7 +474,7 @@ func (a *Authority) init() error {
|
||||||
// erroring out with: ssh: unsupported key type *agent.Key
|
// erroring out with: ssh: unsupported key type *agent.Key
|
||||||
switch s := signer.(type) {
|
switch s := signer.(type) {
|
||||||
case *sshagentkms.WrappedSSHSigner:
|
case *sshagentkms.WrappedSSHSigner:
|
||||||
a.sshCAUserCertSignKey = s.Sshsigner
|
a.sshCAUserCertSignKey = s.Signer
|
||||||
case crypto.Signer:
|
case crypto.Signer:
|
||||||
a.sshCAUserCertSignKey, err = ssh.NewSignerFromSigner(s)
|
a.sshCAUserCertSignKey, err = ssh.NewSignerFromSigner(s)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -9,13 +9,13 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
kms "go.step.sm/crypto/kms/apiv1"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority/policy"
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
cas "github.com/smallstep/certificates/cas/apiv1"
|
cas "github.com/smallstep/certificates/cas/apiv1"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
kms "github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/templates"
|
"github.com/smallstep/certificates/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ type Config struct {
|
||||||
Templates *templates.Templates `json:"templates,omitempty"`
|
Templates *templates.Templates `json:"templates,omitempty"`
|
||||||
CommonName string `json:"commonName,omitempty"`
|
CommonName string `json:"commonName,omitempty"`
|
||||||
CRL *CRLConfig `json:"crl,omitempty"`
|
CRL *CRLConfig `json:"crl,omitempty"`
|
||||||
|
SkipValidation bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer
|
// ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer
|
||||||
|
@ -103,6 +104,7 @@ type AuthConfig struct {
|
||||||
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
|
DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"`
|
||||||
Backdate *provisioner.Duration `json:"backdate,omitempty"`
|
Backdate *provisioner.Duration `json:"backdate,omitempty"`
|
||||||
EnableAdmin bool `json:"enableAdmin,omitempty"`
|
EnableAdmin bool `json:"enableAdmin,omitempty"`
|
||||||
|
DisableGetSSHHosts bool `json:"disableGetSSHHosts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRLConfig represents config options for CRL generation
|
// CRLConfig represents config options for CRL generation
|
||||||
|
@ -207,6 +209,8 @@ func (c *Config) Save(filename string) error {
|
||||||
// Validate validates the configuration.
|
// Validate validates the configuration.
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
|
case c.SkipValidation:
|
||||||
|
return nil
|
||||||
case c.Address == "":
|
case c.Address == "":
|
||||||
return errors.New("address cannot be empty")
|
return errors.New("address cannot be empty")
|
||||||
case len(c.DNSNames) == 0:
|
case len(c.DNSNames) == 0:
|
||||||
|
|
|
@ -35,9 +35,16 @@ func TestConfigValidate(t *testing.T) {
|
||||||
type ConfigValidateTest struct {
|
type ConfigValidateTest struct {
|
||||||
config *Config
|
config *Config
|
||||||
err error
|
err error
|
||||||
tls TLSOptions
|
tls *TLSOptions
|
||||||
}
|
}
|
||||||
tests := map[string]func(*testing.T) ConfigValidateTest{
|
tests := map[string]func(*testing.T) ConfigValidateTest{
|
||||||
|
"skip-validation": func(t *testing.T) ConfigValidateTest {
|
||||||
|
return ConfigValidateTest{
|
||||||
|
config: &Config{
|
||||||
|
SkipValidation: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
"empty-address": func(t *testing.T) ConfigValidateTest {
|
"empty-address": func(t *testing.T) ConfigValidateTest {
|
||||||
return ConfigValidateTest{
|
return ConfigValidateTest{
|
||||||
config: &Config{
|
config: &Config{
|
||||||
|
@ -128,7 +135,7 @@ func TestConfigValidate(t *testing.T) {
|
||||||
Password: "pass",
|
Password: "pass",
|
||||||
AuthorityConfig: ac,
|
AuthorityConfig: ac,
|
||||||
},
|
},
|
||||||
tls: DefaultTLSOptions,
|
tls: &DefaultTLSOptions,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"empty-TLS-values": func(t *testing.T) ConfigValidateTest {
|
"empty-TLS-values": func(t *testing.T) ConfigValidateTest {
|
||||||
|
@ -143,7 +150,7 @@ func TestConfigValidate(t *testing.T) {
|
||||||
AuthorityConfig: ac,
|
AuthorityConfig: ac,
|
||||||
TLS: &TLSOptions{},
|
TLS: &TLSOptions{},
|
||||||
},
|
},
|
||||||
tls: DefaultTLSOptions,
|
tls: &DefaultTLSOptions,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"custom-tls-values": func(t *testing.T) ConfigValidateTest {
|
"custom-tls-values": func(t *testing.T) ConfigValidateTest {
|
||||||
|
@ -165,7 +172,7 @@ func TestConfigValidate(t *testing.T) {
|
||||||
Renegotiation: true,
|
Renegotiation: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tls: TLSOptions{
|
tls: &TLSOptions{
|
||||||
CipherSuites: CipherSuites{
|
CipherSuites: CipherSuites{
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
},
|
},
|
||||||
|
@ -209,9 +216,9 @@ func TestConfigValidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if assert.Nil(t, tc.err) {
|
if assert.Nil(t, tc.err) {
|
||||||
fmt.Printf("tc.tls = %+v\n", tc.tls)
|
fmt.Printf("tc.tls = %v\n", tc.tls)
|
||||||
fmt.Printf("*tc.config.TLS = %+v\n", *tc.config.TLS)
|
fmt.Printf("*tc.config.TLS = %v\n", tc.config.TLS)
|
||||||
assert.Equals(t, *tc.config.TLS, tc.tls)
|
assert.Equals(t, tc.config.TLS, tc.tls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,18 +22,19 @@ var (
|
||||||
}
|
}
|
||||||
// ApprovedTLSCipherSuites smallstep approved ciphersuites.
|
// ApprovedTLSCipherSuites smallstep approved ciphersuites.
|
||||||
ApprovedTLSCipherSuites = CipherSuites{
|
ApprovedTLSCipherSuites = CipherSuites{
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
// AEADs w/ ECDHE
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
|
|
||||||
|
// CBC w/ ECDHE
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
}
|
}
|
||||||
// DefaultTLSOptions represents the default TLS version as well as the cipher
|
// DefaultTLSOptions represents the default TLS version as well as the cipher
|
||||||
// suites used in the TLS certificates.
|
// suites used in the TLS certificates.
|
||||||
|
@ -117,22 +118,22 @@ func (c CipherSuites) Value() []uint16 {
|
||||||
// cipherSuites has the list of supported cipher suites.
|
// cipherSuites has the list of supported cipher suites.
|
||||||
var cipherSuites = map[string]uint16{
|
var cipherSuites = map[string]uint16{
|
||||||
// TLS 1.0 - 1.2 cipher suites.
|
// TLS 1.0 - 1.2 cipher suites.
|
||||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, // lgtm[go/insecure-tls]
|
||||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, // lgtm[go/insecure-tls]
|
||||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, // lgtm[go/insecure-tls]
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, // lgtm[go/insecure-tls]
|
||||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // lgtm[go/insecure-tls]
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, // lgtm[go/insecure-tls]
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
@ -146,8 +147,8 @@ var cipherSuites = map[string]uint16{
|
||||||
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||||
|
|
||||||
// Legacy names.
|
// Legacy names.
|
||||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSOptions represents the TLS options that can be specified on *tls.Config
|
// TLSOptions represents the TLS options that can be specified on *tls.Config
|
||||||
|
@ -168,6 +169,7 @@ func (t *TLSOptions) TLSConfig() *tls.Config {
|
||||||
rs = tls.RenegotiateNever
|
rs = tls.RenegotiateNever
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // default MinVersion 1.2, if defined but empty 1.3 is used
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
CipherSuites: t.CipherSuites.Value(),
|
CipherSuites: t.CipherSuites.Value(),
|
||||||
MinVersion: t.MinVersion.Value(),
|
MinVersion: t.MinVersion.Value(),
|
||||||
|
|
|
@ -89,7 +89,7 @@ func (a *Authority) Export() (c *linkedca.Configuration, err error) {
|
||||||
if v.Type == "" {
|
if v.Type == "" {
|
||||||
typ = int32(linkedca.KMS_SOFTKMS)
|
typ = int32(linkedca.KMS_SOFTKMS)
|
||||||
} else {
|
} else {
|
||||||
typ, ok = linkedca.KMS_Type_value[strings.ToUpper(v.Type)]
|
typ, ok = linkedca.KMS_Type_value[strings.ToUpper(string(v.Type))]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.Errorf("unsupported kms type %s", v.Type)
|
return nil, errors.Errorf("unsupported kms type %s", v.Type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,10 +273,13 @@ func (c *linkedCaClient) GetCertificateData(serial string) (*db.CertificateData,
|
||||||
func (c *linkedCaClient) StoreCertificateChain(p provisioner.Interface, fullchain ...*x509.Certificate) error {
|
func (c *linkedCaClient) StoreCertificateChain(p provisioner.Interface, fullchain ...*x509.Certificate) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
raProvisioner, endpointID := createRegistrationAuthorityProvisioner(p)
|
||||||
_, err := c.client.PostCertificate(ctx, &linkedca.CertificateRequest{
|
_, err := c.client.PostCertificate(ctx, &linkedca.CertificateRequest{
|
||||||
PemCertificate: serializeCertificateChain(fullchain[0]),
|
PemCertificate: serializeCertificateChain(fullchain[0]),
|
||||||
PemCertificateChain: serializeCertificateChain(fullchain[1:]...),
|
PemCertificateChain: serializeCertificateChain(fullchain[1:]...),
|
||||||
Provisioner: createProvisionerIdentity(p),
|
Provisioner: createProvisionerIdentity(p),
|
||||||
|
RaProvisioner: raProvisioner,
|
||||||
|
EndpointId: endpointID,
|
||||||
})
|
})
|
||||||
return errors.Wrap(err, "error posting certificate")
|
return errors.Wrap(err, "error posting certificate")
|
||||||
}
|
}
|
||||||
|
@ -392,6 +395,26 @@ func createProvisionerIdentity(p provisioner.Interface) *linkedca.ProvisionerIde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type raProvisioner interface {
|
||||||
|
RAInfo() *provisioner.RAInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRegistrationAuthorityProvisioner(p provisioner.Interface) (*linkedca.RegistrationAuthorityProvisioner, string) {
|
||||||
|
if rap, ok := p.(raProvisioner); ok {
|
||||||
|
info := rap.RAInfo()
|
||||||
|
typ := linkedca.Provisioner_Type_value[strings.ToUpper(info.ProvisionerType)]
|
||||||
|
return &linkedca.RegistrationAuthorityProvisioner{
|
||||||
|
AuthorityId: info.AuthorityID,
|
||||||
|
Provisioner: &linkedca.ProvisionerIdentity{
|
||||||
|
Id: info.ProvisionerID,
|
||||||
|
Type: linkedca.Provisioner_Type(typ),
|
||||||
|
Name: info.ProvisionerName,
|
||||||
|
},
|
||||||
|
}, info.EndpointID
|
||||||
|
}
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
func serializeCertificate(crt *x509.Certificate) string {
|
func serializeCertificate(crt *x509.Certificate) string {
|
||||||
if crt == nil {
|
if crt == nil {
|
||||||
return ""
|
return ""
|
||||||
|
@ -438,7 +461,8 @@ func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||||
InsecureSkipVerify: true,
|
// nolint:gosec // used in bootstrap protocol
|
||||||
|
InsecureSkipVerify: true, // lgtm[go/disabled-certificate-check]
|
||||||
})))
|
})))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error connecting %s", endpoint)
|
return nil, errors.Wrapf(err, "error connecting %s", endpoint)
|
||||||
|
@ -491,7 +515,8 @@ func login(authority, token string, csr *x509.CertificateRequest, signer crypto.
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||||
RootCAs: rootCAs,
|
MinVersion: tls.VersionTLS12,
|
||||||
|
RootCAs: rootCAs,
|
||||||
})))
|
})))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrapf(err, "error connecting %s", endpoint)
|
return nil, nil, errors.Wrapf(err, "error connecting %s", endpoint)
|
||||||
|
@ -567,6 +592,7 @@ func login(authority, token string, csr *x509.CertificateRequest, signer crypto.
|
||||||
rootCAs.AddCert(bundle[last])
|
rootCAs.AddCert(bundle[last])
|
||||||
|
|
||||||
return cert, &tls.Config{
|
return cert, &tls.Config{
|
||||||
RootCAs: rootCAs,
|
MinVersion: tls.VersionTLS12,
|
||||||
|
RootCAs: rootCAs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,16 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/kms"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority/admin"
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
"github.com/smallstep/certificates/authority/config"
|
"github.com/smallstep/certificates/authority/config"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/cas"
|
"github.com/smallstep/certificates/cas"
|
||||||
casapi "github.com/smallstep/certificates/cas/apiv1"
|
casapi "github.com/smallstep/certificates/cas/apiv1"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/certificates/kms"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Option sets options to the Authority.
|
// Option sets options to the Authority.
|
||||||
|
|
|
@ -35,6 +35,7 @@ const awsIdentityURL = "http://169.254.169.254/latest/dynamic/instance-identity/
|
||||||
const awsSignatureURL = "http://169.254.169.254/latest/dynamic/instance-identity/signature"
|
const awsSignatureURL = "http://169.254.169.254/latest/dynamic/instance-identity/signature"
|
||||||
|
|
||||||
// awsAPITokenURL is the url used to get the IMDSv2 API token
|
// awsAPITokenURL is the url used to get the IMDSv2 API token
|
||||||
|
// nolint:gosec // no credentials here
|
||||||
const awsAPITokenURL = "http://169.254.169.254/latest/api/token"
|
const awsAPITokenURL = "http://169.254.169.254/latest/api/token"
|
||||||
|
|
||||||
// awsAPITokenTTL is the default TTL to use when requesting IMDSv2 API tokens
|
// awsAPITokenTTL is the default TTL to use when requesting IMDSv2 API tokens
|
||||||
|
@ -42,9 +43,11 @@ const awsAPITokenURL = "http://169.254.169.254/latest/api/token"
|
||||||
const awsAPITokenTTL = "30"
|
const awsAPITokenTTL = "30"
|
||||||
|
|
||||||
// awsMetadataTokenHeader is the header that must be passed with every IMDSv2 request
|
// awsMetadataTokenHeader is the header that must be passed with every IMDSv2 request
|
||||||
|
// nolint:gosec // no credentials here
|
||||||
const awsMetadataTokenHeader = "X-aws-ec2-metadata-token"
|
const awsMetadataTokenHeader = "X-aws-ec2-metadata-token"
|
||||||
|
|
||||||
// awsMetadataTokenTTLHeader is the header used to indicate the token TTL requested
|
// awsMetadataTokenTTLHeader is the header used to indicate the token TTL requested
|
||||||
|
// nolint:gosec // no credentials here
|
||||||
const awsMetadataTokenTTLHeader = "X-aws-ec2-metadata-token-ttl-seconds"
|
const awsMetadataTokenTTLHeader = "X-aws-ec2-metadata-token-ttl-seconds"
|
||||||
|
|
||||||
// awsCertificate is the certificate used to validate the instance identity
|
// awsCertificate is the certificate used to validate the instance identity
|
||||||
|
|
|
@ -316,7 +316,7 @@ func TestAWS_authorizeToken(t *testing.T) {
|
||||||
}
|
}
|
||||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
badKey, err := rsa.GenerateKey(rand.Reader, 1024)
|
badKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
|
@ -579,7 +579,7 @@ func TestAWS_AuthorizeSign(t *testing.T) {
|
||||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
badKey, err := rsa.GenerateKey(rand.Reader, 1024)
|
badKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
t4, err := generateAWSToken(
|
t4, err := generateAWSToken(
|
||||||
|
@ -748,6 +748,7 @@ func TestAWS_AuthorizeSSHSign(t *testing.T) {
|
||||||
pub := key.Public().Key
|
pub := key.Public().Key
|
||||||
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
// nolint:gosec // tests minimum size of the key
|
||||||
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
const azureOIDCBaseURL = "https://login.microsoftonline.com"
|
const azureOIDCBaseURL = "https://login.microsoftonline.com"
|
||||||
|
|
||||||
// azureIdentityTokenURL is the URL to get the identity token for an instance.
|
// azureIdentityTokenURL is the URL to get the identity token for an instance.
|
||||||
|
// nolint:gosec // no credentials here
|
||||||
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?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F"
|
||||||
|
|
||||||
// azureDefaultAudience is the default audience used.
|
// azureDefaultAudience is the default audience used.
|
||||||
|
|
|
@ -624,6 +624,7 @@ func TestAzure_AuthorizeSSHSign(t *testing.T) {
|
||||||
pub := key.Public().Key
|
pub := key.Public().Key
|
||||||
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
// nolint:gosec // tests minimum size of the key
|
||||||
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package provisioner
|
package provisioner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // not used for cryptographic security
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -319,6 +319,7 @@ func loadProvisioner(m *sync.Map, key string) (Interface, bool) {
|
||||||
// provisionerSum returns the SHA1 of the provisioners ID. From this we will
|
// provisionerSum returns the SHA1 of the provisioners ID. From this we will
|
||||||
// create the unique and sorted id.
|
// create the unique and sorted id.
|
||||||
func provisionerSum(p Interface) []byte {
|
func provisionerSum(p Interface) []byte {
|
||||||
|
// nolint:gosec // not used for cryptographic security
|
||||||
sum := sha1.Sum([]byte(p.GetID()))
|
sum := sha1.Sum([]byte(p.GetID()))
|
||||||
return sum[:]
|
return sum[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -623,6 +623,7 @@ func TestGCP_AuthorizeSSHSign(t *testing.T) {
|
||||||
pub := key.Public().Key
|
pub := key.Public().Key
|
||||||
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
// nolint:gosec // tests minimum size of the key
|
||||||
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ type jwtPayload struct {
|
||||||
|
|
||||||
type stepPayload struct {
|
type stepPayload struct {
|
||||||
SSH *SignSSHOptions `json:"ssh,omitempty"`
|
SSH *SignSSHOptions `json:"ssh,omitempty"`
|
||||||
|
RA *RAInfo `json:"ra,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWK is the default provisioner, an entity that can sign tokens necessary for
|
// JWK is the default provisioner, an entity that can sign tokens necessary for
|
||||||
|
@ -172,8 +173,17 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign")
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap provisioner if the token is an RA token.
|
||||||
|
var self Interface = p
|
||||||
|
if claims.Step != nil && claims.Step.RA != nil {
|
||||||
|
self = &raProvisioner{
|
||||||
|
Interface: p,
|
||||||
|
raInfo: claims.Step.RA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return []SignOption{
|
return []SignOption{
|
||||||
p,
|
self,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID),
|
newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID),
|
||||||
|
|
|
@ -411,6 +411,7 @@ func TestJWK_AuthorizeSSHSign(t *testing.T) {
|
||||||
pub := key.Public().Key
|
pub := key.Public().Key
|
||||||
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
// nolint:gosec // tests minimum size of the key
|
||||||
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -85,14 +85,14 @@ func (ks *keyStore) reload() {
|
||||||
// 0 it will randomly rotate between 0-12 hours, but every time we call to Get
|
// 0 it will randomly rotate between 0-12 hours, but every time we call to Get
|
||||||
// it will automatically rotate.
|
// it will automatically rotate.
|
||||||
func (ks *keyStore) nextReloadDuration(age time.Duration) time.Duration {
|
func (ks *keyStore) nextReloadDuration(age time.Duration) time.Duration {
|
||||||
n := rand.Int63n(int64(ks.jitter))
|
n := rand.Int63n(int64(ks.jitter)) // nolint:gosec // not used for cryptographic security
|
||||||
age -= time.Duration(n)
|
age -= time.Duration(n)
|
||||||
return abs(age)
|
return abs(age)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKeysFromJWKsURI(uri string) (jose.JSONWebKeySet, time.Duration, error) {
|
func getKeysFromJWKsURI(uri string) (jose.JSONWebKeySet, time.Duration, error) {
|
||||||
var keys jose.JSONWebKeySet
|
var keys jose.JSONWebKeySet
|
||||||
resp, err := http.Get(uri)
|
resp, err := http.Get(uri) // nolint:gosec // openid-configuration jwks_uri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return keys, 0, errors.Wrapf(err, "failed to connect to %s", uri)
|
return keys, 0, errors.Wrapf(err, "failed to connect to %s", uri)
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,31 +376,46 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "oidc.AuthorizeSSHSign")
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "oidc.AuthorizeSSHSign")
|
||||||
}
|
}
|
||||||
// Enforce an email claim
|
|
||||||
|
if claims.Subject == "" {
|
||||||
|
return nil, errs.Unauthorized("oidc.AuthorizeSSHSign: failed to validate oidc token payload: subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var data sshutil.TemplateData
|
||||||
|
var principals []string
|
||||||
|
|
||||||
if claims.Email == "" {
|
if claims.Email == "" {
|
||||||
return nil, errs.Unauthorized("oidc.AuthorizeSSHSign: failed to validate oidc token payload: email not found")
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the identity using either the default identityFunc or one injected
|
principals = nil
|
||||||
// externally. Note that the PreferredUsername might be empty.
|
} else {
|
||||||
// TBD: Would preferred_username present a safety issue here?
|
// Get the identity using either the default identityFunc or one injected
|
||||||
iden, err := o.ctl.GetIdentity(ctx, claims.Email)
|
// externally. Note that the PreferredUsername might be empty.
|
||||||
if err != nil {
|
// TBD: Would preferred_username present a safety issue here?
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "oidc.AuthorizeSSHSign")
|
iden, err := o.ctl.GetIdentity(ctx, claims.Email)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "oidc.AuthorizeSSHSign")
|
||||||
|
}
|
||||||
|
|
||||||
// Certificate templates.
|
// Certificate templates.
|
||||||
data := sshutil.CreateTemplateData(sshutil.UserCert, claims.Email, iden.Usernames)
|
data = sshutil.CreateTemplateData(sshutil.UserCert, claims.Email, iden.Usernames)
|
||||||
if v, err := unsafeParseSigned(token); err == nil {
|
if v, err := unsafeParseSigned(token); err == nil {
|
||||||
data.SetToken(v)
|
data.SetToken(v)
|
||||||
}
|
}
|
||||||
// Add custom extensions added in the identity function.
|
// Add custom extensions added in the identity function.
|
||||||
for k, v := range iden.Permissions.Extensions {
|
for k, v := range iden.Permissions.Extensions {
|
||||||
data.AddExtension(k, v)
|
data.AddExtension(k, v)
|
||||||
}
|
}
|
||||||
// Add custom critical options added in the identity function.
|
// Add custom critical options added in the identity function.
|
||||||
for k, v := range iden.Permissions.CriticalOptions {
|
for k, v := range iden.Permissions.CriticalOptions {
|
||||||
data.AddCriticalOption(k, v)
|
data.AddCriticalOption(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
principals = iden.Usernames
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the default template unless no-templates are configured and email is
|
// Use the default template unless no-templates are configured and email is
|
||||||
|
@ -429,7 +444,7 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
|
||||||
} else {
|
} else {
|
||||||
signOptions = append(signOptions, sshCertOptionsValidator(SignSSHOptions{
|
signOptions = append(signOptions, sshCertOptionsValidator(SignSSHOptions{
|
||||||
CertType: SSHUserCert,
|
CertType: SSHUserCert,
|
||||||
Principals: iden.Usernames,
|
Principals: principals,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +479,7 @@ func (o *OIDC) AuthorizeSSHRevoke(ctx context.Context, token string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAndDecode(uri string, v interface{}) error {
|
func getAndDecode(uri string, v interface{}) error {
|
||||||
resp, err := http.Get(uri)
|
resp, err := http.Get(uri) // nolint:gosec // openid-configuration uri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to connect to %s", uri)
|
return errors.Wrapf(err, "failed to connect to %s", uri)
|
||||||
}
|
}
|
||||||
|
|
|
@ -523,7 +523,12 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
||||||
okAdmin, err := generateOIDCToken("subject", "the-issuer", p3.ClientID, "root@example.com", "", time.Now(), &keys.Keys[0])
|
okAdmin, err := generateOIDCToken("subject", "the-issuer", p3.ClientID, "root@example.com", "", time.Now(), &keys.Keys[0])
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
// Empty email
|
// Empty email
|
||||||
failEmail, err := generateToken("subject", "the-issuer", p3.ClientID, "", []string{}, time.Now(), &keys.Keys[0])
|
emptyEmail, err := generateToken("subject", "the-issuer", p1.ClientID, "", []string{}, time.Now(), &keys.Keys[0])
|
||||||
|
expectemptyEmailOptions := &SignSSHOptions{
|
||||||
|
CertType: "user",
|
||||||
|
Principals: []string{},
|
||||||
|
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(p1.ctl.Claimer.DefaultUserSSHCertDuration())),
|
||||||
|
}
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
key, err := generateJSONWebKey()
|
key, err := generateJSONWebKey()
|
||||||
|
@ -535,6 +540,7 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
||||||
pub := key.Public().Key
|
pub := key.Public().Key
|
||||||
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
rsa2048, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
// nolint:gosec // tests minimum size of the key
|
||||||
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
rsa1024, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
@ -570,6 +576,7 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
||||||
{"ok", p1, args{t1, SignSSHOptions{}, pub}, expectedUserOptions, http.StatusOK, false, false},
|
{"ok", p1, args{t1, SignSSHOptions{}, pub}, expectedUserOptions, http.StatusOK, false, false},
|
||||||
{"ok-rsa2048", p1, args{t1, SignSSHOptions{}, rsa2048.Public()}, expectedUserOptions, http.StatusOK, false, false},
|
{"ok-rsa2048", p1, args{t1, SignSSHOptions{}, rsa2048.Public()}, expectedUserOptions, http.StatusOK, false, false},
|
||||||
{"ok-user", p1, args{t1, SignSSHOptions{CertType: "user"}, pub}, expectedUserOptions, http.StatusOK, false, false},
|
{"ok-user", p1, args{t1, SignSSHOptions{CertType: "user"}, pub}, expectedUserOptions, http.StatusOK, false, false},
|
||||||
|
{"ok-empty-email", p1, args{emptyEmail, SignSSHOptions{CertType: "user"}, pub}, expectemptyEmailOptions, http.StatusOK, false, false},
|
||||||
{"ok-principals", p1, args{t1, SignSSHOptions{Principals: []string{"name"}}, pub},
|
{"ok-principals", p1, args{t1, SignSSHOptions{Principals: []string{"name"}}, pub},
|
||||||
&SignSSHOptions{CertType: "user", Principals: []string{"name", "name@smallstep.com"},
|
&SignSSHOptions{CertType: "user", Principals: []string{"name", "name@smallstep.com"},
|
||||||
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false},
|
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false},
|
||||||
|
@ -592,7 +599,6 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
||||||
{"fail-rsa1024", p1, args{t1, SignSSHOptions{}, rsa1024.Public()}, expectedUserOptions, http.StatusOK, false, true},
|
{"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-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-user-principals", p1, args{t1, SignSSHOptions{Principals: []string{"root"}}, pub}, nil, http.StatusOK, false, true},
|
||||||
{"fail-email", p3, args{failEmail, SignSSHOptions{}, pub}, nil, http.StatusUnauthorized, true, false},
|
|
||||||
{"fail-getIdentity", p5, args{failGetIdentityToken, SignSSHOptions{}, pub}, nil, http.StatusInternalServerError, true, false},
|
{"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},
|
{"fail-sshCA-disabled", p6, args{"foo", SignSSHOptions{}, pub}, nil, http.StatusUnauthorized, true, false},
|
||||||
// Missing parametrs
|
// Missing parametrs
|
||||||
|
|
|
@ -254,6 +254,7 @@ func TestCustomTemplateOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_unsafeParseSigned(t *testing.T) {
|
func Test_unsafeParseSigned(t *testing.T) {
|
||||||
|
// nolint:gosec // no credentials here
|
||||||
okToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqYW5lQGRvZS5jb20iLCJpc3MiOiJodHRwczovL2RvZS5jb20iLCJqdGkiOiI4ZmYzMjQ4MS1mZDVmLTRlMmUtOTZkZi05MDhjMTI3Yzg1ZjciLCJpYXQiOjE1OTUzNjAwMjgsImV4cCI6MTU5NTM2MzYyOH0.aid8UuhFucJOFHXaob9zpNtVvhul9ulTGsA52mU6XIw"
|
okToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqYW5lQGRvZS5jb20iLCJpc3MiOiJodHRwczovL2RvZS5jb20iLCJqdGkiOiI4ZmYzMjQ4MS1mZDVmLTRlMmUtOTZkZi05MDhjMTI3Yzg1ZjciLCJpYXQiOjE1OTUzNjAwMjgsImV4cCI6MTU5NTM2MzYyOH0.aid8UuhFucJOFHXaob9zpNtVvhul9ulTGsA52mU6XIw"
|
||||||
type args struct {
|
type args struct {
|
||||||
s string
|
s string
|
||||||
|
|
|
@ -340,6 +340,27 @@ type Permissions struct {
|
||||||
CriticalOptions map[string]string `json:"criticalOptions"`
|
CriticalOptions map[string]string `json:"criticalOptions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RAInfo is the information about a provisioner present in RA tokens generated
|
||||||
|
// by StepCAS.
|
||||||
|
type RAInfo struct {
|
||||||
|
AuthorityID string `json:"authorityId,omitempty"`
|
||||||
|
EndpointID string `json:"endpointId,omitempty"`
|
||||||
|
ProvisionerID string `json:"provisionerId,omitempty"`
|
||||||
|
ProvisionerType string `json:"provisionerType,omitempty"`
|
||||||
|
ProvisionerName string `json:"provisionerName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// raProvisioner wraps a provisioner with RA data.
|
||||||
|
type raProvisioner struct {
|
||||||
|
Interface
|
||||||
|
raInfo *RAInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAInfo returns the RAInfo in the wrapped provisioner.
|
||||||
|
func (p *raProvisioner) RAInfo() *RAInfo {
|
||||||
|
return p.raInfo
|
||||||
|
}
|
||||||
|
|
||||||
// MockProvisioner for testing
|
// MockProvisioner for testing
|
||||||
type MockProvisioner struct {
|
type MockProvisioner struct {
|
||||||
Mret1, Mret2, Mret3 interface{}
|
Mret1, Mret2, Mret3 interface{}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func validateSSHCertificate(cert *ssh.Certificate, opts *SignSSHOptions) error {
|
||||||
return fmt.Errorf("certificate signature is nil")
|
return fmt.Errorf("certificate signature is nil")
|
||||||
case cert.SignatureKey == nil:
|
case cert.SignatureKey == nil:
|
||||||
return fmt.Errorf("certificate signature is nil")
|
return fmt.Errorf("certificate signature is nil")
|
||||||
case !reflect.DeepEqual(cert.ValidPrincipals, opts.Principals):
|
case !reflect.DeepEqual(cert.ValidPrincipals, opts.Principals) && (len(opts.Principals) > 0 || len(cert.ValidPrincipals) > 0):
|
||||||
return fmt.Errorf("certificate principals are not equal, want %v, got %v", opts.Principals, cert.ValidPrincipals)
|
return fmt.Errorf("certificate principals are not equal, want %v, got %v", opts.Principals, cert.ValidPrincipals)
|
||||||
case cert.CertType != ssh.UserCert && cert.CertType != ssh.HostCert:
|
case cert.CertType != ssh.UserCert && cert.CertType != ssh.HostCert:
|
||||||
return fmt.Errorf("certificate type %v is not valid", cert.CertType)
|
return fmt.Errorf("certificate type %v is not valid", cert.CertType)
|
||||||
|
|
|
@ -449,6 +449,7 @@ func generateAWSWithServer() (*AWS, *httptest.Server, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "error signing document")
|
return nil, nil, errors.Wrap(err, "error signing document")
|
||||||
}
|
}
|
||||||
|
// nolint:gosec // tests minimum size of the key
|
||||||
token := "AQAEAEEO9-7Z88ewKFpboZuDlFYWz9A3AN-wMOVzjEhfAyXW31BvVw=="
|
token := "AQAEAEEO9-7Z88ewKFpboZuDlFYWz9A3AN-wMOVzjEhfAyXW31BvVw=="
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
|
|
|
@ -221,8 +221,17 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign")
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "jwk.AuthorizeSign")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap provisioner if the token is an RA token.
|
||||||
|
var self Interface = p
|
||||||
|
if claims.Step != nil && claims.Step.RA != nil {
|
||||||
|
self = &raProvisioner{
|
||||||
|
Interface: p,
|
||||||
|
raInfo: claims.Step.RA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return []SignOption{
|
return []SignOption{
|
||||||
p,
|
self,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeX5C, p.Name, ""),
|
newProvisionerExtensionOption(TypeX5C, p.Name, ""),
|
||||||
|
|
|
@ -602,6 +602,9 @@ func (a *Authority) CheckSSHHost(ctx context.Context, principal, token string) (
|
||||||
|
|
||||||
// GetSSHHosts returns a list of valid host principals.
|
// GetSSHHosts returns a list of valid host principals.
|
||||||
func (a *Authority) GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) {
|
func (a *Authority) GetSSHHosts(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) {
|
||||||
|
if a.GetConfig().AuthorityConfig.DisableGetSSHHosts {
|
||||||
|
return nil, errs.New(http.StatusNotFound, "ssh hosts list api disabled")
|
||||||
|
}
|
||||||
if a.sshGetHostsFunc != nil {
|
if a.sshGetHostsFunc != nil {
|
||||||
hosts, err := a.sshGetHostsFunc(ctx, cert)
|
hosts, err := a.sshGetHostsFunc(ctx, cert)
|
||||||
return hosts, errs.Wrap(http.StatusInternalServerError, err, "getSSHHosts")
|
return hosts, errs.Wrap(http.StatusInternalServerError, err, "getSSHHosts")
|
||||||
|
|
|
@ -95,12 +95,17 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
||||||
signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
||||||
|
|
||||||
var prov provisioner.Interface
|
var prov provisioner.Interface
|
||||||
|
var pInfo *casapi.ProvisionerInfo
|
||||||
for _, op := range extraOpts {
|
for _, op := range extraOpts {
|
||||||
switch k := op.(type) {
|
switch k := op.(type) {
|
||||||
// Capture current provisioner
|
// Capture current provisioner
|
||||||
case provisioner.Interface:
|
case provisioner.Interface:
|
||||||
prov = k
|
prov = k
|
||||||
|
pInfo = &casapi.ProvisionerInfo{
|
||||||
|
ID: prov.GetID(),
|
||||||
|
Type: prov.GetType().String(),
|
||||||
|
Name: prov.GetName(),
|
||||||
|
}
|
||||||
// Adds new options to NewCertificate
|
// Adds new options to NewCertificate
|
||||||
case provisioner.CertificateOptions:
|
case provisioner.CertificateOptions:
|
||||||
certOptions = append(certOptions, k.Options(signOpts)...)
|
certOptions = append(certOptions, k.Options(signOpts)...)
|
||||||
|
@ -223,10 +228,11 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
||||||
// Sign certificate
|
// Sign certificate
|
||||||
lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate))
|
lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate))
|
||||||
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
||||||
Template: leaf,
|
Template: leaf,
|
||||||
CSR: csr,
|
CSR: csr,
|
||||||
Lifetime: lifetime,
|
Lifetime: lifetime,
|
||||||
Backdate: signOpts.Backdate,
|
Backdate: signOpts.Backdate,
|
||||||
|
Provisioner: pInfo,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...)
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...)
|
||||||
|
@ -741,10 +747,11 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
|
||||||
certTpl.NotAfter = now.Add(24 * time.Hour)
|
certTpl.NotAfter = now.Add(24 * time.Hour)
|
||||||
|
|
||||||
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
||||||
Template: certTpl,
|
Template: certTpl,
|
||||||
CSR: cr,
|
CSR: cr,
|
||||||
Lifetime: 24 * time.Hour,
|
Lifetime: 24 * time.Hour,
|
||||||
Backdate: 1 * time.Minute,
|
Backdate: 1 * time.Minute,
|
||||||
|
IsCAServerCert: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fatal(err)
|
return fatal(err)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
@ -199,6 +199,7 @@ func generateSubjectKeyID(pub crypto.PublicKey) ([]byte, error) {
|
||||||
if _, err = asn1.Unmarshal(b, &info); err != nil {
|
if _, err = asn1.Unmarshal(b, &info); err != nil {
|
||||||
return nil, fmt.Errorf("error unmarshaling public key: %w", err)
|
return nil, fmt.Errorf("error unmarshaling public key: %w", err)
|
||||||
}
|
}
|
||||||
|
// nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
hash := sha1.Sum(info.SubjectPublicKey.Bytes)
|
hash := sha1.Sum(info.SubjectPublicKey.Bytes)
|
||||||
return hash[:], nil
|
return hash[:], nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,7 @@ func TestBootstrap(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // insecure test servers
|
||||||
func TestBootstrapServerWithoutMTLS(t *testing.T) {
|
func TestBootstrapServerWithoutMTLS(t *testing.T) {
|
||||||
srv := startCABootstrapServer()
|
srv := startCABootstrapServer()
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
@ -256,6 +257,7 @@ func TestBootstrapServerWithoutMTLS(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // insecure test servers
|
||||||
func TestBootstrapServerWithMTLS(t *testing.T) {
|
func TestBootstrapServerWithMTLS(t *testing.T) {
|
||||||
srv := startCABootstrapServer()
|
srv := startCABootstrapServer()
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
@ -405,6 +407,7 @@ func TestBootstrapClientServerRotation(t *testing.T) {
|
||||||
|
|
||||||
// Create bootstrap server
|
// Create bootstrap server
|
||||||
token := generateBootstrapToken(caURL, "127.0.0.1", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
token := generateBootstrapToken(caURL, "127.0.0.1", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
|
// nolint:gosec // insecure test server
|
||||||
server, err := BootstrapServer(context.Background(), token, &http.Server{
|
server, err := BootstrapServer(context.Background(), token, &http.Server{
|
||||||
Addr: ":0",
|
Addr: ":0",
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
@ -523,6 +526,7 @@ func TestBootstrapClientServerFederation(t *testing.T) {
|
||||||
|
|
||||||
// Create bootstrap server
|
// Create bootstrap server
|
||||||
token := generateBootstrapToken(caURL1, "127.0.0.1", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
token := generateBootstrapToken(caURL1, "127.0.0.1", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
|
// nolint:gosec // insecure test server
|
||||||
server, err := BootstrapServer(context.Background(), token, &http.Server{
|
server, err := BootstrapServer(context.Background(), token, &http.Server{
|
||||||
Addr: ":0",
|
Addr: ":0",
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
10
ca/ca.go
10
ca/ca.go
|
@ -1,6 +1,7 @@
|
||||||
package ca
|
package ca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/go-chi/chi/middleware"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
acmeAPI "github.com/smallstep/certificates/acme/api"
|
acmeAPI "github.com/smallstep/certificates/acme/api"
|
||||||
|
@ -172,6 +174,10 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
|
||||||
insecureMux := chi.NewRouter()
|
insecureMux := chi.NewRouter()
|
||||||
insecureHandler := http.Handler(insecureMux)
|
insecureHandler := http.Handler(insecureMux)
|
||||||
|
|
||||||
|
// Add HEAD middleware
|
||||||
|
mux.Use(middleware.GetHead)
|
||||||
|
insecureMux.Use(middleware.GetHead)
|
||||||
|
|
||||||
// Add regular CA api endpoints in / and /1.0
|
// Add regular CA api endpoints in / and /1.0
|
||||||
api.Route(mux)
|
api.Route(mux)
|
||||||
mux.Route("/1.0", func(r chi.Router) {
|
mux.Route("/1.0", func(r chi.Router) {
|
||||||
|
@ -342,10 +348,10 @@ func (ca *CA) Run() error {
|
||||||
log.Printf("X.509 Root Fingerprint: %s", x509util.Fingerprint(crt))
|
log.Printf("X.509 Root Fingerprint: %s", x509util.Fingerprint(crt))
|
||||||
}
|
}
|
||||||
if authorityInfo.SSHCAHostPublicKey != nil {
|
if authorityInfo.SSHCAHostPublicKey != nil {
|
||||||
log.Printf("SSH Host CA Key: %s\n", authorityInfo.SSHCAHostPublicKey)
|
log.Printf("SSH Host CA Key: %s\n", bytes.TrimSpace(authorityInfo.SSHCAHostPublicKey))
|
||||||
}
|
}
|
||||||
if authorityInfo.SSHCAUserPublicKey != nil {
|
if authorityInfo.SSHCAUserPublicKey != nil {
|
||||||
log.Printf("SSH User CA Key: %s\n", authorityInfo.SSHCAUserPublicKey)
|
log.Printf("SSH User CA Key: %s\n", bytes.TrimSpace(authorityInfo.SSHCAUserPublicKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
@ -65,6 +65,8 @@ func generateSubjectKeyID(pub crypto.PublicKey) ([]byte, error) {
|
||||||
if _, err = asn1.Unmarshal(b, &info); err != nil {
|
if _, err = asn1.Unmarshal(b, &info); err != nil {
|
||||||
return nil, errors.Wrap(err, "error unmarshaling public key")
|
return nil, errors.Wrap(err, "error unmarshaling public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
hash := sha1.Sum(info.SubjectPublicKey.Bytes)
|
hash := sha1.Sum(info.SubjectPublicKey.Bytes)
|
||||||
return hash[:], nil
|
return hash[:], nil
|
||||||
}
|
}
|
||||||
|
|
17
ca/client.go
17
ca/client.go
|
@ -56,6 +56,7 @@ func newClient(transport http.RoundTripper) *uaClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // used in bootstrap protocol
|
||||||
func newInsecureClient() *uaClient {
|
func newInsecureClient() *uaClient {
|
||||||
return &uaClient{
|
return &uaClient{
|
||||||
Client: &http.Client{
|
Client: &http.Client{
|
||||||
|
@ -201,7 +202,9 @@ func (o *clientOptions) getTransport(endpoint string) (tr http.RoundTripper, err
|
||||||
switch tr := tr.(type) {
|
switch tr := tr.(type) {
|
||||||
case *http.Transport:
|
case *http.Transport:
|
||||||
if tr.TLSClientConfig == nil {
|
if tr.TLSClientConfig == nil {
|
||||||
tr.TLSClientConfig = &tls.Config{}
|
tr.TLSClientConfig = &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(tr.TLSClientConfig.Certificates) == 0 && tr.TLSClientConfig.GetClientCertificate == nil {
|
if len(tr.TLSClientConfig.Certificates) == 0 && tr.TLSClientConfig.GetClientCertificate == nil {
|
||||||
tr.TLSClientConfig.Certificates = []tls.Certificate{o.certificate}
|
tr.TLSClientConfig.Certificates = []tls.Certificate{o.certificate}
|
||||||
|
@ -209,7 +212,9 @@ func (o *clientOptions) getTransport(endpoint string) (tr http.RoundTripper, err
|
||||||
}
|
}
|
||||||
case *http2.Transport:
|
case *http2.Transport:
|
||||||
if tr.TLSClientConfig == nil {
|
if tr.TLSClientConfig == nil {
|
||||||
tr.TLSClientConfig = &tls.Config{}
|
tr.TLSClientConfig = &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(tr.TLSClientConfig.Certificates) == 0 && tr.TLSClientConfig.GetClientCertificate == nil {
|
if len(tr.TLSClientConfig.Certificates) == 0 && tr.TLSClientConfig.GetClientCertificate == nil {
|
||||||
tr.TLSClientConfig.Certificates = []tls.Certificate{o.certificate}
|
tr.TLSClientConfig.Certificates = []tls.Certificate{o.certificate}
|
||||||
|
@ -236,11 +241,15 @@ func WithTransport(tr http.RoundTripper) ClientOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithInsecure adds a insecure transport that bypasses TLS verification.
|
// WithInsecure adds a insecure transport that bypasses TLS verification.
|
||||||
|
// nolint:gosec // insecure option
|
||||||
func WithInsecure() ClientOption {
|
func WithInsecure() ClientOption {
|
||||||
return func(o *clientOptions) error {
|
return func(o *clientOptions) error {
|
||||||
o.transport = &http.Transport{
|
o.transport = &http.Transport{
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ func LoadClient() (*Client, error) {
|
||||||
// Prepare transport with information in defaults.json and identity.json
|
// Prepare transport with information in defaults.json and identity.json
|
||||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
tr.TLSClientConfig = &tls.Config{
|
tr.TLSClientConfig = &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
GetClientCertificate: identity.GetClientCertificateFunc(),
|
GetClientCertificate: identity.GetClientCertificateFunc(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ func TestClient(t *testing.T) {
|
||||||
Certificates: []tls.Certificate{crt},
|
Certificates: []tls.Certificate{crt},
|
||||||
ClientCAs: pool,
|
ClientCAs: pool,
|
||||||
ClientAuth: tls.VerifyClientCertIfGiven,
|
ClientAuth: tls.VerifyClientCertIfGiven,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
}
|
}
|
||||||
okServer.StartTLS()
|
okServer.StartTLS()
|
||||||
|
|
||||||
|
@ -132,6 +133,7 @@ func TestLoadClient(t *testing.T) {
|
||||||
tr.TLSClientConfig = &tls.Config{
|
tr.TLSClientConfig = &tls.Config{
|
||||||
Certificates: []tls.Certificate{crt},
|
Certificates: []tls.Certificate{crt},
|
||||||
RootCAs: pool,
|
RootCAs: pool,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
}
|
}
|
||||||
expected := &Client{
|
expected := &Client{
|
||||||
CaURL: &url.URL{Scheme: "https", Host: "127.0.0.1"},
|
CaURL: &url.URL{Scheme: "https", Host: "127.0.0.1"},
|
||||||
|
|
|
@ -296,6 +296,7 @@ func (i *Identity) Renew(client Renewer) error {
|
||||||
tr.TLSClientConfig = &tls.Config{
|
tr.TLSClientConfig = &tls.Config{
|
||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
RootCAs: client.GetRootCAs(),
|
RootCAs: client.GetRootCAs(),
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
PreferServerCipherSuites: true,
|
PreferServerCipherSuites: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -188,7 +187,7 @@ func Test_fileExists(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteDefaultIdentity(t *testing.T) {
|
func TestWriteDefaultIdentity(t *testing.T) {
|
||||||
tmpDir, err := ioutil.TempDir(os.TempDir(), "go-tests")
|
tmpDir, err := os.MkdirTemp(os.TempDir(), "go-tests")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -373,7 +372,7 @@ func (r *renewer) Renew(tr http.RoundTripper) (*api.SignResponse, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIdentity_Renew(t *testing.T) {
|
func TestIdentity_Renew(t *testing.T) {
|
||||||
tmpDir, err := ioutil.TempDir(os.TempDir(), "go-tests")
|
tmpDir, err := os.MkdirTemp(os.TempDir(), "go-tests")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ func (r *TLSRenewer) renewCertificate() {
|
||||||
cert, err := r.RenewCertificate()
|
cert, err := r.RenewCertificate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
next = r.renewJitter / 2
|
next = r.renewJitter / 2
|
||||||
next += time.Duration(rand.Int63n(int64(next)))
|
next += time.Duration(mathRandInt63n(int64(next)))
|
||||||
} else {
|
} else {
|
||||||
r.setCertificate(cert)
|
r.setCertificate(cert)
|
||||||
next = r.nextRenewDuration(cert.Leaf.NotAfter)
|
next = r.nextRenewDuration(cert.Leaf.NotAfter)
|
||||||
|
@ -185,10 +185,15 @@ func (r *TLSRenewer) renewCertificate() {
|
||||||
|
|
||||||
func (r *TLSRenewer) nextRenewDuration(notAfter time.Time) time.Duration {
|
func (r *TLSRenewer) nextRenewDuration(notAfter time.Time) time.Duration {
|
||||||
d := time.Until(notAfter).Truncate(time.Second) - r.renewBefore
|
d := time.Until(notAfter).Truncate(time.Second) - r.renewBefore
|
||||||
n := rand.Int63n(int64(r.renewJitter))
|
n := mathRandInt63n(int64(r.renewJitter))
|
||||||
d -= time.Duration(n)
|
d -= time.Duration(n)
|
||||||
if d < 0 {
|
if d < 0 {
|
||||||
d = 0
|
d = 0
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // not used for cryptographic security
|
||||||
|
func mathRandInt63n(n int64) int64 {
|
||||||
|
return rand.Int63n(n)
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ func init() {
|
||||||
d := &tls.Dialer{
|
d := &tls.Dialer{
|
||||||
NetDialer: getDefaultDialer(),
|
NetDialer: getDefaultDialer(),
|
||||||
Config: &tls.Config{
|
Config: &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
RootCAs: pool,
|
RootCAs: pool,
|
||||||
GetClientCertificate: id.GetClientCertificateFunc(),
|
GetClientCertificate: id.GetClientCertificateFunc(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func Test_newTLSOptionCtx(t *testing.T) {
|
func Test_newTLSOptionCtx(t *testing.T) {
|
||||||
client, err := NewClient("https://ca.smallstep.com", WithTransport(http.DefaultTransport))
|
client, err := NewClient("https://ca.smallstep.com", WithTransport(http.DefaultTransport))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,6 +41,7 @@ func Test_newTLSOptionCtx(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestTLSOptionCtx_apply(t *testing.T) {
|
func TestTLSOptionCtx_apply(t *testing.T) {
|
||||||
fail := func() TLSOption {
|
fail := func() TLSOption {
|
||||||
return func(ctx *TLSOptionCtx) error {
|
return func(ctx *TLSOptionCtx) error {
|
||||||
|
@ -76,6 +78,7 @@ func TestTLSOptionCtx_apply(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestRequireAndVerifyClientCert(t *testing.T) {
|
func TestRequireAndVerifyClientCert(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -100,6 +103,7 @@ func TestRequireAndVerifyClientCert(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestVerifyClientCertIfGiven(t *testing.T) {
|
func TestVerifyClientCertIfGiven(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -124,6 +128,7 @@ func TestVerifyClientCertIfGiven(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddRootCA(t *testing.T) {
|
func TestAddRootCA(t *testing.T) {
|
||||||
cert := parseCertificate(rootPEM)
|
cert := parseCertificate(rootPEM)
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
|
@ -156,6 +161,7 @@ func TestAddRootCA(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddClientCA(t *testing.T) {
|
func TestAddClientCA(t *testing.T) {
|
||||||
cert := parseCertificate(rootPEM)
|
cert := parseCertificate(rootPEM)
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
|
@ -188,6 +194,7 @@ func TestAddClientCA(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddRootsToRootCAs(t *testing.T) {
|
func TestAddRootsToRootCAs(t *testing.T) {
|
||||||
ca := startCATestServer()
|
ca := startCATestServer()
|
||||||
defer ca.Close()
|
defer ca.Close()
|
||||||
|
@ -242,6 +249,7 @@ func TestAddRootsToRootCAs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddRootsToClientCAs(t *testing.T) {
|
func TestAddRootsToClientCAs(t *testing.T) {
|
||||||
ca := startCATestServer()
|
ca := startCATestServer()
|
||||||
defer ca.Close()
|
defer ca.Close()
|
||||||
|
@ -296,6 +304,7 @@ func TestAddRootsToClientCAs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddFederationToRootCAs(t *testing.T) {
|
func TestAddFederationToRootCAs(t *testing.T) {
|
||||||
ca := startCATestServer()
|
ca := startCATestServer()
|
||||||
defer ca.Close()
|
defer ca.Close()
|
||||||
|
@ -360,6 +369,7 @@ func TestAddFederationToRootCAs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddFederationToClientCAs(t *testing.T) {
|
func TestAddFederationToClientCAs(t *testing.T) {
|
||||||
ca := startCATestServer()
|
ca := startCATestServer()
|
||||||
defer ca.Close()
|
defer ca.Close()
|
||||||
|
@ -424,6 +434,7 @@ func TestAddFederationToClientCAs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddRootsToCAs(t *testing.T) {
|
func TestAddRootsToCAs(t *testing.T) {
|
||||||
ca := startCATestServer()
|
ca := startCATestServer()
|
||||||
defer ca.Close()
|
defer ca.Close()
|
||||||
|
@ -478,6 +489,7 @@ func TestAddRootsToCAs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // test tls config
|
||||||
func TestAddFederationToCAs(t *testing.T) {
|
func TestAddFederationToCAs(t *testing.T) {
|
||||||
ca := startCATestServer()
|
ca := startCATestServer()
|
||||||
defer ca.Close()
|
defer ca.Close()
|
||||||
|
|
|
@ -6,12 +6,17 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/kms"
|
|
||||||
|
"go.step.sm/crypto/kms"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options represents the configuration options used to select and configure the
|
// Options represents the configuration options used to select and configure the
|
||||||
// CertificateAuthorityService (CAS) to use.
|
// CertificateAuthorityService (CAS) to use.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
// AuthorityID is the the id oc the current authority. This is used on
|
||||||
|
// StepCAS to add information about the origin of a certificate.
|
||||||
|
AuthorityID string `json:"-"`
|
||||||
|
|
||||||
// The type of the CAS to use.
|
// The type of the CAS to use.
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
"go.step.sm/crypto/kms/apiv1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CertificateAuthorityType indicates the type of Certificate Authority to
|
// CertificateAuthorityType indicates the type of Certificate Authority to
|
||||||
|
@ -52,11 +52,21 @@ const (
|
||||||
|
|
||||||
// CreateCertificateRequest is the request used to sign a new certificate.
|
// CreateCertificateRequest is the request used to sign a new certificate.
|
||||||
type CreateCertificateRequest struct {
|
type CreateCertificateRequest struct {
|
||||||
Template *x509.Certificate
|
Template *x509.Certificate
|
||||||
CSR *x509.CertificateRequest
|
CSR *x509.CertificateRequest
|
||||||
Lifetime time.Duration
|
Lifetime time.Duration
|
||||||
Backdate time.Duration
|
Backdate time.Duration
|
||||||
RequestID string
|
RequestID string
|
||||||
|
Provisioner *ProvisionerInfo
|
||||||
|
IsCAServerCert bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerInfo contains information of the provisioner used to authorize a
|
||||||
|
// certificate.
|
||||||
|
type ProvisionerInfo struct {
|
||||||
|
ID string
|
||||||
|
Type string
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCertificateResponse is the response to a create certificate request.
|
// CreateCertificateResponse is the response to a create certificate request.
|
||||||
|
|
|
@ -9,10 +9,11 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/kms"
|
||||||
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
"github.com/smallstep/certificates/cas/softcas"
|
"github.com/smallstep/certificates/cas/softcas"
|
||||||
"github.com/smallstep/certificates/kms"
|
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockCAS struct{}
|
type mockCAS struct{}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
|
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
|
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
gax "github.com/googleapis/gax-go/v2"
|
gax "github.com/googleapis/gax-go/v2"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
"google.golang.org/api/option"
|
"google.golang.org/api/option"
|
||||||
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
|
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
|
||||||
longrunningpb "google.golang.org/genproto/googleapis/longrunning"
|
longrunningpb "google.golang.org/genproto/googleapis/longrunning"
|
||||||
|
|
|
@ -9,10 +9,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms"
|
"go.step.sm/crypto/kms"
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
"go.step.sm/crypto/x509util"
|
"go.step.sm/crypto/x509util"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -217,7 +219,7 @@ func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthori
|
||||||
func (c *SoftCAS) initializeKeyManager() (err error) {
|
func (c *SoftCAS) initializeKeyManager() (err error) {
|
||||||
if c.KeyManager == nil {
|
if c.KeyManager == nil {
|
||||||
c.KeyManager, err = kms.New(context.Background(), kmsapi.Options{
|
c.KeyManager, err = kms.New(context.Background(), kmsapi.Options{
|
||||||
Type: string(kmsapi.DefaultKMS),
|
Type: kmsapi.DefaultKMS,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -262,8 +264,23 @@ func createCertificate(template, parent *x509.Certificate, pub crypto.PublicKey,
|
||||||
if sa, ok := signer.(apiv1.SignatureAlgorithmGetter); ok {
|
if sa, ok := signer.(apiv1.SignatureAlgorithmGetter); ok {
|
||||||
template.SignatureAlgorithm = sa.SignatureAlgorithm()
|
template.SignatureAlgorithm = sa.SignatureAlgorithm()
|
||||||
} else if _, ok := parent.PublicKey.(*rsa.PublicKey); ok {
|
} else if _, ok := parent.PublicKey.(*rsa.PublicKey); ok {
|
||||||
template.SignatureAlgorithm = parent.SignatureAlgorithm
|
// For RSA issuers, only overwrite the default algorithm is the
|
||||||
|
// intermediate is signed with an RSA signature scheme.
|
||||||
|
if isRSA(parent.SignatureAlgorithm) {
|
||||||
|
template.SignatureAlgorithm = parent.SignatureAlgorithm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return x509util.CreateCertificate(template, parent, pub, signer)
|
return x509util.CreateCertificate(template, parent, pub, signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isRSA(sa x509.SignatureAlgorithm) bool {
|
||||||
|
switch sa {
|
||||||
|
case x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
||||||
|
return true
|
||||||
|
case x509.SHA256WithRSAPSS, x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
@ -17,8 +19,8 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
"github.com/smallstep/certificates/kms"
|
"go.step.sm/crypto/kms"
|
||||||
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
"go.step.sm/crypto/x509util"
|
"go.step.sm/crypto/x509util"
|
||||||
)
|
)
|
||||||
|
@ -412,6 +414,95 @@ func TestSoftCAS_CreateCertificate_pss(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSoftCAS_CreateCertificate_ec_rsa(t *testing.T) {
|
||||||
|
rootSigner, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
intSigner, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// Root template
|
||||||
|
template := &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Test Root CA"},
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
|
||||||
|
PublicKey: rootSigner.Public(),
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
MaxPathLen: 0,
|
||||||
|
SerialNumber: big.NewInt(1234),
|
||||||
|
NotBefore: now,
|
||||||
|
NotAfter: now.Add(24 * time.Hour),
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := x509util.CreateCertificate(template, template, rootSigner.Public(), rootSigner)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intermediate template
|
||||||
|
template = &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Test Intermediate CA"},
|
||||||
|
KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign,
|
||||||
|
PublicKey: intSigner.Public(),
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
MaxPathLen: 0,
|
||||||
|
SerialNumber: big.NewInt(1234),
|
||||||
|
NotBefore: now,
|
||||||
|
NotAfter: now.Add(24 * time.Hour),
|
||||||
|
}
|
||||||
|
|
||||||
|
iss, err := x509util.CreateCertificate(template, root, intSigner.Public(), rootSigner)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if iss.SignatureAlgorithm != x509.ECDSAWithSHA256 {
|
||||||
|
t.Errorf("Certificate.SignatureAlgorithm = %v, want %v", iss.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &SoftCAS{
|
||||||
|
CertificateChain: []*x509.Certificate{iss},
|
||||||
|
Signer: intSigner,
|
||||||
|
}
|
||||||
|
cert, err := c.CreateCertificate(&apiv1.CreateCertificateRequest{
|
||||||
|
Template: &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "test.smallstep.com"},
|
||||||
|
DNSNames: []string{"test.smallstep.com"},
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||||
|
PublicKey: testSigner.Public(),
|
||||||
|
SerialNumber: big.NewInt(1234),
|
||||||
|
},
|
||||||
|
Lifetime: time.Hour, Backdate: time.Minute,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SoftCAS.CreateCertificate() error = %v", err)
|
||||||
|
}
|
||||||
|
if cert.Certificate.SignatureAlgorithm != x509.SHA256WithRSA {
|
||||||
|
t.Errorf("Certificate.SignatureAlgorithm = %v, want %v", iss.SignatureAlgorithm, x509.SHA256WithRSAPSS)
|
||||||
|
}
|
||||||
|
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
roots.AddCert(root)
|
||||||
|
intermediates := x509.NewCertPool()
|
||||||
|
intermediates.AddCert(iss)
|
||||||
|
if _, err = cert.Certificate.Verify(x509.VerifyOptions{
|
||||||
|
CurrentTime: time.Now(),
|
||||||
|
Roots: roots,
|
||||||
|
Intermediates: intermediates,
|
||||||
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||||
|
}); err != nil {
|
||||||
|
t.Errorf("Certificate.Verify() error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSoftCAS_RenewCertificate(t *testing.T) {
|
func TestSoftCAS_RenewCertificate(t *testing.T) {
|
||||||
mockNow(t)
|
mockNow(t)
|
||||||
|
|
||||||
|
@ -772,3 +863,32 @@ func TestSoftCAS_defaultKeyManager(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_isRSA(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
sa x509.SignatureAlgorithm
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"SHA256WithRSA", args{x509.SHA256WithRSA}, true},
|
||||||
|
{"SHA384WithRSA", args{x509.SHA384WithRSA}, true},
|
||||||
|
{"SHA512WithRSA", args{x509.SHA512WithRSA}, true},
|
||||||
|
{"SHA256WithRSAPSS", args{x509.SHA256WithRSAPSS}, true},
|
||||||
|
{"SHA384WithRSAPSS", args{x509.SHA384WithRSAPSS}, true},
|
||||||
|
{"SHA512WithRSAPSS", args{x509.SHA512WithRSAPSS}, true},
|
||||||
|
{"ECDSAWithSHA256", args{x509.ECDSAWithSHA256}, false},
|
||||||
|
{"ECDSAWithSHA384", args{x509.ECDSAWithSHA384}, false},
|
||||||
|
{"ECDSAWithSHA512", args{x509.ECDSAWithSHA512}, false},
|
||||||
|
{"PureEd25519", args{x509.PureEd25519}, false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := isRSA(tt.args.sa); got != tt.want {
|
||||||
|
t.Errorf("isRSA() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,13 +5,33 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/ca"
|
"github.com/smallstep/certificates/ca"
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// raAuthorityNS is a custom namespace used to generate endpoint ids based on
|
||||||
|
// the authority id.
|
||||||
|
var raAuthorityNS = uuid.MustParse("d6f14c1f-2f92-47bf-a04f-7b2c11382edd")
|
||||||
|
|
||||||
|
// newServerEndpointID returns a uuid v5 using raAuthorityNS as the namespace.
|
||||||
|
// The return uuid will be used as the server endpoint id, it will be unique per
|
||||||
|
// authority.
|
||||||
|
func newServerEndpointID(data string) uuid.UUID {
|
||||||
|
return uuid.NewSHA1(raAuthorityNS, []byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
type raInfo struct {
|
||||||
|
AuthorityID string `json:"authorityId,omitempty"`
|
||||||
|
EndpointID string `json:"endpointId,omitempty"`
|
||||||
|
ProvisionerID string `json:"provisionerId,omitempty"`
|
||||||
|
ProvisionerType string `json:"provisionerType,omitempty"`
|
||||||
|
ProvisionerName string `json:"provisionerName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type stepIssuer interface {
|
type stepIssuer interface {
|
||||||
SignToken(subject string, sans []string) (string, error)
|
SignToken(subject string, sans []string, info *raInfo) (string, error)
|
||||||
RevokeToken(subject string) (string, error)
|
RevokeToken(subject string) (string, error)
|
||||||
Lifetime(d time.Duration) time.Duration
|
Lifetime(d time.Duration) time.Duration
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/smallstep/certificates/ca"
|
"github.com/smallstep/certificates/ca"
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
|
@ -13,7 +14,7 @@ import (
|
||||||
|
|
||||||
type mockErrIssuer struct{}
|
type mockErrIssuer struct{}
|
||||||
|
|
||||||
func (m mockErrIssuer) SignToken(subject string, sans []string) (string, error) {
|
func (m mockErrIssuer) SignToken(subject string, sans []string, info *raInfo) (string, error) {
|
||||||
return "", apiv1.ErrNotImplemented{}
|
return "", apiv1.ErrNotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +36,42 @@ func (s *mockErrSigner) Options() jose.SignerOptions {
|
||||||
return jose.SignerOptions{}
|
return jose.SignerOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_newServerEndpointID(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
{"ok", args{"foo"}, []byte{
|
||||||
|
0x8f, 0x63, 0x69, 0x20, 0x8a, 0x7a, 0x57, 0x0c, 0xbe, 0x4c, 0x46, 0x66, 0x77, 0xf8, 0x54, 0xe7,
|
||||||
|
}},
|
||||||
|
{"ok uuid", args{"e4fa6d2d-fa9c-4fdc-913e-7484cc9516e4"}, []byte{
|
||||||
|
0x8d, 0x8d, 0x7f, 0x04, 0x73, 0xd4, 0x5f, 0x2f, 0xa8, 0xe1, 0x28, 0x9a, 0xd1, 0xa8, 0xcf, 0x7e,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var want uuid.UUID
|
||||||
|
copy(want[:], tt.want)
|
||||||
|
got := newServerEndpointID(tt.args.name)
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("newServerEndpointID() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
// Check version
|
||||||
|
if v := (got[6] & 0xf0) >> 4; v != 5 {
|
||||||
|
t.Errorf("newServerEndpointID() version = %d, want 5", v)
|
||||||
|
}
|
||||||
|
// Check variant
|
||||||
|
if v := (got[8] & 0x80) >> 6; v != 2 {
|
||||||
|
t.Errorf("newServerEndpointID() variant = %d, want 2", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_newStepIssuer(t *testing.T) {
|
func Test_newStepIssuer(t *testing.T) {
|
||||||
caURL, client := testCAHelper(t)
|
caURL, client := testCAHelper(t)
|
||||||
signer, err := newJWKSignerFromEncryptedKey(testKeyID, testEncryptedJWKKey, testPassword)
|
signer, err := newJWKSignerFromEncryptedKey(testKeyID, testEncryptedJWKKey, testPassword)
|
||||||
|
|
|
@ -53,25 +53,25 @@ func newJWKIssuer(caURL *url.URL, client *ca.Client, cfg *apiv1.CertificateIssue
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *jwkIssuer) SignToken(subject string, sans []string) (string, error) {
|
func (i *jwkIssuer) SignToken(subject string, sans []string, info *raInfo) (string, error) {
|
||||||
aud := i.caURL.ResolveReference(&url.URL{
|
aud := i.caURL.ResolveReference(&url.URL{
|
||||||
Path: "/1.0/sign",
|
Path: "/1.0/sign",
|
||||||
}).String()
|
}).String()
|
||||||
return i.createToken(aud, subject, sans)
|
return i.createToken(aud, subject, sans, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *jwkIssuer) RevokeToken(subject string) (string, error) {
|
func (i *jwkIssuer) RevokeToken(subject string) (string, error) {
|
||||||
aud := i.caURL.ResolveReference(&url.URL{
|
aud := i.caURL.ResolveReference(&url.URL{
|
||||||
Path: "/1.0/revoke",
|
Path: "/1.0/revoke",
|
||||||
}).String()
|
}).String()
|
||||||
return i.createToken(aud, subject, nil)
|
return i.createToken(aud, subject, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *jwkIssuer) Lifetime(d time.Duration) time.Duration {
|
func (i *jwkIssuer) Lifetime(d time.Duration) time.Duration {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *jwkIssuer) createToken(aud, sub string, sans []string) (string, error) {
|
func (i *jwkIssuer) createToken(aud, sub string, sans []string, info *raInfo) (string, error) {
|
||||||
id, err := randutil.Hex(64) // 256 bits
|
id, err := randutil.Hex(64) // 256 bits
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -84,6 +84,13 @@ func (i *jwkIssuer) createToken(aud, sub string, sans []string) (string, error)
|
||||||
"sans": sans,
|
"sans": sans,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if info != nil {
|
||||||
|
builder = builder.Claims(map[string]interface{}{
|
||||||
|
"step": map[string]interface{}{
|
||||||
|
"ra": info,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
tok, err := builder.CompactSerialize()
|
tok, err := builder.CompactSerialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -27,11 +27,16 @@ func Test_jwkIssuer_SignToken(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
subject string
|
subject string
|
||||||
sans []string
|
sans []string
|
||||||
|
info *raInfo
|
||||||
|
}
|
||||||
|
type stepClaims struct {
|
||||||
|
RA *raInfo `json:"ra"`
|
||||||
}
|
}
|
||||||
type claims struct {
|
type claims struct {
|
||||||
Aud []string `json:"aud"`
|
Aud []string `json:"aud"`
|
||||||
Sub string `json:"sub"`
|
Sub string `json:"sub"`
|
||||||
Sans []string `json:"sans"`
|
Sans []string `json:"sans"`
|
||||||
|
Step stepClaims `json:"step"`
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -39,8 +44,14 @@ func Test_jwkIssuer_SignToken(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}}, false},
|
{"ok", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}, nil}, false},
|
||||||
{"fail", fields{caURL, "ra@doe.org", &mockErrSigner{}}, args{"doe", []string{"doe.org"}}, true},
|
{"ok ra", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}, &raInfo{
|
||||||
|
AuthorityID: "authority-id", ProvisionerID: "provisioner-id", ProvisionerType: "provisioner-type",
|
||||||
|
}}, false},
|
||||||
|
{"ok ra endpoint id", fields{caURL, "ra@doe.org", signer}, args{"doe", []string{"doe.org"}, &raInfo{
|
||||||
|
AuthorityID: "authority-id", EndpointID: "endpoint-id",
|
||||||
|
}}, false},
|
||||||
|
{"fail", fields{caURL, "ra@doe.org", &mockErrSigner{}}, args{"doe", []string{"doe.org"}, nil}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
@ -49,7 +60,7 @@ func Test_jwkIssuer_SignToken(t *testing.T) {
|
||||||
issuer: tt.fields.issuer,
|
issuer: tt.fields.issuer,
|
||||||
signer: tt.fields.signer,
|
signer: tt.fields.signer,
|
||||||
}
|
}
|
||||||
got, err := i.SignToken(tt.args.subject, tt.args.sans)
|
got, err := i.SignToken(tt.args.subject, tt.args.sans, tt.args.info)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("jwkIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("jwkIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
|
@ -65,6 +76,9 @@ func Test_jwkIssuer_SignToken(t *testing.T) {
|
||||||
Sub: tt.args.subject,
|
Sub: tt.args.subject,
|
||||||
Sans: tt.args.sans,
|
Sans: tt.args.sans,
|
||||||
}
|
}
|
||||||
|
if tt.args.info != nil {
|
||||||
|
want.Step.RA = tt.args.info
|
||||||
|
}
|
||||||
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
|
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
|
||||||
t.Errorf("jwt.Claims() error = %v", err)
|
t.Errorf("jwt.Claims() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ func init() {
|
||||||
type StepCAS struct {
|
type StepCAS struct {
|
||||||
iss stepIssuer
|
iss stepIssuer
|
||||||
client *ca.Client
|
client *ca.Client
|
||||||
|
authorityID string
|
||||||
fingerprint string
|
fingerprint string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) {
|
||||||
return &StepCAS{
|
return &StepCAS{
|
||||||
iss: iss,
|
iss: iss,
|
||||||
client: client,
|
client: client,
|
||||||
|
authorityID: opts.AuthorityID,
|
||||||
fingerprint: opts.CertificateAuthorityFingerprint,
|
fingerprint: opts.CertificateAuthorityFingerprint,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -73,7 +75,19 @@ func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1
|
||||||
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
|
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, chain, err := s.createCertificate(req.CSR, req.Lifetime)
|
info := &raInfo{
|
||||||
|
AuthorityID: s.authorityID,
|
||||||
|
}
|
||||||
|
if req.IsCAServerCert {
|
||||||
|
info.EndpointID = newServerEndpointID(s.authorityID).String()
|
||||||
|
}
|
||||||
|
if p := req.Provisioner; p != nil {
|
||||||
|
info.ProvisionerID = p.ID
|
||||||
|
info.ProvisionerType = p.Type
|
||||||
|
info.ProvisionerName = p.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, chain, err := s.createCertificate(req.CSR, req.Lifetime, info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -135,7 +149,7 @@ func (s *StepCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityRequ
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.Duration) (*x509.Certificate, []*x509.Certificate, error) {
|
func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.Duration, raInfo *raInfo) (*x509.Certificate, []*x509.Certificate, error) {
|
||||||
sans := make([]string, 0, len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses)+len(cr.URIs))
|
sans := make([]string, 0, len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses)+len(cr.URIs))
|
||||||
sans = append(sans, cr.DNSNames...)
|
sans = append(sans, cr.DNSNames...)
|
||||||
sans = append(sans, cr.EmailAddresses...)
|
sans = append(sans, cr.EmailAddresses...)
|
||||||
|
@ -151,7 +165,7 @@ func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.D
|
||||||
commonName = sans[0]
|
commonName = sans[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := s.iss.SignToken(commonName, sans)
|
token, err := s.iss.SignToken(commonName, sans, raInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -291,7 +290,7 @@ func TestMain(m *testing.M) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create test files.
|
// Create test files.
|
||||||
path, err := ioutil.TempDir(os.TempDir(), "stepcas")
|
path, err := os.MkdirTemp(os.TempDir(), "stepcas")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -665,6 +664,22 @@ func TestStepCAS_CreateCertificate(t *testing.T) {
|
||||||
Certificate: testCrt,
|
Certificate: testCrt,
|
||||||
CertificateChain: []*x509.Certificate{testIssCrt},
|
CertificateChain: []*x509.Certificate{testIssCrt},
|
||||||
}, false},
|
}, false},
|
||||||
|
{"ok with provisioner", fields{jwk, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||||
|
CSR: testCR,
|
||||||
|
Lifetime: time.Hour,
|
||||||
|
Provisioner: &apiv1.ProvisionerInfo{ID: "provisioner-id", Type: "ACME"},
|
||||||
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
|
Certificate: testCrt,
|
||||||
|
CertificateChain: []*x509.Certificate{testIssCrt},
|
||||||
|
}, false},
|
||||||
|
{"ok with server cert", fields{jwk, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||||
|
CSR: testCR,
|
||||||
|
Lifetime: time.Hour,
|
||||||
|
IsCAServerCert: true,
|
||||||
|
}}, &apiv1.CreateCertificateResponse{
|
||||||
|
Certificate: testCrt,
|
||||||
|
CertificateChain: []*x509.Certificate{testIssCrt},
|
||||||
|
}, false},
|
||||||
{"fail CSR", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
{"fail CSR", fields{x5c, client, testRootFingerprint}, args{&apiv1.CreateCertificateRequest{
|
||||||
CSR: nil,
|
CSR: nil,
|
||||||
Lifetime: time.Hour,
|
Lifetime: time.Hour,
|
||||||
|
@ -691,6 +706,7 @@ func TestStepCAS_CreateCertificate(t *testing.T) {
|
||||||
s := &StepCAS{
|
s := &StepCAS{
|
||||||
iss: tt.fields.iss,
|
iss: tt.fields.iss,
|
||||||
client: tt.fields.client,
|
client: tt.fields.client,
|
||||||
|
authorityID: "authority-id",
|
||||||
fingerprint: tt.fields.fingerprint,
|
fingerprint: tt.fields.fingerprint,
|
||||||
}
|
}
|
||||||
got, err := s.CreateCertificate(tt.args.req)
|
got, err := s.CreateCertificate(tt.args.req)
|
||||||
|
|
|
@ -46,13 +46,13 @@ func newX5CIssuer(caURL *url.URL, cfg *apiv1.CertificateIssuer) (*x5cIssuer, err
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *x5cIssuer) SignToken(subject string, sans []string) (string, error) {
|
func (i *x5cIssuer) SignToken(subject string, sans []string, info *raInfo) (string, error) {
|
||||||
aud := i.caURL.ResolveReference(&url.URL{
|
aud := i.caURL.ResolveReference(&url.URL{
|
||||||
Path: "/1.0/sign",
|
Path: "/1.0/sign",
|
||||||
Fragment: "x5c/" + i.issuer,
|
Fragment: "x5c/" + i.issuer,
|
||||||
}).String()
|
}).String()
|
||||||
|
|
||||||
return i.createToken(aud, subject, sans)
|
return i.createToken(aud, subject, sans, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *x5cIssuer) RevokeToken(subject string) (string, error) {
|
func (i *x5cIssuer) RevokeToken(subject string) (string, error) {
|
||||||
|
@ -61,7 +61,7 @@ func (i *x5cIssuer) RevokeToken(subject string) (string, error) {
|
||||||
Fragment: "x5c/" + i.issuer,
|
Fragment: "x5c/" + i.issuer,
|
||||||
}).String()
|
}).String()
|
||||||
|
|
||||||
return i.createToken(aud, subject, nil)
|
return i.createToken(aud, subject, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *x5cIssuer) Lifetime(d time.Duration) time.Duration {
|
func (i *x5cIssuer) Lifetime(d time.Duration) time.Duration {
|
||||||
|
@ -76,7 +76,7 @@ func (i *x5cIssuer) Lifetime(d time.Duration) time.Duration {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error) {
|
func (i *x5cIssuer) createToken(aud, sub string, sans []string, info *raInfo) (string, error) {
|
||||||
signer, err := newX5CSigner(i.certFile, i.keyFile, i.password)
|
signer, err := newX5CSigner(i.certFile, i.keyFile, i.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -94,6 +94,13 @@ func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error)
|
||||||
"sans": sans,
|
"sans": sans,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if info != nil {
|
||||||
|
builder = builder.Claims(map[string]interface{}{
|
||||||
|
"step": map[string]interface{}{
|
||||||
|
"ra": info,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
tok, err := builder.CompactSerialize()
|
tok, err := builder.CompactSerialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -51,11 +51,16 @@ func Test_x5cIssuer_SignToken(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
subject string
|
subject string
|
||||||
sans []string
|
sans []string
|
||||||
|
info *raInfo
|
||||||
|
}
|
||||||
|
type stepClaims struct {
|
||||||
|
RA *raInfo `json:"ra"`
|
||||||
}
|
}
|
||||||
type claims struct {
|
type claims struct {
|
||||||
Aud []string `json:"aud"`
|
Aud []string `json:"aud"`
|
||||||
Sub string `json:"sub"`
|
Sub string `json:"sub"`
|
||||||
Sans []string `json:"sans"`
|
Sans []string `json:"sans"`
|
||||||
|
Step stepClaims `json:"step"`
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -63,10 +68,16 @@ func Test_x5cIssuer_SignToken(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}}, false},
|
{"ok", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, nil}, false},
|
||||||
{"fail crt", fields{caURL, "", testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}}, true},
|
{"ok ra", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, &raInfo{
|
||||||
{"fail key", fields{caURL, testX5CPath, "", "X5C"}, args{"doe", []string{"doe.org"}}, true},
|
AuthorityID: "authority-id", ProvisionerID: "provisioner-id", ProvisionerType: "provisioner-type",
|
||||||
{"fail no signer", fields{caURL, testIssKeyPath, testIssPath, "X5C"}, args{"doe", []string{"doe.org"}}, true},
|
}}, false},
|
||||||
|
{"ok ra endpoint id", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, &raInfo{
|
||||||
|
AuthorityID: "authority-id", EndpointID: "endpoint-id",
|
||||||
|
}}, false},
|
||||||
|
{"fail crt", fields{caURL, "", testX5CKeyPath, "X5C"}, args{"doe", []string{"doe.org"}, nil}, true},
|
||||||
|
{"fail key", fields{caURL, testX5CPath, "", "X5C"}, args{"doe", []string{"doe.org"}, nil}, true},
|
||||||
|
{"fail no signer", fields{caURL, testIssKeyPath, testIssPath, "X5C"}, args{"doe", []string{"doe.org"}, nil}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
@ -76,7 +87,7 @@ func Test_x5cIssuer_SignToken(t *testing.T) {
|
||||||
keyFile: tt.fields.keyFile,
|
keyFile: tt.fields.keyFile,
|
||||||
issuer: tt.fields.issuer,
|
issuer: tt.fields.issuer,
|
||||||
}
|
}
|
||||||
got, err := i.SignToken(tt.args.subject, tt.args.sans)
|
got, err := i.SignToken(tt.args.subject, tt.args.sans, tt.args.info)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("x5cIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("x5cIssuer.SignToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
|
@ -91,6 +102,9 @@ func Test_x5cIssuer_SignToken(t *testing.T) {
|
||||||
Sub: tt.args.subject,
|
Sub: tt.args.subject,
|
||||||
Sans: tt.args.sans,
|
Sans: tt.args.sans,
|
||||||
}
|
}
|
||||||
|
if tt.args.info != nil {
|
||||||
|
want.Step.RA = tt.args.info
|
||||||
|
}
|
||||||
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
|
if err := jwt.Claims(testX5CKey.Public(), &c); err != nil {
|
||||||
t.Errorf("jwt.Claims() error = %v", err)
|
t.Errorf("jwt.Claims() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
@ -14,10 +14,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/awskms"
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
"go.step.sm/cli-utils/fileutil"
|
||||||
"go.step.sm/cli-utils/ui"
|
"go.step.sm/cli-utils/ui"
|
||||||
|
"go.step.sm/crypto/kms/apiv1"
|
||||||
|
"go.step.sm/crypto/kms/awskms"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
@ -34,8 +34,11 @@ func main() {
|
||||||
// Initialize windows terminal
|
// Initialize windows terminal
|
||||||
ui.Init()
|
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{
|
c, err := awskms.New(context.Background(), apiv1.Options{
|
||||||
Type: string(apiv1.AmazonKMS),
|
Type: apiv1.AmazonKMS,
|
||||||
Region: region,
|
Region: region,
|
||||||
CredentialsFile: credentialsFile,
|
CredentialsFile: credentialsFile,
|
||||||
})
|
})
|
||||||
|
@ -239,6 +242,7 @@ func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
hash := sha1.Sum(b)
|
hash := sha1.Sum(b)
|
||||||
return hash[:]
|
return hash[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
// Server profiler
|
// Server profiler
|
||||||
|
// nolint:gosec // profile server, if enabled runs on a different port
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
|
@ -26,15 +27,13 @@ import (
|
||||||
"go.step.sm/cli-utils/usage"
|
"go.step.sm/cli-utils/usage"
|
||||||
|
|
||||||
// Enabled kms interfaces.
|
// Enabled kms interfaces.
|
||||||
_ "github.com/smallstep/certificates/kms/awskms"
|
_ "go.step.sm/crypto/kms/awskms"
|
||||||
_ "github.com/smallstep/certificates/kms/azurekms"
|
_ "go.step.sm/crypto/kms/azurekms"
|
||||||
_ "github.com/smallstep/certificates/kms/cloudkms"
|
_ "go.step.sm/crypto/kms/cloudkms"
|
||||||
_ "github.com/smallstep/certificates/kms/softkms"
|
_ "go.step.sm/crypto/kms/pkcs11"
|
||||||
_ "github.com/smallstep/certificates/kms/sshagentkms"
|
_ "go.step.sm/crypto/kms/softkms"
|
||||||
|
_ "go.step.sm/crypto/kms/sshagentkms"
|
||||||
// Experimental kms interfaces.
|
_ "go.step.sm/crypto/kms/yubikey"
|
||||||
_ "github.com/smallstep/certificates/kms/pkcs11"
|
|
||||||
_ "github.com/smallstep/certificates/kms/yubikey"
|
|
||||||
|
|
||||||
// Enabled cas interfaces.
|
// Enabled cas interfaces.
|
||||||
_ "github.com/smallstep/certificates/cas/cloudcas"
|
_ "github.com/smallstep/certificates/cas/cloudcas"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
@ -15,10 +15,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/cloudkms"
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
"go.step.sm/cli-utils/fileutil"
|
||||||
"go.step.sm/cli-utils/ui"
|
"go.step.sm/cli-utils/ui"
|
||||||
|
"go.step.sm/crypto/kms/apiv1"
|
||||||
|
"go.step.sm/crypto/kms/cloudkms"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
@ -65,8 +65,11 @@ func main() {
|
||||||
// Initialize windows terminal
|
// Initialize windows terminal
|
||||||
ui.Init()
|
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{
|
c, err := cloudkms.New(context.Background(), apiv1.Options{
|
||||||
Type: string(apiv1.CloudKMS),
|
Type: apiv1.CloudKMS,
|
||||||
CredentialsFile: credentialsFile,
|
CredentialsFile: credentialsFile,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -277,6 +280,7 @@ func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
hash := sha1.Sum(b)
|
hash := sha1.Sum(b)
|
||||||
return hash[:]
|
return hash[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
@ -18,15 +18,15 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/kms"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/uri"
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
"go.step.sm/cli-utils/fileutil"
|
||||||
"go.step.sm/cli-utils/ui"
|
"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"
|
"go.step.sm/crypto/pemutil"
|
||||||
|
|
||||||
// Enable pkcs11.
|
// Enable pkcs11.
|
||||||
_ "github.com/smallstep/certificates/kms/pkcs11"
|
_ "go.step.sm/crypto/kms/pkcs11"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is a mapping of the cli flags.
|
// Config is a mapping of the cli flags.
|
||||||
|
@ -151,6 +151,9 @@ func main() {
|
||||||
// Initialize windows terminal
|
// Initialize windows terminal
|
||||||
ui.Init()
|
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 {
|
switch {
|
||||||
case u.Get("pin-value") != "":
|
case u.Get("pin-value") != "":
|
||||||
case u.Get("pin-source") != "":
|
case u.Get("pin-source") != "":
|
||||||
|
@ -171,7 +174,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
k, err := kms.New(context.Background(), apiv1.Options{
|
k, err := kms.New(context.Background(), apiv1.Options{
|
||||||
Type: string(apiv1.PKCS11),
|
Type: apiv1.PKCS11,
|
||||||
URI: c.KMS,
|
URI: c.KMS,
|
||||||
Pin: c.Pin,
|
Pin: c.Pin,
|
||||||
})
|
})
|
||||||
|
@ -544,6 +547,7 @@ func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
hash := sha1.Sum(b)
|
hash := sha1.Sum(b)
|
||||||
return hash[:]
|
return hash[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha1" // nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
@ -18,14 +18,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/kms"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"go.step.sm/cli-utils/fileutil"
|
"go.step.sm/cli-utils/fileutil"
|
||||||
"go.step.sm/cli-utils/ui"
|
"go.step.sm/cli-utils/ui"
|
||||||
|
"go.step.sm/crypto/kms"
|
||||||
|
"go.step.sm/crypto/kms/apiv1"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
|
|
||||||
// Enable yubikey.
|
// Enable yubikey.
|
||||||
_ "github.com/smallstep/certificates/kms/yubikey"
|
_ "go.step.sm/crypto/kms/yubikey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is a mapping of the cli flags.
|
// Config is a mapping of the cli flags.
|
||||||
|
@ -90,6 +90,9 @@ func main() {
|
||||||
// Initialize windows terminal
|
// Initialize windows terminal
|
||||||
ui.Init()
|
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?")
|
pin, err := ui.PromptPassword("What is the YubiKey PIN?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
|
@ -97,7 +100,7 @@ func main() {
|
||||||
c.Pin = string(pin)
|
c.Pin = string(pin)
|
||||||
|
|
||||||
k, err := kms.New(context.Background(), apiv1.Options{
|
k, err := kms.New(context.Background(), apiv1.Options{
|
||||||
Type: string(apiv1.YubiKey),
|
Type: apiv1.YubiKey,
|
||||||
Pin: c.Pin,
|
Pin: c.Pin,
|
||||||
ManagementKey: c.ManagementKey,
|
ManagementKey: c.ManagementKey,
|
||||||
})
|
})
|
||||||
|
@ -346,6 +349,7 @@ func mustSubjectKeyID(key crypto.PublicKey) []byte {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// nolint:gosec // used to create the Subject Key Identifier by RFC 5280
|
||||||
hash := sha1.Sum(b)
|
hash := sha1.Sum(b)
|
||||||
return hash[:]
|
return hash[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,15 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/config"
|
"github.com/smallstep/certificates/authority/config"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/ca"
|
"github.com/smallstep/certificates/ca"
|
||||||
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/certificates/pki"
|
"github.com/smallstep/certificates/pki"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"go.step.sm/cli-utils/errs"
|
"go.step.sm/cli-utils/errs"
|
||||||
|
@ -99,10 +102,35 @@ func appAction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.LoadConfiguration(configFile)
|
cfg, err := config.LoadConfiguration(configFile)
|
||||||
if err != nil {
|
if err != nil && token == "" {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize a basic configuration to be used with an automatically
|
||||||
|
// configured linked RA. Default configuration includes:
|
||||||
|
// * badgerv2 on $(step path)/db
|
||||||
|
// * JSON logger
|
||||||
|
// * Default TLS options
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = &config.Config{
|
||||||
|
SkipValidation: true,
|
||||||
|
Logger: []byte(`{"format":"json"}`),
|
||||||
|
DB: &db.Config{
|
||||||
|
Type: "badgerv2",
|
||||||
|
DataSource: filepath.Join(step.Path(), "db"),
|
||||||
|
},
|
||||||
|
AuthorityConfig: &config.AuthConfig{
|
||||||
|
DeploymentType: pki.LinkedDeployment.String(),
|
||||||
|
Provisioners: provisioner.List{},
|
||||||
|
Template: &config.ASN1DN{},
|
||||||
|
Backdate: &provisioner.Duration{
|
||||||
|
Duration: config.DefaultBackdate,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &config.DefaultTLSOptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.AuthorityConfig != nil {
|
if cfg.AuthorityConfig != nil {
|
||||||
if token == "" && strings.EqualFold(cfg.AuthorityConfig.DeploymentType, pki.LinkedDeployment.String()) {
|
if token == "" && strings.EqualFold(cfg.AuthorityConfig.DeploymentType, pki.LinkedDeployment.String()) {
|
||||||
return errors.New(`'step-ca' requires the '--token' flag for linked deploy type.
|
return errors.New(`'step-ca' requires the '--token' flag for linked deploy type.
|
||||||
|
|
|
@ -92,6 +92,7 @@ func onboardAction(ctx *cli.Context) error {
|
||||||
token := ctx.Args().Get(0)
|
token := ctx.Args().Get(0)
|
||||||
onboardingURL := u.ResolveReference(&url.URL{Path: token}).String()
|
onboardingURL := u.ResolveReference(&url.URL{Path: token}).String()
|
||||||
|
|
||||||
|
// nolint:gosec // onboarding url
|
||||||
res, err := http.Get(onboardingURL)
|
res, err := http.Get(onboardingURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error connecting onboarding guide")
|
return errors.Wrap(err, "error connecting onboarding guide")
|
||||||
|
@ -132,6 +133,7 @@ func onboardAction(ctx *cli.Context) error {
|
||||||
return errors.Wrap(err, "error marshaling payload")
|
return errors.Wrap(err, "error marshaling payload")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:gosec // onboarding url
|
||||||
resp, err := http.Post(onboardingURL, "application/json", bytes.NewBuffer(payload))
|
resp, err := http.Post(onboardingURL, "application/json", bytes.NewBuffer(payload))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error connecting onboarding guide")
|
return errors.Wrap(err, "error connecting onboarding guide")
|
||||||
|
|
136
go.mod
136
go.mod
|
@ -1,41 +1,36 @@
|
||||||
module github.com/smallstep/certificates
|
module github.com/smallstep/certificates
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.100.2
|
cloud.google.com/go v0.100.2
|
||||||
cloud.google.com/go/kms v1.4.0
|
|
||||||
cloud.google.com/go/security v1.3.0
|
cloud.google.com/go/security v1.3.0
|
||||||
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible
|
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible // indirect
|
||||||
github.com/Azure/go-autorest/autorest v0.11.17
|
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0
|
github.com/Azure/go-autorest/autorest/date v0.3.0 // 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/Masterminds/sprig/v3 v3.2.2
|
github.com/Masterminds/sprig/v3 v3.2.2
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.4
|
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.37.0
|
github.com/aws/aws-sdk-go v1.44.37 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect
|
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect
|
||||||
github.com/fatih/color v1.9.0 // indirect
|
github.com/fatih/color v1.9.0 // indirect
|
||||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
github.com/go-chi/chi v4.1.2+incompatible
|
||||||
github.com/go-chi/chi v4.0.2+incompatible
|
|
||||||
github.com/go-kit/kit v0.10.0 // indirect
|
github.com/go-kit/kit v0.10.0 // indirect
|
||||||
github.com/go-piv/piv-go v1.7.0
|
github.com/go-piv/piv-go v1.10.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/go-cmp v0.5.7
|
github.com/google/go-cmp v0.5.8
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/googleapis/gax-go/v2 v2.1.1
|
github.com/googleapis/gax-go/v2 v2.4.0
|
||||||
github.com/hashicorp/vault/api v1.3.1
|
github.com/hashicorp/vault/api v1.3.1
|
||||||
github.com/hashicorp/vault/api/auth/approle v0.1.1
|
github.com/hashicorp/vault/api/auth/approle v0.1.1
|
||||||
github.com/hashicorp/vault/api/auth/kubernetes v0.1.0
|
github.com/hashicorp/vault/api/auth/kubernetes v0.1.0
|
||||||
github.com/jhump/protoreflect v1.9.0 // indirect
|
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-colorable v0.1.8 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
github.com/micromdm/scep/v2 v2.1.0
|
github.com/micromdm/scep/v2 v2.1.0
|
||||||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
github.com/newrelic/go-agent/v3 v3.18.0
|
||||||
github.com/newrelic/go-agent v2.15.0+incompatible
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rs/xid v1.2.1
|
github.com/rs/xid v1.2.1
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
|
@ -44,20 +39,107 @@ require (
|
||||||
github.com/smallstep/nosql v0.4.0
|
github.com/smallstep/nosql v0.4.0
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
github.com/urfave/cli v1.22.4
|
github.com/urfave/cli v1.22.4
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
|
||||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
||||||
go.step.sm/cli-utils v0.7.0
|
go.step.sm/cli-utils v0.7.4
|
||||||
go.step.sm/crypto v0.16.2
|
go.step.sm/crypto v0.18.0
|
||||||
go.step.sm/linkedca v0.16.1
|
go.step.sm/linkedca v0.18.0
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b
|
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
|
||||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect
|
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
google.golang.org/api v0.70.0
|
google.golang.org/api v0.84.0
|
||||||
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de
|
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/grpc v1.47.0
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go/compute v1.6.1 // indirect
|
||||||
|
cloud.google.com/go/iam v0.1.0 // indirect
|
||||||
|
cloud.google.com/go/kms v1.4.0 // indirect
|
||||||
|
filippo.io/edwards25519 v1.0.0-rc.1 // 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/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/cenkalti/backoff/v3 v3.0.0 // indirect
|
||||||
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // 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/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-logfmt/logfmt v0.5.0 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa // 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.3 // 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.1 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // 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.3.0 // indirect
|
||||||
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
|
||||||
|
github.com/huandu/xstrings v1.3.2 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
|
github.com/jackc/pgconn v1.10.1 // indirect
|
||||||
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
|
github.com/jackc/pgtype v1.9.0 // indirect
|
||||||
|
github.com/jackc/pgx/v4 v4.14.0 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.12.3 // indirect
|
||||||
|
github.com/manifoldco/promptui v0.9.0 // 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.4.2 // 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/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||||
|
github.com/ryanuber/go-glob v1.0.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
|
||||||
|
go.etcd.io/bbolt v1.3.6 // indirect
|
||||||
|
go.opencensus.io v0.23.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect
|
||||||
|
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
235
go.sum
235
go.sum
|
@ -36,8 +36,11 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
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/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
|
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
|
||||||
cloud.google.com/go/compute v1.3.0 h1:mPL/MzDDYHsh5tHRS9mhmhWlcgClCrCa6ApQCU6wnHI=
|
|
||||||
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
|
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
|
||||||
|
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
|
||||||
|
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||||
|
cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc=
|
||||||
|
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
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/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
cloud.google.com/go/iam v0.1.0 h1:W2vbGCrE3Z7J/x3WXLxxGl9LMSB2uhsAA7Ss/6u/qRY=
|
cloud.google.com/go/iam v0.1.0 h1:W2vbGCrE3Z7J/x3WXLxxGl9LMSB2uhsAA7Ss/6u/qRY=
|
||||||
|
@ -60,29 +63,30 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmG
|
||||||
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
filippo.io/edwards25519 v1.0.0-rc.1/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 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible h1:Cw16jiP4dI+CK761aq44ol4RV5dUiIIXky1+EKpoiVM=
|
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
|
||||||
github.com/Azure/azure-sdk-for-go v58.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v65.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 h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.17 h1:2zCdHwNgRH+St1J+ZMf66xI8aLr/5KMy+wWLH97zwYM=
|
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.11 h1:L4/pmq7poLdsy41Bj1FayKvBhayuWRYkx9HU5i4Ybl0=
|
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
|
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.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg=
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA=
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4=
|
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.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
|
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.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
|
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 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/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
|
||||||
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.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 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/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 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||||
github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
|
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
|
||||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
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 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
@ -101,59 +105,38 @@ github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFP
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E=
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8=
|
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8=
|
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
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/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
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/template v0.0.0-20190718012654-fb15b899a751/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-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
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-20190717042225-c3de453c63f4/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-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
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/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
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.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
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 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18=
|
||||||
github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
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 v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
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 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||||
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-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
github.com/aws/aws-sdk-go v1.27.0/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.37.0 h1:GzFnhOIsrGyQ69s7VgqtrG2BG8v7X7vwB3Xpbd/DBBk=
|
github.com/aws/aws-sdk-go v1.44.37 h1:KvDxCX6dfJeEDC77U5GPGSP0ErecmNnhDHFxw+NIvlI=
|
||||||
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.44.37/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
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 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||||
|
@ -161,7 +144,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
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 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.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
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/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
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/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||||
|
@ -179,6 +161,7 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP
|
||||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
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-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-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/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 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
@ -191,14 +174,12 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/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 v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
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-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 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.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
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.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432/go.mod h1:xwIwAxMvYnVrGJPe2FKx5prTrnAjGOD8zvDOnxnrrkM=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -214,7 +195,6 @@ 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-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 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
|
||||||
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
|
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/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
@ -233,6 +213,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
||||||
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.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.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/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
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/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
@ -240,10 +221,6 @@ 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/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 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
|
||||||
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 h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
|
|
||||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
|
||||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
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/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.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||||
|
@ -252,8 +229,8 @@ github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
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/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
|
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
|
||||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
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 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-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-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
@ -268,8 +245,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-piv/piv-go v1.7.0 h1:rfjdFdASfGV5KLJhSjgpGJ5lzVZVtRWn8ovy/H9HQ/U=
|
github.com/go-piv/piv-go v1.10.0 h1:P1Y1VjBI5DnXW0+YkKmTuh5opWnMIrKriUaIOblee9Q=
|
||||||
github.com/go-piv/piv-go v1.7.0/go.mod h1:ON2WvQncm7dIkCQ7kYJs+nc3V4jHGfrrJnSF8HKy7Gk=
|
github.com/go-piv/piv-go v1.10.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
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.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
|
@ -285,7 +262,9 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
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.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/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-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
@ -339,10 +318,10 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.4/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.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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+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.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.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
@ -367,11 +346,16 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||||
github.com/google/uuid v1.1.2/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 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa h1:7MYGT2XEMam7Mtzv1yDUYXANedWvwk3HKkR3MyGowy8=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
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.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||||
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
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/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
@ -468,7 +452,6 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
@ -489,7 +472,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
|
||||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
|
||||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
@ -527,32 +509,26 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
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/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
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.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.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.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.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.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/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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
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/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
|
github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
|
||||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||||
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.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.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/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
|
||||||
github.com/kr/pretty v0.2.1/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.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
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.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
@ -565,8 +541,6 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
|
||||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
|
||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
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.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||||
|
@ -594,10 +568,9 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyex
|
||||||
github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU=
|
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/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/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
|
||||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/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 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
|
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
|
@ -624,7 +597,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
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/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
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/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=
|
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||||
|
@ -632,9 +604,8 @@ 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.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nkeys v0.1.3/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/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f/go.mod h1:nwPd6pDNId/Xi16qtKrFHrauSwMNuvk+zcjk89wrnlA=
|
github.com/newrelic/go-agent/v3 v3.18.0 h1:AOR3hhF2ZVE0yfvNPuOaEhEvNMYyIfEBY8EizQpnt7g=
|
||||||
github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU=
|
github.com/newrelic/go-agent/v3 v3.18.0/go.mod h1:BFJOlbZWRlPTXKYIC1TTTtQKTnYntEJaU0VU507hDc0=
|
||||||
github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
|
|
||||||
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
|
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/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 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
|
@ -676,8 +647,6 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
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.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.4.0/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.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-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-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-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
@ -688,26 +657,20 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||||
github.com/prometheus/common v0.4.1/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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
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.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
|
||||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
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-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/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.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.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/fastuuid v1.2.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.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/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
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/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
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 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.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
@ -726,10 +689,8 @@ 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.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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
|
||||||
github.com/slackhq/nebula v1.5.2 h1:wuIOHsOnrNw3rQx8yPxXiGu8wAtAxxtUI/K8W7Vj7EI=
|
github.com/slackhq/nebula v1.5.2 h1:wuIOHsOnrNw3rQx8yPxXiGu8wAtAxxtUI/K8W7Vj7EI=
|
||||||
github.com/slackhq/nebula v1.5.2/go.mod h1:xaCM6wqbFk/NRmmUe1bv88fWBm3a1UioXJVIpR52WlE=
|
github.com/slackhq/nebula v1.5.2/go.mod h1:xaCM6wqbFk/NRmmUe1bv88fWBm3a1UioXJVIpR52WlE=
|
||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
||||||
|
@ -742,7 +703,6 @@ github.com/smallstep/pkcs7 v0.0.0-20211016004704-52592125d6f6/go.mod h1:SNgMg+Eg
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
|
||||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
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 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
@ -783,9 +743,6 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
|
||||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
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/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.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
@ -793,7 +750,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
github.com/yuin/goldmark v1.1.32/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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
go.etcd.io/bbolt v1.3.3/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.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
|
@ -811,13 +767,13 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.step.sm/cli-utils v0.7.0 h1:2GvY5Muid1yzp7YQbfCCS+gK3q7zlHjjLL5Z0DXz8ds=
|
go.step.sm/cli-utils v0.7.4 h1:oI7PStZqlvjPZ0u2EB4lN7yZ4R3ShTotdGL/L84Oorg=
|
||||||
go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/E=
|
go.step.sm/cli-utils v0.7.4/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I=
|
||||||
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
||||||
go.step.sm/crypto v0.16.2 h1:Pr9aazTwWBBZNogUsOqhOrPSdwAa9pPs+lMB602lnDA=
|
go.step.sm/crypto v0.18.0 h1:saD/tMG7uKJmUIPyOyudidVTHPnozTU02CDd+oqwKn0=
|
||||||
go.step.sm/crypto v0.16.2/go.mod h1:1WkTOTY+fOX/RY4TnZREp6trQAsBHRQ7nu6QJBiNQF8=
|
go.step.sm/crypto v0.18.0/go.mod h1:qZ+pNU1nV+THwP7TPTNCRMRr9xrRURhETTAK7U5psfw=
|
||||||
go.step.sm/linkedca v0.16.1 h1:CdbMV5SjnlRsgeYTXaaZmQCkYIgJq8BOzpewri57M2k=
|
go.step.sm/linkedca v0.18.0 h1:uxRBd2WDvJNZ2i0nJm/QmG4lkRxWoebYKJinchX7T7o=
|
||||||
go.step.sm/linkedca v0.16.1/go.mod h1:W59ucS4vFpuR0g4PtkGbbtXAwxbDEnNCg+ovkej1ANM=
|
go.step.sm/linkedca v0.18.0/go.mod h1:qSuYlIIhvPmA2+DSSS03E2IXhbXWTLW61Xh9zDQJ3VM=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
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.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.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
@ -844,14 +800,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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-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-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
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-20210711020723-a769d52b0f97/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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -934,14 +886,14 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
|
||||||
|
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -957,8 +909,12 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/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.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb h1:8tDJ3aechhddbdPAxpycgXHJRMLpk/Ab+aa4OgdN5/g=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -970,6 +926,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -989,7 +946,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/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-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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/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-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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -1001,13 +957,11 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/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-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-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-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-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-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-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-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -1016,21 +970,15 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/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-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-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-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-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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-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-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-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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/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-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -1038,26 +986,26 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-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-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-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-20210615035016-665e8c7367d1/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-20210616094352-59db8d763f22/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-20210630005230-0f9fa26af87c/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/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-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/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-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 h1:D1v9ucDTYBtbz5vNuBbAhIMAGhQhJ6Ym5ah3maMVNX4=
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/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-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s=
|
||||||
|
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
|
@ -1126,7 +1074,6 @@ golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roY
|
||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/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-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-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-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-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-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
@ -1136,23 +1083,20 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
|
||||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/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-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-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.2/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.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.4/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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk=
|
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
@ -1186,8 +1130,13 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr
|
||||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||||
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
||||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
||||||
google.golang.org/api v0.70.0 h1:67zQnAE0T2rB0A3CwLSas0K+SbVzSxP+zTLkQLexeiw=
|
|
||||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
||||||
|
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
|
||||||
|
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
|
||||||
|
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||||
|
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||||
|
google.golang.org/api v0.84.0 h1:NMB9J4cCxs9xEm+1Z9QiO3eFvn7EnQj3Eo3hN6ugVlg=
|
||||||
|
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
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.2.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -1263,8 +1212,18 @@ google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ6
|
||||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de h1:9Ti5SG2U4cAcluryUo/sFay3TQKoxiFMfaT0pbizU7k=
|
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
|
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||||
|
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||||
|
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||||
|
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||||
|
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||||
|
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||||
|
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||||
|
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
|
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad h1:kqrS+lhvaMHCxul6sKQvKJ8nAAhlVItmZV822hYFH/U=
|
||||||
|
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
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.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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
@ -1299,8 +1258,10 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
|
||||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||||
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
|
|
||||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||||
|
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||||
|
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
|
||||||
|
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
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-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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
@ -1321,9 +1282,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
@ -1341,7 +1301,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
package apiv1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KeyManager is the interface implemented by all the KMS.
|
|
||||||
type KeyManager interface {
|
|
||||||
GetPublicKey(req *GetPublicKeyRequest) (crypto.PublicKey, error)
|
|
||||||
CreateKey(req *CreateKeyRequest) (*CreateKeyResponse, error)
|
|
||||||
CreateSigner(req *CreateSignerRequest) (crypto.Signer, error)
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypter is an interface implemented by KMSes that are used
|
|
||||||
// in operations that require decryption
|
|
||||||
type Decrypter interface {
|
|
||||||
CreateDecrypter(req *CreateDecrypterRequest) (crypto.Decrypter, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CertificateManager is the interface implemented by the KMS that can load and
|
|
||||||
// store x509.Certificates.
|
|
||||||
type CertificateManager interface {
|
|
||||||
LoadCertificate(req *LoadCertificateRequest) (*x509.Certificate, error)
|
|
||||||
StoreCertificate(req *StoreCertificateRequest) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateName is an interface that KeyManager can implement to validate a
|
|
||||||
// given name or URI.
|
|
||||||
type NameValidator interface {
|
|
||||||
ValidateName(s string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrNotImplemented is the type of error returned if an operation is not
|
|
||||||
// implemented.
|
|
||||||
type ErrNotImplemented struct {
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrNotImplemented) Error() string {
|
|
||||||
if e.Message != "" {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
return "not implemented"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrAlreadyExists is the type of error returned if a key already exists. This
|
|
||||||
// is currently only implmented on pkcs11.
|
|
||||||
type ErrAlreadyExists struct {
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrAlreadyExists) Error() string {
|
|
||||||
if e.Message != "" {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
return "key already exists"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type represents the KMS type used.
|
|
||||||
type Type string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DefaultKMS is a KMS implementation using software.
|
|
||||||
DefaultKMS Type = ""
|
|
||||||
// SoftKMS is a KMS implementation using software.
|
|
||||||
SoftKMS Type = "softkms"
|
|
||||||
// CloudKMS is a KMS implementation using Google's Cloud KMS.
|
|
||||||
CloudKMS Type = "cloudkms"
|
|
||||||
// AmazonKMS is a KMS implementation using Amazon AWS KMS.
|
|
||||||
AmazonKMS Type = "awskms"
|
|
||||||
// PKCS11 is a KMS implementation using the PKCS11 standard.
|
|
||||||
PKCS11 Type = "pkcs11"
|
|
||||||
// YubiKey is a KMS implementation using a YubiKey PIV.
|
|
||||||
YubiKey Type = "yubikey"
|
|
||||||
// SSHAgentKMS is a KMS implementation using ssh-agent to access keys.
|
|
||||||
SSHAgentKMS Type = "sshagentkms"
|
|
||||||
// AzureKMS is a KMS implementation using Azure Key Vault.
|
|
||||||
AzureKMS Type = "azurekms"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options are the KMS options. They represent the kms object in the ca.json.
|
|
||||||
type Options struct {
|
|
||||||
// The type of the KMS to use.
|
|
||||||
Type string `json:"type"`
|
|
||||||
|
|
||||||
// Path to the credentials file used in CloudKMS and AmazonKMS.
|
|
||||||
CredentialsFile string `json:"credentialsFile,omitempty"`
|
|
||||||
|
|
||||||
// URI is based on the PKCS #11 URI Scheme defined in
|
|
||||||
// https://tools.ietf.org/html/rfc7512 and represents the configuration used
|
|
||||||
// to connect to the KMS.
|
|
||||||
//
|
|
||||||
// Used by: pkcs11
|
|
||||||
URI string `json:"uri,omitempty"`
|
|
||||||
|
|
||||||
// Pin used to access the PKCS11 module. It can be defined in the URI using
|
|
||||||
// the pin-value or pin-source properties.
|
|
||||||
Pin string `json:"pin,omitempty"`
|
|
||||||
|
|
||||||
// ManagementKey used in YubiKeys. Default management key is the hexadecimal
|
|
||||||
// string 010203040506070801020304050607080102030405060708:
|
|
||||||
// []byte{
|
|
||||||
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
||||||
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
||||||
// 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
||||||
// }
|
|
||||||
ManagementKey string `json:"managementKey,omitempty"`
|
|
||||||
|
|
||||||
// Region to use in AmazonKMS.
|
|
||||||
Region string `json:"region,omitempty"`
|
|
||||||
|
|
||||||
// Profile to use in AmazonKMS.
|
|
||||||
Profile string `json:"profile,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks the fields in Options.
|
|
||||||
func (o *Options) Validate() error {
|
|
||||||
if o == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch Type(strings.ToLower(o.Type)) {
|
|
||||||
case DefaultKMS, SoftKMS: // Go crypto based kms.
|
|
||||||
case CloudKMS, AmazonKMS, AzureKMS: // Cloud based kms.
|
|
||||||
case YubiKey, PKCS11: // Hardware based kms.
|
|
||||||
case SSHAgentKMS: // Others
|
|
||||||
default:
|
|
||||||
return errors.Errorf("unsupported kms type %s", o.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package apiv1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestOptions_Validate(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
options *Options
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"nil", nil, false},
|
|
||||||
{"softkms", &Options{Type: "softkms"}, false},
|
|
||||||
{"cloudkms", &Options{Type: "cloudkms"}, false},
|
|
||||||
{"awskms", &Options{Type: "awskms"}, false},
|
|
||||||
{"sshagentkms", &Options{Type: "sshagentkms"}, false},
|
|
||||||
{"pkcs11", &Options{Type: "pkcs11"}, false},
|
|
||||||
{"unsupported", &Options{Type: "unsupported"}, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if err := tt.options.Validate(); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Options.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrNotImplemented_Error(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"default", fields{}, "not implemented"},
|
|
||||||
{"custom", fields{"custom message: not implemented"}, "custom message: not implemented"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
e := ErrNotImplemented{
|
|
||||||
Message: tt.fields.msg,
|
|
||||||
}
|
|
||||||
if got := e.Error(); got != tt.want {
|
|
||||||
t.Errorf("ErrNotImplemented.Error() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrAlreadyExists_Error(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"default", fields{}, "key already exists"},
|
|
||||||
{"custom", fields{"custom message: key already exists"}, "custom message: key already exists"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
e := ErrAlreadyExists{
|
|
||||||
Message: tt.fields.msg,
|
|
||||||
}
|
|
||||||
if got := e.Error(); got != tt.want {
|
|
||||||
t.Errorf("ErrAlreadyExists.Error() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package apiv1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var registry = new(sync.Map)
|
|
||||||
|
|
||||||
// KeyManagerNewFunc is the type that represents the method to initialize a new
|
|
||||||
// KeyManager.
|
|
||||||
type KeyManagerNewFunc func(ctx context.Context, opts Options) (KeyManager, error)
|
|
||||||
|
|
||||||
// Register adds to the registry a method to create a KeyManager of type t.
|
|
||||||
func Register(t Type, fn KeyManagerNewFunc) {
|
|
||||||
registry.Store(t, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadKeyManagerNewFunc returns the function initialize a KayManager.
|
|
||||||
func LoadKeyManagerNewFunc(t Type) (KeyManagerNewFunc, bool) {
|
|
||||||
v, ok := registry.Load(t)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
fn, ok := v.(KeyManagerNewFunc)
|
|
||||||
return fn, ok
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
package apiv1
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ProtectionLevel specifies on some KMS how cryptographic operations are
|
|
||||||
// performed.
|
|
||||||
type ProtectionLevel int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Protection level not specified.
|
|
||||||
UnspecifiedProtectionLevel ProtectionLevel = iota
|
|
||||||
// Crypto operations are performed in software.
|
|
||||||
Software
|
|
||||||
// Crypto operations are performed in a Hardware Security Module.
|
|
||||||
HSM
|
|
||||||
)
|
|
||||||
|
|
||||||
// String returns a string representation of p.
|
|
||||||
func (p ProtectionLevel) String() string {
|
|
||||||
switch p {
|
|
||||||
case UnspecifiedProtectionLevel:
|
|
||||||
return "unspecified"
|
|
||||||
case Software:
|
|
||||||
return "software"
|
|
||||||
case HSM:
|
|
||||||
return "hsm"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown(%d)", p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignatureAlgorithm used for cryptographic signing.
|
|
||||||
type SignatureAlgorithm int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Not specified.
|
|
||||||
UnspecifiedSignAlgorithm SignatureAlgorithm = iota
|
|
||||||
// RSASSA-PKCS1-v1_5 key and a SHA256 digest.
|
|
||||||
SHA256WithRSA
|
|
||||||
// RSASSA-PKCS1-v1_5 key and a SHA384 digest.
|
|
||||||
SHA384WithRSA
|
|
||||||
// RSASSA-PKCS1-v1_5 key and a SHA512 digest.
|
|
||||||
SHA512WithRSA
|
|
||||||
// RSASSA-PSS key with a SHA256 digest.
|
|
||||||
SHA256WithRSAPSS
|
|
||||||
// RSASSA-PSS key with a SHA384 digest.
|
|
||||||
SHA384WithRSAPSS
|
|
||||||
// RSASSA-PSS key with a SHA512 digest.
|
|
||||||
SHA512WithRSAPSS
|
|
||||||
// ECDSA on the NIST P-256 curve with a SHA256 digest.
|
|
||||||
ECDSAWithSHA256
|
|
||||||
// ECDSA on the NIST P-384 curve with a SHA384 digest.
|
|
||||||
ECDSAWithSHA384
|
|
||||||
// ECDSA on the NIST P-521 curve with a SHA512 digest.
|
|
||||||
ECDSAWithSHA512
|
|
||||||
// EdDSA on Curve25519 with a SHA512 digest.
|
|
||||||
PureEd25519
|
|
||||||
)
|
|
||||||
|
|
||||||
// String returns a string representation of s.
|
|
||||||
func (s SignatureAlgorithm) String() string {
|
|
||||||
switch s {
|
|
||||||
case UnspecifiedSignAlgorithm:
|
|
||||||
return "unspecified"
|
|
||||||
case SHA256WithRSA:
|
|
||||||
return "SHA256-RSA"
|
|
||||||
case SHA384WithRSA:
|
|
||||||
return "SHA384-RSA"
|
|
||||||
case SHA512WithRSA:
|
|
||||||
return "SHA512-RSA"
|
|
||||||
case SHA256WithRSAPSS:
|
|
||||||
return "SHA256-RSAPSS"
|
|
||||||
case SHA384WithRSAPSS:
|
|
||||||
return "SHA384-RSAPSS"
|
|
||||||
case SHA512WithRSAPSS:
|
|
||||||
return "SHA512-RSAPSS"
|
|
||||||
case ECDSAWithSHA256:
|
|
||||||
return "ECDSA-SHA256"
|
|
||||||
case ECDSAWithSHA384:
|
|
||||||
return "ECDSA-SHA384"
|
|
||||||
case ECDSAWithSHA512:
|
|
||||||
return "ECDSA-SHA512"
|
|
||||||
case PureEd25519:
|
|
||||||
return "Ed25519"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("unknown(%d)", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPublicKeyRequest is the parameter used in the kms.GetPublicKey method.
|
|
||||||
type GetPublicKeyRequest struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKeyRequest is the parameter used in the kms.CreateKey method.
|
|
||||||
type CreateKeyRequest struct {
|
|
||||||
// Name represents the key name or label used to identify a key.
|
|
||||||
//
|
|
||||||
// Used by: awskms, cloudkms, azurekms, pkcs11, yubikey.
|
|
||||||
Name string
|
|
||||||
|
|
||||||
// SignatureAlgorithm represents the type of key to create.
|
|
||||||
SignatureAlgorithm SignatureAlgorithm
|
|
||||||
|
|
||||||
// Bits is the number of bits on RSA keys.
|
|
||||||
Bits int
|
|
||||||
|
|
||||||
// ProtectionLevel specifies how cryptographic operations are performed.
|
|
||||||
// Used by: cloudkms, azurekms.
|
|
||||||
ProtectionLevel ProtectionLevel
|
|
||||||
|
|
||||||
// Extractable defines if the new key may be exported from the HSM under a
|
|
||||||
// wrap key. On pkcs11 sets the CKA_EXTRACTABLE bit.
|
|
||||||
//
|
|
||||||
// Used by: pkcs11
|
|
||||||
Extractable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKeyResponse is the response value of the kms.CreateKey method.
|
|
||||||
type CreateKeyResponse struct {
|
|
||||||
Name string
|
|
||||||
PublicKey crypto.PublicKey
|
|
||||||
PrivateKey crypto.PrivateKey
|
|
||||||
CreateSignerRequest CreateSignerRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSignerRequest is the parameter used in the kms.CreateSigner method.
|
|
||||||
type CreateSignerRequest struct {
|
|
||||||
Signer crypto.Signer
|
|
||||||
SigningKey string
|
|
||||||
SigningKeyPEM []byte
|
|
||||||
TokenLabel string
|
|
||||||
PublicKey string
|
|
||||||
PublicKeyPEM []byte
|
|
||||||
Password []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDecrypterRequest is the parameter used in the kms.Decrypt method.
|
|
||||||
type CreateDecrypterRequest struct {
|
|
||||||
Decrypter crypto.Decrypter
|
|
||||||
DecryptionKey string
|
|
||||||
DecryptionKeyPEM []byte
|
|
||||||
Password []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadCertificateRequest is the parameter used in the LoadCertificate method of
|
|
||||||
// a CertificateManager.
|
|
||||||
type LoadCertificateRequest struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreCertificateRequest is the parameter used in the StoreCertificate method
|
|
||||||
// of a CertificateManager.
|
|
||||||
type StoreCertificateRequest struct {
|
|
||||||
Name string
|
|
||||||
Certificate *x509.Certificate
|
|
||||||
|
|
||||||
// Extractable defines if the new certificate may be exported from the HSM
|
|
||||||
// under a wrap key. On pkcs11 sets the CKA_EXTRACTABLE bit.
|
|
||||||
//
|
|
||||||
// Used by: pkcs11
|
|
||||||
Extractable bool
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package apiv1
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestProtectionLevel_String(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
p ProtectionLevel
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"unspecified", UnspecifiedProtectionLevel, "unspecified"},
|
|
||||||
{"software", Software, "software"},
|
|
||||||
{"hsm", HSM, "hsm"},
|
|
||||||
{"unknown", ProtectionLevel(100), "unknown(100)"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := tt.p.String(); got != tt.want {
|
|
||||||
t.Errorf("ProtectionLevel.String() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignatureAlgorithm_String(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
s SignatureAlgorithm
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"UnspecifiedSignAlgorithm", UnspecifiedSignAlgorithm, "unspecified"},
|
|
||||||
{"SHA256WithRSA", SHA256WithRSA, "SHA256-RSA"},
|
|
||||||
{"SHA384WithRSA", SHA384WithRSA, "SHA384-RSA"},
|
|
||||||
{"SHA512WithRSA", SHA512WithRSA, "SHA512-RSA"},
|
|
||||||
{"SHA256WithRSAPSS", SHA256WithRSAPSS, "SHA256-RSAPSS"},
|
|
||||||
{"SHA384WithRSAPSS", SHA384WithRSAPSS, "SHA384-RSAPSS"},
|
|
||||||
{"SHA512WithRSAPSS", SHA512WithRSAPSS, "SHA512-RSAPSS"},
|
|
||||||
{"ECDSAWithSHA256", ECDSAWithSHA256, "ECDSA-SHA256"},
|
|
||||||
{"ECDSAWithSHA384", ECDSAWithSHA384, "ECDSA-SHA384"},
|
|
||||||
{"ECDSAWithSHA512", ECDSAWithSHA512, "ECDSA-SHA512"},
|
|
||||||
{"PureEd25519", PureEd25519, "Ed25519"},
|
|
||||||
{"unknown", SignatureAlgorithm(100), "unknown(100)"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := tt.s.String(); got != tt.want {
|
|
||||||
t.Errorf("SignatureAlgorithm.String() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,267 +0,0 @@
|
||||||
package awskms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/aws/aws-sdk-go/service/kms"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/uri"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scheme is the scheme used in uris.
|
|
||||||
const Scheme = "awskms"
|
|
||||||
|
|
||||||
// KMS implements a KMS using AWS Key Management Service.
|
|
||||||
type KMS struct {
|
|
||||||
session *session.Session
|
|
||||||
service KeyManagementClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyManagementClient defines the methods on KeyManagementClient that this
|
|
||||||
// package will use. This interface will be used for unit testing.
|
|
||||||
type KeyManagementClient interface {
|
|
||||||
GetPublicKeyWithContext(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error)
|
|
||||||
CreateKeyWithContext(ctx aws.Context, input *kms.CreateKeyInput, opts ...request.Option) (*kms.CreateKeyOutput, error)
|
|
||||||
CreateAliasWithContext(ctx aws.Context, input *kms.CreateAliasInput, opts ...request.Option) (*kms.CreateAliasOutput, error)
|
|
||||||
SignWithContext(ctx aws.Context, input *kms.SignInput, opts ...request.Option) (*kms.SignOutput, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// customerMasterKeySpecMapping is a mapping between the step signature algorithm,
|
|
||||||
// and bits for RSA keys, with awskms CustomerMasterKeySpec.
|
|
||||||
var customerMasterKeySpecMapping = map[apiv1.SignatureAlgorithm]interface{}{
|
|
||||||
apiv1.UnspecifiedSignAlgorithm: kms.CustomerMasterKeySpecEccNistP256,
|
|
||||||
apiv1.SHA256WithRSA: map[int]string{
|
|
||||||
0: kms.CustomerMasterKeySpecRsa3072,
|
|
||||||
2048: kms.CustomerMasterKeySpecRsa2048,
|
|
||||||
3072: kms.CustomerMasterKeySpecRsa3072,
|
|
||||||
4096: kms.CustomerMasterKeySpecRsa4096,
|
|
||||||
},
|
|
||||||
apiv1.SHA512WithRSA: map[int]string{
|
|
||||||
0: kms.CustomerMasterKeySpecRsa4096,
|
|
||||||
4096: kms.CustomerMasterKeySpecRsa4096,
|
|
||||||
},
|
|
||||||
apiv1.SHA256WithRSAPSS: map[int]string{
|
|
||||||
0: kms.CustomerMasterKeySpecRsa3072,
|
|
||||||
2048: kms.CustomerMasterKeySpecRsa2048,
|
|
||||||
3072: kms.CustomerMasterKeySpecRsa3072,
|
|
||||||
4096: kms.CustomerMasterKeySpecRsa4096,
|
|
||||||
},
|
|
||||||
apiv1.SHA512WithRSAPSS: map[int]string{
|
|
||||||
0: kms.CustomerMasterKeySpecRsa4096,
|
|
||||||
4096: kms.CustomerMasterKeySpecRsa4096,
|
|
||||||
},
|
|
||||||
apiv1.ECDSAWithSHA256: kms.CustomerMasterKeySpecEccNistP256,
|
|
||||||
apiv1.ECDSAWithSHA384: kms.CustomerMasterKeySpecEccNistP384,
|
|
||||||
apiv1.ECDSAWithSHA512: kms.CustomerMasterKeySpecEccNistP521,
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new AWSKMS. By default, sessions will be created using the
|
|
||||||
// credentials in `~/.aws/credentials`, but this can be overridden using the
|
|
||||||
// CredentialsFile option, the Region and Profile can also be configured as
|
|
||||||
// options.
|
|
||||||
//
|
|
||||||
// AWS sessions can also be configured with environment variables, see docs at
|
|
||||||
// https://docs.aws.amazon.com/sdk-for-go/api/aws/session/ for all the options.
|
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*KMS, error) {
|
|
||||||
var o session.Options
|
|
||||||
|
|
||||||
if opts.URI != "" {
|
|
||||||
u, err := uri.ParseWithScheme(Scheme, opts.URI)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
o.Profile = u.Get("profile")
|
|
||||||
if v := u.Get("region"); v != "" {
|
|
||||||
o.Config.Region = new(string)
|
|
||||||
*o.Config.Region = v
|
|
||||||
}
|
|
||||||
if f := u.Get("credentials-file"); f != "" {
|
|
||||||
o.SharedConfigFiles = []string{f}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated way to set configuration parameters.
|
|
||||||
if opts.Region != "" {
|
|
||||||
o.Config.Region = &opts.Region
|
|
||||||
}
|
|
||||||
if opts.Profile != "" {
|
|
||||||
o.Profile = opts.Profile
|
|
||||||
}
|
|
||||||
if opts.CredentialsFile != "" {
|
|
||||||
o.SharedConfigFiles = []string{opts.CredentialsFile}
|
|
||||||
}
|
|
||||||
|
|
||||||
sess, err := session.NewSessionWithOptions(o)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "error creating AWS session")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &KMS{
|
|
||||||
session: sess,
|
|
||||||
service: kms.New(sess),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
apiv1.Register(apiv1.AmazonKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) {
|
|
||||||
return New(ctx, opts)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPublicKey returns a public key from KMS.
|
|
||||||
func (k *KMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) {
|
|
||||||
if req.Name == "" {
|
|
||||||
return nil, errors.New("getPublicKey 'name' cannot be empty")
|
|
||||||
}
|
|
||||||
keyID, err := parseKeyID(req.Name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := k.service.GetPublicKeyWithContext(ctx, &kms.GetPublicKeyInput{
|
|
||||||
KeyId: &keyID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "awskms GetPublicKeyWithContext failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return pemutil.ParseDER(resp.PublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey generates a new key in KMS and returns the public key version
|
|
||||||
// of it.
|
|
||||||
func (k *KMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) {
|
|
||||||
if req.Name == "" {
|
|
||||||
return nil, errors.New("createKeyRequest 'name' cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
keySpec, err := getCustomerMasterKeySpecMapping(req.SignatureAlgorithm, req.Bits)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := new(kms.Tag)
|
|
||||||
tag.SetTagKey("name")
|
|
||||||
tag.SetTagValue(req.Name)
|
|
||||||
|
|
||||||
input := &kms.CreateKeyInput{
|
|
||||||
Description: &req.Name,
|
|
||||||
CustomerMasterKeySpec: &keySpec,
|
|
||||||
Tags: []*kms.Tag{tag},
|
|
||||||
}
|
|
||||||
input.SetKeyUsage(kms.KeyUsageTypeSignVerify)
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := k.service.CreateKeyWithContext(ctx, input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "awskms CreateKeyWithContext failed")
|
|
||||||
}
|
|
||||||
if err := k.createKeyAlias(*resp.KeyMetadata.KeyId, req.Name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create uri for key
|
|
||||||
name := uri.New("awskms", url.Values{
|
|
||||||
"key-id": []string{*resp.KeyMetadata.KeyId},
|
|
||||||
}).String()
|
|
||||||
|
|
||||||
publicKey, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names uses Amazon Resource Name
|
|
||||||
// https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
|
|
||||||
return &apiv1.CreateKeyResponse{
|
|
||||||
Name: name,
|
|
||||||
PublicKey: publicKey,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: name,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KMS) createKeyAlias(keyID, alias string) error {
|
|
||||||
alias = "alias/" + alias + "-" + keyID[:8]
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := k.service.CreateAliasWithContext(ctx, &kms.CreateAliasInput{
|
|
||||||
AliasName: &alias,
|
|
||||||
TargetKeyId: &keyID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "awskms CreateAliasWithContext failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSigner creates a new crypto.Signer with a previously configured key.
|
|
||||||
func (k *KMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) {
|
|
||||||
if req.SigningKey == "" {
|
|
||||||
return nil, errors.New("createSigner 'signingKey' cannot be empty")
|
|
||||||
}
|
|
||||||
return NewSigner(k.service, req.SigningKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the connection of the KMS client.
|
|
||||||
func (k *KMS) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultContext() (context.Context, context.CancelFunc) {
|
|
||||||
return context.WithTimeout(context.Background(), 15*time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseKeyID extracts the key-id from an uri.
|
|
||||||
func parseKeyID(name string) (string, error) {
|
|
||||||
name = strings.ToLower(name)
|
|
||||||
if strings.HasPrefix(name, "awskms:") || strings.HasPrefix(name, "aws:") {
|
|
||||||
u, err := uri.Parse(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if k := u.Get("key-id"); k != "" {
|
|
||||||
return k, nil
|
|
||||||
}
|
|
||||||
return "", errors.Errorf("failed to get key-id from %s", name)
|
|
||||||
}
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCustomerMasterKeySpecMapping(alg apiv1.SignatureAlgorithm, bits int) (string, error) {
|
|
||||||
v, ok := customerMasterKeySpecMapping[alg]
|
|
||||||
if !ok {
|
|
||||||
return "", errors.Errorf("awskms does not support signature algorithm '%s'", alg)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := v.(type) {
|
|
||||||
case string:
|
|
||||||
return v, nil
|
|
||||||
case map[int]string:
|
|
||||||
s, ok := v[bits]
|
|
||||||
if !ok {
|
|
||||||
return "", errors.Errorf("awskms does not support signature algorithm '%s' with '%d' bits", alg, bits)
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unexpected error: this should not happen")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,364 +0,0 @@
|
||||||
package awskms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/aws/aws-sdk-go/service/kms"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
sess, err := session.NewSessionWithOptions(session.Options{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
expected := &KMS{
|
|
||||||
session: sess,
|
|
||||||
service: kms.New(sess),
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will force an error in the session creation.
|
|
||||||
// It does not fail with missing credentials.
|
|
||||||
forceError := func(t *testing.T) {
|
|
||||||
key := "AWS_CA_BUNDLE"
|
|
||||||
value := os.Getenv(key)
|
|
||||||
os.Setenv(key, filepath.Join(os.TempDir(), "missing-ca.crt"))
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if value == "" {
|
|
||||||
os.Unsetenv(key)
|
|
||||||
} else {
|
|
||||||
os.Setenv(key, value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
opts apiv1.Options
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want *KMS
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{ctx, apiv1.Options{}}, expected, false},
|
|
||||||
{"ok with options", args{ctx, apiv1.Options{
|
|
||||||
Region: "us-east-1",
|
|
||||||
Profile: "smallstep",
|
|
||||||
CredentialsFile: "~/aws/credentials",
|
|
||||||
}}, expected, false},
|
|
||||||
{"ok with uri", args{ctx, apiv1.Options{
|
|
||||||
URI: "awskms:region=us-east-1;profile=smallstep;credentials-file=/var/run/aws/credentials",
|
|
||||||
}}, expected, false},
|
|
||||||
{"fail", args{ctx, apiv1.Options{}}, nil, true},
|
|
||||||
{"fail uri", args{ctx, apiv1.Options{
|
|
||||||
URI: "pkcs11:region=us-east-1;profile=smallstep;credentials-file=/var/run/aws/credentials",
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// Force an error in the session loading
|
|
||||||
if tt.wantErr {
|
|
||||||
forceError(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
got, err := New(tt.args.ctx, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("New() = %#v, want %#v", got, tt.want)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if got.session == nil || got.service == nil {
|
|
||||||
t.Errorf("New() = %#v, want %#v", got, tt.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKMS_GetPublicKey(t *testing.T) {
|
|
||||||
okClient := getOKClient()
|
|
||||||
key, err := pemutil.ParseKey([]byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
session *session.Session
|
|
||||||
service KeyManagementClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.GetPublicKeyRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want crypto.PublicKey
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{nil, okClient}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
}}, key, false},
|
|
||||||
{"fail empty", fields{nil, okClient}, args{&apiv1.GetPublicKeyRequest{}}, nil, true},
|
|
||||||
{"fail name", fields{nil, okClient}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "awskms:key-id=",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail getPublicKey", fields{nil, &MockClient{
|
|
||||||
getPublicKeyWithContext: func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
return nil, fmt.Errorf("an error")
|
|
||||||
},
|
|
||||||
}}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail not der", fields{nil, &MockClient{
|
|
||||||
getPublicKeyWithContext: func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
return &kms.GetPublicKeyOutput{
|
|
||||||
KeyId: input.KeyId,
|
|
||||||
PublicKey: []byte(publicKey),
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KMS{
|
|
||||||
session: tt.fields.session,
|
|
||||||
service: tt.fields.service,
|
|
||||||
}
|
|
||||||
got, err := k.GetPublicKey(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KMS.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("KMS.GetPublicKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKMS_CreateKey(t *testing.T) {
|
|
||||||
okClient := getOKClient()
|
|
||||||
key, err := pemutil.ParseKey([]byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
session *session.Session
|
|
||||||
service KeyManagementClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.CreateKeyRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want *apiv1.CreateKeyResponse
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{nil, okClient}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
PublicKey: key,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok rsa", fields{nil, okClient}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.SHA256WithRSA,
|
|
||||||
Bits: 2048,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
PublicKey: key,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"fail empty", fields{nil, okClient}, args{&apiv1.CreateKeyRequest{}}, nil, true},
|
|
||||||
{"fail unsupported alg", fields{nil, okClient}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.PureEd25519,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail unsupported bits", fields{nil, okClient}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.SHA256WithRSA,
|
|
||||||
Bits: 1234,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail createKey", fields{nil, &MockClient{
|
|
||||||
createKeyWithContext: func(ctx aws.Context, input *kms.CreateKeyInput, opts ...request.Option) (*kms.CreateKeyOutput, error) {
|
|
||||||
return nil, fmt.Errorf("an error")
|
|
||||||
},
|
|
||||||
createAliasWithContext: okClient.createAliasWithContext,
|
|
||||||
getPublicKeyWithContext: okClient.getPublicKeyWithContext,
|
|
||||||
}}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail createAlias", fields{nil, &MockClient{
|
|
||||||
createKeyWithContext: okClient.createKeyWithContext,
|
|
||||||
createAliasWithContext: func(ctx aws.Context, input *kms.CreateAliasInput, opts ...request.Option) (*kms.CreateAliasOutput, error) {
|
|
||||||
return nil, fmt.Errorf("an error")
|
|
||||||
},
|
|
||||||
getPublicKeyWithContext: okClient.getPublicKeyWithContext,
|
|
||||||
}}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail getPublicKey", fields{nil, &MockClient{
|
|
||||||
createKeyWithContext: okClient.createKeyWithContext,
|
|
||||||
createAliasWithContext: okClient.createAliasWithContext,
|
|
||||||
getPublicKeyWithContext: func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
return nil, fmt.Errorf("an error")
|
|
||||||
},
|
|
||||||
}}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "root",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KMS{
|
|
||||||
session: tt.fields.session,
|
|
||||||
service: tt.fields.service,
|
|
||||||
}
|
|
||||||
got, err := k.CreateKey(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KMS.CreateKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("KMS.CreateKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKMS_CreateSigner(t *testing.T) {
|
|
||||||
client := getOKClient()
|
|
||||||
key, err := pemutil.ParseKey([]byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
session *session.Session
|
|
||||||
service KeyManagementClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.CreateSignerRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want crypto.Signer
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{nil, client}, args{&apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
}}, &Signer{
|
|
||||||
service: client,
|
|
||||||
keyID: "be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
publicKey: key,
|
|
||||||
}, false},
|
|
||||||
{"fail empty", fields{nil, client}, args{&apiv1.CreateSignerRequest{}}, nil, true},
|
|
||||||
{"fail preload", fields{nil, client}, args{&apiv1.CreateSignerRequest{}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KMS{
|
|
||||||
session: tt.fields.session,
|
|
||||||
service: tt.fields.service,
|
|
||||||
}
|
|
||||||
got, err := k.CreateSigner(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KMS.CreateSigner() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("KMS.CreateSigner() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKMS_Close(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
session *session.Session
|
|
||||||
service KeyManagementClient
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{nil, getOKClient()}, false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KMS{
|
|
||||||
session: tt.fields.session,
|
|
||||||
service: tt.fields.service,
|
|
||||||
}
|
|
||||||
if err := k.Close(); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KMS.Close() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_parseKeyID(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok uri", args{"awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936"}, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", false},
|
|
||||||
{"ok key id", args{"be468355-ca7a-40d9-a28b-8ae1c4c7f936"}, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", false},
|
|
||||||
{"ok arn", args{"arn:aws:kms:us-east-1:123456789:key/be468355-ca7a-40d9-a28b-8ae1c4c7f936"}, "arn:aws:kms:us-east-1:123456789:key/be468355-ca7a-40d9-a28b-8ae1c4c7f936", false},
|
|
||||||
{"fail parse", args{"awskms:key-id=%ZZ"}, "", true},
|
|
||||||
{"fail empty key", args{"awskms:key-id="}, "", true},
|
|
||||||
{"fail missing", args{"awskms:foo=bar"}, "", true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := parseKeyID(tt.args.name)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("parseKeyID() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("parseKeyID() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package awskms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/pem"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/service/kms"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MockClient struct {
|
|
||||||
getPublicKeyWithContext func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error)
|
|
||||||
createKeyWithContext func(ctx aws.Context, input *kms.CreateKeyInput, opts ...request.Option) (*kms.CreateKeyOutput, error)
|
|
||||||
createAliasWithContext func(ctx aws.Context, input *kms.CreateAliasInput, opts ...request.Option) (*kms.CreateAliasOutput, error)
|
|
||||||
signWithContext func(ctx aws.Context, input *kms.SignInput, opts ...request.Option) (*kms.SignOutput, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) GetPublicKeyWithContext(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
return m.getPublicKeyWithContext(ctx, input, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) CreateKeyWithContext(ctx aws.Context, input *kms.CreateKeyInput, opts ...request.Option) (*kms.CreateKeyOutput, error) {
|
|
||||||
return m.createKeyWithContext(ctx, input, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) CreateAliasWithContext(ctx aws.Context, input *kms.CreateAliasInput, opts ...request.Option) (*kms.CreateAliasOutput, error) {
|
|
||||||
return m.createAliasWithContext(ctx, input, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) SignWithContext(ctx aws.Context, input *kms.SignInput, opts ...request.Option) (*kms.SignOutput, error) {
|
|
||||||
return m.signWithContext(ctx, input, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
publicKey = `-----BEGIN PUBLIC KEY-----
|
|
||||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8XWlIWkOThxNjGbZLYUgRHmsvCrW
|
|
||||||
KF+HLktPfPTIK3lGd1k4849WQs59XIN+LXZQ6b2eRBEBKAHEyQus8UU7gw==
|
|
||||||
-----END PUBLIC KEY-----`
|
|
||||||
keyID = "be468355-ca7a-40d9-a28b-8ae1c4c7f936"
|
|
||||||
)
|
|
||||||
|
|
||||||
var signature = []byte{
|
|
||||||
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
|
|
||||||
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOKClient() *MockClient {
|
|
||||||
return &MockClient{
|
|
||||||
getPublicKeyWithContext: func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
block, _ := pem.Decode([]byte(publicKey))
|
|
||||||
return &kms.GetPublicKeyOutput{
|
|
||||||
KeyId: input.KeyId,
|
|
||||||
PublicKey: block.Bytes,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
createKeyWithContext: func(ctx aws.Context, input *kms.CreateKeyInput, opts ...request.Option) (*kms.CreateKeyOutput, error) {
|
|
||||||
md := new(kms.KeyMetadata)
|
|
||||||
md.SetKeyId(keyID)
|
|
||||||
return &kms.CreateKeyOutput{
|
|
||||||
KeyMetadata: md,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
createAliasWithContext: func(ctx aws.Context, input *kms.CreateAliasInput, opts ...request.Option) (*kms.CreateAliasOutput, error) {
|
|
||||||
return &kms.CreateAliasOutput{}, nil
|
|
||||||
},
|
|
||||||
signWithContext: func(ctx aws.Context, input *kms.SignInput, opts ...request.Option) (*kms.SignOutput, error) {
|
|
||||||
return &kms.SignOutput{
|
|
||||||
Signature: signature,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
package awskms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/kms"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Signer implements a crypto.Signer using the AWS KMS.
|
|
||||||
type Signer struct {
|
|
||||||
service KeyManagementClient
|
|
||||||
keyID string
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSigner creates a new signer using a key in the AWS KMS.
|
|
||||||
func NewSigner(svc KeyManagementClient, signingKey string) (*Signer, error) {
|
|
||||||
keyID, err := parseKeyID(signingKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the key exists.
|
|
||||||
signer := &Signer{
|
|
||||||
service: svc,
|
|
||||||
keyID: keyID,
|
|
||||||
}
|
|
||||||
if err := signer.preloadKey(keyID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return signer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Signer) preloadKey(keyID string) error {
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := s.service.GetPublicKeyWithContext(ctx, &kms.GetPublicKeyInput{
|
|
||||||
KeyId: &keyID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "awskms GetPublicKeyWithContext failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.publicKey, err = pemutil.ParseDER(resp.PublicKey)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public returns the public key of this signer or an error.
|
|
||||||
func (s *Signer) Public() crypto.PublicKey {
|
|
||||||
return s.publicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs digest with the private key stored in the AWS KMS.
|
|
||||||
func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
|
||||||
alg, err := getSigningAlgorithm(s.Public(), opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &kms.SignInput{
|
|
||||||
KeyId: &s.keyID,
|
|
||||||
SigningAlgorithm: &alg,
|
|
||||||
Message: digest,
|
|
||||||
}
|
|
||||||
req.SetMessageType("DIGEST")
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := s.service.SignWithContext(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "awsKMS SignWithContext failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Signature, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (string, error) {
|
|
||||||
switch key.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
_, isPSS := opts.(*rsa.PSSOptions)
|
|
||||||
switch h := opts.HashFunc(); h {
|
|
||||||
case crypto.SHA256:
|
|
||||||
if isPSS {
|
|
||||||
return kms.SigningAlgorithmSpecRsassaPssSha256, nil
|
|
||||||
}
|
|
||||||
return kms.SigningAlgorithmSpecRsassaPkcs1V15Sha256, nil
|
|
||||||
case crypto.SHA384:
|
|
||||||
if isPSS {
|
|
||||||
return kms.SigningAlgorithmSpecRsassaPssSha384, nil
|
|
||||||
}
|
|
||||||
return kms.SigningAlgorithmSpecRsassaPkcs1V15Sha384, nil
|
|
||||||
case crypto.SHA512:
|
|
||||||
if isPSS {
|
|
||||||
return kms.SigningAlgorithmSpecRsassaPssSha512, nil
|
|
||||||
}
|
|
||||||
return kms.SigningAlgorithmSpecRsassaPkcs1V15Sha512, nil
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported hash function %v", h)
|
|
||||||
}
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
switch h := opts.HashFunc(); h {
|
|
||||||
case crypto.SHA256:
|
|
||||||
return kms.SigningAlgorithmSpecEcdsaSha256, nil
|
|
||||||
case crypto.SHA384:
|
|
||||||
return kms.SigningAlgorithmSpecEcdsaSha384, nil
|
|
||||||
case crypto.SHA512:
|
|
||||||
return kms.SigningAlgorithmSpecEcdsaSha512, nil
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported hash function %v", h)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported key type %T", key)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
package awskms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/service/kms"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewSigner(t *testing.T) {
|
|
||||||
okClient := getOKClient()
|
|
||||||
key, err := pemutil.ParseKey([]byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
svc KeyManagementClient
|
|
||||||
signingKey string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want *Signer
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{okClient, "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936"}, &Signer{
|
|
||||||
service: okClient,
|
|
||||||
keyID: "be468355-ca7a-40d9-a28b-8ae1c4c7f936",
|
|
||||||
publicKey: key,
|
|
||||||
}, false},
|
|
||||||
{"fail parse", args{okClient, "awskms:key-id="}, nil, true},
|
|
||||||
{"fail preload", args{&MockClient{
|
|
||||||
getPublicKeyWithContext: func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
return nil, fmt.Errorf("an error")
|
|
||||||
},
|
|
||||||
}, "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936"}, nil, true},
|
|
||||||
{"fail preload not der", args{&MockClient{
|
|
||||||
getPublicKeyWithContext: func(ctx aws.Context, input *kms.GetPublicKeyInput, opts ...request.Option) (*kms.GetPublicKeyOutput, error) {
|
|
||||||
return &kms.GetPublicKeyOutput{
|
|
||||||
KeyId: input.KeyId,
|
|
||||||
PublicKey: []byte(publicKey),
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}, "awskms:key-id=be468355-ca7a-40d9-a28b-8ae1c4c7f936"}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := NewSigner(tt.args.svc, tt.args.signingKey)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("NewSigner() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewSigner() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSigner_Public(t *testing.T) {
|
|
||||||
okClient := getOKClient()
|
|
||||||
key, err := pemutil.ParseKey([]byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
service KeyManagementClient
|
|
||||||
keyID string
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want crypto.PublicKey
|
|
||||||
}{
|
|
||||||
{"ok", fields{okClient, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", key}, key},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &Signer{
|
|
||||||
service: tt.fields.service,
|
|
||||||
keyID: tt.fields.keyID,
|
|
||||||
publicKey: tt.fields.publicKey,
|
|
||||||
}
|
|
||||||
if got := s.Public(); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Signer.Public() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSigner_Sign(t *testing.T) {
|
|
||||||
okClient := getOKClient()
|
|
||||||
key, err := pemutil.ParseKey([]byte(publicKey))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
service KeyManagementClient
|
|
||||||
keyID string
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
rand io.Reader
|
|
||||||
digest []byte
|
|
||||||
opts crypto.SignerOpts
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want []byte
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{okClient, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", key}, args{rand.Reader, []byte("digest"), crypto.SHA256}, signature, false},
|
|
||||||
{"fail alg", fields{okClient, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", key}, args{rand.Reader, []byte("digest"), crypto.MD5}, nil, true},
|
|
||||||
{"fail key", fields{okClient, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", []byte("key")}, args{rand.Reader, []byte("digest"), crypto.SHA256}, nil, true},
|
|
||||||
{"fail sign", fields{&MockClient{
|
|
||||||
signWithContext: func(ctx aws.Context, input *kms.SignInput, opts ...request.Option) (*kms.SignOutput, error) {
|
|
||||||
return nil, fmt.Errorf("an error")
|
|
||||||
},
|
|
||||||
}, "be468355-ca7a-40d9-a28b-8ae1c4c7f936", key}, args{rand.Reader, []byte("digest"), crypto.SHA256}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &Signer{
|
|
||||||
service: tt.fields.service,
|
|
||||||
keyID: tt.fields.keyID,
|
|
||||||
publicKey: tt.fields.publicKey,
|
|
||||||
}
|
|
||||||
got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Signer.Sign() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_getSigningAlgorithm(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
key crypto.PublicKey
|
|
||||||
opts crypto.SignerOpts
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"rsa+sha256", args{&rsa.PublicKey{}, crypto.SHA256}, "RSASSA_PKCS1_V1_5_SHA_256", false},
|
|
||||||
{"rsa+sha384", args{&rsa.PublicKey{}, crypto.SHA384}, "RSASSA_PKCS1_V1_5_SHA_384", false},
|
|
||||||
{"rsa+sha512", args{&rsa.PublicKey{}, crypto.SHA512}, "RSASSA_PKCS1_V1_5_SHA_512", false},
|
|
||||||
{"pssrsa+sha256", args{&rsa.PublicKey{}, &rsa.PSSOptions{Hash: crypto.SHA256.HashFunc()}}, "RSASSA_PSS_SHA_256", false},
|
|
||||||
{"pssrsa+sha384", args{&rsa.PublicKey{}, &rsa.PSSOptions{Hash: crypto.SHA384.HashFunc()}}, "RSASSA_PSS_SHA_384", false},
|
|
||||||
{"pssrsa+sha512", args{&rsa.PublicKey{}, &rsa.PSSOptions{Hash: crypto.SHA512.HashFunc()}}, "RSASSA_PSS_SHA_512", false},
|
|
||||||
{"P256", args{&ecdsa.PublicKey{}, crypto.SHA256}, "ECDSA_SHA_256", false},
|
|
||||||
{"P384", args{&ecdsa.PublicKey{}, crypto.SHA384}, "ECDSA_SHA_384", false},
|
|
||||||
{"P521", args{&ecdsa.PublicKey{}, crypto.SHA512}, "ECDSA_SHA_512", false},
|
|
||||||
{"fail type", args{[]byte("key"), crypto.SHA256}, "", true},
|
|
||||||
{"fail rsa alg", args{&rsa.PublicKey{}, crypto.MD5}, "", true},
|
|
||||||
{"fail ecdsa alg", args{&ecdsa.PublicKey{}, crypto.MD5}, "", true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := getSigningAlgorithm(tt.args.key, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("getSigningAlgorithm() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("getSigningAlgorithm() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: github.com/smallstep/certificates/kms/azurekms (interfaces: KeyVaultClient)
|
|
||||||
|
|
||||||
// Package mock is a generated GoMock package.
|
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "context"
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
keyvault "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KeyVaultClient is a mock of KeyVaultClient interface
|
|
||||||
type KeyVaultClient struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *KeyVaultClientMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyVaultClientMockRecorder is the mock recorder for KeyVaultClient
|
|
||||||
type KeyVaultClientMockRecorder struct {
|
|
||||||
mock *KeyVaultClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeyVaultClient creates a new mock instance
|
|
||||||
func NewKeyVaultClient(ctrl *gomock.Controller) *KeyVaultClient {
|
|
||||||
mock := &KeyVaultClient{ctrl: ctrl}
|
|
||||||
mock.recorder = &KeyVaultClientMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (m *KeyVaultClient) EXPECT() *KeyVaultClientMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey mocks base method
|
|
||||||
func (m *KeyVaultClient) CreateKey(arg0 context.Context, arg1, arg2 string, arg3 keyvault.KeyCreateParameters) (keyvault.KeyBundle, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "CreateKey", arg0, arg1, arg2, arg3)
|
|
||||||
ret0, _ := ret[0].(keyvault.KeyBundle)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey indicates an expected call of CreateKey
|
|
||||||
func (mr *KeyVaultClientMockRecorder) CreateKey(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateKey", reflect.TypeOf((*KeyVaultClient)(nil).CreateKey), arg0, arg1, arg2, arg3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKey mocks base method
|
|
||||||
func (m *KeyVaultClient) GetKey(arg0 context.Context, arg1, arg2, arg3 string) (keyvault.KeyBundle, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "GetKey", arg0, arg1, arg2, arg3)
|
|
||||||
ret0, _ := ret[0].(keyvault.KeyBundle)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKey indicates an expected call of GetKey
|
|
||||||
func (mr *KeyVaultClientMockRecorder) GetKey(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKey", reflect.TypeOf((*KeyVaultClient)(nil).GetKey), arg0, arg1, arg2, arg3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign mocks base method
|
|
||||||
func (m *KeyVaultClient) Sign(arg0 context.Context, arg1, arg2, arg3 string, arg4 keyvault.KeySignParameters) (keyvault.KeyOperationResult, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Sign", arg0, arg1, arg2, arg3, arg4)
|
|
||||||
ret0, _ := ret[0].(keyvault.KeyOperationResult)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign indicates an expected call of Sign
|
|
||||||
func (mr *KeyVaultClientMockRecorder) Sign(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*KeyVaultClient)(nil).Sign), arg0, arg1, arg2, arg3, arg4)
|
|
||||||
}
|
|
|
@ -1,342 +0,0 @@
|
||||||
package azurekms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
|
||||||
"github.com/Azure/go-autorest/autorest/date"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/uri"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
apiv1.Register(apiv1.AzureKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) {
|
|
||||||
return New(ctx, opts)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scheme is the scheme used for the Azure Key Vault uris.
|
|
||||||
const Scheme = "azurekms"
|
|
||||||
|
|
||||||
// keyIDRegexp is the regular expression that Key Vault uses on the kid. We can
|
|
||||||
// extract the vault, name and version of the key.
|
|
||||||
var keyIDRegexp = regexp.MustCompile(`^https://([0-9a-zA-Z-]+)\.vault\.azure\.net/keys/([0-9a-zA-Z-]+)/([0-9a-zA-Z-]+)$`)
|
|
||||||
|
|
||||||
var (
|
|
||||||
valueTrue = true
|
|
||||||
value2048 int32 = 2048
|
|
||||||
value3072 int32 = 3072
|
|
||||||
value4096 int32 = 4096
|
|
||||||
)
|
|
||||||
|
|
||||||
var now = func() time.Time {
|
|
||||||
return time.Now().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
type keyType struct {
|
|
||||||
Kty keyvault.JSONWebKeyType
|
|
||||||
Curve keyvault.JSONWebKeyCurveName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k keyType) KeyType(pl apiv1.ProtectionLevel) keyvault.JSONWebKeyType {
|
|
||||||
switch k.Kty {
|
|
||||||
case keyvault.EC:
|
|
||||||
if pl == apiv1.HSM {
|
|
||||||
return keyvault.ECHSM
|
|
||||||
}
|
|
||||||
return k.Kty
|
|
||||||
case keyvault.RSA:
|
|
||||||
if pl == apiv1.HSM {
|
|
||||||
return keyvault.RSAHSM
|
|
||||||
}
|
|
||||||
return k.Kty
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]keyType{
|
|
||||||
apiv1.UnspecifiedSignAlgorithm: {
|
|
||||||
Kty: keyvault.EC,
|
|
||||||
Curve: keyvault.P256,
|
|
||||||
},
|
|
||||||
apiv1.SHA256WithRSA: {
|
|
||||||
Kty: keyvault.RSA,
|
|
||||||
},
|
|
||||||
apiv1.SHA384WithRSA: {
|
|
||||||
Kty: keyvault.RSA,
|
|
||||||
},
|
|
||||||
apiv1.SHA512WithRSA: {
|
|
||||||
Kty: keyvault.RSA,
|
|
||||||
},
|
|
||||||
apiv1.SHA256WithRSAPSS: {
|
|
||||||
Kty: keyvault.RSA,
|
|
||||||
},
|
|
||||||
apiv1.SHA384WithRSAPSS: {
|
|
||||||
Kty: keyvault.RSA,
|
|
||||||
},
|
|
||||||
apiv1.SHA512WithRSAPSS: {
|
|
||||||
Kty: keyvault.RSA,
|
|
||||||
},
|
|
||||||
apiv1.ECDSAWithSHA256: {
|
|
||||||
Kty: keyvault.EC,
|
|
||||||
Curve: keyvault.P256,
|
|
||||||
},
|
|
||||||
apiv1.ECDSAWithSHA384: {
|
|
||||||
Kty: keyvault.EC,
|
|
||||||
Curve: keyvault.P384,
|
|
||||||
},
|
|
||||||
apiv1.ECDSAWithSHA512: {
|
|
||||||
Kty: keyvault.EC,
|
|
||||||
Curve: keyvault.P521,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// vaultResource is the value the client will use as audience.
|
|
||||||
const vaultResource = "https://vault.azure.net"
|
|
||||||
|
|
||||||
// KeyVaultClient is the interface implemented by keyvault.BaseClient. It will
|
|
||||||
// be used for testing purposes.
|
|
||||||
type KeyVaultClient interface {
|
|
||||||
GetKey(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string) (keyvault.KeyBundle, error)
|
|
||||||
CreateKey(ctx context.Context, vaultBaseURL string, keyName string, parameters keyvault.KeyCreateParameters) (keyvault.KeyBundle, error)
|
|
||||||
Sign(ctx context.Context, vaultBaseURL string, keyName string, keyVersion string, parameters keyvault.KeySignParameters) (keyvault.KeyOperationResult, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyVault implements a KMS using Azure Key Vault.
|
|
||||||
//
|
|
||||||
// The URI format used in Azure Key Vault is the following:
|
|
||||||
//
|
|
||||||
// - azurekms:name=key-name;vault=vault-name
|
|
||||||
// - azurekms:name=key-name;vault=vault-name?version=key-version
|
|
||||||
// - azurekms:name=key-name;vault=vault-name?hsm=true
|
|
||||||
//
|
|
||||||
// The scheme is "azurekms"; "name" is the key name; "vault" is the key vault
|
|
||||||
// name where the key is located; "version" is an optional parameter that
|
|
||||||
// defines the version of they key, if version is not given, the latest one will
|
|
||||||
// be used; "hsm" defines if an HSM want to be used for this key, this is
|
|
||||||
// specially useful when this is used from `step`.
|
|
||||||
//
|
|
||||||
// TODO(mariano): The implementation is using /services/keyvault/v7.1/keyvault
|
|
||||||
// package, at some point Azure might create a keyvault client with all the
|
|
||||||
// functionality in /sdk/keyvault, we should migrate to that once available.
|
|
||||||
type KeyVault struct {
|
|
||||||
baseClient KeyVaultClient
|
|
||||||
defaults DefaultOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultOptions are custom options that can be passed as defaults using the
|
|
||||||
// URI in apiv1.Options.
|
|
||||||
type DefaultOptions struct {
|
|
||||||
Vault string
|
|
||||||
ProtectionLevel apiv1.ProtectionLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
var createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
|
||||||
baseClient := keyvault.New()
|
|
||||||
|
|
||||||
// With an URI, try to log in only using client credentials in the URI.
|
|
||||||
// Client credentials requires:
|
|
||||||
// - client-id
|
|
||||||
// - client-secret
|
|
||||||
// - tenant-id
|
|
||||||
// And optionally the aad-endpoint to support custom clouds:
|
|
||||||
// - aad-endpoint (defaults to https://login.microsoftonline.com/)
|
|
||||||
if opts.URI != "" {
|
|
||||||
u, err := uri.ParseWithScheme(Scheme, opts.URI)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required options
|
|
||||||
clientID := u.Get("client-id")
|
|
||||||
clientSecret := u.Get("client-secret")
|
|
||||||
tenantID := u.Get("tenant-id")
|
|
||||||
// optional
|
|
||||||
aadEndpoint := u.Get("aad-endpoint")
|
|
||||||
|
|
||||||
if clientID != "" && clientSecret != "" && tenantID != "" {
|
|
||||||
s := auth.EnvironmentSettings{
|
|
||||||
Values: map[string]string{
|
|
||||||
auth.ClientID: clientID,
|
|
||||||
auth.ClientSecret: clientSecret,
|
|
||||||
auth.TenantID: tenantID,
|
|
||||||
auth.Resource: vaultResource,
|
|
||||||
},
|
|
||||||
Environment: azure.PublicCloud,
|
|
||||||
}
|
|
||||||
if aadEndpoint != "" {
|
|
||||||
s.Environment.ActiveDirectoryEndpoint = aadEndpoint
|
|
||||||
}
|
|
||||||
baseClient.Authorizer, err = s.GetAuthorizer()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return baseClient, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to authorize with the following methods:
|
|
||||||
// 1. Environment variables.
|
|
||||||
// - Client credentials
|
|
||||||
// - Client certificate
|
|
||||||
// - Username and password
|
|
||||||
// - MSI
|
|
||||||
// 2. Using Azure CLI 2.0 on local development.
|
|
||||||
authorizer, err := auth.NewAuthorizerFromEnvironmentWithResource(vaultResource)
|
|
||||||
if err != nil {
|
|
||||||
authorizer, err = auth.NewAuthorizerFromCLIWithResource(vaultResource)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "error getting authorizer for key vault")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
baseClient.Authorizer = authorizer
|
|
||||||
return &baseClient, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New initializes a new KMS implemented using Azure Key Vault.
|
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*KeyVault, error) {
|
|
||||||
baseClient, err := createClient(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// step and step-ca do not need and URI, but having a default vault and
|
|
||||||
// protection level is useful if this package is used as an api
|
|
||||||
var defaults DefaultOptions
|
|
||||||
if opts.URI != "" {
|
|
||||||
u, err := uri.ParseWithScheme(Scheme, opts.URI)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defaults.Vault = u.Get("vault")
|
|
||||||
if u.GetBool("hsm") {
|
|
||||||
defaults.ProtectionLevel = apiv1.HSM
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &KeyVault{
|
|
||||||
baseClient: baseClient,
|
|
||||||
defaults: defaults,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPublicKey loads a public key from Azure Key Vault by its resource name.
|
|
||||||
func (k *KeyVault) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) {
|
|
||||||
if req.Name == "" {
|
|
||||||
return nil, errors.New("getPublicKeyRequest 'name' cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
vault, name, version, _, err := parseKeyName(req.Name, k.defaults)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := k.baseClient.GetKey(ctx, vaultBaseURL(vault), name, version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "keyVault GetKey failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertKey(resp.Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey creates a asymmetric key in Azure Key Vault.
|
|
||||||
func (k *KeyVault) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) {
|
|
||||||
if req.Name == "" {
|
|
||||||
return nil, errors.New("createKeyRequest 'name' cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
vault, name, _, hsm, err := parseKeyName(req.Name, k.defaults)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override protection level to HSM only if it's not specified, and is given
|
|
||||||
// in the uri.
|
|
||||||
protectionLevel := req.ProtectionLevel
|
|
||||||
if protectionLevel == apiv1.UnspecifiedProtectionLevel && hsm {
|
|
||||||
protectionLevel = apiv1.HSM
|
|
||||||
}
|
|
||||||
|
|
||||||
kt, ok := signatureAlgorithmMapping[req.SignatureAlgorithm]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.Errorf("keyVault does not support signature algorithm '%s'", req.SignatureAlgorithm)
|
|
||||||
}
|
|
||||||
var keySize *int32
|
|
||||||
if kt.Kty == keyvault.RSA || kt.Kty == keyvault.RSAHSM {
|
|
||||||
switch req.Bits {
|
|
||||||
case 2048:
|
|
||||||
keySize = &value2048
|
|
||||||
case 0, 3072:
|
|
||||||
keySize = &value3072
|
|
||||||
case 4096:
|
|
||||||
keySize = &value4096
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("keyVault does not support key size %d", req.Bits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
created := date.UnixTime(now())
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := k.baseClient.CreateKey(ctx, vaultBaseURL(vault), name, keyvault.KeyCreateParameters{
|
|
||||||
Kty: kt.KeyType(protectionLevel),
|
|
||||||
KeySize: keySize,
|
|
||||||
Curve: kt.Curve,
|
|
||||||
KeyOps: &[]keyvault.JSONWebKeyOperation{
|
|
||||||
keyvault.Sign, keyvault.Verify,
|
|
||||||
},
|
|
||||||
KeyAttributes: &keyvault.KeyAttributes{
|
|
||||||
Enabled: &valueTrue,
|
|
||||||
Created: &created,
|
|
||||||
NotBefore: &created,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "keyVault CreateKey failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
publicKey, err := convertKey(resp.Key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
keyURI := getKeyName(vault, name, resp)
|
|
||||||
return &apiv1.CreateKeyResponse{
|
|
||||||
Name: keyURI,
|
|
||||||
PublicKey: publicKey,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: keyURI,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSigner returns a crypto.Signer from a previously created asymmetric key.
|
|
||||||
func (k *KeyVault) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) {
|
|
||||||
if req.SigningKey == "" {
|
|
||||||
return nil, errors.New("createSignerRequest 'signingKey' cannot be empty")
|
|
||||||
}
|
|
||||||
return NewSigner(k.baseClient, req.SigningKey, k.defaults)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the client connection to the Azure Key Vault. This is a noop.
|
|
||||||
func (k *KeyVault) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateName validates that the given string is a valid URI.
|
|
||||||
func (k *KeyVault) ValidateName(s string) error {
|
|
||||||
_, _, _, _, err := parseKeyName(s, k.defaults)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,653 +0,0 @@
|
||||||
//go:generate mockgen -package mock -mock_names=KeyVaultClient=KeyVaultClient -destination internal/mock/key_vault_client.go github.com/smallstep/certificates/kms/azurekms KeyVaultClient
|
|
||||||
package azurekms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
"github.com/Azure/go-autorest/autorest/date"
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/azurekms/internal/mock"
|
|
||||||
"go.step.sm/crypto/keyutil"
|
|
||||||
"gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errTest = fmt.Errorf("test error")
|
|
||||||
|
|
||||||
func mockNow(t *testing.T) time.Time {
|
|
||||||
old := now
|
|
||||||
t0 := time.Unix(1234567890, 123).UTC()
|
|
||||||
now = func() time.Time {
|
|
||||||
return t0
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
now = old
|
|
||||||
})
|
|
||||||
return t0
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockClient(t *testing.T) *mock.KeyVaultClient {
|
|
||||||
t.Helper()
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
ctrl.Finish()
|
|
||||||
})
|
|
||||||
return mock.NewKeyVaultClient(ctrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createJWK(t *testing.T, pub crypto.PublicKey) *keyvault.JSONWebKey {
|
|
||||||
t.Helper()
|
|
||||||
b, err := json.Marshal(&jose.JSONWebKey{
|
|
||||||
Key: pub,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
key := new(keyvault.JSONWebKey)
|
|
||||||
if err := json.Unmarshal(b, key); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_now(t *testing.T) {
|
|
||||||
t0 := now()
|
|
||||||
if loc := t0.Location(); loc != time.UTC {
|
|
||||||
t.Errorf("now() Location = %v, want %v", loc, time.UTC)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
client := mockClient(t)
|
|
||||||
old := createClient
|
|
||||||
t.Cleanup(func() {
|
|
||||||
createClient = old
|
|
||||||
})
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
opts apiv1.Options
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
setup func()
|
|
||||||
args args
|
|
||||||
want *KeyVault
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", func() {
|
|
||||||
createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
}, args{context.Background(), apiv1.Options{}}, &KeyVault{
|
|
||||||
baseClient: client,
|
|
||||||
}, false},
|
|
||||||
{"ok with vault", func() {
|
|
||||||
createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
}, args{context.Background(), apiv1.Options{
|
|
||||||
URI: "azurekms:vault=my-vault",
|
|
||||||
}}, &KeyVault{
|
|
||||||
baseClient: client,
|
|
||||||
defaults: DefaultOptions{
|
|
||||||
Vault: "my-vault",
|
|
||||||
ProtectionLevel: apiv1.UnspecifiedProtectionLevel,
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok with vault + hsm", func() {
|
|
||||||
createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
}, args{context.Background(), apiv1.Options{
|
|
||||||
URI: "azurekms:vault=my-vault;hsm=true",
|
|
||||||
}}, &KeyVault{
|
|
||||||
baseClient: client,
|
|
||||||
defaults: DefaultOptions{
|
|
||||||
Vault: "my-vault",
|
|
||||||
ProtectionLevel: apiv1.HSM,
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"fail", func() {
|
|
||||||
createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
|
||||||
return nil, errTest
|
|
||||||
}
|
|
||||||
}, args{context.Background(), apiv1.Options{}}, nil, true},
|
|
||||||
{"fail uri", func() {
|
|
||||||
createClient = func(ctx context.Context, opts apiv1.Options) (KeyVaultClient, error) {
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
}, args{context.Background(), apiv1.Options{
|
|
||||||
URI: "kms:vault=my-vault;hsm=true",
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
tt.setup()
|
|
||||||
got, err := New(tt.args.ctx, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("New() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyVault_createClient(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
opts apiv1.Options
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
skip bool
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{context.Background(), apiv1.Options{}}, true, false},
|
|
||||||
{"ok with uri", args{context.Background(), apiv1.Options{
|
|
||||||
URI: "azurekms:client-id=id;client-secret=secret;tenant-id=id",
|
|
||||||
}}, false, false},
|
|
||||||
{"ok with uri+aad", args{context.Background(), apiv1.Options{
|
|
||||||
URI: "azurekms:client-id=id;client-secret=secret;tenant-id=id;aad-enpoint=https%3A%2F%2Flogin.microsoftonline.us%2F",
|
|
||||||
}}, false, false},
|
|
||||||
{"ok with uri no config", args{context.Background(), apiv1.Options{
|
|
||||||
URI: "azurekms:",
|
|
||||||
}}, true, false},
|
|
||||||
{"fail uri", args{context.Background(), apiv1.Options{
|
|
||||||
URI: "kms:client-id=id;client-secret=secret;tenant-id=id",
|
|
||||||
}}, false, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if tt.skip {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
_, err := createClient(tt.args.ctx, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyVault_GetPublicKey(t *testing.T) {
|
|
||||||
key, err := keyutil.GenerateDefaultSigner()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pub := key.Public()
|
|
||||||
jwk := createJWK(t, pub)
|
|
||||||
|
|
||||||
client := mockClient(t)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest)
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
baseClient KeyVaultClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.GetPublicKeyRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want crypto.PublicKey
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
}}, pub, false},
|
|
||||||
{"ok with version", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key?version=my-version",
|
|
||||||
}}, pub, false},
|
|
||||||
{"fail GetKey", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=not-found?version=my-version",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail empty", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail vault", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "azurekms:vault=;name=not-found?version=my-version",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail id", fields{client}, args{&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: "azurekms:vault=;name=?version=my-version",
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KeyVault{
|
|
||||||
baseClient: tt.fields.baseClient,
|
|
||||||
}
|
|
||||||
got, err := k.GetPublicKey(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KeyVault.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("KeyVault.GetPublicKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyVault_CreateKey(t *testing.T) {
|
|
||||||
ecKey, err := keyutil.GenerateDefaultSigner()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rsaKey, err := keyutil.GenerateSigner("RSA", "", 2048)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
ecPub := ecKey.Public()
|
|
||||||
rsaPub := rsaKey.Public()
|
|
||||||
ecJWK := createJWK(t, ecPub)
|
|
||||||
rsaJWK := createJWK(t, rsaPub)
|
|
||||||
|
|
||||||
t0 := date.UnixTime(mockNow(t))
|
|
||||||
client := mockClient(t)
|
|
||||||
|
|
||||||
expects := []struct {
|
|
||||||
Name string
|
|
||||||
Kty keyvault.JSONWebKeyType
|
|
||||||
KeySize *int32
|
|
||||||
Curve keyvault.JSONWebKeyCurveName
|
|
||||||
Key *keyvault.JSONWebKey
|
|
||||||
}{
|
|
||||||
{"P-256", keyvault.EC, nil, keyvault.P256, ecJWK},
|
|
||||||
{"P-256 HSM", keyvault.ECHSM, nil, keyvault.P256, ecJWK},
|
|
||||||
{"P-256 HSM (uri)", keyvault.ECHSM, nil, keyvault.P256, ecJWK},
|
|
||||||
{"P-256 Default", keyvault.EC, nil, keyvault.P256, ecJWK},
|
|
||||||
{"P-384", keyvault.EC, nil, keyvault.P384, ecJWK},
|
|
||||||
{"P-521", keyvault.EC, nil, keyvault.P521, ecJWK},
|
|
||||||
{"RSA 0", keyvault.RSA, &value3072, "", rsaJWK},
|
|
||||||
{"RSA 0 HSM", keyvault.RSAHSM, &value3072, "", rsaJWK},
|
|
||||||
{"RSA 0 HSM (uri)", keyvault.RSAHSM, &value3072, "", rsaJWK},
|
|
||||||
{"RSA 2048", keyvault.RSA, &value2048, "", rsaJWK},
|
|
||||||
{"RSA 3072", keyvault.RSA, &value3072, "", rsaJWK},
|
|
||||||
{"RSA 4096", keyvault.RSA, &value4096, "", rsaJWK},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range expects {
|
|
||||||
client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", keyvault.KeyCreateParameters{
|
|
||||||
Kty: e.Kty,
|
|
||||||
KeySize: e.KeySize,
|
|
||||||
Curve: e.Curve,
|
|
||||||
KeyOps: &[]keyvault.JSONWebKeyOperation{
|
|
||||||
keyvault.Sign, keyvault.Verify,
|
|
||||||
},
|
|
||||||
KeyAttributes: &keyvault.KeyAttributes{
|
|
||||||
Enabled: &valueTrue,
|
|
||||||
Created: &t0,
|
|
||||||
NotBefore: &t0,
|
|
||||||
},
|
|
||||||
}).Return(keyvault.KeyBundle{
|
|
||||||
Key: e.Key,
|
|
||||||
}, nil)
|
|
||||||
}
|
|
||||||
client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", gomock.Any()).Return(keyvault.KeyBundle{}, errTest)
|
|
||||||
client.EXPECT().CreateKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", gomock.Any()).Return(keyvault.KeyBundle{
|
|
||||||
Key: nil,
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
baseClient KeyVaultClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.CreateKeyRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want *apiv1.CreateKeyResponse
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok P-256", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
ProtectionLevel: apiv1.Software,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: ecPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok P-256 HSM", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
ProtectionLevel: apiv1.HSM,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: ecPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok P-256 HSM (uri)", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key?hsm=true",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: ecPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok P-256 Default", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: ecPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok P-384", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA384,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: ecPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok P-521", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA512,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: ecPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok RSA 0", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
Bits: 0,
|
|
||||||
SignatureAlgorithm: apiv1.SHA256WithRSA,
|
|
||||||
ProtectionLevel: apiv1.Software,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: rsaPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok RSA 0 HSM", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
Bits: 0,
|
|
||||||
SignatureAlgorithm: apiv1.SHA256WithRSAPSS,
|
|
||||||
ProtectionLevel: apiv1.HSM,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: rsaPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok RSA 0 HSM (uri)", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key;hsm=true",
|
|
||||||
Bits: 0,
|
|
||||||
SignatureAlgorithm: apiv1.SHA256WithRSAPSS,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: rsaPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok RSA 2048", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
Bits: 2048,
|
|
||||||
SignatureAlgorithm: apiv1.SHA384WithRSA,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: rsaPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok RSA 3072", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
Bits: 3072,
|
|
||||||
SignatureAlgorithm: apiv1.SHA512WithRSA,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: rsaPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"ok RSA 4096", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
Bits: 4096,
|
|
||||||
SignatureAlgorithm: apiv1.SHA512WithRSAPSS,
|
|
||||||
}}, &apiv1.CreateKeyResponse{
|
|
||||||
Name: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
PublicKey: rsaPub,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:name=my-key;vault=my-vault",
|
|
||||||
},
|
|
||||||
}, false},
|
|
||||||
{"fail createKey", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=not-found",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail convertKey", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=not-found",
|
|
||||||
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail name", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail vault", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=;name=not-found?version=my-version",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail id", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=?version=my-version",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail SignatureAlgorithm", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=not-found",
|
|
||||||
SignatureAlgorithm: apiv1.PureEd25519,
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail bit size", fields{client}, args{&apiv1.CreateKeyRequest{
|
|
||||||
Name: "azurekms:vault=my-vault;name=not-found",
|
|
||||||
SignatureAlgorithm: apiv1.SHA384WithRSAPSS,
|
|
||||||
Bits: 1024,
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KeyVault{
|
|
||||||
baseClient: tt.fields.baseClient,
|
|
||||||
}
|
|
||||||
got, err := k.CreateKey(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KeyVault.CreateKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("KeyVault.CreateKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyVault_CreateSigner(t *testing.T) {
|
|
||||||
key, err := keyutil.GenerateDefaultSigner()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pub := key.Public()
|
|
||||||
jwk := createJWK(t, pub)
|
|
||||||
|
|
||||||
client := mockClient(t)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest)
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
baseClient KeyVaultClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.CreateSignerRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want crypto.Signer
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{client}, args{&apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:vault=my-vault;name=my-key",
|
|
||||||
}}, &Signer{
|
|
||||||
client: client,
|
|
||||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
|
||||||
name: "my-key",
|
|
||||||
version: "",
|
|
||||||
publicKey: pub,
|
|
||||||
}, false},
|
|
||||||
{"ok with version", fields{client}, args{&apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:vault=my-vault;name=my-key;version=my-version",
|
|
||||||
}}, &Signer{
|
|
||||||
client: client,
|
|
||||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
|
||||||
name: "my-key",
|
|
||||||
version: "my-version",
|
|
||||||
publicKey: pub,
|
|
||||||
}, false},
|
|
||||||
{"fail GetKey", fields{client}, args{&apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "azurekms:vault=my-vault;name=not-found;version=my-version",
|
|
||||||
}}, nil, true},
|
|
||||||
{"fail SigningKey", fields{client}, args{&apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: "",
|
|
||||||
}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KeyVault{
|
|
||||||
baseClient: tt.fields.baseClient,
|
|
||||||
}
|
|
||||||
got, err := k.CreateSigner(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KeyVault.CreateSigner() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("KeyVault.CreateSigner() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyVault_Close(t *testing.T) {
|
|
||||||
client := mockClient(t)
|
|
||||||
type fields struct {
|
|
||||||
baseClient KeyVaultClient
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{client}, false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KeyVault{
|
|
||||||
baseClient: tt.fields.baseClient,
|
|
||||||
}
|
|
||||||
if err := k.Close(); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KeyVault.Close() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_keyType_KeyType(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Kty keyvault.JSONWebKeyType
|
|
||||||
Curve keyvault.JSONWebKeyCurveName
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
pl apiv1.ProtectionLevel
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want keyvault.JSONWebKeyType
|
|
||||||
}{
|
|
||||||
{"ec", fields{keyvault.EC, keyvault.P256}, args{apiv1.UnspecifiedProtectionLevel}, keyvault.EC},
|
|
||||||
{"ec software", fields{keyvault.EC, keyvault.P384}, args{apiv1.Software}, keyvault.EC},
|
|
||||||
{"ec hsm", fields{keyvault.EC, keyvault.P521}, args{apiv1.HSM}, keyvault.ECHSM},
|
|
||||||
{"rsa", fields{keyvault.RSA, keyvault.P256}, args{apiv1.UnspecifiedProtectionLevel}, keyvault.RSA},
|
|
||||||
{"rsa software", fields{keyvault.RSA, ""}, args{apiv1.Software}, keyvault.RSA},
|
|
||||||
{"rsa hsm", fields{keyvault.RSA, ""}, args{apiv1.HSM}, keyvault.RSAHSM},
|
|
||||||
{"empty", fields{"FOO", ""}, args{apiv1.UnspecifiedProtectionLevel}, ""},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := keyType{
|
|
||||||
Kty: tt.fields.Kty,
|
|
||||||
Curve: tt.fields.Curve,
|
|
||||||
}
|
|
||||||
if got := k.KeyType(tt.args.pl); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("keyType.KeyType() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeyVault_ValidateName(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
s string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{"azurekms:name=my-key;vault=my-vault"}, false},
|
|
||||||
{"ok hsm", args{"azurekms:name=my-key;vault=my-vault?hsm=true"}, false},
|
|
||||||
{"fail scheme", args{"azure:name=my-key;vault=my-vault"}, true},
|
|
||||||
{"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault"}, true},
|
|
||||||
{"fail no name", args{"azurekms:vault=my-vault"}, true},
|
|
||||||
{"fail no vault", args{"azurekms:name=my-key"}, true},
|
|
||||||
{"fail empty", args{""}, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &KeyVault{}
|
|
||||||
if err := k.ValidateName(tt.args.s); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("KeyVault.ValidateName() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,182 +0,0 @@
|
||||||
package azurekms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/base64"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
"golang.org/x/crypto/cryptobyte/asn1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Signer implements a crypto.Signer using the AWS KMS.
|
|
||||||
type Signer struct {
|
|
||||||
client KeyVaultClient
|
|
||||||
vaultBaseURL string
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSigner creates a new signer using a key in the AWS KMS.
|
|
||||||
func NewSigner(client KeyVaultClient, signingKey string, defaults DefaultOptions) (crypto.Signer, error) {
|
|
||||||
vault, name, version, _, err := parseKeyName(signingKey, defaults)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the key exists.
|
|
||||||
signer := &Signer{
|
|
||||||
client: client,
|
|
||||||
vaultBaseURL: vaultBaseURL(vault),
|
|
||||||
name: name,
|
|
||||||
version: version,
|
|
||||||
}
|
|
||||||
if err := signer.preloadKey(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return signer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Signer) preloadKey() error {
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := s.client.GetKey(ctx, s.vaultBaseURL, s.name, s.version)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "keyVault GetKey failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.publicKey, err = convertKey(resp.Key)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public returns the public key of this signer or an error.
|
|
||||||
func (s *Signer) Public() crypto.PublicKey {
|
|
||||||
return s.publicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign signs digest with the private key stored in the AWS KMS.
|
|
||||||
func (s *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
|
||||||
alg, err := getSigningAlgorithm(s.Public(), opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b64 := base64.RawURLEncoding.EncodeToString(digest)
|
|
||||||
|
|
||||||
// Sign with retry if the key is not ready
|
|
||||||
resp, err := s.signWithRetry(alg, b64, 3)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "keyVault Sign failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := base64.RawURLEncoding.DecodeString(*resp.Result)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "error decoding keyVault Sign result")
|
|
||||||
}
|
|
||||||
|
|
||||||
var octetSize int
|
|
||||||
switch alg {
|
|
||||||
case keyvault.ES256:
|
|
||||||
octetSize = 32 // 256-bit, concat(R,S) = 64 bytes
|
|
||||||
case keyvault.ES384:
|
|
||||||
octetSize = 48 // 384-bit, concat(R,S) = 96 bytes
|
|
||||||
case keyvault.ES512:
|
|
||||||
octetSize = 66 // 528-bit, concat(R,S) = 132 bytes
|
|
||||||
default:
|
|
||||||
return sig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to asn1
|
|
||||||
if len(sig) != octetSize*2 {
|
|
||||||
return nil, errors.Errorf("keyVault Sign failed: unexpected signature length")
|
|
||||||
}
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
|
||||||
b.AddASN1BigInt(new(big.Int).SetBytes(sig[:octetSize])) // R
|
|
||||||
b.AddASN1BigInt(new(big.Int).SetBytes(sig[octetSize:])) // S
|
|
||||||
})
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Signer) signWithRetry(alg keyvault.JSONWebKeySignatureAlgorithm, b64 string, retryAttempts int) (keyvault.KeyOperationResult, error) {
|
|
||||||
retry:
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resp, err := s.client.Sign(ctx, s.vaultBaseURL, s.name, s.version, keyvault.KeySignParameters{
|
|
||||||
Algorithm: alg,
|
|
||||||
Value: &b64,
|
|
||||||
})
|
|
||||||
if err != nil && retryAttempts > 0 {
|
|
||||||
var requestError *azure.RequestError
|
|
||||||
if errors.As(err, &requestError) {
|
|
||||||
if se := requestError.ServiceError; se != nil && se.InnerError != nil {
|
|
||||||
code, ok := se.InnerError["code"].(string)
|
|
||||||
if ok && code == "KeyNotYetValid" {
|
|
||||||
time.Sleep(time.Second / time.Duration(retryAttempts))
|
|
||||||
retryAttempts--
|
|
||||||
goto retry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSigningAlgorithm(key crypto.PublicKey, opts crypto.SignerOpts) (keyvault.JSONWebKeySignatureAlgorithm, error) {
|
|
||||||
switch key.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
hashFunc := opts.HashFunc()
|
|
||||||
pss, isPSS := opts.(*rsa.PSSOptions)
|
|
||||||
// Random salt lengths are not supported
|
|
||||||
if isPSS &&
|
|
||||||
pss.SaltLength != rsa.PSSSaltLengthAuto &&
|
|
||||||
pss.SaltLength != rsa.PSSSaltLengthEqualsHash &&
|
|
||||||
pss.SaltLength != hashFunc.Size() {
|
|
||||||
return "", errors.Errorf("unsupported RSA-PSS salt length %d", pss.SaltLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch h := hashFunc; h {
|
|
||||||
case crypto.SHA256:
|
|
||||||
if isPSS {
|
|
||||||
return keyvault.PS256, nil
|
|
||||||
}
|
|
||||||
return keyvault.RS256, nil
|
|
||||||
case crypto.SHA384:
|
|
||||||
if isPSS {
|
|
||||||
return keyvault.PS384, nil
|
|
||||||
}
|
|
||||||
return keyvault.RS384, nil
|
|
||||||
case crypto.SHA512:
|
|
||||||
if isPSS {
|
|
||||||
return keyvault.PS512, nil
|
|
||||||
}
|
|
||||||
return keyvault.RS512, nil
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported hash function %v", h)
|
|
||||||
}
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
switch h := opts.HashFunc(); h {
|
|
||||||
case crypto.SHA256:
|
|
||||||
return keyvault.ES256, nil
|
|
||||||
case crypto.SHA384:
|
|
||||||
return keyvault.ES384, nil
|
|
||||||
case crypto.SHA512:
|
|
||||||
return keyvault.ES512, nil
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported hash function %v", h)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported key type %T", key)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,493 +0,0 @@
|
||||||
package azurekms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/base64"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
"github.com/Azure/go-autorest/autorest"
|
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/keyutil"
|
|
||||||
"golang.org/x/crypto/cryptobyte"
|
|
||||||
"golang.org/x/crypto/cryptobyte/asn1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewSigner(t *testing.T) {
|
|
||||||
key, err := keyutil.GenerateDefaultSigner()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pub := key.Public()
|
|
||||||
jwk := createJWK(t, pub)
|
|
||||||
|
|
||||||
client := mockClient(t)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", "my-version").Return(keyvault.KeyBundle{
|
|
||||||
Key: jwk,
|
|
||||||
}, nil)
|
|
||||||
client.EXPECT().GetKey(gomock.Any(), "https://my-vault.vault.azure.net/", "not-found", "my-version").Return(keyvault.KeyBundle{}, errTest)
|
|
||||||
|
|
||||||
var noOptions DefaultOptions
|
|
||||||
type args struct {
|
|
||||||
client KeyVaultClient
|
|
||||||
signingKey string
|
|
||||||
defaults DefaultOptions
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want crypto.Signer
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{client, "azurekms:vault=my-vault;name=my-key", noOptions}, &Signer{
|
|
||||||
client: client,
|
|
||||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
|
||||||
name: "my-key",
|
|
||||||
version: "",
|
|
||||||
publicKey: pub,
|
|
||||||
}, false},
|
|
||||||
{"ok with version", args{client, "azurekms:name=my-key;vault=my-vault?version=my-version", noOptions}, &Signer{
|
|
||||||
client: client,
|
|
||||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
|
||||||
name: "my-key",
|
|
||||||
version: "my-version",
|
|
||||||
publicKey: pub,
|
|
||||||
}, false},
|
|
||||||
{"ok with options", args{client, "azurekms:name=my-key?version=my-version", DefaultOptions{Vault: "my-vault", ProtectionLevel: apiv1.HSM}}, &Signer{
|
|
||||||
client: client,
|
|
||||||
vaultBaseURL: "https://my-vault.vault.azure.net/",
|
|
||||||
name: "my-key",
|
|
||||||
version: "my-version",
|
|
||||||
publicKey: pub,
|
|
||||||
}, false},
|
|
||||||
{"fail GetKey", args{client, "azurekms:name=not-found;vault=my-vault?version=my-version", noOptions}, nil, true},
|
|
||||||
{"fail vault", args{client, "azurekms:name=not-found;vault=", noOptions}, nil, true},
|
|
||||||
{"fail id", args{client, "azurekms:name=;vault=my-vault?version=my-version", noOptions}, nil, true},
|
|
||||||
{"fail scheme", args{client, "kms:name=not-found;vault=my-vault?version=my-version", noOptions}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := NewSigner(tt.args.client, tt.args.signingKey, tt.args.defaults)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("NewSigner() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewSigner() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSigner_Public(t *testing.T) {
|
|
||||||
key, err := keyutil.GenerateDefaultSigner()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pub := key.Public()
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want crypto.PublicKey
|
|
||||||
}{
|
|
||||||
{"ok", fields{pub}, pub},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &Signer{
|
|
||||||
publicKey: tt.fields.publicKey,
|
|
||||||
}
|
|
||||||
if got := s.Public(); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Signer.Public() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSigner_Sign(t *testing.T) {
|
|
||||||
sign := func(kty, crv string, bits int, opts crypto.SignerOpts) (crypto.PublicKey, []byte, string, []byte) {
|
|
||||||
key, err := keyutil.GenerateSigner(kty, crv, bits)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
h := opts.HashFunc().New()
|
|
||||||
h.Write([]byte("random-data"))
|
|
||||||
sum := h.Sum(nil)
|
|
||||||
|
|
||||||
var sig, resultSig []byte
|
|
||||||
if priv, ok := key.(*ecdsa.PrivateKey); ok {
|
|
||||||
r, s, err := ecdsa.Sign(rand.Reader, priv, sum)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
curveBits := priv.Params().BitSize
|
|
||||||
keyBytes := curveBits / 8
|
|
||||||
if curveBits%8 > 0 {
|
|
||||||
keyBytes++
|
|
||||||
}
|
|
||||||
rBytes := r.Bytes()
|
|
||||||
rBytesPadded := make([]byte, keyBytes)
|
|
||||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
|
||||||
|
|
||||||
sBytes := s.Bytes()
|
|
||||||
sBytesPadded := make([]byte, keyBytes)
|
|
||||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
|
||||||
// nolint:gocritic
|
|
||||||
resultSig = append(rBytesPadded, sBytesPadded...)
|
|
||||||
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
|
||||||
b.AddASN1BigInt(r)
|
|
||||||
b.AddASN1BigInt(s)
|
|
||||||
})
|
|
||||||
sig, err = b.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sig, err = key.Sign(rand.Reader, sum, opts)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
resultSig = sig
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.Public(), h.Sum(nil), base64.RawURLEncoding.EncodeToString(resultSig), sig
|
|
||||||
}
|
|
||||||
|
|
||||||
p256, p256Digest, p256ResultSig, p256Sig := sign("EC", "P-256", 0, crypto.SHA256)
|
|
||||||
p384, p384Digest, p386ResultSig, p384Sig := sign("EC", "P-384", 0, crypto.SHA384)
|
|
||||||
p521, p521Digest, p521ResultSig, p521Sig := sign("EC", "P-521", 0, crypto.SHA512)
|
|
||||||
rsaSHA256, rsaSHA256Digest, rsaSHA256ResultSig, rsaSHA256Sig := sign("RSA", "", 2048, crypto.SHA256)
|
|
||||||
rsaSHA384, rsaSHA384Digest, rsaSHA384ResultSig, rsaSHA384Sig := sign("RSA", "", 2048, crypto.SHA384)
|
|
||||||
rsaSHA512, rsaSHA512Digest, rsaSHA512ResultSig, rsaSHA512Sig := sign("RSA", "", 2048, crypto.SHA512)
|
|
||||||
rsaPSSSHA256, rsaPSSSHA256Digest, rsaPSSSHA256ResultSig, rsaPSSSHA256Sig := sign("RSA", "", 2048, &rsa.PSSOptions{
|
|
||||||
SaltLength: rsa.PSSSaltLengthAuto,
|
|
||||||
Hash: crypto.SHA256,
|
|
||||||
})
|
|
||||||
rsaPSSSHA384, rsaPSSSHA384Digest, rsaPSSSHA384ResultSig, rsaPSSSHA384Sig := sign("RSA", "", 2048, &rsa.PSSOptions{
|
|
||||||
SaltLength: rsa.PSSSaltLengthAuto,
|
|
||||||
Hash: crypto.SHA512,
|
|
||||||
})
|
|
||||||
rsaPSSSHA512, rsaPSSSHA512Digest, rsaPSSSHA512ResultSig, rsaPSSSHA512Sig := sign("RSA", "", 2048, &rsa.PSSOptions{
|
|
||||||
SaltLength: rsa.PSSSaltLengthAuto,
|
|
||||||
Hash: crypto.SHA512,
|
|
||||||
})
|
|
||||||
|
|
||||||
ed25519Key, err := keyutil.GenerateSigner("OKP", "Ed25519", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := mockClient(t)
|
|
||||||
expects := []struct {
|
|
||||||
name string
|
|
||||||
keyVersion string
|
|
||||||
alg keyvault.JSONWebKeySignatureAlgorithm
|
|
||||||
digest []byte
|
|
||||||
result keyvault.KeyOperationResult
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{"P-256", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &p256ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"P-384", "my-version", keyvault.ES384, p384Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &p386ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"P-521", "my-version", keyvault.ES512, p521Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &p521ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"RSA SHA256", "", keyvault.RS256, rsaSHA256Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaSHA256ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"RSA SHA384", "", keyvault.RS384, rsaSHA384Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaSHA384ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"RSA SHA512", "", keyvault.RS512, rsaSHA512Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaSHA512ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"RSA-PSS SHA256", "", keyvault.PS256, rsaPSSSHA256Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaPSSSHA256ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"RSA-PSS SHA384", "", keyvault.PS384, rsaPSSSHA384Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaPSSSHA384ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"RSA-PSS SHA512", "", keyvault.PS512, rsaPSSSHA512Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaPSSSHA512ResultSig,
|
|
||||||
}, nil},
|
|
||||||
// Errors
|
|
||||||
{"fail Sign", "", keyvault.RS256, rsaSHA256Digest, keyvault.KeyOperationResult{}, errTest},
|
|
||||||
{"fail sign length", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: &rsaSHA256ResultSig,
|
|
||||||
}, nil},
|
|
||||||
{"fail base64", "", keyvault.ES256, p256Digest, keyvault.KeyOperationResult{
|
|
||||||
Result: func() *string {
|
|
||||||
v := "😎"
|
|
||||||
return &v
|
|
||||||
}(),
|
|
||||||
}, nil},
|
|
||||||
}
|
|
||||||
for _, e := range expects {
|
|
||||||
value := base64.RawURLEncoding.EncodeToString(e.digest)
|
|
||||||
client.EXPECT().Sign(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", e.keyVersion, keyvault.KeySignParameters{
|
|
||||||
Algorithm: e.alg,
|
|
||||||
Value: &value,
|
|
||||||
}).Return(e.result, e.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
client KeyVaultClient
|
|
||||||
vaultBaseURL string
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
rand io.Reader
|
|
||||||
digest []byte
|
|
||||||
opts crypto.SignerOpts
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want []byte
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok P-256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
|
||||||
rand.Reader, p256Digest, crypto.SHA256,
|
|
||||||
}, p256Sig, false},
|
|
||||||
{"ok P-384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p384}, args{
|
|
||||||
rand.Reader, p384Digest, crypto.SHA384,
|
|
||||||
}, p384Sig, false},
|
|
||||||
{"ok P-521", fields{client, "https://my-vault.vault.azure.net/", "my-key", "my-version", p521}, args{
|
|
||||||
rand.Reader, p521Digest, crypto.SHA512,
|
|
||||||
}, p521Sig, false},
|
|
||||||
{"ok RSA SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{
|
|
||||||
rand.Reader, rsaSHA256Digest, crypto.SHA256,
|
|
||||||
}, rsaSHA256Sig, false},
|
|
||||||
{"ok RSA SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA384}, args{
|
|
||||||
rand.Reader, rsaSHA384Digest, crypto.SHA384,
|
|
||||||
}, rsaSHA384Sig, false},
|
|
||||||
{"ok RSA SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA512}, args{
|
|
||||||
rand.Reader, rsaSHA512Digest, crypto.SHA512,
|
|
||||||
}, rsaSHA512Sig, false},
|
|
||||||
{"ok RSA-PSS SHA256", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{
|
|
||||||
rand.Reader, rsaPSSSHA256Digest, &rsa.PSSOptions{
|
|
||||||
SaltLength: rsa.PSSSaltLengthAuto,
|
|
||||||
Hash: crypto.SHA256,
|
|
||||||
},
|
|
||||||
}, rsaPSSSHA256Sig, false},
|
|
||||||
{"ok RSA-PSS SHA384", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA384}, args{
|
|
||||||
rand.Reader, rsaPSSSHA384Digest, &rsa.PSSOptions{
|
|
||||||
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
|
||||||
Hash: crypto.SHA384,
|
|
||||||
},
|
|
||||||
}, rsaPSSSHA384Sig, false},
|
|
||||||
{"ok RSA-PSS SHA512", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA512}, args{
|
|
||||||
rand.Reader, rsaPSSSHA512Digest, &rsa.PSSOptions{
|
|
||||||
SaltLength: 64,
|
|
||||||
Hash: crypto.SHA512,
|
|
||||||
},
|
|
||||||
}, rsaPSSSHA512Sig, false},
|
|
||||||
{"fail Sign", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{
|
|
||||||
rand.Reader, rsaSHA256Digest, crypto.SHA256,
|
|
||||||
}, nil, true},
|
|
||||||
{"fail sign length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
|
||||||
rand.Reader, p256Digest, crypto.SHA256,
|
|
||||||
}, nil, true},
|
|
||||||
{"fail base64", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
|
||||||
rand.Reader, p256Digest, crypto.SHA256,
|
|
||||||
}, nil, true},
|
|
||||||
{"fail RSA-PSS salt length", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaPSSSHA256}, args{
|
|
||||||
rand.Reader, rsaPSSSHA256Digest, &rsa.PSSOptions{
|
|
||||||
SaltLength: 64,
|
|
||||||
Hash: crypto.SHA256,
|
|
||||||
},
|
|
||||||
}, nil, true},
|
|
||||||
{"fail RSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", rsaSHA256}, args{
|
|
||||||
rand.Reader, rsaSHA256Digest, crypto.SHA1,
|
|
||||||
}, nil, true},
|
|
||||||
{"fail ECDSA Hash", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
|
||||||
rand.Reader, p256Digest, crypto.MD5,
|
|
||||||
}, nil, true},
|
|
||||||
{"fail Ed25519", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", ed25519Key}, args{
|
|
||||||
rand.Reader, []byte("message"), crypto.Hash(0),
|
|
||||||
}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &Signer{
|
|
||||||
client: tt.fields.client,
|
|
||||||
vaultBaseURL: tt.fields.vaultBaseURL,
|
|
||||||
name: tt.fields.name,
|
|
||||||
version: tt.fields.version,
|
|
||||||
publicKey: tt.fields.publicKey,
|
|
||||||
}
|
|
||||||
got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Signer.Sign() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSigner_Sign_signWithRetry(t *testing.T) {
|
|
||||||
sign := func(kty, crv string, bits int, opts crypto.SignerOpts) (crypto.PublicKey, []byte, string, []byte) {
|
|
||||||
key, err := keyutil.GenerateSigner(kty, crv, bits)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
h := opts.HashFunc().New()
|
|
||||||
h.Write([]byte("random-data"))
|
|
||||||
sum := h.Sum(nil)
|
|
||||||
|
|
||||||
var sig, resultSig []byte
|
|
||||||
if priv, ok := key.(*ecdsa.PrivateKey); ok {
|
|
||||||
r, s, err := ecdsa.Sign(rand.Reader, priv, sum)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
curveBits := priv.Params().BitSize
|
|
||||||
keyBytes := curveBits / 8
|
|
||||||
if curveBits%8 > 0 {
|
|
||||||
keyBytes++
|
|
||||||
}
|
|
||||||
rBytes := r.Bytes()
|
|
||||||
rBytesPadded := make([]byte, keyBytes)
|
|
||||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
|
||||||
|
|
||||||
sBytes := s.Bytes()
|
|
||||||
sBytesPadded := make([]byte, keyBytes)
|
|
||||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
|
||||||
// nolint:gocritic
|
|
||||||
resultSig = append(rBytesPadded, sBytesPadded...)
|
|
||||||
|
|
||||||
var b cryptobyte.Builder
|
|
||||||
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
|
||||||
b.AddASN1BigInt(r)
|
|
||||||
b.AddASN1BigInt(s)
|
|
||||||
})
|
|
||||||
sig, err = b.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sig, err = key.Sign(rand.Reader, sum, opts)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
resultSig = sig
|
|
||||||
}
|
|
||||||
|
|
||||||
return key.Public(), h.Sum(nil), base64.RawURLEncoding.EncodeToString(resultSig), sig
|
|
||||||
}
|
|
||||||
|
|
||||||
p256, p256Digest, p256ResultSig, p256Sig := sign("EC", "P-256", 0, crypto.SHA256)
|
|
||||||
okResult := keyvault.KeyOperationResult{
|
|
||||||
Result: &p256ResultSig,
|
|
||||||
}
|
|
||||||
failResult := keyvault.KeyOperationResult{}
|
|
||||||
retryError := autorest.DetailedError{
|
|
||||||
Original: &azure.RequestError{
|
|
||||||
ServiceError: &azure.ServiceError{
|
|
||||||
InnerError: map[string]interface{}{
|
|
||||||
"code": "KeyNotYetValid",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
client := mockClient(t)
|
|
||||||
expects := []struct {
|
|
||||||
name string
|
|
||||||
keyVersion string
|
|
||||||
alg keyvault.JSONWebKeySignatureAlgorithm
|
|
||||||
digest []byte
|
|
||||||
result keyvault.KeyOperationResult
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{"ok 1", "", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
{"ok 2", "", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
{"ok 3", "", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
{"ok 4", "", keyvault.ES256, p256Digest, okResult, nil},
|
|
||||||
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
{"fail", "fail-version", keyvault.ES256, p256Digest, failResult, retryError},
|
|
||||||
}
|
|
||||||
for _, e := range expects {
|
|
||||||
value := base64.RawURLEncoding.EncodeToString(e.digest)
|
|
||||||
client.EXPECT().Sign(gomock.Any(), "https://my-vault.vault.azure.net/", "my-key", e.keyVersion, keyvault.KeySignParameters{
|
|
||||||
Algorithm: e.alg,
|
|
||||||
Value: &value,
|
|
||||||
}).Return(e.result, e.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
client KeyVaultClient
|
|
||||||
vaultBaseURL string
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
publicKey crypto.PublicKey
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
rand io.Reader
|
|
||||||
digest []byte
|
|
||||||
opts crypto.SignerOpts
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want []byte
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{client, "https://my-vault.vault.azure.net/", "my-key", "", p256}, args{
|
|
||||||
rand.Reader, p256Digest, crypto.SHA256,
|
|
||||||
}, p256Sig, false},
|
|
||||||
{"fail", fields{client, "https://my-vault.vault.azure.net/", "my-key", "fail-version", p256}, args{
|
|
||||||
rand.Reader, p256Digest, crypto.SHA256,
|
|
||||||
}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
s := &Signer{
|
|
||||||
client: tt.fields.client,
|
|
||||||
vaultBaseURL: tt.fields.vaultBaseURL,
|
|
||||||
name: tt.fields.name,
|
|
||||||
version: tt.fields.version,
|
|
||||||
publicKey: tt.fields.publicKey,
|
|
||||||
}
|
|
||||||
got, err := s.Sign(tt.args.rand, tt.args.digest, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Signer.Sign() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("Signer.Sign() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
package azurekms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/uri"
|
|
||||||
"go.step.sm/crypto/jose"
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultContext returns the default context used in requests to azure.
|
|
||||||
func defaultContext() (context.Context, context.CancelFunc) {
|
|
||||||
return context.WithTimeout(context.Background(), 15*time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getKeyName returns the uri of the key vault key.
|
|
||||||
func getKeyName(vault, name string, bundle keyvault.KeyBundle) string {
|
|
||||||
if bundle.Key != nil && bundle.Key.Kid != nil {
|
|
||||||
sm := keyIDRegexp.FindAllStringSubmatch(*bundle.Key.Kid, 1)
|
|
||||||
if len(sm) == 1 && len(sm[0]) == 4 {
|
|
||||||
m := sm[0]
|
|
||||||
u := uri.New(Scheme, url.Values{
|
|
||||||
"vault": []string{m[1]},
|
|
||||||
"name": []string{m[2]},
|
|
||||||
})
|
|
||||||
u.RawQuery = url.Values{"version": []string{m[3]}}.Encode()
|
|
||||||
return u.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Fallback to URI without id.
|
|
||||||
return uri.New(Scheme, url.Values{
|
|
||||||
"vault": []string{vault},
|
|
||||||
"name": []string{name},
|
|
||||||
}).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseKeyName returns the key vault, name and version from URIs like:
|
|
||||||
//
|
|
||||||
// - azurekms:vault=key-vault;name=key-name
|
|
||||||
// - azurekms:vault=key-vault;name=key-name?version=key-id
|
|
||||||
// - azurekms:vault=key-vault;name=key-name?version=key-id&hsm=true
|
|
||||||
//
|
|
||||||
// The key-id defines the version of the key, if it is not passed the latest
|
|
||||||
// version will be used.
|
|
||||||
//
|
|
||||||
// HSM can also be passed to define the protection level if this is not given in
|
|
||||||
// CreateQuery.
|
|
||||||
func parseKeyName(rawURI string, defaults DefaultOptions) (vault, name, version string, hsm bool, err error) {
|
|
||||||
var u *uri.URI
|
|
||||||
|
|
||||||
u, err = uri.ParseWithScheme(Scheme, rawURI)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if name = u.Get("name"); name == "" {
|
|
||||||
err = errors.Errorf("key uri %s is not valid: name is missing", rawURI)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if vault = u.Get("vault"); vault == "" {
|
|
||||||
if defaults.Vault == "" {
|
|
||||||
name = ""
|
|
||||||
err = errors.Errorf("key uri %s is not valid: vault is missing", rawURI)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vault = defaults.Vault
|
|
||||||
}
|
|
||||||
if u.Get("hsm") == "" {
|
|
||||||
hsm = (defaults.ProtectionLevel == apiv1.HSM)
|
|
||||||
} else {
|
|
||||||
hsm = u.GetBool("hsm")
|
|
||||||
}
|
|
||||||
|
|
||||||
version = u.Get("version")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func vaultBaseURL(vault string) string {
|
|
||||||
return "https://" + vault + ".vault.azure.net/"
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertKey(key *keyvault.JSONWebKey) (crypto.PublicKey, error) {
|
|
||||||
b, err := json.Marshal(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "error marshaling key")
|
|
||||||
}
|
|
||||||
var jwk jose.JSONWebKey
|
|
||||||
if err := jwk.UnmarshalJSON(b); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "error unmarshaling key")
|
|
||||||
}
|
|
||||||
return jwk.Key, nil
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
package azurekms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_getKeyName(t *testing.T) {
|
|
||||||
getBundle := func(kid string) keyvault.KeyBundle {
|
|
||||||
return keyvault.KeyBundle{
|
|
||||||
Key: &keyvault.JSONWebKey{
|
|
||||||
Kid: &kid,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
vault string
|
|
||||||
name string
|
|
||||||
bundle keyvault.KeyBundle
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"ok", args{"my-vault", "my-key", getBundle("https://my-vault.vault.azure.net/keys/my-key/my-version")}, "azurekms:name=my-key;vault=my-vault?version=my-version"},
|
|
||||||
{"ok default", args{"my-vault", "my-key", getBundle("https://my-vault.foo.net/keys/my-key/my-version")}, "azurekms:name=my-key;vault=my-vault"},
|
|
||||||
{"ok too short", args{"my-vault", "my-key", getBundle("https://my-vault.vault.azure.net/keys/my-version")}, "azurekms:name=my-key;vault=my-vault"},
|
|
||||||
{"ok too long", args{"my-vault", "my-key", getBundle("https://my-vault.vault.azure.net/keys/my-key/my-version/sign")}, "azurekms:name=my-key;vault=my-vault"},
|
|
||||||
{"ok nil key", args{"my-vault", "my-key", keyvault.KeyBundle{}}, "azurekms:name=my-key;vault=my-vault"},
|
|
||||||
{"ok nil kid", args{"my-vault", "my-key", keyvault.KeyBundle{Key: &keyvault.JSONWebKey{}}}, "azurekms:name=my-key;vault=my-vault"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := getKeyName(tt.args.vault, tt.args.name, tt.args.bundle); got != tt.want {
|
|
||||||
t.Errorf("getKeyName() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_parseKeyName(t *testing.T) {
|
|
||||||
var noOptions DefaultOptions
|
|
||||||
type args struct {
|
|
||||||
rawURI string
|
|
||||||
defaults DefaultOptions
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantVault string
|
|
||||||
wantName string
|
|
||||||
wantVersion string
|
|
||||||
wantHsm bool
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{"azurekms:name=my-key;vault=my-vault?version=my-version", noOptions}, "my-vault", "my-key", "my-version", false, false},
|
|
||||||
{"ok opaque version", args{"azurekms:name=my-key;vault=my-vault;version=my-version", noOptions}, "my-vault", "my-key", "my-version", false, false},
|
|
||||||
{"ok no version", args{"azurekms:name=my-key;vault=my-vault", noOptions}, "my-vault", "my-key", "", false, false},
|
|
||||||
{"ok hsm", args{"azurekms:name=my-key;vault=my-vault?hsm=true", noOptions}, "my-vault", "my-key", "", true, false},
|
|
||||||
{"ok hsm false", args{"azurekms:name=my-key;vault=my-vault?hsm=false", noOptions}, "my-vault", "my-key", "", false, false},
|
|
||||||
{"ok default vault", args{"azurekms:name=my-key?version=my-version", DefaultOptions{Vault: "my-vault"}}, "my-vault", "my-key", "my-version", false, false},
|
|
||||||
{"ok default hsm", args{"azurekms:name=my-key;vault=my-vault?version=my-version", DefaultOptions{Vault: "other-vault", ProtectionLevel: apiv1.HSM}}, "my-vault", "my-key", "my-version", true, false},
|
|
||||||
{"fail scheme", args{"azure:name=my-key;vault=my-vault", noOptions}, "", "", "", false, true},
|
|
||||||
{"fail parse uri", args{"azurekms:name=%ZZ;vault=my-vault", noOptions}, "", "", "", false, true},
|
|
||||||
{"fail no name", args{"azurekms:vault=my-vault", noOptions}, "", "", "", false, true},
|
|
||||||
{"fail empty name", args{"azurekms:name=;vault=my-vault", noOptions}, "", "", "", false, true},
|
|
||||||
{"fail no vault", args{"azurekms:name=my-key", noOptions}, "", "", "", false, true},
|
|
||||||
{"fail empty vault", args{"azurekms:name=my-key;vault=", noOptions}, "", "", "", false, true},
|
|
||||||
{"fail empty", args{"", noOptions}, "", "", "", false, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotVault, gotName, gotVersion, gotHsm, err := parseKeyName(tt.args.rawURI, tt.args.defaults)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("parseKeyName() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if gotVault != tt.wantVault {
|
|
||||||
t.Errorf("parseKeyName() gotVault = %v, want %v", gotVault, tt.wantVault)
|
|
||||||
}
|
|
||||||
if gotName != tt.wantName {
|
|
||||||
t.Errorf("parseKeyName() gotName = %v, want %v", gotName, tt.wantName)
|
|
||||||
}
|
|
||||||
if gotVersion != tt.wantVersion {
|
|
||||||
t.Errorf("parseKeyName() gotVersion = %v, want %v", gotVersion, tt.wantVersion)
|
|
||||||
}
|
|
||||||
if gotHsm != tt.wantHsm {
|
|
||||||
t.Errorf("parseKeyName() gotHsm = %v, want %v", gotHsm, tt.wantHsm)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,348 +0,0 @@
|
||||||
package cloudkms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"crypto/x509"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
|
|
||||||
cloudkms "cloud.google.com/go/kms/apiv1"
|
|
||||||
gax "github.com/googleapis/gax-go/v2"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"github.com/smallstep/certificates/kms/uri"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
"google.golang.org/api/option"
|
|
||||||
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scheme is the scheme used in uris.
|
|
||||||
const Scheme = "cloudkms"
|
|
||||||
|
|
||||||
const pendingGenerationRetries = 10
|
|
||||||
|
|
||||||
// protectionLevelMapping maps step protection levels with cloud kms ones.
|
|
||||||
var protectionLevelMapping = map[apiv1.ProtectionLevel]kmspb.ProtectionLevel{
|
|
||||||
apiv1.UnspecifiedProtectionLevel: kmspb.ProtectionLevel_PROTECTION_LEVEL_UNSPECIFIED,
|
|
||||||
apiv1.Software: kmspb.ProtectionLevel_SOFTWARE,
|
|
||||||
apiv1.HSM: kmspb.ProtectionLevel_HSM,
|
|
||||||
}
|
|
||||||
|
|
||||||
// signatureAlgorithmMapping is a mapping between the step signature algorithm,
|
|
||||||
// and bits for RSA keys, with cloud kms one.
|
|
||||||
//
|
|
||||||
// Cloud KMS does not support SHA384WithRSA, SHA384WithRSAPSS, SHA384WithRSAPSS,
|
|
||||||
// ECDSAWithSHA512, and PureEd25519.
|
|
||||||
var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]interface{}{
|
|
||||||
apiv1.UnspecifiedSignAlgorithm: kmspb.CryptoKeyVersion_CRYPTO_KEY_VERSION_ALGORITHM_UNSPECIFIED,
|
|
||||||
apiv1.SHA256WithRSA: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{
|
|
||||||
0: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_3072_SHA256,
|
|
||||||
2048: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_2048_SHA256,
|
|
||||||
3072: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_3072_SHA256,
|
|
||||||
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256,
|
|
||||||
},
|
|
||||||
apiv1.SHA512WithRSA: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{
|
|
||||||
0: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512,
|
|
||||||
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512,
|
|
||||||
},
|
|
||||||
apiv1.SHA256WithRSAPSS: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{
|
|
||||||
0: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256,
|
|
||||||
2048: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_2048_SHA256,
|
|
||||||
3072: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256,
|
|
||||||
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA256,
|
|
||||||
},
|
|
||||||
apiv1.SHA512WithRSAPSS: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{
|
|
||||||
0: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA512,
|
|
||||||
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA512,
|
|
||||||
},
|
|
||||||
apiv1.ECDSAWithSHA256: kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256,
|
|
||||||
apiv1.ECDSAWithSHA384: kmspb.CryptoKeyVersion_EC_SIGN_P384_SHA384,
|
|
||||||
}
|
|
||||||
|
|
||||||
var cryptoKeyVersionMapping = map[kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm]x509.SignatureAlgorithm{
|
|
||||||
kmspb.CryptoKeyVersion_EC_SIGN_P256_SHA256: x509.ECDSAWithSHA256,
|
|
||||||
kmspb.CryptoKeyVersion_EC_SIGN_P384_SHA384: x509.ECDSAWithSHA384,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_2048_SHA256: x509.SHA256WithRSA,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_3072_SHA256: x509.SHA256WithRSA,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256: x509.SHA256WithRSA,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512: x509.SHA512WithRSA,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PSS_2048_SHA256: x509.SHA256WithRSAPSS,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256: x509.SHA256WithRSAPSS,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA256: x509.SHA256WithRSAPSS,
|
|
||||||
kmspb.CryptoKeyVersion_RSA_SIGN_PSS_4096_SHA512: x509.SHA512WithRSAPSS,
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyManagementClient defines the methods on KeyManagementClient that this
|
|
||||||
// package will use. This interface will be used for unit testing.
|
|
||||||
type KeyManagementClient interface {
|
|
||||||
Close() error
|
|
||||||
GetPublicKey(context.Context, *kmspb.GetPublicKeyRequest, ...gax.CallOption) (*kmspb.PublicKey, error)
|
|
||||||
AsymmetricSign(context.Context, *kmspb.AsymmetricSignRequest, ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error)
|
|
||||||
CreateCryptoKey(context.Context, *kmspb.CreateCryptoKeyRequest, ...gax.CallOption) (*kmspb.CryptoKey, error)
|
|
||||||
GetKeyRing(context.Context, *kmspb.GetKeyRingRequest, ...gax.CallOption) (*kmspb.KeyRing, error)
|
|
||||||
CreateKeyRing(context.Context, *kmspb.CreateKeyRingRequest, ...gax.CallOption) (*kmspb.KeyRing, error)
|
|
||||||
CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest, opts ...gax.CallOption) (*kmspb.CryptoKeyVersion, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var newKeyManagementClient = func(ctx context.Context, opts ...option.ClientOption) (KeyManagementClient, error) {
|
|
||||||
return cloudkms.NewKeyManagementClient(ctx, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloudKMS implements a KMS using Google's Cloud apiv1.
|
|
||||||
type CloudKMS struct {
|
|
||||||
client KeyManagementClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new CloudKMS configured with a new client.
|
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*CloudKMS, error) {
|
|
||||||
var cloudOpts []option.ClientOption
|
|
||||||
|
|
||||||
if opts.URI != "" {
|
|
||||||
u, err := uri.ParseWithScheme(Scheme, opts.URI)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if f := u.Get("credentials-file"); f != "" {
|
|
||||||
cloudOpts = append(cloudOpts, option.WithCredentialsFile(f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated way to set configuration parameters.
|
|
||||||
if opts.CredentialsFile != "" {
|
|
||||||
cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := newKeyManagementClient(ctx, cloudOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &CloudKMS{
|
|
||||||
client: client,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
apiv1.Register(apiv1.CloudKMS, func(ctx context.Context, opts apiv1.Options) (apiv1.KeyManager, error) {
|
|
||||||
return New(ctx, opts)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCloudKMS creates a CloudKMS with a given client.
|
|
||||||
func NewCloudKMS(client KeyManagementClient) *CloudKMS {
|
|
||||||
return &CloudKMS{
|
|
||||||
client: client,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the connection of the Cloud KMS client.
|
|
||||||
func (k *CloudKMS) Close() error {
|
|
||||||
if err := k.client.Close(); err != nil {
|
|
||||||
return errors.Wrap(err, "cloudKMS Close failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSigner returns a new cloudkms signer configured with the given signing
|
|
||||||
// key name.
|
|
||||||
func (k *CloudKMS) CreateSigner(req *apiv1.CreateSignerRequest) (crypto.Signer, error) {
|
|
||||||
if req.SigningKey == "" {
|
|
||||||
return nil, errors.New("signing key cannot be empty")
|
|
||||||
}
|
|
||||||
return NewSigner(k.client, req.SigningKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey creates in Google's Cloud KMS a new asymmetric key for signing.
|
|
||||||
func (k *CloudKMS) CreateKey(req *apiv1.CreateKeyRequest) (*apiv1.CreateKeyResponse, error) {
|
|
||||||
if req.Name == "" {
|
|
||||||
return nil, errors.New("createKeyRequest 'name' cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
protectionLevel, ok := protectionLevelMapping[req.ProtectionLevel]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.Errorf("cloudKMS does not support protection level '%s'", req.ProtectionLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
var signatureAlgorithm kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm
|
|
||||||
v, ok := signatureAlgorithmMapping[req.SignatureAlgorithm]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.Errorf("cloudKMS does not support signature algorithm '%s'", req.SignatureAlgorithm)
|
|
||||||
}
|
|
||||||
switch v := v.(type) {
|
|
||||||
case kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm:
|
|
||||||
signatureAlgorithm = v
|
|
||||||
case map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm:
|
|
||||||
if signatureAlgorithm, ok = v[req.Bits]; !ok {
|
|
||||||
return nil, errors.Errorf("cloudKMS does not support signature algorithm '%s' with '%d' bits", req.SignatureAlgorithm, req.Bits)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("unexpected error: this should not happen")
|
|
||||||
}
|
|
||||||
|
|
||||||
var crytoKeyName string
|
|
||||||
|
|
||||||
// Split `projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID`
|
|
||||||
// to `projects/PROJECT_ID/locations/global/keyRings/RING_ID` and `KEY_ID`.
|
|
||||||
keyRing, keyID := Parent(req.Name)
|
|
||||||
if err := k.createKeyRingIfNeeded(keyRing); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// Create private key in CloudKMS.
|
|
||||||
response, err := k.client.CreateCryptoKey(ctx, &kmspb.CreateCryptoKeyRequest{
|
|
||||||
Parent: keyRing,
|
|
||||||
CryptoKeyId: keyID,
|
|
||||||
CryptoKey: &kmspb.CryptoKey{
|
|
||||||
Purpose: kmspb.CryptoKey_ASYMMETRIC_SIGN,
|
|
||||||
VersionTemplate: &kmspb.CryptoKeyVersionTemplate{
|
|
||||||
ProtectionLevel: protectionLevel,
|
|
||||||
Algorithm: signatureAlgorithm,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if status.Code(err) != codes.AlreadyExists {
|
|
||||||
return nil, errors.Wrap(err, "cloudKMS CreateCryptoKey failed")
|
|
||||||
}
|
|
||||||
// Create a new version if the key already exists.
|
|
||||||
//
|
|
||||||
// Note that it will have the same purpose, protection level and
|
|
||||||
// algorithm than as previous one.
|
|
||||||
req := &kmspb.CreateCryptoKeyVersionRequest{
|
|
||||||
Parent: req.Name,
|
|
||||||
CryptoKeyVersion: &kmspb.CryptoKeyVersion{
|
|
||||||
State: kmspb.CryptoKeyVersion_ENABLED,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
response, err := k.client.CreateCryptoKeyVersion(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "cloudKMS CreateCryptoKeyVersion failed")
|
|
||||||
}
|
|
||||||
crytoKeyName = response.Name
|
|
||||||
} else {
|
|
||||||
crytoKeyName = response.Name + "/cryptoKeyVersions/1"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep deterministically to avoid retries because of PENDING_GENERATING.
|
|
||||||
// One second is often enough.
|
|
||||||
if protectionLevel == kmspb.ProtectionLevel_HSM {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve public key to add it to the response.
|
|
||||||
pk, err := k.GetPublicKey(&apiv1.GetPublicKeyRequest{
|
|
||||||
Name: crytoKeyName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "cloudKMS GetPublicKey failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &apiv1.CreateKeyResponse{
|
|
||||||
Name: crytoKeyName,
|
|
||||||
PublicKey: pk,
|
|
||||||
CreateSignerRequest: apiv1.CreateSignerRequest{
|
|
||||||
SigningKey: crytoKeyName,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *CloudKMS) createKeyRingIfNeeded(name string) error {
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := k.client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
parent, child := Parent(name)
|
|
||||||
_, err = k.client.CreateKeyRing(ctx, &kmspb.CreateKeyRingRequest{
|
|
||||||
Parent: parent,
|
|
||||||
KeyRingId: child,
|
|
||||||
})
|
|
||||||
if err != nil && status.Code(err) != codes.AlreadyExists {
|
|
||||||
return errors.Wrap(err, "cloudKMS CreateKeyRing failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPublicKey gets from Google's Cloud KMS a public key by name. Key names
|
|
||||||
// follow the pattern:
|
|
||||||
//
|
|
||||||
// projects/([^/]+)/locations/([a-zA-Z0-9_-]{1,63})/keyRings/([a-zA-Z0-9_-]{1,63})/cryptoKeys/([a-zA-Z0-9_-]{1,63})/cryptoKeyVersions/([a-zA-Z0-9_-]{1,63})
|
|
||||||
func (k *CloudKMS) GetPublicKey(req *apiv1.GetPublicKeyRequest) (crypto.PublicKey, error) {
|
|
||||||
if req.Name == "" {
|
|
||||||
return nil, errors.New("createKeyRequest 'name' cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := k.getPublicKeyWithRetries(req.Name, pendingGenerationRetries)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "cloudKMS GetPublicKey failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
pk, err := pemutil.ParseKey([]byte(response.Pem))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPublicKeyWithRetries retries the request if the error is
|
|
||||||
// FailedPrecondition, caused because the key is in the PENDING_GENERATION
|
|
||||||
// status.
|
|
||||||
func (k *CloudKMS) getPublicKeyWithRetries(name string, retries int) (response *kmspb.PublicKey, err error) {
|
|
||||||
workFn := func() (*kmspb.PublicKey, error) {
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
return k.client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for i := 0; i < retries; i++ {
|
|
||||||
if response, err = workFn(); err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if status.Code(err) == codes.FailedPrecondition {
|
|
||||||
log.Println("Waiting for key generation ...")
|
|
||||||
time.Sleep(time.Duration(i+1) * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultContext() (context.Context, context.CancelFunc) {
|
|
||||||
return context.WithTimeout(context.Background(), 15*time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent splits a string in the format `key/value/key2/value2` in a parent and
|
|
||||||
// child, for the previous string it will return `key/value` and `value2`.
|
|
||||||
func Parent(name string) (string, string) {
|
|
||||||
a, b := parent(name)
|
|
||||||
a, _ = parent(a)
|
|
||||||
return a, b
|
|
||||||
}
|
|
||||||
|
|
||||||
func parent(name string) (string, string) {
|
|
||||||
i := strings.LastIndex(name, "/")
|
|
||||||
switch i {
|
|
||||||
case -1:
|
|
||||||
return "", name
|
|
||||||
case 0:
|
|
||||||
return "", name[i+1:]
|
|
||||||
default:
|
|
||||||
return name[:i], name[i+1:]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,464 +0,0 @@
|
||||||
package cloudkms
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
gax "github.com/googleapis/gax-go/v2"
|
|
||||||
"github.com/smallstep/certificates/kms/apiv1"
|
|
||||||
"go.step.sm/crypto/pemutil"
|
|
||||||
"google.golang.org/api/option"
|
|
||||||
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParent(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
want1 string
|
|
||||||
}{
|
|
||||||
{"zero", args{"child"}, "", "child"},
|
|
||||||
{"one", args{"parent/child"}, "", "child"},
|
|
||||||
{"two", args{"grandparent/parent/child"}, "grandparent", "child"},
|
|
||||||
{"three", args{"great-grandparent/grandparent/parent/child"}, "great-grandparent/grandparent", "child"},
|
|
||||||
{"empty", args{""}, "", ""},
|
|
||||||
{"root", args{"/"}, "", ""},
|
|
||||||
{"child", args{"/child"}, "", "child"},
|
|
||||||
{"parent", args{"parent/"}, "", ""},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, got1 := Parent(tt.args.name)
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("Parent() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
if got1 != tt.want1 {
|
|
||||||
t.Errorf("Parent() got1 = %v, want %v", got1, tt.want1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
tmp := newKeyManagementClient
|
|
||||||
t.Cleanup(func() {
|
|
||||||
newKeyManagementClient = tmp
|
|
||||||
})
|
|
||||||
newKeyManagementClient = func(ctx context.Context, opts ...option.ClientOption) (KeyManagementClient, error) {
|
|
||||||
if len(opts) > 0 {
|
|
||||||
return nil, fmt.Errorf("test error")
|
|
||||||
}
|
|
||||||
return &MockClient{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
opts apiv1.Options
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want *CloudKMS
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", args{context.Background(), apiv1.Options{}}, &CloudKMS{client: &MockClient{}}, false},
|
|
||||||
{"ok with uri", args{context.Background(), apiv1.Options{URI: "cloudkms:"}}, &CloudKMS{client: &MockClient{}}, false},
|
|
||||||
{"fail credentials", args{context.Background(), apiv1.Options{CredentialsFile: "testdata/missing"}}, nil, true},
|
|
||||||
{"fail with uri", args{context.Background(), apiv1.Options{URI: "cloudkms:credentials-file=testdata/missing"}}, nil, true},
|
|
||||||
{"fail schema", args{context.Background(), apiv1.Options{URI: "pkcs11:"}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := New(tt.args.ctx, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("New() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNew_real(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
opts apiv1.Options
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want *CloudKMS
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"fail credentials", args{context.Background(), apiv1.Options{CredentialsFile: "testdata/missing"}}, nil, true},
|
|
||||||
{"fail with uri", args{context.Background(), apiv1.Options{URI: "cloudkms:credentials-file=testdata/missing"}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := New(tt.args.ctx, tt.args.opts)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("New() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewCloudKMS(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
client KeyManagementClient
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want *CloudKMS
|
|
||||||
}{
|
|
||||||
{"ok", args{&MockClient{}}, &CloudKMS{&MockClient{}}},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := NewCloudKMS(tt.args.client); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("NewCloudKMS() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudKMS_Close(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
client KeyManagementClient
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{&MockClient{close: func() error { return nil }}}, false},
|
|
||||||
{"fail", fields{&MockClient{close: func() error { return fmt.Errorf("an error") }}}, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &CloudKMS{
|
|
||||||
client: tt.fields.client,
|
|
||||||
}
|
|
||||||
if err := k.Close(); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("CloudKMS.Close() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudKMS_CreateSigner(t *testing.T) {
|
|
||||||
keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1"
|
|
||||||
pemBytes, err := os.ReadFile("testdata/pub.pem")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pk, err := pemutil.ParseKey(pemBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fields struct {
|
|
||||||
client KeyManagementClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.CreateSignerRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want crypto.Signer
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{&MockClient{
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}}, args{&apiv1.CreateSignerRequest{SigningKey: keyName}}, &Signer{client: &MockClient{}, signingKey: keyName, publicKey: pk}, false},
|
|
||||||
{"fail", fields{&MockClient{
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return nil, fmt.Errorf("test error")
|
|
||||||
},
|
|
||||||
}}, args{&apiv1.CreateSignerRequest{SigningKey: ""}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &CloudKMS{
|
|
||||||
client: tt.fields.client,
|
|
||||||
}
|
|
||||||
got, err := k.CreateSigner(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("CloudKMS.CreateSigner() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if signer, ok := got.(*Signer); ok {
|
|
||||||
signer.client = &MockClient{}
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("CloudKMS.CreateSigner() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudKMS_CreateKey(t *testing.T) {
|
|
||||||
keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c"
|
|
||||||
testError := fmt.Errorf("an error")
|
|
||||||
alreadyExists := status.Error(codes.AlreadyExists, "already exists")
|
|
||||||
|
|
||||||
pemBytes, err := os.ReadFile("testdata/pub.pem")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pk, err := pemutil.ParseKey(pemBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var retries int
|
|
||||||
type fields struct {
|
|
||||||
client KeyManagementClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.CreateKeyRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want *apiv1.CreateKeyResponse
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return &kmspb.KeyRing{}, nil
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return &kmspb.CryptoKey{Name: keyName}, nil
|
|
||||||
},
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
&apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/1", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/1"}}, false},
|
|
||||||
{"ok new key ring", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
createKeyRing: func(_ context.Context, _ *kmspb.CreateKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return nil, alreadyExists
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return &kmspb.CryptoKey{Name: keyName}, nil
|
|
||||||
},
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.Software, SignatureAlgorithm: apiv1.SHA256WithRSA, Bits: 3072}},
|
|
||||||
&apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/1", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/1"}}, false},
|
|
||||||
{"ok new key version", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return &kmspb.KeyRing{}, nil
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return nil, alreadyExists
|
|
||||||
},
|
|
||||||
createCryptoKeyVersion: func(_ context.Context, _ *kmspb.CreateCryptoKeyVersionRequest, _ ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) {
|
|
||||||
return &kmspb.CryptoKeyVersion{Name: keyName + "/cryptoKeyVersions/2"}, nil
|
|
||||||
},
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
&apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/2", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/2"}}, false},
|
|
||||||
{"ok with retries", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return &kmspb.KeyRing{}, nil
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return &kmspb.CryptoKey{Name: keyName}, nil
|
|
||||||
},
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
if retries != 2 {
|
|
||||||
retries++
|
|
||||||
return nil, status.Error(codes.FailedPrecondition, "key is not enabled, current state is: PENDING_GENERATION")
|
|
||||||
}
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
&apiv1.CreateKeyResponse{Name: keyName + "/cryptoKeyVersions/1", PublicKey: pk, CreateSignerRequest: apiv1.CreateSignerRequest{SigningKey: keyName + "/cryptoKeyVersions/1"}}, false},
|
|
||||||
{"fail name", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{}}, nil, true},
|
|
||||||
{"fail protection level", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.ProtectionLevel(100)}}, nil, true},
|
|
||||||
{"fail signature algorithm", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.Software, SignatureAlgorithm: apiv1.SignatureAlgorithm(100)}}, nil, true},
|
|
||||||
{"fail number of bits", fields{&MockClient{}}, args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.Software, SignatureAlgorithm: apiv1.SHA256WithRSA, Bits: 1024}},
|
|
||||||
nil, true},
|
|
||||||
{"fail create key ring", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
createKeyRing: func(_ context.Context, _ *kmspb.CreateKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
nil, true},
|
|
||||||
{"fail create key", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return &kmspb.KeyRing{}, nil
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
nil, true},
|
|
||||||
{"fail create key version", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return &kmspb.KeyRing{}, nil
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return nil, alreadyExists
|
|
||||||
},
|
|
||||||
createCryptoKeyVersion: func(_ context.Context, _ *kmspb.CreateCryptoKeyVersionRequest, _ ...gax.CallOption) (*kmspb.CryptoKeyVersion, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
nil, true},
|
|
||||||
{"fail get public key", fields{
|
|
||||||
&MockClient{
|
|
||||||
getKeyRing: func(_ context.Context, _ *kmspb.GetKeyRingRequest, _ ...gax.CallOption) (*kmspb.KeyRing, error) {
|
|
||||||
return &kmspb.KeyRing{}, nil
|
|
||||||
},
|
|
||||||
createCryptoKey: func(_ context.Context, _ *kmspb.CreateCryptoKeyRequest, _ ...gax.CallOption) (*kmspb.CryptoKey, error) {
|
|
||||||
return &kmspb.CryptoKey{Name: keyName}, nil
|
|
||||||
},
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.CreateKeyRequest{Name: keyName, ProtectionLevel: apiv1.HSM, SignatureAlgorithm: apiv1.ECDSAWithSHA256}},
|
|
||||||
nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &CloudKMS{
|
|
||||||
client: tt.fields.client,
|
|
||||||
}
|
|
||||||
got, err := k.CreateKey(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("CloudKMS.CreateKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("CloudKMS.CreateKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloudKMS_GetPublicKey(t *testing.T) {
|
|
||||||
keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1"
|
|
||||||
testError := fmt.Errorf("an error")
|
|
||||||
|
|
||||||
pemBytes, err := os.ReadFile("testdata/pub.pem")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
pk, err := pemutil.ParseKey(pemBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var retries int
|
|
||||||
type fields struct {
|
|
||||||
client KeyManagementClient
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
req *apiv1.GetPublicKeyRequest
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
args args
|
|
||||||
want crypto.PublicKey
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{"ok", fields{
|
|
||||||
&MockClient{
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.GetPublicKeyRequest{Name: keyName}}, pk, false},
|
|
||||||
{"ok with retries", fields{
|
|
||||||
&MockClient{
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
if retries != 2 {
|
|
||||||
retries++
|
|
||||||
return nil, status.Error(codes.FailedPrecondition, "key is not enabled, current state is: PENDING_GENERATION")
|
|
||||||
}
|
|
||||||
return &kmspb.PublicKey{Pem: string(pemBytes)}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.GetPublicKeyRequest{Name: keyName}}, pk, false},
|
|
||||||
{"fail name", fields{&MockClient{}}, args{&apiv1.GetPublicKeyRequest{}}, nil, true},
|
|
||||||
{"fail get public key", fields{
|
|
||||||
&MockClient{
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return nil, testError
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.GetPublicKeyRequest{Name: keyName}}, nil, true},
|
|
||||||
{"fail parse pem", fields{
|
|
||||||
&MockClient{
|
|
||||||
getPublicKey: func(_ context.Context, _ *kmspb.GetPublicKeyRequest, _ ...gax.CallOption) (*kmspb.PublicKey, error) {
|
|
||||||
return &kmspb.PublicKey{Pem: string("bad pem")}, nil
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
args{&apiv1.GetPublicKeyRequest{Name: keyName}}, nil, true},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
k := &CloudKMS{
|
|
||||||
client: tt.fields.client,
|
|
||||||
}
|
|
||||||
got, err := k.GetPublicKey(tt.args.req)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("CloudKMS.GetPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("CloudKMS.GetPublicKey() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue