From e10e35014dbee2145084d6ad44039d737014c76b Mon Sep 17 00:00:00 2001 From: Nick Cabatoff Date: Tue, 14 Dec 2021 16:49:54 -0500 Subject: [PATCH] Upgrade to Go 1.16.12. (#13417) * Move to Go 1.16.12 and newer cimg docker images for CI. (#13422) * Update MongoDB tests to not fail in Go 1.16 (#11533) * Upgrade snappy to fix panic with identity/packer on Go 1.16+arm64. (#12371) (#12375) * creds/aws: Add support for DSA signature verification for EC2 (#12340) --- .circleci/config.yml | 78 +- .circleci/config/commands/@yarn-cache.yml | 2 +- .circleci/config/commands/go_test.yml | 10 +- .circleci/config/commands/setup-go.yml | 2 +- .circleci/config/executors/@executors.yml | 22 +- .github/workflows/build.yml | 12 +- Makefile | 2 +- README.md | 2 +- api/go.sum | 1 + builtin/credential/aws/backend_test.go | 17 + builtin/credential/aws/path_login.go | 6 +- builtin/credential/aws/pkcs7/README.md | 5 + .../credential/aws}/pkcs7/ber.go | 101 +- builtin/credential/aws/pkcs7/ber_test.go | 62 ++ builtin/credential/aws/pkcs7/decrypt.go | 177 ++++ builtin/credential/aws/pkcs7/decrypt_test.go | 61 ++ builtin/credential/aws/pkcs7/encrypt.go | 399 ++++++++ builtin/credential/aws/pkcs7/encrypt_test.go | 102 ++ builtin/credential/aws/pkcs7/pkcs7.go | 291 ++++++ builtin/credential/aws/pkcs7/pkcs7_test.go | 326 ++++++ builtin/credential/aws/pkcs7/sign.go | 429 ++++++++ builtin/credential/aws/pkcs7/sign_test.go | 266 +++++ builtin/credential/aws/pkcs7/verify.go | 399 ++++++++ .../credential/aws/pkcs7/verify_test_dsa.go | 182 ++++ changelog/12340.txt | 3 + changelog/12371.txt | 3 + changelog/13422.txt | 3 + go.mod | 2 +- go.sum | 4 +- plugins/database/mongodb/mongodb_test.go | 29 +- scripts/docker/Dockerfile | 2 +- scripts/docker/Dockerfile.ui | 2 +- sdk/go.mod | 2 +- sdk/go.sum | 2 + vendor/github.com/fullsailor/pkcs7/.gitignore | 24 - .../github.com/fullsailor/pkcs7/.travis.yml | 7 - vendor/github.com/fullsailor/pkcs7/LICENSE | 22 - vendor/github.com/fullsailor/pkcs7/README.md | 8 - vendor/github.com/fullsailor/pkcs7/pkcs7.go | 962 ------------------ vendor/github.com/fullsailor/pkcs7/x509.go | 133 --- vendor/github.com/golang/snappy/AUTHORS | 1 + vendor/github.com/golang/snappy/CONTRIBUTORS | 2 + vendor/github.com/golang/snappy/decode.go | 83 +- .../github.com/golang/snappy/decode_arm64.s | 45 +- .../github.com/golang/snappy/encode_arm64.s | 85 +- .../google/go-cmp/cmp/cmpopts/equate.go | 148 +++ .../google/go-cmp/cmp/cmpopts/errors_go113.go | 15 + .../go-cmp/cmp/cmpopts/errors_xerrors.go | 18 + .../google/go-cmp/cmp/cmpopts/ignore.go | 206 ++++ .../google/go-cmp/cmp/cmpopts/sort.go | 147 +++ .../go-cmp/cmp/cmpopts/struct_filter.go | 187 ++++ .../google/go-cmp/cmp/cmpopts/xform.go | 35 + vendor/github.com/hashicorp/vault/api/go.sum | 1 + vendor/modules.txt | 7 +- 54 files changed, 3753 insertions(+), 1389 deletions(-) create mode 100644 builtin/credential/aws/pkcs7/README.md rename {vendor/github.com/fullsailor => builtin/credential/aws}/pkcs7/ber.go (69%) create mode 100644 builtin/credential/aws/pkcs7/ber_test.go create mode 100644 builtin/credential/aws/pkcs7/decrypt.go create mode 100644 builtin/credential/aws/pkcs7/decrypt_test.go create mode 100644 builtin/credential/aws/pkcs7/encrypt.go create mode 100644 builtin/credential/aws/pkcs7/encrypt_test.go create mode 100644 builtin/credential/aws/pkcs7/pkcs7.go create mode 100644 builtin/credential/aws/pkcs7/pkcs7_test.go create mode 100644 builtin/credential/aws/pkcs7/sign.go create mode 100644 builtin/credential/aws/pkcs7/sign_test.go create mode 100644 builtin/credential/aws/pkcs7/verify.go create mode 100644 builtin/credential/aws/pkcs7/verify_test_dsa.go create mode 100644 changelog/12340.txt create mode 100644 changelog/12371.txt create mode 100644 changelog/13422.txt delete mode 100644 vendor/github.com/fullsailor/pkcs7/.gitignore delete mode 100644 vendor/github.com/fullsailor/pkcs7/.travis.yml delete mode 100644 vendor/github.com/fullsailor/pkcs7/LICENSE delete mode 100644 vendor/github.com/fullsailor/pkcs7/README.md delete mode 100644 vendor/github.com/fullsailor/pkcs7/pkcs7.go delete mode 100644 vendor/github.com/fullsailor/pkcs7/x509.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go create mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go diff --git a/.circleci/config.yml b/.circleci/config.yml index b4f27c80ebdc4..ff9b8af3c0bef 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,11 +9,11 @@ jobs: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault steps: - checkout - restore_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} - run: command: | cd ui @@ -21,19 +21,19 @@ jobs: npm rebuild node-sass name: Install UI dependencies - save_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} paths: - ui/node_modules test-ui: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault resource_class: xlarge steps: - checkout - restore_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} - attach_workspace: at: . - run: @@ -66,12 +66,12 @@ jobs: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault resource_class: xlarge steps: - checkout - restore_cache: - key: yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: yarn-lock-v7-{{ checksum "ui/yarn.lock" }} - attach_workspace: at: . - run: @@ -83,7 +83,7 @@ jobs: build-go-dev: machine: true shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault steps: - run: command: | @@ -93,7 +93,7 @@ jobs: sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - GOPATH="/go" + GOPATH="/home/circleci/go" mkdir $GOPATH 2>/dev/null || { sudo mkdir $GOPATH && sudo chmod 777 $GOPATH; } echo "export GOPATH='$GOPATH'" >> "$BASH_ENV" echo "export PATH='$PATH:$GOPATH/bin:/usr/local/go/bin'" >> "$BASH_ENV" @@ -121,7 +121,7 @@ jobs: environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - GO_TAGS: '' - - GO_VERSION: 1.15.15 + - GO_VERSION: 1.16.12 - GO111MODULE: 'off' - GOTESTSUM_VERSION: 0.5.2 algolia-index: @@ -141,9 +141,9 @@ jobs: name: Push content to Algolia Index test-go-remote-docker: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.16.12 resource_class: medium - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -222,15 +222,15 @@ jobs: -e DOCKER_CERT_PATH -e DOCKER_HOST -e DOCKER_MACHINE_NAME -e DOCKER_TLS_VERIFY -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer circleci/golang:1.15.15-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.16.12 \ tail -f /dev/null # Run tests - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e GO111MODULE -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ testcontainer \ gotestsum --format=short-verbose \ @@ -257,7 +257,7 @@ jobs: no_output_timeout: 60m - run: command: | - docker cp testcontainer:/go/src/github.com/hashicorp/vault/test-results . + docker cp testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/test-results . name: Copy test results when: always - store_artifacts: @@ -272,9 +272,9 @@ jobs: - GO111MODULE: 'off' test-go-race: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.16.12 resource_class: xlarge - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -349,15 +349,15 @@ jobs: -e DOCKER_CERT_PATH -e DOCKER_HOST -e DOCKER_MACHINE_NAME -e DOCKER_TLS_VERIFY -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer circleci/golang:1.15.15-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.16.12 \ tail -f /dev/null # Run tests - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e GO111MODULE -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ testcontainer \ gotestsum --format=short-verbose \ @@ -417,9 +417,9 @@ jobs: name: Build Docker Image if Necessary test-go: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.16.12 resource_class: large - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -494,15 +494,15 @@ jobs: -e DOCKER_CERT_PATH -e DOCKER_HOST -e DOCKER_MACHINE_NAME -e DOCKER_TLS_VERIFY -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer circleci/golang:1.15.15-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.16.12 \ tail -f /dev/null # Run tests - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e GO111MODULE -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ testcontainer \ gotestsum --format=short-verbose \ @@ -569,7 +569,7 @@ jobs: go-mod-vendor: machine: true shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault steps: - run: command: | @@ -579,7 +579,7 @@ jobs: sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - GOPATH="/go" + GOPATH="/home/circleci/go" mkdir $GOPATH 2>/dev/null || { sudo mkdir $GOPATH && sudo chmod 777 $GOPATH; } echo "export GOPATH='$GOPATH'" >> "$BASH_ENV" echo "export PATH='$PATH:$GOPATH/bin:/usr/local/go/bin'" >> "$BASH_ENV" @@ -599,14 +599,14 @@ jobs: environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - GO_TAGS: '' - - GO_VERSION: 1.15.15 + - GO_VERSION: 1.16.12 - GO111MODULE: 'off' - GOTESTSUM_VERSION: 0.5.2 test-go-race-remote-docker: docker: - - image: docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster + - image: docker.mirror.hashicorp.services/cimg/go:1.16.12 resource_class: medium - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 steps: - run: @@ -685,15 +685,15 @@ jobs: -e DOCKER_CERT_PATH -e DOCKER_HOST -e DOCKER_MACHINE_NAME -e DOCKER_TLS_VERIFY -e NO_PROXY \ -e VAULT_TEST_LOG_DIR=/tmp/testlogs \ --network vaulttest --name \ - testcontainer circleci/golang:1.15.15-buster \ + testcontainer docker.mirror.hashicorp.services/cimg/go:1.16.12 \ tail -f /dev/null # Run tests - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e GO111MODULE -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ testcontainer \ gotestsum --format=short-verbose \ @@ -720,7 +720,7 @@ jobs: no_output_timeout: 60m - run: command: | - docker cp testcontainer:/go/src/github.com/hashicorp/vault/test-results . + docker cp testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/test-results . name: Copy test results when: always - store_artifacts: diff --git a/.circleci/config/commands/@yarn-cache.yml b/.circleci/config/commands/@yarn-cache.yml index a284d4bd8ed9b..9acfcf00780d1 100644 --- a/.circleci/config/commands/@yarn-cache.yml +++ b/.circleci/config/commands/@yarn-cache.yml @@ -1,7 +1,7 @@ restore_yarn_cache: steps: - restore_cache: - key: &YARN_LOCK_CACHE_KEY yarn-lock-v6-{{ checksum "ui/yarn.lock" }} + key: &YARN_LOCK_CACHE_KEY yarn-lock-v7-{{ checksum "ui/yarn.lock" }} save_yarn_cache: steps: - save_cache: diff --git a/.circleci/config/commands/go_test.yml b/.circleci/config/commands/go_test.yml index bc887570441c0..4459154013a80 100644 --- a/.circleci/config/commands/go_test.yml +++ b/.circleci/config/commands/go_test.yml @@ -8,7 +8,7 @@ parameters: default: "/tmp/testlogs" go_image: type: string - default: "circleci/golang:1.15.15-buster" + default: "docker.mirror.hashicorp.services/cimg/go:1.16.12" use_docker: type: boolean default: false @@ -82,11 +82,11 @@ steps: tail -f /dev/null # Run tests - docker exec testcontainer sh -c 'mkdir -p /go/src/github.com/hashicorp/vault' - docker cp . testcontainer:/go/src/github.com/hashicorp/vault/ + docker exec testcontainer sh -c 'mkdir -p /home/circleci/go/src/github.com/hashicorp/vault' + docker cp . testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/ docker cp $DOCKER_CERT_PATH/ testcontainer:$DOCKER_CERT_PATH - docker exec -w /go/src/github.com/hashicorp/vault/ \ + docker exec -w /home/circleci/go/src/github.com/hashicorp/vault/ \ -e GO111MODULE -e CIRCLECI -e VAULT_CI_GO_TEST_RACE \ testcontainer \ gotestsum --format=short-verbose \ @@ -117,4 +117,4 @@ steps: name: Copy test results when: always command: | - docker cp testcontainer:/go/src/github.com/hashicorp/vault/test-results . + docker cp testcontainer:/home/circleci/go/src/github.com/hashicorp/vault/test-results . diff --git a/.circleci/config/commands/setup-go.yml b/.circleci/config/commands/setup-go.yml index fcfe4e88e988d..5e1c1fbdc3f46 100644 --- a/.circleci/config/commands/setup-go.yml +++ b/.circleci/config/commands/setup-go.yml @@ -12,7 +12,7 @@ steps: sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - GOPATH="/go" + GOPATH="/home/circleci/go" mkdir $GOPATH 2>/dev/null || { sudo mkdir $GOPATH && sudo chmod 777 $GOPATH; } echo "export GOPATH='$GOPATH'" >> "$BASH_ENV" echo "export PATH='$PATH:$GOPATH/bin:/usr/local/go/bin'" >> "$BASH_ENV" diff --git a/.circleci/config/executors/@executors.yml b/.circleci/config/executors/@executors.yml index 687b6a6d353d4..3d84931617ae5 100644 --- a/.circleci/config/executors/@executors.yml +++ b/.circleci/config/executors/@executors.yml @@ -4,49 +4,49 @@ go-machine: environment: GO111MODULE: "off" CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) - GO_VERSION: 1.15.15 # Pin Go to patch version (ex: 1.2.3) + GO_VERSION: 1.16.12 # Pin Go to patch version (ex: 1.2.3) GOTESTSUM_VERSION: 0.5.2 # Pin gotestsum to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault node: docker: - image: docker.mirror.hashicorp.services/node:14-buster shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault python: docker: - image: docker.mirror.hashicorp.services/python:3-alpine shell: /usr/bin/env bash -euo pipefail -c - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault alpine: docker: - image: docker.mirror.hashicorp.services/alpine:3.13 shell: /bin/sh - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault docker-env-go-test-remote-docker: resource_class: medium docker: - - image: "docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster" + - image: "docker.mirror.hashicorp.services/cimg/go:1.16.12" environment: GO111MODULE: "off" CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault docker-env-go-test: resource_class: large docker: - - image: "docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster" + - image: "docker.mirror.hashicorp.services/cimg/go:1.16.12" environment: GO111MODULE: "off" CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault docker-env-go-test-race: resource_class: xlarge docker: - - image: "docker.mirror.hashicorp.services/circleci/golang:1.15.15-buster" + - image: "docker.mirror.hashicorp.services/cimg/go:1.16.12" environment: GO111MODULE: "off" CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) GO_TAGS: "" - working_directory: /go/src/github.com/hashicorp/vault + working_directory: /home/circleci/go/src/github.com/hashicorp/vault diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 405981582437e..d9b2802ef9bbb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,7 @@ jobs: matrix: goos: [ freebsd, windows, netbsd, openbsd, solaris ] goarch: [ "386", "amd64", "arm" ] - go: [ "1.15.15" ] + go: [ "1.16.12" ] exclude: - goos: solaris goarch: 386 @@ -89,7 +89,7 @@ jobs: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} CGO_ENABLED: 0 - + run: | mkdir dist out VAULT_VERSION=${{ needs.get-product-version.outputs.product-version }} VAULT_COMMIT=${GITHUB_SHA} GO_TAGS="${{ env.GO_TAGS }}" make build @@ -106,7 +106,7 @@ jobs: matrix: goos: [linux] goarch: ["arm", "arm64", "386", "amd64"] - go: ["1.15.15"] + go: ["1.16.12"] fail-fast: true name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} build @@ -138,7 +138,7 @@ jobs: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} CGO_ENABLED: 0 - + run: | mkdir dist out VAULT_VERSION=${{ needs.get-product-version.outputs.product-version }} VAULT_COMMIT=${GITHUB_SHA} GO_TAGS="${{ env.GO_TAGS }}" make build @@ -186,7 +186,7 @@ jobs: matrix: goos: [ darwin ] goarch: [ "amd64" ] - go: [ "1.15.15" ] + go: [ "1.16.12" ] fail-fast: true name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} build steps: @@ -216,7 +216,7 @@ jobs: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} CGO_ENABLED: 0 - + run: | mkdir dist out VAULT_VERSION=${{ needs.get-product-version.outputs.product-version }} VAULT_COMMIT=${GITHUB_SHA} GO_TAGS="${{ env.GO_TAGS }}" make build diff --git a/Makefile b/Makefile index 9020e93433ef3..9504caba7b46e 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ EXTERNAL_TOOLS=\ GOFMT_FILES?=$$(find . -name '*.go' | grep -v pb.go | grep -v vendor) -GO_VERSION_MIN=1.15.15 +GO_VERSION_MIN=1.16.12 GO_CMD?=go CGO_ENABLED?=0 ifneq ($(FDB_ENABLED), ) diff --git a/README.md b/README.md index be2f5246ad4b3..3bebf5e434694 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Developing Vault If you wish to work on Vault itself or any of its built-in systems, you'll first need [Go](https://www.golang.org) installed on your machine. Go version -1.15.15+ is *required*. +1.16+ is *required*. For local dev first make sure Go is properly installed, including setting up a [GOPATH](https://golang.org/doc/code.html#GOPATH). Ensure that `$GOPATH/bin` is in diff --git a/api/go.sum b/api/go.sum index 749ad0d191d33..972a6ac85298f 100644 --- a/api/go.sum +++ b/api/go.sum @@ -85,6 +85,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= diff --git a/builtin/credential/aws/backend_test.go b/builtin/credential/aws/backend_test.go index 4ea90b7b1799a..726576037da06 100644 --- a/builtin/credential/aws/backend_test.go +++ b/builtin/credential/aws/backend_test.go @@ -1125,6 +1125,22 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing } } + // Configure additional metadata to be returned for ec2 logins. + identity := map[string]interface{}{ + "ec2_metadata": []string{"instance_id", "region", "ami_id"}, + } + + // store the identity + _, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Storage: storage, + Path: "config/identity", + Data: identity, + }) + if err != nil { + t.Fatal(err) + } + loginInput := map[string]interface{}{ "pkcs7": pkcs7, "nonce": "vault-client-nonce", @@ -1237,6 +1253,7 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndAccessListIdentity(t *testing delete(loginInput, "pkcs7") loginInput["identity"] = identityDoc loginInput["signature"] = identityDocSig + resp, err = b.HandleRequest(context.Background(), loginRequest) if err != nil { t.Fatal(err) diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index 959e4493b7f56..059457abef62e 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -20,11 +20,11 @@ import ( awsClient "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" - "github.com/fullsailor/pkcs7" "github.com/hashicorp/errwrap" cleanhttp "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-retryablehttp" uuid "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/builtin/credential/aws/pkcs7" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/awsutil" "github.com/hashicorp/vault/sdk/helper/cidrutil" @@ -349,8 +349,8 @@ func (b *backend) parseIdentityDocument(ctx context.Context, s logical.Storage, // Verify extracts the authenticated attributes in the PKCS#7 signature, and verifies // the authenticity of the content using 'dsa.PublicKey' embedded in the public certificate. - if pkcs7Data.Verify() != nil { - return nil, fmt.Errorf("failed to verify the signature") + if err := pkcs7Data.Verify(); err != nil { + return nil, fmt.Errorf("failed to verify the signature: %w", err) } // Check if the signature has content inside of it diff --git a/builtin/credential/aws/pkcs7/README.md b/builtin/credential/aws/pkcs7/README.md new file mode 100644 index 0000000000000..1831a15ad021d --- /dev/null +++ b/builtin/credential/aws/pkcs7/README.md @@ -0,0 +1,5 @@ +# PKCS7 + +This code is used to verify PKCS7 signatures for the EC2 auth method. The code +was forked from [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) +and modified for Vault. \ No newline at end of file diff --git a/vendor/github.com/fullsailor/pkcs7/ber.go b/builtin/credential/aws/pkcs7/ber.go similarity index 69% rename from vendor/github.com/fullsailor/pkcs7/ber.go rename to builtin/credential/aws/pkcs7/ber.go index 89e96d30c791d..585256739c038 100644 --- a/vendor/github.com/fullsailor/pkcs7/ber.go +++ b/builtin/credential/aws/pkcs7/ber.go @@ -5,7 +5,7 @@ import ( "errors" ) -// var encodeIndent = 0 +var encodeIndent = 0 type asn1Object interface { EncodeTo(writer *bytes.Buffer) error @@ -18,7 +18,7 @@ type asn1Structured struct { func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes) - //encodeIndent++ + encodeIndent++ inner := new(bytes.Buffer) for _, obj := range s.content { err := obj.EncodeTo(inner) @@ -26,7 +26,7 @@ func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { return err } } - //encodeIndent-- + encodeIndent-- out.Write(s.tagBytes) encodeLength(out, inner.Len()) out.Write(inner.Bytes()) @@ -133,35 +133,49 @@ func encodeLength(out *bytes.Buffer, length int) (err error) { } func readObject(ber []byte, offset int) (asn1Object, int, error) { - //fmt.Printf("\n====> Starting readObject at offset: %d\n\n", offset) + berLen := len(ber) + if offset >= berLen { + return nil, 0, errors.New("ber2der: offset is after end of ber data") + } tagStart := offset b := ber[offset] offset++ + if offset >= berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } tag := b & 0x1F // last 5 bits if tag == 0x1F { tag = 0 for ber[offset] >= 0x80 { tag = tag*128 + ber[offset] - 0x80 offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } } - tag = tag*128 + ber[offset] - 0x80 + // jvehent 20170227: this doesn't appear to be used anywhere... + //tag = tag*128 + ber[offset] - 0x80 offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } } tagEnd := offset kind := b & 0x20 - /* - if kind == 0 { - fmt.Print("--> Primitive\n") - } else { - fmt.Print("--> Constructed\n") - } - */ + if kind == 0 { + debugprint("--> Primitive\n") + } else { + debugprint("--> Constructed\n") + } // read length var length int l := ber[offset] offset++ - indefinite := false + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } + hack := 0 if l > 0x80 { numberOfBytes := (int)(l & 0x7F) if numberOfBytes > 4 { // int is only guaranteed to be 32bit @@ -170,33 +184,42 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F { return nil, 0, errors.New("ber2der: BER tag length is negative") } - if 0x0 == (int)(ber[offset]) { + if (int)(ber[offset]) == 0x0 { return nil, 0, errors.New("ber2der: BER tag length has leading zero") } - //fmt.Printf("--> (compute length) indicator byte: %x\n", l) - //fmt.Printf("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) + debugprint("--> (compute length) indicator byte: %x\n", l) + debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) for i := 0; i < numberOfBytes; i++ { length = length*256 + (int)(ber[offset]) offset++ + if offset > berLen { + return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") + } } } else if l == 0x80 { - indefinite = true + // find length by searching content + markerIndex := bytes.LastIndex(ber[offset:], []byte{0x0, 0x0}) + if markerIndex == -1 { + return nil, 0, errors.New("ber2der: Invalid BER format") + } + length = markerIndex + hack = 2 + debugprint("--> (compute length) marker found at offset: %d\n", markerIndex+offset) } else { length = (int)(l) } - + if length < 0 { + return nil, 0, errors.New("ber2der: invalid negative value found in BER tag length") + } //fmt.Printf("--> length : %d\n", length) contentEnd := offset + length if contentEnd > len(ber) { return nil, 0, errors.New("ber2der: BER tag length is more than available data") } - //fmt.Printf("--> content start : %d\n", offset) - //fmt.Printf("--> content end : %d\n", contentEnd) - //fmt.Printf("--> content : % X\n", ber[offset:contentEnd]) + debugprint("--> content start : %d\n", offset) + debugprint("--> content end : %d\n", contentEnd) + debugprint("--> content : % X\n", ber[offset:contentEnd]) var obj asn1Object - if indefinite && kind == 0 { - return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding") - } if kind == 0 { obj = asn1Primitive{ tagBytes: ber[tagStart:tagEnd], @@ -205,25 +228,14 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { } } else { var subObjects []asn1Object - for (offset < contentEnd) || indefinite { + for offset < contentEnd { var subObj asn1Object var err error - subObj, offset, err = readObject(ber, offset) + subObj, offset, err = readObject(ber[:contentEnd], offset) if err != nil { return nil, 0, err } subObjects = append(subObjects, subObj) - - if indefinite { - terminated, err := isIndefiniteTermination(ber, offset) - if err != nil { - return nil, 0, err - } - - if terminated { - break - } - } } obj = asn1Structured{ tagBytes: ber[tagStart:tagEnd], @@ -231,18 +243,9 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { } } - // Apply indefinite form length with 0x0000 terminator. - if indefinite { - contentEnd = offset + 2 - } - - return obj, contentEnd, nil + return obj, contentEnd + hack, nil } -func isIndefiniteTermination(ber []byte, offset int) (bool, error) { - if len(ber) - offset < 2 { - return false, errors.New("ber2der: Invalid BER format") - } - - return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil +func debugprint(format string, a ...interface{}) { + //fmt.Printf(format, a) } diff --git a/builtin/credential/aws/pkcs7/ber_test.go b/builtin/credential/aws/pkcs7/ber_test.go new file mode 100644 index 0000000000000..fcb4b6a24f023 --- /dev/null +++ b/builtin/credential/aws/pkcs7/ber_test.go @@ -0,0 +1,62 @@ +package pkcs7 + +import ( + "bytes" + "encoding/asn1" + "strings" + "testing" +) + +func TestBer2Der(t *testing.T) { + // indefinite length fixture + ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00} + expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01} + der, err := ber2der(ber) + if err != nil { + t.Fatalf("ber2der failed with error: %v", err) + } + if !bytes.Equal(der, expected) { + t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) + } + + if der2, err := ber2der(der); err != nil { + t.Errorf("ber2der on DER bytes failed with error: %v", err) + } else { + if !bytes.Equal(der, der2) { + t.Error("ber2der is not idempotent") + } + } + var thing struct { + Number int + } + rest, err := asn1.Unmarshal(der, &thing) + if err != nil { + t.Errorf("Cannot parse resulting DER because: %v", err) + } else if len(rest) > 0 { + t.Errorf("Resulting DER has trailing data: % X", rest) + } +} + +func TestBer2Der_Negatives(t *testing.T) { + fixtures := []struct { + Input []byte + ErrorContains string + }{ + {[]byte{0x30, 0x85}, "tag length too long"}, + {[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"}, + {[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"}, + {[]byte{0x30, 0x80, 0x1, 0x2}, "Invalid BER format"}, + {[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"}, + {[]byte{0x30}, "end of ber data reached"}, + } + + for _, fixture := range fixtures { + _, err := ber2der(fixture.Input) + if err == nil { + t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains) + } + if !strings.Contains(err.Error(), fixture.ErrorContains) { + t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error()) + } + } +} diff --git a/builtin/credential/aws/pkcs7/decrypt.go b/builtin/credential/aws/pkcs7/decrypt.go new file mode 100644 index 0000000000000..0d088d6287c9c --- /dev/null +++ b/builtin/credential/aws/pkcs7/decrypt.go @@ -0,0 +1,177 @@ +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "errors" + "fmt" +) + +// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed +var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") + +// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data +var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") + +// Decrypt decrypts encrypted content info for recipient cert and private key +func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pkey crypto.PrivateKey) ([]byte, error) { + data, ok := p7.raw.(envelopedData) + if !ok { + return nil, ErrNotEncryptedContent + } + recipient := selectRecipientForCertificate(data.RecipientInfos, cert) + if recipient.EncryptedKey == nil { + return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") + } + switch pkey := pkey.(type) { + case *rsa.PrivateKey: + var contentKey []byte + contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, pkey, recipient.EncryptedKey) + if err != nil { + return nil, err + } + return data.EncryptedContentInfo.decrypt(contentKey) + } + return nil, ErrUnsupportedAlgorithm +} + +// DecryptUsingPSK decrypts encrypted data using caller provided +// pre-shared secret +func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) { + data, ok := p7.raw.(encryptedData) + if !ok { + return nil, ErrNotEncryptedContent + } + return data.EncryptedContentInfo.decrypt(key) +} + +func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { + alg := eci.ContentEncryptionAlgorithm.Algorithm + if !alg.Equal(OIDEncryptionAlgorithmDESCBC) && + !alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC) && + !alg.Equal(OIDEncryptionAlgorithmAES256CBC) && + !alg.Equal(OIDEncryptionAlgorithmAES128CBC) && + !alg.Equal(OIDEncryptionAlgorithmAES128GCM) && + !alg.Equal(OIDEncryptionAlgorithmAES256GCM) { + fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) + return nil, ErrUnsupportedAlgorithm + } + + // EncryptedContent can either be constructed of multple OCTET STRINGs + // or _be_ a tagged OCTET STRING + var cyphertext []byte + if eci.EncryptedContent.IsCompound { + // Complex case to concat all of the children OCTET STRINGs + var buf bytes.Buffer + cypherbytes := eci.EncryptedContent.Bytes + for { + var part []byte + cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) + buf.Write(part) + if cypherbytes == nil { + break + } + } + cyphertext = buf.Bytes() + } else { + // Simple case, the bytes _are_ the cyphertext + cyphertext = eci.EncryptedContent.Bytes + } + + var block cipher.Block + var err error + + switch { + case alg.Equal(OIDEncryptionAlgorithmDESCBC): + block, err = des.NewCipher(key) + case alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC): + block, err = des.NewTripleDESCipher(key) + case alg.Equal(OIDEncryptionAlgorithmAES256CBC), alg.Equal(OIDEncryptionAlgorithmAES256GCM): + fallthrough + case alg.Equal(OIDEncryptionAlgorithmAES128GCM), alg.Equal(OIDEncryptionAlgorithmAES128CBC): + block, err = aes.NewCipher(key) + } + + if err != nil { + return nil, err + } + + if alg.Equal(OIDEncryptionAlgorithmAES128GCM) || alg.Equal(OIDEncryptionAlgorithmAES256GCM) { + params := aesGCMParameters{} + paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes + + _, err := asn1.Unmarshal(paramBytes, ¶ms) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + if len(params.Nonce) != gcm.NonceSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + if params.ICVLen != gcm.Overhead() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + + plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil + } + + iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes + if len(iv) != block.BlockSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") + } + mode := cipher.NewCBCDecrypter(block, iv) + plaintext := make([]byte, len(cyphertext)) + mode.CryptBlocks(plaintext, cyphertext) + if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { + return nil, err + } + return plaintext, nil +} + +func unpad(data []byte, blocklen int) ([]byte, error) { + if blocklen < 1 { + return nil, fmt.Errorf("invalid blocklen %d", blocklen) + } + if len(data)%blocklen != 0 || len(data) == 0 { + return nil, fmt.Errorf("invalid data len %d", len(data)) + } + + // the last byte is the length of padding + padlen := int(data[len(data)-1]) + + // check padding integrity, all bytes should be the same + pad := data[len(data)-padlen:] + for _, padbyte := range pad { + if padbyte != byte(padlen) { + return nil, errors.New("invalid padding") + } + } + + return data[:len(data)-padlen], nil +} + +func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo { + for _, recp := range recipients { + if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) { + return recp + } + } + return recipientInfo{} +} diff --git a/builtin/credential/aws/pkcs7/decrypt_test.go b/builtin/credential/aws/pkcs7/decrypt_test.go new file mode 100644 index 0000000000000..f8d1592c132e6 --- /dev/null +++ b/builtin/credential/aws/pkcs7/decrypt_test.go @@ -0,0 +1,61 @@ +package pkcs7 + +import ( + "bytes" + "testing" +) + +func TestDecrypt(t *testing.T) { + fixture := UnmarshalTestFixture(EncryptedTestFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Fatal(err) + } + content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey) + if err != nil { + t.Errorf("Cannot Decrypt with error: %v", err) + } + expected := []byte("This is a test") + if !bytes.Equal(content, expected) { + t.Errorf("Decrypted result does not match.\n\tExpected:%s\n\tActual:%s", expected, content) + } +} + +// echo -n "This is a test" > test.txt +// openssl cms -encrypt -in test.txt cert.pem +var EncryptedTestFixture = ` +-----BEGIN PKCS7----- +MIIBGgYJKoZIhvcNAQcDoIIBCzCCAQcCAQAxgcwwgckCAQAwMjApMRAwDgYDVQQK +EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMA0GCSqGSIb3 +DQEBAQUABIGAyFz7bfI2noUs4FpmYfztm1pVjGyB00p9x0H3gGHEYNXdqlq8VG8d +iq36poWtEkatnwsOlURWZYECSi0g5IAL0U9sj82EN0xssZNaK0S5FTGnB3DPvYgt +HJvcKq7YvNLKMh4oqd17C6GB4oXyEBDj0vZnL7SUoCAOAWELPeC8CTUwMwYJKoZI +hvcNAQcBMBQGCCqGSIb3DQMHBAhEowTkot3a7oAQFD//J/IhFnk+JbkH7HZQFA== +-----END PKCS7----- +-----BEGIN CERTIFICATE----- +MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj +bWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0xNTA1MDYwMzU2NDBaFw0x +NjA1MDYwMzU2NDBaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBT +bm93MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK6NU0R0eiCYVquU4RcjKc +LzGfx0aa1lMr2TnLQUSeLFZHFxsyyMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg +8+Zg2r8xnnney7abxcuv0uATWSIeKlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP ++Zxp2ni5qHNraf3wE2VPIQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCAKAwCwYJKoZI +hvcNAQELA4GBAIr2F7wsqmEU/J/kLyrCgEVXgaV/sKZq4pPNnzS0tBYk8fkV3V18 +sBJyHKRLL/wFZASvzDcVGCplXyMdAOCyfd8jO3F9Ac/xdlz10RrHJT75hNu3a7/n +9KNwKhfN4A1CQv2x372oGjRhCW5bHNCWx4PIVeNzCyq/KZhyY9sxHE6f +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICXgIBAAKBgQDK6NU0R0eiCYVquU4RcjKcLzGfx0aa1lMr2TnLQUSeLFZHFxsy +yMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg8+Zg2r8xnnney7abxcuv0uATWSIe +KlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP+Zxp2ni5qHNraf3wE2VPIQIDAQAB +AoGBALyvnSt7KUquDen7nXQtvJBudnf9KFPt//OjkdHHxNZNpoF/JCSqfQeoYkeu +MdAVYNLQGMiRifzZz4dDhA9xfUAuy7lcGQcMCxEQ1dwwuFaYkawbS0Tvy2PFlq2d +H5/HeDXU4EDJ3BZg0eYj2Bnkt1sJI35UKQSxblQ0MY2q0uFBAkEA5MMOogkgUx1C +67S1tFqMUSM8D0mZB0O5vOJZC5Gtt2Urju6vywge2ArExWRXlM2qGl8afFy2SgSv +Xk5eybcEiQJBAOMRwwbEoW5NYHuFFbSJyWll4n71CYuWuQOCzehDPyTb80WFZGLV +i91kFIjeERyq88eDE5xVB3ZuRiXqaShO/9kCQQCKOEkpInaDgZSjskZvuJ47kByD +6CYsO4GIXQMMeHML8ncFH7bb6AYq5ybJVb2NTU7QLFJmfeYuhvIm+xdOreRxAkEA +o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b +dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy +KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA== +-----END PRIVATE KEY-----` diff --git a/builtin/credential/aws/pkcs7/encrypt.go b/builtin/credential/aws/pkcs7/encrypt.go new file mode 100644 index 0000000000000..6b2655708c689 --- /dev/null +++ b/builtin/credential/aws/pkcs7/encrypt.go @@ -0,0 +1,399 @@ +package pkcs7 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" +) + +type envelopedData struct { + Version int + RecipientInfos []recipientInfo `asn1:"set"` + EncryptedContentInfo encryptedContentInfo +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type recipientInfo struct { + Version int + IssuerAndSerialNumber issuerAndSerial + KeyEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedKey []byte +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent asn1.RawValue `asn1:"tag:0,optional"` +} + +const ( + // EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm + EncryptionAlgorithmDESCBC = iota + + // EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm + // Avoid this algorithm unless required for interoperability; use AES GCM instead. + EncryptionAlgorithmAES128CBC + + // EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm + // Avoid this algorithm unless required for interoperability; use AES GCM instead. + EncryptionAlgorithmAES256CBC + + // EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm + EncryptionAlgorithmAES128GCM + + // EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm + EncryptionAlgorithmAES256GCM +) + +// ContentEncryptionAlgorithm determines the algorithm used to encrypt the +// plaintext message. Change the value of this variable to change which +// algorithm is used in the Encrypt() function. +var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC + +// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt +// content with an unsupported algorithm. +var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported") + +// ErrPSKNotProvided is returned when attempting to encrypt +// using a PSK without actually providing the PSK. +var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided") + +const nonceSize = 12 + +type aesGCMParameters struct { + Nonce []byte `asn1:"tag:4"` + ICVLen int +} + +func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { + var keyLen int + var algID asn1.ObjectIdentifier + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmAES128GCM: + keyLen = 16 + algID = OIDEncryptionAlgorithmAES128GCM + case EncryptionAlgorithmAES256GCM: + keyLen = 32 + algID = OIDEncryptionAlgorithmAES256GCM + default: + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", ContentEncryptionAlgorithm) + } + if key == nil { + // Create AES key + key = make([]byte, keyLen) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + } + + // Create nonce + nonce := make([]byte, nonceSize) + + _, err := rand.Read(nonce) + if err != nil { + return nil, nil, err + } + + // Encrypt content + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + + ciphertext := gcm.Seal(nil, nonce, content, nil) + + // Prepare ASN.1 Encrypted Content Info + paramSeq := aesGCMParameters{ + Nonce: nonce, + ICVLen: gcm.Overhead(), + } + + paramBytes, err := asn1.Marshal(paramSeq) + if err != nil { + return nil, nil, err + } + + eci := encryptedContentInfo{ + ContentType: OIDData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: algID, + Parameters: asn1.RawValue{ + Tag: asn1.TagSequence, + Bytes: paramBytes, + }, + }, + EncryptedContent: marshalEncryptedContent(ciphertext), + } + + return key, &eci, nil +} + +func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { + if key == nil { + // Create DES key + key = make([]byte, 8) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + } + + // Create CBC IV + iv := make([]byte, des.BlockSize) + _, err := rand.Read(iv) + if err != nil { + return nil, nil, err + } + + // Encrypt padded content + block, err := des.NewCipher(key) + if err != nil { + return nil, nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + plaintext, err := pad(content, mode.BlockSize()) + if err != nil { + return nil, nil, err + } + cyphertext := make([]byte, len(plaintext)) + mode.CryptBlocks(cyphertext, plaintext) + + // Prepare ASN.1 Encrypted Content Info + eci := encryptedContentInfo{ + ContentType: OIDData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: OIDEncryptionAlgorithmDESCBC, + Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, + }, + EncryptedContent: marshalEncryptedContent(cyphertext), + } + + return key, &eci, nil +} + +func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { + var keyLen int + var algID asn1.ObjectIdentifier + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmAES128CBC: + keyLen = 16 + algID = OIDEncryptionAlgorithmAES128CBC + case EncryptionAlgorithmAES256CBC: + keyLen = 32 + algID = OIDEncryptionAlgorithmAES256CBC + default: + return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", ContentEncryptionAlgorithm) + } + + if key == nil { + // Create AES key + key = make([]byte, keyLen) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + } + + // Create CBC IV + iv := make([]byte, aes.BlockSize) + _, err := rand.Read(iv) + if err != nil { + return nil, nil, err + } + + // Encrypt padded content + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + mode := cipher.NewCBCEncrypter(block, iv) + plaintext, err := pad(content, mode.BlockSize()) + if err != nil { + return nil, nil, err + } + cyphertext := make([]byte, len(plaintext)) + mode.CryptBlocks(cyphertext, plaintext) + + // Prepare ASN.1 Encrypted Content Info + eci := encryptedContentInfo{ + ContentType: OIDData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: algID, + Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, + }, + EncryptedContent: marshalEncryptedContent(cyphertext), + } + + return key, &eci, nil +} + +// Encrypt creates and returns an envelope data PKCS7 structure with encrypted +// recipient keys for each recipient public key. +// +// The algorithm used to perform encryption is determined by the current value +// of the global ContentEncryptionAlgorithm package variable. By default, the +// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the +// value before calling Encrypt(). For example: +// +// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM +// +// TODO(fullsailor): Add support for encrypting content with other algorithms +func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { + var eci *encryptedContentInfo + var key []byte + var err error + + // Apply chosen symmetric encryption method + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmDESCBC: + key, eci, err = encryptDESCBC(content, nil) + case EncryptionAlgorithmAES128CBC: + fallthrough + case EncryptionAlgorithmAES256CBC: + key, eci, err = encryptAESCBC(content, nil) + case EncryptionAlgorithmAES128GCM: + fallthrough + case EncryptionAlgorithmAES256GCM: + key, eci, err = encryptAESGCM(content, nil) + + default: + return nil, ErrUnsupportedEncryptionAlgorithm + } + + if err != nil { + return nil, err + } + + // Prepare each recipient's encrypted cipher key + recipientInfos := make([]recipientInfo, len(recipients)) + for i, recipient := range recipients { + encrypted, err := encryptKey(key, recipient) + if err != nil { + return nil, err + } + ias, err := cert2issuerAndSerial(recipient) + if err != nil { + return nil, err + } + info := recipientInfo{ + Version: 0, + IssuerAndSerialNumber: ias, + KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: OIDEncryptionAlgorithmRSA, + }, + EncryptedKey: encrypted, + } + recipientInfos[i] = info + } + + // Prepare envelope content + envelope := envelopedData{ + EncryptedContentInfo: *eci, + Version: 0, + RecipientInfos: recipientInfos, + } + innerContent, err := asn1.Marshal(envelope) + if err != nil { + return nil, err + } + + // Prepare outer payload structure + wrapper := contentInfo{ + ContentType: OIDEnvelopedData, + Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, + } + + return asn1.Marshal(wrapper) +} + +// EncryptUsingPSK creates and returns an encrypted data PKCS7 structure, +// encrypted using caller provided pre-shared secret. +func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) { + var eci *encryptedContentInfo + var err error + + if key == nil { + return nil, ErrPSKNotProvided + } + + // Apply chosen symmetric encryption method + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmDESCBC: + _, eci, err = encryptDESCBC(content, key) + + case EncryptionAlgorithmAES128GCM: + fallthrough + case EncryptionAlgorithmAES256GCM: + _, eci, err = encryptAESGCM(content, key) + + default: + return nil, ErrUnsupportedEncryptionAlgorithm + } + + if err != nil { + return nil, err + } + + // Prepare encrypted-data content + ed := encryptedData{ + Version: 0, + EncryptedContentInfo: *eci, + } + innerContent, err := asn1.Marshal(ed) + if err != nil { + return nil, err + } + + // Prepare outer payload structure + wrapper := contentInfo{ + ContentType: OIDEncryptedData, + Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, + } + + return asn1.Marshal(wrapper) +} + +func marshalEncryptedContent(content []byte) asn1.RawValue { + asn1Content, _ := asn1.Marshal(content) + return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} +} + +func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) { + if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { + return rsa.EncryptPKCS1v15(rand.Reader, pub, key) + } + return nil, ErrUnsupportedAlgorithm +} + +func pad(data []byte, blocklen int) ([]byte, error) { + if blocklen < 1 { + return nil, fmt.Errorf("invalid blocklen %d", blocklen) + } + padlen := blocklen - (len(data) % blocklen) + if padlen == 0 { + padlen = blocklen + } + pad := bytes.Repeat([]byte{byte(padlen)}, padlen) + return append(data, pad...), nil +} diff --git a/builtin/credential/aws/pkcs7/encrypt_test.go b/builtin/credential/aws/pkcs7/encrypt_test.go new file mode 100644 index 0000000000000..c64381e215517 --- /dev/null +++ b/builtin/credential/aws/pkcs7/encrypt_test.go @@ -0,0 +1,102 @@ +package pkcs7 + +import ( + "bytes" + "crypto/x509" + "testing" +) + +func TestEncrypt(t *testing.T) { + modes := []int{ + EncryptionAlgorithmDESCBC, + EncryptionAlgorithmAES128CBC, + EncryptionAlgorithmAES256CBC, + EncryptionAlgorithmAES128GCM, + EncryptionAlgorithmAES256GCM, + } + sigalgs := []x509.SignatureAlgorithm{ + x509.SHA1WithRSA, + x509.SHA256WithRSA, + x509.SHA512WithRSA, + } + for _, mode := range modes { + for _, sigalg := range sigalgs { + ContentEncryptionAlgorithm = mode + + plaintext := []byte("Hello Secret World!") + cert, err := createTestCertificate(sigalg) + if err != nil { + t.Fatal(err) + } + encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate}) + if err != nil { + t.Fatal(err) + } + p7, err := Parse(encrypted) + if err != nil { + t.Fatalf("cannot Parse encrypted result: %s", err) + } + result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) + if err != nil { + t.Fatalf("cannot Decrypt encrypted result: %s", err) + } + if !bytes.Equal(plaintext, result) { + t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) + } + } + } +} + +func TestEncryptUsingPSK(t *testing.T) { + modes := []int{ + EncryptionAlgorithmDESCBC, + EncryptionAlgorithmAES128GCM, + } + + for _, mode := range modes { + ContentEncryptionAlgorithm = mode + plaintext := []byte("Hello Secret World!") + var key []byte + + switch mode { + case EncryptionAlgorithmDESCBC: + key = []byte("64BitKey") + case EncryptionAlgorithmAES128GCM: + key = []byte("128BitKey4AESGCM") + } + ciphertext, err := EncryptUsingPSK(plaintext, key) + if err != nil { + t.Fatal(err) + } + + p7, _ := Parse(ciphertext) + result, err := p7.DecryptUsingPSK(key) + if err != nil { + t.Fatalf("cannot Decrypt encrypted result: %s", err) + } + if !bytes.Equal(plaintext, result) { + t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) + } + } +} + +func TestPad(t *testing.T) { + tests := []struct { + Original []byte + Expected []byte + BlockSize int + }{ + {[]byte{0x1, 0x2, 0x3, 0x10}, []byte{0x1, 0x2, 0x3, 0x10, 0x4, 0x4, 0x4, 0x4}, 8}, + {[]byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0}, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}, 8}, + } + for _, test := range tests { + padded, err := pad(test.Original, test.BlockSize) + if err != nil { + t.Errorf("pad encountered error: %s", err) + continue + } + if !bytes.Equal(test.Expected, padded) { + t.Errorf("pad results mismatch:\n\tExpected: %X\n\tActual: %X", test.Expected, padded) + } + } +} diff --git a/builtin/credential/aws/pkcs7/pkcs7.go b/builtin/credential/aws/pkcs7/pkcs7.go new file mode 100644 index 0000000000000..ccc6cc6dfe089 --- /dev/null +++ b/builtin/credential/aws/pkcs7/pkcs7.go @@ -0,0 +1,291 @@ +// Package pkcs7 implements parsing and generation of some PKCS#7 structures. +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "sort" + + _ "crypto/sha1" // for crypto.SHA1 +) + +// PKCS7 Represents a PKCS7 structure +type PKCS7 struct { + Content []byte + Certificates []*x509.Certificate + CRLs []pkix.CertificateList + Signers []signerInfo + raw interface{} +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"explicit,optional,tag:0"` +} + +// ErrUnsupportedContentType is returned when a PKCS7 content is not supported. +// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), +// and Enveloped Data are supported (1.2.840.113549.1.7.3) +var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") + +type unsignedData []byte + +var ( + // Signed Data OIDs + OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} + OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} + OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} + OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} + OIDAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} + OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} + OIDAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} + + // Digest Algorithms + OIDDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} + OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} + OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} + OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} + + OIDDigestAlgorithmDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + + OIDDigestAlgorithmECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + + // Signature Algorithms + OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} + OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + + OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} + + // Encryption Algorithms + OIDEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} + OIDEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} + OIDEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} + OIDEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} + OIDEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} + OIDEncryptionAlgorithmAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} +) + +func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { + switch { + case oid.Equal(OIDDigestAlgorithmSHA1), oid.Equal(OIDDigestAlgorithmECDSASHA1), + oid.Equal(OIDDigestAlgorithmDSA), oid.Equal(OIDDigestAlgorithmDSASHA1), + oid.Equal(OIDEncryptionAlgorithmRSA): + return crypto.SHA1, nil + case oid.Equal(OIDDigestAlgorithmSHA256), oid.Equal(OIDDigestAlgorithmECDSASHA256): + return crypto.SHA256, nil + case oid.Equal(OIDDigestAlgorithmSHA384), oid.Equal(OIDDigestAlgorithmECDSASHA384): + return crypto.SHA384, nil + case oid.Equal(OIDDigestAlgorithmSHA512), oid.Equal(OIDDigestAlgorithmECDSASHA512): + return crypto.SHA512, nil + } + return crypto.Hash(0), ErrUnsupportedAlgorithm +} + +// getDigestOIDForSignatureAlgorithm takes an x509.SignatureAlgorithm +// and returns the corresponding OID digest algorithm +func getDigestOIDForSignatureAlgorithm(digestAlg x509.SignatureAlgorithm) (asn1.ObjectIdentifier, error) { + switch digestAlg { + case x509.SHA1WithRSA, x509.ECDSAWithSHA1: + return OIDDigestAlgorithmSHA1, nil + case x509.SHA256WithRSA, x509.ECDSAWithSHA256: + return OIDDigestAlgorithmSHA256, nil + case x509.SHA384WithRSA, x509.ECDSAWithSHA384: + return OIDDigestAlgorithmSHA384, nil + case x509.SHA512WithRSA, x509.ECDSAWithSHA512: + return OIDDigestAlgorithmSHA512, nil + } + return nil, fmt.Errorf("pkcs7: cannot convert hash to oid, unknown hash algorithm") +} + +// getOIDForEncryptionAlgorithm takes the private key type of the signer and +// the OID of a digest algorithm to return the appropriate signerInfo.DigestEncryptionAlgorithm +func getOIDForEncryptionAlgorithm(pkey crypto.PrivateKey, OIDDigestAlg asn1.ObjectIdentifier) (asn1.ObjectIdentifier, error) { + switch pkey.(type) { + case *rsa.PrivateKey: + switch { + default: + return OIDEncryptionAlgorithmRSA, nil + case OIDDigestAlg.Equal(OIDEncryptionAlgorithmRSA): + return OIDEncryptionAlgorithmRSA, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): + return OIDEncryptionAlgorithmRSASHA1, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): + return OIDEncryptionAlgorithmRSASHA256, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): + return OIDEncryptionAlgorithmRSASHA384, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): + return OIDEncryptionAlgorithmRSASHA512, nil + } + case *ecdsa.PrivateKey: + switch { + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): + return OIDDigestAlgorithmECDSASHA1, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): + return OIDDigestAlgorithmECDSASHA256, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): + return OIDDigestAlgorithmECDSASHA384, nil + case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): + return OIDDigestAlgorithmECDSASHA512, nil + } + case *dsa.PrivateKey: + return OIDDigestAlgorithmDSA, nil + } + return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown private key type %T", pkey) + +} + +// Parse decodes a DER encoded PKCS7 package +func Parse(data []byte) (p7 *PKCS7, err error) { + if len(data) == 0 { + return nil, errors.New("pkcs7: input data is empty") + } + var info contentInfo + der, err := ber2der(data) + if err != nil { + return nil, err + } + rest, err := asn1.Unmarshal(der, &info) + if len(rest) > 0 { + err = asn1.SyntaxError{Msg: "trailing data"} + return + } + if err != nil { + return + } + + // fmt.Printf("--> Content Type: %s", info.ContentType) + switch { + case info.ContentType.Equal(OIDSignedData): + return parseSignedData(info.Content.Bytes) + case info.ContentType.Equal(OIDEnvelopedData): + return parseEnvelopedData(info.Content.Bytes) + case info.ContentType.Equal(OIDEncryptedData): + return parseEncryptedData(info.Content.Bytes) + } + return nil, ErrUnsupportedContentType +} + +func parseEnvelopedData(data []byte) (*PKCS7, error) { + var ed envelopedData + if _, err := asn1.Unmarshal(data, &ed); err != nil { + return nil, err + } + return &PKCS7{ + raw: ed, + }, nil +} + +func parseEncryptedData(data []byte) (*PKCS7, error) { + var ed encryptedData + if _, err := asn1.Unmarshal(data, &ed); err != nil { + return nil, err + } + return &PKCS7{ + raw: ed, + }, nil +} + +func (raw rawCertificates) Parse() ([]*x509.Certificate, error) { + if len(raw.Raw) == 0 { + return nil, nil + } + + var val asn1.RawValue + if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil { + return nil, err + } + + return x509.ParseCertificates(val.Bytes) +} + +func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool { + return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Equal(cert.RawIssuer, ias.IssuerName.FullBytes) +} + +// Attribute represents a key value pair attribute. Value must be marshalable byte +// `encoding/asn1` +type Attribute struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +type attributes struct { + types []asn1.ObjectIdentifier + values []interface{} +} + +// Add adds the attribute, maintaining insertion order +func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) { + attrs.types = append(attrs.types, attrType) + attrs.values = append(attrs.values, value) +} + +type sortableAttribute struct { + SortKey []byte + Attribute attribute +} + +type attributeSet []sortableAttribute + +func (sa attributeSet) Len() int { + return len(sa) +} + +func (sa attributeSet) Less(i, j int) bool { + return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0 +} + +func (sa attributeSet) Swap(i, j int) { + sa[i], sa[j] = sa[j], sa[i] +} + +func (sa attributeSet) Attributes() []attribute { + attrs := make([]attribute, len(sa)) + for i, attr := range sa { + attrs[i] = attr.Attribute + } + return attrs +} + +func (attrs *attributes) ForMarshalling() ([]attribute, error) { + sortables := make(attributeSet, len(attrs.types)) + for i := range sortables { + attrType := attrs.types[i] + attrValue := attrs.values[i] + asn1Value, err := asn1.Marshal(attrValue) + if err != nil { + return nil, err + } + attr := attribute{ + Type: attrType, + Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag + } + encoded, err := asn1.Marshal(attr) + if err != nil { + return nil, err + } + sortables[i] = sortableAttribute{ + SortKey: encoded, + Attribute: attr, + } + } + sort.Sort(sortables) + return sortables.Attributes(), nil +} diff --git a/builtin/credential/aws/pkcs7/pkcs7_test.go b/builtin/credential/aws/pkcs7/pkcs7_test.go new file mode 100644 index 0000000000000..934085d427c94 --- /dev/null +++ b/builtin/credential/aws/pkcs7/pkcs7_test.go @@ -0,0 +1,326 @@ +package pkcs7 + +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "log" + "math/big" + "os" + "time" +) + +var test1024Key, test2048Key, test3072Key, test4096Key *rsa.PrivateKey + +func init() { + test1024Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("123024078101403810516614073341068864574068590522569345017786163424062310013967742924377390210586226651760719671658568413826602264886073432535341149584680111145880576802262550990305759285883150470245429547886689754596541046564560506544976611114898883158121012232676781340602508151730773214407220733898059285561"), + E: 65537, + }, + D: fromBase10("118892427340746627750435157989073921703209000249285930635312944544706203626114423392257295670807166199489096863209592887347935991101581502404113203993092422730000157893515953622392722273095289787303943046491132467130346663160540744582438810535626328230098940583296878135092036661410664695896115177534496784545"), + Primes: []*big.Int{ + fromBase10("12172745919282672373981903347443034348576729562395784527365032103134165674508405592530417723266847908118361582847315228810176708212888860333051929276459099"), + fromBase10("10106518193772789699356660087736308350857919389391620140340519320928952625438936098550728858345355053201610649202713962702543058578827268756755006576249339"), + }, + } + test1024Key.Precompute() + test2048Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557"), + E: 3, + }, + D: fromBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731"), + Primes: []*big.Int{ + fromBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433"), + fromBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029"), + }, + } + test2048Key.Precompute() + test3072Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("4799422180968749215324244710281712119910779465109490663934897082847293004098645365195947978124390029272750644394844443980065532911010718425428791498896288210928474905407341584968381379157418577471272697781778686372450913810019702928839200328075568223462554606149618941566459398862673532997592879359280754226882565483298027678735544377401276021471356093819491755877827249763065753555051973844057308627201762456191918852016986546071426986328720794061622370410645440235373576002278045257207695462423797272017386006110722769072206022723167102083033531426777518054025826800254337147514768377949097720074878744769255210076910190151785807232805749219196645305822228090875616900385866236956058984170647782567907618713309775105943700661530312800231153745705977436176908325539234432407050398510090070342851489496464612052853185583222422124535243967989533830816012180864309784486694786581956050902756173889941244024888811572094961378021"), + E: 65537, + }, + D: fromBase10("4068124900056380177006532461065648259352178312499768312132802353620854992915205894105621345694615110794369150964768050224096623567443679436821868510233726084582567244003894477723706516831312989564775159596496449435830457803384416702014837685962523313266832032687145914871879794104404800823188153886925022171560391765913739346955738372354826804228989767120353182641396181570533678315099748218734875742705419933837638038793286534641711407564379950728858267828581787483317040753987167237461567332386718574803231955771633274184646232632371006762852623964054645811527580417392163873708539175349637050049959954373319861427407953413018816604365474462455009323937599275324390953644555294418021286807661559165324810415569396577697316798600308544755741549699523972971375304826663847015905713096287495342701286542193782001358775773848824496321550110946106870685499577993864871847542645561943034990484973293461948058147956373115641615329"), + Primes: []*big.Int{ + fromBase10("2378529069722721185825622840841310902793949682948530343491428052737890236476884657507685118578733560141370511507721598189068683665232991988491561624429938984370132428230072355214627085652359350722926394699707232921674771664421591347888367477300909202851476404132163673865768760147403525700174918450753162242834161458300343282159799476695001920226357456953682236859505243928716782707623075239350380352265954107362618991716602898266999700316937680986690964564264877"), + fromBase10("2017811025336026464312837780072272578817919741496395062543647660689775637351085991504709917848745137013798005682591633910555599626950744674459976829106750083386168859581016361317479081273480343110649405858059581933773354781034946787147300862495438979895430001323443224335618577322449133208754541656374335100929456885995320929464029817626916719434010943205170760536768893924932021302887114400922813817969176636993508191950649313115712159241971065134077636674146073"), + }, + } + test3072Key.Precompute() + test4096Key = &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: fromBase10("633335480064287130853997429184971616419051348693342219741748040433588285601270210251206421401040394238592139790962887290698043839174341843721930134010306454716566698330215646704263665452264344664385995704186692432827662862845900348526672531755932642433662686500295989783595767573119607065791980381547677840410600100715146047382485989885183858757974681241303484641390718944520330953604501686666386926996348457928415093305041429178744778762826377713889019740060910363468343855830206640274442887621960581569183233822878661711798998132931623726434336448716605363514220760343097572198620479297583609779817750646169845195672483600293522186340560792255595411601450766002877850696008003794520089358819042318331840490155176019070646738739580486357084733208876620846449161909966690602374519398451042362690200166144326179405976024265116931974936425064291406950542193873313447617169603706868220189295654943247311295475722243471700112334609817776430552541319671117235957754556272646031356496763094955985615723596562217985372503002989591679252640940571608314743271809251568670314461039035793703429977801961867815257832671786542212589906513979094156334941265621017752516999186481477500481433634914622735206243841674973785078408289183000133399026553"), + E: 65537, + }, + D: fromBase10("439373650557744155078930178606343279553665694488479749802070836418412881168612407941793966086633543867614175621952769177088930851151267623886678906158545451731745754402575409204816390946376103491325109185445659065122640946673660760274557781540431107937331701243915001777636528502669576801704352961341634812275635811512806966908648671988644114352046582195051714797831307925775689566757438907578527366568747104508496278929566712224252103563340770696548181508180254674236716995730292431858611476396845443056967589437890065663497768422598977743046882539288481002449571403783500529740184608873520856954837631427724158592309018382711485601884461168736465751756282510065053161144027097169985941910909130083273691945578478173708396726266170473745329617793866669307716920992380350270584929908460462802627239204245339385636926433446418108504614031393494119344916828744888432279343816084433424594432427362258172264834429525166677273382617457205387388293888430391895615438030066428745187333897518037597413369705720436392869403948934993623418405908467147848576977008003556716087129242155836114780890054057743164411952731290520995017097151300091841286806603044227906213832083363876549637037625314539090155417589796428888619937329669464810549362433"), + Primes: []*big.Int{ + fromBase10("25745433817240673759910623230144796182285844101796353869339294232644316274580053211056707671663014355388701931204078502829809738396303142990312095225333440050808647355535878394534263839500592870406002873182360027755750148248672968563366185348499498613479490545488025779331426515670185366021612402246813511722553210128074701620113404560399242413747318161403908617342170447610792422053460359960010544593668037305465806912471260799852789913123044326555978680190904164976511331681163576833618899773550873682147782263100803907156362439021929408298804955194748640633152519828940133338948391986823456836070708197320166146761"), + fromBase10("24599914864909676687852658457515103765368967514652318497893275892114442089314173678877914038802355565271545910572804267918959612739009937926962653912943833939518967731764560204997062096919833970670512726396663920955497151415639902788974842698619579886297871162402643104696160155894685518587660015182381685605752989716946154299190561137541792784125356553411300817844325739404126956793095254412123887617931225840421856505925283322918693259047428656823141903489964287619982295891439430302405252447010728112098326033634688757933930065610737780413018498561434074501822951716586796047404555397992425143397497639322075233073"), + }, + } + test4096Key.Precompute() +} + +func fromBase10(base10 string) *big.Int { + i, ok := new(big.Int).SetString(base10, 10) + if !ok { + panic("bad number: " + base10) + } + return i +} + +type certKeyPair struct { + Certificate *x509.Certificate + PrivateKey *crypto.PrivateKey +} + +func createTestCertificate(sigAlg x509.SignatureAlgorithm) (certKeyPair, error) { + signer, err := createTestCertificateByIssuer("Eddard Stark", nil, sigAlg, true) + if err != nil { + return certKeyPair{}, err + } + pair, err := createTestCertificateByIssuer("Jon Snow", signer, sigAlg, false) + if err != nil { + return certKeyPair{}, err + } + return *pair, nil +} + +func createTestCertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509.SignatureAlgorithm, isCA bool) (*certKeyPair, error) { + var ( + err error + priv crypto.PrivateKey + derCert []byte + issuerCert *x509.Certificate + issuerKey crypto.PrivateKey + ) + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: name, + Organization: []string{"Acme Co"}, + }, + NotBefore: time.Now().Add(-1 * time.Second), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}, + } + if issuer != nil { + issuerCert = issuer.Certificate + issuerKey = *issuer.PrivateKey + } + switch sigAlg { + case x509.SHA1WithRSA: + priv = test1024Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA1WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA1 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA1 + } + case x509.SHA256WithRSA: + priv = test2048Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA256WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA256 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.SHA384WithRSA: + priv = test3072Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA384WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA384 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.SHA512WithRSA: + priv = test4096Key + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA512WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA512 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.ECDSAWithSHA1: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA1WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA1 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA1 + } + case x509.ECDSAWithSHA256: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA256WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA256 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.ECDSAWithSHA384: + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA384WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA384 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.ECDSAWithSHA512: + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA512WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA512 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA256 + } + case x509.DSAWithSHA1: + var dsaPriv dsa.PrivateKey + params := &dsaPriv.Parameters + err = dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160) + if err != nil { + return nil, err + } + err = dsa.GenerateKey(&dsaPriv, rand.Reader) + if err != nil { + return nil, err + } + switch issuerKey.(type) { + case *rsa.PrivateKey: + template.SignatureAlgorithm = x509.SHA1WithRSA + case *ecdsa.PrivateKey: + template.SignatureAlgorithm = x509.ECDSAWithSHA1 + case *dsa.PrivateKey: + template.SignatureAlgorithm = x509.DSAWithSHA1 + } + priv = &dsaPriv + } + if isCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + template.BasicConstraintsValid = true + } + if issuer == nil { + // no issuer given,make this a self-signed root cert + issuerCert = &template + issuerKey = priv + } + + log.Println("creating cert", name, "issued by", issuerCert.Subject.CommonName, "with sigalg", sigAlg) + switch priv.(type) { + case *rsa.PrivateKey: + switch issuerKey.(type) { + case *rsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) + case *ecdsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) + case *dsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) + } + case *ecdsa.PrivateKey: + switch issuerKey.(type) { + case *rsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) + case *ecdsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) + case *dsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) + } + case *dsa.PrivateKey: + pub := &priv.(*dsa.PrivateKey).PublicKey + switch issuerKey := issuerKey.(type) { + case *rsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, pub, issuerKey) + case *ecdsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) + case *dsa.PrivateKey: + derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) + } + } + if err != nil { + return nil, err + } + if len(derCert) == 0 { + return nil, fmt.Errorf("no certificate created, probably due to wrong keys. types were %T and %T", priv, issuerKey) + } + cert, err := x509.ParseCertificate(derCert) + if err != nil { + return nil, err + } + pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) + return &certKeyPair{ + Certificate: cert, + PrivateKey: &priv, + }, nil +} + +type TestFixture struct { + Input []byte + Certificate *x509.Certificate + PrivateKey *rsa.PrivateKey +} + +func UnmarshalTestFixture(testPEMBlock string) TestFixture { + var result TestFixture + var derBlock *pem.Block + var pemBlock = []byte(testPEMBlock) + for { + derBlock, pemBlock = pem.Decode(pemBlock) + if derBlock == nil { + break + } + switch derBlock.Type { + case "PKCS7": + result.Input = derBlock.Bytes + case "CERTIFICATE": + result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes) + case "PRIVATE KEY": + result.PrivateKey, _ = x509.ParsePKCS1PrivateKey(derBlock.Bytes) + } + } + + return result +} diff --git a/builtin/credential/aws/pkcs7/sign.go b/builtin/credential/aws/pkcs7/sign.go new file mode 100644 index 0000000000000..31c3654c517a8 --- /dev/null +++ b/builtin/credential/aws/pkcs7/sign.go @@ -0,0 +1,429 @@ +package pkcs7 + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "math/big" + "time" +) + +// SignedData is an opaque data structure for creating signed data payloads +type SignedData struct { + sd signedData + certs []*x509.Certificate + data, messageDigest []byte + digestOid asn1.ObjectIdentifier + encryptionOid asn1.ObjectIdentifier +} + +// NewSignedData takes data and initializes a PKCS7 SignedData struct that is +// ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default +// and can be changed by calling SetDigestAlgorithm. +func NewSignedData(data []byte) (*SignedData, error) { + content, err := asn1.Marshal(data) + if err != nil { + return nil, err + } + ci := contentInfo{ + ContentType: OIDData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, + } + sd := signedData{ + ContentInfo: ci, + Version: 1, + } + return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1}, nil +} + +// SignerInfoConfig are optional values to include when adding a signer +type SignerInfoConfig struct { + ExtraSignedAttributes []Attribute + ExtraUnsignedAttributes []Attribute +} + +type signedData struct { + Version int `asn1:"default:1"` + DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` + ContentInfo contentInfo + Certificates rawCertificates `asn1:"optional,tag:0"` + CRLs []pkix.CertificateList `asn1:"optional,tag:1"` + SignerInfos []signerInfo `asn1:"set"` +} + +type signerInfo struct { + Version int `asn1:"default:1"` + IssuerAndSerialNumber issuerAndSerial + DigestAlgorithm pkix.AlgorithmIdentifier + AuthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:0"` + DigestEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedDigest []byte + UnauthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:1"` +} + +type attribute struct { + Type asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +func marshalAttributes(attrs []attribute) ([]byte, error) { + encodedAttributes, err := asn1.Marshal(struct { + A []attribute `asn1:"set"` + }{A: attrs}) + if err != nil { + return nil, err + } + + // Remove the leading sequence octets + var raw asn1.RawValue + asn1.Unmarshal(encodedAttributes, &raw) + return raw.Bytes, nil +} + +type rawCertificates struct { + Raw asn1.RawContent +} + +type issuerAndSerial struct { + IssuerName asn1.RawValue + SerialNumber *big.Int +} + +// SetDigestAlgorithm sets the digest algorithm to be used in the signing process. +// +// This should be called before adding signers +func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) { + sd.digestOid = d +} + +// SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process. +// +// This should be called before adding signers +func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) { + sd.encryptionOid = d +} + +// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent. +func (sd *SignedData) AddSigner(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { + var parents []*x509.Certificate + return sd.AddSignerChain(ee, pkey, parents, config) +} + +// AddSignerChain signs attributes about the content and adds certificates +// and signers infos to the Signed Data. The certificate and private key +// of the end-entity signer are used to issue the signature, and any +// parent of that end-entity that need to be added to the list of +// certifications can be specified in the parents slice. +// +// The signature algorithm used to hash the data is the one of the end-entity +// certificate. +func (sd *SignedData) AddSignerChain(ee *x509.Certificate, pkey crypto.PrivateKey, parents []*x509.Certificate, config SignerInfoConfig) error { + // Following RFC 2315, 9.2 SignerInfo type, the distinguished name of + // the issuer of the end-entity signer is stored in the issuerAndSerialNumber + // section of the SignedData.SignerInfo, alongside the serial number of + // the end-entity. + var ias issuerAndSerial + ias.SerialNumber = ee.SerialNumber + if len(parents) == 0 { + // no parent, the issuer is the end-entity cert itself + ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} + } else { + err := verifyPartialChain(ee, parents) + if err != nil { + return err + } + // the first parent is the issuer + ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject} + } + sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, + pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, + ) + hash, err := getHashForOID(sd.digestOid) + if err != nil { + return err + } + h := hash.New() + h.Write(sd.data) + sd.messageDigest = h.Sum(nil) + encryptionOid, err := getOIDForEncryptionAlgorithm(pkey, sd.digestOid) + if err != nil { + return err + } + attrs := &attributes{} + attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType) + attrs.Add(OIDAttributeMessageDigest, sd.messageDigest) + attrs.Add(OIDAttributeSigningTime, time.Now().UTC()) + for _, attr := range config.ExtraSignedAttributes { + attrs.Add(attr.Type, attr.Value) + } + finalAttrs, err := attrs.ForMarshalling() + if err != nil { + return err + } + unsignedAttrs := &attributes{} + for _, attr := range config.ExtraUnsignedAttributes { + unsignedAttrs.Add(attr.Type, attr.Value) + } + finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() + if err != nil { + return err + } + // create signature of signed attributes + signature, err := signAttributes(finalAttrs, pkey, hash) + if err != nil { + return err + } + signer := signerInfo{ + AuthenticatedAttributes: finalAttrs, + UnauthenticatedAttributes: finalUnsignedAttrs, + DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: encryptionOid}, + IssuerAndSerialNumber: ias, + EncryptedDigest: signature, + Version: 1, + } + sd.certs = append(sd.certs, ee) + if len(parents) > 0 { + sd.certs = append(sd.certs, parents...) + } + sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) + return nil +} + +// SignWithoutAttr issues a signature on the content of the pkcs7 SignedData. +// Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone +// and does not include any signed attributes like timestamp and so on. +// +// This function is needed to sign old Android APKs, something you probably +// shouldn't do unless you're maintaining backward compatibility for old +// applications. +func (sd *SignedData) SignWithoutAttr(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { + var signature []byte + sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}) + hash, err := getHashForOID(sd.digestOid) + if err != nil { + return err + } + h := hash.New() + h.Write(sd.data) + sd.messageDigest = h.Sum(nil) + switch pkey := pkey.(type) { + case *dsa.PrivateKey: + // dsa doesn't implement crypto.Signer so we make a special case + // https://github.com/golang/go/issues/27889 + r, s, err := dsa.Sign(rand.Reader, pkey, sd.messageDigest) + if err != nil { + return err + } + signature, err = asn1.Marshal(dsaSignature{r, s}) + if err != nil { + return err + } + default: + key, ok := pkey.(crypto.Signer) + if !ok { + return errors.New("pkcs7: private key does not implement crypto.Signer") + } + signature, err = key.Sign(rand.Reader, sd.messageDigest, hash) + if err != nil { + return err + } + } + var ias issuerAndSerial + ias.SerialNumber = ee.SerialNumber + // no parent, the issue is the end-entity cert itself + ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} + if sd.encryptionOid == nil { + // if the encryption algorithm wasn't set by SetEncryptionAlgorithm, + // infer it from the digest algorithm + sd.encryptionOid, err = getOIDForEncryptionAlgorithm(pkey, sd.digestOid) + } + if err != nil { + return err + } + signer := signerInfo{ + DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.encryptionOid}, + IssuerAndSerialNumber: ias, + EncryptedDigest: signature, + Version: 1, + } + // create signature of signed attributes + sd.certs = append(sd.certs, ee) + sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) + return nil +} + +func (si *signerInfo) SetUnauthenticatedAttributes(extraUnsignedAttrs []Attribute) error { + unsignedAttrs := &attributes{} + for _, attr := range extraUnsignedAttrs { + unsignedAttrs.Add(attr.Type, attr.Value) + } + finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() + if err != nil { + return err + } + + si.UnauthenticatedAttributes = finalUnsignedAttrs + + return nil +} + +// AddCertificate adds the certificate to the payload. Useful for parent certificates +func (sd *SignedData) AddCertificate(cert *x509.Certificate) { + sd.certs = append(sd.certs, cert) +} + +// Detach removes content from the signed data struct to make it a detached signature. +// This must be called right before Finish() +func (sd *SignedData) Detach() { + sd.sd.ContentInfo = contentInfo{ContentType: OIDData} +} + +// GetSignedData returns the private Signed Data +func (sd *SignedData) GetSignedData() *signedData { + return &sd.sd +} + +// Finish marshals the content and its signers +func (sd *SignedData) Finish() ([]byte, error) { + sd.sd.Certificates = marshalCertificates(sd.certs) + inner, err := asn1.Marshal(sd.sd) + if err != nil { + return nil, err + } + outer := contentInfo{ + ContentType: OIDSignedData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, + } + return asn1.Marshal(outer) +} + +// RemoveAuthenticatedAttributes removes authenticated attributes from signedData +// similar to OpenSSL's PKCS7_NOATTR or -noattr flags +func (sd *SignedData) RemoveAuthenticatedAttributes() { + for i := range sd.sd.SignerInfos { + sd.sd.SignerInfos[i].AuthenticatedAttributes = nil + } +} + +// RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData +func (sd *SignedData) RemoveUnauthenticatedAttributes() { + for i := range sd.sd.SignerInfos { + sd.sd.SignerInfos[i].UnauthenticatedAttributes = nil + } +} + +// verifyPartialChain checks that a given cert is issued by the first parent in the list, +// then continue down the path. It doesn't require the last parent to be a root CA, +// or to be trusted in any truststore. It simply verifies that the chain provided, albeit +// partial, makes sense. +func verifyPartialChain(cert *x509.Certificate, parents []*x509.Certificate) error { + if len(parents) == 0 { + return fmt.Errorf("pkcs7: zero parents provided to verify the signature of certificate %q", cert.Subject.CommonName) + } + err := cert.CheckSignatureFrom(parents[0]) + if err != nil { + return fmt.Errorf("pkcs7: certificate signature from parent is invalid: %v", err) + } + if len(parents) == 1 { + // there is no more parent to check, return + return nil + } + return verifyPartialChain(parents[0], parents[1:]) +} + +func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) { + var ias issuerAndSerial + // The issuer RDNSequence has to match exactly the sequence in the certificate + // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence + ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer} + ias.SerialNumber = cert.SerialNumber + + return ias, nil +} + +// signs the DER encoded form of the attributes with the private key +func signAttributes(attrs []attribute, pkey crypto.PrivateKey, digestAlg crypto.Hash) ([]byte, error) { + attrBytes, err := marshalAttributes(attrs) + if err != nil { + return nil, err + } + h := digestAlg.New() + h.Write(attrBytes) + hash := h.Sum(nil) + + // dsa doesn't implement crypto.Signer so we make a special case + // https://github.com/golang/go/issues/27889 + switch pkey := pkey.(type) { + case *dsa.PrivateKey: + r, s, err := dsa.Sign(rand.Reader, pkey, hash) + if err != nil { + return nil, err + } + return asn1.Marshal(dsaSignature{r, s}) + } + + key, ok := pkey.(crypto.Signer) + if !ok { + return nil, errors.New("pkcs7: private key does not implement crypto.Signer") + } + return key.Sign(rand.Reader, hash, digestAlg) +} + +type dsaSignature struct { + R, S *big.Int +} + +// concats and wraps the certificates in the RawValue structure +func marshalCertificates(certs []*x509.Certificate) rawCertificates { + var buf bytes.Buffer + for _, cert := range certs { + buf.Write(cert.Raw) + } + rawCerts, _ := marshalCertificateBytes(buf.Bytes()) + return rawCerts +} + +// Even though, the tag & length are stripped out during marshalling the +// RawContent, we have to encode it into the RawContent. If its missing, +// then `asn1.Marshal()` will strip out the certificate wrapper instead. +func marshalCertificateBytes(certs []byte) (rawCertificates, error) { + var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} + b, err := asn1.Marshal(val) + if err != nil { + return rawCertificates{}, err + } + return rawCertificates{Raw: b}, nil +} + +// DegenerateCertificate creates a signed data structure containing only the +// provided certificate or certificate chain. +func DegenerateCertificate(cert []byte) ([]byte, error) { + rawCert, err := marshalCertificateBytes(cert) + if err != nil { + return nil, err + } + emptyContent := contentInfo{ContentType: OIDData} + sd := signedData{ + Version: 1, + ContentInfo: emptyContent, + Certificates: rawCert, + CRLs: []pkix.CertificateList{}, + } + content, err := asn1.Marshal(sd) + if err != nil { + return nil, err + } + signedContent := contentInfo{ + ContentType: OIDSignedData, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, + } + return asn1.Marshal(signedContent) +} diff --git a/builtin/credential/aws/pkcs7/sign_test.go b/builtin/credential/aws/pkcs7/sign_test.go new file mode 100644 index 0000000000000..0ba6324d6c2b0 --- /dev/null +++ b/builtin/credential/aws/pkcs7/sign_test.go @@ -0,0 +1,266 @@ +package pkcs7 + +import ( + "bytes" + "crypto/dsa" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "fmt" + "io/ioutil" + "log" + "math/big" + "os" + "os/exec" + "testing" +) + +func TestSign(t *testing.T) { + content := []byte("Hello World") + sigalgs := []x509.SignatureAlgorithm{ + x509.SHA1WithRSA, + x509.SHA256WithRSA, + x509.SHA512WithRSA, + x509.ECDSAWithSHA1, + x509.ECDSAWithSHA256, + x509.ECDSAWithSHA384, + x509.ECDSAWithSHA512, + } + for _, sigalgroot := range sigalgs { + rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) + if err != nil { + t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) + } + truststore := x509.NewCertPool() + truststore.AddCert(rootCert.Certificate) + for _, sigalginter := range sigalgs { + interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) + if err != nil { + t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) + } + var parents []*x509.Certificate + parents = append(parents, interCert.Certificate) + for _, sigalgsigner := range sigalgs { + signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + for _, testDetach := range []bool{false, true} { + log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach) + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + + // Set the digest to match the end entity cert + signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm) + toBeSigned.SetDigestAlgorithm(signerDigest) + + if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil { + t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + if testDetach { + toBeSigned.Detach() + } + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) + p7, err := Parse(signed) + if err != nil { + t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + if testDetach { + p7.Content = content + } + if !bytes.Equal(content, p7.Content) { + t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content) + } + if err := p7.VerifyWithChain(truststore); err != nil { + t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) + } + if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) { + t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q", + sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm) + } + } + } + } + } +} + +func TestDSASignAndVerifyWithOpenSSL(t *testing.T) { + content := []byte("Hello World") + // write the content to a temp file + tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpContentFile.Name(), content, 0755) + + block, _ := pem.Decode([]byte(dsaPublicCert)) + if block == nil { + t.Fatal("failed to parse certificate PEM") + } + signerCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatal("failed to parse certificate: " + err.Error()) + } + + // write the signer cert to a temp file + tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) + + priv := dsa.PrivateKey{ + PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"), + Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"), + G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"), + }, + }, + X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"), + } + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("test case: cannot initialize signed data: %s", err) + } + if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil { + t.Fatalf("Cannot add signer: %s", err) + } + toBeSigned.Detach() + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("test case: cannot finish signing data: %s", err) + } + + // write the signature to a temp file + tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755) + + // call openssl to verify the signature on the content using the root + opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify", + "-in", tmpSignatureFile.Name(), "-inform", "PEM", + "-content", tmpContentFile.Name()) + out, err := opensslCMD.CombinedOutput() + if err != nil { + t.Fatalf("test case: openssl command failed with %s: %s", err, out) + } + os.Remove(tmpSignatureFile.Name()) // clean up + os.Remove(tmpContentFile.Name()) // clean up + os.Remove(tmpSignerCertFile.Name()) // clean up +} + +func ExampleSignedData() { + // generate a signing cert or load a key pair + cert, err := createTestCertificate(x509.SHA256WithRSA) + if err != nil { + fmt.Printf("Cannot create test certificates: %s", err) + } + + // Initialize a SignedData struct with content to be signed + signedData, err := NewSignedData([]byte("Example data to be signed")) + if err != nil { + fmt.Printf("Cannot initialize signed data: %s", err) + } + + // Add the signing cert and private key + if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { + fmt.Printf("Cannot add signer: %s", err) + } + + // Call Detach() is you want to remove content from the signature + // and generate an S/MIME detached signature + signedData.Detach() + + // Finish() to obtain the signature bytes + detachedSignature, err := signedData.Finish() + if err != nil { + fmt.Printf("Cannot finish signing data: %s", err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature}) +} + +func TestUnmarshalSignedAttribute(t *testing.T) { + cert, err := createTestCertificate(x509.SHA512WithRSA) + if err != nil { + t.Fatal(err) + } + content := []byte("Hello World") + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("Cannot initialize signed data: %s", err) + } + oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7} + testValue := "TestValue" + if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{ + ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}}, + }); err != nil { + t.Fatalf("Cannot add signer: %s", err) + } + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("Cannot finish signing data: %s", err) + } + p7, err := Parse(signed) + if err != nil { + t.Fatalf("Cannot parse signed data: %v", err) + } + var actual string + err = p7.UnmarshalSignedAttribute(oidTest, &actual) + if err != nil { + t.Fatalf("Cannot unmarshal test value: %s", err) + } + if testValue != actual { + t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual) + } +} + +func TestDegenerateCertificate(t *testing.T) { + cert, err := createTestCertificate(x509.SHA1WithRSA) + if err != nil { + t.Fatal(err) + } + deg, err := DegenerateCertificate(cert.Certificate.Raw) + if err != nil { + t.Fatal(err) + } + testOpenSSLParse(t, deg) + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg}) +} + +// writes the cert to a temporary file and tests that openssl can read it. +func testOpenSSLParse(t *testing.T, certBytes []byte) { + tmpCertFile, err := ioutil.TempFile("", "testCertificate") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpCertFile.Name()) // clean up + + if _, err := tmpCertFile.Write(certBytes); err != nil { + t.Fatal(err) + } + + opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name()) + _, err = opensslCMD.Output() + if err != nil { + t.Fatal(err) + } + + if err := tmpCertFile.Close(); err != nil { + t.Fatal(err) + } + +} +func fromHex(s string) *big.Int { + result, ok := new(big.Int).SetString(s, 16) + if !ok { + panic(s) + } + return result +} diff --git a/builtin/credential/aws/pkcs7/verify.go b/builtin/credential/aws/pkcs7/verify.go new file mode 100644 index 0000000000000..06e78d140b1ff --- /dev/null +++ b/builtin/credential/aws/pkcs7/verify.go @@ -0,0 +1,399 @@ +package pkcs7 + +import ( + "crypto" + "crypto/dsa" + "crypto/subtle" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "time" +) + +// Verify is a wrapper around VerifyWithChain() that initializes an empty +// trust store, effectively disabling certificate verification when validating +// a signature. +func (p7 *PKCS7) Verify() (err error) { + return p7.VerifyWithChain(nil) +} + +// VerifyWithChain checks the signatures of a PKCS7 object. +// +// If truststore is not nil, it also verifies the chain of trust of +// the end-entity signer cert to one of the roots in the +// truststore. When the PKCS7 object includes the signing time +// authenticated attr verifies the chain at that time and UTC now +// otherwise. +func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error) { + if len(p7.Signers) == 0 { + return errors.New("pkcs7: Message has no signers") + } + for _, signer := range p7.Signers { + if err := verifySignature(p7, signer, truststore); err != nil { + return err + } + } + return nil +} + +// VerifyWithChainAtTime checks the signatures of a PKCS7 object. +// +// If truststore is not nil, it also verifies the chain of trust of +// the end-entity signer cert to a root in the truststore at +// currentTime. It does not use the signing time authenticated +// attribute. +func (p7 *PKCS7) VerifyWithChainAtTime(truststore *x509.CertPool, currentTime time.Time) (err error) { + if len(p7.Signers) == 0 { + return errors.New("pkcs7: Message has no signers") + } + for _, signer := range p7.Signers { + if err := verifySignatureAtTime(p7, signer, truststore, currentTime); err != nil { + return err + } + } + return nil +} + +func verifySignatureAtTime(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool, currentTime time.Time) (err error) { + signedData := p7.Content + ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) + if ee == nil { + return errors.New("pkcs7: No certificate for signer") + } + if len(signer.AuthenticatedAttributes) > 0 { + // TODO(fullsailor): First check the content type match + var ( + digest []byte + signingTime time.Time + ) + err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest) + if err != nil { + return err + } + hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) + if err != nil { + return err + } + h := hash.New() + h.Write(p7.Content) + computed := h.Sum(nil) + if subtle.ConstantTimeCompare(digest, computed) != 1 { + return &MessageDigestMismatchError{ + ExpectedDigest: digest, + ActualDigest: computed, + } + } + signedData, err = marshalAttributes(signer.AuthenticatedAttributes) + if err != nil { + return err + } + err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime) + if err == nil { + // signing time found, performing validity check + if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) { + return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q", + signingTime.Format(time.RFC3339), + ee.NotBefore.Format(time.RFC3339), + ee.NotAfter.Format(time.RFC3339)) + } + } + } + if truststore != nil { + _, err = verifyCertChain(ee, p7.Certificates, truststore, currentTime) + if err != nil { + return err + } + } + sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm) + if err != nil { + return err + } + switch sigalg { + case x509.DSAWithSHA1, x509.DSAWithSHA256: + return dsaCheckSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey) + default: + return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest) + } +} + +// dsaSignature verifies the DSA signature on a PKCS7 document. DSA support was +// removed from Go's crypto/x509 support prior to Go 1.16. This allows +// verifying legacy signatures until affected applications can be migrated off +// of DSA. +func dsaCheckSignature(algo x509.SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) error { + dsaKey, ok := publicKey.(*dsa.PublicKey) + if !ok { + return ErrUnsupportedAlgorithm + } + + var hashType crypto.Hash + switch algo { + case x509.DSAWithSHA1: + hashType = crypto.SHA1 + case x509.DSAWithSHA256: + hashType = crypto.SHA256 + default: + return ErrUnsupportedAlgorithm + } + h := hashType.New() + h.Write(signed) + signed = h.Sum(nil) + + dsaSig := new(dsaSignature) + if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil { + return err + } else if len(rest) != 0 { + return errors.New("x509: trailing data after DSA signature") + } + if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { + return errors.New("x509: DSA signature contained zero or negative values") + } + // According to FIPS 186-3, section 4.6, the hash must be truncated if it is longer + // than the key length, but crypto/dsa doesn't do it automatically. + if maxHashLen := dsaKey.Q.BitLen() / 8; maxHashLen < len(signed) { + signed = signed[:maxHashLen] + } + if !dsa.Verify(dsaKey, signed, dsaSig.R, dsaSig.S) { + return errors.New("x509: DSA verification failure") + } + return nil +} + +func verifySignature(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool) (err error) { + signedData := p7.Content + ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) + if ee == nil { + return errors.New("pkcs7: No certificate for signer") + } + signingTime := time.Now().UTC() + if len(signer.AuthenticatedAttributes) > 0 { + // TODO(fullsailor): First check the content type match + var digest []byte + err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest) + if err != nil { + return err + } + hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) + if err != nil { + return err + } + h := hash.New() + h.Write(p7.Content) + computed := h.Sum(nil) + if subtle.ConstantTimeCompare(digest, computed) != 1 { + return &MessageDigestMismatchError{ + ExpectedDigest: digest, + ActualDigest: computed, + } + } + signedData, err = marshalAttributes(signer.AuthenticatedAttributes) + if err != nil { + return err + } + err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime) + if err == nil { + // signing time found, performing validity check + if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) { + return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q", + signingTime.Format(time.RFC3339), + ee.NotBefore.Format(time.RFC3339), + ee.NotAfter.Format(time.RFC3339)) + } + } + } + if truststore != nil { + _, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime) + if err != nil { + return err + } + } + sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm) + if err != nil { + return err + } + + switch sigalg { + case x509.DSAWithSHA1, x509.DSAWithSHA256: + return dsaCheckSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey) + default: + return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest) + } +} + +// GetOnlySigner returns an x509.Certificate for the first signer of the signed +// data payload. If there are more or less than one signer, nil is returned +func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { + if len(p7.Signers) != 1 { + return nil + } + signer := p7.Signers[0] + return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) +} + +// UnmarshalSignedAttribute decodes a single attribute from the signer info +func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error { + sd, ok := p7.raw.(signedData) + if !ok { + return errors.New("pkcs7: payload is not signedData content") + } + if len(sd.SignerInfos) < 1 { + return errors.New("pkcs7: payload has no signers") + } + attributes := sd.SignerInfos[0].AuthenticatedAttributes + return unmarshalAttribute(attributes, attributeType, out) +} + +func parseSignedData(data []byte) (*PKCS7, error) { + var sd signedData + asn1.Unmarshal(data, &sd) + certs, err := sd.Certificates.Parse() + if err != nil { + return nil, err + } + // fmt.Printf("--> Signed Data Version %d\n", sd.Version) + + var compound asn1.RawValue + var content unsignedData + + // The Content.Bytes maybe empty on PKI responses. + if len(sd.ContentInfo.Content.Bytes) > 0 { + if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { + return nil, err + } + } + // Compound octet string + if compound.IsCompound { + if compound.Tag == 4 { + if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { + return nil, err + } + } else { + content = compound.Bytes + } + } else { + // assuming this is tag 04 + content = compound.Bytes + } + return &PKCS7{ + Content: content, + Certificates: certs, + CRLs: sd.CRLs, + Signers: sd.SignerInfos, + raw: sd}, nil +} + +// verifyCertChain takes an end-entity certs, a list of potential intermediates and a +// truststore, and built all potential chains between the EE and a trusted root. +// +// When verifying chains that may have expired, currentTime can be set to a past date +// to allow the verification to pass. If unset, currentTime is set to the current UTC time. +func verifyCertChain(ee *x509.Certificate, certs []*x509.Certificate, truststore *x509.CertPool, currentTime time.Time) (chains [][]*x509.Certificate, err error) { + intermediates := x509.NewCertPool() + for _, intermediate := range certs { + intermediates.AddCert(intermediate) + } + verifyOptions := x509.VerifyOptions{ + Roots: truststore, + Intermediates: intermediates, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + CurrentTime: currentTime, + } + chains, err = ee.Verify(verifyOptions) + if err != nil { + return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err) + } + return +} + +// MessageDigestMismatchError is returned when the signer data digest does not +// match the computed digest for the contained content +type MessageDigestMismatchError struct { + ExpectedDigest []byte + ActualDigest []byte +} + +func (err *MessageDigestMismatchError) Error() string { + return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest) +} + +func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) { + switch { + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1): + return x509.ECDSAWithSHA1, nil + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256): + return x509.ECDSAWithSHA256, nil + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384): + return x509.ECDSAWithSHA384, nil + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512): + return x509.ECDSAWithSHA512, nil + case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512): + switch { + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): + return x509.SHA1WithRSA, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): + return x509.SHA256WithRSA, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): + return x509.SHA384WithRSA, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): + return x509.SHA512WithRSA, nil + default: + return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", + digest.Algorithm.String(), digestEncryption.Algorithm.String()) + } + case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA), + digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1): + switch { + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): + return x509.DSAWithSHA1, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): + return x509.DSAWithSHA256, nil + default: + return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", + digest.Algorithm.String(), digestEncryption.Algorithm.String()) + } + case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384), + digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521): + switch { + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): + return x509.ECDSAWithSHA1, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): + return x509.ECDSAWithSHA256, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): + return x509.ECDSAWithSHA384, nil + case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): + return x509.ECDSAWithSHA512, nil + default: + return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", + digest.Algorithm.String(), digestEncryption.Algorithm.String()) + } + default: + return -1, fmt.Errorf("pkcs7: unsupported algorithm %q", + digestEncryption.Algorithm.String()) + } +} + +func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate { + for _, cert := range certs { + if isCertMatchForIssuerAndSerial(cert, ias) { + return cert + } + } + return nil +} + +func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error { + for _, attr := range attrs { + if attr.Type.Equal(attributeType) { + _, err := asn1.Unmarshal(attr.Value.Bytes, out) + return err + } + } + return errors.New("pkcs7: attribute type not in attributes") +} diff --git a/builtin/credential/aws/pkcs7/verify_test_dsa.go b/builtin/credential/aws/pkcs7/verify_test_dsa.go new file mode 100644 index 0000000000000..1eb05bc3eae66 --- /dev/null +++ b/builtin/credential/aws/pkcs7/verify_test_dsa.go @@ -0,0 +1,182 @@ +// +build go1.11 go1.12 go1.13 go1.14 go1.15 + +package pkcs7 + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + "os/exec" + "testing" +) + +func TestVerifyEC2(t *testing.T) { + fixture := UnmarshalDSATestFixture(EC2IdentityDocumentFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + p7.Certificates = []*x509.Certificate{fixture.Certificate} + if err := p7.Verify(); err != nil { + t.Errorf("Verify failed with error: %v", err) + } +} + +var EC2IdentityDocumentFixture = ` +-----BEGIN PKCS7----- +MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA +JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh +eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1 +cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh +bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs +bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg +OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK +ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj +aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy +YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA +AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n +dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi +IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B +CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG +CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w +LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA +AAAAAA== +-----END PKCS7----- +-----BEGIN CERTIFICATE----- +MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw +FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD +VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z +ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u +IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl +cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e +ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3 +VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P +hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j +k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U +hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF +lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf +MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW +MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw +vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw +7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K +-----END CERTIFICATE-----` + +func TestDSASignWithOpenSSLAndVerify(t *testing.T) { + content := []byte(` +A ship in port is safe, +but that's not what ships are built for. +-- Grace Hopper`) + // write the content to a temp file + tmpContentFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_content") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpContentFile.Name(), content, 0755) + + // write the signer cert to a temp file + tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signer") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) + + // write the signer key to a temp file + tmpSignerKeyFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_key") + if err != nil { + t.Fatal(err) + } + ioutil.WriteFile(tmpSignerKeyFile.Name(), dsaPrivateKey, 0755) + + tmpSignedFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signature") + if err != nil { + t.Fatal(err) + } + // call openssl to sign the content + opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", "-md", "sha1", + "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), + "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), + "-certfile", tmpSignerCertFile.Name(), "-outform", "PEM") + out, err := opensslCMD.CombinedOutput() + if err != nil { + t.Fatalf("openssl command failed with %s: %s", err, out) + } + + // verify the signed content + pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) + if err != nil { + t.Fatal(err) + } + fmt.Printf("%s\n", pemSignature) + derBlock, _ := pem.Decode(pemSignature) + if derBlock == nil { + t.Fatalf("failed to read DER block from signature PEM %s", tmpSignedFile.Name()) + } + p7, err := Parse(derBlock.Bytes) + if err != nil { + t.Fatalf("Parse encountered unexpected error: %v", err) + } + if err := p7.Verify(); err != nil { + t.Fatalf("Verify failed with error: %v", err) + } + os.Remove(tmpSignerCertFile.Name()) // clean up + os.Remove(tmpSignerKeyFile.Name()) // clean up + os.Remove(tmpContentFile.Name()) // clean up +} + +var dsaPrivateKey = []byte(`-----BEGIN PRIVATE KEY----- +MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS +PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl +pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith +1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L +vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3 +zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo +g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUfW4aPdQBn9gJZp2KuNpzgHzvfsE= +-----END PRIVATE KEY-----`) + +var dsaPublicCert = []byte(`-----BEGIN CERTIFICATE----- +MIIDOjCCAvWgAwIBAgIEPCY/UDANBglghkgBZQMEAwIFADBsMRAwDgYDVQQGEwdV +bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD +VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du +MB4XDTE4MTAyMjEzNDMwN1oXDTQ2MDMwOTEzNDMwN1owbDEQMA4GA1UEBhMHVW5r +bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE +ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC +AbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD +Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE +exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii +Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4 +V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI +puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl +nwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDCriMPbEVBoRK4SOUeFwg7+VRf4TTp +rcOQC9IVVoCjXzuWEGrp3ZI7YWJSpFnSch4lk29RH8O0HpI/NOzKnOBtnKr782pt +1k/bJVMH9EaLd6MKnAVjrCDMYBB0MhebZ8QHY2elZZCWoqDYAcIDOsEx+m4NLErT +ypPnjS5M0jm1PKMhMB8wHQYDVR0OBBYEFC0Yt5XdM0Kc95IX8NQ8XRssGPx7MA0G +CWCGSAFlAwQDAgUAAzAAMC0CFQCIgQtrZZ9hdZG1ROhR5hc8nYEmbgIUAIlgC688 +qzy/7yePTlhlpj+ahMM= +-----END CERTIFICATE-----`) + +type DSATestFixture struct { + Input []byte + Certificate *x509.Certificate +} + +func UnmarshalDSATestFixture(testPEMBlock string) DSATestFixture { + var result DSATestFixture + var derBlock *pem.Block + var pemBlock = []byte(testPEMBlock) + for { + derBlock, pemBlock = pem.Decode(pemBlock) + if derBlock == nil { + break + } + switch derBlock.Type { + case "PKCS7": + result.Input = derBlock.Bytes + case "CERTIFICATE": + result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes) + } + } + + return result +} diff --git a/changelog/12340.txt b/changelog/12340.txt new file mode 100644 index 0000000000000..f40253a672cdd --- /dev/null +++ b/changelog/12340.txt @@ -0,0 +1,3 @@ +```release-note: bug +auth/aws: Fixes ec2 login no longer supporting DSA signature verification +``` diff --git a/changelog/12371.txt b/changelog/12371.txt new file mode 100644 index 0000000000000..eb36e1d1603b9 --- /dev/null +++ b/changelog/12371.txt @@ -0,0 +1,3 @@ +```release-note: bug +identity: Fix a panic on arm64 platform when doing identity I/O. +``` \ No newline at end of file diff --git a/changelog/13422.txt b/changelog/13422.txt new file mode 100644 index 0000000000000..8b29f89340604 --- /dev/null +++ b/changelog/13422.txt @@ -0,0 +1,3 @@ +```release-note:change +go: Update go version to 1.16.12 +``` diff --git a/go.mod b/go.mod index b969972a8f87d..3d93e121767c2 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,6 @@ require ( github.com/elazarl/go-bindata-assetfs v1.0.1-0.20200509193318-234c15e7648f github.com/fatih/color v1.11.0 github.com/fatih/structs v1.1.0 - github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-errors/errors v1.0.1 github.com/go-ldap/ldap/v3 v3.2.4 @@ -51,6 +50,7 @@ require ( github.com/go-test/deep v1.0.7 github.com/gocql/gocql v0.0.0-20210401103645-80ab1e13e309 github.com/golang/protobuf v1.4.2 + github.com/google/go-cmp v0.5.5 github.com/google/go-github v17.0.0+incompatible github.com/google/go-metrics-stackdriver v0.2.0 github.com/hashicorp/consul-template v0.25.2 diff --git a/go.sum b/go.sum index ea75d5c846166..003c3d76face7 100644 --- a/go.sum +++ b/go.sum @@ -352,8 +352,6 @@ github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8 github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7 h1:D2LrfOPgGHQprIxmsTpxtzhpmF66HoM6rXSmcqaX7h8= github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7/go.mod h1:GeIq9qoE43YdGnDXURnmKTnGg15pQz4mYkXSTChbneI= github.com/gammazero/workerpool v0.0.0-20190406235159-88d534f22b56 h1:VzbudKn/nvxYKOdzgkEBS6SSreRjAgoJ+ZeS4wPFkgc= @@ -477,6 +475,8 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= diff --git a/plugins/database/mongodb/mongodb_test.go b/plugins/database/mongodb/mongodb_test.go index cb088892487de..832b0ce1f7d16 100644 --- a/plugins/database/mongodb/mongodb_test.go +++ b/plugins/database/mongodb/mongodb_test.go @@ -7,9 +7,13 @@ import ( "fmt" "reflect" "strings" + "sync" "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/vault/helper/testhelpers/certhelpers" "github.com/hashicorp/vault/helper/testhelpers/mongodb" dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" @@ -346,9 +350,7 @@ func TestGetTLSAuth(t *testing.T) { if !test.expectErr && err != nil { t.Fatalf("no error expected, got: %s", err) } - if !reflect.DeepEqual(actual, test.expectOpts) { - t.Fatalf("Actual:\n%#v\nExpected:\n%#v", actual, test.expectOpts) - } + assertDeepEqual(t, test.expectOpts, actual) }) } } @@ -363,6 +365,27 @@ func appendToCertPool(t *testing.T, pool *x509.CertPool, caPem []byte) *x509.Cer return pool } +var cmpClientOptionsOpts = cmp.Options{ + cmp.AllowUnexported(options.ClientOptions{}), + + cmp.AllowUnexported(tls.Config{}), + cmpopts.IgnoreTypes(sync.Mutex{}, sync.RWMutex{}), + + // 'lazyCerts' has a func field which can't be compared. + cmpopts.IgnoreFields(x509.CertPool{}, "lazyCerts"), + cmp.AllowUnexported(x509.CertPool{}), +} + +// Need a special comparison for ClientOptions because reflect.DeepEquals won't work in Go 1.16. +// See: https://github.com/golang/go/issues/45891 +func assertDeepEqual(t *testing.T, a, b *options.ClientOptions) { + t.Helper() + + if diff := cmp.Diff(a, b, cmpClientOptionsOpts); diff != "" { + t.Fatalf("assertion failed: values are not equal\n--- expected\n+++ actual\n%v", diff) + } +} + func createDBUser(t testing.TB, connURL, db, username, password string) { t.Helper() diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile index 9454f3096ed3b..56c329af36276 100644 --- a/scripts/docker/Dockerfile +++ b/scripts/docker/Dockerfile @@ -1,7 +1,7 @@ # Multi-stage builder to avoid polluting users environment with wrong # architecture binaries. Since this binary is used in an alpine container, # we're explicitly compiling for 'linux/amd64' -ARG VERSION=1.15.15 +ARG VERSION=1.16.12 FROM golang:${VERSION} AS builder diff --git a/scripts/docker/Dockerfile.ui b/scripts/docker/Dockerfile.ui index b7e1fde22c0a6..833762f44be66 100644 --- a/scripts/docker/Dockerfile.ui +++ b/scripts/docker/Dockerfile.ui @@ -3,7 +3,7 @@ # we're explicitly compiling for 'linux/amd64' FROM debian:buster AS builder -ARG VERSION=1.15.15 +ARG VERSION=1.16.12 ARG CGO_ENABLED=0 ARG BUILD_TAGS ENV JOBS=2 diff --git a/sdk/go.mod b/sdk/go.mod index 1c40f11b2285c..89cc27353b822 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -18,7 +18,7 @@ require ( github.com/go-ldap/ldap/v3 v3.1.10 github.com/go-test/deep v1.0.2 github.com/golang/protobuf v1.4.2 - github.com/golang/snappy v0.0.1 + github.com/golang/snappy v0.0.4 github.com/gorilla/mux v1.7.4 // indirect github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/go-cleanhttp v0.5.1 diff --git a/sdk/go.sum b/sdk/go.sum index 259bd7dd17f81..91488083a8d28 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -117,6 +117,8 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= diff --git a/vendor/github.com/fullsailor/pkcs7/.gitignore b/vendor/github.com/fullsailor/pkcs7/.gitignore deleted file mode 100644 index daf913b1b347a..0000000000000 --- a/vendor/github.com/fullsailor/pkcs7/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/vendor/github.com/fullsailor/pkcs7/.travis.yml b/vendor/github.com/fullsailor/pkcs7/.travis.yml deleted file mode 100644 index bc1204376304d..0000000000000 --- a/vendor/github.com/fullsailor/pkcs7/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go - -go: - - 1.8 - - 1.9 - - "1.10" - - tip diff --git a/vendor/github.com/fullsailor/pkcs7/LICENSE b/vendor/github.com/fullsailor/pkcs7/LICENSE deleted file mode 100644 index 75f3209085b8e..0000000000000 --- a/vendor/github.com/fullsailor/pkcs7/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Andrew Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/vendor/github.com/fullsailor/pkcs7/README.md b/vendor/github.com/fullsailor/pkcs7/README.md deleted file mode 100644 index bfd948f321a88..0000000000000 --- a/vendor/github.com/fullsailor/pkcs7/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# pkcs7 - -[![GoDoc](https://godoc.org/github.com/fullsailor/pkcs7?status.svg)](https://godoc.org/github.com/fullsailor/pkcs7) -[![Build Status](https://travis-ci.org/fullsailor/pkcs7.svg?branch=master)](https://travis-ci.org/fullsailor/pkcs7) - -pkcs7 implements parsing and creating signed and enveloped messages. - -- Documentation on [GoDoc](http://godoc.org/github.com/fullsailor/pkcs7) diff --git a/vendor/github.com/fullsailor/pkcs7/pkcs7.go b/vendor/github.com/fullsailor/pkcs7/pkcs7.go deleted file mode 100644 index 0264466b467e2..0000000000000 --- a/vendor/github.com/fullsailor/pkcs7/pkcs7.go +++ /dev/null @@ -1,962 +0,0 @@ -// Package pkcs7 implements parsing and generation of some PKCS#7 structures. -package pkcs7 - -import ( - "bytes" - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/hmac" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "errors" - "fmt" - "math/big" - "sort" - "time" - - _ "crypto/sha1" // for crypto.SHA1 -) - -// PKCS7 Represents a PKCS7 structure -type PKCS7 struct { - Content []byte - Certificates []*x509.Certificate - CRLs []pkix.CertificateList - Signers []signerInfo - raw interface{} -} - -type contentInfo struct { - ContentType asn1.ObjectIdentifier - Content asn1.RawValue `asn1:"explicit,optional,tag:0"` -} - -// ErrUnsupportedContentType is returned when a PKCS7 content is not supported. -// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), -// and Enveloped Data are supported (1.2.840.113549.1.7.3) -var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") - -type unsignedData []byte - -var ( - oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} - oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} - oidEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} - oidSignedAndEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4} - oidDigestedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5} - oidEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} - oidAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} - oidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} - oidAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} -) - -type signedData struct { - Version int `asn1:"default:1"` - DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` - ContentInfo contentInfo - Certificates rawCertificates `asn1:"optional,tag:0"` - CRLs []pkix.CertificateList `asn1:"optional,tag:1"` - SignerInfos []signerInfo `asn1:"set"` -} - -type rawCertificates struct { - Raw asn1.RawContent -} - -type envelopedData struct { - Version int - RecipientInfos []recipientInfo `asn1:"set"` - EncryptedContentInfo encryptedContentInfo -} - -type recipientInfo struct { - Version int - IssuerAndSerialNumber issuerAndSerial - KeyEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedKey []byte -} - -type encryptedContentInfo struct { - ContentType asn1.ObjectIdentifier - ContentEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedContent asn1.RawValue `asn1:"tag:0,optional"` -} - -type attribute struct { - Type asn1.ObjectIdentifier - Value asn1.RawValue `asn1:"set"` -} - -type issuerAndSerial struct { - IssuerName asn1.RawValue - SerialNumber *big.Int -} - -// MessageDigestMismatchError is returned when the signer data digest does not -// match the computed digest for the contained content -type MessageDigestMismatchError struct { - ExpectedDigest []byte - ActualDigest []byte -} - -func (err *MessageDigestMismatchError) Error() string { - return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest) -} - -type signerInfo struct { - Version int `asn1:"default:1"` - IssuerAndSerialNumber issuerAndSerial - DigestAlgorithm pkix.AlgorithmIdentifier - AuthenticatedAttributes []attribute `asn1:"optional,tag:0"` - DigestEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedDigest []byte - UnauthenticatedAttributes []attribute `asn1:"optional,tag:1"` -} - -// Parse decodes a DER encoded PKCS7 package -func Parse(data []byte) (p7 *PKCS7, err error) { - if len(data) == 0 { - return nil, errors.New("pkcs7: input data is empty") - } - var info contentInfo - der, err := ber2der(data) - if err != nil { - return nil, err - } - rest, err := asn1.Unmarshal(der, &info) - if len(rest) > 0 { - err = asn1.SyntaxError{Msg: "trailing data"} - return - } - if err != nil { - return - } - - // fmt.Printf("--> Content Type: %s", info.ContentType) - switch { - case info.ContentType.Equal(oidSignedData): - return parseSignedData(info.Content.Bytes) - case info.ContentType.Equal(oidEnvelopedData): - return parseEnvelopedData(info.Content.Bytes) - } - return nil, ErrUnsupportedContentType -} - -func parseSignedData(data []byte) (*PKCS7, error) { - var sd signedData - asn1.Unmarshal(data, &sd) - certs, err := sd.Certificates.Parse() - if err != nil { - return nil, err - } - // fmt.Printf("--> Signed Data Version %d\n", sd.Version) - - var compound asn1.RawValue - var content unsignedData - - // The Content.Bytes maybe empty on PKI responses. - if len(sd.ContentInfo.Content.Bytes) > 0 { - if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { - return nil, err - } - } - // Compound octet string - if compound.IsCompound { - if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { - return nil, err - } - } else { - // assuming this is tag 04 - content = compound.Bytes - } - return &PKCS7{ - Content: content, - Certificates: certs, - CRLs: sd.CRLs, - Signers: sd.SignerInfos, - raw: sd}, nil -} - -func (raw rawCertificates) Parse() ([]*x509.Certificate, error) { - if len(raw.Raw) == 0 { - return nil, nil - } - - var val asn1.RawValue - if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil { - return nil, err - } - - return x509.ParseCertificates(val.Bytes) -} - -func parseEnvelopedData(data []byte) (*PKCS7, error) { - var ed envelopedData - if _, err := asn1.Unmarshal(data, &ed); err != nil { - return nil, err - } - return &PKCS7{ - raw: ed, - }, nil -} - -// Verify checks the signatures of a PKCS7 object -// WARNING: Verify does not check signing time or verify certificate chains at -// this time. -func (p7 *PKCS7) Verify() (err error) { - if len(p7.Signers) == 0 { - return errors.New("pkcs7: Message has no signers") - } - for _, signer := range p7.Signers { - if err := verifySignature(p7, signer); err != nil { - return err - } - } - return nil -} - -func verifySignature(p7 *PKCS7, signer signerInfo) error { - signedData := p7.Content - hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) - if err != nil { - return err - } - if len(signer.AuthenticatedAttributes) > 0 { - // TODO(fullsailor): First check the content type match - var digest []byte - err := unmarshalAttribute(signer.AuthenticatedAttributes, oidAttributeMessageDigest, &digest) - if err != nil { - return err - } - h := hash.New() - h.Write(p7.Content) - computed := h.Sum(nil) - if !hmac.Equal(digest, computed) { - return &MessageDigestMismatchError{ - ExpectedDigest: digest, - ActualDigest: computed, - } - } - // TODO(fullsailor): Optionally verify certificate chain - // TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore - signedData, err = marshalAttributes(signer.AuthenticatedAttributes) - if err != nil { - return err - } - } - cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) - if cert == nil { - return errors.New("pkcs7: No certificate for signer") - } - - algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm) - if algo == x509.UnknownSignatureAlgorithm { - // I'm not sure what the spec here is, and the openssl sources were not - // helpful. But, this is what App Store receipts appear to do. - // The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)" - // But we're expecting a digest + encryption algorithm. So... we're going - // to determine an algorithm based on the DigestAlgorithm and this - // encryption algorithm. - if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) { - algo = getRSASignatureAlgorithmForDigestAlgorithm(hash) - } - } - return cert.CheckSignature(algo, signedData, signer.EncryptedDigest) -} - -func marshalAttributes(attrs []attribute) ([]byte, error) { - encodedAttributes, err := asn1.Marshal(struct { - A []attribute `asn1:"set"` - }{A: attrs}) - if err != nil { - return nil, err - } - - // Remove the leading sequence octets - var raw asn1.RawValue - asn1.Unmarshal(encodedAttributes, &raw) - return raw.Bytes, nil -} - -var ( - oidDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} - oidEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} -) - -func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate { - for _, cert := range certs { - if isCertMatchForIssuerAndSerial(cert, ias) { - return cert - } - } - return nil -} - -func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { - switch { - case oid.Equal(oidDigestAlgorithmSHA1): - return crypto.SHA1, nil - case oid.Equal(oidSHA256): - return crypto.SHA256, nil - } - return crypto.Hash(0), ErrUnsupportedAlgorithm -} - -func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm { - for _, details := range signatureAlgorithmDetails { - if details.pubKeyAlgo == x509.RSA && details.hash == hash { - return details.algo - } - } - return x509.UnknownSignatureAlgorithm -} - -// GetOnlySigner returns an x509.Certificate for the first signer of the signed -// data payload. If there are more or less than one signer, nil is returned -func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { - if len(p7.Signers) != 1 { - return nil - } - signer := p7.Signers[0] - return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) -} - -// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed -var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") - -// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data -var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") - -// Decrypt decrypts encrypted content info for recipient cert and private key -func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, error) { - data, ok := p7.raw.(envelopedData) - if !ok { - return nil, ErrNotEncryptedContent - } - recipient := selectRecipientForCertificate(data.RecipientInfos, cert) - if recipient.EncryptedKey == nil { - return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") - } - if priv := pk.(*rsa.PrivateKey); priv != nil { - var contentKey []byte - contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, recipient.EncryptedKey) - if err != nil { - return nil, err - } - return data.EncryptedContentInfo.decrypt(contentKey) - } - fmt.Printf("Unsupported Private Key: %v\n", pk) - return nil, ErrUnsupportedAlgorithm -} - -var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} -var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} -var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} -var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} -var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} - -func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { - alg := eci.ContentEncryptionAlgorithm.Algorithm - if !alg.Equal(oidEncryptionAlgorithmDESCBC) && - !alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) && - !alg.Equal(oidEncryptionAlgorithmAES256CBC) && - !alg.Equal(oidEncryptionAlgorithmAES128CBC) && - !alg.Equal(oidEncryptionAlgorithmAES128GCM) { - fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) - return nil, ErrUnsupportedAlgorithm - } - - // EncryptedContent can either be constructed of multple OCTET STRINGs - // or _be_ a tagged OCTET STRING - var cyphertext []byte - if eci.EncryptedContent.IsCompound { - // Complex case to concat all of the children OCTET STRINGs - var buf bytes.Buffer - cypherbytes := eci.EncryptedContent.Bytes - for { - var part []byte - cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) - buf.Write(part) - if cypherbytes == nil { - break - } - } - cyphertext = buf.Bytes() - } else { - // Simple case, the bytes _are_ the cyphertext - cyphertext = eci.EncryptedContent.Bytes - } - - var block cipher.Block - var err error - - switch { - case alg.Equal(oidEncryptionAlgorithmDESCBC): - block, err = des.NewCipher(key) - case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC): - block, err = des.NewTripleDESCipher(key) - case alg.Equal(oidEncryptionAlgorithmAES256CBC): - fallthrough - case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC): - block, err = aes.NewCipher(key) - } - - if err != nil { - return nil, err - } - - if alg.Equal(oidEncryptionAlgorithmAES128GCM) { - params := aesGCMParameters{} - paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes - - _, err := asn1.Unmarshal(paramBytes, ¶ms) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - if len(params.Nonce) != gcm.NonceSize() { - return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") - } - if params.ICVLen != gcm.Overhead() { - return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") - } - - plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil - } - - iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes - if len(iv) != block.BlockSize() { - return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") - } - mode := cipher.NewCBCDecrypter(block, iv) - plaintext := make([]byte, len(cyphertext)) - mode.CryptBlocks(plaintext, cyphertext) - if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { - return nil, err - } - return plaintext, nil -} - -func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo { - for _, recp := range recipients { - if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) { - return recp - } - } - return recipientInfo{} -} - -func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool { - return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Compare(cert.RawIssuer, ias.IssuerName.FullBytes) == 0 -} - -func pad(data []byte, blocklen int) ([]byte, error) { - if blocklen < 1 { - return nil, fmt.Errorf("invalid blocklen %d", blocklen) - } - padlen := blocklen - (len(data) % blocklen) - if padlen == 0 { - padlen = blocklen - } - pad := bytes.Repeat([]byte{byte(padlen)}, padlen) - return append(data, pad...), nil -} - -func unpad(data []byte, blocklen int) ([]byte, error) { - if blocklen < 1 { - return nil, fmt.Errorf("invalid blocklen %d", blocklen) - } - if len(data)%blocklen != 0 || len(data) == 0 { - return nil, fmt.Errorf("invalid data len %d", len(data)) - } - - // the last byte is the length of padding - padlen := int(data[len(data)-1]) - - // check padding integrity, all bytes should be the same - pad := data[len(data)-padlen:] - for _, padbyte := range pad { - if padbyte != byte(padlen) { - return nil, errors.New("invalid padding") - } - } - - return data[:len(data)-padlen], nil -} - -func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error { - for _, attr := range attrs { - if attr.Type.Equal(attributeType) { - _, err := asn1.Unmarshal(attr.Value.Bytes, out) - return err - } - } - return errors.New("pkcs7: attribute type not in attributes") -} - -// UnmarshalSignedAttribute decodes a single attribute from the signer info -func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error { - sd, ok := p7.raw.(signedData) - if !ok { - return errors.New("pkcs7: payload is not signedData content") - } - if len(sd.SignerInfos) < 1 { - return errors.New("pkcs7: payload has no signers") - } - attributes := sd.SignerInfos[0].AuthenticatedAttributes - return unmarshalAttribute(attributes, attributeType, out) -} - -// SignedData is an opaque data structure for creating signed data payloads -type SignedData struct { - sd signedData - certs []*x509.Certificate - messageDigest []byte -} - -// Attribute represents a key value pair attribute. Value must be marshalable byte -// `encoding/asn1` -type Attribute struct { - Type asn1.ObjectIdentifier - Value interface{} -} - -// SignerInfoConfig are optional values to include when adding a signer -type SignerInfoConfig struct { - ExtraSignedAttributes []Attribute -} - -// NewSignedData initializes a SignedData with content -func NewSignedData(data []byte) (*SignedData, error) { - content, err := asn1.Marshal(data) - if err != nil { - return nil, err - } - ci := contentInfo{ - ContentType: oidData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, - } - digAlg := pkix.AlgorithmIdentifier{ - Algorithm: oidDigestAlgorithmSHA1, - } - h := crypto.SHA1.New() - h.Write(data) - md := h.Sum(nil) - sd := signedData{ - ContentInfo: ci, - Version: 1, - DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digAlg}, - } - return &SignedData{sd: sd, messageDigest: md}, nil -} - -type attributes struct { - types []asn1.ObjectIdentifier - values []interface{} -} - -// Add adds the attribute, maintaining insertion order -func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) { - attrs.types = append(attrs.types, attrType) - attrs.values = append(attrs.values, value) -} - -type sortableAttribute struct { - SortKey []byte - Attribute attribute -} - -type attributeSet []sortableAttribute - -func (sa attributeSet) Len() int { - return len(sa) -} - -func (sa attributeSet) Less(i, j int) bool { - return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0 -} - -func (sa attributeSet) Swap(i, j int) { - sa[i], sa[j] = sa[j], sa[i] -} - -func (sa attributeSet) Attributes() []attribute { - attrs := make([]attribute, len(sa)) - for i, attr := range sa { - attrs[i] = attr.Attribute - } - return attrs -} - -func (attrs *attributes) ForMarshaling() ([]attribute, error) { - sortables := make(attributeSet, len(attrs.types)) - for i := range sortables { - attrType := attrs.types[i] - attrValue := attrs.values[i] - asn1Value, err := asn1.Marshal(attrValue) - if err != nil { - return nil, err - } - attr := attribute{ - Type: attrType, - Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag - } - encoded, err := asn1.Marshal(attr) - if err != nil { - return nil, err - } - sortables[i] = sortableAttribute{ - SortKey: encoded, - Attribute: attr, - } - } - sort.Sort(sortables) - return sortables.Attributes(), nil -} - -// AddSigner signs attributes about the content and adds certificate to payload -func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { - attrs := &attributes{} - attrs.Add(oidAttributeContentType, sd.sd.ContentInfo.ContentType) - attrs.Add(oidAttributeMessageDigest, sd.messageDigest) - attrs.Add(oidAttributeSigningTime, time.Now()) - for _, attr := range config.ExtraSignedAttributes { - attrs.Add(attr.Type, attr.Value) - } - finalAttrs, err := attrs.ForMarshaling() - if err != nil { - return err - } - signature, err := signAttributes(finalAttrs, pkey, crypto.SHA1) - if err != nil { - return err - } - - ias, err := cert2issuerAndSerial(cert) - if err != nil { - return err - } - - signer := signerInfo{ - AuthenticatedAttributes: finalAttrs, - DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1}, - DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA}, - IssuerAndSerialNumber: ias, - EncryptedDigest: signature, - Version: 1, - } - // create signature of signed attributes - sd.certs = append(sd.certs, cert) - sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) - return nil -} - -// AddCertificate adds the certificate to the payload. Useful for parent certificates -func (sd *SignedData) AddCertificate(cert *x509.Certificate) { - sd.certs = append(sd.certs, cert) -} - -// Detach removes content from the signed data struct to make it a detached signature. -// This must be called right before Finish() -func (sd *SignedData) Detach() { - sd.sd.ContentInfo = contentInfo{ContentType: oidData} -} - -// Finish marshals the content and its signers -func (sd *SignedData) Finish() ([]byte, error) { - sd.sd.Certificates = marshalCertificates(sd.certs) - inner, err := asn1.Marshal(sd.sd) - if err != nil { - return nil, err - } - outer := contentInfo{ - ContentType: oidSignedData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, - } - return asn1.Marshal(outer) -} - -func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) { - var ias issuerAndSerial - // The issuer RDNSequence has to match exactly the sequence in the certificate - // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence - ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer} - ias.SerialNumber = cert.SerialNumber - - return ias, nil -} - -// signs the DER encoded form of the attributes with the private key -func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hash crypto.Hash) ([]byte, error) { - attrBytes, err := marshalAttributes(attrs) - if err != nil { - return nil, err - } - h := hash.New() - h.Write(attrBytes) - hashed := h.Sum(nil) - switch priv := pkey.(type) { - case *rsa.PrivateKey: - return rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hashed) - } - return nil, ErrUnsupportedAlgorithm -} - -// concats and wraps the certificates in the RawValue structure -func marshalCertificates(certs []*x509.Certificate) rawCertificates { - var buf bytes.Buffer - for _, cert := range certs { - buf.Write(cert.Raw) - } - rawCerts, _ := marshalCertificateBytes(buf.Bytes()) - return rawCerts -} - -// Even though, the tag & length are stripped out during marshalling the -// RawContent, we have to encode it into the RawContent. If its missing, -// then `asn1.Marshal()` will strip out the certificate wrapper instead. -func marshalCertificateBytes(certs []byte) (rawCertificates, error) { - var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} - b, err := asn1.Marshal(val) - if err != nil { - return rawCertificates{}, err - } - return rawCertificates{Raw: b}, nil -} - -// DegenerateCertificate creates a signed data structure containing only the -// provided certificate or certificate chain. -func DegenerateCertificate(cert []byte) ([]byte, error) { - rawCert, err := marshalCertificateBytes(cert) - if err != nil { - return nil, err - } - emptyContent := contentInfo{ContentType: oidData} - sd := signedData{ - Version: 1, - ContentInfo: emptyContent, - Certificates: rawCert, - CRLs: []pkix.CertificateList{}, - } - content, err := asn1.Marshal(sd) - if err != nil { - return nil, err - } - signedContent := contentInfo{ - ContentType: oidSignedData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, - } - return asn1.Marshal(signedContent) -} - -const ( - EncryptionAlgorithmDESCBC = iota - EncryptionAlgorithmAES128GCM -) - -// ContentEncryptionAlgorithm determines the algorithm used to encrypt the -// plaintext message. Change the value of this variable to change which -// algorithm is used in the Encrypt() function. -var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC - -// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt -// content with an unsupported algorithm. -var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported") - -const nonceSize = 12 - -type aesGCMParameters struct { - Nonce []byte `asn1:"tag:4"` - ICVLen int -} - -func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) { - // Create AES key and nonce - key := make([]byte, 16) - nonce := make([]byte, nonceSize) - - _, err := rand.Read(key) - if err != nil { - return nil, nil, err - } - - _, err = rand.Read(nonce) - if err != nil { - return nil, nil, err - } - - // Encrypt content - block, err := aes.NewCipher(key) - if err != nil { - return nil, nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, nil, err - } - - ciphertext := gcm.Seal(nil, nonce, content, nil) - - // Prepare ASN.1 Encrypted Content Info - paramSeq := aesGCMParameters{ - Nonce: nonce, - ICVLen: gcm.Overhead(), - } - - paramBytes, err := asn1.Marshal(paramSeq) - if err != nil { - return nil, nil, err - } - - eci := encryptedContentInfo{ - ContentType: oidData, - ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidEncryptionAlgorithmAES128GCM, - Parameters: asn1.RawValue{ - Tag: asn1.TagSequence, - Bytes: paramBytes, - }, - }, - EncryptedContent: marshalEncryptedContent(ciphertext), - } - - return key, &eci, nil -} - -func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) { - // Create DES key & CBC IV - key := make([]byte, 8) - iv := make([]byte, des.BlockSize) - _, err := rand.Read(key) - if err != nil { - return nil, nil, err - } - _, err = rand.Read(iv) - if err != nil { - return nil, nil, err - } - - // Encrypt padded content - block, err := des.NewCipher(key) - if err != nil { - return nil, nil, err - } - mode := cipher.NewCBCEncrypter(block, iv) - plaintext, err := pad(content, mode.BlockSize()) - cyphertext := make([]byte, len(plaintext)) - mode.CryptBlocks(cyphertext, plaintext) - - // Prepare ASN.1 Encrypted Content Info - eci := encryptedContentInfo{ - ContentType: oidData, - ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidEncryptionAlgorithmDESCBC, - Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, - }, - EncryptedContent: marshalEncryptedContent(cyphertext), - } - - return key, &eci, nil -} - -// Encrypt creates and returns an envelope data PKCS7 structure with encrypted -// recipient keys for each recipient public key. -// -// The algorithm used to perform encryption is determined by the current value -// of the global ContentEncryptionAlgorithm package variable. By default, the -// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the -// value before calling Encrypt(). For example: -// -// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM -// -// TODO(fullsailor): Add support for encrypting content with other algorithms -func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { - var eci *encryptedContentInfo - var key []byte - var err error - - // Apply chosen symmetric encryption method - switch ContentEncryptionAlgorithm { - case EncryptionAlgorithmDESCBC: - key, eci, err = encryptDESCBC(content) - - case EncryptionAlgorithmAES128GCM: - key, eci, err = encryptAES128GCM(content) - - default: - return nil, ErrUnsupportedEncryptionAlgorithm - } - - if err != nil { - return nil, err - } - - // Prepare each recipient's encrypted cipher key - recipientInfos := make([]recipientInfo, len(recipients)) - for i, recipient := range recipients { - encrypted, err := encryptKey(key, recipient) - if err != nil { - return nil, err - } - ias, err := cert2issuerAndSerial(recipient) - if err != nil { - return nil, err - } - info := recipientInfo{ - Version: 0, - IssuerAndSerialNumber: ias, - KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidEncryptionAlgorithmRSA, - }, - EncryptedKey: encrypted, - } - recipientInfos[i] = info - } - - // Prepare envelope content - envelope := envelopedData{ - EncryptedContentInfo: *eci, - Version: 0, - RecipientInfos: recipientInfos, - } - innerContent, err := asn1.Marshal(envelope) - if err != nil { - return nil, err - } - - // Prepare outer payload structure - wrapper := contentInfo{ - ContentType: oidEnvelopedData, - Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, - } - - return asn1.Marshal(wrapper) -} - -func marshalEncryptedContent(content []byte) asn1.RawValue { - asn1Content, _ := asn1.Marshal(content) - return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} -} - -func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) { - if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { - return rsa.EncryptPKCS1v15(rand.Reader, pub, key) - } - return nil, ErrUnsupportedAlgorithm -} diff --git a/vendor/github.com/fullsailor/pkcs7/x509.go b/vendor/github.com/fullsailor/pkcs7/x509.go deleted file mode 100644 index 195fd0e4bb0ba..0000000000000 --- a/vendor/github.com/fullsailor/pkcs7/x509.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the go/golang LICENSE file. - -package pkcs7 - -// These are private constants and functions from the crypto/x509 package that -// are useful when dealing with signatures verified by x509 certificates - -import ( - "bytes" - "crypto" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" -) - -var ( - oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} - oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} - oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} - oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} - oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} - - oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} - oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} - oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} - - oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} - - // oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA - // but it's specified by ISO. Microsoft's makecert.exe has been known - // to produce certificates with this OID. - oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} -) - -var signatureAlgorithmDetails = []struct { - algo x509.SignatureAlgorithm - name string - oid asn1.ObjectIdentifier - pubKeyAlgo x509.PublicKeyAlgorithm - hash crypto.Hash -}{ - {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, - {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, - {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, - {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, - {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, - {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, - {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, - {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256}, - {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, - {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, - {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, - {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, - {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, - {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, - {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, - {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, -} - -// pssParameters reflects the parameters in an AlgorithmIdentifier that -// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3 -type pssParameters struct { - // The following three fields are not marked as - // optional because the default values specify SHA-1, - // which is no longer suitable for use in signatures. - Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"` - MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"` - SaltLength int `asn1:"explicit,tag:2"` - TrailerField int `asn1:"optional,explicit,tag:3,default:1"` -} - -// asn1.NullBytes is not available prior to Go 1.9 -var nullBytes = []byte{5, 0} - -func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm { - if !ai.Algorithm.Equal(oidSignatureRSAPSS) { - for _, details := range signatureAlgorithmDetails { - if ai.Algorithm.Equal(details.oid) { - return details.algo - } - } - return x509.UnknownSignatureAlgorithm - } - - // RSA PSS is special because it encodes important parameters - // in the Parameters. - - var params pssParameters - if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil { - return x509.UnknownSignatureAlgorithm - } - - var mgf1HashFunc pkix.AlgorithmIdentifier - if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil { - return x509.UnknownSignatureAlgorithm - } - - // PSS is greatly overburdened with options. This code forces - // them into three buckets by requiring that the MGF1 hash - // function always match the message hash function (as - // recommended in - // https://tools.ietf.org/html/rfc3447#section-8.1), that the - // salt length matches the hash length, and that the trailer - // field has the default value. - if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) || - !params.MGF.Algorithm.Equal(oidMGF1) || - !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || - !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) || - params.TrailerField != 1 { - return x509.UnknownSignatureAlgorithm - } - - switch { - case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32: - return x509.SHA256WithRSAPSS - case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48: - return x509.SHA384WithRSAPSS - case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64: - return x509.SHA512WithRSAPSS - } - - return x509.UnknownSignatureAlgorithm -} diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS index 203e84ebab0d0..52ccb5a934d19 100644 --- a/vendor/github.com/golang/snappy/AUTHORS +++ b/vendor/github.com/golang/snappy/AUTHORS @@ -10,6 +10,7 @@ Amazon.com, Inc Damian Gryski +Eric Buth Google Inc. Jan Mercl <0xjnml@gmail.com> Klaus Post diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS index d9914732b5fc5..ea6524ddd02ff 100644 --- a/vendor/github.com/golang/snappy/CONTRIBUTORS +++ b/vendor/github.com/golang/snappy/CONTRIBUTORS @@ -26,7 +26,9 @@ # Please keep the list sorted. +Alex Legg Damian Gryski +Eric Buth Jan Mercl <0xjnml@gmail.com> Jonathan Swinney Kai Backman diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go index f1e04b172c579..23c6e26c6b9b3 100644 --- a/vendor/github.com/golang/snappy/decode.go +++ b/vendor/github.com/golang/snappy/decode.go @@ -118,32 +118,23 @@ func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { return true } -// Read satisfies the io.Reader interface. -func (r *Reader) Read(p []byte) (int, error) { - if r.err != nil { - return 0, r.err - } - for { - if r.i < r.j { - n := copy(p, r.decoded[r.i:r.j]) - r.i += n - return n, nil - } +func (r *Reader) fill() error { + for r.i >= r.j { if !r.readFull(r.buf[:4], true) { - return 0, r.err + return r.err } chunkType := r.buf[0] if !r.readHeader { if chunkType != chunkTypeStreamIdentifier { r.err = ErrCorrupt - return 0, r.err + return r.err } r.readHeader = true } chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 if chunkLen > len(r.buf) { r.err = ErrUnsupported - return 0, r.err + return r.err } // The chunk types are specified at @@ -153,11 +144,11 @@ func (r *Reader) Read(p []byte) (int, error) { // Section 4.2. Compressed data (chunk type 0x00). if chunkLen < checksumSize { r.err = ErrCorrupt - return 0, r.err + return r.err } buf := r.buf[:chunkLen] if !r.readFull(buf, false) { - return 0, r.err + return r.err } checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 buf = buf[checksumSize:] @@ -165,19 +156,19 @@ func (r *Reader) Read(p []byte) (int, error) { n, err := DecodedLen(buf) if err != nil { r.err = err - return 0, r.err + return r.err } if n > len(r.decoded) { r.err = ErrCorrupt - return 0, r.err + return r.err } if _, err := Decode(r.decoded, buf); err != nil { r.err = err - return 0, r.err + return r.err } if crc(r.decoded[:n]) != checksum { r.err = ErrCorrupt - return 0, r.err + return r.err } r.i, r.j = 0, n continue @@ -186,25 +177,25 @@ func (r *Reader) Read(p []byte) (int, error) { // Section 4.3. Uncompressed data (chunk type 0x01). if chunkLen < checksumSize { r.err = ErrCorrupt - return 0, r.err + return r.err } buf := r.buf[:checksumSize] if !r.readFull(buf, false) { - return 0, r.err + return r.err } checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 // Read directly into r.decoded instead of via r.buf. n := chunkLen - checksumSize if n > len(r.decoded) { r.err = ErrCorrupt - return 0, r.err + return r.err } if !r.readFull(r.decoded[:n], false) { - return 0, r.err + return r.err } if crc(r.decoded[:n]) != checksum { r.err = ErrCorrupt - return 0, r.err + return r.err } r.i, r.j = 0, n continue @@ -213,15 +204,15 @@ func (r *Reader) Read(p []byte) (int, error) { // Section 4.1. Stream identifier (chunk type 0xff). if chunkLen != len(magicBody) { r.err = ErrCorrupt - return 0, r.err + return r.err } if !r.readFull(r.buf[:len(magicBody)], false) { - return 0, r.err + return r.err } for i := 0; i < len(magicBody); i++ { if r.buf[i] != magicBody[i] { r.err = ErrCorrupt - return 0, r.err + return r.err } } continue @@ -230,12 +221,44 @@ func (r *Reader) Read(p []byte) (int, error) { if chunkType <= 0x7f { // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). r.err = ErrUnsupported - return 0, r.err + return r.err } // Section 4.4 Padding (chunk type 0xfe). // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). if !r.readFull(r.buf[:chunkLen], false) { - return 0, r.err + return r.err } } + + return nil +} + +// Read satisfies the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + + if err := r.fill(); err != nil { + return 0, err + } + + n := copy(p, r.decoded[r.i:r.j]) + r.i += n + return n, nil +} + +// ReadByte satisfies the io.ByteReader interface. +func (r *Reader) ReadByte() (byte, error) { + if r.err != nil { + return 0, r.err + } + + if err := r.fill(); err != nil { + return 0, err + } + + c := r.decoded[r.i] + r.i++ + return c, nil } diff --git a/vendor/github.com/golang/snappy/decode_arm64.s b/vendor/github.com/golang/snappy/decode_arm64.s index bfafa0ccfce89..7a3ead17eacfe 100644 --- a/vendor/github.com/golang/snappy/decode_arm64.s +++ b/vendor/github.com/golang/snappy/decode_arm64.s @@ -70,7 +70,7 @@ loop: // x := uint32(src[s] >> 2) // switch MOVW $60, R1 - ADD R4>>2, ZR, R4 + LSRW $2, R4, R4 CMPW R4, R1 BLS tagLit60Plus @@ -111,13 +111,12 @@ doLit: // is contiguous in memory and so it needs to leave enough source bytes to // read the next tag without refilling buffers, but Go's Decode assumes // contiguousness (the src argument is a []byte). - MOVD $16, R1 - CMP R1, R4 - BGT callMemmove - CMP R1, R2 - BLT callMemmove - CMP R1, R3 - BLT callMemmove + CMP $16, R4 + BGT callMemmove + CMP $16, R2 + BLT callMemmove + CMP $16, R3 + BLT callMemmove // !!! Implement the copy from src to dst as a 16-byte load and store. // (Decode's documentation says that dst and src must not overlap.) @@ -130,9 +129,8 @@ doLit: // Note that on arm64, it is legal and cheap to issue unaligned 8-byte or // 16-byte loads and stores. This technique probably wouldn't be as // effective on architectures that are fussier about alignment. - - VLD1 0(R6), [V0.B16] - VST1 [V0.B16], 0(R7) + LDP 0(R6), (R14, R15) + STP (R14, R15), 0(R7) // d += length // s += length @@ -210,8 +208,7 @@ tagLit61: B doLit tagLit62Plus: - MOVW $62, R1 - CMPW R1, R4 + CMPW $62, R4 BHI tagLit63 // case x == 62: @@ -273,10 +270,9 @@ tagCopy: // We have a copy tag. We assume that: // - R3 == src[s] & 0x03 // - R4 == src[s] - MOVD $2, R1 - CMP R1, R3 - BEQ tagCopy2 - BGT tagCopy4 + CMP $2, R3 + BEQ tagCopy2 + BGT tagCopy4 // case tagCopy1: // s += 2 @@ -346,13 +342,11 @@ doCopy: // } // copy 16 bytes // d += length - MOVD $16, R1 - MOVD $8, R0 - CMP R1, R4 + CMP $16, R4 BGT slowForwardCopy - CMP R0, R5 + CMP $8, R5 BLT slowForwardCopy - CMP R1, R14 + CMP $16, R14 BLT slowForwardCopy MOVD 0(R15), R2 MOVD R2, 0(R7) @@ -426,8 +420,7 @@ makeOffsetAtLeast8: // // The two previous lines together means that d-offset, and therefore // // R15, is unchanged. // } - MOVD $8, R1 - CMP R1, R5 + CMP $8, R5 BGE fixUpSlowForwardCopy MOVD (R15), R3 MOVD R3, (R7) @@ -477,9 +470,7 @@ verySlowForwardCopy: ADD $1, R15, R15 ADD $1, R7, R7 SUB $1, R4, R4 - MOVD $0, R1 - CMP R1, R4 - BNE verySlowForwardCopy + CBNZ R4, verySlowForwardCopy B loop // The code above handles copy tags. diff --git a/vendor/github.com/golang/snappy/encode_arm64.s b/vendor/github.com/golang/snappy/encode_arm64.s index 1f565ee75f2c6..f8d54adfc5c1d 100644 --- a/vendor/github.com/golang/snappy/encode_arm64.s +++ b/vendor/github.com/golang/snappy/encode_arm64.s @@ -35,11 +35,9 @@ TEXT ·emitLiteral(SB), NOSPLIT, $32-56 MOVW R3, R4 SUBW $1, R4, R4 - MOVW $60, R2 - CMPW R2, R4 + CMPW $60, R4 BLT oneByte - MOVW $256, R2 - CMPW R2, R4 + CMPW $256, R4 BLT twoBytes threeBytes: @@ -98,8 +96,7 @@ TEXT ·emitCopy(SB), NOSPLIT, $0-48 loop0: // for length >= 68 { etc } - MOVW $68, R2 - CMPW R2, R3 + CMPW $68, R3 BLT step1 // Emit a length 64 copy, encoded as 3 bytes. @@ -112,9 +109,8 @@ loop0: step1: // if length > 64 { etc } - MOVD $64, R2 - CMP R2, R3 - BLE step2 + CMP $64, R3 + BLE step2 // Emit a length 60 copy, encoded as 3 bytes. MOVD $0xee, R2 @@ -125,11 +121,9 @@ step1: step2: // if length >= 12 || offset >= 2048 { goto step3 } - MOVD $12, R2 - CMP R2, R3 + CMP $12, R3 BGE step3 - MOVW $2048, R2 - CMPW R2, R11 + CMPW $2048, R11 BGE step3 // Emit the remaining copy, encoded as 2 bytes. @@ -295,27 +289,24 @@ varTable: // var table [maxTableSize]uint16 // // In the asm code, unlike the Go code, we can zero-initialize only the - // first tableSize elements. Each uint16 element is 2 bytes and each VST1 - // writes 64 bytes, so we can do only tableSize/32 writes instead of the - // 2048 writes that would zero-initialize all of table's 32768 bytes. - // This clear could overrun the first tableSize elements, but it won't - // overrun the allocated stack size. + // first tableSize elements. Each uint16 element is 2 bytes and each + // iterations writes 64 bytes, so we can do only tableSize/32 writes + // instead of the 2048 writes that would zero-initialize all of table's + // 32768 bytes. This clear could overrun the first tableSize elements, but + // it won't overrun the allocated stack size. ADD $128, RSP, R17 MOVD R17, R4 // !!! R6 = &src[tableSize] ADD R6<<1, R17, R6 - // zero the SIMD registers - VEOR V0.B16, V0.B16, V0.B16 - VEOR V1.B16, V1.B16, V1.B16 - VEOR V2.B16, V2.B16, V2.B16 - VEOR V3.B16, V3.B16, V3.B16 - memclr: - VST1.P [V0.B16, V1.B16, V2.B16, V3.B16], 64(R4) - CMP R4, R6 - BHI memclr + STP.P (ZR, ZR), 64(R4) + STP (ZR, ZR), -48(R4) + STP (ZR, ZR), -32(R4) + STP (ZR, ZR), -16(R4) + CMP R4, R6 + BHI memclr // !!! R6 = &src[0] MOVD R7, R6 @@ -391,7 +382,7 @@ inner0: // if load32(src, s) != load32(src, candidate) { continue } break MOVW 0(R7), R3 - MOVW (R6)(R15*1), R4 + MOVW (R6)(R15), R4 CMPW R4, R3 BNE inner0 @@ -404,8 +395,7 @@ fourByteMatch: // on inputMargin in encode.go. MOVD R7, R3 SUB R10, R3, R3 - MOVD $16, R2 - CMP R2, R3 + CMP $16, R3 BLE emitLiteralFastPath // ---------------------------------------- @@ -454,18 +444,21 @@ inlineEmitLiteralMemmove: MOVD R3, 24(RSP) // Finish the "d +=" part of "d += emitLiteral(etc)". - ADD R3, R8, R8 - MOVD R7, 80(RSP) - MOVD R8, 88(RSP) - MOVD R15, 120(RSP) - CALL runtime·memmove(SB) - MOVD 64(RSP), R5 - MOVD 72(RSP), R6 - MOVD 80(RSP), R7 - MOVD 88(RSP), R8 - MOVD 96(RSP), R9 - MOVD 120(RSP), R15 - B inner1 + ADD R3, R8, R8 + MOVD R7, 80(RSP) + MOVD R8, 88(RSP) + MOVD R15, 120(RSP) + CALL runtime·memmove(SB) + MOVD 64(RSP), R5 + MOVD 72(RSP), R6 + MOVD 80(RSP), R7 + MOVD 88(RSP), R8 + MOVD 96(RSP), R9 + MOVD 120(RSP), R15 + ADD $128, RSP, R17 + MOVW $0xa7bd, R16 + MOVKW $(0x1e35<<16), R16 + B inner1 inlineEmitLiteralEnd: // End inline of the emitLiteral call. @@ -489,9 +482,9 @@ emitLiteralFastPath: // Note that on arm64, it is legal and cheap to issue unaligned 8-byte or // 16-byte loads and stores. This technique probably wouldn't be as // effective on architectures that are fussier about alignment. - VLD1 0(R10), [V0.B16] - VST1 [V0.B16], 0(R8) - ADD R3, R8, R8 + LDP 0(R10), (R0, R1) + STP (R0, R1), 0(R8) + ADD R3, R8, R8 inner1: // for { etc } @@ -679,7 +672,7 @@ inlineEmitCopyEnd: MOVHU R3, 0(R17)(R11<<1) // if uint32(x>>8) == load32(src, candidate) { continue } - MOVW (R6)(R15*1), R4 + MOVW (R6)(R15), R4 CMPW R4, R14 BEQ inner1 diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go new file mode 100644 index 0000000000000..e4ffca838a17a --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go @@ -0,0 +1,148 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmpopts provides common options for the cmp package. +package cmpopts + +import ( + "math" + "reflect" + "time" + + "github.com/google/go-cmp/cmp" +) + +func equateAlways(_, _ interface{}) bool { return true } + +// EquateEmpty returns a Comparer option that determines all maps and slices +// with a length of zero to be equal, regardless of whether they are nil. +// +// EquateEmpty can be used in conjunction with SortSlices and SortMaps. +func EquateEmpty() cmp.Option { + return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) +} + +func isEmpty(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && + (vx.Len() == 0 && vy.Len() == 0) +} + +// EquateApprox returns a Comparer option that determines float32 or float64 +// values to be equal if they are within a relative fraction or absolute margin. +// This option is not used when either x or y is NaN or infinite. +// +// The fraction determines that the difference of two values must be within the +// smaller fraction of the two values, while the margin determines that the two +// values must be within some absolute margin. +// To express only a fraction or only a margin, use 0 for the other parameter. +// The fraction and margin must be non-negative. +// +// The mathematical expression used is equivalent to: +// |x-y| ≤ max(fraction*min(|x|, |y|), margin) +// +// EquateApprox can be used in conjunction with EquateNaNs. +func EquateApprox(fraction, margin float64) cmp.Option { + if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { + panic("margin or fraction must be a non-negative number") + } + a := approximator{fraction, margin} + return cmp.Options{ + cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)), + cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)), + } +} + +type approximator struct{ frac, marg float64 } + +func areRealF64s(x, y float64) bool { + return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0) +} +func areRealF32s(x, y float32) bool { + return areRealF64s(float64(x), float64(y)) +} +func (a approximator) compareF64(x, y float64) bool { + relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y)) + return math.Abs(x-y) <= math.Max(a.marg, relMarg) +} +func (a approximator) compareF32(x, y float32) bool { + return a.compareF64(float64(x), float64(y)) +} + +// EquateNaNs returns a Comparer option that determines float32 and float64 +// NaN values to be equal. +// +// EquateNaNs can be used in conjunction with EquateApprox. +func EquateNaNs() cmp.Option { + return cmp.Options{ + cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), + cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)), + } +} + +func areNaNsF64s(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) +} +func areNaNsF32s(x, y float32) bool { + return areNaNsF64s(float64(x), float64(y)) +} + +// EquateApproxTime returns a Comparer option that determines two non-zero +// time.Time values to be equal if they are within some margin of one another. +// If both times have a monotonic clock reading, then the monotonic time +// difference will be used. The margin must be non-negative. +func EquateApproxTime(margin time.Duration) cmp.Option { + if margin < 0 { + panic("margin must be a non-negative number") + } + a := timeApproximator{margin} + return cmp.FilterValues(areNonZeroTimes, cmp.Comparer(a.compare)) +} + +func areNonZeroTimes(x, y time.Time) bool { + return !x.IsZero() && !y.IsZero() +} + +type timeApproximator struct { + margin time.Duration +} + +func (a timeApproximator) compare(x, y time.Time) bool { + // Avoid subtracting times to avoid overflow when the + // difference is larger than the largest representible duration. + if x.After(y) { + // Ensure x is always before y + x, y = y, x + } + // We're within the margin if x+margin >= y. + // Note: time.Time doesn't have AfterOrEqual method hence the negation. + return !x.Add(a.margin).Before(y) +} + +// AnyError is an error that matches any non-nil error. +var AnyError anyError + +type anyError struct{} + +func (anyError) Error() string { return "any error" } +func (anyError) Is(err error) bool { return err != nil } + +// EquateErrors returns a Comparer option that determines errors to be equal +// if errors.Is reports them to match. The AnyError error can be used to +// match any non-nil error. +func EquateErrors() cmp.Option { + return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors)) +} + +// areConcreteErrors reports whether x and y are types that implement error. +// The input types are deliberately of the interface{} type rather than the +// error type so that we can handle situations where the current type is an +// interface{}, but the underlying concrete types both happen to implement +// the error interface. +func areConcreteErrors(x, y interface{}) bool { + _, ok1 := x.(error) + _, ok2 := y.(error) + return ok1 && ok2 +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go new file mode 100644 index 0000000000000..26fe25d6afbcb --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_go113.go @@ -0,0 +1,15 @@ +// Copyright 2021, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 + +package cmpopts + +import "errors" + +func compareErrors(x, y interface{}) bool { + xe := x.(error) + ye := y.(error) + return errors.Is(xe, ye) || errors.Is(ye, xe) +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go new file mode 100644 index 0000000000000..6eeb8d6e6543e --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/errors_xerrors.go @@ -0,0 +1,18 @@ +// Copyright 2021, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.13 + +// TODO(≥go1.13): For support on = 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) { + panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i))) + } + start = -1 + } else if start == -1 { + start = i + } + } +} +func (ss sliceSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i), v.Index(j) + return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} + +// SortMaps returns a Transformer option that flattens map[K]V types to be a +// sorted []struct{K, V}. The less function must be of the form +// "func(T, T) bool" which is used to sort any map with key K that is +// assignable to T. +// +// Flattening the map into a slice has the property that cmp.Equal is able to +// use Comparers on K or the K.Equal method if it exists. +// +// The less function must be: +// • Deterministic: less(x, y) == less(x, y) +// • Irreflexive: !less(x, x) +// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// • Total: if x != y, then either less(x, y) or less(y, x) +// +// SortMaps can be used in conjunction with EquateEmpty. +func SortMaps(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ms := mapSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) +} + +type mapSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ms mapSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) && + (vx.Len() != 0 || vy.Len() != 0) +} +func (ms mapSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + outType := reflect.StructOf([]reflect.StructField{ + {Name: "K", Type: src.Type().Key()}, + {Name: "V", Type: src.Type().Elem()}, + }) + dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len()) + for i, k := range src.MapKeys() { + v := reflect.New(outType).Elem() + v.Field(0).Set(k) + v.Field(1).Set(src.MapIndex(k)) + dst.Index(i).Set(v) + } + sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) }) + ms.checkSort(dst) + return dst.Interface() +} +func (ms mapSorter) checkSort(v reflect.Value) { + for i := 1; i < v.Len(); i++ { + if !ms.less(v, i-1, i) { + panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i))) + } + } +} +func (ms mapSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) + return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go new file mode 100644 index 0000000000000..a09829c3af920 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go @@ -0,0 +1,187 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" +) + +// filterField returns a new Option where opt is only evaluated on paths that +// include a specific exported field on a single struct type. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a +// specific sub-field that is embedded or nested within the parent struct. +func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option { + // TODO: This is currently unexported over concerns of how helper filters + // can be composed together easily. + // TODO: Add tests for FilterField. + + sf := newStructFilter(typ, name) + return cmp.FilterPath(sf.filter, opt) +} + +type structFilter struct { + t reflect.Type // The root struct type to match on + ft fieldTree // Tree of fields to match on +} + +func newStructFilter(typ interface{}, names ...string) structFilter { + // TODO: Perhaps allow * as a special identifier to allow ignoring any + // number of path steps until the next field match? + // This could be useful when a concrete struct gets transformed into + // an anonymous struct where it is not possible to specify that by type, + // but the transformer happens to provide guarantees about the names of + // the transformed fields. + + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T must be a non-pointer struct", typ)) + } + var ft fieldTree + for _, name := range names { + cname, err := canonicalName(t, name) + if err != nil { + panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err)) + } + ft.insert(cname) + } + return structFilter{t, ft} +} + +func (sf structFilter) filter(p cmp.Path) bool { + for i, ps := range p { + if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) { + return true + } + } + return false +} + +// fieldTree represents a set of dot-separated identifiers. +// +// For example, inserting the following selectors: +// Foo +// Foo.Bar.Baz +// Foo.Buzz +// Nuka.Cola.Quantum +// +// Results in a tree of the form: +// {sub: { +// "Foo": {ok: true, sub: { +// "Bar": {sub: { +// "Baz": {ok: true}, +// }}, +// "Buzz": {ok: true}, +// }}, +// "Nuka": {sub: { +// "Cola": {sub: { +// "Quantum": {ok: true}, +// }}, +// }}, +// }} +type fieldTree struct { + ok bool // Whether this is a specified node + sub map[string]fieldTree // The sub-tree of fields under this node +} + +// insert inserts a sequence of field accesses into the tree. +func (ft *fieldTree) insert(cname []string) { + if ft.sub == nil { + ft.sub = make(map[string]fieldTree) + } + if len(cname) == 0 { + ft.ok = true + return + } + sub := ft.sub[cname[0]] + sub.insert(cname[1:]) + ft.sub[cname[0]] = sub +} + +// matchPrefix reports whether any selector in the fieldTree matches +// the start of path p. +func (ft fieldTree) matchPrefix(p cmp.Path) bool { + for _, ps := range p { + switch ps := ps.(type) { + case cmp.StructField: + ft = ft.sub[ps.Name()] + if ft.ok { + return true + } + if len(ft.sub) == 0 { + return false + } + case cmp.Indirect: + default: + return false + } + } + return false +} + +// canonicalName returns a list of identifiers where any struct field access +// through an embedded field is expanded to include the names of the embedded +// types themselves. +// +// For example, suppose field "Foo" is not directly in the parent struct, +// but actually from an embedded struct of type "Bar". Then, the canonical name +// of "Foo" is actually "Bar.Foo". +// +// Suppose field "Foo" is not directly in the parent struct, but actually +// a field in two different embedded structs of types "Bar" and "Baz". +// Then the selector "Foo" causes a panic since it is ambiguous which one it +// refers to. The user must specify either "Bar.Foo" or "Baz.Foo". +func canonicalName(t reflect.Type, sel string) ([]string, error) { + var name string + sel = strings.TrimPrefix(sel, ".") + if sel == "" { + return nil, fmt.Errorf("name must not be empty") + } + if i := strings.IndexByte(sel, '.'); i < 0 { + name, sel = sel, "" + } else { + name, sel = sel[:i], sel[i:] + } + + // Type must be a struct or pointer to struct. + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("%v must be a struct", t) + } + + // Find the canonical name for this current field name. + // If the field exists in an embedded struct, then it will be expanded. + sf, _ := t.FieldByName(name) + if !isExported(name) { + // Avoid using reflect.Type.FieldByName for unexported fields due to + // buggy behavior with regard to embeddeding and unexported fields. + // See https://golang.org/issue/4876 for details. + sf = reflect.StructField{} + for i := 0; i < t.NumField() && sf.Name == ""; i++ { + if t.Field(i).Name == name { + sf = t.Field(i) + } + } + } + if sf.Name == "" { + return []string{name}, fmt.Errorf("does not exist") + } + var ss []string + for i := range sf.Index { + ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name) + } + if sel == "" { + return ss, nil + } + ssPost, err := canonicalName(sf.Type, sel) + return append(ss, ssPost...), err +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go new file mode 100644 index 0000000000000..4eb49d63db311 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go @@ -0,0 +1,35 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "github.com/google/go-cmp/cmp" +) + +type xformFilter struct{ xform cmp.Option } + +func (xf xformFilter) filter(p cmp.Path) bool { + for _, ps := range p { + if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform { + return false + } + } + return true +} + +// AcyclicTransformer returns a Transformer with a filter applied that ensures +// that the transformer cannot be recursively applied upon its own output. +// +// An example use case is a transformer that splits a string by lines: +// AcyclicTransformer("SplitLines", func(s string) []string{ +// return strings.Split(s, "\n") +// }) +// +// Had this been an unfiltered Transformer instead, this would result in an +// infinite cycle converting a string to []string to [][]string and so on. +func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { + xf := xformFilter{cmp.Transformer(name, xformFunc)} + return cmp.FilterPath(xf.filter, xf.xform) +} diff --git a/vendor/github.com/hashicorp/vault/api/go.sum b/vendor/github.com/hashicorp/vault/api/go.sum index 749ad0d191d33..972a6ac85298f 100644 --- a/vendor/github.com/hashicorp/vault/api/go.sum +++ b/vendor/github.com/hashicorp/vault/api/go.sum @@ -85,6 +85,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= diff --git a/vendor/modules.txt b/vendor/modules.txt index b3310d794ccdf..8ad0207c9bc07 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -371,9 +371,6 @@ github.com/fatih/color github.com/fatih/structs # github.com/form3tech-oss/jwt-go v3.2.2+incompatible github.com/form3tech-oss/jwt-go -# github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa -## explicit -github.com/fullsailor/pkcs7 # github.com/gammazero/deque v0.0.0-20190130191400-2afb3858e9c7 github.com/gammazero/deque # github.com/gammazero/workerpool v0.0.0-20190406235159-88d534f22b56 @@ -433,12 +430,14 @@ github.com/golang/protobuf/ptypes/empty github.com/golang/protobuf/ptypes/struct github.com/golang/protobuf/ptypes/timestamp github.com/golang/protobuf/ptypes/wrappers -# github.com/golang/snappy v0.0.2 +# github.com/golang/snappy v0.0.4 github.com/golang/snappy # github.com/google/flatbuffers v1.11.0 github.com/google/flatbuffers/go # github.com/google/go-cmp v0.5.5 +## explicit github.com/google/go-cmp/cmp +github.com/google/go-cmp/cmp/cmpopts github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function