File sops-3.7.3.obscpio of Package sops

07070100000000000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001500000000sops-3.7.3/.circleci07070100000001000081A400000000000000000000000162794F9300000EB0000000000000000000000000000000000000002000000000sops-3.7.3/.circleci/config.ymlversion: 2.1

workflows:
  build-and-deploy:
    jobs:
      - build
      - push:
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
jobs:
  build:
    working_directory: /go/src/go.mozilla.org/sops
    docker:
      - image: circleci/golang:1.13
    resource_class: large
    steps:
      - checkout
      - setup_remote_docker:
          version: 20.10.11
      - run:
          name: Build containers
          command: |
            docker build -t mozilla/sops .
            docker tag mozilla/sops "mozilla/sops:$CIRCLE_SHA1"
      - run:
          name: Build containers (alpine)
          command: |
            # Just to ensure the container can be built.
            docker build -f Dockerfile.alpine -t mozilla/sops:alpine .

  push:
    machine:
      image: ubuntu-2004:202111-02
    resource_class: large
    steps:
      - checkout
      - run:
          name: semver check
          command: |
            MAJOR=$(echo ${CIRCLE_TAG#v} | cut -d"." -f1)
            MINOR=$(echo ${CIRCLE_TAG#v} | cut -d"." -f2)
            PATCH=$(echo ${CIRCLE_TAG#v} | cut -d"." -f3)
            echo "export MAJOR=${MAJOR}" >> $BASH_ENV
            echo "export MINOR=${MINOR}" >> $BASH_ENV
            echo "export PATCH=${PATCH}" >> $BASH_ENV

            if [ -z $MAJOR ];then
            cat \<< EOF
            Failure Info:

            This job uses the semver from the git TAG as the public version to publish.

            - This should only run on workflows triggered by a tag.
            - The tag name should be a semver like 'v1.2.3'
            - The version should follow conventions documented at https://github.com/fsaintjacques/semver-tool
            EOF
            exit 1
            fi
      - run:
          name: Build containers
          command: |
            docker build -t mozilla/sops .
            docker build -f Dockerfile.alpine -t mozilla/sops:alpine .
      - run:
          name: Tag & Push containers
          command: |
            #latest
            bin/ci/deploy_dockerhub.sh "latest"
            bin/ci/deploy_dockerhub.sh "alpine"

            # by sha
            echo "Tag and push mozilla/sops:$CIRCLE_SHA1"
            docker tag mozilla/sops "mozilla/sops:$CIRCLE_SHA1"
            bin/ci/deploy_dockerhub.sh "$CIRCLE_SHA1"

            # no sha for alpine

            # by semver
            # v1.2.3
            if [ ! -z $PATCH ];then
              echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR.$PATCH"
              docker tag mozilla/sops "mozilla/sops:v$MAJOR.$MINOR.$PATCH"
              bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR.$PATCH"

              echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR.$PATCH-alpine"
              docker tag mozilla/sops:alpine "mozilla/sops:v$MAJOR.$MINOR.$PATCH-alpine"
              bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR.$PATCH-alpine"
            fi
            # v1.2
            if [ ! -z $MINOR ];then
              echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR"
              docker tag mozilla/sops "mozilla/sops:v$MAJOR.$MINOR"
              bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR"

              echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR-alpine"
              docker tag mozilla/sops:alpine "mozilla/sops:v$MAJOR.$MINOR-alpine"
              bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR-alpine"
            fi
            # v1
            echo "Tag and Push mozilla/sops:v$MAJOR"
            docker tag mozilla/sops "mozilla/sops:v$MAJOR"
            bin/ci/deploy_dockerhub.sh "v$MAJOR"

            echo "Tag and Push mozilla/sops:v$MAJOR-alpine"
            docker tag mozilla/sops:alpine "mozilla/sops:v$MAJOR-alpine"
            bin/ci/deploy_dockerhub.sh "v$MAJOR-alpine"
07070100000002000081A400000000000000000000000162794F9300000025000000000000000000000000000000000000001900000000sops-3.7.3/.dockerignore/.git
/Dockerfile
/Dockerfile.alpine
07070100000003000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/.github07070100000004000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001D00000000sops-3.7.3/.github/workflows07070100000005000081A400000000000000000000000162794F9300000D85000000000000000000000000000000000000002500000000sops-3.7.3/.github/workflows/cli.ymlname: CLI

on:
  push:
    branches:
      - develop
      - master
  pull_request:
    branches:
      - develop
      - master

jobs:
  build:
    name: Build and test ${{ matrix.os }} ${{ matrix.arch }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [linux, darwin, windows]
        arch: [amd64, arm64]
        exclude:
          - os: windows
            arch: arm64
    env:
      VAULT_VERSION: "1.1.3"
      VAULT_TOKEN: "root"
      VAULT_ADDR: "http://127.0.0.1:8200"
    steps:
      - name: Install dependencies
        run: sudo apt-get update && sudo apt-get install git -y
      - name: Set up Go 1.17
        uses: actions/setup-go@v2
        with:
          go-version: 1.17
        id: go
      - name: Check out code into the Go module directory
        uses: actions/checkout@v2
      - uses: actions/cache@v2
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-
      - name: Build Linux and Darwin
        if: matrix.os != 'windows'
        run: GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }} -v ./cmd/sops
      - name: Build Windows
        if: matrix.os == 'windows'
        run: GOOS=${{ matrix.os }} go build -o sops-${{ matrix.os }}-${{ github.sha }} -v ./cmd/sops
      - name: Import test GPG keys
        run: for i in 1 2 3 4 5; do gpg --import pgp/sops_functional_tests_key.asc && break || sleep 15; done
      - name: Test
        run: make test
      - name: Upload artifact for Linux and Darwin
        if: matrix.os != 'windows'
        uses: actions/upload-artifact@v2
        with:
          name: sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
          path: sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
      - name: Upload artifact for Windows
        if: matrix.os == 'windows'
        uses: actions/upload-artifact@v2
        with:
          name: sops-${{ matrix.os }}-${{ github.sha }}
          path: sops-${{ matrix.os }}-${{ github.sha }}
  test:
    name: Functional tests
    runs-on: ubuntu-latest
    needs: [build]
    env:
      VAULT_VERSION: "1.1.3"
      VAULT_TOKEN: "root"
      VAULT_ADDR: "http://127.0.0.1:8200"
    steps:
      - name: Install rustup
        run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain 1.47.0
      - name: Check out code
        uses: actions/checkout@v2
      - uses: actions/download-artifact@v2
        with:
          name: sops-linux-amd64-${{ github.sha }}
      - name: Move SOPS binary
        run: mv sops-linux-amd64-${{ github.sha }} ./functional-tests/sops
      - name: Make SOPS binary executable
        run: chmod +x ./functional-tests/sops
      - name: Download Vault
        run: curl -O "https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip" && sudo unzip vault_${VAULT_VERSION}_linux_amd64.zip -d /usr/local/bin/
      - name: Start Vault server
        run: vault server -dev -dev-root-token-id="$VAULT_TOKEN" &
      - name: Enable Vault KV
        run: vault secrets enable -version=1 kv
      - name: Import test GPG keys
        run: for i in 1 2 3 4 5; do gpg --import pgp/sops_functional_tests_key.asc && break || sleep 15; done
      - name: Run tests
        run: cargo test
        working-directory: ./functional-tests
07070100000006000081A400000000000000000000000162794F9300000C2E000000000000000000000000000000000000002900000000sops-3.7.3/.github/workflows/release.ymlname: Release

on:
  push:
    tags:
      - "v*"

jobs:
  tagged-release:
    name: "Tagged Release"
    runs-on: ubuntu-latest

    steps:
      - name: Install dependencies
        run: sudo apt-get update && sudo apt-get install git ruby rpm -y
      - name: Install fpm
        run: gem install fpm || sudo gem install fpm
      - name: Set up Go 1.17
        uses: actions/setup-go@v2
        with:
          go-version: 1.17
        id: go
      - name: Check out code into the Go module directory
        uses: actions/checkout@v2
      - name: Go vendor
        run: go mod vendor
      - name: Make release directory
        run: mkdir dist
      - name: Build deb and rpm
        run: make deb-pkg rpm-pkg
      - name: Move deb and rpm into release directory
        run: mv *.deb *.rpm dist/
      - name: Set RELEASE_VERSION
        run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
      - name: Set RELEASE_NUMBER
        run: echo "RELEASE_NUMBER=$(echo $RELEASE_VERSION | cut -c2-)" >> $GITHUB_ENV
      - name: Build linux amd64 binary
        run: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.linux.amd64 go.mozilla.org/sops/v3/cmd/sops && cp dist/sops-${{ env.RELEASE_VERSION }}.linux.amd64 dist/sops-${{ env.RELEASE_VERSION }}.linux
      - name: Build linux arm64 binary
        run: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.linux.arm64 go.mozilla.org/sops/v3/cmd/sops
      - name: Build darwin amd64 binary
        run: GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.darwin.amd64 go.mozilla.org/sops/v3/cmd/sops
      - name: Copy darwin amd64 to have a no-architecture labeled version
        run: cp dist/sops-${{ env.RELEASE_VERSION }}.darwin.amd64 dist/sops-${{ env.RELEASE_VERSION }}.darwin
      - name: Build darwin arm64 binary
        run: GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.darwin.arm64 go.mozilla.org/sops/v3/cmd/sops
      - name: Build windows binary
        run: GOOS=windows CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.exe go.mozilla.org/sops/v3/cmd/sops
      - name: Create release
        uses: "mozilla/action-automatic-releases@latest"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          prerelease: true
          files: |
            dist/sops-${{ env.RELEASE_VERSION }}.exe
            dist/sops-${{ env.RELEASE_VERSION }}.darwin.amd64
            dist/sops-${{ env.RELEASE_VERSION }}.darwin.arm64
            dist/sops-${{ env.RELEASE_VERSION }}.darwin
            dist/sops-${{ env.RELEASE_VERSION }}.linux.amd64
            dist/sops-${{ env.RELEASE_VERSION }}.linux.arm64
            dist/sops-${{ env.RELEASE_VERSION }}.linux
            dist/sops_${{ env.RELEASE_NUMBER }}_amd64.deb
            dist/sops_${{ env.RELEASE_NUMBER }}_arm64.deb
            dist/sops-${{ env.RELEASE_NUMBER }}-1.x86_64.rpm
            dist/sops-${{ env.RELEASE_NUMBER }}-1.aarch64.rpm
07070100000007000081A400000000000000000000000162794F9300000033000000000000000000000000000000000000001600000000sops-3.7.3/.gitignoretarget
Cargo.lock
vendor/
coverage.txt
profile.out
07070100000008000081A400000000000000000000000162794F9300000081000000000000000000000000000000000000001600000000sops-3.7.3/.sops.yamlcreation_rules:
    - pgp: >-
        FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4,
        D7229043384BCC60326C6FB9D8720D957C3D3074
07070100000009000081A400000000000000000000000162794F93000021EE000000000000000000000000000000000000001900000000sops-3.7.3/CHANGELOG.rstChangelog
=========

3.7.3
-----
Changes:

* Upgrade dependencies (#1024, #1045)
* Build alpine container in CI (#1018, #1032, #1025)
* keyservice: accept KeyServiceServer in LocalClient (#1035)
* Add support for GCP Service Account within `GOOGLE_CREDENTIALS` (#953)

Bug fixes:

* Upload the correct binary for the linux amd64 build (#1026)
* Fix bug when specifying multiple age recipients (#966)
* Allow for empty yaml maps (#908)
* Limit AWS role names to 64 characters (#1037)

3.7.2
-----
Changes:

* README updates (#861, #860)
* Various test fixes (#909, #906, #1008)
* Added Linux and Darwin arm64 releases (#911, #891)
* Upgrade to go v1.17 (#1012)
* Support SOPS_AGE_KEY environment variable (#1006)

Bug fixes:

* Make sure comments in yaml files are not duplicated (#866)
* Make sure configuration file paths work correctly relative to the config file in us (#853)

3.7.1
-----
Changes:

* Security fix
* Add release workflow (#843)
* Fix issue where CI wouldn't run against master (#848)
* Trim extra whitespace around age keys (#846)

3.7.0
-----
Features:

* Add support for age (#688)
* Add filename to exec-file (#761)

Changes:

* On failed decryption with GPG, return the error returned by GPG to the sops user (#762)
* Use yaml.v3 instead of modified yaml.v2 for handling YAML files (#791)
* Update aws-sdk-go to version v1.37.18 (#823)

Project Changes:

* Switch from TravisCI to Github Actions (#792)

3.6.1
-----
Features:

* Add support for --unencrypted-regex (#715)

Changes:

* Use keys.openpgp.org instead of gpg.mozilla.org (#732)
* Upgrade AWS SDK version (#714)
* Support --input-type for exec-file (#699)

Bug fixes:

* Fixes broken Vault tests (#731)
* Revert "Add standard newline/quoting behavior to dotenv store" (#706)


3.6.0
-----
Features:

* Support for encrypting data through the use of Hashicorp Vault (#655)
* `sops publish` now supports `--recursive` flag for publishing all files in a directory (#602)
* `sops publish` now supports `--omit-extensions` flag for omitting the extension in the destination path (#602)
* sops now supports JSON arrays of arrays (#642)

Improvements:

* Updates and standardization for the dotenv store (#612, #622)
* Close temp files after using them for edit command (#685)

Bug fixes:

* AWS SDK usage now correctly resolves the `~/.aws/config` file (#680)
* `sops updatekeys` now correctly matches config rules (#682)
* `sops updatekeys` now correctly uses the config path cli flag (#672)
* Partially empty sops config files don't break the use of sops anymore (#662)
* Fix possible infinite loop in PGP's passphrase prompt call (#690)

Project changes:

* Dockerfile now based off of golang version 1.14 (#649)
* Push alpine version of docker image to Dockerhub (#609)
* Push major, major.minor, and major.minor.patch tagged docker images to Dockerhub (#607)
* Removed out of date contact information (#668)
* Update authors in the cli help text (#645)


3.5.0
-----
Features:

* `sops exec-env` and `sops exec-file`, two new commands for utilizing sops secrets within a temporary file or env vars

Bug fixes:

* Sanitize AWS STS session name, as sops creates it based off of the machines hostname
* Fix for `decrypt.Data` to support `.ini` files
* Various package fixes related to switching to Go Modules
* Fixes for Vault-related tests running locally and in CI.

Project changes:

* Change to proper use of go modules, changing to primary module name to `go.mozilla.org/sops/v3`
* Change tags to requiring a `v` prefix.
* Add documentation for `sops updatekeys` command

3.4.0
-----
Features:

* `sops publish`, a new command for publishing sops encrypted secrets to S3, GCS, or Hashicorp Vault
* Support for multiple Azure authentication mechanisms
* Azure Keyvault support to the sops config file
* `encrypted_regex` option to the sops config file

Bug fixes:

* Return non-zero exit code for invalid CLI flags
* Broken path handling for sops editing on Windows
* `go lint/fmt` violations
* Check for pgp fingerprint before slicing it

Project changes:

* Build container using golang 1.12
* Switch to using go modules
* Hashicorp Vault server in Travis CI build
* Mozilla Publice License file to repo
* Replaced expiring test gpg keys

3.3.1
-----

Bug fixes:

* Make sure the pgp key fingerprint is longer than 16 characters before
  slicing it. (#463)
* Allow for `--set` value to be a string. (#461)

Project changes:

* Using `develop` as a staging branch to create releases off of. What
  is in `master` is now the current stable release.
* Upgrade to using Go 1.12 to build sops
* Updated all vendored packages

3.3.0
-----

New features:

* Multi-document support for YAML files
* Support referencing AWS KMS keys by their alias
* Support for INI files
* Support for AWS CLI profiles
* Comment support in .env files
* Added vi to the list of known editors
* Added a way to specify the GPG key server to use through the
  SOPS_GPG_KEYSERVER environment variable

Bug fixes:

* Now uses $HOME instead of ~ (which didn't work) to find the GPG home
* Fix panic when vim was not available as an editor, but other
  alternative editors were
* Fix issue with AWS KMS Encryption Contexts (#445) with more than one
  context value failing to decrypt intermittently. Includes an
  automatic fix for old files affected by this issue.

Project infrastructure changes:

* Added integration tests for AWS KMS
* Added Code of Conduct


3.2.0
-----

* Added --output flag to write output a file directly instead of
  through stdout
* Added support for dotenv files

3.1.1
-----

* Fix incorrect version number from previous release

3.1.0
-----

* Add support for Azure Key Service

* Fix bug that prevented JSON escapes in input files from working

3.0.5
-----

* Prevent files from being encrypted twice

* Fix empty comments not being decrypted correctly

* If keyservicecmd returns an error, log it.

* Initial sops workspace auditing support (still wip)

* Refactor Store interface to reflect operations SOPS performs

3.0.3
-----

* --set now works with nested data structures and not just simple
  values

* Changed default log level to warn instead of info

* Avoid creating empty files when using the editor mode to create new
  files and not making any changes to the example files

* Output unformatted strings when using --extract instead of encoding
  them to yaml

* Allow forcing binary input and output types from command line flags

* Deprecate filename_regex in favor of path_regex. filename_regex had
  a bug and matched on the whole file path, when it should have only
  matched on the file name. path_regex on the other hand is documented
  to match on the whole file path.

* Add an encrypted-suffix option, the exact opposite of
  unencrypted-suffix

* Allow specifying unencrypted_suffix and encrypted_suffix rules in
  the .sops.yaml configuration file

* Introduce key service flag optionally prompting users on
  encryption/decryption

3.0.1
-----

* Don't consider io.EOF returned by Decoder.Token as error

* add IsBinary: true to FileHints when encoding with crypto/openpgp 

* some improvements to error messages

3.0.0
-----

* Shamir secret sharing scheme support allows SOPS to require multiple master
  keys to access a data key and decrypt a file. See `sops groups -help` and the
  documentation in README.

* Keyservice to forward access to a local master key on a socket, similar to
  gpg-agent. See `sops keyservice --help` and the documentation in README.

* Encrypt comments by default

* Support for Google Compute Platform KMS

* Refactor of the store logic to separate the internal representation SOPS
  has of files from the external representation used in JSON and YAML files

* Reencoding of versions as string on sops 1.X files.
  **WARNING** this change breaks backward compatibility.
  SOPS shows an error message with instructions on how to solve
  this if it happens.
  
* Added command to reconfigure the keys used to encrypt/decrypt a file based on the .sops.yaml config file

* Retrieve missing PGP keys from gpg.mozilla.org

* Improved error messages for errors when decrypting files


2.0.0
-----

* [major] rewrite in Go

1.14
----

* [medium] Support AWS KMS Encryption Contexts
* [minor] Support insertion in encrypted documents via --set
* [minor] Read location of gpg binary from SOPS_GPG_EXEC env variables

1.13
----

* [minor] handle $EDITOR variable with parameters

1.12
----

* [minor] make sure filename_regex gets applied to file names, not paths
* [minor] move check of latest version under the -V flag
* [medium] fix handling of binary data to preserve file integrity
* [minor] try to use configuration when encrypting existing files
0707010000000A000081A400000000000000000000000162794F93000001EF000000000000000000000000000000000000001E00000000sops-3.7.3/CODE_OF_CONDUCT.md# Community Participation Guidelines

This repository is governed by Mozilla's code of conduct and etiquette guidelines. 
For more details, please read the
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 

## How to Report
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
0707010000000B000081A400000000000000000000000162794F9300000830000000000000000000000000000000000000001B00000000sops-3.7.3/CONTRIBUTING.md# Contributing to SOPS

Mozilla welcomes contributions from everyone. Here are a few guidelines and instructions if you're thinking of helping with the development of SOPS.

# Getting started

* Make sure you have Go 1.12 or greater installed. You can find information on how to install Go [here](https://golang.org/dl/)
* After following the [Go installation guide](https://golang.org/doc/install), run `go get go.mozilla.org/sops`. This will automatically clone this repository.
* Switch into sops's directory, which will be in `$GOPATH/src/go.mozilla.org/sops`.
* Run the tests with `make test`. They should all pass.
* Fork the project on GitHub.
* Add your fork to git's remotes:
  * If you use SSH authentication: `git remote add <your username> git@github.com:<your username>/sops.git`.
  * Otherwise: `git remote add <your username> https://github.com/<your username>/sops.git`.
* **Switch to the `develop` branch: `git checkout develop`**
* Make any changes you want to sops, commit them, and push them to your fork.
* **Create a pull request against `develop`**, and a contributor will come by and review your code. They may ask for some changes, and hopefully your contribution will be merged to the `develop` branch!

# Guidelines

* Unless it's particularly hard, changes that fix a bug should have a regression test to make sure that the bug is not introduced again.
* New features and changes to existing features should be documented, and, if possible, tested.

# Regenerating mocks

If you encounter an error like `kms/mocks/KMSAPI.go:1607: cannot use (*KMSAPI)(nil) (type *KMSAPI) as type kmsiface.KMSAPI in assignment: *KMSAPI does not implement kmsiface.KMSAPI (missing ListResourceTags method)`, you need to regenerate mocks, probably because the interface was changed by a vendoring update. There is a make command to do this for you. Simply run `make mock`, and the new mocks will be automatically generated.

# Communication

If you need any help contributing to sops, several contributors are on the `#go` channel on [Mozilla's IRC server](https://wiki.mozilla.org/IRC).
0707010000000C000081A400000000000000000000000162794F93000000DF000000000000000000000000000000000000001600000000sops-3.7.3/DockerfileFROM golang:1.17

COPY . /go/src/go.mozilla.org/sops
WORKDIR /go/src/go.mozilla.org/sops

RUN CGO_ENABLED=1 make install
RUN apt-get update
RUN apt-get install -y vim python3-pip emacs
RUN pip install awscli
ENV EDITOR vim
0707010000000D000081A400000000000000000000000162794F9300000156000000000000000000000000000000000000001D00000000sops-3.7.3/Dockerfile.alpineFROM golang:1.17-alpine3.15 AS builder

RUN apk --no-cache add make

COPY . /go/src/go.mozilla.org/sops
WORKDIR /go/src/go.mozilla.org/sops

RUN CGO_ENABLED=1 make install


FROM alpine:3.15

RUN apk --no-cache add \
  vim ca-certificates
ENV EDITOR vim
COPY --from=builder /go/bin/sops /usr/local/bin/sops
ENTRYPOINT ["/usr/local/bin/sops"]
0707010000000E000081A400000000000000000000000162794F9300004155000000000000000000000000000000000000001300000000sops-3.7.3/LICENSEMozilla Public License Version 2.0
==================================

1. Definitions
--------------

1.1. "Contributor"
    means each individual or legal entity that creates, contributes to
    the creation of, or owns Covered Software.

1.2. "Contributor Version"
    means the combination of the Contributions of others (if any) used
    by a Contributor and that particular Contributor's Contribution.

1.3. "Contribution"
    means Covered Software of a particular Contributor.

1.4. "Covered Software"
    means Source Code Form to which the initial Contributor has attached
    the notice in Exhibit A, the Executable Form of such Source Code
    Form, and Modifications of such Source Code Form, in each case
    including portions thereof.

1.5. "Incompatible With Secondary Licenses"
    means

    (a) that the initial Contributor has attached the notice described
        in Exhibit B to the Covered Software; or

    (b) that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the
        terms of a Secondary License.

1.6. "Executable Form"
    means any form of the work other than Source Code Form.

1.7. "Larger Work"
    means a work that combines Covered Software with other material, in
    a separate file or files, that is not Covered Software.

1.8. "License"
    means this document.

1.9. "Licensable"
    means having the right to grant, to the maximum extent possible,
    whether at the time of the initial grant or subsequently, any and
    all of the rights conveyed by this License.

1.10. "Modifications"
    means any of the following:

    (a) any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered
        Software; or

    (b) any new file in Source Code Form that contains any Covered
        Software.

1.11. "Patent Claims" of a Contributor
    means any patent claim(s), including without limitation, method,
    process, and apparatus claims, in any patent Licensable by such
    Contributor that would be infringed, but for the grant of the
    License, by the making, using, selling, offering for sale, having
    made, import, or transfer of either its Contributions or its
    Contributor Version.

1.12. "Secondary License"
    means either the GNU General Public License, Version 2.0, the GNU
    Lesser General Public License, Version 2.1, the GNU Affero General
    Public License, Version 3.0, or any later versions of those
    licenses.

1.13. "Source Code Form"
    means the form of the work preferred for making modifications.

1.14. "You" (or "Your")
    means an individual or a legal entity exercising rights under this
    License. For legal entities, "You" includes any entity that
    controls, is controlled by, or is under common control with You. For
    purposes of this definition, "control" means (a) the power, direct
    or indirect, to cause the direction or management of such entity,
    whether by contract or otherwise, or (b) ownership of more than
    fifty percent (50%) of the outstanding shares or beneficial
    ownership of such entity.

2. License Grants and Conditions
--------------------------------

2.1. Grants

Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:

(a) under intellectual property rights (other than patent or trademark)
    Licensable by such Contributor to use, reproduce, make available,
    modify, display, perform, distribute, and otherwise exploit its
    Contributions, either on an unmodified basis, with Modifications, or
    as part of a Larger Work; and

(b) under Patent Claims of such Contributor to make, use, sell, offer
    for sale, have made, import, and otherwise transfer either its
    Contributions or its Contributor Version.

2.2. Effective Date

The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.

2.3. Limitations on Grant Scope

The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:

(a) for any code that a Contributor has removed from Covered Software;
    or

(b) for infringements caused by: (i) Your and any other third party's
    modifications of Covered Software, or (ii) the combination of its
    Contributions with other software (except as part of its Contributor
    Version); or

(c) under Patent Claims infringed by Covered Software in the absence of
    its Contributions.

This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).

2.4. Subsequent Licenses

No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).

2.5. Representation

Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.

2.7. Conditions

Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.

3. Responsibilities
-------------------

3.1. Distribution of Source Form

All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.

3.2. Distribution of Executable Form

If You distribute Covered Software in Executable Form then:

(a) such Covered Software must also be made available in Source Code
    Form, as described in Section 3.1, and You must inform recipients of
    the Executable Form how they can obtain a copy of such Source Code
    Form by reasonable means in a timely manner, at a charge no more
    than the cost of distribution to the recipient; and

(b) You may distribute such Executable Form under the terms of this
    License, or sublicense it under different terms, provided that the
    license for the Executable Form does not attempt to limit or alter
    the recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).

3.4. Notices

You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.

4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------

If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.

5. Termination
--------------

5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.

************************************************************************
*                                                                      *
*  6. Disclaimer of Warranty                                           *
*  -------------------------                                           *
*                                                                      *
*  Covered Software is provided under this License on an "as is"       *
*  basis, without warranty of any kind, either expressed, implied, or  *
*  statutory, including, without limitation, warranties that the       *
*  Covered Software is free of defects, merchantable, fit for a        *
*  particular purpose or non-infringing. The entire risk as to the     *
*  quality and performance of the Covered Software is with You.        *
*  Should any Covered Software prove defective in any respect, You     *
*  (not any Contributor) assume the cost of any necessary servicing,   *
*  repair, or correction. This disclaimer of warranty constitutes an   *
*  essential part of this License. No use of any Covered Software is   *
*  authorized under this License except under this disclaimer.         *
*                                                                      *
************************************************************************

************************************************************************
*                                                                      *
*  7. Limitation of Liability                                          *
*  --------------------------                                          *
*                                                                      *
*  Under no circumstances and under no legal theory, whether tort      *
*  (including negligence), contract, or otherwise, shall any           *
*  Contributor, or anyone who distributes Covered Software as          *
*  permitted above, be liable to You for any direct, indirect,         *
*  special, incidental, or consequential damages of any character      *
*  including, without limitation, damages for lost profits, loss of    *
*  goodwill, work stoppage, computer failure or malfunction, or any    *
*  and all other commercial damages or losses, even if such party      *
*  shall have been informed of the possibility of such damages. This   *
*  limitation of liability shall not apply to liability for death or   *
*  personal injury resulting from such party's negligence to the       *
*  extent applicable law prohibits such limitation. Some               *
*  jurisdictions do not allow the exclusion or limitation of           *
*  incidental or consequential damages, so this exclusion and          *
*  limitation may not apply to You.                                    *
*                                                                      *
************************************************************************

8. Litigation
-------------

Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.

9. Miscellaneous
----------------

This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.

10. Versions of the License
---------------------------

10.1. New Versions

Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.

10.2. Effect of New Versions

You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.

10.3. Modified Versions

If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses

If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice
-------------------------------------------

  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------

  This Source Code Form is "Incompatible With Secondary Licenses", as
  defined by the Mozilla Public License, v. 2.0.
0707010000000F000081A400000000000000000000000162794F93000011E0000000000000000000000000000000000000001400000000sops-3.7.3/Makefile# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

PROJECT		:= go.mozilla.org/sops/v3
GO 		:= GOPROXY=https://proxy.golang.org go
GOLINT 		:= golint

all: test vet generate install functional-tests
origin-build: test vet generate install functional-tests-all

install:
	$(GO) install go.mozilla.org/sops/v3/cmd/sops

tag: all
	git tag -s $(TAGVER) -a -m "$(TAGMSG)"

lint:
	$(GOLINT) $(PROJECT)

vendor:
	$(GO) mod tidy
	$(GO) mod vendor

vet:
	$(GO) vet $(PROJECT)

test: vendor
	gpg --import pgp/sops_functional_tests_key.asc 2>&1 1>/dev/null || exit 0
	./test.sh

showcoverage: test
	$(GO) tool cover -html=coverage.out

generate: keyservice/keyservice.pb.go
	$(GO) generate

%.pb.go: %.proto
	protoc --go_out=plugins=grpc:. $<

functional-tests:
	$(GO) build -o functional-tests/sops go.mozilla.org/sops/v3/cmd/sops
	cd functional-tests && cargo test

# Ignored tests are ones that require external services (e.g. AWS KMS)
# 	TODO: Once `--include-ignored` lands in rust stable, switch to that.
functional-tests-all:
	$(GO) build -o functional-tests/sops go.mozilla.org/sops/v3/cmd/sops
	cd functional-tests && cargo test && cargo test -- --ignored

# Creates variables during target re-definition. Basically this block allows the particular variables to be used in the final target
build-deb-%: OS = $(word 1,$(subst -, ,$*))
build-deb-%: ARCH = $(word 2,$(subst -, ,$*))
build-deb-%: FPM_ARCH = $(word 3,$(subst -, ,$*))
# Poor-mans function with parameters being split out from the variable part of it's name
build-deb-%:
	rm -rf tmppkg
	mkdir -p tmppkg/usr/local/bin
	GOOS=$(OS) GOARCH="$(ARCH)" CGO_ENABLED=0 go build -mod vendor -o tmppkg/usr/local/bin/sops go.mozilla.org/sops/v3/cmd/sops
	fpm -C tmppkg -n sops --license MPL2.0 --vendor mozilla \
		--description "Sops is an editor of encrypted files that supports YAML, JSON and BINARY formats and encrypts with AWS KMS and PGP." \
		-m "AJ Bahnken <ajvb+sops@mozilla.com>" \
		--url https://go.mozilla.org/sops \
		--architecture $(FPM_ARCH) \
		-v "$$(grep '^const Version' version/version.go |cut -d \" -f 2)" \
		-s dir -t deb .

# Create .deb packages for multiple architectures
deb-pkg: vendor build-deb-linux-amd64-x86_64 build-deb-linux-arm64-arm64

# Creates variables during target re-definition. Basically this block allows the particular variables to be used in the final target
build-rpm-%: OS = $(word 1,$(subst -, ,$*))
build-rpm-%: ARCH = $(word 2,$(subst -, ,$*))
build-rpm-%: FPM_ARCH = $(word 3,$(subst -, ,$*))
# Poor-mans function with parameters being split out from the variable part of it's name
build-rpm-%:
	rm -rf tmppkg
	mkdir -p tmppkg/usr/local/bin
	GOOS=$(OS) GOARCH="$(ARCH)" CGO_ENABLED=0 go build -mod vendor -o tmppkg/usr/local/bin/sops go.mozilla.org/sops/v3/cmd/sops
	fpm -C tmppkg -n sops --license MPL2.0 --vendor mozilla \
		--description "Sops is an editor of encrypted files that supports YAML, JSON and BINARY formats and encrypts with AWS KMS and PGP." \
		-m "AJ Bahnken <ajvb+sops@mozilla.com>" \
		--url https://go.mozilla.org/sops \
		--architecture $(FPM_ARCH) \
		--rpm-os $(OS) \
		-v "$$(grep '^const Version' version/version.go |cut -d \" -f 2)" \
		-s dir -t rpm .

# Create .rpm packages for multiple architectures
rpm-pkg: vendor build-rpm-linux-amd64-x86_64 build-rpm-linux-arm64-arm64

dmg-pkg: install
ifneq ($(OS),darwin)
		echo 'you must be on MacOS and set OS=darwin on the make command line to build an OSX package'
else
	rm -rf tmppkg
	mkdir -p tmppkg/usr/local/bin
	cp $$GOPATH/bin/sops tmppkg/usr/local/bin/
	fpm -C tmppkg -n sops --license MPL2.0 --vendor mozilla \
		--description "Sops is an editor of encrypted files that supports YAML, JSON and BINARY formats and encrypts with AWS KMS and PGP." \
		-m "Mozilla Security <security@mozilla.org>" \
		--url https://go.mozilla.org/sops \
		--architecture x86_64 \
		-v "$$(grep '^const Version' version/version.go |cut -d \" -f 2)" \
		-s dir -t osxpkg \
		--osxpkg-identifier-prefix org.mozilla.sops \
		-p tmppkg/sops-$$(git describe --abbrev=0 --tags).pkg .
	hdiutil makehybrid -hfs -hfs-volume-name "Mozilla Sops" \
		-o tmppkg/sops-$$(git describe --abbrev=0 --tags).dmg tmpdmg
endif

download-index:
	bash make_download_page.sh

mock:
	go install github.com/vektra/mockery/.../
	mockery -dir vendor/github.com/aws/aws-sdk-go/service/kms/kmsiface/ -name KMSAPI -output kms/mocks

.PHONY: all test generate clean vendor functional-tests mock
07070100000010000081A400000000000000000000000162794F930000FB57000000000000000000000000000000000000001600000000sops-3.7.3/README.rstSOPS: Secrets OPerationS
========================

**sops** is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, age, and PGP.
(`demo <https://www.youtube.com/watch?v=YTEVyLXFiq0>`_)

.. image:: https://i.imgur.com/X0TM5NI.gif

------------

.. image:: https://pkg.go.dev/badge/go.mozilla.org/sops/v3.svg
	:target: https://pkg.go.dev/go.mozilla.org/sops/v3

Download
--------

Stable release
~~~~~~~~~~~~~~
Binaries and packages of the latest stable release are available at `https://github.com/mozilla/sops/releases <https://github.com/mozilla/sops/releases>`_.

Development branch
~~~~~~~~~~~~~~~~~~
For the adventurous, unstable features are available in the `develop` branch, which you can install from source:

.. code:: bash

	$ mkdir -p $GOPATH/src/go.mozilla.org/sops/
        $ git clone https://github.com/mozilla/sops.git $GOPATH/src/go.mozilla.org/sops/
        $ cd $GOPATH/src/go.mozilla.org/sops/
        $ git checkout develop
        $ make install

(requires Go >= 1.17)

If you don't have Go installed, set it up with:

.. code:: bash

	$ {apt,yum,brew} install golang
	$ echo 'export GOPATH=~/go' >> ~/.bashrc
	$ source ~/.bashrc
	$ mkdir $GOPATH

Or whatever variation of the above fits your system and shell.

To use **sops** as a library, take a look at the `decrypt package <https://pkg.go.dev/go.mozilla.org/sops/v3/decrypt>`_.

.. sectnum::
.. contents:: Table of Contents

Usage
-----

For a quick presentation of Sops, check out this Youtube tutorial:

.. image:: https://img.youtube.com/vi/V2PRhxphH2w/0.jpg
   :target: https://www.youtube.com/watch?v=V2PRhxphH2w

If you're using AWS KMS, create one or multiple master keys in the IAM console
and export them, comma separated, in the **SOPS_KMS_ARN** env variable. It is
recommended to use at least two master keys in different regions.

.. code:: bash

	export SOPS_KMS_ARN="arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e,arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d"

Your AWS credentials must be present in ``~/.aws/credentials``. sops uses aws-sdk-go.

.. code::

	$ cat ~/.aws/credentials
	[default]
	aws_access_key_id = AKI.....
	aws_secret_access_key = mw......

If you want to use PGP, export the fingerprints of the public keys, comma
separated, in the **SOPS_PGP_FP** env variable.

.. code:: bash

	export SOPS_PGP_FP="85D77543B3D624B63CEA9E6DBC17301B491B3F21,E60892BB9BD89A69F759A1A0A3D652173B763E8F"

Note: you can use both PGP and KMS simultaneously.

Then simply call ``sops`` with a file path as argument. It will handle the
encryption/decryption transparently and open the cleartext file in an editor

.. code:: shell

	$ sops mynewtestfile.yaml
	mynewtestfile.yaml doesn't exist, creating it.
	please wait while an encryption key is being generated and stored in a secure fashion
	file written to mynewtestfile.yaml

Editing will happen in whatever ``$EDITOR`` is set to, or, if it's not set, in vim.
Keep in mind that sops will wait for the editor to exit, and then try to reencrypt
the file. Some GUI editors (atom, sublime) spawn a child process and then exit
immediately. They usually have an option to wait for the main editor window to be
closed before exiting. See `#127 <https://github.com/mozilla/sops/issues/127>`_ for
more information.

The resulting encrypted file looks like this:

.. code:: yaml

    myapp1: ENC[AES256_GCM,data:Tr7o=,iv:1=,aad:No=,tag:k=]
    app2:
        db:
            user: ENC[AES256_GCM,data:CwE4O1s=,iv:2k=,aad:o=,tag:w==]
            password: ENC[AES256_GCM,data:p673w==,iv:YY=,aad:UQ=,tag:A=]
        # private key for secret operations in app2
        key: |-
            ENC[AES256_GCM,data:Ea3kL5O5U8=,iv:DM=,aad:FKA=,tag:EA==]
    an_array:
    - ENC[AES256_GCM,data:v8jQ=,iv:HBE=,aad:21c=,tag:gA==]
    - ENC[AES256_GCM,data:X10=,iv:o8=,aad:CQ=,tag:Hw==]
    - ENC[AES256_GCM,data:KN=,iv:160=,aad:fI4=,tag:tNw==]
    sops:
        kms:
        -   created_at: 1441570389.775376
            enc: CiC....Pm1Hm
            arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
        -   created_at: 1441570391.925734
            enc: Ci...awNx
            arn: arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d
        pgp:
        -   fp: 85D77543B3D624B63CEA9E6DBC17301B491B3F21
            created_at: 1441570391.930042
            enc: |
                -----BEGIN PGP MESSAGE-----
                hQIMA0t4uZHfl9qgAQ//UvGAwGePyHuf2/zayWcloGaDs0MzI+zw6CmXvMRNPUsA
				...=oJgS
                -----END PGP MESSAGE-----

A copy of the encryption/decryption key is stored securely in each KMS and PGP
block. As long as one of the KMS or PGP method is still usable, you will be able
to access your data.

To decrypt a file in a ``cat`` fashion, use the ``-d`` flag:

.. code:: bash

	$ sops -d mynewtestfile.yaml

``sops`` encrypted files contain the necessary information to decrypt their content.
All a user of ``sops`` needs is valid AWS credentials and the necessary
permissions on KMS keys.

Given that, the only command a ``sops`` user needs is:

.. code:: bash

	$ sops <file>

`<file>` will be opened, decrypted, passed to a text editor (vim by default),
encrypted if modified, and saved back to its original location. All of these
steps, apart from the actual editing, are transparent to the user.

Test with the dev PGP key
~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to test **sops** without having to do a bunch of setup, you can use
the example files and pgp key provided with the repository::

	$ git clone https://github.com/mozilla/sops.git
	$ cd sops
	$ gpg --import pgp/sops_functional_tests_key.asc
	$ sops example.yaml

This last step will decrypt ``example.yaml`` using the test private key.


Encrypting using age
~~~~~~~~~~~~~~~~~~~~

`age <https://age-encryption.org/>`_ is a simple, modern, and secure tool for
encrypting files. It's recommended to use age over PGP, if possible.

You can encrypt a file for one or more age recipients (comma separated) using
the ``--age`` option or the **SOPS_AGE_RECIPIENTS** environment variable:

.. code:: bash

   $ sops --encrypt --age age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw test.yaml > test.enc.yaml

When decrypting a file with the corresponding identity, sops will look for a
text file name ``keys.txt`` located in a ``sops`` subdirectory of your user
configuration directory. On Linux, this would be ``$XDG_CONFIG_HOME/sops/age/keys.txt``.
On macOS, this would be ``$HOME/Library/Application Support/sops/age/keys.txt``. On
Windows, this would be ``%AppData%\sops\age\keys.txt``. You can specify the location
of this file manually by setting the environment variable **SOPS_AGE_KEY_FILE**.
Alternatively you can provide the the key(s) directly by setting the **SOPS_AGE_KEY**
environment variable.

The contents of this key file should be a list of age X25519 identities, one
per line. Lines beginning with ``#`` are considered comments and ignored. Each
identity will be tried in sequence until one is able to decrypt the data.

Encrypting with SSH keys via age is not yet supported by sops.


Encrypting using GCP KMS
~~~~~~~~~~~~~~~~~~~~~~~~
GCP KMS uses `Application Default Credentials
<https://developers.google.com/identity/protocols/application-default-credentials>`_.
If you already logged in using

.. code:: bash

	$ gcloud auth login

you can enable application default credentials using the sdk::

	$ gcloud auth application-default login

Encrypting/decrypting with GCP KMS requires a KMS ResourceID. You can use the
cloud console the get the ResourceID or you can create one using the gcloud
sdk:

.. code:: bash

	$ gcloud kms keyrings create sops --location global
	$ gcloud kms keys create sops-key --location global --keyring sops --purpose encryption
	$ gcloud kms keys list --location global --keyring sops

	# you should see
	NAME                                                                   PURPOSE          PRIMARY_STATE
	projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key ENCRYPT_DECRYPT  ENABLED

Now you can encrypt a file using::

	$ sops --encrypt --gcp-kms projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key test.yaml > test.enc.yaml

And decrypt it using::

	 $ sops --decrypt test.enc.yaml

Encrypting using Azure Key Vault
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The Azure Key Vault integration tries several authentication methods, in
this order:

  1. Client credentials
  2. Client Certificate
  3. Username Password
  4. MSI
  5. Azure CLI auth

You can force a specific authentication method through the AZURE_AUTH_METHOD
environment variable, which may be one of: clientcredentials, clientcertificate,
usernamepassword, msi, or cli (default).

For example, you can use service principals with the following environment variables:

.. code:: bash

	AZURE_TENANT_ID
	AZURE_CLIENT_ID
	AZURE_CLIENT_SECRET

You can create a service principal using the cli like this:

.. code:: bash

	$ az ad sp create-for-rbac -n my-keyvault-sp

	{
		"appId": "<some-uuid>",
		"displayName": "my-keyvault-sp",
		"name": "http://my-keyvault-sp",
		"password": "<some-uuid>",
		"tenant": "<tenant-id>"
	}

The appId is the client id, and the password is the client secret.

Encrypting/decrypting with Azure Key Vault requires the resource identifier for
a key. This has the following form::

	https://${VAULT_URL}/keys/${KEY_NAME}/${KEY_VERSION}

To create a Key Vault and assign your service principal permissions on it
from the commandline:

.. code:: bash

	# Create a resource group if you do not have one:
	$ az group create --name sops-rg --location westeurope
	# Key Vault names are globally unique, so generate one:
	$ keyvault_name=sops-$(uuidgen | tr -d - | head -c 16)
	# Create a Vault, a key, and give the service principal access:
	$ az keyvault create --name $keyvault_name --resource-group sops-rg --location westeurope
	$ az keyvault key create --name sops-key --vault-name $keyvault_name --protection software --ops encrypt decrypt
	$ az keyvault set-policy --name $keyvault_name --resource-group sops-rg --spn $AZURE_CLIENT_ID \
		--key-permissions encrypt decrypt
	# Read the key id:
	$ az keyvault key show --name sops-key --vault-name $keyvault_name --query key.kid

	https://sops.vault.azure.net/keys/sops-key/some-string

Now you can encrypt a file using::

	$ sops --encrypt --azure-kv https://sops.vault.azure.net/keys/sops-key/some-string test.yaml > test.enc.yaml

And decrypt it using::

	 $ sops --decrypt test.enc.yaml


Encrypting using Hashicorp Vault
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We assume you have an instance (or more) of Vault running and you have privileged access to it. For instructions on how to deploy a secure instance of Vault, refer to Hashicorp's official documentation.

To easily deploy Vault locally: (DO NOT DO THIS FOR PRODUCTION!!!) 

.. code:: bash

	$ docker run -d -p8200:8200 vault:1.2.0 server -dev -dev-root-token-id=toor


.. code:: bash

	$ # Substitute this with the address Vault is running on
	$ export VAULT_ADDR=http://127.0.0.1:8200 

	$ # this may not be necessary in case you previously used `vault login` for production use
	$ export VAULT_TOKEN=toor 
	
	$ # to check if Vault started and is configured correctly
	$ vault status
	Key             Value
	---             -----
	Seal Type       shamir
	Initialized     true
	Sealed          false
	Total Shares    1
	Threshold       1
	Version         1.2.0
	Cluster Name    vault-cluster-618cc902
	Cluster ID      e532e461-e8f0-1352-8a41-fc7c11096908
	HA Enabled      false

	$ # It is required to enable a transit engine if not already done (It is suggested to create a transit engine specifically for sops, in which it is possible to have multiple keys with various permission levels)
	$ vault secrets enable -path=sops transit
	Success! Enabled the transit secrets engine at: sops/

	$ # Then create one or more keys
	$ vault write sops/keys/firstkey type=rsa-4096
	Success! Data written to: sops/keys/firstkey

	$ vault write sops/keys/secondkey type=rsa-2048
	Success! Data written to: sops/keys/secondkey

	$ vault write sops/keys/thirdkey type=chacha20-poly1305
	Success! Data written to: sops/keys/thirdkey

	$ sops --hc-vault-transit $VAULT_ADDR/v1/sops/keys/firstkey vault_example.yml

	$ cat <<EOF > .sops.yaml
	creation_rules:
		- path_regex: \.dev\.yaml$
		  hc_vault_transit_uri: "$VAULT_ADDR/v1/sops/keys/secondkey"
		- path_regex: \.prod\.yaml$
		  hc_vault_transit_uri: "$VAULT_ADDR/v1/sops/keys/thirdkey"
	EOF

	$ sops --verbose -e prod/raw.yaml > prod/encrypted.yaml

Adding and removing keys
~~~~~~~~~~~~~~~~~~~~~~~~

When creating new files, ``sops`` uses the PGP, KMS and GCP KMS defined in the
command line arguments ``--kms``, ``--pgp``, ``--gcp-kms`` or ``--azure-kv``, or from
the environment variables ``SOPS_KMS_ARN``, ``SOPS_PGP_FP``, ``SOPS_GCP_KMS_IDS``,
``SOPS_AZURE_KEYVAULT_URLS``. That information is stored in the file under the
``sops`` section, such that decrypting files does not require providing those
parameters again.

Master PGP and KMS keys can be added and removed from a ``sops`` file in one of
three ways::

1. By using a .sops.yaml file and the ``updatekeys`` command.

2. By using command line flags.

3. By editing the file directly.

The sops team recommends the ``updatekeys`` approach.


``updatekeys`` command
**********************

The ``updatekeys`` command uses the `.sops.yaml <#using-sops-yaml-conf-to-select-kms-pgp-for-new-files>`_
configuration file to update (add or remove) the corresponding secrets in the
encrypted file. Note that the example below uses the
`Block Scalar yaml construct <https://yaml-multiline.info/>`_ to build a space
separated list.

.. code:: yaml

    creation_rules:
        - pgp: >-
            85D77543B3D624B63CEA9E6DBC17301B491B3F21,
            FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4

.. code:: bash

	$ sops updatekeys test.enc.yaml

Sops will prompt you with the changes to be made. This interactivity can be
disabled by supplying the ``-y`` flag.

Command Line
************

Command line flag ``--add-kms``, ``--add-pgp``, ``--add-gcp-kms``, ``--add-azure-kv``,
``--rm-kms``, ``--rm-pgp``, ``--rm-gcp-kms`` and ``--rm-azure-kv`` can be used to add
and remove keys from a file.
These flags use the comma separated syntax as the ``--kms``, ``--pgp``, ``--gcp-kms``
and ``--azure-kv`` arguments when creating new files.

Note that ``-r`` or ``--rotate`` is mandatory in this mode. Not specifying
rotate will ignore the ``--add-*`` options. Use ``updatekeys`` if you want to
add a key without rotating the data key.

.. code:: bash

	# add a new pgp key to the file and rotate the data key
	$ sops -r -i --add-pgp 85D77543B3D624B63CEA9E6DBC17301B491B3F21 example.yaml

	# remove a pgp key from the file and rotate the data key
	$ sops -r -i --rm-pgp 85D77543B3D624B63CEA9E6DBC17301B491B3F21 example.yaml


Direct Editing
**************

Alternatively, invoking ``sops`` with the flag **-s** will display the master keys
while editing. This method can be used to add or remove kms or pgp keys under the
sops section. Invoking ``sops`` with the **-i** flag will perform an in-place edit
instead of redirecting output to ``stdout``.

For example, to add a KMS master key to a file, add the following entry while
editing:

.. code:: yaml

	sops:
	    kms:
	    - arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e

And, similarly, to add a PGP master key, we add its fingerprint:

.. code:: yaml

	sops:
	    pgp:
	    - fp: 85D77543B3D624B63CEA9E6DBC17301B491B3F21

When the file is saved, ``sops`` will update its metadata and encrypt the data key
with the freshly added master keys. The removed entries are simply deleted from
the file.

When removing keys, it is recommended to rotate the data key using ``-r``,
otherwise owners of the removed key may have add access to the data key in the
past.

KMS AWS Profiles
~~~~~~~~~~~~~~~~

If you want to use a specific profile, you can do so with `aws_profile`:

.. code:: yaml

	sops:
	    kms:
	    -	arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
	        aws_profile: foo

If no AWS profile is set, default credentials will be used.

Similarly the `--aws-profile` flag can be set with the command line with any of the KMS commands.


Assuming roles and using KMS in various AWS accounts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

SOPS has the ability to use KMS in multiple AWS accounts by assuming roles in
each account. Being able to assume roles is a nice feature of AWS that allows
administrators to establish trust relationships between accounts, typically from
the most secure account to the least secure one. In our use-case, we use roles
to indicate that a user of the Master AWS account is allowed to make use of KMS
master keys in development and staging AWS accounts. Using roles, a single file
can be encrypted with KMS keys in multiple accounts, thus increasing reliability
and ease of use.

You can use keys in various accounts by tying each KMS master key to a role that
the user is allowed to assume in each account. The `IAM roles
<http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html>`_
documentation has full details on how this needs to be configured on AWS's side.

From the point of view of ``sops``, you only need to specify the role a KMS key
must assume alongside its ARN, as follows:

.. code:: yaml

	sops:
	    kms:
	    -	arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
	        role: arn:aws:iam::927034868273:role/sops-dev-xyz

The role must have permission to call Encrypt and Decrypt using KMS. An example
policy is shown below.

.. code:: json

	{
	  "Sid": "Allow use of the key",
	  "Effect": "Allow",
	  "Action": [
		"kms:Encrypt",
		"kms:Decrypt",
		"kms:ReEncrypt*",
		"kms:GenerateDataKey*",
		"kms:DescribeKey"
	  ],
	  "Resource": "*",
	  "Principal": {
		"AWS": [
		  "arn:aws:iam::927034868273:role/sops-dev-xyz"
		]
	  }
	}

You can specify a role in the ``--kms`` flag and ``SOPS_KMS_ARN`` variable by
appending it to the ARN of the master key, separated by a **+** sign::

	<KMS ARN>+<ROLE ARN>
	arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500+arn:aws:iam::927034868273:role/sops-dev-xyz

AWS KMS Encryption Context
~~~~~~~~~~~~~~~~~~~~~~~~~~

SOPS has the ability to use `AWS KMS key policy and encryption context
<http://docs.aws.amazon.com/kms/latest/developerguide/encryption-context.html>`_
to refine the access control of a given KMS master key.

When creating a new file, you can specify encryption context in the
``--encryption-context`` flag by comma separated list of key-value pairs:

.. code:: bash

	$ sops --encryption-context Environment:production,Role:web-server test.dev.yaml

The format of the Encrypt Context string is ``<EncryptionContext Key>:<EncryptionContext Value>,<EncryptionContext Key>:<EncryptionContext Value>,...``

The encryption context will be stored in the file metadata and does
not need to be provided at decryption.

Encryption contexts can be used in conjunction with KMS Key Policies to define
roles that can only access a given context. An example policy is shown below:

.. code:: json

    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/RoleForExampleApp"
      },
      "Action": "kms:Decrypt",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "kms:EncryptionContext:AppName": "ExampleApp",
          "kms:EncryptionContext:FilePath": "/var/opt/secrets/"
        }
      }
    }

Key Rotation
~~~~~~~~~~~~

It is recommended to renew the data key on a regular basis. ``sops`` supports key
rotation via the ``-r`` flag. Invoking it on an existing file causes sops to
reencrypt the file with a new data key, which is then encrypted with the various
KMS and PGP master keys defined in the file.

.. code:: bash

	sops -r example.yaml

Using .sops.yaml conf to select KMS/PGP for new files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is often tedious to specify the ``--kms`` ``--gcp-kms`` and ``--pgp`` parameters for creation
of all new files. If your secrets are stored under a specific directory, like a
``git`` repository, you can create a ``.sops.yaml`` configuration file at the root
directory to define which keys are used for which filename.

Let's take an example:

* file named **something.dev.yaml** should use one set of KMS A
* file named **something.prod.yaml** should use another set of KMS B
* other files use a third set of KMS C
* all live under **mysecretrepo/something.{dev,prod,gcp}.yaml**

Under those circumstances, a file placed at **mysecretrepo/.sops.yaml**
can manage the three sets of configurations for the three types of files:

.. code:: yaml

	# creation rules are evaluated sequentially, the first match wins
	creation_rules:
		# upon creation of a file that matches the pattern *.dev.yaml,
		# KMS set A is used
		- path_regex: \.dev\.yaml$
		  kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod'
		  pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'

		# prod files use KMS set B in the PROD IAM
		- path_regex: \.prod\.yaml$
		  kms: 'arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod,arn:aws:kms:eu-central-1:361527076523:key/cb1fab90-8d17-42a1-a9d8-334968904f94+arn:aws:iam::361527076523:role/hiera-sops-prod'
		  pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
		  hc_vault_uris: "http://localhost:8200/v1/sops/keys/thirdkey"

		# gcp files using GCP KMS
		- path_regex: \.gcp\.yaml$
		  gcp_kms: projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekey

		# Finally, if the rules above have not matched, this one is a
		# catchall that will encrypt the file using KMS set C
		# The absence of a path_regex means it will match everything
		- kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:142069644989:key/846cfb17-373d-49b9-8baf-f36b04512e47,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e'
		  pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'

When creating any file under **mysecretrepo**, whether at the root or under
a subdirectory, sops will recursively look for a ``.sops.yaml`` file. If one is
found, the filename of the file being created is compared with the filename
regexes of the configuration file. The first regex that matches is selected,
and its KMS and PGP keys are used to encrypt the file. It should be noted that
the looking up of ``.sops.yaml`` is from the working directory (CWD) instead of
the directory of the encrypting file (see `Issue 242 <https://github.com/mozilla/sops/issues/242>`_).

The path_regex checks the path of the encrypting file relative to the .sops.yaml config file. Here is another example:

* files located under directory **development** should use one set of KMS A
* files located under directory **production** should use another set of KMS B
* other files use a third set of KMS C

.. code:: yaml

    creation_rules:
        # upon creation of a file under development,
        # KMS set A is used
        - path_regex: .*/development/.*
          kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod'
          pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'

        # prod files use KMS set B in the PROD IAM
        - path_regex: .*/production/.*
          kms: 'arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod,arn:aws:kms:eu-central-1:361527076523:key/cb1fab90-8d17-42a1-a9d8-334968904f94+arn:aws:iam::361527076523:role/hiera-sops-prod'
          pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'

        # other files use KMS set C
        - kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:142069644989:key/846cfb17-373d-49b9-8baf-f36b04512e47,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e'
          pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'

Creating a new file with the right keys is now as simple as

.. code:: bash

	$ sops <newfile>.prod.yaml

Note that the configuration file is ignored when KMS or PGP parameters are
passed on the sops command line or in environment variables.

Specify a different GPG executable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``sops`` checks for the ``SOPS_GPG_EXEC`` environment variable. If specified,
it will attempt to use the executable set there instead of the default
of ``gpg``.

Example: place the following in your ``~/.bashrc``

.. code:: bash

	SOPS_GPG_EXEC = 'your_gpg_client_wrapper'


Specify a different GPG key server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

By default, ``sops`` uses the key server ``keys.openpgp.org`` to retrieve the GPG
keys that are not present in the local keyring.
This is no longer configurable. You can learn more about why from this write-up: `SKS Keyserver Network Under Attack <https://gist.github.com/rjhansen/67ab921ffb4084c865b3618d6955275f>`_.


Key groups
~~~~~~~~~~

By default, ``sops`` encrypts the data key for a file with each of the master keys,
such that if any of the master keys is available, the file can be decrypted.
However, it is sometimes desirable to require access to multiple master keys
in order to decrypt files. This can be achieved with key groups.

When using key groups in sops, data keys are split into parts such that keys from
multiple groups are required to decrypt a file. ``sops`` uses Shamir's Secret Sharing
to split the data key such that each key group has a fragment, each key in the
key group can decrypt that fragment, and a configurable number of fragments (threshold)
are needed to decrypt and piece together the complete data key. When decrypting a
file using multiple key groups, ``sops`` goes through key groups in order, and in
each group, tries to recover the fragment of the data key using a master key from
that group. Once the fragment is recovered, ``sops`` moves on to the next group,
until enough fragments have been recovered to obtain the complete data key.

By default, the threshold is set to the number of key groups. For example, if
you have three key groups configured in your SOPS file and you don't override
the default threshold, then one master key from each of the three groups will
be required to decrypt the file.

Management of key groups is done with the ``sops groups`` command.

For example, you can add a new key group with 3 PGP keys and 3 KMS keys to the
file ``my_file.yaml``:

.. code:: bash

    $ sops groups add --file my_file.yaml --pgp fingerprint1 --pgp fingerprint2 --pgp fingerprint3 --kms arn1 --kms arn2 --kms arn3

Or you can delete the 1st group (group number 0, as groups are zero-indexed)
from ``my_file.yaml``:

.. code:: bash

    $ sops groups delete --file my_file.yaml 0

Key groups can also be specified in the ``.sops.yaml`` config file,
like so:

.. code:: yaml

    creation_rules:
        - path_regex: .*keygroups.*
          key_groups:
          # First key group
          - pgp:
            - fingerprint1
            - fingerprint2
            kms:
            - arn: arn1
              role: role1
              context:
                foo: bar
            - arn: arn2
          # Second key group
          - pgp:
            - fingerprint3
            - fingerprint4
            kms:
            - arn: arn3
            - arn: arn4
          # Third key group
          - pgp:
            - fingerprint5

Given this configuration, we can create a new encrypted file like we normally
would, and optionally provide the ``--shamir-secret-sharing-threshold`` command line
flag if we want to override the default threshold. ``sops`` will then split the data
key into three parts (from the number of key groups) and encrypt each fragment with
the master keys found in each group.

For example:

.. code:: bash

    $ sops --shamir-secret-sharing-threshold 2 example.json

Alternatively, you can configure the Shamir threshold for each creation rule in the ``.sops.yaml`` config
with ``shamir_threshold``:

.. code:: yaml

    creation_rules:
        - path_regex: .*keygroups.*
          shamir_threshold: 2
          key_groups:
          # First key group
          - pgp:
            - fingerprint1
            - fingerprint2
            kms:
            - arn: arn1
              role: role1
              context:
                foo: bar
            - arn: arn2
          # Second key group
          - pgp:
            - fingerprint3
            - fingerprint4
            kms:
            - arn: arn3
            - arn: arn4
          # Third key group
          - pgp:
            - fingerprint5

And then run ``sops example.json``.

The threshold (``shamir_threshold``) is set to 2, so this configuration will require
master keys from two of the three different key groups in order to decrypt the file.
You can then decrypt the file the same way as with any other SOPS file:

.. code:: bash

    $ sops -d example.json

Key service
~~~~~~~~~~~

There are situations where you might want to run ``sops`` on a machine that
doesn't have direct access to encryption keys such as PGP keys. The ``sops`` key
service allows you to forward a socket so that ``sops`` can access encryption
keys stored on a remote machine. This is similar to GPG Agent, but more
portable.

SOPS uses a client-server approach to encrypting and decrypting the data
key. By default, SOPS runs a local key service in-process. SOPS uses a key
service client to send an encrypt or decrypt request to a key service, which
then performs the operation. The requests are sent using gRPC and Protocol
Buffers. The requests contain an identifier for the key they should perform
the operation with, and the plaintext or encrypted data key. The requests do
not contain any cryptographic keys, public or private.

**WARNING: the key service connection currently does not use any sort of
authentication or encryption. Therefore, it is recommended that you make sure
the connection is authenticated and encrypted in some other way, for example
through an SSH tunnel.**

Whenever we try to encrypt or decrypt a data key, SOPS will try to do so first
with the local key service (unless it's disabled), and if that fails, it will
try all other remote key services until one succeeds.

You can start a key service server by running ``sops keyservice``.

You can specify the key services the ``sops`` binary uses with ``--keyservice``.
This flag can be specified more than once, so you can use multiple key
services. The local key service can be disabled with
``enable-local-keyservice=false``.

For example, to decrypt a file using both the local key service and the key
service exposed on the unix socket located in ``/tmp/sops.sock``, you can run:

.. code:: bash

    $ sops --keyservice unix:///tmp/sops.sock -d file.yaml`

And if you only want to use the key service exposed on the unix socket located
in ``/tmp/sops.sock`` and not the local key service, you can run:

.. code:: bash

    $ sops --enable-local-keyservice=false --keyservice unix:///tmp/sops.sock -d file.yaml

Auditing
~~~~~~~~

Sometimes, users want to be able to tell what files were accessed by whom in an
environment they control. For this reason, SOPS can generate audit logs to
record activity on encrypted files. When enabled, SOPS will write a log entry
into a pre-configured PostgreSQL database when a file is decrypted. The log
includes a timestamp, the username SOPS is running as, and the file that was
decrypted.

In order to enable auditing, you must first create the database and credentials
using the schema found in ``audit/schema.sql``. This schema defines the
tables that store the audit events and a role named ``sops`` that only has
permission to add entries to the audit event tables. The default password for
the role ``sops`` is ``sops``. You should change this password.

Once you have created the database, you have to tell SOPS how to connect to it.
Because we don't want users of SOPS to be able to control auditing, the audit
configuration file location is not configurable, and must be at
``/etc/sops/audit.yaml``. This file should have strict permissions such
that only the root user can modify it.

For example, to enable auditing to a PostgreSQL database named ``sops`` running
on localhost, using the user ``sops`` and the password ``sops``,
``/etc/sops/audit.yaml`` should have the following contents:

.. code:: yaml

    backends:
        postgres:
            - connection_string: "postgres://sops:sops@localhost/sops?sslmode=verify-full"


You can find more information on the ``connection_string`` format in the
`PostgreSQL docs <https://www.postgresql.org/docs/current/static/libpq-connect.html#libpq-connstring>`_.

Under the ``postgres`` map entry in the above YAML is a list, so one can
provide more than one backend, and SOPS will log to all of them:

.. code:: yaml

    backends:
        postgres:
            - connection_string: "postgres://sops:sops@localhost/sops?sslmode=verify-full"
            - connection_string: "postgres://sops:sops@remotehost/sops?sslmode=verify-full"

Saving Output to a File
~~~~~~~~~~~~~~~~~~~~~~~
By default ``sops`` just dumps all the output to the standard output. We can use the
``--output`` flag followed by a filename to save the output to the file specified.
Beware using both ``--in-place`` and ``--output`` flags will result in an error.

Passing Secrets to Other Processes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In addition to writing secrets to standard output and to files on disk, ``sops``
has two commands for passing decrypted secrets to a new process: ``exec-env``
and ``exec-file``. These commands will place all output into the environment of
a child process and into a temporary file, respectively. For example, if a
program looks for credentials in its environment, ``exec-env`` can be used to
ensure that the decrypted contents are available only to this process and never
written to disk.

.. code:: bash

   # print secrets to stdout to confirm values
   $ sops -d out.json
   {
           "database_password": "jf48t9wfw094gf4nhdf023r",
           "AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
           "AWS_SECRET_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
   }

   # decrypt out.json and run a command
   # the command prints the environment variable and runs a script that uses it
   $ sops exec-env out.json 'echo secret: $database_password; ./database-import'
   secret: jf48t9wfw094gf4nhdf023r

   # launch a shell with the secrets available in its environment
   $ sops exec-env out.json 'sh'
   sh-3.2# echo $database_password
   jf48t9wfw094gf4nhdf023r

   # the secret is not accessible anywhere else
   sh-3.2$ exit
   $ echo your password: $database_password
   your password:


If the command you want to run only operates on files, you can use ``exec-file``
instead. By default ``sops`` will use a FIFO to pass the contents of the
decrypted file to the new program. Using a FIFO, secrets are only passed in
memory which has two benefits: the plaintext secrets never touch the disk, and
the child process can only read the secrets once. In contexts where this won't
work, eg platforms like Windows where FIFOs unavailable or secret files that need
to be available to the child process longer term, the ``--no-fifo`` flag can be
used to instruct ``sops`` to use a traditional temporary file that will get cleaned
up once the process is finished executing. ``exec-file`` behaves similar to
``find(1)`` in that ``{}`` is used as a placeholder in the command which will be
substituted with the temporary file path (whether a FIFO or an actual file).

.. code:: bash

   # operating on the same file as before, but as a file this time
   $ sops exec-file out.json 'echo your temporary file: {}; cat {}'
   your temporary file: /tmp/.sops894650499/tmp-file
   {
           "database_password": "jf48t9wfw094gf4nhdf023r",
           "AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
           "AWS_SECRET_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
   }

   # launch a shell with a variable TMPFILE pointing to the temporary file
   $ sops exec-file --no-fifo out.json 'TMPFILE={} sh'
   sh-3.2$ echo $TMPFILE
   /tmp/.sops506055069/tmp-file291138648
   sh-3.2$ cat $TMPFILE
   {
           "database_password": "jf48t9wfw094gf4nhdf023r",
           "AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
           "AWS_SECRET_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
   }
   sh-3.2$ ./program --config $TMPFILE
   sh-3.2$ exit

   # try to open the temporary file from earlier
   $ cat /tmp/.sops506055069/tmp-file291138648
   cat: /tmp/.sops506055069/tmp-file291138648: No such file or directory

Additionally, on unix-like platforms, both ``exec-env`` and ``exec-file``
support dropping privileges before executing the new program via the
``--user <username>`` flag. This is particularly useful in cases where the
encrypted file is only readable by root, but the target program does not
need root privileges to function. This flag should be used where possible
for added security.

To overwrite the default file name (``tmp-file``) in ``exec-file`` use the
``--filename <filename>`` parameter.

.. code:: bash

   # the encrypted file can't be read by the current user
   $ cat out.json
   cat: out.json: Permission denied

   # execute sops as root, decrypt secrets, then drop privileges
   $ sudo sops exec-env --user nobody out.json 'sh'
   sh-3.2$ echo $database_password
   jf48t9wfw094gf4nhdf023r

   # dropped privileges, still can't load the original file
   sh-3.2$ id
   uid=4294967294(nobody) gid=4294967294(nobody) groups=4294967294(nobody)
   sh-3.2$ cat out.json
   cat: out.json: Permission denied

Using the publish command
~~~~~~~~~~~~~~~~~~~~~~~~~
``sops publish $file`` publishes a file to a pre-configured destination (this lives in the sops
config file). Additionally, support re-encryption rules that work just like the creation rules.

This command requires a ``.sops.yaml`` configuration file. Below is an example:

.. code:: yaml

   destination_rules:
      - s3_bucket: "sops-secrets"
        path_regex: s3/*
        recreation_rule:
           pgp: F69E4901EDBAD2D1753F8C67A64535C4163FB307
      - gcs_bucket: "sops-secrets"
        path_regex: gcs/*
        recreation_rule:
           pgp: F69E4901EDBAD2D1753F8C67A64535C4163FB307
      - vault_path: "sops/"
        vault_kv_mount_name: "secret/" # default
        vault_kv_version: 2 # default
        path_regex: vault/*
        omit_extensions: true

The above configuration will place all files under ``s3/*`` into the S3 bucket ``sops-secrets``,
all files under ``gcs/*`` into the GCS bucket ``sops-secrets``, and the contents of all files under
``vault/*`` into Vault's KV store under the path ``secrets/sops/``. For the files that will be
published to S3 and GCS, it will decrypt them and re-encrypt them using the
``F69E4901EDBAD2D1753F8C67A64535C4163FB307`` pgp key.

You would deploy a file to S3 with a command like: ``sops publish s3/app.yaml``

To publish all files in selected directory recursively, you need to specify ``--recursive`` flag.

If you don't want file extension to appear in destination secret path, use ``--omit-extensions``
flag or ``omit_extensions: true`` in the destination rule in ``.sops.yaml``.

Publishing to Vault
*******************

There are a few settings for Vault that you can place in your destination rules. The first
is ``vault_path``, which is required. The others are optional, and they are
``vault_address``, ``vault_kv_mount_name``, ``vault_kv_version``.

``sops`` uses the official Vault API provided by Hashicorp, which makes use of `environment
variables <https://www.vaultproject.io/docs/commands/#environment-variables>`_ for
configuring the client.

``vault_kv_mount_name`` is used if your Vault KV is mounted somewhere other than ``secret/``.
``vault_kv_version`` supports ``1`` and ``2``, with ``2`` being the default.

If destination secret path already exists in Vault and contains same data as the source file, it
will be skipped.

Below is an example of publishing to Vault (using token auth with a local dev instance of Vault).

.. code:: bash

   $ export VAULT_TOKEN=...
   $ export VAULT_ADDR='http://127.0.0.1:8200'
   $ sops -d vault/test.yaml
   example_string: bar
   example_number: 42
   example_map:
       key: value
   $ sops publish vault/test.yaml
   uploading /home/user/sops_directory/vault/test.yaml to http://127.0.0.1:8200/v1/secret/data/sops/test.yaml ? (y/n): y
   $ vault kv get secret/sops/test.yaml
   ====== Metadata ======
   Key              Value
   ---              -----
   created_time     2019-07-11T03:32:17.074792017Z
   deletion_time    n/a
   destroyed        false
   version          3

   ========= Data =========
   Key               Value
   ---               -----
   example_map       map[key:value]
   example_number    42
   example_string    bar


Important information on types
------------------------------

YAML and JSON type extensions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``sops`` uses the file extension to decide which encryption method to use on the file
content. ``YAML``, ``JSON``, ``ENV``, and ``INI`` files are treated as trees of data, and key/values are
extracted from the files to only encrypt the leaf values. The tree structure is also
used to check the integrity of the file.

Therefore, if a file is encrypted using a specific format, it need to be decrypted
in the same format. The easiest way to achieve this is to conserve the original file
extension after encrypting a file. For example:

.. code:: bash

	$ sops -e -i myfile.json
	$ sops -d myfile.json

If you want to change the extension of the file once encrypted, you need to provide
sops with the ``--input-type`` flag upon decryption. For example:

.. code:: bash

	$ sops -e myfile.json > myfile.json.enc

	$ sops -d --input-type json myfile.json.enc

When operating on stdin, use the ``--input-type`` and ``--output-type`` flags as follows:

.. code:: bash

    $ cat myfile.json | sops --input-type json --output-type json -d /dev/stdin

YAML anchors
~~~~~~~~~~~~
``sops`` only supports a subset of ``YAML``'s many types. Encrypting YAML files that
contain strings, numbers and booleans will work fine, but files that contain anchors
will not work, because the anchors redefine the structure of the file at load time.

This file will not work in ``sops``:

.. code:: yaml

	bill-to:  &id001
	    street: |
	        123 Tornado Alley
	        Suite 16
	    city:   East Centerville
	    state:  KS

	ship-to:  *id001

``sops`` uses the path to a value as additional data in the AEAD encryption, and thus
dynamic paths generated by anchors break the authentication step.

JSON and TEXT file types do not support anchors and thus have no such limitation.

YAML Streams
~~~~~~~~~~~~

``YAML`` supports having more than one "document" in a single file, while
formats like ``JSON`` do not. ``sops`` is able to handle both. This means the
following multi-document will be encrypted as expected:

.. code:: yaml

	---
	data: foo
	---
	data: bar

Note that the ``sops`` metadata, i.e. the hash, etc, is computed for the physical
file rather than each internal "document".

Top-level arrays
~~~~~~~~~~~~~~~~
``YAML`` and ``JSON`` top-level arrays are not supported, because ``sops``
needs a top-level ``sops`` key to store its metadata.

This file will not work in sops:

.. code:: yaml

	---
	  - some
	  - array
	  - elements

But this one will work because the ``sops`` key can be added at the same level as the
``data`` key.

.. code:: yaml

	data:
	  - some
	  - array
	  - elements

Similarly, with ``JSON`` arrays, this document will not work:

.. code:: json

	[
	  "some",
	  "array",
	  "elements"
	]


But this one will work just fine:

.. code:: json

	{
	  "data": [
	    "some",
	    "array",
	    "elements"
	  ]
	}


Examples
--------

Take a look into the `examples <https://github.com/mozilla/sops/tree/master/examples>`_ folder for detailed use cases of sops in a CI environment. The section below describes specific tips for common use cases.

Creating a new file
~~~~~~~~~~~~~~~~~~~

The command below creates a new file with a data key encrypted by KMS and PGP.

.. code:: bash

	$ sops --kms "arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500" --pgp C9CAB0AF1165060DB58D6D6B2653B624D620786D /path/to/new/file.yaml

Encrypting an existing file
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Similar to the previous command, we tell sops to use one KMS and one PGP key.
The path points to an existing cleartext file, so we give sops flag ``-e`` to
encrypt the file, and redirect the output to a destination file.

.. code:: bash

	$ export SOPS_KMS_ARN="arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500"
	$ export SOPS_PGP_FP="C9CAB0AF1165060DB58D6D6B2653B624D620786D"
	$ sops -e /path/to/existing/file.yaml > /path/to/new/encrypted/file.yaml

Decrypt the file with ``-d``.

.. code:: bash

	$ sops -d /path/to/new/encrypted/file.yaml

Encrypt or decrypt a file in place
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Rather than redirecting the output of ``-e`` or ``-d``, sops can replace the
original file after encrypting or decrypting it.

.. code:: bash

	# file.yaml is in cleartext
	$ sops -e -i /path/to/existing/file.yaml
	# file.yaml is now encrypted
	$ sops -d -i /path/to/existing/file.yaml
	# file.yaml is back in cleartext

Encrypting binary files
~~~~~~~~~~~~~~~~~~~~~~~

``sops`` primary use case is encrypting YAML and JSON configuration files, but it
also has the ability to manage binary files. When encrypting a binary, sops will
read the data as bytes, encrypt it, store the encrypted base64 under
``tree['data']`` and write the result as JSON.

Note that the base64 encoding of encrypted data can actually make the encrypted
file larger than the cleartext one.

In-place encryption/decryption also works on binary files.

.. code::

	$ dd if=/dev/urandom of=/tmp/somerandom bs=1024
	count=512
	512+0 records in
	512+0 records out
	524288 bytes (524 kB) copied, 0.0466158 s, 11.2 MB/s

	$ sha512sum /tmp/somerandom
	9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandom

	$ sops -e -i /tmp/somerandom
	please wait while a data encryption key is being generated and stored securely

	$ sops -d -i /tmp/somerandom

	$ sha512sum /tmp/somerandom
	9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandom

Extract a sub-part of a document tree
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``sops`` can extract a specific part of a YAML or JSON document, by provided the
path in the ``--extract`` command line flag. This is useful to extract specific
values, like keys, without needing an extra parser.

.. code:: bash

	$ sops -d --extract '["app2"]["key"]' ~/git/svc/sops/example.yaml
	-----BEGIN RSA PRIVATE KEY-----
	MIIBPAIBAAJBAPTMNIyHuZtpLYc7VsHQtwOkWYobkUblmHWRmbXzlAX6K8tMf3Wf
	ImcbNkqAKnELzFAPSBeEMhrBN0PyOC9lYlMCAwEAAQJBALXD4sjuBn1E7Y9aGiMz
	bJEBuZJ4wbhYxomVoQKfaCu+kH80uLFZKoSz85/ySauWE8LgZcMLIBoiXNhDKfQL
	vHECIQD6tCG9NMFWor69kgbX8vK5Y+QL+kRq+9HK6yZ9a+hsLQIhAPn4Ie6HGTjw
	fHSTXWZpGSan7NwTkIu4U5q2SlLjcZh/AiEA78NYRRBwGwAYNUqzutGBqyXKUl4u
	Erb0xAEyVV7e8J0CIQC8VBY8f8yg+Y7Kxbw4zDYGyb3KkXL10YorpeuZR4LuQQIg
	bKGPkMM4w5blyE1tqGN0T7sJwEx+EUOgacRNqM2ljVA=
	-----END RSA PRIVATE KEY-----

The tree path syntax uses regular python dictionary syntax, without the
variable name. Extract keys by naming them, and array elements by numbering
them.

.. code:: bash

	$ sops -d --extract '["an_array"][1]' ~/git/svc/sops/example.yaml
	secretuser2

Set a sub-part in a document tree
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``sops`` can set a specific part of a YAML or JSON document, by providing
the path and value in the ``--set`` command line flag. This is useful to
set specific values, like keys, without needing an editor.

.. code:: bash

	$ sops --set '["app2"]["key"] "app2keystringvalue"'  ~/git/svc/sops/example.yaml

The tree path syntax uses regular python dictionary syntax, without the
variable name. Set to keys by naming them, and array elements by
numbering them.

.. code:: bash

	$ sops --set '["an_array"][1] "secretuser2"' ~/git/svc/sops/example.yaml

The value must be formatted as json.

.. code:: bash

	$ sops --set '["an_array"][1] {"uid1":null,"uid2":1000,"uid3":["bob"]}' ~/git/svc/sops/example.yaml

Showing diffs in cleartext in git
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You most likely want to store encrypted files in a version controlled repository.
Sops can be used with git to decrypt files when showing diffs between versions.
This is very handy for reviewing changes or visualizing history.

To configure sops to decrypt files during diff, create a ``.gitattributes`` file
at the root of your repository that contains a filter and a command.

.. code::

	*.yaml diff=sopsdiffer

Here we only care about YAML files. ``sopsdiffer`` is an arbitrary name that we map
to a sops command in the git configuration file of the repository.

.. code:: bash

	$ git config diff.sopsdiffer.textconv "sops -d"

	$ grep -A 1 sopsdiffer .git/config
	[diff "sopsdiffer"]
		textconv = "sops -d"

With this in place, calls to ``git diff`` will decrypt both previous and current
versions of the target file prior to displaying the diff. And it even works with
git client interfaces, because they call git diff under the hood!

Encrypting only parts of a file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Note: this only works on YAML and JSON files, not on BINARY files.

By default, ``sops`` encrypts all the values of a YAML or JSON file and leaves the
keys in cleartext. In some instances, you may want to exclude some values from
being encrypted. This can be accomplished by adding the suffix **_unencrypted**
to any key of a file. When set, all values underneath the key that set the
**_unencrypted** suffix will be left in cleartext.

Note that, while in cleartext, unencrypted content is still added to the
checksum of the file, and thus cannot be modified outside of sops without
breaking the file integrity check.

The unencrypted suffix can be set to a different value using the
``--unencrypted-suffix`` option.

Conversely, you can opt in to only encrypt some values in a YAML or JSON file,
by adding a chosen suffix to those keys and passing it to the ``--encrypted-suffix`` option.

A third method is to use the ``--encrypted-regex`` which will only encrypt values under
keys that match the supplied regular expression.  For example, this command:

.. code:: bash

	$ sops --encrypt --encrypted-regex '^(data|stringData)$' k8s-secrets.yaml

will encrypt the values under the ``data`` and ``stringData`` keys in a YAML file
containing kubernetes secrets.  It will not encrypt other values that help you to
navigate the file, like ``metadata`` which contains the secrets' names.

Conversely, you can opt in to only left certain keys without encrypting by using the 
``--unencrypted-regex`` option, which will leave the values unencrypted of those keys 
that match the supplied regular expression. For example, this command:

.. code:: bash

  $ sops --encrypt --unencrypted-regex '^(description|metadata)$' k8s-secrets.yaml

will not encrypt the values under the ``description`` and ``metadata`` keys in a YAML file
containing kubernetes secrets, while encrypting everything else.

You can also specify these options in the ``.sops.yaml`` config file.

Note: these four options ``--unencrypted-suffix``, ``--encrypted-suffix``, ``--encrypted-regex`` and ``--unencrypted-regex`` are
mutually exclusive and cannot all be used in the same file.

Encryption Protocol
-------------------

When sops creates a file, it generates a random 256 bit data key and asks each
KMS and PGP master key to encrypt the data key. The encrypted version of the data
key is stored in the ``sops`` metadata under ``sops.kms`` and ``sops.pgp``.

For KMS:

.. code:: yaml

    sops:
        kms:
        -   enc: CiC6yCOtzsnFhkfdIslYZ0bAf//gYLYCmIu87B3sy/5yYxKnAQEBAQB4usgjrc7JxYZH3SLJWGdGwH//4GC2ApiLvOwd7Mv+cmMAAAB+MHwGCSqGSIb3DQEHBqBvMG0CAQAwaAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyGdRODuYMHbA8Ozj8CARCAO7opMolPJUmBXd39Zlp0L2H9fzMKidHm1vvaF6nNFq0ClRY7FlIZmTm4JfnOebPseffiXFn9tG8cq7oi
            enc_ts: 1439568549.245995
            arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e

For PGP:

.. code:: yaml

    sops:
        pgp:
        -   fp: 85D77543B3D624B63CEA9E6DBC17301B491B3F21
            created_at: 1441570391.930042
            enc: |
                -----BEGIN PGP MESSAGE-----
                Version: GnuPG v1

                hQIMA0t4uZHfl9qgAQ//UvGAwGePyHuf2/zayWcloGaDs0MzI+zw6CmXvMRNPUsA
                pAgRKczJmDu4+XzN+cxX5Iq9xEWIbny9B5rOjwTXT3qcUYZ4Gkzbq4MWkjuPp/Iv
                qO4MJaYzoH5YxC4YORQ2LvzhA2YGsCzYnljmatGEUNg01yJ6r5mwFwDxl4Nc80Cn
                RwnHuGExK8j1jYJZu/juK1qRbuBOAuruIPPWVdFB845PA7waacG1IdUW3ZtBkOy3
                O0BIfG2ekRg0Nik6sTOhDUA+l2bewCcECI8FYCEjwHm9Sg5cxmP2V5m1mby+uKAm
                kewaoOyjbmV1Mh3iI1b/AQMr+/6ZE9MT2KnsoWosYamFyjxV5r1ZZM7cWKnOT+tu
                KOvGhTV1TeOfVpajNTNwtV/Oyh3mMLQ0F0HgCTqomQVqw5+sj7OWAASuD3CU/dyo
                pcmY5Qe0TNL1JsMNEH8LJDqSh+E0hsUxdY1ouVsg3ysf6mdM8ciWb3WRGxih1Vmf
                unfLy8Ly3V7ZIC8EHV8aLJqh32jIZV4i2zXIoO4ZBKrudKcECY1C2+zb/TziVAL8
                qyPe47q8gi1rIyEv5uirLZjgpP+JkDUgoMnzlX334FZ9pWtQMYW4Y67urAI4xUq6
                /q1zBAeHoeeeQK+YKDB7Ak/Y22YsiqQbNp2n4CKSKAE4erZLWVtDvSp+49SWmS/S
                XgGi+13MaXIp0ecPKyNTBjF+NOw/I3muyKr8EbDHrd2XgIT06QXqjYLsCb1TZ0zm
                xgXsOTY3b+ONQ2zjhcovanDp7/k77B+gFitLYKg4BLZsl7gJB12T8MQnpfSmRT4=
                =oJgS
                -----END PGP MESSAGE-----

``sops`` then opens a text editor on the newly created file. The user adds data to the
file and saves it when done.

Upon save, sops browses the entire file as a key/value tree. Every time sops
encounters a leaf value (a value that does not have children), it encrypts the
value with AES256_GCM using the data key and a 256 bit random initialization
vector.

Each file uses a single data key to encrypt all values of a document, but each
value receives a unique initialization vector and has unique authentication data.

Additional data is used to guarantee the integrity of the encrypted data
and of the tree structure: when encrypting the tree, key names are concatenated
into a byte string that is used as AEAD additional data (aad) when encrypting
values. We expect that keys do not carry sensitive information, and
keeping them in cleartext allows for better diff and overall readability.

Any valid KMS or PGP master key can later decrypt the data key and access the
data.

Multiple master keys allow for sharing encrypted files without sharing master
keys, and provide a disaster recovery solution. The recommended way to use sops
is to have two KMS master keys in different regions and one PGP public key with
the private key stored offline. If, by any chance, both KMS master keys are
lost, you can always recover the encrypted data using the PGP private key.

Message Authentication Code
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In addition to authenticating branches of the tree using keys as additional
data, sops computes a MAC on all the values to ensure that no value has been
added or removed fraudulently. The MAC is stored encrypted with AES_GCM and
the data key under tree->`sops`->`mac`.

Motivation
----------

Automating the distribution of secrets and credentials to components of an
infrastructure is a hard problem. We know how to encrypt secrets and share them
between humans, but extending that trust to systems is difficult. Particularly
when these systems follow devops principles and are created and destroyed
without human intervention. The issue boils down to establishing the initial
trust of a system that just joined the infrastructure, and providing it access
to the secrets it needs to configure itself.

The initial trust
~~~~~~~~~~~~~~~~~

In many infrastructures, even highly dynamic ones, the initial trust is
established by a human. An example is seen in Puppet by the way certificates are
issued: when a new system attempts to join a Puppetmaster, an administrator
must, by default, manually approve the issuance of the certificate the system
needs. This is cumbersome, and many puppetmasters are configured to auto-sign
new certificates to work around that issue. This is obviously not recommended
and far from ideal.

AWS provides a more flexible approach to trusting new systems. It uses a
powerful mechanism of roles and identities. In AWS, it is possible to verify
that a new system has been granted a specific role at creation, and it is
possible to map that role to specific resources. Instead of trusting new systems
directly, the administrator trusts the AWS permission model and its automation
infrastructure. As long as AWS keys are safe, and the AWS API is secure, we can
assume that trust is maintained and systems are who they say they are.

KMS, Trust and secrets distribution
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Using the AWS trust model, we can create fine grained access controls to
Amazon's Key Management Service (KMS). KMS is a service that encrypts and
decrypts data with AES_GCM, using keys that are never visible to users of the
service. Each KMS master key has a set of role-based access controls, and
individual roles are permitted to encrypt or decrypt using the master key. KMS
helps solve the problem of distributing keys, by shifting it into an access
control problem that can be solved using AWS's trust model.

Operational requirements
~~~~~~~~~~~~~~~~~~~~~~~~

When Mozilla's Services Operations team started revisiting the issue of
distributing secrets to EC2 instances, we set a goal to store these secrets
encrypted until the very last moment, when they need to be decrypted on target
systems. Not unlike many other organizations that operate sufficiently complex
automation, we found this to be a hard problem with a number of prerequisites:

1. Secrets must be stored in YAML files for easy integration into hiera

2. Secrets must be stored in GIT, and when a new CloudFormation stack is
   built, the current HEAD is pinned to the stack. (This allows secrets to
   be changed in GIT without impacting the current stack that may
   autoscale).

3. Entries must be encrypted separately. Encrypting entire files as blobs makes
   git conflict resolution almost impossible. Encrypting each entry
   separately is much easier to manage.

4. Secrets must always be encrypted on disk (admin laptop, upstream
   git repo, jenkins and S3) and only be decrypted on the target
   systems

SOPS can be used to encrypt YAML, JSON and BINARY files. In BINARY mode, the
content of the file is treated as a blob, the same way PGP would encrypt an
entire file. In YAML and JSON modes, however, the content of the file is
manipulated as a tree where keys are stored in cleartext, and values are
encrypted. hiera-eyaml does something similar, and over the years we learned
to appreciate its benefits, namely:

* diffs are meaningful. If a single value of a file is modified, only that
  value will show up in the diff. The diff is still limited to only showing
  encrypted data, but that information is already more granular that
  indicating that an entire file has changed.

* conflicts are easier to resolve. If multiple users are working on the
  same encrypted files, as long as they don't modify the same values,
  changes are easy to merge. This is an improvement over the PGP
  encryption approach where unsolvable conflicts often happen when
  multiple users work on the same file.

OpenPGP integration
~~~~~~~~~~~~~~~~~~~

OpenPGP gets a lot of bad press for being an outdated crypto protocol, and while
true, what really made us look for alternatives is the difficulty of managing and
distributing keys to systems. With KMS, we manage permissions to an API, not keys,
and that's a lot easier to do.

But PGP is not dead yet, and we still rely on it heavily as a backup solution:
all our files are encrypted with KMS and with one PGP public key, with its
private key stored securely for emergency decryption in the event that we lose
all our KMS master keys.

SOPS can be used without KMS entirely, the same way you would use an encrypted
PGP file: by referencing the pubkeys of each individual who has access to the file.
It can easily be done by providing sops with a comma-separated list of public keys
when creating a new file:

.. code:: bash

	$ sops --pgp "E60892BB9BD89A69F759A1A0A3D652173B763E8F,84050F1D61AF7C230A12217687DF65059EF093D3,85D77543B3D624B63CEA9E6DBC17301B491B3F21" mynewfile.yaml

Threat Model
------------

The security of the data stored using sops is as strong as the weakest
cryptographic mechanism. Values are encrypted using AES256_GCM which is the
strongest symmetric encryption algorithm known today. Data keys are encrypted
in either KMS, which also uses AES256_GCM, or PGP which uses either RSA or
ECDSA keys.

Going from the most likely to the least likely, the threats are as follows:

Compromised AWS credentials grant access to KMS master key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

An attacker with access to an AWS console can grant itself access to one of
the KMS master keys used to encrypt a sops data key. This threat should be
mitigated by protecting AWS accesses with strong controls, such as multi-factor
authentication, and also by performing regular audits of permissions granted
to AWS users.

Compromised PGP key
~~~~~~~~~~~~~~~~~~~

PGP keys are routinely mishandled, either because owners copy them from
machine to machine, or because the key is left forgotten on an unused machine
an attacker gains access to. When using PGP encryption, sops users should take
special care of PGP private keys, and store them on smart cards or offline
as often as possible.

Factorized RSA key
~~~~~~~~~~~~~~~~~~

sops doesn't apply any restriction on the size or type of PGP keys. A weak PGP
keys, for example 512 bits RSA, could be factorized by an attacker to gain
access to the private key and decrypt the data key. Users of sops should rely
on strong keys, such as 2048+ bits RSA keys, or 256+ bits ECDSA keys.

Weak AES cryptography
~~~~~~~~~~~~~~~~~~~~~

A vulnerability in AES256_GCM could potentially leak the data key or the KMS
master key used by a sops encrypted file. While no such vulnerability exists
today, we recommend that users keep their encrypted files reasonably private.

Backward compatibility
----------------------

``sops`` will remain backward compatible on the major version, meaning that all
improvements brought to the 1.X and 2.X branches (current) will maintain the
file format introduced in **1.0**.

Security
--------

Please report security issues to security at mozilla dot org, or by using one
of the contact method available here: `https://www.mozilla.org/en-US/security/#For_Developers <https://www.mozilla.org/en-US/security/#For_Developers>`_

License
-------
Mozilla Public License Version 2.0

Authors
-------

The core team is composed of:

* AJ Banhken @ajvb

The original authors were:

* Adrian Utrilla @autrilla
* Julien Vehent @jvehent

And a whole bunch of `contributors <https://github.com/mozilla/sops/graphs/contributors>`_

Credits
-------

`sops` was inspired by `hiera-eyaml <https://github.com/TomPoulton/hiera-eyaml>`_,
`credstash <https://github.com/LuminalOSS/credstash>`_ ,
`sneaker <https://github.com/codahale/sneaker>`_,
`password store <http://www.passwordstore.org/>`_ and too many years managing
PGP encrypted files by hand...
07070100000011000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/aes07070100000012000081A400000000000000000000000162794F9300001562000000000000000000000000000000000000001900000000sops-3.7.3/aes/cipher.go/*
Package aes defines a Cipher that uses 256-bit AES-GCM authenticated encryption to encrypt values the SOPS tree.
*/
package aes //import "go.mozilla.org/sops/v3/aes"

import (
	cryptoaes "crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/logging"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("AES")
}

type encryptedValue struct {
	data     []byte
	iv       []byte
	tag      []byte
	datatype string
}

const nonceSize int = 32

type stashKey struct {
	additionalData string
	plaintext      interface{}
}

// Cipher encrypts and decrypts data keys with AES GCM 256
type Cipher struct {
	// stash is a map that stores IVs for reuse, so that the ciphertext doesn't change when decrypting and reencrypting
	// the same values.
	stash map[stashKey][]byte
}

// NewCipher is the constructor for a new Cipher object
func NewCipher() Cipher {
	return Cipher{
		stash: make(map[stashKey][]byte),
	}
}

var encre = regexp.MustCompile(`^ENC\[AES256_GCM,data:(.+),iv:(.+),tag:(.+),type:(.+)\]`)

func parse(value string) (*encryptedValue, error) {
	matches := encre.FindStringSubmatch(value)
	if matches == nil {
		return nil, fmt.Errorf("Input string %s does not match sops' data format", value)
	}
	data, err := base64.StdEncoding.DecodeString(matches[1])
	if err != nil {
		return nil, fmt.Errorf("Error base64-decoding data: %s", err)
	}
	iv, err := base64.StdEncoding.DecodeString(matches[2])
	if err != nil {
		return nil, fmt.Errorf("Error base64-decoding iv: %s", err)
	}
	tag, err := base64.StdEncoding.DecodeString(matches[3])
	if err != nil {
		return nil, fmt.Errorf("Error base64-decoding tag: %s", err)
	}
	datatype := string(matches[4])

	return &encryptedValue{data, iv, tag, datatype}, nil
}

// Decrypt takes a sops-format value string and a key and returns the decrypted value and a stash value
func (c Cipher) Decrypt(ciphertext string, key []byte, additionalData string) (plaintext interface{}, err error) {
	if isEmpty(ciphertext) {
		return "", nil
	}
	encryptedValue, err := parse(ciphertext)
	if err != nil {
		return nil, err
	}
	aescipher, err := cryptoaes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCMWithNonceSize(aescipher, len(encryptedValue.iv))
	if err != nil {
		return nil, err
	}
	data := append(encryptedValue.data, encryptedValue.tag...)
	decryptedBytes, err := gcm.Open(nil, encryptedValue.iv, data, []byte(additionalData))
	if err != nil {
		return nil, fmt.Errorf("Could not decrypt with AES_GCM: %s", err)
	}
	decryptedValue := string(decryptedBytes)
	switch encryptedValue.datatype {
	case "str":
		plaintext = decryptedValue
	case "int":
		plaintext, err = strconv.Atoi(decryptedValue)
	case "float":
		plaintext, err = strconv.ParseFloat(decryptedValue, 64)
	case "bytes":
		plaintext = decryptedBytes
	case "bool":
		plaintext, err = strconv.ParseBool(decryptedValue)
	case "comment":
		plaintext = sops.Comment{Value: decryptedValue}
	default:
		return nil, fmt.Errorf("Unknown datatype: %s", encryptedValue.datatype)
	}
	c.stash[stashKey{plaintext: plaintext, additionalData: additionalData}] = encryptedValue.iv
	return plaintext, err
}

func isEmpty(value interface{}) bool {
	switch value := value.(type) {
	case string:
		return value == ""
	case []byte:
		return len(value) == 0
	case sops.Comment:
		return isEmpty(value.Value)
	default:
		return false
	}
}

// Encrypt takes one of (string, int, float, bool) and encrypts it with the provided key and additional auth data, returning a sops-format encrypted string.
func (c Cipher) Encrypt(plaintext interface{}, key []byte, additionalData string) (ciphertext string, err error) {
	if isEmpty(plaintext) {
		return "", nil
	}
	aescipher, err := cryptoaes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("Could not initialize AES GCM encryption cipher: %s", err)
	}
	var iv []byte
	if stash, ok := c.stash[stashKey{plaintext: plaintext, additionalData: additionalData}]; !ok {
		iv = make([]byte, nonceSize)
		_, err = rand.Read(iv)
		if err != nil {
			return "", fmt.Errorf("Could not generate random bytes for IV: %s", err)
		}
	} else {
		iv = stash
	}
	gcm, err := cipher.NewGCMWithNonceSize(aescipher, nonceSize)
	if err != nil {
		return "", fmt.Errorf("Could not create GCM: %s", err)
	}
	var plainBytes []byte
	var encryptedType string
	switch value := plaintext.(type) {
	case string:
		encryptedType = "str"
		plainBytes = []byte(value)
	case int:
		encryptedType = "int"
		plainBytes = []byte(strconv.Itoa(value))
	case float64:
		encryptedType = "float"
		// The Python version encodes floats without padding 0s after the decimal point.
		plainBytes = []byte(strconv.FormatFloat(value, 'f', -1, 64))
	case bool:
		encryptedType = "bool"
		// The Python version encodes booleans with Titlecase
		plainBytes = []byte(strings.Title(strconv.FormatBool(value)))
	case sops.Comment:
		encryptedType = "comment"
		plainBytes = []byte(value.Value)
	default:
		return "", fmt.Errorf("Value to encrypt has unsupported type %T", value)
	}
	out := gcm.Seal(nil, iv, plainBytes, []byte(additionalData))
	return fmt.Sprintf("ENC[AES256_GCM,data:%s,iv:%s,tag:%s,type:%s]",
		base64.StdEncoding.EncodeToString(out[:len(out)-cryptoaes.BlockSize]),
		base64.StdEncoding.EncodeToString(iv),
		base64.StdEncoding.EncodeToString(out[len(out)-cryptoaes.BlockSize:]),
		encryptedType), nil
}
07070100000013000081A400000000000000000000000162794F9300000AED000000000000000000000000000000000000001E00000000sops-3.7.3/aes/cipher_test.gopackage aes

import (
	"crypto/rand"
	"strings"
	"testing"
	"testing/quick"

	"github.com/stretchr/testify/assert"
	"go.mozilla.org/sops/v3"
)

func TestDecrypt(t *testing.T) {
	expected := "foo"
	key := []byte(strings.Repeat("f", 32))
	message := `ENC[AES256_GCM,data:oYyi,iv:MyIDYbT718JRr11QtBkcj3Dwm4k1aCGZBVeZf0EyV8o=,tag:t5z2Z023Up0kxwCgw1gNxg==,type:str]`
	decryption, err := NewCipher().Decrypt(message, key, "bar:")
	if err != nil {
		t.Errorf("%s", err)
	}
	if decryption != expected {
		t.Errorf("Decrypt(\"%s\", \"%s\") == \"%s\", expected %s", message, key, decryption, expected)
	}
}

func TestDecryptInvalidAad(t *testing.T) {
	message := `ENC[AES256_GCM,data:oYyi,iv:MyIDYbT718JRr11QtBkcj3Dwm4k1aCGZBVeZf0EyV8o=,tag:t5z2Z023Up0kxwCgw1gNxg==,type:str]`
	_, err := NewCipher().Decrypt(message, []byte(strings.Repeat("f", 32)), "")
	if err == nil {
		t.Errorf("Decrypting with an invalid AAC should fail")
	}
}

func TestRoundtripString(t *testing.T) {
	f := func(x, aad string) bool {
		key := make([]byte, 32)
		rand.Read(key)
		s, err := NewCipher().Encrypt(x, key, aad)
		if err != nil {
			log.Println(err)
			return false
		}
		d, err := NewCipher().Decrypt(s, key, aad)
		if err != nil {
			return false
		}
		return x == d
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestRoundtripFloat(t *testing.T) {
	key := []byte(strings.Repeat("f", 32))
	f := func(x float64) bool {
		s, err := NewCipher().Encrypt(x, key, "")
		if err != nil {
			log.Println(err)
			return false
		}
		d, err := NewCipher().Decrypt(s, key, "")
		if err != nil {
			return false
		}
		return x == d
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestRoundtripInt(t *testing.T) {
	key := []byte(strings.Repeat("f", 32))
	f := func(x int) bool {
		s, err := NewCipher().Encrypt(x, key, "")
		if err != nil {
			log.Println(err)
			return false
		}
		d, err := NewCipher().Decrypt(s, key, "")
		if err != nil {
			return false
		}
		return x == d
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestRoundtripBool(t *testing.T) {
	key := []byte(strings.Repeat("f", 32))
	f := func(x bool) bool {
		s, err := NewCipher().Encrypt(x, key, "")
		if err != nil {
			log.Println(err)
			return false
		}
		d, err := NewCipher().Decrypt(s, key, "")
		if err != nil {
			return false
		}
		return x == d
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestEncryptEmptyComment(t *testing.T) {
	key := []byte(strings.Repeat("f", 32))
	s, err := NewCipher().Encrypt(sops.Comment{}, key, "")
	assert.Nil(t, err)
	assert.Equal(t, "", s)
}

func TestDecryptEmptyValue(t *testing.T) {
	key := []byte(strings.Repeat("f", 32))
	s, err := NewCipher().Decrypt("", key, "")
	assert.Nil(t, err)
	assert.Equal(t, "", s)
}
07070100000014000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/age07070100000015000081A400000000000000000000000162794F9300000179000000000000000000000000000000000000001800000000sops-3.7.3/age/keys.txt# created: 2020-07-18T03:16:47-07:00
# public key: age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw
AGE-SECRET-KEY-1NJT5YCS2LWU4V4QAJQ6R4JNU7LXPDX602DZ9NUFANVU5GDTGUWCQ5T59M6
# created: 2021-12-12T01:39:30+01:00
# public key: age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep
AGE-SECRET-KEY-1T0Z66WSXS6RMNCPSL7P2E8N4Q7SUD8VMG9ND27S08JL7Y2XAU9EQECHDS707070100000016000081A400000000000000000000000162794F93000017E3000000000000000000000000000000000000001C00000000sops-3.7.3/age/keysource.gopackage age

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"filippo.io/age"
	"filippo.io/age/armor"
	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3/logging"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("AGE")
}

const SopsAgeKeyEnv = "SOPS_AGE_KEY"
const SopsAgeKeyFileEnv = "SOPS_AGE_KEY_FILE"

// MasterKey is an age key used to encrypt and decrypt sops' data key.
type MasterKey struct {
	Identity     string // a Bech32-encoded private key
	Recipient    string // a Bech32-encoded public key
	EncryptedKey string // a sops data key encrypted with age

	parsedRecipient *age.X25519Recipient // a parsed age public key
}

// Encrypt takes a sops data key, encrypts it with age and stores the result in the EncryptedKey field.
func (key *MasterKey) Encrypt(datakey []byte) error {
	buffer := &bytes.Buffer{}

	if key.parsedRecipient == nil {
		parsedRecipient, err := parseRecipient(key.Recipient)

		if err != nil {
			log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
			return err
		}

		key.parsedRecipient = parsedRecipient
	}

	aw := armor.NewWriter(buffer)
	w, err := age.Encrypt(aw, key.parsedRecipient)
	if err != nil {
		return fmt.Errorf("failed to open file for encrypting sops data key with age: %w", err)
	}

	if _, err := w.Write(datakey); err != nil {
		log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
		return fmt.Errorf("failed to encrypt sops data key with age: %w", err)
	}

	if err := w.Close(); err != nil {
		log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
		return fmt.Errorf("failed to close file for encrypting sops data key with age: %w", err)
	}

	if err := aw.Close(); err != nil {
		log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
		return fmt.Errorf("failed to close armored writer: %w", err)
	}

	key.EncryptedKey = buffer.String()

	log.WithField("recipient", key.parsedRecipient).Info("Encryption succeeded")

	return nil
}

// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet.
func (key *MasterKey) EncryptIfNeeded(datakey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(datakey)
	}

	return nil
}

// EncryptedDataKey returns the encrypted data key this master key holds.
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)
}

// SetEncryptedDataKey sets the encrypted data key for this master key.
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
	key.EncryptedKey = string(enc)
}

// Decrypt decrypts the EncryptedKey field with the age identity and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
	var ageKeyReader io.Reader
	var ageKeyReaderName string

	if ageKeyReader == nil {
		ageKey, ok := os.LookupEnv(SopsAgeKeyEnv)
		if ok {
			ageKeyReader = strings.NewReader(ageKey)
			ageKeyReaderName = "environment variable"
		}
	}

	if ageKeyReader == nil {
		ageKeyFilePath, ok := os.LookupEnv(SopsAgeKeyFileEnv)
		if ok {
			ageKeyFile, err := os.Open(ageKeyFilePath)
			if err != nil {
				return nil, fmt.Errorf("failed to open file: %w", err)
			}
			defer ageKeyFile.Close()
			ageKeyReader = ageKeyFile
			ageKeyReaderName = ageKeyFilePath
		}
	}

	if ageKeyReader == nil {
		userConfigDir, err := os.UserConfigDir()
		if err != nil {
			return nil, fmt.Errorf("user config directory could not be determined: %w", err)
		}
		ageKeyFilePath := filepath.Join(userConfigDir, "sops", "age", "keys.txt")
		ageKeyFile, err := os.Open(ageKeyFilePath)
		if err != nil {
			return nil, fmt.Errorf("failed to open file: %w", err)
		}
		defer ageKeyFile.Close()
		ageKeyReader = ageKeyFile
		ageKeyReaderName = ageKeyFilePath
	}

	identities, err := age.ParseIdentities(ageKeyReader)

	if err != nil {
		return nil, err
	}

	src := bytes.NewReader([]byte(key.EncryptedKey))
	ar := armor.NewReader(src)
	r, err := age.Decrypt(ar, identities...)

	if err != nil {
		return nil, fmt.Errorf("no age identity found in %q that could decrypt the data", ageKeyReaderName)
	}

	var b bytes.Buffer
	if _, err := io.Copy(&b, r); err != nil {
		return nil, fmt.Errorf("failed to copy decrypted data into bytes.Buffer: %w", err)
	}

	return b.Bytes(), nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	return false
}

// ToString converts the key to a string representation.
func (key *MasterKey) ToString() string {
	return key.Recipient
}

// ToMap converts the MasterKey to a map for serialization purposes.
func (key *MasterKey) ToMap() map[string]interface{} {
	return map[string]interface{}{"recipient": key.Recipient, "enc": key.EncryptedKey}
}

// MasterKeysFromRecipients takes a comma-separated list of Bech32-encoded public keys and returns a
// slice of new MasterKeys.
func MasterKeysFromRecipients(commaSeparatedRecipients string) ([]*MasterKey, error) {
	if commaSeparatedRecipients == "" {
		// otherwise Split returns [""] and MasterKeyFromRecipient is unhappy
		return make([]*MasterKey, 0), nil
	}
	recipients := strings.Split(commaSeparatedRecipients, ",")

	var keys []*MasterKey

	for _, recipient := range recipients {
		key, err := masterKeyFromRecipient(recipient)

		if err != nil {
			return nil, err
		}

		keys = append(keys, key)
	}

	return keys, nil
}

// masterKeyFromRecipient takes a Bech32-encoded public key and returns a new MasterKey.
func masterKeyFromRecipient(recipient string) (*MasterKey, error) {
	recipient = strings.TrimSpace(recipient)
	parsedRecipient, err := parseRecipient(recipient)

	if err != nil {
		return nil, err
	}

	return &MasterKey{
		Recipient:       recipient,
		parsedRecipient: parsedRecipient,
	}, nil
}

// parseRecipient attempts to parse a string containing an encoded age public key
func parseRecipient(recipient string) (*age.X25519Recipient, error) {
	parsedRecipient, err := age.ParseX25519Recipient(recipient)

	if err != nil {
		return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err)
	}

	return parsedRecipient, nil
}
07070100000017000081A400000000000000000000000162794F93000014B7000000000000000000000000000000000000002100000000sops-3.7.3/age/keysource_test.gopackage age

import (
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestMasterKeysFromRecipientsEmpty(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := ""
	recipients, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(recipients, make([]*MasterKey, 0))
}

func TestMasterKeyFromRecipientWithLeadingAndTrailingSpacesSingle(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "  age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw  "
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 1)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
}

func TestMasterKeyFromRecipientWithLeadingAndTrailingSpacesMultiple(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "  age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw  ,  age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep  "
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 2)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
	assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
}

func TestMasterKeysFromRecipientsWithSingle(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw"
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 1)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
}

func TestMasterKeysFromRecipientsWithMultiple(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 2)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
	assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
}

func TestAge(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 2)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
	assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")

	dataKey := []byte("abcdefghijklmnopqrstuvwxyz123456")

	for _, key := range keys {
		err = key.Encrypt(dataKey)
		assert.NoError(err)

		_, filename, _, _ := runtime.Caller(0)
		err = os.Setenv("SOPS_AGE_KEY_FILE", path.Join(path.Dir(filename), "keys.txt"))
		assert.NoError(err)

		decryptedKey, err := key.Decrypt()
		assert.NoError(err)
		assert.Equal(dataKey, decryptedKey)
	}

}

func TestAgeDotEnv(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 2)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
	assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")

	dotenv := `IMAGE_PREFIX=repo/service-
APPLICATION_KEY=K6pfAWuUVND9Fz5SC7jmA6pfAWuUVND9Fz5SC7jmA
KEY_ID=003683d721f2ae683d721f2a1
DOMAIN=files.127.0.0.1.nip.io`
	dataKey := []byte(dotenv)

	err = keys[0].Encrypt(dataKey)
	assert.NoError(err)

	_, filename, _, _ := runtime.Caller(0)
	err = os.Setenv(SopsAgeKeyFileEnv, path.Join(path.Dir(filename), "keys.txt"))
	defer os.Unsetenv(SopsAgeKeyFileEnv)
	assert.NoError(err)

	decryptedKey, err := keys[0].Decrypt()
	assert.NoError(err)
	assert.Equal(dataKey, decryptedKey)
}

func TestAgeEnv(t *testing.T) {
	assert := assert.New(t)

	commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
	keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)

	assert.NoError(err)

	assert.Equal(len(keys), 2)
	assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
	assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")

	dataKey := []byte("abcdefghijklmnopqrstuvwxyz123456")

	err = keys[0].Encrypt(dataKey)
	assert.NoError(err)

	_, filename, _, _ := runtime.Caller(0)
	keysBytes, err := ioutil.ReadFile(path.Join(path.Dir(filename), "keys.txt"))
	assert.NoError(err)
	err = os.Setenv(SopsAgeKeyEnv, string(keysBytes))
	defer os.Unsetenv(SopsAgeKeyEnv)
	assert.NoError(err)

	decryptedKey, err := keys[0].Decrypt()
	assert.NoError(err)
	assert.Equal(dataKey, decryptedKey)
}
07070100000018000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001100000000sops-3.7.3/audit07070100000019000081A400000000000000000000000162794F9300001352000000000000000000000000000000000000001A00000000sops-3.7.3/audit/audit.gopackage audit

import (
	"database/sql"
	"flag"
	"fmt"
	"io/ioutil"
	"os/user"

	"github.com/pkg/errors"

	// empty import as per https://godoc.org/github.com/lib/pq
	_ "github.com/lib/pq"

	"gopkg.in/yaml.v3"
	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3/logging"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("AUDIT")
	confBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.WithField("error", err).Debugf("Error reading config")
		return
	}
	var conf config
	err = yaml.Unmarshal(confBytes, &conf)
	if err != nil {
		log.WithField("error", err).Panicf("Error unmarshalling config")
	}
	// If we are running test, then don't create auditors.
	// This is pretty hacky, but doing it The Right Way would require
	// restructuring SOPS to use dependency injection instead of just using
	// globals everywhere.
	if flag.Lookup("test.v") != nil {
		return
	}
	var auditErrors []error

	for _, pgConf := range conf.Backends.Postgres {
		auditDb, err := NewPostgresAuditor(pgConf.ConnStr)
		if err != nil {
			auditErrors = append(auditErrors, errors.Wrap(err, fmt.Sprintf("connectStr: %s, err", pgConf.ConnStr)))
		}
		auditors = append(auditors, auditDb)
	}
	if len(auditErrors) > 0 {
		log.Errorf("connecting to audit database, defined in %s", configFile)
		for _, err := range auditErrors {
			log.Error(err)
		}
		log.Fatal("one or more audit backends reported errors, exiting")
	}
}

// TODO: Make platform agnostic
const configFile = "/etc/sops/audit.yaml"

type config struct {
	Backends struct {
		Postgres []struct {
			ConnStr string `yaml:"connection_string"`
		} `yaml:"postgres"`
	} `yaml:"backends"`
}

var auditors []Auditor

// SubmitEvent handles an event for all auditors
func SubmitEvent(event interface{}) {
	for _, auditor := range auditors {
		auditor.Handle(event)
	}
}

// Register registers a new Auditor in the global auditor list
func Register(auditor Auditor) {
	auditors = append(auditors, auditor)
}

// Auditor is notified when noteworthy events happen,
// for example when a file is encrypted or decrypted.
type Auditor interface {
	// Handle() takes an audit event and attempts to persists it;
	// how it is persisted and how errors are handled is up to the
	// implementation of this interface.
	Handle(event interface{})
}

// DecryptEvent contains fields relevant to a decryption event
type DecryptEvent struct {
	File string
}

// EncryptEvent contains fields relevant to an encryption event
type EncryptEvent struct {
	File string
}

// RotateEvent contains fields relevant to a key rotation event
type RotateEvent struct {
	File string
}

// PostgresAuditor is a Postgres SQL DB implementation of the Auditor interface.
// It persists the audit event by writing a row to the 'audit_event' table.
// Errors with writing to the database will output a log message and the
// process will exit with status set to 1
type PostgresAuditor struct {
	DB *sql.DB
}

// NewPostgresAuditor is the constructor for a new PostgresAuditor struct
// initialized with the given db connection string
func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) {
	db, err := sql.Open("postgres", connStr)
	pg := &PostgresAuditor{DB: db}
	if err != nil {
		return pg, err
	}
	var result int
	err = pg.DB.QueryRow("SELECT 1").Scan(&result)
	if err != nil {
		return pg, fmt.Errorf("Pinging audit database failed: %s", err)
	} else if result != 1 {
		return pg, fmt.Errorf("Database malfunction: SELECT 1 should return 1, but returned %d", result)
	}
	return pg, nil
}

// Handle persists the audit event by writing a row to the
// 'audit_event' postgres table
func (p *PostgresAuditor) Handle(event interface{}) {
	u, err := user.Current()
	if err != nil {
		log.Fatalf("Error getting current user for auditing: %s", err)
	}
	switch event := event.(type) {
	case DecryptEvent:
		// Save the event to the database
		log.WithField("file", event.File).
			Debug("Saving decrypt event to database")
		_, err = p.DB.Exec("INSERT INTO audit_event (action, username, file) VALUES ($1, $2, $3)", "decrypt", u.Username, event.File)
		if err != nil {
			log.Fatalf("Failed to insert audit record: %s", err)
		}
	case EncryptEvent:
		// Save the event to the database
		log.WithField("file", event.File).
			Debug("Saving encrypt event to database")
		_, err = p.DB.Exec("INSERT INTO audit_event (action, username, file) VALUES ($1, $2, $3)", "encrypt", u.Username, event.File)
		if err != nil {
			log.Fatalf("Failed to insert audit record: %s", err)
		}
	case RotateEvent:
		// Save the event to the database
		log.WithField("file", event.File).
			Debug("Saving rotate event to database")
		_, err = p.DB.Exec("INSERT INTO audit_event (action, username, file) VALUES ($1, $2, $3)", "rotate", u.Username, event.File)
		if err != nil {
			log.Fatalf("Failed to insert audit record: %s", err)
		}
	default:
		log.WithField("type", fmt.Sprintf("%T", event)).
			Info("Received unknown event")
	}
}
0707010000001A000081A400000000000000000000000162794F930000013F000000000000000000000000000000000000001C00000000sops-3.7.3/audit/schema.sqlCREATE TABLE audit_event (
  id SERIAL PRIMARY KEY,
  timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  action TEXT,
  username TEXT,
  file TEXT
);

CREATE ROLE sops WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN PASSWORD 'sops';

GRANT INSERT ON audit_event TO sops;
GRANT USAGE ON audit_event_id_seq TO sops;
0707010000001B000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001000000000sops-3.7.3/azkv0707010000001C000081A400000000000000000000000162794F93000022C7000000000000000000000000000000000000001D00000000sops-3.7.3/azkv/keysource.go/*
Package azkv contains an implementation of the go.mozilla.org/sops/v3/keys.MasterKey interface that encrypts and decrypts the
data key using Azure Key Vault with the Azure Go SDK.
*/
package azkv //import "go.mozilla.org/sops/v3/azkv"

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"go.mozilla.org/sops/v3/logging"

	"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/Azure/go-autorest/autorest/azure/auth"
	"github.com/sirupsen/logrus"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("AZKV")
}

// MasterKey is a Azure Key Vault key used to encrypt and decrypt sops' data key.
type MasterKey struct {
	VaultURL string
	Name     string
	Version  string

	EncryptedKey string
	CreationDate time.Time
}

func newKeyVaultClient() (keyvault.BaseClient, error) {
	var err error
	c := keyvault.New()
	c.Authorizer, err = newAuthorizer()
	if err != nil {
		log.WithError(err).Error("Failed to create Azure authorizer")
		return c, err
	}

	return c, nil
}

// newAuthorizer returns the correct authorizer for the given settings and/or based on the value
// of the AZURE_AUTH_METHOD environment variable, which may be one of:
// clientcredentials, clientcertificate, usernamepassword, msi, or cli (default).
func newAuthorizer() (autorest.Authorizer, error) {
	settings := struct {
		authMethod          string
		tenantID            string
		clientID            string
		clientSecret        string
		certificatePath     string
		certificatePassword string
		username            string
		password            string
		envName             string
		resource            string
		environment         azure.Environment
	}{
		authMethod:          os.Getenv("AZURE_AUTH_METHOD"),
		tenantID:            os.Getenv("AZURE_TENANT_ID"),
		clientID:            os.Getenv("AZURE_CLIENT_ID"),
		clientSecret:        os.Getenv("AZURE_CLIENT_SECRET"),
		certificatePath:     os.Getenv("AZURE_CERTIFICATE_PATH"),
		certificatePassword: os.Getenv("AZURE_CERTIFICATE_PASSWORD"),
		username:            os.Getenv("AZURE_USERNAME"),
		password:            os.Getenv("AZURE_PASSWORD"),
		envName:             os.Getenv("AZURE_ENVIRONMENT"),
		resource:            os.Getenv("AZURE_AD_RESOURCE"),
	}

	settings.environment = azure.PublicCloud
	if settings.envName != "" {
		val, err := azure.EnvironmentFromName(settings.envName)
		if err != nil {
			return nil, err
		}
		settings.environment = val
	}

	if settings.resource == "" {
		settings.resource = strings.TrimSuffix(settings.environment.KeyVaultEndpoint, "/")
	}

	if os.Getenv("MSI_ENDPOINT") != "" {
		settings.authMethod = "msi"
	}

	// 1. Client credentials
	if (settings.clientSecret != "") || settings.authMethod == "clientcredentials" {
		config := auth.NewClientCredentialsConfig(settings.clientID, settings.clientSecret, settings.tenantID)
		config.AADEndpoint = settings.environment.ActiveDirectoryEndpoint
		config.Resource = settings.resource
		return config.Authorizer()
	}

	// 2. Client Certificate
	if (settings.certificatePath != "") || settings.authMethod == "clientcertificate" {
		config := auth.NewClientCertificateConfig(settings.certificatePath, settings.certificatePassword, settings.clientID, settings.tenantID)
		config.AADEndpoint = settings.environment.ActiveDirectoryEndpoint
		config.Resource = settings.resource
		return config.Authorizer()
	}

	// 3. Username Password
	if (settings.username != "" && settings.password != "") || settings.authMethod == "usernamepassword" {
		config := auth.NewUsernamePasswordConfig(settings.username, settings.password, settings.clientID, settings.tenantID)
		config.AADEndpoint = settings.environment.ActiveDirectoryEndpoint
		config.Resource = settings.resource
		return config.Authorizer()
	}

	// 4. MSI
	if settings.authMethod == "msi" {
		config := auth.NewMSIConfig()
		config.Resource = settings.resource
		config.ClientID = settings.clientID
		return config.Authorizer()
	}

	// 5. Device Code
	if settings.authMethod == "devicecode" {
		// TODO: Removed until we decide how to handle prompt on stdout, etc.
		//// TODO: This will be required on every execution. Consider caching.
		//config := auth.NewDeviceFlowConfig(settings.clientID, settings.tenantID)
		//return config.Authorizer()
		return nil, errors.New("device code flow not implemented")
	}

	// 6. CLI
	return auth.NewAuthorizerFromCLIWithResource(settings.resource)
}

// NewMasterKey creates a new MasterKey from an URL, key name and version, setting the creation date to the current date
func NewMasterKey(vaultURL string, keyName string, keyVersion string) *MasterKey {
	return &MasterKey{
		VaultURL:     vaultURL,
		Name:         keyName,
		Version:      keyVersion,
		CreationDate: time.Now().UTC(),
	}
}

// MasterKeysFromURLs takes a comma separated list of Azure Key Vault URLs and returns a slice of new MasterKeys for them
func MasterKeysFromURLs(urls string) ([]*MasterKey, error) {
	var keys []*MasterKey
	if urls == "" {
		return keys, nil
	}
	for _, s := range strings.Split(urls, ",") {
		k, err := NewMasterKeyFromURL(s)
		if err != nil {
			return nil, err
		}
		keys = append(keys, k)
	}
	return keys, nil
}

// NewMasterKeyFromURL takes an Azure Key Vault key URL and returns a new MasterKey
// URL format is {vaultUrl}/keys/{key-name}/{key-version}
func NewMasterKeyFromURL(url string) (*MasterKey, error) {
	k := &MasterKey{}
	re := regexp.MustCompile("^(https://[^/]+)/keys/([^/]+)/([^/]+)$")
	parts := re.FindStringSubmatch(url)
	if parts == nil || len(parts) < 2 {
		return nil, fmt.Errorf("Could not parse valid key from %q", url)
	}

	k.VaultURL = parts[1]
	k.Name = parts[2]
	k.Version = parts[3]
	k.CreationDate = time.Now().UTC()
	return k, nil
}

// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)
}

// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
	key.EncryptedKey = string(enc)
}

// Encrypt takes a sops data key, encrypts it with Key Vault and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
	c, err := newKeyVaultClient()
	if err != nil {
		return err
	}
	data := base64.RawURLEncoding.EncodeToString(dataKey)
	p := keyvault.KeyOperationsParameters{Value: &data, Algorithm: keyvault.RSAOAEP256}

	res, err := c.Encrypt(context.Background(), key.VaultURL, key.Name, key.Version, p)
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"key":     key.Name,
			"version": key.Version,
		}).Error("Encryption failed")
		return fmt.Errorf("Failed to encrypt data: %w", err)
	}

	key.EncryptedKey = *res.Result
	log.WithFields(logrus.Fields{
		"key":     key.Name,
		"version": key.Version,
	}).Info("Encryption succeeded")

	return nil
}

// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(dataKey)
	}
	return nil
}

// Decrypt decrypts the EncryptedKey field with Azure Key Vault and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
	c, err := newKeyVaultClient()
	if err != nil {
		return nil, err
	}
	p := keyvault.KeyOperationsParameters{Value: &key.EncryptedKey, Algorithm: keyvault.RSAOAEP256}

	res, err := c.Decrypt(context.TODO(), key.VaultURL, key.Name, key.Version, p)
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"key":     key.Name,
			"version": key.Version,
		}).Error("Decryption failed")
		return nil, fmt.Errorf("Error decrypting key: %w", err)
	}

	plaintext, err := base64.RawURLEncoding.DecodeString(*res.Result)
	if err != nil {
		log.WithError(err).WithFields(logrus.Fields{
			"key":     key.Name,
			"version": key.Version,
		}).Error("Decryption failed")
		return nil, err
	}

	log.WithFields(logrus.Fields{
		"key":     key.Name,
		"version": key.Version,
	}).Info("Decryption succeeded")
	return plaintext, nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}

// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
	return fmt.Sprintf("%s/keys/%s/%s", key.VaultURL, key.Name, key.Version)
}

// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
	out := make(map[string]interface{})
	out["vaultUrl"] = key.VaultURL
	out["key"] = key.Name
	out["version"] = key.Version
	out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
	out["enc"] = key.EncryptedKey
	return out
}
0707010000001D000081A400000000000000000000000162794F9300000C7D000000000000000000000000000000000000002200000000sops-3.7.3/azkv/keysource_test.gopackage azkv

import (
	"flag"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestAzureKeySourceFromUrl(t *testing.T) {
	cases := []struct {
		name              string
		input             string
		expectSuccess     bool
		expectedFoundKeys int
		expectedKeys      []MasterKey
	}{
		{
			name:              "Single url",
			input:             "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90",
			expectSuccess:     true,
			expectedFoundKeys: 1,
			expectedKeys: []MasterKey{
				{
					VaultURL: "https://test.vault.azure.net",
					Name:     "test-key",
					Version:  "a2a690a4fcc04166b739da342a912c90",
				},
			},
		},
		{
			name:              "Multiple url",
			input:             "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90,https://test2.vault.azure.net/keys/another-test-key/cf0021e8b743453bae758e7fbf71b60e",
			expectSuccess:     true,
			expectedFoundKeys: 2,
			expectedKeys: []MasterKey{
				{
					VaultURL: "https://test.vault.azure.net",
					Name:     "test-key",
					Version:  "a2a690a4fcc04166b739da342a912c90",
				},
				{
					VaultURL: "https://test2.vault.azure.net",
					Name:     "another-test-key",
					Version:  "cf0021e8b743453bae758e7fbf71b60e",
				},
			},
		},
		{
			name:          "Single malformed url",
			input:         "https://test.vault.azure.net/no-keys-here/test-key/a2a690a4fcc04166b739da342a912c90",
			expectSuccess: false,
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			keys, err := MasterKeysFromURLs(c.input)
			if err != nil && c.expectSuccess {
				t.Fatalf("Unexpected error %v", err)
			} else if err == nil && !c.expectSuccess {
				t.Fatal("Expected error, but no error was returned")
			}

			if c.expectedFoundKeys != len(keys) {
				t.Errorf("Unexpected number of keys returned, expected %d, got %d", c.expectedFoundKeys, len(keys))
			}
			for idx := range keys {
				assert.Equal(t, c.expectedKeys[idx].VaultURL, keys[idx].VaultURL)
				assert.Equal(t, c.expectedKeys[idx].Name, keys[idx].Name)
				assert.Equal(t, c.expectedKeys[idx].Version, keys[idx].Version)
			}
		})
	}
}

func TestKeyToMap(t *testing.T) {
	key := MasterKey{
		CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
		VaultURL:     "https://test.vault.azure.net",
		Name:         "test-key",
		Version:      "1",
		EncryptedKey: "this is encrypted",
	}
	assert.Equal(t, map[string]interface{}{
		"vaultUrl":   key.VaultURL,
		"key":        key.Name,
		"version":    key.Version,
		"enc":        "this is encrypted",
		"created_at": "2016-10-31T10:00:00Z",
	}, key.ToMap())
}

var azureKeyAcceptanceTestURL = flag.String("azure-key", "", "URL to Azure Key Vault (note that this can incur real costs!)")

func TestRoundtrip(t *testing.T) {
	if *azureKeyAcceptanceTestURL == "" {
		t.Skip("Azure URL not provided, skipping acceptance test")
	}

	input := []byte("test-string")

	key, err := NewMasterKeyFromURL(*azureKeyAcceptanceTestURL)
	if err != nil {
		t.Fatal(err)
	}
	err = key.Encrypt(input)
	if err != nil {
		t.Fatal(err)
	}

	output, err := key.Decrypt()
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, input, output)
}
0707010000001E000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/bin0707010000001F000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/bin/ci07070100000020000081ED00000000000000000000000162794F9300000324000000000000000000000000000000000000002600000000sops-3.7.3/bin/ci/deploy_dockerhub.sh#!/bin/bash

# THIS IS MEANT TO BE RUN BY CI

set -e
set +x

# Usage: retry MAX CMD...
# Retry CMD up to MAX times. If it fails MAX times, returns failure.
# Example: retry 3 docker push "mozilla/sops:$TAG"
function retry() {
    max=$1
    shift
    count=1
    until "$@"; do
        count=$((count + 1))
        if [[ $count -gt $max ]]; then
            return 1
        fi
        echo "$count / $max"
    done
    return 0
}
if [[ "$DOCKER_DEPLOY" == "true" ]]; then 
    # configure docker creds
    retry 3 docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
    # docker tag and push git branch to dockerhub
    if [ -n "$1" ]; then
        retry 3 docker push "mozilla/sops:$1" ||
            (echo "Couldn't push mozilla/sops:$1" && false)
        echo "Pushed mozilla/sops:$1"
    fi
fi
07070100000021000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/cmd07070100000022000041ED00000000000000000000000662794F9300000000000000000000000000000000000000000000001400000000sops-3.7.3/cmd/sops07070100000023000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001A00000000sops-3.7.3/cmd/sops/codes07070100000024000081A400000000000000000000000162794F9300000548000000000000000000000000000000000000002300000000sops-3.7.3/cmd/sops/codes/codes.go// Package codes the exit statuses returned by the sops binary
package codes

// Exit statuses returned by the binary
const (
	ErrorGeneric                           int = 1
	CouldNotReadInputFile                  int = 2
	CouldNotWriteOutputFile                int = 3
	ErrorDumpingTree                       int = 4
	ErrorReadingConfig                     int = 5
	ErrorInvalidKMSEncryptionContextFormat int = 6
	ErrorInvalidSetFormat                  int = 7
	ErrorConflictingParameters             int = 8
	ErrorEncryptingMac                     int = 21
	ErrorEncryptingTree                    int = 23
	ErrorDecryptingMac                     int = 24
	ErrorDecryptingTree                    int = 25
	CannotChangeKeysFromNonExistentFile    int = 49
	MacMismatch                            int = 51
	MacNotFound                            int = 52
	ConfigFileNotFound                     int = 61
	KeyboardInterrupt                      int = 85
	InvalidTreePathFormat                  int = 91
	NoFileSpecified                        int = 100
	CouldNotRetrieveKey                    int = 128
	NoEncryptionKeyFound                   int = 111
	FileHasNotBeenModified                 int = 200
	NoEditorFound                          int = 201
	FailedToCompareVersions                int = 202
	FileAlreadyEncrypted                   int = 203
)
07070100000025000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001B00000000sops-3.7.3/cmd/sops/common07070100000026000081A400000000000000000000000162794F930000345C000000000000000000000000000000000000002500000000sops-3.7.3/cmd/sops/common/common.gopackage common

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/fatih/color"
	wordwrap "github.com/mitchellh/go-wordwrap"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	. "go.mozilla.org/sops/v3/cmd/sops/formats"
	"go.mozilla.org/sops/v3/keys"
	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/kms"
	"go.mozilla.org/sops/v3/stores/dotenv"
	"go.mozilla.org/sops/v3/stores/ini"
	"go.mozilla.org/sops/v3/stores/json"
	"go.mozilla.org/sops/v3/stores/yaml"
	"go.mozilla.org/sops/v3/version"
	"golang.org/x/crypto/ssh/terminal"
	"gopkg.in/urfave/cli.v1"
)

// ExampleFileEmitter emits example files. This is used by the `sops` binary
// whenever a new file is created, in order to present the user with a non-empty file
type ExampleFileEmitter interface {
	EmitExample() []byte
}

// Store handles marshaling and unmarshaling from SOPS files
type Store interface {
	sops.Store
	ExampleFileEmitter
}

type storeConstructor = func() Store

func newBinaryStore() Store {
	return &json.BinaryStore{}
}

func newDotenvStore() Store {
	return &dotenv.Store{}
}

func newIniStore() Store {
	return &ini.Store{}
}

func newJsonStore() Store {
	return &json.Store{}
}

func newYamlStore() Store {
	return &yaml.Store{}
}

var storeConstructors = map[Format]storeConstructor{
	Binary: newBinaryStore,
	Dotenv: newDotenvStore,
	Ini:    newIniStore,
	Json:   newJsonStore,
	Yaml:   newYamlStore,
}

// DecryptTreeOpts are the options needed to decrypt a tree
type DecryptTreeOpts struct {
	// Tree is the tree to be decrypted
	Tree *sops.Tree
	// KeyServices are the key services to be used for decryption of the data key
	KeyServices []keyservice.KeyServiceClient
	// IgnoreMac is whether or not to ignore the Message Authentication Code included in the SOPS tree
	IgnoreMac bool
	// Cipher is the cryptographic cipher to use to decrypt the values inside the tree
	Cipher sops.Cipher
}

// DecryptTree decrypts the tree passed in through the DecryptTreeOpts and additionally returns the decrypted data key
func DecryptTree(opts DecryptTreeOpts) (dataKey []byte, err error) {
	dataKey, err = opts.Tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
	if err != nil {
		return nil, NewExitError(err, codes.CouldNotRetrieveKey)
	}
	computedMac, err := opts.Tree.Decrypt(dataKey, opts.Cipher)
	if err != nil {
		return nil, NewExitError(fmt.Sprintf("Error decrypting tree: %s", err), codes.ErrorDecryptingTree)
	}
	fileMac, err := opts.Cipher.Decrypt(opts.Tree.Metadata.MessageAuthenticationCode, dataKey, opts.Tree.Metadata.LastModified.Format(time.RFC3339))
	if !opts.IgnoreMac {
		if fileMac != computedMac {
			// If the file has an empty MAC, display "no MAC" instead of not displaying anything
			if fileMac == "" {
				fileMac = "no MAC"
			}
			return nil, NewExitError(fmt.Sprintf("MAC mismatch. File has %s, computed %s", fileMac, computedMac), codes.MacMismatch)
		}
	}
	return dataKey, nil
}

// EncryptTreeOpts are the options needed to encrypt a tree
type EncryptTreeOpts struct {
	// Tree is the tree to be encrypted
	Tree *sops.Tree
	// Cipher is the cryptographic cipher to use to encrypt the values inside the tree
	Cipher sops.Cipher
	// DataKey is the key the cipher should use to encrypt the values inside the tree
	DataKey []byte
}

// EncryptTree encrypts the tree passed in through the EncryptTreeOpts
func EncryptTree(opts EncryptTreeOpts) error {
	unencryptedMac, err := opts.Tree.Encrypt(opts.DataKey, opts.Cipher)
	if err != nil {
		return NewExitError(fmt.Sprintf("Error encrypting tree: %s", err), codes.ErrorEncryptingTree)
	}
	opts.Tree.Metadata.LastModified = time.Now().UTC()
	opts.Tree.Metadata.MessageAuthenticationCode, err = opts.Cipher.Encrypt(unencryptedMac, opts.DataKey, opts.Tree.Metadata.LastModified.Format(time.RFC3339))
	if err != nil {
		return NewExitError(fmt.Sprintf("Could not encrypt MAC: %s", err), codes.ErrorEncryptingMac)
	}
	return nil
}

// LoadEncryptedFile loads an encrypted SOPS file, returning a SOPS tree
func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops.Tree, error) {
	fileBytes, err := ioutil.ReadFile(inputPath)
	if err != nil {
		return nil, NewExitError(fmt.Sprintf("Error reading file: %s", err), codes.CouldNotReadInputFile)
	}
	path, err := filepath.Abs(inputPath)
	if err != nil {
		return nil, err
	}
	tree, err := loader.LoadEncryptedFile(fileBytes)
	tree.FilePath = path
	return &tree, err
}

// NewExitError returns a cli.ExitError given an error (wrapped in a generic interface{})
// and an exit code to represent the failure
func NewExitError(i interface{}, exitCode int) *cli.ExitError {
	if userErr, ok := i.(sops.UserError); ok {
		return NewExitError(userErr.UserError(), exitCode)
	}
	return cli.NewExitError(i, exitCode)
}

// StoreForFormat returns the correct format-specific implementation
// of the Store interface given the format.
func StoreForFormat(format Format) Store {
	storeConst, found := storeConstructors[format]
	if !found {
		storeConst = storeConstructors[Binary] // default
	}
	return storeConst()
}

// DefaultStoreForPath returns the correct format-specific implementation
// of the Store interface given the path to a file
func DefaultStoreForPath(path string) Store {
	format := FormatForPath(path)
	return StoreForFormat(format)
}

// DefaultStoreForPathOrFormat returns the correct format-specific implementation
// of the Store interface given the formatString if specified, or the path to a file.
// This is to support the cli, where both are provided.
func DefaultStoreForPathOrFormat(path, format string) Store {
	formatFmt := FormatForPathOrString(path, format)
	return StoreForFormat(formatFmt)
}

// KMS_ENC_CTX_BUG_FIXED_VERSION represents the SOPS version in which the
// encryption context bug was fixed
const KMS_ENC_CTX_BUG_FIXED_VERSION = "3.3.0"

// DetectKMSEncryptionContextBug returns true if the encryption context bug is detected
// in a given runtime sops.Tree object
func DetectKMSEncryptionContextBug(tree *sops.Tree) (bool, error) {
	versionCheck, err := version.AIsNewerThanB(KMS_ENC_CTX_BUG_FIXED_VERSION, tree.Metadata.Version)
	if err != nil {
		return false, err
	}

	if versionCheck {
		_, _, key := GetKMSKeyWithEncryptionCtx(tree)
		if key != nil {
			return true, nil
		}
	}

	return false, nil
}

// GetKMSKeyWithEncryptionCtx returns the first KMS key affected by the encryption context bug as well as its location in the key groups.
func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex int, key *kms.MasterKey) {
	for i, kg := range tree.Metadata.KeyGroups {
		for n, k := range kg {
			kmsKey, ok := k.(*kms.MasterKey)
			if ok {
				if kmsKey.EncryptionContext != nil && len(kmsKey.EncryptionContext) >= 2 {
					duplicateValues := map[string]int{}
					for _, v := range kmsKey.EncryptionContext {
						duplicateValues[*v] = duplicateValues[*v] + 1
					}
					if len(duplicateValues) > 1 {
						return i, n, kmsKey
					}
				}
			}
		}
	}
	return 0, 0, nil
}

// GenericDecryptOpts represents decryption options and config
type GenericDecryptOpts struct {
	Cipher      sops.Cipher
	InputStore  sops.Store
	InputPath   string
	IgnoreMAC   bool
	KeyServices []keyservice.KeyServiceClient
}

// LoadEncryptedFileWithBugFixes is a wrapper around LoadEncryptedFile which includes
// check for the issue described in https://github.com/mozilla/sops/pull/435
func LoadEncryptedFileWithBugFixes(opts GenericDecryptOpts) (*sops.Tree, error) {
	tree, err := LoadEncryptedFile(opts.InputStore, opts.InputPath)
	if err != nil {
		return nil, err
	}

	encCtxBug, err := DetectKMSEncryptionContextBug(tree)
	if err != nil {
		return nil, err
	}
	if encCtxBug {
		tree, err = FixAWSKMSEncryptionContextBug(opts, tree)
		if err != nil {
			return nil, err
		}
	}

	return tree, nil
}

// FixAWSKMSEncryptionContextBug is used to fix the issue described in https://github.com/mozilla/sops/pull/435
func FixAWSKMSEncryptionContextBug(opts GenericDecryptOpts, tree *sops.Tree) (*sops.Tree, error) {
	message := "Up until version 3.3.0 of sops there was a bug surrounding the " +
		"use of encryption context with AWS KMS." +
		"\nYou can read the full description of the issue here:" +
		"\nhttps://github.com/mozilla/sops/pull/435" +
		"\n\nIf a TTY is detected, sops will ask you if you'd like for this issue to be " +
		"automatically fixed, which will require re-encrypting the data keys used by " +
		"each key." +
		"\n\nIf you are not using a TTY, sops will fix the issue for this run.\n\n"
	fmt.Println(wordwrap.WrapString(message, 75))

	persistFix := false

	if terminal.IsTerminal(int(os.Stdout.Fd())) {
		var response string
		for response != "y" && response != "n" {
			fmt.Println("Would you like sops to automatically fix this issue? (y/n): ")
			_, err := fmt.Scanln(&response)
			if err != nil {
				return nil, err
			}
		}
		if response == "n" {
			return nil, fmt.Errorf("Exiting. User responded no")
		}
		persistFix = true
	}

	// If there is another key, then we should be able to just decrypt
	// without having to try different variations of the encryption context.
	dataKey, err := DecryptTree(DecryptTreeOpts{
		Cipher:      opts.Cipher,
		IgnoreMac:   opts.IgnoreMAC,
		Tree:        tree,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		dataKey = RecoverDataKeyFromBuggyKMS(opts, tree)
	}

	if dataKey == nil {
		return nil, NewExitError(fmt.Sprintf("Failed to decrypt, meaning there is likely another problem from the encryption context bug: %s", err), codes.ErrorDecryptingTree)
	}

	errs := tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices)
	if len(errs) > 0 {
		err = fmt.Errorf("Could not re-encrypt data key: %s", errs)
		return nil, err
	}

	err = EncryptTree(EncryptTreeOpts{
		DataKey: dataKey,
		Tree:    tree,
		Cipher:  opts.Cipher,
	})
	if err != nil {
		return nil, err
	}

	// If we are not going to persist the fix, just return the re-encrypted tree.
	if !persistFix {
		return tree, nil
	}

	encryptedFile, err := opts.InputStore.EmitEncryptedFile(*tree)
	if err != nil {
		return nil, NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}

	file, err := os.Create(opts.InputPath)
	defer file.Close()
	if err != nil {
		return nil, NewExitError(fmt.Sprintf("Could not open file for writing: %s", err), codes.CouldNotWriteOutputFile)
	}
	_, err = file.Write(encryptedFile)
	if err != nil {
		return nil, err
	}

	newTree, err := LoadEncryptedFile(opts.InputStore, opts.InputPath)
	if err != nil {
		return nil, err
	}

	return newTree, nil
}

// RecoverDataKeyFromBuggyKMS loops through variations on Encryption Context to
// recover the datakey. This is used to fix the issue described in https://github.com/mozilla/sops/pull/435
func RecoverDataKeyFromBuggyKMS(opts GenericDecryptOpts, tree *sops.Tree) []byte {
	kgndx, kndx, originalKey := GetKMSKeyWithEncryptionCtx(tree)

	keyToEdit := *originalKey

	encCtxVals := map[string]interface{}{}
	for _, v := range keyToEdit.EncryptionContext {
		encCtxVals[*v] = ""
	}

	encCtxVariations := []map[string]*string{}
	for ctxVal := range encCtxVals {
		encCtxVariation := map[string]*string{}
		for key := range keyToEdit.EncryptionContext {
			val := ctxVal
			encCtxVariation[key] = &val
		}
		encCtxVariations = append(encCtxVariations, encCtxVariation)
	}

	for _, encCtxVar := range encCtxVariations {
		keyToEdit.EncryptionContext = encCtxVar
		tree.Metadata.KeyGroups[kgndx][kndx] = &keyToEdit
		dataKey, err := DecryptTree(DecryptTreeOpts{
			Cipher:      opts.Cipher,
			IgnoreMac:   opts.IgnoreMAC,
			Tree:        tree,
			KeyServices: opts.KeyServices,
		})
		if err == nil {
			tree.Metadata.KeyGroups[kgndx][kndx] = originalKey
			tree.Metadata.Version = version.Version
			return dataKey
		}
	}

	return nil
}

// Diff represents a key diff
type Diff struct {
	Common  []keys.MasterKey
	Added   []keys.MasterKey
	Removed []keys.MasterKey
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// DiffKeyGroups returns the list of diffs found in two sops.keyGroup slices
func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff {
	var diffs []Diff
	for i := 0; i < max(len(ours), len(theirs)); i++ {
		var diff Diff
		var ourGroup, theirGroup sops.KeyGroup
		if len(ours) > i {
			ourGroup = ours[i]
		}
		if len(theirs) > i {
			theirGroup = theirs[i]
		}
		ourKeys := make(map[string]struct{})
		theirKeys := make(map[string]struct{})
		for _, key := range ourGroup {
			ourKeys[key.ToString()] = struct{}{}
		}
		for _, key := range theirGroup {
			if _, ok := ourKeys[key.ToString()]; ok {
				diff.Common = append(diff.Common, key)
			} else {
				diff.Added = append(diff.Added, key)
			}
			theirKeys[key.ToString()] = struct{}{}
		}
		for _, key := range ourGroup {
			if _, ok := theirKeys[key.ToString()]; !ok {
				diff.Removed = append(diff.Removed, key)
			}
		}
		diffs = append(diffs, diff)
	}
	return diffs
}

// PrettyPrintDiffs prints a slice of Diff objects to stdout
func PrettyPrintDiffs(diffs []Diff) {
	for i, diff := range diffs {
		color.New(color.Underline).Printf("Group %d\n", i+1)
		for _, c := range diff.Common {
			fmt.Printf("    %s\n", c.ToString())
		}
		for _, c := range diff.Added {
			color.New(color.FgGreen).Printf("+++ %s\n", c.ToString())
		}
		for _, c := range diff.Removed {
			color.New(color.FgRed).Printf("--- %s\n", c.ToString())
		}
	}
}
07070100000027000081A400000000000000000000000162794F93000007DC000000000000000000000000000000000000001F00000000sops-3.7.3/cmd/sops/decrypt.gopackage main

import (
	"fmt"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keyservice"
)

type decryptOpts struct {
	Cipher      sops.Cipher
	InputStore  sops.Store
	OutputStore sops.Store
	InputPath   string
	IgnoreMAC   bool
	Extract     []interface{}
	KeyServices []keyservice.KeyServiceClient
}

func decrypt(opts decryptOpts) (decryptedFile []byte, err error) {
	tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
		Cipher:      opts.Cipher,
		InputStore:  opts.InputStore,
		InputPath:   opts.InputPath,
		IgnoreMAC:   opts.IgnoreMAC,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	_, err = common.DecryptTree(common.DecryptTreeOpts{
		Cipher:      opts.Cipher,
		IgnoreMac:   opts.IgnoreMAC,
		Tree:        tree,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	if len(opts.Extract) > 0 {
		return extract(tree, opts.Extract, opts.OutputStore)
	}
	decryptedFile, err = opts.OutputStore.EmitPlainFile(tree.Branches)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Error dumping file: %s", err), codes.ErrorDumpingTree)
	}
	return decryptedFile, err
}

func extract(tree *sops.Tree, path []interface{}, outputStore sops.Store) (output []byte, err error) {
	v, err := tree.Branches[0].Truncate(path)
	if err != nil {
		return nil, fmt.Errorf("error truncating tree: %s", err)
	}
	if newBranch, ok := v.(sops.TreeBranch); ok {
		tree.Branches[0] = newBranch
		decrypted, err := outputStore.EmitPlainFile(tree.Branches)
		if err != nil {
			return nil, common.NewExitError(fmt.Sprintf("Error dumping file: %s", err), codes.ErrorDumpingTree)
		}
		return decrypted, err
	} else if str, ok := v.(string); ok {
		return []byte(str), nil
	}
	bytes, err := outputStore.EmitValue(v)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Error dumping tree: %s", err), codes.ErrorDumpingTree)
	}
	return bytes, nil
}
07070100000028000081A400000000000000000000000162794F9300001FFF000000000000000000000000000000000000001C00000000sops-3.7.3/cmd/sops/edit.gopackage main

import (
	"fmt"
	"io/ioutil"
	"os"

	"crypto/md5"
	exec "golang.org/x/sys/execabs"
	"io"
	"strings"

	"bufio"
	"bytes"

	"path/filepath"

	"github.com/google/shlex"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/version"
)

type editOpts struct {
	Cipher         sops.Cipher
	InputStore     common.Store
	OutputStore    common.Store
	InputPath      string
	IgnoreMAC      bool
	KeyServices    []keyservice.KeyServiceClient
	ShowMasterKeys bool
}

type editExampleOpts struct {
	editOpts
	UnencryptedSuffix string
	EncryptedSuffix   string
	UnencryptedRegex  string
	EncryptedRegex    string
	KeyGroups         []sops.KeyGroup
	GroupThreshold    int
}

type runEditorUntilOkOpts struct {
	TmpFile        *os.File
	OriginalHash   []byte
	InputStore     sops.Store
	ShowMasterKeys bool
	Tree           *sops.Tree
}

func editExample(opts editExampleOpts) ([]byte, error) {
	fileBytes := opts.InputStore.EmitExample()
	branches, err := opts.InputStore.LoadPlainFile(fileBytes)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Error unmarshalling file: %s", err), codes.CouldNotReadInputFile)
	}
	path, err := filepath.Abs(opts.InputPath)
	if err != nil {
		return nil, err
	}
	tree := sops.Tree{
		Branches: branches,
		Metadata: sops.Metadata{
			KeyGroups:         opts.KeyGroups,
			UnencryptedSuffix: opts.UnencryptedSuffix,
			EncryptedSuffix:   opts.EncryptedSuffix,
			UnencryptedRegex:  opts.UnencryptedRegex,
			EncryptedRegex:    opts.EncryptedRegex,
			Version:           version.Version,
			ShamirThreshold:   opts.GroupThreshold,
		},
		FilePath: path,
	}

	// Generate a data key
	dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
	if len(errs) > 0 {
		return nil, common.NewExitError(fmt.Sprintf("Error encrypting the data key with one or more master keys: %s", errs), codes.CouldNotRetrieveKey)
	}

	return editTree(opts.editOpts, &tree, dataKey)
}

func edit(opts editOpts) ([]byte, error) {
	// Load the file
	tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
		Cipher:      opts.Cipher,
		InputStore:  opts.InputStore,
		InputPath:   opts.InputPath,
		IgnoreMAC:   opts.IgnoreMAC,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}
	// Decrypt the file
	dataKey, err := common.DecryptTree(common.DecryptTreeOpts{
		Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree, KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	return editTree(opts, tree, dataKey)
}

func editTree(opts editOpts, tree *sops.Tree, dataKey []byte) ([]byte, error) {
	// Create temporary file for editing
	tmpdir, err := ioutil.TempDir("", "")
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not create temporary directory: %s", err), codes.CouldNotWriteOutputFile)
	}
	defer os.RemoveAll(tmpdir)

	tmpfile, err := os.Create(filepath.Join(tmpdir, filepath.Base(opts.InputPath)))
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not create temporary file: %s", err), codes.CouldNotWriteOutputFile)
	}

	// Write to temporary file
	var out []byte
	if opts.ShowMasterKeys {
		out, err = opts.OutputStore.EmitEncryptedFile(*tree)
	} else {
		out, err = opts.OutputStore.EmitPlainFile(tree.Branches)
	}
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}
	_, err = tmpfile.Write(out)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not write output file: %s", err), codes.CouldNotWriteOutputFile)
	}

	// Close temporary file, since Windows won't delete the file unless it's closed beforehand
	defer tmpfile.Close()

	// Compute file hash to detect if the file has been edited
	origHash, err := hashFile(tmpfile.Name())
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not hash file: %s", err), codes.CouldNotReadInputFile)
	}

	// Let the user edit the file
	err = runEditorUntilOk(runEditorUntilOkOpts{
		InputStore: opts.InputStore, OriginalHash: origHash, TmpFile: tmpfile,
		ShowMasterKeys: opts.ShowMasterKeys, Tree: tree})
	if err != nil {
		return nil, err
	}

	// Encrypt the file
	err = common.EncryptTree(common.EncryptTreeOpts{
		DataKey: dataKey, Tree: tree, Cipher: opts.Cipher,
	})
	if err != nil {
		return nil, err
	}

	// Output the file
	encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}
	return encryptedFile, nil
}

func runEditorUntilOk(opts runEditorUntilOkOpts) error {
	for {
		err := runEditor(opts.TmpFile.Name())
		if err != nil {
			return common.NewExitError(fmt.Sprintf("Could not run editor: %s", err), codes.NoEditorFound)
		}
		newHash, err := hashFile(opts.TmpFile.Name())
		if err != nil {
			return common.NewExitError(fmt.Sprintf("Could not hash file: %s", err), codes.CouldNotReadInputFile)
		}
		if bytes.Equal(newHash, opts.OriginalHash) {
			return common.NewExitError("File has not changed, exiting.", codes.FileHasNotBeenModified)
		}
		edited, err := ioutil.ReadFile(opts.TmpFile.Name())
		if err != nil {
			return common.NewExitError(fmt.Sprintf("Could not read edited file: %s", err), codes.CouldNotReadInputFile)
		}
		newBranches, err := opts.InputStore.LoadPlainFile(edited)
		if err != nil {
			log.WithField(
				"error",
				err,
			).Errorf("Could not load tree, probably due to invalid " +
				"syntax. Press a key to return to the editor, or Ctrl+C to " +
				"exit.")
			bufio.NewReader(os.Stdin).ReadByte()
			continue
		}
		if opts.ShowMasterKeys {
			// The file is not actually encrypted, but it contains SOPS
			// metadata
			t, err := opts.InputStore.LoadEncryptedFile(edited)
			if err != nil {
				log.WithField(
					"error",
					err,
				).Errorf("SOPS metadata is invalid. Press a key to " +
					"return to the editor, or Ctrl+C to exit.")
				bufio.NewReader(os.Stdin).ReadByte()
				continue
			}
			// Replace the whole tree, because otherwise newBranches would
			// contain the SOPS metadata
			opts.Tree = &t
		}
		opts.Tree.Branches = newBranches
		needVersionUpdated, err := version.AIsNewerThanB(version.Version, opts.Tree.Metadata.Version)
		if err != nil {
			return common.NewExitError(fmt.Sprintf("Failed to compare document version %q with program version %q: %v", opts.Tree.Metadata.Version, version.Version, err), codes.FailedToCompareVersions)
		}
		if needVersionUpdated {
			opts.Tree.Metadata.Version = version.Version
		}
		if opts.Tree.Metadata.MasterKeyCount() == 0 {
			log.Error("No master keys were provided, so sops can't " +
				"encrypt the file. Press a key to return to the editor, or " +
				"Ctrl+C to exit.")
			bufio.NewReader(os.Stdin).ReadByte()
			continue
		}
		break
	}
	return nil
}

func hashFile(filePath string) ([]byte, error) {
	var result []byte
	file, err := os.Open(filePath)
	if err != nil {
		return result, err
	}
	defer file.Close()
	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return result, err
	}
	return hash.Sum(result), nil
}

func runEditor(path string) error {
	editor := os.Getenv("EDITOR")
	var cmd *exec.Cmd
	if editor == "" {
		editor, err := lookupAnyEditor("vim", "nano", "vi")
		if err != nil {
			return err
		}
		cmd = exec.Command(editor, path)
	} else {
		parts, err := shlex.Split(editor)
		if err != nil {
			return fmt.Errorf("invalid $EDITOR: %s", editor)
		}
		parts = append(parts, path)
		cmd = exec.Command(parts[0], parts[1:]...)
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func lookupAnyEditor(editorNames ...string) (editorPath string, err error) {
	for _, editorName := range editorNames {
		editorPath, err = exec.LookPath(editorName)
		if err == nil {
			return editorPath, nil
		}
	}
	return "", fmt.Errorf("no editor available: sops attempts to use the editor defined in the EDITOR environment variable, and if that's not set defaults to any of %s, but none of them could be found", strings.Join(editorNames, ", "))
}
07070100000029000081A400000000000000000000000162794F9300000C76000000000000000000000000000000000000001F00000000sops-3.7.3/cmd/sops/encrypt.gopackage main

import (
	"io/ioutil"
	"path/filepath"

	"fmt"

	wordwrap "github.com/mitchellh/go-wordwrap"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/version"
)

type encryptOpts struct {
	Cipher            sops.Cipher
	InputStore        sops.Store
	OutputStore       sops.Store
	InputPath         string
	KeyServices       []keyservice.KeyServiceClient
	UnencryptedSuffix string
	EncryptedSuffix   string
	UnencryptedRegex  string
	EncryptedRegex    string
	KeyGroups         []sops.KeyGroup
	GroupThreshold    int
}

type fileAlreadyEncryptedError struct{}

func (err *fileAlreadyEncryptedError) Error() string {
	return "File already encrypted"
}

func (err *fileAlreadyEncryptedError) UserError() string {
	message := "The file you have provided contains a top-level entry called " +
		"'sops'. This is generally due to the file already being encrypted. " +
		"SOPS uses a top-level entry called 'sops' to store the metadata " +
		"required to decrypt the file. For this reason, SOPS can not " +
		"encrypt files that already contain such an entry.\n\n" +
		"If this is an unencrypted file, rename the 'sops' entry.\n\n" +
		"If this is an encrypted file and you want to edit it, use the " +
		"editor mode, for example: `sops my_file.yaml`"
	return wordwrap.WrapString(message, 75)
}

func ensureNoMetadata(opts encryptOpts, branch sops.TreeBranch) error {
	for _, b := range branch {
		if b.Key == "sops" {
			return &fileAlreadyEncryptedError{}
		}
	}
	return nil
}

func encrypt(opts encryptOpts) (encryptedFile []byte, err error) {
	// Load the file
	fileBytes, err := ioutil.ReadFile(opts.InputPath)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Error reading file: %s", err), codes.CouldNotReadInputFile)
	}
	branches, err := opts.InputStore.LoadPlainFile(fileBytes)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Error unmarshalling file: %s", err), codes.CouldNotReadInputFile)
	}
	if err := ensureNoMetadata(opts, branches[0]); err != nil {
		return nil, common.NewExitError(err, codes.FileAlreadyEncrypted)
	}
	path, err := filepath.Abs(opts.InputPath)
	if err != nil {
		return nil, err
	}
	tree := sops.Tree{
		Branches: branches,
		Metadata: sops.Metadata{
			KeyGroups:         opts.KeyGroups,
			UnencryptedSuffix: opts.UnencryptedSuffix,
			EncryptedSuffix:   opts.EncryptedSuffix,
			UnencryptedRegex:  opts.UnencryptedRegex,
			EncryptedRegex:    opts.EncryptedRegex,
			Version:           version.Version,
			ShamirThreshold:   opts.GroupThreshold,
		},
		FilePath: path,
	}
	dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
	if len(errs) > 0 {
		err = fmt.Errorf("Could not generate data key: %s", errs)
		return nil, err
	}

	err = common.EncryptTree(common.EncryptTreeOpts{
		DataKey: dataKey,
		Tree:    &tree,
		Cipher:  opts.Cipher,
	})
	if err != nil {
		return nil, err
	}

	encryptedFile, err = opts.OutputStore.EmitEncryptedFile(tree)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}
	return
}
0707010000002A000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001C00000000sops-3.7.3/cmd/sops/formats0707010000002B000081A400000000000000000000000162794F9300000764000000000000000000000000000000000000002700000000sops-3.7.3/cmd/sops/formats/formats.gopackage formats

import "strings"

// Format is an enum type
type Format int

const (
	Binary Format = iota
	Dotenv
	Ini
	Json
	Yaml
)

var stringToFormat = map[string]Format{
	"binary": Binary,
	"dotenv": Dotenv,
	"ini":    Ini,
	"json":   Json,
	"yaml":   Yaml,
}

// FormatFromString returns a Format from a string.
// This is used for converting string cli options.
func FormatFromString(formatString string) Format {
	format, found := stringToFormat[formatString]
	if !found {
		return Binary
	}
	return format
}

// IsYAMLFile returns true if a given file path corresponds to a YAML file
func IsYAMLFile(path string) bool {
	return strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")
}

// IsJSONFile returns true if a given file path corresponds to a JSON file
func IsJSONFile(path string) bool {
	return strings.HasSuffix(path, ".json")
}

// IsEnvFile returns true if a given file path corresponds to a .env file
func IsEnvFile(path string) bool {
	return strings.HasSuffix(path, ".env")
}

// IsIniFile returns true if a given file path corresponds to a INI file
func IsIniFile(path string) bool {
	return strings.HasSuffix(path, ".ini")
}

// FormatForPath returns the correct format given the path to a file
func FormatForPath(path string) Format {
	format := Binary // default
	if IsYAMLFile(path) {
		format = Yaml
	} else if IsJSONFile(path) {
		format = Json
	} else if IsEnvFile(path) {
		format = Dotenv
	} else if IsIniFile(path) {
		format = Ini
	}
	return format
}

// FormatForPathOrString returns the correct format-specific implementation
// of the Store interface given the formatString if specified, or the path to a file.
// This is to support the cli, where both are provided.
func FormatForPathOrString(path, format string) Format {
	formatFmt, found := stringToFormat[format]
	if !found {
		formatFmt = FormatForPath(path)
	}
	return formatFmt
}
0707010000002C000081A400000000000000000000000162794F9300000671000000000000000000000000000000000000002C00000000sops-3.7.3/cmd/sops/formats/formats_test.gopackage formats

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFormatFromString(t *testing.T) {
	assert.Equal(t, Binary, FormatFromString("foobar"))
	assert.Equal(t, Dotenv, FormatFromString("dotenv"))
	assert.Equal(t, Ini, FormatFromString("ini"))
	assert.Equal(t, Yaml, FormatFromString("yaml"))
	assert.Equal(t, Json, FormatFromString("json"))
}

func TestFormatForPath(t *testing.T) {
	assert.Equal(t, Binary, FormatForPath("/path/to/foobar"))
	assert.Equal(t, Dotenv, FormatForPath("/path/to/foobar.env"))
	assert.Equal(t, Ini, FormatForPath("/path/to/foobar.ini"))
	assert.Equal(t, Json, FormatForPath("/path/to/foobar.json"))
	assert.Equal(t, Yaml, FormatForPath("/path/to/foobar.yml"))
	assert.Equal(t, Yaml, FormatForPath("/path/to/foobar.yaml"))
}

func TestFormatForPathOrString(t *testing.T) {
	assert.Equal(t, Binary, FormatForPathOrString("/path/to/foobar", ""))
	assert.Equal(t, Dotenv, FormatForPathOrString("/path/to/foobar", "dotenv"))
	assert.Equal(t, Dotenv, FormatForPathOrString("/path/to/foobar.env", ""))
	assert.Equal(t, Ini, FormatForPathOrString("/path/to/foobar", "ini"))
	assert.Equal(t, Ini, FormatForPathOrString("/path/to/foobar.ini", ""))
	assert.Equal(t, Json, FormatForPathOrString("/path/to/foobar", "json"))
	assert.Equal(t, Json, FormatForPathOrString("/path/to/foobar.json", ""))
	assert.Equal(t, Yaml, FormatForPathOrString("/path/to/foobar", "yaml"))
	assert.Equal(t, Yaml, FormatForPathOrString("/path/to/foobar.yml", ""))

	assert.Equal(t, Ini, FormatForPathOrString("/path/to/foobar.yml", "ini"))
	assert.Equal(t, Binary, FormatForPathOrString("/path/to/foobar.yml", "binary"))
}
0707010000002D000081A400000000000000000000000162794F9300009F5A000000000000000000000000000000000000001C00000000sops-3.7.3/cmd/sops/main.gopackage main //import "go.mozilla.org/sops/v3/cmd/sops"

import (
	encodingjson "encoding/json"
	"fmt"
	"net"
	"net/url"
	"os"
	osExec "os/exec"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/aes"
	"go.mozilla.org/sops/v3/age"
	_ "go.mozilla.org/sops/v3/audit"
	"go.mozilla.org/sops/v3/azkv"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/cmd/sops/subcommand/exec"
	"go.mozilla.org/sops/v3/cmd/sops/subcommand/groups"
	keyservicecmd "go.mozilla.org/sops/v3/cmd/sops/subcommand/keyservice"
	publishcmd "go.mozilla.org/sops/v3/cmd/sops/subcommand/publish"
	"go.mozilla.org/sops/v3/cmd/sops/subcommand/updatekeys"
	"go.mozilla.org/sops/v3/config"
	"go.mozilla.org/sops/v3/gcpkms"
	"go.mozilla.org/sops/v3/hcvault"
	"go.mozilla.org/sops/v3/keys"
	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/kms"
	"go.mozilla.org/sops/v3/logging"
	"go.mozilla.org/sops/v3/pgp"
	"go.mozilla.org/sops/v3/stores/dotenv"
	"go.mozilla.org/sops/v3/stores/json"
	"go.mozilla.org/sops/v3/version"
	"google.golang.org/grpc"
	"gopkg.in/urfave/cli.v1"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("CMD")
}

func main() {
	cli.VersionPrinter = version.PrintVersion
	app := cli.NewApp()

	keyserviceFlags := []cli.Flag{
		cli.BoolTFlag{
			Name:  "enable-local-keyservice",
			Usage: "use local key service",
		},
		cli.StringSliceFlag{
			Name:  "keyservice",
			Usage: "Specify the key services to use in addition to the local one. Can be specified more than once. Syntax: protocol://address. Example: tcp://myserver.com:5000",
		},
	}
	app.Name = "sops"
	app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, Azure Key Vault, age, and GPG support"
	app.ArgsUsage = "sops [options] file"
	app.Version = version.Version
	app.Authors = []cli.Author{
		{Name: "AJ Bahnken", Email: "ajvb@mozilla.com"},
		{Name: "Adrian Utrilla", Email: "adrianutrilla@gmail.com"},
		{Name: "Julien Vehent", Email: "jvehent@mozilla.com"},
	}
	app.UsageText = `sops is an editor of encrypted files that supports AWS KMS and PGP

   To encrypt or decrypt a document with AWS KMS, specify the KMS ARN
   in the -k flag or in the SOPS_KMS_ARN environment variable.
   (you need valid credentials in ~/.aws/credentials or in your env)

   To encrypt or decrypt a document with GCP KMS, specify the
   GCP KMS resource ID in the --gcp-kms flag or in the SOPS_GCP_KMS_IDS
   environment variable.
   (you need to setup google application default credentials. See
    https://developers.google.com/identity/protocols/application-default-credentials)


   To encrypt or decrypt a document with HashiCorp Vault's Transit Secret Engine, specify the
   Vault key URI name in the --hc-vault-transit flag or in the SOPS_VAULT_URIS environment variable (eg. https://vault.example.org:8200/v1/transit/keys/dev
      where 'https://vault.example.org:8200' is the vault server, 'transit' the enginePath, and 'dev' is the name of the key )
   environment variable.
   (you need to enable the Transit Secrets Engine in Vault. See
      https://www.vaultproject.io/docs/secrets/transit/index.html)

   To encrypt or decrypt a document with Azure Key Vault, specify the
   Azure Key Vault key URL in the --azure-kv flag or in the SOPS_AZURE_KEYVAULT_URL
   environment variable.
   (authentication is based on environment variables, see
    https://docs.microsoft.com/en-us/go/azure/azure-sdk-go-authorization#use-environment-based-authentication.
    The user/sp needs the key/encrypt and key/decrypt permissions)

   To encrypt or decrypt using age, specify the recipient in the -a flag, or
   in the SOPS_AGE_RECIPIENTS environment variable.

   To encrypt or decrypt using PGP, specify the PGP fingerprint in the
   -p flag or in the SOPS_PGP_FP environment variable.

   To use multiple KMS or PGP keys, separate them by commas. For example:
       $ sops -p "10F2...0A, 85D...B3F21" file.yaml

   The -p, -k, --gcp-kms, --hc-vault-transit and --azure-kv flags are only used to encrypt new documents. Editing
   or decrypting existing documents can be done with "sops file" or
   "sops -d file" respectively. The KMS and PGP keys listed in the encrypted
   documents are used then. To manage master keys in existing documents, use
   the "add-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}" and "rm-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}" flags.

   To use a different GPG binary than the one in your PATH, set SOPS_GPG_EXEC.

   To select a different editor than the default (vim), set EDITOR.

   For more information, see the README at github.com/mozilla/sops`
	app.EnableBashCompletion = true
	app.Commands = []cli.Command{
		{
			Name:      "exec-env",
			Usage:     "execute a command with decrypted values inserted into the environment",
			ArgsUsage: "[file to decrypt] [command to run]",
			Flags: append([]cli.Flag{
				cli.BoolFlag{
					Name:  "background",
					Usage: "background the process and don't wait for it to complete",
				},
				cli.StringFlag{
					Name:  "user",
					Usage: "the user to run the command as",
				},
			}, keyserviceFlags...),
			Action: func(c *cli.Context) error {
				if len(c.Args()) != 2 {
					return common.NewExitError(fmt.Errorf("error: missing file to decrypt"), codes.ErrorGeneric)
				}

				fileName := c.Args()[0]
				command := c.Args()[1]

				inputStore := inputStore(c, fileName)

				svcs := keyservices(c)
				opts := decryptOpts{
					OutputStore: &dotenv.Store{},
					InputStore:  inputStore,
					InputPath:   fileName,
					Cipher:      aes.NewCipher(),
					KeyServices: svcs,
					IgnoreMAC:   c.Bool("ignore-mac"),
				}

				output, err := decrypt(opts)
				if err != nil {
					return toExitError(err)
				}

				if err := exec.ExecWithEnv(exec.ExecOpts{
					Command:    command,
					Plaintext:  output,
					Background: c.Bool("background"),
					User:       c.String("user"),
				}); err != nil {
					return toExitError(err)
				}

				return nil
			},
		},
		{
			Name:      "exec-file",
			Usage:     "execute a command with the decrypted contents as a temporary file",
			ArgsUsage: "[file to decrypt] [command to run]",
			Flags: append([]cli.Flag{
				cli.BoolFlag{
					Name:  "background",
					Usage: "background the process and don't wait for it to complete",
				},
				cli.BoolFlag{
					Name:  "no-fifo",
					Usage: "use a regular file instead of a fifo to temporarily hold the decrypted contents",
				},
				cli.StringFlag{
					Name:  "user",
					Usage: "the user to run the command as",
				},
				cli.StringFlag{
					Name:  "input-type",
					Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the file's extension to determine the type",
				},
				cli.StringFlag{
					Name:  "output-type",
					Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the input file's extension to determine the output format",
				},
				cli.StringFlag{
					Name:  "filename",
					Usage: "filename for the temporarily file (default: tmp-file)",
				},
			}, keyserviceFlags...),
			Action: func(c *cli.Context) error {
				if len(c.Args()) != 2 {
					return common.NewExitError(fmt.Errorf("error: missing file to decrypt"), codes.ErrorGeneric)
				}

				fileName := c.Args()[0]
				command := c.Args()[1]

				inputStore := inputStore(c, fileName)
				outputStore := outputStore(c, fileName)

				svcs := keyservices(c)
				opts := decryptOpts{
					OutputStore: outputStore,
					InputStore:  inputStore,
					InputPath:   fileName,
					Cipher:      aes.NewCipher(),
					KeyServices: svcs,
					IgnoreMAC:   c.Bool("ignore-mac"),
				}

				output, err := decrypt(opts)
				if err != nil {
					return toExitError(err)
				}

				filename := c.String("filename")
				if filename == "" {
					filename = "tmp-file"
				}

				if err := exec.ExecWithFile(exec.ExecOpts{
					Command:    command,
					Plaintext:  output,
					Background: c.Bool("background"),
					Fifo:       !c.Bool("no-fifo"),
					User:       c.String("user"),
					Filename:   filename,
				}); err != nil {
					return toExitError(err)
				}

				return nil
			},
		},
		{
			Name:      "publish",
			Usage:     "Publish sops file or directory to a configured destination",
			ArgsUsage: `file`,
			Flags: append([]cli.Flag{
				cli.BoolFlag{
					Name:  "yes, y",
					Usage: `pre-approve all changes and run non-interactively`,
				},
				cli.BoolFlag{
					Name:  "omit-extensions",
					Usage: "Omit file extensions in destination path when publishing sops file to configured destinations",
				},
				cli.BoolFlag{
					Name:  "recursive",
					Usage: "If the source path is a directory, publish all its content recursively",
				},
				cli.BoolFlag{
					Name:  "verbose",
					Usage: "Enable verbose logging output",
				},
			}, keyserviceFlags...),
			Action: func(c *cli.Context) error {
				if c.Bool("verbose") || c.GlobalBool("verbose") {
					logging.SetLevel(logrus.DebugLevel)
				}
				configPath, err := config.FindConfigFile(".")
				if err != nil {
					return common.NewExitError(err, codes.ErrorGeneric)
				}
				if c.NArg() < 1 {
					return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
				}
				path := c.Args()[0]
				info, err := os.Stat(path)
				if err != nil {
					return err
				}
				if info.IsDir() && !c.Bool("recursive") {
					return fmt.Errorf("can't operate on a directory without --recursive flag.")
				}
				err = filepath.Walk(path, func(subPath string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if !info.IsDir() {
						err = publishcmd.Run(publishcmd.Opts{
							ConfigPath:     configPath,
							InputPath:      subPath,
							Cipher:         aes.NewCipher(),
							KeyServices:    keyservices(c),
							InputStore:     inputStore(c, subPath),
							Interactive:    !c.Bool("yes"),
							OmitExtensions: c.Bool("omit-extensions"),
							Recursive:      c.Bool("recursive"),
							RootPath:       path,
						})
						if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
							return cliErr
						} else if err != nil {
							return common.NewExitError(err, codes.ErrorGeneric)
						}
					}
					return nil
				})
				if err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:  "keyservice",
			Usage: "start a SOPS key service server",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "network, net",
					Usage: "network to listen on, e.g. 'tcp' or 'unix'",
					Value: "tcp",
				},
				cli.StringFlag{
					Name:  "address, addr",
					Usage: "address to listen on, e.g. '127.0.0.1:5000' or '/tmp/sops.sock'",
					Value: "127.0.0.1:5000",
				},
				cli.BoolFlag{
					Name:  "prompt",
					Usage: "Prompt user to confirm every incoming request",
				},
				cli.BoolFlag{
					Name:  "verbose",
					Usage: "Enable verbose logging output",
				},
			},
			Action: func(c *cli.Context) error {
				if c.Bool("verbose") || c.GlobalBool("verbose") {
					logging.SetLevel(logrus.DebugLevel)
				}
				err := keyservicecmd.Run(keyservicecmd.Opts{
					Network: c.String("network"),
					Address: c.String("address"),
					Prompt:  c.Bool("prompt"),
				})
				if err != nil {
					log.Errorf("Error running keyservice: %s", err)
					return err
				}
				return nil
			},
		},
		{
			Name:  "groups",
			Usage: "modify the groups on a SOPS file",
			Subcommands: []cli.Command{
				{
					Name:  "add",
					Usage: "add a new group to a SOPS file",
					Flags: append([]cli.Flag{
						cli.StringFlag{
							Name:  "file, f",
							Usage: "the file to add the group to",
						},
						cli.StringSliceFlag{
							Name:  "pgp",
							Usage: "the PGP fingerprints the new group should contain. Can be specified more than once",
						},
						cli.StringSliceFlag{
							Name:  "kms",
							Usage: "the KMS ARNs the new group should contain. Can be specified more than once",
						},
						cli.StringFlag{
							Name:  "aws-profile",
							Usage: "The AWS profile to use for requests to AWS",
						},
						cli.StringSliceFlag{
							Name:  "gcp-kms",
							Usage: "the GCP KMS Resource ID the new group should contain. Can be specified more than once",
						},
						cli.StringSliceFlag{
							Name:  "azure-kv",
							Usage: "the Azure Key Vault key URL the new group should contain. Can be specified more than once",
						},
						cli.StringSliceFlag{
							Name:  "hc-vault-transit",
							Usage: "the full vault path to the key used to encrypt/decrypt. Make you choose and configure a key with encrption/decryption enabled (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev'). Can be specified more than once",
						},
						cli.StringSliceFlag{
							Name:  "age",
							Usage: "the age recipient the new group should contain. Can be specified more than once",
						},
						cli.BoolFlag{
							Name:  "in-place, i",
							Usage: "write output back to the same file instead of stdout",
						},
						cli.IntFlag{
							Name:  "shamir-secret-sharing-threshold",
							Usage: "the number of master keys required to retrieve the data key with shamir",
						},
						cli.StringFlag{
							Name:  "encryption-context",
							Usage: "comma separated list of KMS encryption context key:value pairs",
						},
					}, keyserviceFlags...),
					Action: func(c *cli.Context) error {
						pgpFps := c.StringSlice("pgp")
						kmsArns := c.StringSlice("kms")
						gcpKmses := c.StringSlice("gcp-kms")
						vaultURIs := c.StringSlice("hc-vault-transit")
						azkvs := c.StringSlice("azure-kv")
						ageRecipients := c.StringSlice("age")
						var group sops.KeyGroup
						for _, fp := range pgpFps {
							group = append(group, pgp.NewMasterKeyFromFingerprint(fp))
						}
						for _, arn := range kmsArns {
							group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context")), c.String("aws-profile")))
						}
						for _, kms := range gcpKmses {
							group = append(group, gcpkms.NewMasterKeyFromResourceID(kms))
						}
						for _, uri := range vaultURIs {
							k, err := hcvault.NewMasterKeyFromURI(uri)
							if err != nil {
								log.WithError(err).Error("Failed to add key")
								continue
							}
							group = append(group, k)
						}
						for _, url := range azkvs {
							k, err := azkv.NewMasterKeyFromURL(url)
							if err != nil {
								log.WithError(err).Error("Failed to add key")
								continue
							}
							group = append(group, k)
						}
						for _, recipient := range ageRecipients {
							keys, err := age.MasterKeysFromRecipients(recipient)
							if err != nil {
								log.WithError(err).Error("Failed to add key")
								continue
							}
							for _, key := range keys {
								group = append(group, key)
							}
						}
						return groups.Add(groups.AddOpts{
							InputPath:      c.String("file"),
							InPlace:        c.Bool("in-place"),
							InputStore:     inputStore(c, c.String("file")),
							OutputStore:    outputStore(c, c.String("file")),
							Group:          group,
							GroupThreshold: c.Int("shamir-secret-sharing-threshold"),
							KeyServices:    keyservices(c),
						})
					},
				},
				{
					Name:  "delete",
					Usage: "delete a key group from a SOPS file",
					Flags: append([]cli.Flag{
						cli.StringFlag{
							Name:  "file, f",
							Usage: "the file to add the group to",
						},
						cli.BoolFlag{
							Name:  "in-place, i",
							Usage: "write output back to the same file instead of stdout",
						},
						cli.IntFlag{
							Name:  "shamir-secret-sharing-threshold",
							Usage: "the number of master keys required to retrieve the data key with shamir",
						},
					}, keyserviceFlags...),
					ArgsUsage: `[index]`,
					Action: func(c *cli.Context) error {
						group, err := strconv.ParseUint(c.Args().First(), 10, 32)
						if err != nil {
							return fmt.Errorf("failed to parse [index] argument: %s", err)
						}

						return groups.Delete(groups.DeleteOpts{
							InputPath:      c.String("file"),
							InPlace:        c.Bool("in-place"),
							InputStore:     inputStore(c, c.String("file")),
							OutputStore:    outputStore(c, c.String("file")),
							Group:          uint(group),
							GroupThreshold: c.Int("shamir-secret-sharing-threshold"),
							KeyServices:    keyservices(c),
						})
					},
				},
			},
		},
		{
			Name:      "updatekeys",
			Usage:     "update the keys of a SOPS file using the config file",
			ArgsUsage: `file`,
			Flags: append([]cli.Flag{
				cli.BoolFlag{
					Name:  "yes, y",
					Usage: `pre-approve all changes and run non-interactively`,
				},
			}, keyserviceFlags...),
			Action: func(c *cli.Context) error {
				var err error
				var configPath string
				if c.GlobalString("config") != "" {
					configPath = c.GlobalString("config")
				} else {
					configPath, err = config.FindConfigFile(".")
					if err != nil {
						return common.NewExitError(err, codes.ErrorGeneric)
					}
				}
				if c.NArg() < 1 {
					return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
				}
				err = updatekeys.UpdateKeys(updatekeys.Opts{
					InputPath:   c.Args()[0],
					GroupQuorum: c.Int("shamir-secret-sharing-quorum"),
					KeyServices: keyservices(c),
					Interactive: !c.Bool("yes"),
					ConfigPath:  configPath,
				})
				if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
					return cliErr
				} else if err != nil {
					return common.NewExitError(err, codes.ErrorGeneric)
				}
				return nil
			},
		},
	}
	app.Flags = append([]cli.Flag{
		cli.BoolFlag{
			Name:  "decrypt, d",
			Usage: "decrypt a file and output the result to stdout",
		},
		cli.BoolFlag{
			Name:  "encrypt, e",
			Usage: "encrypt a file and output the result to stdout",
		},
		cli.BoolFlag{
			Name:  "rotate, r",
			Usage: "generate a new data encryption key and reencrypt all values with the new key",
		},
		cli.StringFlag{
			Name:   "kms, k",
			Usage:  "comma separated list of KMS ARNs",
			EnvVar: "SOPS_KMS_ARN",
		},
		cli.StringFlag{
			Name:  "aws-profile",
			Usage: "The AWS profile to use for requests to AWS",
		},
		cli.StringFlag{
			Name:   "gcp-kms",
			Usage:  "comma separated list of GCP KMS resource IDs",
			EnvVar: "SOPS_GCP_KMS_IDS",
		},
		cli.StringFlag{
			Name:   "azure-kv",
			Usage:  "comma separated list of Azure Key Vault URLs",
			EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
		},
		cli.StringFlag{
			Name:   "hc-vault-transit",
			Usage:  "comma separated list of vault's key URI (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev')",
			EnvVar: "SOPS_VAULT_URIS",
		},
		cli.StringFlag{
			Name:   "pgp, p",
			Usage:  "comma separated list of PGP fingerprints",
			EnvVar: "SOPS_PGP_FP",
		},
		cli.StringFlag{
			Name:   "age, a",
			Usage:  "comma separated list of age recipients",
			EnvVar: "SOPS_AGE_RECIPIENTS",
		},
		cli.BoolFlag{
			Name:  "in-place, i",
			Usage: "write output back to the same file instead of stdout",
		},
		cli.StringFlag{
			Name:  "extract",
			Usage: "extract a specific key or branch from the input document. Decrypt mode only. Example: --extract '[\"somekey\"][0]'",
		},
		cli.StringFlag{
			Name:  "input-type",
			Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the file's extension to determine the type",
		},
		cli.StringFlag{
			Name:  "output-type",
			Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the input file's extension to determine the output format",
		},
		cli.BoolFlag{
			Name:  "show-master-keys, s",
			Usage: "display master encryption keys in the file during editing",
		},
		cli.StringFlag{
			Name:  "add-gcp-kms",
			Usage: "add the provided comma-separated list of GCP KMS key resource IDs to the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "rm-gcp-kms",
			Usage: "remove the provided comma-separated list of GCP KMS key resource IDs from the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "add-azure-kv",
			Usage: "add the provided comma-separated list of Azure Key Vault key URLs to the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "rm-azure-kv",
			Usage: "remove the provided comma-separated list of Azure Key Vault key URLs from the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "add-kms",
			Usage: "add the provided comma-separated list of KMS ARNs to the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "rm-kms",
			Usage: "remove the provided comma-separated list of KMS ARNs from the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "add-hc-vault-transit",
			Usage: "add the provided comma-separated list of Vault's URI key to the list of master keys on the given file ( eg. https://vault.example.org:8200/v1/transit/keys/dev)",
		},
		cli.StringFlag{
			Name:  "rm-hc-vault-transit",
			Usage: "remove the provided comma-separated list of Vault's URI key from the list of master keys on the given file ( eg. https://vault.example.org:8200/v1/transit/keys/dev)",
		},
		cli.StringFlag{
			Name:  "add-age",
			Usage: "add the provided comma-separated list of age recipients fingerprints to the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "rm-age",
			Usage: "remove the provided comma-separated list of age recipients from the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "add-pgp",
			Usage: "add the provided comma-separated list of PGP fingerprints to the list of master keys on the given file",
		},
		cli.StringFlag{
			Name:  "rm-pgp",
			Usage: "remove the provided comma-separated list of PGP fingerprints from the list of master keys on the given file",
		},
		cli.BoolFlag{
			Name:  "ignore-mac",
			Usage: "ignore Message Authentication Code during decryption",
		},
		cli.StringFlag{
			Name:  "unencrypted-suffix",
			Usage: "override the unencrypted key suffix.",
		},
		cli.StringFlag{
			Name:  "encrypted-suffix",
			Usage: "override the encrypted key suffix. When empty, all keys will be encrypted, unless otherwise marked with unencrypted-suffix.",
		},
		cli.StringFlag{
			Name:  "unencrypted-regex",
			Usage: "set the unencrypted key suffix. When specified, only keys matching the regex will be left unencrypted.",
		},
		cli.StringFlag{
			Name:  "encrypted-regex",
			Usage: "set the encrypted key suffix. When specified, only keys matching the regex will be encrypted.",
		},
		cli.StringFlag{
			Name:  "config",
			Usage: "path to sops' config file. If set, sops will not search for the config file recursively.",
		},
		cli.StringFlag{
			Name:  "encryption-context",
			Usage: "comma separated list of KMS encryption context key:value pairs",
		},
		cli.StringFlag{
			Name:  "set",
			Usage: `set a specific key or branch in the input document. value must be a json encoded string. (edit mode only). eg. --set '["somekey"][0] {"somevalue":true}'`,
		},
		cli.IntFlag{
			Name:  "shamir-secret-sharing-threshold",
			Usage: "the number of master keys required to retrieve the data key with shamir",
		},
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Enable verbose logging output",
		},
		cli.StringFlag{
			Name:  "output",
			Usage: "Save the output after encryption or decryption to the file specified",
		},
	}, keyserviceFlags...)

	app.Action = func(c *cli.Context) error {
		if c.Bool("verbose") {
			logging.SetLevel(logrus.DebugLevel)
		}
		if c.NArg() < 1 {
			return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
		}
		if c.Bool("in-place") && c.String("output") != "" {
			return common.NewExitError("Error: cannot operate on both --output and --in-place", codes.ErrorConflictingParameters)
		}
		fileName, err := filepath.Abs(c.Args()[0])
		if err != nil {
			return toExitError(err)
		}
		if _, err := os.Stat(fileName); os.IsNotExist(err) {
			if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
				c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
				return common.NewExitError("Error: cannot add or remove keys on non-existent files, use `--kms` and `--pgp` instead.", codes.CannotChangeKeysFromNonExistentFile)
			}
			if c.Bool("encrypt") || c.Bool("decrypt") || c.Bool("rotate") {
				return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
			}
		}

		unencryptedSuffix := c.String("unencrypted-suffix")
		encryptedSuffix := c.String("encrypted-suffix")
		encryptedRegex := c.String("encrypted-regex")
		unencryptedRegex := c.String("unencrypted-regex")
		conf, err := loadConfig(c, fileName, nil)
		if err != nil {
			return toExitError(err)
		}
		if conf != nil {
			// command line options have precedence
			if unencryptedSuffix == "" {
				unencryptedSuffix = conf.UnencryptedSuffix
			}
			if encryptedSuffix == "" {
				encryptedSuffix = conf.EncryptedSuffix
			}
			if encryptedRegex == "" {
				encryptedRegex = conf.EncryptedRegex
			}
			if unencryptedRegex == "" {
				unencryptedRegex = conf.UnencryptedRegex
			}
		}

		cryptRuleCount := 0
		if unencryptedSuffix != "" {
			cryptRuleCount++
		}
		if encryptedSuffix != "" {
			cryptRuleCount++
		}
		if encryptedRegex != "" {
			cryptRuleCount++
		}
		if unencryptedRegex != "" {
			cryptRuleCount++
		}

		if cryptRuleCount > 1 {
			return common.NewExitError("Error: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex or unencrypted_regex in the same file", codes.ErrorConflictingParameters)
		}

		// only supply the default UnencryptedSuffix when EncryptedSuffix and EncryptedRegex are not provided
		if cryptRuleCount == 0 {
			unencryptedSuffix = sops.DefaultUnencryptedSuffix
		}

		inputStore := inputStore(c, fileName)
		outputStore := outputStore(c, fileName)
		svcs := keyservices(c)

		var output []byte
		if c.Bool("encrypt") {
			var groups []sops.KeyGroup
			groups, err = keyGroups(c, fileName)
			if err != nil {
				return toExitError(err)
			}
			var threshold int
			threshold, err = shamirThreshold(c, fileName)
			if err != nil {
				return toExitError(err)
			}
			output, err = encrypt(encryptOpts{
				OutputStore:       outputStore,
				InputStore:        inputStore,
				InputPath:         fileName,
				Cipher:            aes.NewCipher(),
				UnencryptedSuffix: unencryptedSuffix,
				EncryptedSuffix:   encryptedSuffix,
				UnencryptedRegex:  unencryptedRegex,
				EncryptedRegex:    encryptedRegex,
				KeyServices:       svcs,
				KeyGroups:         groups,
				GroupThreshold:    threshold,
			})
		}

		if c.Bool("decrypt") {
			var extract []interface{}
			extract, err = parseTreePath(c.String("extract"))
			if err != nil {
				return common.NewExitError(fmt.Errorf("error parsing --extract path: %s", err), codes.InvalidTreePathFormat)
			}
			output, err = decrypt(decryptOpts{
				OutputStore: outputStore,
				InputStore:  inputStore,
				InputPath:   fileName,
				Cipher:      aes.NewCipher(),
				Extract:     extract,
				KeyServices: svcs,
				IgnoreMAC:   c.Bool("ignore-mac"),
			})
		}
		if c.Bool("rotate") {
			var addMasterKeys []keys.MasterKey
			kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
			for _, k := range kms.MasterKeysFromArnString(c.String("add-kms"), kmsEncryptionContext, c.String("aws-profile")) {
				addMasterKeys = append(addMasterKeys, k)
			}
			for _, k := range pgp.MasterKeysFromFingerprintString(c.String("add-pgp")) {
				addMasterKeys = append(addMasterKeys, k)
			}
			for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("add-gcp-kms")) {
				addMasterKeys = append(addMasterKeys, k)
			}
			azureKeys, err := azkv.MasterKeysFromURLs(c.String("add-azure-kv"))
			if err != nil {
				return err
			}
			for _, k := range azureKeys {
				addMasterKeys = append(addMasterKeys, k)
			}
			hcVaultKeys, err := hcvault.NewMasterKeysFromURIs(c.String("add-hc-vault-transit"))
			if err != nil {
				return err
			}
			for _, k := range hcVaultKeys {
				addMasterKeys = append(addMasterKeys, k)
			}
			ageKeys, err := age.MasterKeysFromRecipients(c.String("add-age"))
			if err != nil {
				return err
			}
			for _, k := range ageKeys {
				addMasterKeys = append(addMasterKeys, k)
			}

			var rmMasterKeys []keys.MasterKey
			for _, k := range kms.MasterKeysFromArnString(c.String("rm-kms"), kmsEncryptionContext, c.String("aws-profile")) {
				rmMasterKeys = append(rmMasterKeys, k)
			}
			for _, k := range pgp.MasterKeysFromFingerprintString(c.String("rm-pgp")) {
				rmMasterKeys = append(rmMasterKeys, k)
			}
			for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("rm-gcp-kms")) {
				rmMasterKeys = append(rmMasterKeys, k)
			}
			azureKeys, err = azkv.MasterKeysFromURLs(c.String("rm-azure-kv"))
			if err != nil {
				return err
			}
			for _, k := range azureKeys {
				rmMasterKeys = append(rmMasterKeys, k)
			}
			hcVaultKeys, err = hcvault.NewMasterKeysFromURIs(c.String("rm-hc-vault-transit"))
			if err != nil {
				return err
			}
			for _, k := range hcVaultKeys {
				rmMasterKeys = append(rmMasterKeys, k)
			}
			ageKeys, err = age.MasterKeysFromRecipients(c.String("rm-age"))
			if err != nil {
				return err
			}
			for _, k := range ageKeys {
				rmMasterKeys = append(rmMasterKeys, k)
			}

			output, err = rotate(rotateOpts{
				OutputStore:      outputStore,
				InputStore:       inputStore,
				InputPath:        fileName,
				Cipher:           aes.NewCipher(),
				KeyServices:      svcs,
				IgnoreMAC:        c.Bool("ignore-mac"),
				AddMasterKeys:    addMasterKeys,
				RemoveMasterKeys: rmMasterKeys,
			})
		}

		if c.String("set") != "" {
			var path []interface{}
			var value interface{}
			path, value, err = extractSetArguments(c.String("set"))
			if err != nil {
				return toExitError(err)
			}
			output, err = set(setOpts{
				OutputStore: outputStore,
				InputStore:  inputStore,
				InputPath:   fileName,
				Cipher:      aes.NewCipher(),
				KeyServices: svcs,
				IgnoreMAC:   c.Bool("ignore-mac"),
				Value:       value,
				TreePath:    path,
			})
		}

		isEditMode := !c.Bool("encrypt") && !c.Bool("decrypt") && !c.Bool("rotate") && c.String("set") == ""
		if isEditMode {
			_, statErr := os.Stat(fileName)
			fileExists := statErr == nil
			opts := editOpts{
				OutputStore:    outputStore,
				InputStore:     inputStore,
				InputPath:      fileName,
				Cipher:         aes.NewCipher(),
				KeyServices:    svcs,
				IgnoreMAC:      c.Bool("ignore-mac"),
				ShowMasterKeys: c.Bool("show-master-keys"),
			}
			if fileExists {
				output, err = edit(opts)
			} else {
				// File doesn't exist, edit the example file instead
				var groups []sops.KeyGroup
				groups, err = keyGroups(c, fileName)
				if err != nil {
					return toExitError(err)
				}
				var threshold int
				threshold, err = shamirThreshold(c, fileName)
				if err != nil {
					return toExitError(err)
				}
				output, err = editExample(editExampleOpts{
					editOpts:          opts,
					UnencryptedSuffix: unencryptedSuffix,
					EncryptedSuffix:   encryptedSuffix,
					UnencryptedRegex:  unencryptedRegex,
					EncryptedRegex:    encryptedRegex,
					KeyGroups:         groups,
					GroupThreshold:    threshold,
				})
			}
		}

		if err != nil {
			return toExitError(err)
		}

		// We open the file *after* the operations on the tree have been
		// executed to avoid truncating it when there's errors
		if c.Bool("in-place") || isEditMode || c.String("set") != "" {
			file, err := os.Create(fileName)
			if err != nil {
				return common.NewExitError(fmt.Sprintf("Could not open in-place file for writing: %s", err), codes.CouldNotWriteOutputFile)
			}
			defer file.Close()
			_, err = file.Write(output)
			if err != nil {
				return toExitError(err)
			}
			log.Info("File written successfully")
			return nil
		}

		outputFile := os.Stdout
		if c.String("output") != "" {
			file, err := os.Create(c.String("output"))
			if err != nil {
				return common.NewExitError(fmt.Sprintf("Could not open output file for writing: %s", err), codes.CouldNotWriteOutputFile)
			}
			defer file.Close()
			outputFile = file
		}
		_, err = outputFile.Write(output)
		return toExitError(err)
	}
	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func toExitError(err error) error {
	if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
		return cliErr
	} else if execErr, ok := err.(*osExec.ExitError); ok && execErr != nil {
		return cli.NewExitError(err, execErr.ExitCode())
	} else if err != nil {
		return cli.NewExitError(err, codes.ErrorGeneric)
	}
	return nil
}

func keyservices(c *cli.Context) (svcs []keyservice.KeyServiceClient) {
	if c.Bool("enable-local-keyservice") {
		svcs = append(svcs, keyservice.NewLocalClient())
	}
	uris := c.StringSlice("keyservice")
	for _, uri := range uris {
		url, err := url.Parse(uri)
		if err != nil {
			log.WithField("uri", uri).
				Warnf("Error parsing URI for keyservice, skipping")
			continue
		}
		addr := url.Host
		if url.Scheme == "unix" {
			addr = url.Path
		}
		opts := []grpc.DialOption{
			grpc.WithInsecure(),
			grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
				return net.DialTimeout(url.Scheme, addr, timeout)
			}),
		}
		log.WithField(
			"address",
			fmt.Sprintf("%s://%s", url.Scheme, addr),
		).Infof("Connecting to key service")
		conn, err := grpc.Dial(addr, opts...)
		if err != nil {
			log.Fatalf("failed to listen: %v", err)
		}
		svcs = append(svcs, keyservice.NewKeyServiceClient(conn))
	}
	return
}

func inputStore(context *cli.Context, path string) common.Store {
	return common.DefaultStoreForPathOrFormat(path, context.String("input-type"))
}

func outputStore(context *cli.Context, path string) common.Store {
	return common.DefaultStoreForPathOrFormat(path, context.String("output-type"))
}

func parseTreePath(arg string) ([]interface{}, error) {
	var path []interface{}
	components := strings.Split(arg, "[")
	for _, component := range components {
		if component == "" {
			continue
		}
		if component[len(component)-1] != ']' {
			return nil, fmt.Errorf("component %s doesn't end with ]", component)
		}
		component = component[:len(component)-1]
		if component[0] == byte('"') || component[0] == byte('\'') {
			// The component is a string
			component = component[1 : len(component)-1]
			path = append(path, component)
		} else {
			// The component must be a number
			i, err := strconv.Atoi(component)
			if err != nil {
				return nil, err
			}
			path = append(path, i)
		}
	}
	return path, nil
}

func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
	var kmsKeys []keys.MasterKey
	var pgpKeys []keys.MasterKey
	var cloudKmsKeys []keys.MasterKey
	var azkvKeys []keys.MasterKey
	var hcVaultMkKeys []keys.MasterKey
	var ageMasterKeys []keys.MasterKey
	kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
	if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
		return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat)
	}
	if c.String("kms") != "" {
		for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext, c.String("aws-profile")) {
			kmsKeys = append(kmsKeys, k)
		}
	}
	if c.String("gcp-kms") != "" {
		for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("gcp-kms")) {
			cloudKmsKeys = append(cloudKmsKeys, k)
		}
	}
	if c.String("azure-kv") != "" {
		azureKeys, err := azkv.MasterKeysFromURLs(c.String("azure-kv"))
		if err != nil {
			return nil, err
		}
		for _, k := range azureKeys {
			azkvKeys = append(azkvKeys, k)
		}
	}
	if c.String("hc-vault-transit") != "" {
		hcVaultKeys, err := hcvault.NewMasterKeysFromURIs(c.String("hc-vault-transit"))
		if err != nil {
			return nil, err
		}
		for _, k := range hcVaultKeys {
			hcVaultMkKeys = append(hcVaultMkKeys, k)
		}
	}
	if c.String("pgp") != "" {
		for _, k := range pgp.MasterKeysFromFingerprintString(c.String("pgp")) {
			pgpKeys = append(pgpKeys, k)
		}
	}
	if c.String("age") != "" {
		ageKeys, err := age.MasterKeysFromRecipients(c.String("age"))
		if err != nil {
			return nil, err
		}
		for _, k := range ageKeys {
			ageMasterKeys = append(ageMasterKeys, k)
		}
	}
	if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
		conf, err := loadConfig(c, file, kmsEncryptionContext)
		// config file might just not be supplied, without any error
		if conf == nil {
			errMsg := "config file not found and no keys provided through command line options"
			if err != nil {
				errMsg = fmt.Sprintf("%s: %s", errMsg, err)
			}
			return nil, fmt.Errorf(errMsg)
		}
		return conf.KeyGroups, err
	}
	var group sops.KeyGroup
	group = append(group, kmsKeys...)
	group = append(group, cloudKmsKeys...)
	group = append(group, azkvKeys...)
	group = append(group, pgpKeys...)
	group = append(group, hcVaultMkKeys...)
	group = append(group, ageMasterKeys...)
	log.Debugf("Master keys available:  %+v", group)
	return []sops.KeyGroup{group}, nil
}

// loadConfig will look for an existing config file, either provided through the command line, or using config.FindConfigFile.
// Since a config file is not required, this function does not error when one is not found, and instead returns a nil config pointer
func loadConfig(c *cli.Context, file string, kmsEncryptionContext map[string]*string) (*config.Config, error) {
	var err error
	var configPath string
	if c.String("config") != "" {
		configPath = c.String("config")
	} else {
		// Ignore config not found errors returned from FindConfigFile since the config file is not mandatory
		configPath, err = config.FindConfigFile(".")
		if err != nil {
			// If we can't find a config file, but we were not explicitly requested to, assume it does not exist
			return nil, nil
		}
	}
	conf, err := config.LoadCreationRuleForFile(configPath, file, kmsEncryptionContext)
	if err != nil {
		return nil, err
	}
	return conf, nil
}

func shamirThreshold(c *cli.Context, file string) (int, error) {
	if c.Int("shamir-secret-sharing-threshold") != 0 {
		return c.Int("shamir-secret-sharing-threshold"), nil
	}
	conf, err := loadConfig(c, file, nil)
	if conf == nil {
		// This takes care of the following two case:
		// 1. No config was provided. Err will be nil and ShamirThreshold will be the default value of 0.
		// 2. We did find a config file, but failed to load it. In that case the calling function will print the error and exit.
		return 0, err
	}
	return conf.ShamirThreshold, nil
}

func jsonValueToTreeInsertableValue(jsonValue string) (interface{}, error) {
	var valueToInsert interface{}
	err := encodingjson.Unmarshal([]byte(jsonValue), &valueToInsert)
	if err != nil {
		return nil, common.NewExitError("Value for --set is not valid JSON", codes.ErrorInvalidSetFormat)
	}
	// Check if decoding it as json we find a single value
	// and not a map or slice, in which case we can't marshal
	// it to a sops.TreeBranch
	kind := reflect.ValueOf(valueToInsert).Kind()
	if kind == reflect.Map || kind == reflect.Slice {
		var err error
		valueToInsert, err = (&json.Store{}).LoadPlainFile([]byte(jsonValue))
		if err != nil {
			return nil, common.NewExitError("Invalid --set value format", codes.ErrorInvalidSetFormat)
		}
	}
	// Fix for #461
	// Attempt conversion to TreeBranches to handle yaml multidoc. If conversion fails it's
	// most likely a string value, so just return it as-is.
	values, ok := valueToInsert.(sops.TreeBranches)
	if !ok {
		return valueToInsert, nil
	}
	return values[0], nil
}

func extractSetArguments(set string) (path []interface{}, valueToInsert interface{}, err error) {
	// Set is a string with the format "python-dict-index json-value"
	// Since python-dict-index has to end with ], we split at "] " to get the two parts
	pathValuePair := strings.SplitAfterN(set, "] ", 2)
	if len(pathValuePair) < 2 {
		return nil, nil, common.NewExitError("Invalid --set format", codes.ErrorInvalidSetFormat)
	}
	fullPath := strings.TrimRight(pathValuePair[0], " ")
	jsonValue := pathValuePair[1]
	valueToInsert, err = jsonValueToTreeInsertableValue(jsonValue)

	path, err = parseTreePath(fullPath)
	if err != nil {
		return nil, nil, common.NewExitError("Invalid --set format", codes.ErrorInvalidSetFormat)
	}
	return path, valueToInsert, nil
}
0707010000002E000081A400000000000000000000000162794F9300000897000000000000000000000000000000000000001E00000000sops-3.7.3/cmd/sops/rotate.gopackage main

import (
	"fmt"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/audit"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keys"
	"go.mozilla.org/sops/v3/keyservice"
)

type rotateOpts struct {
	Cipher           sops.Cipher
	InputStore       sops.Store
	OutputStore      sops.Store
	InputPath        string
	IgnoreMAC        bool
	AddMasterKeys    []keys.MasterKey
	RemoveMasterKeys []keys.MasterKey
	KeyServices      []keyservice.KeyServiceClient
}

func rotate(opts rotateOpts) ([]byte, error) {
	tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
		Cipher:      opts.Cipher,
		InputStore:  opts.InputStore,
		InputPath:   opts.InputPath,
		IgnoreMAC:   opts.IgnoreMAC,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	audit.SubmitEvent(audit.RotateEvent{
		File: tree.FilePath,
	})

	_, err = common.DecryptTree(common.DecryptTreeOpts{
		Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	// Add new master keys
	for _, key := range opts.AddMasterKeys {
		tree.Metadata.KeyGroups[0] = append(tree.Metadata.KeyGroups[0], key)
	}
	// Remove master keys
	for _, rmKey := range opts.RemoveMasterKeys {
		for i := range tree.Metadata.KeyGroups {
			for j, groupKey := range tree.Metadata.KeyGroups[i] {
				if rmKey.ToString() == groupKey.ToString() {
					tree.Metadata.KeyGroups[i] = append(tree.Metadata.KeyGroups[i][:j], tree.Metadata.KeyGroups[i][j+1:]...)
				}
			}
		}
	}

	// Create a new data key
	dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
	if len(errs) > 0 {
		err = fmt.Errorf("Could not generate data key: %s", errs)
		return nil, err
	}

	// Reencrypt the file with the new key
	err = common.EncryptTree(common.EncryptTreeOpts{
		DataKey: dataKey, Tree: tree, Cipher: opts.Cipher,
	})
	if err != nil {
		return nil, err
	}

	encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}
	return encryptedFile, nil
}
0707010000002F000081A400000000000000000000000162794F9300000602000000000000000000000000000000000000001B00000000sops-3.7.3/cmd/sops/set.gopackage main

import (
	"fmt"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keyservice"
)

type setOpts struct {
	Cipher      sops.Cipher
	InputStore  sops.Store
	OutputStore sops.Store
	InputPath   string
	IgnoreMAC   bool
	TreePath    []interface{}
	Value       interface{}
	KeyServices []keyservice.KeyServiceClient
}

func set(opts setOpts) ([]byte, error) {
	// Load the file
	// TODO: Issue #173: if the file does not exist, create it with the contents passed in as opts.Value
	tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
		Cipher:      opts.Cipher,
		InputStore:  opts.InputStore,
		InputPath:   opts.InputPath,
		IgnoreMAC:   opts.IgnoreMAC,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	// Decrypt the file
	dataKey, err := common.DecryptTree(common.DecryptTreeOpts{
		Cipher:      opts.Cipher,
		IgnoreMac:   opts.IgnoreMAC,
		Tree:        tree,
		KeyServices: opts.KeyServices,
	})
	if err != nil {
		return nil, err
	}

	// Set the value
	tree.Branches[0] = tree.Branches[0].Set(opts.TreePath, opts.Value)

	err = common.EncryptTree(common.EncryptTreeOpts{
		DataKey: dataKey, Tree: tree, Cipher: opts.Cipher,
	})
	if err != nil {
		return nil, err
	}

	encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree)
	if err != nil {
		return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}
	return encryptedFile, err
}
07070100000030000041ED00000000000000000000000762794F9300000000000000000000000000000000000000000000001F00000000sops-3.7.3/cmd/sops/subcommand07070100000031000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002400000000sops-3.7.3/cmd/sops/subcommand/exec07070100000032000081A400000000000000000000000162794F9300000795000000000000000000000000000000000000002C00000000sops-3.7.3/cmd/sops/subcommand/exec/exec.gopackage exec

import (
	"bytes"
	"io/ioutil"
	"os"
	"runtime"
	"strings"

	"go.mozilla.org/sops/v3/logging"

	"github.com/sirupsen/logrus"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("EXEC")
}

type ExecOpts struct {
	Command    string
	Plaintext  []byte
	Background bool
	Fifo       bool
	User       string
	Filename   string
}

func GetFile(dir, filename string) *os.File {
	handle, err := ioutil.TempFile(dir, filename)
	if err != nil {
		log.Fatal(err)
	}
	return handle
}

func ExecWithFile(opts ExecOpts) error {
	if opts.User != "" {
		SwitchUser(opts.User)
	}

	if runtime.GOOS == "windows" && opts.Fifo {
		log.Warn("no fifos on windows, use --no-fifo next time")
		opts.Fifo = false
	}

	dir, err := ioutil.TempDir("", ".sops")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)

	var filename string
	if opts.Fifo {
		// fifo handling needs to be async, even opening to write
		// will block if there is no reader present
		filename = GetPipe(dir, opts.Filename)
		go WritePipe(filename, opts.Plaintext)
	} else {
		handle := GetFile(dir, opts.Filename)
		handle.Write(opts.Plaintext)
		handle.Close()
		filename = handle.Name()
	}

	placeholdered := strings.Replace(opts.Command, "{}", filename, -1)
	cmd := BuildCommand(placeholdered)
	cmd.Env = os.Environ()

	if opts.Background {
		return cmd.Start()
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

func ExecWithEnv(opts ExecOpts) error {
	if opts.User != "" {
		SwitchUser(opts.User)
	}

	env := os.Environ()
	lines := bytes.Split(opts.Plaintext, []byte("\n"))
	for _, line := range lines {
		if len(line) == 0 {
			continue
		}
		if line[0] == '#' {
			continue
		}
		env = append(env, string(line))
	}

	cmd := BuildCommand(opts.Command)
	cmd.Env = env

	if opts.Background {
		return cmd.Start()
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}
07070100000033000081A400000000000000000000000162794F93000003EA000000000000000000000000000000000000003100000000sops-3.7.3/cmd/sops/subcommand/exec/exec_unix.go// +build !windows

package exec

import (
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"syscall"
)

func BuildCommand(command string) *exec.Cmd {
	return exec.Command("/bin/sh", "-c", command)
}

func WritePipe(pipe string, contents []byte) {
	handle, err := os.OpenFile(pipe, os.O_WRONLY, 0600)

	if err != nil {
		os.Remove(pipe)
		log.Fatal(err)
	}

	handle.Write(contents)
	handle.Close()
}

func GetPipe(dir, filename string) string {
	tmpfn := filepath.Join(dir, filename)
	err := syscall.Mkfifo(tmpfn, 0600)
	if err != nil {
		log.Fatal(err)
	}

	return tmpfn
}

func SwitchUser(username string) {
	user, err := user.Lookup(username)
	if err != nil {
		log.Fatal(err)
	}

	uid, _ := strconv.Atoi(user.Uid)

	err = syscall.Setgid(uid)
	if err != nil {
		log.Fatal(err)
	}

	err = syscall.Setuid(uid)
	if err != nil {
		log.Fatal(err)
	}

	err = syscall.Setreuid(uid, uid)
	if err != nil {
		log.Fatal(err)
	}

	err = syscall.Setregid(uid, uid)
	if err != nil {
		log.Fatal(err)
	}
}
07070100000034000081A400000000000000000000000162794F93000001AE000000000000000000000000000000000000003400000000sops-3.7.3/cmd/sops/subcommand/exec/exec_windows.gopackage exec

import (
	"os/exec"
)

func BuildCommand(command string) *exec.Cmd {
	return exec.Command("cmd.exe", "/C", command)
}

func WritePipe(pipe string, contents []byte) {
	log.Fatal("fifos are not available on windows")
}

func GetPipe(dir, filename string) string {
	log.Fatal("fifos are not available on windows")
	return ""
}

func SwitchUser(username string) {
	log.Fatal("user switching not available on windows")
}
07070100000035000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002600000000sops-3.7.3/cmd/sops/subcommand/groups07070100000036000081A400000000000000000000000162794F93000004DF000000000000000000000000000000000000002D00000000sops-3.7.3/cmd/sops/subcommand/groups/add.gopackage groups

import (
	"os"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keyservice"
)

// AddOpts are the options for adding a key group to a SOPS file
type AddOpts struct {
	InputPath      string
	InputStore     sops.Store
	OutputStore    sops.Store
	Group          sops.KeyGroup
	GroupThreshold int
	InPlace        bool
	KeyServices    []keyservice.KeyServiceClient
}

// Add adds a key group to a SOPS file
func Add(opts AddOpts) error {
	tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath)
	if err != nil {
		return err
	}
	dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
	if err != nil {
		return err
	}
	tree.Metadata.KeyGroups = append(tree.Metadata.KeyGroups, opts.Group)

	if opts.GroupThreshold != 0 {
		tree.Metadata.ShamirThreshold = opts.GroupThreshold
	}
	tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices)
	output, err := opts.OutputStore.EmitEncryptedFile(*tree)
	if err != nil {
		return err
	}
	var outputFile = os.Stdout
	if opts.InPlace {
		var err error
		outputFile, err = os.Create(opts.InputPath)
		if err != nil {
			return err
		}
		defer outputFile.Close()
	}
	outputFile.Write(output)
	return nil
}
07070100000037000081A400000000000000000000000162794F930000064D000000000000000000000000000000000000003000000000sops-3.7.3/cmd/sops/subcommand/groups/delete.gopackage groups

import (
	"os"

	"fmt"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/keyservice"
)

// DeleteOpts are the options for deleting a key group from a SOPS file
type DeleteOpts struct {
	InputPath      string
	InputStore     sops.Store
	OutputStore    sops.Store
	Group          uint
	GroupThreshold int
	InPlace        bool
	KeyServices    []keyservice.KeyServiceClient
}

// Delete deletes a key group from a SOPS file
func Delete(opts DeleteOpts) error {
	tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath)
	if err != nil {
		return err
	}
	dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
	if err != nil {
		return err
	}
	tree.Metadata.KeyGroups = append(tree.Metadata.KeyGroups[:opts.Group], tree.Metadata.KeyGroups[opts.Group+1:]...)

	if opts.GroupThreshold != 0 {
		tree.Metadata.ShamirThreshold = opts.GroupThreshold
	}

	if len(tree.Metadata.KeyGroups) < tree.Metadata.ShamirThreshold {
		return fmt.Errorf("removing this key group will make the Shamir threshold impossible to satisfy: "+
			"Shamir threshold is %d, but we only have %d key groups", tree.Metadata.ShamirThreshold,
			len(tree.Metadata.KeyGroups))
	}

	tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices)
	output, err := opts.OutputStore.EmitEncryptedFile(*tree)
	if err != nil {
		return err
	}
	var outputFile = os.Stdout
	if opts.InPlace {
		var err error
		outputFile, err = os.Create(opts.InputPath)
		if err != nil {
			return err
		}
		defer outputFile.Close()
	}
	outputFile.Write(output)
	return nil
}
07070100000038000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002A00000000sops-3.7.3/cmd/sops/subcommand/keyservice07070100000039000081A400000000000000000000000162794F930000041E000000000000000000000000000000000000003800000000sops-3.7.3/cmd/sops/subcommand/keyservice/keyservice.gopackage keyservice

import (
	"net"
	"os"
	"os/signal"
	"syscall"

	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/logging"

	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("KEYSERVICE")
}

// Opts are the options the key service server can take
type Opts struct {
	Network string
	Address string
	Prompt  bool
}

// Run runs a SOPS key service server
func Run(opts Opts) error {
	lis, err := net.Listen(opts.Network, opts.Address)
	if err != nil {
		return err
	}
	defer lis.Close()
	grpcServer := grpc.NewServer()
	keyservice.RegisterKeyServiceServer(grpcServer, keyservice.Server{
		Prompt: opts.Prompt,
	})
	log.Infof("Listening on %s://%s", opts.Network, opts.Address)

	// Close socket if we get killed
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func(c chan os.Signal) {
		sig := <-c
		log.Infof("Caught signal %s: shutting down.", sig)
		lis.Close()
		os.Exit(0)
	}(sigc)
	return grpcServer.Serve(lis)
}
0707010000003A000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002700000000sops-3.7.3/cmd/sops/subcommand/publish0707010000003B000081A400000000000000000000000162794F93000011C5000000000000000000000000000000000000003200000000sops-3.7.3/cmd/sops/subcommand/publish/publish.gopackage publish

import (
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/config"
	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/logging"
	"go.mozilla.org/sops/v3/publish"
	"go.mozilla.org/sops/v3/version"

	"github.com/sirupsen/logrus"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("PUBLISH")
}

// Opts represents publish options and config
type Opts struct {
	Interactive    bool
	Cipher         sops.Cipher
	ConfigPath     string
	InputPath      string
	KeyServices    []keyservice.KeyServiceClient
	InputStore     sops.Store
	OmitExtensions bool
	Recursive      bool
	RootPath       string
}

// Run publish operation
func Run(opts Opts) error {
	var fileContents []byte
	path, err := filepath.Abs(opts.InputPath)
	if err != nil {
		return err
	}

	conf, err := config.LoadDestinationRuleForFile(opts.ConfigPath, opts.InputPath, make(map[string]*string))
	if err != nil {
		return err
	}
	if conf.Destination == nil {
		return errors.New("no destination configured for this file")
	}

	var destinationPath string
	if opts.Recursive {
		destinationPath, err = filepath.Rel(opts.RootPath, opts.InputPath)
		if err != nil {
			return err
		}
	} else {
		_, destinationPath = filepath.Split(path)
	}
	if opts.OmitExtensions || conf.OmitExtensions {
		destinationPath = strings.TrimSuffix(destinationPath, filepath.Ext(path))
	}

	// Check that this is a sops-encrypted file
	tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath)
	if err != nil {
		return err
	}

	data := map[string]interface{}{}

	switch conf.Destination.(type) {
	case *publish.S3Destination, *publish.GCSDestination:
		// Re-encrypt if settings exist to do so
		if len(conf.KeyGroups[0]) != 0 {
			log.Debug("Re-encrypting tree before publishing")
			_, err = common.DecryptTree(common.DecryptTreeOpts{
				Cipher:      opts.Cipher,
				IgnoreMac:   false,
				Tree:        tree,
				KeyServices: opts.KeyServices,
			})
			if err != nil {
				return err
			}

			diffs := common.DiffKeyGroups(tree.Metadata.KeyGroups, conf.KeyGroups)
			keysWillChange := false
			for _, diff := range diffs {
				if len(diff.Added) > 0 || len(diff.Removed) > 0 {
					keysWillChange = true
				}
			}
			if keysWillChange {
				fmt.Printf("The following changes will be made to the file's key groups:\n")
				common.PrettyPrintDiffs(diffs)
			}

			tree.Metadata = sops.Metadata{
				KeyGroups:         conf.KeyGroups,
				UnencryptedSuffix: conf.UnencryptedSuffix,
				EncryptedSuffix:   conf.EncryptedSuffix,
				Version:           version.Version,
				ShamirThreshold:   conf.ShamirThreshold,
			}

			dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
			if len(errs) > 0 {
				err = fmt.Errorf("Could not generate data key: %s", errs)
				return err
			}

			err = common.EncryptTree(common.EncryptTreeOpts{
				DataKey: dataKey,
				Tree:    tree,
				Cipher:  opts.Cipher,
			})
			if err != nil {
				return err
			}

			fileContents, err = opts.InputStore.EmitEncryptedFile(*tree)
			if err != nil {
				return common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
			}
		} else {
			fileContents, err = ioutil.ReadFile(path)
			if err != nil {
				return fmt.Errorf("could not read file: %s", err)
			}
		}
	case *publish.VaultDestination:
		_, err = common.DecryptTree(common.DecryptTreeOpts{
			Cipher:      opts.Cipher,
			IgnoreMac:   false,
			Tree:        tree,
			KeyServices: opts.KeyServices,
		})
		if err != nil {
			return err
		}
		data, err = sops.EmitAsMap(tree.Branches)
		if err != nil {
			return err
		}
	}

	if opts.Interactive {
		var response string
		for response != "y" && response != "n" {
			fmt.Printf("uploading %s to %s ? (y/n): ", path, conf.Destination.Path(destinationPath))
			_, err := fmt.Scanln(&response)
			if err != nil {
				return err
			}
		}
		if response == "n" {
			msg := fmt.Sprintf("Publication of %s canceled", path)
			if opts.Recursive {
				fmt.Println(msg)
				return nil
			} else {
				return errors.New(msg)
			}
		}
	}

	switch dest := conf.Destination.(type) {
	case *publish.S3Destination, *publish.GCSDestination:
		err = dest.Upload(fileContents, destinationPath)
	case *publish.VaultDestination:
		err = dest.UploadUnencrypted(data, destinationPath)
	}

	if err != nil {
		return err
	}

	return nil
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
0707010000003C000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002A00000000sops-3.7.3/cmd/sops/subcommand/updatekeys0707010000003D000081A400000000000000000000000162794F9300000B9C000000000000000000000000000000000000003800000000sops-3.7.3/cmd/sops/subcommand/updatekeys/updatekeys.gopackage updatekeys

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"go.mozilla.org/sops/v3/cmd/sops/codes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	"go.mozilla.org/sops/v3/config"
	"go.mozilla.org/sops/v3/keyservice"
)

// Opts represents key operation options and config
type Opts struct {
	InputPath   string
	GroupQuorum int
	KeyServices []keyservice.KeyServiceClient
	Interactive bool
	ConfigPath  string
}

// UpdateKeys update the keys for a given file
func UpdateKeys(opts Opts) error {
	path, err := filepath.Abs(opts.InputPath)
	if err != nil {
		return err
	}
	info, err := os.Stat(path)
	if err != nil {
		return err
	}
	if info.IsDir() {
		return fmt.Errorf("can't operate on a directory")
	}
	opts.InputPath = path
	return updateFile(opts)
}

func updateFile(opts Opts) error {
	store := common.DefaultStoreForPath(opts.InputPath)
	log.Printf("Syncing keys for file %s", opts.InputPath)
	tree, err := common.LoadEncryptedFile(store, opts.InputPath)
	if err != nil {
		return err
	}
	conf, err := config.LoadCreationRuleForFile(opts.ConfigPath, opts.InputPath, make(map[string]*string))
	if err != nil {
		return err
	}

	diffs := common.DiffKeyGroups(tree.Metadata.KeyGroups, conf.KeyGroups)
	keysWillChange := false
	for _, diff := range diffs {
		if len(diff.Added) > 0 || len(diff.Removed) > 0 {
			keysWillChange = true
		}
	}
	if !keysWillChange {
		log.Printf("File %s already up to date", opts.InputPath)
		return nil
	}
	fmt.Printf("The following changes will be made to the file's groups:\n")
	common.PrettyPrintDiffs(diffs)

	if opts.Interactive {
		var response string
		for response != "y" && response != "n" {
			fmt.Printf("Is this okay? (y/n):")
			_, err = fmt.Scanln(&response)
			if err != nil {
				return err
			}
		}
		if response == "n" {
			log.Printf("File %s left unchanged", opts.InputPath)
			return nil
		}
	}
	key, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
	if err != nil {
		return common.NewExitError(err, codes.CouldNotRetrieveKey)
	}
	tree.Metadata.KeyGroups = conf.KeyGroups
	if opts.GroupQuorum != 0 {
		tree.Metadata.ShamirThreshold = opts.GroupQuorum
	}
	tree.Metadata.ShamirThreshold = min(tree.Metadata.ShamirThreshold, len(tree.Metadata.KeyGroups))
	errs := tree.Metadata.UpdateMasterKeysWithKeyServices(key, opts.KeyServices)
	if len(errs) > 0 {
		return fmt.Errorf("error updating one or more master keys: %s", errs)
	}
	output, err := store.EmitEncryptedFile(*tree)
	if err != nil {
		return common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
	}
	outputFile, err := os.Create(opts.InputPath)
	if err != nil {
		return fmt.Errorf("could not open file for writing: %s", err)
	}
	defer outputFile.Close()
	_, err = outputFile.Write(output)
	if err != nil {
		return fmt.Errorf("error writing to file: %s", err)
	}
	log.Printf("File %s synced with new keys", opts.InputPath)
	return nil
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
0707010000003E000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/config0707010000003F000081A400000000000000000000000162794F9300002B3E000000000000000000000000000000000000001C00000000sops-3.7.3/config/config.go/*
Package config provides a way to find and load SOPS configuration files
*/
package config //import "go.mozilla.org/sops/v3/config"

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/age"
	"go.mozilla.org/sops/v3/azkv"
	"go.mozilla.org/sops/v3/gcpkms"
	"go.mozilla.org/sops/v3/hcvault"
	"go.mozilla.org/sops/v3/kms"
	"go.mozilla.org/sops/v3/logging"
	"go.mozilla.org/sops/v3/pgp"
	"go.mozilla.org/sops/v3/publish"
	"gopkg.in/yaml.v3"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("CONFIG")
}

type fileSystem interface {
	Stat(name string) (os.FileInfo, error)
}

type osFS struct {
	stat func(string) (os.FileInfo, error)
}

func (fs osFS) Stat(name string) (os.FileInfo, error) {
	return fs.stat(name)
}

var fs fileSystem = osFS{stat: os.Stat}

const (
	maxDepth       = 100
	configFileName = ".sops.yaml"
)

// FindConfigFile looks for a sops config file in the current working directory and on parent directories, up to the limit defined by the maxDepth constant.
func FindConfigFile(start string) (string, error) {
	filepath := path.Dir(start)
	for i := 0; i < maxDepth; i++ {
		_, err := fs.Stat(path.Join(filepath, configFileName))
		if err != nil {
			filepath = path.Join(filepath, "..")
		} else {
			return path.Join(filepath, configFileName), nil
		}
	}
	return "", fmt.Errorf("Config file not found")
}

type configFile struct {
	CreationRules    []creationRule    `yaml:"creation_rules"`
	DestinationRules []destinationRule `yaml:"destination_rules"`
}

type keyGroup struct {
	KMS     []kmsKey
	GCPKMS  []gcpKmsKey  `yaml:"gcp_kms"`
	AzureKV []azureKVKey `yaml:"azure_keyvault"`
	Vault   []string     `yaml:"hc_vault"`
	Age     []string     `yaml:"age"`
	PGP     []string
}

type gcpKmsKey struct {
	ResourceID string `yaml:"resource_id"`
}

type kmsKey struct {
	Arn        string             `yaml:"arn"`
	Role       string             `yaml:"role,omitempty"`
	Context    map[string]*string `yaml:"context"`
	AwsProfile string             `yaml:"aws_profile"`
}

type azureKVKey struct {
	VaultURL string `yaml:"vaultUrl"`
	Key      string `yaml:"key"`
	Version  string `yaml:"version"`
}

type destinationRule struct {
	PathRegex        string       `yaml:"path_regex"`
	S3Bucket         string       `yaml:"s3_bucket"`
	S3Prefix         string       `yaml:"s3_prefix"`
	GCSBucket        string       `yaml:"gcs_bucket"`
	GCSPrefix        string       `yaml:"gcs_prefix"`
	VaultPath        string       `yaml:"vault_path"`
	VaultAddress     string       `yaml:"vault_address"`
	VaultKVMountName string       `yaml:"vault_kv_mount_name"`
	VaultKVVersion   int          `yaml:"vault_kv_version"`
	RecreationRule   creationRule `yaml:"recreation_rule,omitempty"`
	OmitExtensions   bool         `yaml:"omit_extensions"`
}

type creationRule struct {
	PathRegex         string `yaml:"path_regex"`
	KMS               string
	AwsProfile        string `yaml:"aws_profile"`
	Age               string `yaml:"age"`
	PGP               string
	GCPKMS            string     `yaml:"gcp_kms"`
	AzureKeyVault     string     `yaml:"azure_keyvault"`
	VaultURI          string     `yaml:"hc_vault_transit_uri"`
	KeyGroups         []keyGroup `yaml:"key_groups"`
	ShamirThreshold   int        `yaml:"shamir_threshold"`
	UnencryptedSuffix string     `yaml:"unencrypted_suffix"`
	EncryptedSuffix   string     `yaml:"encrypted_suffix"`
	UnencryptedRegex  string     `yaml:"unencrypted_regex"`
	EncryptedRegex    string     `yaml:"encrypted_regex"`
}

// Load loads a sops config file into a temporary struct
func (f *configFile) load(bytes []byte) error {
	err := yaml.Unmarshal(bytes, f)
	if err != nil {
		return fmt.Errorf("Could not unmarshal config file: %s", err)
	}
	return nil
}

// Config is the configuration for a given SOPS file
type Config struct {
	KeyGroups         []sops.KeyGroup
	ShamirThreshold   int
	UnencryptedSuffix string
	EncryptedSuffix   string
	UnencryptedRegex  string
	EncryptedRegex    string
	Destination       publish.Destination
	OmitExtensions    bool
}

func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[string]*string) ([]sops.KeyGroup, error) {
	var groups []sops.KeyGroup
	if len(cRule.KeyGroups) > 0 {
		for _, group := range cRule.KeyGroups {
			var keyGroup sops.KeyGroup
			for _, k := range group.Age {
				keys, err := age.MasterKeysFromRecipients(k)
				if err != nil {
					return nil, err
				}
				for _, key := range keys {
					keyGroup = append(keyGroup, key)
				}
			}
			for _, k := range group.PGP {
				keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k))
			}
			for _, k := range group.KMS {
				keyGroup = append(keyGroup, kms.NewMasterKey(k.Arn, k.Role, k.Context))
			}
			for _, k := range group.GCPKMS {
				keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID))
			}
			for _, k := range group.AzureKV {
				keyGroup = append(keyGroup, azkv.NewMasterKey(k.VaultURL, k.Key, k.Version))
			}
			for _, k := range group.Vault {
				if masterKey, err := hcvault.NewMasterKeyFromURI(k); err == nil {
					keyGroup = append(keyGroup, masterKey)
				} else {
					return nil, err
				}
			}
			groups = append(groups, keyGroup)
		}
	} else {
		var keyGroup sops.KeyGroup
		if cRule.Age != "" {
			ageKeys, err := age.MasterKeysFromRecipients(cRule.Age)
			if err != nil {
				return nil, err
			} else {
				for _, ak := range ageKeys {
					keyGroup = append(keyGroup, ak)
				}
			}
		}
		for _, k := range pgp.MasterKeysFromFingerprintString(cRule.PGP) {
			keyGroup = append(keyGroup, k)
		}
		for _, k := range kms.MasterKeysFromArnString(cRule.KMS, kmsEncryptionContext, cRule.AwsProfile) {
			keyGroup = append(keyGroup, k)
		}
		for _, k := range gcpkms.MasterKeysFromResourceIDString(cRule.GCPKMS) {
			keyGroup = append(keyGroup, k)
		}
		azureKeys, err := azkv.MasterKeysFromURLs(cRule.AzureKeyVault)
		if err != nil {
			return nil, err
		}
		for _, k := range azureKeys {
			keyGroup = append(keyGroup, k)
		}
		vaultKeys, err := hcvault.NewMasterKeysFromURIs(cRule.VaultURI)
		if err != nil {
			return nil, err
		}
		for _, k := range vaultKeys {
			keyGroup = append(keyGroup, k)
		}
		groups = append(groups, keyGroup)
	}
	return groups, nil
}

func loadConfigFile(confPath string) (*configFile, error) {
	confBytes, err := ioutil.ReadFile(confPath)
	if err != nil {
		return nil, fmt.Errorf("could not read config file: %s", err)
	}
	conf := &configFile{}
	err = conf.load(confBytes)
	if err != nil {
		return nil, fmt.Errorf("error loading config: %s", err)
	}
	return conf, nil
}

func configFromRule(rule *creationRule, kmsEncryptionContext map[string]*string) (*Config, error) {
	cryptRuleCount := 0
	if rule.UnencryptedSuffix != "" {
		cryptRuleCount++
	}
	if rule.EncryptedSuffix != "" {
		cryptRuleCount++
	}
	if rule.EncryptedRegex != "" {
		cryptRuleCount++
	}

	if cryptRuleCount > 1 {
		return nil, fmt.Errorf("error loading config: cannot use more than one of encrypted_suffix, unencrypted_suffix, or encrypted_regex for the same rule")
	}

	groups, err := getKeyGroupsFromCreationRule(rule, kmsEncryptionContext)
	if err != nil {
		return nil, err
	}

	return &Config{
		KeyGroups:         groups,
		ShamirThreshold:   rule.ShamirThreshold,
		UnencryptedSuffix: rule.UnencryptedSuffix,
		EncryptedSuffix:   rule.EncryptedSuffix,
		UnencryptedRegex:  rule.UnencryptedRegex,
		EncryptedRegex:    rule.EncryptedRegex,
	}, nil
}

func parseDestinationRuleForFile(conf *configFile, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
	var rule *creationRule
	var dRule *destinationRule

	if len(conf.DestinationRules) > 0 {
		for _, r := range conf.DestinationRules {
			if r.PathRegex == "" {
				dRule = &r
				rule = &dRule.RecreationRule
				break
			}
			if r.PathRegex != "" {
				if match, _ := regexp.MatchString(r.PathRegex, filePath); match {
					dRule = &r
					rule = &dRule.RecreationRule
					break
				}
			}
		}
	}

	if dRule == nil {
		return nil, fmt.Errorf("error loading config: no matching destination found in config")
	}

	var dest publish.Destination
	if dRule != nil {
		if dRule.S3Bucket != "" && dRule.GCSBucket != "" && dRule.VaultPath != "" {
			return nil, fmt.Errorf("error loading config: more than one destinations were found in a single destination rule, you can only use one per rule")
		}
		if dRule.S3Bucket != "" {
			dest = publish.NewS3Destination(dRule.S3Bucket, dRule.S3Prefix)
		}
		if dRule.GCSBucket != "" {
			dest = publish.NewGCSDestination(dRule.GCSBucket, dRule.GCSPrefix)
		}
		if dRule.VaultPath != "" {
			dest = publish.NewVaultDestination(dRule.VaultAddress, dRule.VaultPath, dRule.VaultKVMountName, dRule.VaultKVVersion)
		}
	}

	config, err := configFromRule(rule, kmsEncryptionContext)
	if err != nil {
		return nil, err
	}
	config.Destination = dest
	config.OmitExtensions = dRule.OmitExtensions

	return config, nil
}

func parseCreationRuleForFile(conf *configFile, confPath, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
	// If config file doesn't contain CreationRules (it's empty or only contains DestionationRules), assume it does not exist
	if conf.CreationRules == nil {
		return nil, nil
	}

	configDir, err := filepath.Abs(filepath.Dir(confPath))
	if err != nil {
		return nil, err
	}

	// compare file path relative to path of config file
	filePath = strings.TrimPrefix(filePath, configDir + string(filepath.Separator))

	var rule *creationRule

	for _, r := range conf.CreationRules {
		if r.PathRegex == "" {
			rule = &r
			break
		}
		reg, err := regexp.Compile(r.PathRegex)
		if err != nil {
			return nil, fmt.Errorf("can not compile regexp: %w", err)
		}
		if reg.MatchString(filePath) {
			rule = &r
			break
		}
	}

	if rule == nil {
		return nil, fmt.Errorf("error loading config: no matching creation rules found")
	}

	config, err := configFromRule(rule, kmsEncryptionContext)
	if err != nil {
		return nil, err
	}

	return config, nil
}

// LoadCreationRuleForFile load the configuration for a given SOPS file from the config file at confPath. A kmsEncryptionContext
// should be provided for configurations that do not contain key groups, as there's no way to specify context inside
// a SOPS config file outside of key groups.
func LoadCreationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
	conf, err := loadConfigFile(confPath)
	if err != nil {
		return nil, err
	}

	return parseCreationRuleForFile(conf, confPath, filePath, kmsEncryptionContext)
}

// LoadDestinationRuleForFile works the same as LoadCreationRuleForFile, but gets the "creation_rule" from the matching destination_rule's
// "recreation_rule".
func LoadDestinationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
	conf, err := loadConfigFile(confPath)
	if err != nil {
		return nil, err
	}
	return parseDestinationRuleForFile(conf, filePath, kmsEncryptionContext)
}
07070100000040000081A400000000000000000000000162794F9300003113000000000000000000000000000000000000002100000000sops-3.7.3/config/config_test.gopackage config

import (
	"os"
	"path"
	"testing"

	"github.com/stretchr/testify/assert"
)

type mockFS struct {
	stat func(string) (os.FileInfo, error)
}

func (fs mockFS) Stat(name string) (os.FileInfo, error) {
	return fs.stat(name)
}

func TestFindConfigFileRecursive(t *testing.T) {
	expectedPath := path.Clean("./../../.sops.yaml")
	fs = mockFS{stat: func(name string) (os.FileInfo, error) {
		if name == expectedPath {
			return nil, nil
		}
		return nil, &os.PathError{}
	}}
	filepath, err := FindConfigFile(".")
	assert.Nil(t, err)
	assert.Equal(t, expectedPath, filepath)
}

func TestFindConfigFileCurrentDir(t *testing.T) {
	expectedPath := path.Clean(".sops.yaml")
	fs = mockFS{stat: func(name string) (os.FileInfo, error) {
		if name == expectedPath {
			return nil, nil
		}
		return nil, &os.PathError{}
	}}
	filepath, err := FindConfigFile(".")
	assert.Nil(t, err)
	assert.Equal(t, expectedPath, filepath)
}

var sampleConfig = []byte(`
creation_rules:
  - path_regex: foobar*
    kms: "1"
    pgp: "2"
    gcp_kms: "3"
    hc_vault_transit_uri: http://4:8200/v1/4/keys/4
  - path_regex: ""
    kms: foo
    pgp: bar
    gcp_kms: baz
    hc_vault_transit_uri: http://127.0.1.1/v1/baz/keys/baz
`)

var sampleConfigWithPath = []byte(`
creation_rules:
  - path_regex: foo/bar*
    kms: "1"
    pgp: "2"
    gcp_kms: "3"
    hc_vault_uris: http://4:8200/v1/4/keys/4
  - path_regex: somefilename.yml
    kms: bilbo
    pgp: baggins
    gcp_kms: precious
    hc_vault_uris: https://pluto/v1/pluto/keys/pluto
  - path_regex: ""
    kms: foo
    pgp: bar
    gcp_kms: baz
    hc_vault_uris: https://foz:443/v1/foz/keys/foz
`)

var sampleConfigWithAmbiguousPath = []byte(`
creation_rules:
  - path_regex: foo/*
    kms: "1"
    pgp: "2"
    gcp_kms: "3"
    hc_vault_uris: http://4:8200/v1/4/keys/4
`)

var sampleConfigWithGroups = []byte(`
creation_rules:
  - path_regex: foobar*
    kms: "1"
    pgp: "2"
  - path_regex: ""
    key_groups:
    - kms:
      - arn: foo
      pgp:
      - bar
      gcp_kms:
      - resource_id: foo
      azure_keyvault:
      - vaultUrl: https://foo.vault.azure.net
        key: foo-key
        version: fooversion
      hc_vault:
      - 'https://foo.vault:8200/v1/foo/keys/foo-key'
    - kms:
      - arn: baz
      pgp:
      - qux
      gcp_kms:
      - resource_id: bar
      - resource_id: baz
      azure_keyvault:
      - vaultUrl: https://bar.vault.azure.net
        key: bar-key
        version: barversion
      hc_vault:
      - 'https://baz.vault:8200/v1/baz/keys/baz-key'
`)

var sampleConfigWithSuffixParameters = []byte(`
creation_rules:
  - path_regex: foobar*
    kms: "1"
    pgp: "2"
    unencrypted_suffix: _unencrypted
  - path_regex: bar?foo$
    encrypted_suffix: _enc
    key_groups:
      - kms:
          - arn: baz
        pgp:
          - qux
        gcp_kms:
          - resource_id: bar
          - resource_id: baz
        azure_keyvault:
        - vaultUrl: https://foo.vault.azure.net
          key: foo-key
          version: fooversion
    `)

var sampleConfigWithRegexParameters = []byte(`
creation_rules:
  - path_regex: barbar*
    kms: "1"
    pgp: "2"
    encrypted_regex: "^enc:"
    unencrypted_regex: "^dec:"
    `)

var sampleConfigWithInvalidParameters = []byte(`
creation_rules:
  - path_regex: foobar*
    kms: "1"
    pgp: "2"
    hc_vault_uris: "https://vault.com/v1/bug/keys/pr"
    unencrypted_suffix: _unencrypted
    encrypted_suffix: _enc
    `)

var sampleConfigWithNoMatchingRules = []byte(`
creation_rules:
  - path_regex: notexisting
    pgp: bar
`)

var sampleEmptyConfig = []byte(``)

var sampleConfigWithEmptyCreationRules = []byte(`
creation_rules:
`)

var sampleConfigWithOnlyDestinationRules = []byte(`
destination_rules:
  - path_regex: ""
    s3_bucket: "foobar"
    s3_prefix: "test/"
    recreation_rule:
      pgp: newpgp
`)

var sampleConfigWithDestinationRule = []byte(`
creation_rules:
  - path_regex: foobar*
    kms: "1"
    pgp: "2"
    gcp_kms: "3"
  - path_regex: ""
    kms: foo
    pgp: bar
    gcp_kms: baz
destination_rules:
  - path_regex: ""
    s3_bucket: "foobar"
    s3_prefix: "test/"
    recreation_rule:
      pgp: newpgp
`)

var sampleConfigWithVaultDestinationRules = []byte(`
creation_rules:
  - path_regex: foobar*
    kms: "1"
    pgp: "2"
    gcp_kms: "3"
  - path_regex: ""
    kms: foo
    pgp: bar
    gcp_kms: baz
destination_rules:
  - vault_path: "foobar/"
    path_regex: "vault-v2/*"
  - vault_path: "barfoo/"
    vault_kv_mount_name: "kv/"
    vault_kv_version: 1
    path_regex: "vault-v1/*"
`)

var sampleConfigWithInvalidComplicatedRegexp = []byte(`
creation_rules:
  - path_regex: "[ ]\\K(?<!\\d )(?="
    kms: default
`)

var sampleConfigWithComplicatedRegexp = []byte(`
creation_rules:
  - path_regex: "stage/dev/feature-.*"
    kms: dev-feature 
  - path_regex: "stage/dev/.*"
    kms: dev
  - path_regex: "stage/staging/.*"
    kms: staging
  - path_regex: "stage/.*/.*"
    kms: default
`)

func parseConfigFile(confBytes []byte, t *testing.T) *configFile {
	conf := &configFile{}
	err := conf.load(confBytes)
	assert.Nil(t, err)
	return conf
}

func TestLoadConfigFile(t *testing.T) {
	expected := configFile{
		CreationRules: []creationRule{
			{
				PathRegex: "foobar*",
				KMS:       "1",
				PGP:       "2",
				GCPKMS:    "3",
				VaultURI:  "http://4:8200/v1/4/keys/4",
			},
			{
				PathRegex: "",
				KMS:       "foo",
				PGP:       "bar",
				GCPKMS:    "baz",
				VaultURI:  "http://127.0.1.1/v1/baz/keys/baz",
			},
		},
	}

	conf := configFile{}
	err := conf.load(sampleConfig)
	assert.Nil(t, err)
	assert.Equal(t, expected, conf)
}

func TestLoadConfigFileWithGroups(t *testing.T) {
	expected := configFile{
		CreationRules: []creationRule{
			{
				PathRegex: "foobar*",
				KMS:       "1",
				PGP:       "2",
			},
			{
				PathRegex: "",
				KeyGroups: []keyGroup{
					{
						KMS:     []kmsKey{{Arn: "foo"}},
						PGP:     []string{"bar"},
						GCPKMS:  []gcpKmsKey{{ResourceID: "foo"}},
						AzureKV: []azureKVKey{{VaultURL: "https://foo.vault.azure.net", Key: "foo-key", Version: "fooversion"}},
						Vault:   []string{"https://foo.vault:8200/v1/foo/keys/foo-key"},
					},
					{
						KMS: []kmsKey{{Arn: "baz"}},
						PGP: []string{"qux"},
						GCPKMS: []gcpKmsKey{
							{ResourceID: "bar"},
							{ResourceID: "baz"},
						},
						AzureKV: []azureKVKey{{VaultURL: "https://bar.vault.azure.net", Key: "bar-key", Version: "barversion"}},
						Vault:   []string{"https://baz.vault:8200/v1/baz/keys/baz-key"},
					},
				},
			},
		},
	}

	conf := configFile{}
	err := conf.load(sampleConfigWithGroups)
	assert.Nil(t, err)
	assert.Equal(t, expected, conf)
}

func TestLoadConfigFileWithNoMatchingRules(t *testing.T) {
	_, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithNoMatchingRules, t), "/conf/path", "foobar2000", nil)
	assert.NotNil(t, err)
}

func TestLoadConfigFileWithInvalidComplicatedRegexp(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithInvalidComplicatedRegexp, t), "/conf/path", "stage/prod/api.yml", nil)
	assert.Equal(t, "can not compile regexp: error parsing regexp: invalid escape sequence: `\\K`", err.Error())
	assert.Nil(t, conf)
}

func TestLoadConfigFileWithComplicatedRegexp(t *testing.T) {
	for filePath, k := range map[string]string{
		"stage/prod/api.yml":        "default",
		"stage/dev/feature-foo.yml": "dev-feature",
		"stage/dev/api.yml":         "dev",
	} {
		conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithComplicatedRegexp, t), "/conf/path", filePath, nil)
		assert.Nil(t, err)
		assert.Equal(t, k, conf.KeyGroups[0][0].ToString())
	}
}

func TestLoadEmptyConfigFile(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleEmptyConfig, t), "/conf/path", "foobar2000", nil)
	assert.Nil(t, conf)
	assert.Nil(t, err)
}

func TestLoadConfigFileWithEmptyCreationRules(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithEmptyCreationRules, t), "/conf/path", "foobar2000", nil)
	assert.Nil(t, conf)
	assert.Nil(t, err)
}

func TestLoadConfigFileWithOnlyDestinationRules(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithOnlyDestinationRules, t), "/conf/path", "foobar2000", nil)
	assert.Nil(t, conf)
	assert.Nil(t, err)
}

func TestKeyGroupsForFile(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfig, t), "/conf/path", "foobar2000", nil)
	assert.Nil(t, err)
	assert.Equal(t, "2", conf.KeyGroups[0][0].ToString())
	assert.Equal(t, "1", conf.KeyGroups[0][1].ToString())
	conf, err = parseCreationRuleForFile(parseConfigFile(sampleConfig, t), "/conf/path", "whatever", nil)
	assert.Nil(t, err)
	assert.Equal(t, "bar", conf.KeyGroups[0][0].ToString())
	assert.Equal(t, "foo", conf.KeyGroups[0][1].ToString())
}

func TestKeyGroupsForFileWithPath(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithPath, t), "/conf/path", "foo/bar2000", nil)
	assert.Nil(t, err)
	assert.Equal(t, "2", conf.KeyGroups[0][0].ToString())
	assert.Equal(t, "1", conf.KeyGroups[0][1].ToString())
	conf, err = parseCreationRuleForFile(parseConfigFile(sampleConfigWithPath, t), "/conf/path", "somefilename.yml", nil)
	assert.Nil(t, err)
	assert.Equal(t, "baggins", conf.KeyGroups[0][0].ToString())
	assert.Equal(t, "bilbo", conf.KeyGroups[0][1].ToString())
	conf, err = parseCreationRuleForFile(parseConfigFile(sampleConfig, t), "/conf/path", "whatever", nil)
	assert.Nil(t, err)
	assert.Equal(t, "bar", conf.KeyGroups[0][0].ToString())
	assert.Equal(t, "foo", conf.KeyGroups[0][1].ToString())
}

func TestKeyGroupsForFileWithGroups(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithGroups, t), "/conf/path", "whatever", nil)
	assert.Nil(t, err)
	assert.Equal(t, "bar", conf.KeyGroups[0][0].ToString())
	assert.Equal(t, "foo", conf.KeyGroups[0][1].ToString())
	assert.Equal(t, "qux", conf.KeyGroups[1][0].ToString())
	assert.Equal(t, "baz", conf.KeyGroups[1][1].ToString())
}

func TestLoadConfigFileWithUnencryptedSuffix(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithSuffixParameters, t), "/conf/path", "foobar", nil)
	assert.Nil(t, err)
	assert.Equal(t, "_unencrypted", conf.UnencryptedSuffix)
}

func TestLoadConfigFileWithEncryptedSuffix(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithSuffixParameters, t), "/conf/path", "barfoo", nil)
	assert.Nil(t, err)
	assert.Equal(t, "_enc", conf.EncryptedSuffix)
}

func TestLoadConfigFileWithUnencryptedRegex(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithRegexParameters, t), "/conf/path", "barbar", nil)
	assert.Equal(t, nil, err)
	assert.Equal(t, "^dec:", conf.UnencryptedRegex)
}

func TestLoadConfigFileWithEncryptedRegex(t *testing.T) {
	conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithRegexParameters, t), "/conf/path", "barbar", nil)
	assert.Equal(t, nil, err)
	assert.Equal(t, "^enc:", conf.EncryptedRegex)
}

func TestLoadConfigFileWithInvalidParameters(t *testing.T) {
	_, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithInvalidParameters, t), "/conf/path", "foobar", nil)
	assert.NotNil(t, err)
}

func TestLoadConfigFileWithAmbiguousPath(t *testing.T) {
	config := parseConfigFile(sampleConfigWithAmbiguousPath, t)
	_, err := parseCreationRuleForFile(config, "/foo/config", "/foo/foo/bar", nil)
	assert.Nil(t, err)
	_, err = parseCreationRuleForFile(config, "/foo/config", "/foo/fuu/bar", nil)
	assert.NotNil(t, err)
}

func TestLoadConfigFileWithDestinationRule(t *testing.T) {
	conf, err := parseDestinationRuleForFile(parseConfigFile(sampleConfigWithDestinationRule, t), "barfoo", nil)
	assert.Nil(t, err)
	assert.Equal(t, "newpgp", conf.KeyGroups[0][0].ToString())
	assert.NotNil(t, conf.Destination)
	assert.Equal(t, "s3://foobar/test/barfoo", conf.Destination.Path("barfoo"))
}

func TestLoadConfigFileWithVaultDestinationRules(t *testing.T) {
	conf, err := parseDestinationRuleForFile(parseConfigFile(sampleConfigWithVaultDestinationRules, t), "vault-v2/barfoo", nil)
	assert.Nil(t, err)
	assert.NotNil(t, conf.Destination)
	assert.Contains(t, conf.Destination.Path("barfoo"), "/v1/secret/data/foobar/barfoo")
	conf, err = parseDestinationRuleForFile(parseConfigFile(sampleConfigWithVaultDestinationRules, t), "vault-v1/barfoo", nil)
	assert.Nil(t, err)
	assert.NotNil(t, conf.Destination)
	assert.Contains(t, conf.Destination.Path("barfoo"), "/v1/kv/barfoo/barfoo")
}
07070100000041000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002100000000sops-3.7.3/config/test_resources07070100000042000081A400000000000000000000000162794F9300000B59000000000000000000000000000000000000002E00000000sops-3.7.3/config/test_resources/example.yamlexample_key: ENC[AES256_GCM,data:mOtEkS9nmmncpxRBYA==,iv:ecPCSny/r62lJr2DuwvucwK/SGiAWLSbariWob7r4Ek=,tag:3m8Dul5yxqEmunMJom+Cag==,type:str]
example_array:
-   ENC[AES256_GCM,data:vYoTCRm2gudQ/XFkIm8=,iv:DOM9XZRFFdty4kjMWvqT4UCiwHr5sd7Z3CiS6Xg4q8Q=,tag:07gBVF4mSeG/7Xs+qBeMew==,type:str]
-   ENC[AES256_GCM,data:Wf3LVG1677UaZSzzIYc=,iv:8sQd09YT7BkJSntvKUudfXtjPyqzsPqayu0Qo8v+SHI=,tag:zVz8aWLVox40iEKOSUGW+Q==,type:str]
example_multiline: |-
    ENC[AES256_GCM,data:Jw34myS8s3NPrOnPnBdJ67bVyNxKmq7pmp0=,iv:8pjw5k5XpV2x4bM2jpISMHy97b6ffkCFvbtKOiZ22VU=,tag:V3NFD/WtgBDXujNTxst1HQ==,type:str]
example_number: ENC[AES256_GCM,data:/KVkicl+xk6x,iv:M+qJNRywTlnkk28nLV3aO6pszNoLcNlrSs05aPIwgwE=,tag:Zku+tQMIeF/Sqnuy8QW4zg==,type:float]
example:
    nested:
        values: ENC[AES256_GCM,data:y/Gbd4dFl8X4,iv:zOKYeVFBLEoliQ/WuxgNwui+9Vy1CcR+Eg9Zrs9d2qs=,tag:UUC/23KmH4ZGPT+dhHBYGg==,type:str]
example_booleans:
-   ENC[AES256_GCM,data:RYzb/A==,iv:7cQpPBZ/0Woa5X3kdlx+4pOW15EmJMXopelmOGfYd1w=,tag:RT0L/KZJTrHQ9E3fgFfqhA==,type:bool]
-   ENC[AES256_GCM,data:H/wjpX4=,iv:EDRADHc4Z65yJzXHpVsWgaqcNPhwEdmSNeRtmi14p0g=,tag:ED3o+9Yv4NkoMg2BeS03Cw==,type:bool]
sops:
    lastmodified: '2016-08-02T18:34:06Z'
    attention: This section contains key material that should only be modified with
        extra care. See `sops -h`.
    unencrypted_suffix: _unencrypted
    mac: ENC[AES256_GCM,data:n6bRU4NrPaMvQj0nI7j7x+Ihkdvr61SDYKw7atVeTDGj+puvd+/0DuFyeWJADNtlAZq7pasSobUMuA66MrNpavMLc5/tBX+NZbT1cbGD0nBt4y7ztog1MFSay03CDrhbepmWNSs8ihgYtHfxfGiqzTU5sRTM+iEHAiwJ1h7eU4k=,iv:oqO1KMNkWUC+aAxgNV4Kng4CBWzO7wIIswDwbRvlv8g=,tag:uUmgKKFeNGvAzZZLI0RViA==,type:str]
    version: '1.13'
    pgp:
    -   fp: E5297818703249D0C60E19E6824612478D1A4CCD
        created_at: '2016-08-02T18:34:06Z'
        enc: |
            -----BEGIN PGP MESSAGE-----
            Version: GnuPG v1

            hQIMA2X8rvoeiASBAQ/+MYitjFEMfLz5N6JeZ0IoYAbpJZPWkUavhXF9H4SU9mYi
            8Tp4KgJVRhv7IOsqmuMrxIWZL9++HqEpe+HWWy6luzPrWU7HIPpwGcYRCyWrdt2u
            UjBVYMVgLwGMfrWe6sr0yWrVXrLQ2ltPBn8J5vyHDZUYXY33B1vj7F2BuF2oL32J
            f9ZTAikiHJI/4Q62P5x7RQrqHqV/Ncczf+w/ni+ZnOwH5kay306DKNwTX64AWoc6
            iW/nPCRzIfhWx89BPu+SwGaEwQbHvS7zMLZ/UtarNWAFQ5QmlBW6dyH9Edj5bA7u
            BuhxjBT/6ZaXX6iSAl98021eplf6s945vB7gPuzrgW1LM4t6X8M9P0KMMrDujz/S
            cm7Vrhm//syT2IJqFR9x/PylbpHhVTwTWZIFvLRRJlM66NYsvxMvJdknovN/889D
            I+mQkA8oxtjyof8fgIOvU7ph6yBzRbb838a0Fyu5U5wwrNzBc5N23WNjzNcT1XRy
            kl7W7MElHnjzq2jNJIB+XipaV6uoMbaoiYZNi+bpHj6pASCaBko7c8aPYVLruPbZ
            G3fCMY1mxwRBYyn6d1sTqzVGR4WBJLc+4ikm+H4O4aa4sv137/cnRRbVNhpgefob
            H9SSjSr2gr9YiHgMsG0kdilyTnccYaeljBqOJKUKiWOPk8OBiycNtU+rESagw5rS
            XgHpMul9uyxksAQf7VpXJJQYYgk+DlZhkkd/hVu59vxJo2oxxp5aGyBijGSy7mcI
            3FajmsEFKdPaHpbsSDP0/83RHIwVwbQK5N+IcNGHk/rnh7XEJ73vtrsA9muAmoo=
            =OxaE
            -----END PGP MESSAGE-----
07070100000043000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/decrypt07070100000044000081A400000000000000000000000162794F930000090C000000000000000000000000000000000000001E00000000sops-3.7.3/decrypt/decrypt.go/*
Package decrypt is the external API other Go programs can use to decrypt SOPS files. It is the only package in SOPS with
a stable API.
*/
package decrypt // import "go.mozilla.org/sops/v3/decrypt"

import (
	"fmt"
	"io/ioutil"
	"time"

	"go.mozilla.org/sops/v3/aes"
	"go.mozilla.org/sops/v3/cmd/sops/common"
	. "go.mozilla.org/sops/v3/cmd/sops/formats" // Re-export
)

// File is a wrapper around Data that reads a local encrypted
// file and returns its cleartext data in an []byte
func File(path, format string) (cleartext []byte, err error) {
	// Read the file into an []byte
	encryptedData, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("Failed to read %q: %w", path, err)
	}

	// uses same logic as cli.
	formatFmt := FormatForPathOrString(path, format)
	return DataWithFormat(encryptedData, formatFmt)
}

// DataWithFormat is a helper that takes encrypted data, and a format enum value,
// decrypts the data and returns its cleartext in an []byte.
func DataWithFormat(data []byte, format Format) (cleartext []byte, err error) {

	store := common.StoreForFormat(format)

	// Load SOPS file and access the data key
	tree, err := store.LoadEncryptedFile(data)
	if err != nil {
		return nil, err
	}
	key, err := tree.Metadata.GetDataKey()
	if err != nil {
		return nil, err
	}

	// Decrypt the tree
	cipher := aes.NewCipher()
	mac, err := tree.Decrypt(key, cipher)
	if err != nil {
		return nil, err
	}

	// Compute the hash of the cleartext tree and compare it with
	// the one that was stored in the document. If they match,
	// integrity was preserved
	originalMac, err := cipher.Decrypt(
		tree.Metadata.MessageAuthenticationCode,
		key,
		tree.Metadata.LastModified.Format(time.RFC3339),
	)
	if originalMac != mac {
		return nil, fmt.Errorf("Failed to verify data integrity. expected mac %q, got %q", originalMac, mac)
	}

	return store.EmitPlainFile(tree.Branches)
}

// Data is a helper that takes encrypted data and a format string,
// decrypts the data and returns its cleartext in an []byte.
// The format string can be `json`, `yaml`, `ini`, `dotenv` or `binary`.
// If the format string is empty, binary format is assumed.
func Data(data []byte, format string) (cleartext []byte, err error) {
	formatFmt := FormatFromString(format)
	return DataWithFormat(data, formatFmt)
}
07070100000045000081A400000000000000000000000162794F9300000522000000000000000000000000000000000000002300000000sops-3.7.3/decrypt/example_test.gopackage decrypt

import (
	"encoding/json"

	"go.mozilla.org/sops/v3/logging"

	"github.com/sirupsen/logrus"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("DECRYPT")
}

type configuration struct {
	FirstName string  `json:"firstName"`
	LastName  string  `json:"lastName"`
	Age       float64 `json:"age"`
	Address   struct {
		City          string `json:"city"`
		PostalCode    string `json:"postalCode"`
		State         string `json:"state"`
		StreetAddress string `json:"streetAddress"`
	} `json:"address"`
	PhoneNumbers []struct {
		Number string `json:"number"`
		Type   string `json:"type"`
	} `json:"phoneNumbers"`
	AnEmptyValue string `json:"anEmptyValue"`
}

func ExampleDecryptFile() {
	var (
		confPath string = "./example.json"
		cfg      configuration
		err      error
	)
	confData, err := File(confPath, "json")
	if err != nil {
		log.Fatalf("cleartext configuration marshalling failed with error: %v", err)
	}
	err = json.Unmarshal(confData, &cfg)
	if err != nil {
		log.Fatalf("cleartext configuration unmarshalling failed with error: %v", err)
	}
	if cfg.FirstName != "John" ||
		cfg.LastName != "Smith" ||
		cfg.Age != 25.4 ||
		cfg.PhoneNumbers[1].Number != "646 555-4567" {
		log.Fatalf("configuration does not contain expected values: %+v", cfg)
	}
	log.Printf("%+v", cfg)
}
07070100000046000081A400000000000000000000000162794F9300000C64000000000000000000000000000000000000001700000000sops-3.7.3/example.ini; ENC[AES256_GCM,data:EexYkXJHDv1E9WwU8wUrBakxAZcnlIkAJQgRFts3alF+5w==,iv:/wd7bCxSkJ85eG69NMq2RH70Dlw5q/diL42GcdTbRYs=,tag:8W4B3JjLYAtJgG3Zi6Hu+A==,type:comment]
[name]
firstName = ENC[AES256_GCM,data:7cWtsg==,iv:rh/1l0yiOE3JmmSJB9CXp4Ctb5en0FcNUZZkLyWsX3c=,tag:d7We6RtSs3AYhA6jan/CoA==,type:str]
lastName  = ENC[AES256_GCM,data:Iq50v00=,iv:o+bfCsNBsv/+XdXS4M7TCOTPzG38ft14/Q9/vZvnNvM=,tag:cRivFOZyIpMDXdKA9R+6mA==,type:str]
age       = ENC[AES256_GCM,data:aSt9Fg==,iv:z1NjXdRRFs8+dAFkrngS7PyhUSCOTzAmxtzCcfafkAM=,tag:6kpo65TxMu1oKF9d7L1wJg==,type:str]

[address]
city          = ENC[AES256_GCM,data:3hQ6RXAc8ik=,iv:ajAjhQIJiNWy9PGj8ZCI5k+3uw7igweqZ+eiZ9tuvG0=,tag:bGX7PK+/hTQE/1uYmXexyA==,type:str]
postalCode    = ENC[AES256_GCM,data:1Ha0avZTe6IflQ==,iv:l8rbQbhSwxr3AJzqe/H8ALrHcXsntcBfkyUGt9/6k/U=,tag:f6s6ObY5QaqmG4CUpV+UKg==,type:str]
state         = ENC[AES256_GCM,data:ba0=,iv:P/VsGYbhT8ihRvluWc8HcUDPPigh/IcHXBnj0Xb86AE=,tag:JN7Y77eOaF1+tJjN2cxtnQ==,type:str]
streetAddress = ENC[AES256_GCM,data:LYYluDyJzryoYT2Nkg==,iv:KTmXPVV5tSY8/piTb/uPcfJL2mUqkS1aUI9pukQm1Dc=,tag:AzdSsRjEWx8kzclSZIdIcA==,type:str]

[phoneNumbers]
home   = ENC[AES256_GCM,data:wv3pG8J7wmiotVY6,iv:YdX9RKlN9t0PohEu+Dxws0POUf8hjDMi4PJre+4/lsg=,tag:2XadWng1DxnfMituylFC5g==,type:str]
office = ENC[AES256_GCM,data:Hu3X626TUgV3Ix7i,iv:P1SBjvZPogJih4KLFLcbxeWeZT2aTNXPBtwlC9yZZ70=,tag:4SOkQZw4ynKfrFwrmre6Lw==,type:str]

[not private]
notsecret_unencrypted = hi there!

[sops]
pgp__list_0__map_fp         = FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
pgp__list_1__map_created_at = 2019-12-10T22:45:52Z
pgp__list_1__map_fp         = D7229043384BCC60326C6FB9D8720D957C3D3074
lastmodified                = 2019-12-10T22:45:52Z
pgp__list_0__map_created_at = 2019-12-10T22:45:52Z
pgp__list_0__map_enc        = -----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgAGb6tDB1gDtwLcVuENgd/AU1ZjxDbUY46OJzi1fkQ4jyo\nvxfCviqfslYyncVrKab0S9U+alIu8Po8BrHCTwmDkcnPG7HWMcqRo+Nq3JShir3y\ntVMbHuRGykPmGjs7GOUn1WibOisiJjHzLdFOsyNexveBV9AVln7kZazCRlXwbLqc\nyJ9IQsWi1ET6DjtViejiBKblDzZ87WgvL2Z0E8QfYXmrB3GpFa+K4Aqgb3pHkQQo\nGQArNAe8BmrhEXotj0IS/K3v2wjX5Sqli+0dh4qhaXiIeYb16nfc4M1TUo4Vvj7Q\nyWdjufTi+2RIxxUEkIdRg6lDVabm98hLn6/jsQ5c5tLgAeT99N9te4j1eeO62xwy\nz4S+4Xok4Cng9OFHFODz4hh6ah3gYuXRf4qFh++GUaxGwG/iGNSiY7A9eWg1gM+N\nPB6MtiOnhuB15N5mbD2V1xrzlF0fu0K98Vviv75h4eGzYQA=\n=RaGg\n-----END PGP MESSAGE-----
mac                         = ENC[AES256_GCM,data:5NouZe0uY8581eqXLe31TWYqThPlfePdpIyYDBgj3FwGvPfhiWB+dn/diSt4NpZ9zUNI4VhF2cmNesk8NgZdUK9/N9WbGgmJjjdBXvo0xhvr8RR22AiVCB9ipDlSFqnOMyJzo+CAvpSDbwF1bMU0qxwKW+zpDZV2uLX+px7b2qM=,iv:em3/lIV7eYA46u2kM0bR1/3O6Ga2vRfS5/r2qh0jges=,tag:ow9277u7mFNcs4IdsU4YUQ==,type:str]
pgp__list_1__map_enc        = -----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBAAQYkLcQ1MORGYYprtFwUZPO2J3CBOU4u76qJQod5JXZuAa\n3aCynkyrOHIpgh2cKoyUle4u/FK68mFl9+TxixlFDRxt1CsvMR8dHP0EFJOMSq5U\nNwpcnEh20++3DNiq7bCtS2W68FRh8bVxmhEEXPxW4HLJV4WZSE4pu9B7w9kd5NLg\nAeQyj0ugpILq3IonJdLsgLkg4RGV4GDgFOEqxOCT4uKeSX/gdOXvr5WJzlWJRLjT\nA51CtgnUVXlNO5zfCiFRMXOFyE1FCeBm5Ivkgh4KxkQvwI6jSZpU1rjiz4QYTOGO\n4gA=\n=XdTd\n-----END PGP MESSAGE-----
unencrypted_suffix          = _unencrypted
version                     = 3.5.0

07070100000047000081A400000000000000000000000162794F9300000CBB000000000000000000000000000000000000001800000000sops-3.7.3/example.json{
	"firstName": "ENC[AES256_GCM,data:f8++3g==,iv:rYuVzzb+C40QlYgO4Dl2V7atZUx0ITBcyb5fUsftKMo=,tag:krquPqa1HQltZqidzNamrA==,type:str]",
	"lastName": "ENC[AES256_GCM,data:94a2Q8c=,iv:c3NC7L80UTtbz7gdvPV5oSUwg30lC3Kg82uvRVs5CZw=,tag:kUXRNerUWmSe44mwD4w5uA==,type:str]",
	"age": "ENC[AES256_GCM,data:gjwWkw==,iv:XEWFpsyvEsPwr3qqsOJlfZ+vSZdiA+D6DAc6aoq/BS0=,tag:pcnUyMtYFa9v5DB6sNV15w==,type:float]",
	"address": {
		"city": "ENC[AES256_GCM,data:vSeyQwN1Z9k=,iv:DBmuX4w6w14Z/1b820OE3SM3MPx3oLGAeSoR4CWxdhg=,tag:ClpJZLb4ObIOdDD441clrw==,type:str]",
		"postalCode": "ENC[AES256_GCM,data:SZadC4tZh106eg==,iv:z/yWCZTd19j+3cFY5mwVkxY8a7i6veTBnwh4fsw5Kbw=,tag:iel9Pqh0jS0KhjxklXeqIg==,type:str]",
		"state": "ENC[AES256_GCM,data:b0Y=,iv:7Ar/Tb7XCDo5ABZNdSNBqGquaGEQF7dNxd1VvW7Nwak=,tag:uEjmOKwPlkYSq4IV1tQjwQ==,type:str]",
		"streetAddress": "ENC[AES256_GCM,data:dVFPTRPKOFSJ1plV9w==,iv:08Ks4C1FzFozezKBBYSPEAIkC5DkthDFmMW0R3zVbkI=,tag:waInfXMCAcx5C7avXHahOw==,type:str]"
	},
	"phoneNumbers": [
		{
			"number": "ENC[AES256_GCM,data:lkUEC7s3qU9AY6W+,iv:KjF9i0K9u7THbb3Bn1adQrIKpv1ZqA3PiJkctgFm3Bw=,tag:LBSqqr+gn15x0Pz5JKMJJQ==,type:str]",
			"type": "ENC[AES256_GCM,data:aXBHGg==,iv:ulcjNwVfGFvUtVN8q0h1LMYM5zRDmOsqtoFC1JOHREY=,tag:7vdMoCOD+7IeUHWQqQJ8XA==,type:str]"
		},
		{
			"number": "ENC[AES256_GCM,data:z9Ujp3n2yXBqPNM1,iv:1nbZrIKozuS2p2AgD5/gHgjMN/VSd8SFeCbWdjy9Cf0=,tag:tfwaYihQDMAsmy/yt6ScLg==,type:str]",
			"type": "ENC[AES256_GCM,data:ddkB7Iu6,iv:4t31C5r1zhCpLQ64idoJ8OBC7ocME15zCUXCmgf2ItY=,tag:RmBsmbhQW10oOTDuzfxEaA==,type:str]"
		}
	],
	"anEmptyValue": "",
	"sops": {
		"kms": null,
		"gcp_kms": null,
		"azure_kv": null,
		"lastmodified": "2019-12-10T22:45:55Z",
		"mac": "ENC[AES256_GCM,data:VoXDgYpIYCvFSLyKGx4c8yk56Mk5GkeIwM8IyUi4RgkKBY/xEIzNUOuMqBzWEOvTTsivF/JtUOrBIsDRxGY0u0qNJK1R5WFuSYr3TA5sdu3ytMcu+mKY4THSJN8uuri/tXVcoF+ywLOS6NRFbHDJPtJGcy1XQwJJAgvdw+sIvQA=,iv:dvk3FYXHr2N6gYIw2OqbYiHn6FfXzuyHqZvJIo6IVGM=,tag:rjnNkJSGpXJ762EyjDltXg==,type:str]",
		"pgp": [
			{
				"created_at": "2019-12-10T22:45:55Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgAMSeWf3F8kIm8EFiVgGVQgWGIHUVoolToi8d8lAC8/UdK\ncx9dIqlR43IFvvmCKyNZ6Q+/a1ERc07xLpVp3wmN80sE4NZCGZioThZjp2qNS42e\n/HtLfDu+Rie1eKcXEik40rMDn7d8gaFVOpD3FbzoZUFVm8hN5ChzqQqL1nLy9ZgY\nbthH3Rzt58Z1/sxARLNF2/yUqAEX/YEoL0MxM68Z55kwiMqSZ1rdmLKKfXdJbdoL\nSRrFyi+XaAwr1bTD+BnqHqgmYEEWEfHPDW7e1St/4IS4PU98kKuVLjhBKbfTRUpF\nkzxt+XQV6uDfPzdeOzf+JrFMRaoxTRMpcUi4Jn0vstLgAeSAjSzqkUr6DEVsuS8V\nAqRD4Z9I4HHgy+GYruDQ4kDtTvPgbeUWFma5yz25JOoORZIHaGiEB3T8ZhrFD8VI\ndFLxtxLtCeCg5BhRcrxFgWPQCMK/uCt/GFniNf4Y2OGOHQA=\n=r/lV\n-----END PGP MESSAGE-----",
				"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
			},
			{
				"created_at": "2019-12-10T22:45:55Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBABs0ZEghsU+mdsRFROvlT8ACqk4Ru0Bssw3N+lkSrpR+QyY\nuRgAFNmZgPKz4DhZLHbhOD0zAAHuMCGriXVqtxMYMveX1nbXom+ayBmU0jja+6X2\njwk0MghHS1bgIsGrrhPoD7c3iirdaXSgHKAxwl4bpw5OxW6t91moOtJ4DAyGXdLg\nAeTkQxUyLFEeUbx4xX7kNCm+4Yho4CngDuH4oOD44gSWJsjgU+XiRBuJa50tiloC\nA10yQkWZtuyPOWppO5qOdXqV3XDIeOBl5JSNFuv5B80w8UIYhPPXWpriV1nBveE/\nwAA=\n=Vg44\n-----END PGP MESSAGE-----",
				"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
			}
		],
		"unencrypted_suffix": "_unencrypted",
		"version": "3.5.0"
	}
}07070100000048000081A400000000000000000000000162794F93000007E9000000000000000000000000000000000000001700000000sops-3.7.3/example.txt{
	"data": "ENC[AES256_GCM,data:p6kOd9e7KOYw47VlNlKa52wPFfbY3xaJYQrO5QDT1LyNvUIVBSRTrJxvn5MCC7vdnTOkcBzWmlr6Z/Q23/sx22++3Y7nXTSgFPQxPVIA8X33OoIsCamNHS8+8JWOReALCf2Cd3rzedu0GWR+/f2YBSHNA3C4nffEDbWbXRyAvcvCv3G4umH+Jh9auWUlfbk3Bx/8LvX6DodcxhQ=,iv:ESrDyOG6qetEWGBNHWRpT6ra1NhpaFH3SnjBSdMj2r0=,tag:aP5vOboB64cJDUls9WKsTA==,type:str]",
	"sops": {
		"kms": null,
		"gcp_kms": null,
		"azure_kv": null,
		"lastmodified": "2019-12-10T22:44:49Z",
		"mac": "ENC[AES256_GCM,data:wN+npCzfJVz6nwZQ40FTPD23Ly1CEiU1N6aDua+Mgj9cH7NwJOklW8QKTs3+q3f4HEkbeuFE6VQN+Jm05Zsj1inGjAdG2MfDurspJl6Jpe5DBKgk3zudAcc66gm4T4Dn3h7zFvNovOl+VEa4+ntaxIoVNugVDq3ZLTj/wMd3XwU=,iv:RadNg2jPeQEkE1F/GzrdcPIZHbxXoZpo+iOHpRGlLhc=,tag:ID8N4xhN7p3N5EYGTkYKxg==,type:str]",
		"pgp": [
			{
				"created_at": "2019-12-10T22:44:49Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgANOTnicDHAmqwi76yIm2eAgzm32k34hsPS40vKeCKtbIP\niR91/hDmklYXgR9yL9xgBI0SRTMGySSk9YJ9daZd61JVh1IVuxr93Y8GSxhDldAn\n1Wc2dXJ24x7zxfUs4sfZYCtzXZBUb/eAPLDIkeKPzkVKN4kLdVdccOig/2lOuuVo\nw3Xy+m7cx0VPdsFFzVWok15oHj8n0+J8v6Vnyiyx7yI7xgsynNwpZDUN+K15NyGs\nkaO21AeQnxDWmwo4H93+r10esFYns0kyLOCNwN5/XLskT31f9MCo8H4bBDyeO1lE\nrfLKAn0mh81qKedQLTssjElCLBgY4CpcL9B688P/otLgAeSR+v/JrgslAw+QhiBC\nPxqj4ZUC4KbgFeERieC34sjLWuPgxOUoC769iqiM3ArscWLYG6jYb9Acigwtf5/r\nNkFoXHoZPOD15Ne/ElmCDPowh0aAFCwVp6/ipRc0teELTQA=\n=FyYT\n-----END PGP MESSAGE-----",
				"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
			},
			{
				"created_at": "2019-12-10T22:44:49Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBACebuDSGC4caG1iSviC+IQKXw/mQXmWDDWLA1RDH7/ZEa/2\nqsA0Eb5bsd4Hf5pW6UpK/TDZpFn3eKeCn8wP2G792Ez19UmhwHB+0Zid9Zq76UAZ\n1bcwX2YJeCgd8/OgIxfh7a6MDDz6TDNGL916BIE6kFJwT3Vvm9EzF7vDglE5mtLg\nAeTK87HB76QLJNDI03Q8JYrN4e1S4KzgxOFhkeAv4lmhR+zgCeU3u0ripltx+Hys\ngtiGJSnuoJrYrwhSIO8JOoc2iR0bkuCn5Gf4VirHEUwrIUqgQVsmLQnick1iv+Hk\n3QA=\n=exjM\n-----END PGP MESSAGE-----",
				"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
			}
		],
		"unencrypted_suffix": "_unencrypted",
		"version": "3.5.0"
	}
}07070100000049000081A400000000000000000000000162794F930000140F000000000000000000000000000000000000001800000000sops-3.7.3/example.yamlmyapp1: ENC[AES256_GCM,data:zlGNmhTYX5xol4ZZFsiaoGkD73nn,iv:ql9mkhoU1I64E/FJi3iA0HaAe2U3kQVFee2ZLwPnBik=,tag:SqVSfu/JkRrwqidAT/i0pg==,type:str]
app2:
    db:
        user: ENC[AES256_GCM,data:tQ1l,iv:o9MiMveNYO7T82yDab+4pAt17DO6B4wl8yGy3oFbDb8=,tag:0RjvUUtSQwaUc9SF3RSTZQ==,type:str]
        password: ENC[AES256_GCM,data:8Ll+TDCgzQ==,iv:aao0OSVdFwaB9EGZ0O+Wn5bRJ6do7hgiQioqiwDi1w0=,tag:WKj0nuSSPkm2dBOBwinYvg==,type:str]
        #ENC[AES256_GCM,data:uPuUQAahmq1xL5L2B0+a2gHSl7zXucjqa4Kr0AgNKTpgHw3IPx2lCoK+,iv:qYxjRmWMir47YKhmrrHwV0atmGjcJ3Dts+xvQ+6skHQ=,tag:dsj+0DsfiDyJNGUmvZwnHg==,type:comment]
    key: ENC[AES256_GCM,data:xRjmLiX4BCoSUElToUs5twDq1WNWQNvNMi8yitXly43iGQiwltIs0FsY5u+7fzLepS66oLTGdL0NfWwCGxksYYzUKz5OXRLEatacZP40D71861zu+njmGdXepY0q0q5VOG7ObgIAMMMElVKRIFdjpVgmgUa+/h6R77mEbDztk6lqb28r15XyR6GmdubierTE7aialFzNoC+XO/yk7bnMi0XA/aomj1H5RdZ37LBR2k+rhqXmhkPdGTdJ39t4Ou1Q2Oc4RHRGvgs/EeFqQHcq0AilqXsFIv/PE0bQP564LaOcXK33B+PnoV1D+lXZ7mLOjKlq04c+UojwXjpZeXBr6Ip4H3dAGkPAoQKMtyGwHYzuLCNesxwPv2tPqbxkbVev0AKezGhnPjvCNvRN3S1Y0LPf1atfwzOCBQBhdUTpmXtxCdTNG0cUUZjeIKAyJXDWJooYHlstzDti/dSGMEedOnKq6648Yp7tLNDjAg5CGbDEWjTWehtqgvixUoRdYc2/r/ie3t09XB9h70BzLFDnbNdTNHhg0/aivHeDf8LJ2Co8YvIjLTwlm7GV0mSwzJIoY/2YxXFtw+XFqt+BCt1G8wq3R6OdXZK/+6fUKH/D4EVym5nrGkZIuOCiTB+wpzy09QmZ80Fo+ba0x/1g9ZTMKHk=,iv:ZtzvrO7QSHEOCnKCrIYcaesKnyScV8KaHZr22tUMLlU=,tag:2A3nJBIPF2Q3FlwMYLvG2w==,type:str]
number: ENC[AES256_GCM,data:DX0qiTOWhQvG/w==,iv:ouWsby8JoFwCRj/mLVCnNcYhP2sdyf4h6nwZuGksE7Q=,tag:lPU6AId2JrlquHnYRw+E8Q==,type:float]
an_array:
- ENC[AES256_GCM,data:vyczE8EQr9qHkaM=,iv:sT5jKk3LZ61Zq/neTli5tcnDFxCxY5RuGr2k5oGQWJQ=,tag:1HgaHWfyh6EJLkI1V2kOrw==,type:str]
- ENC[AES256_GCM,data:XtBinnYXR7bx1GY=,iv:KvT9smKVmgMNrab+RzfuWscyvJav2r8j1P08ucNmhgQ=,tag:IllAEvIPPeKOfqi1XbmT8w==,type:str]
- ENC[AES256_GCM,data:gpZ7nwWTGaI+Ti+lk+CPQOoM0ypwK7UMMBUiZAniQHDNJelipqc8hyhNeV+tpJLNaRt74OHs04EX8g==,iv:mKcwVelqLvwVDPjR8NeyMZ7AhsjRgmnYmyEuwPNPrQ8=,tag:vrkoccUfJs105yLCmXYYCw==,type:str]
- ENC[AES256_GCM,data:L9jPh+7+XsdqEpUnFcD4nA==,iv:xyfKjOXVrBDCIQG5786pSu5yvHdl/PK8eVxkIUWoCIw=,tag:Q0wlTV2e2vZeU6eTF5Oacg==,type:str]
somebooleans:
- ENC[AES256_GCM,data:ExiXxg==,iv:K7FUwomqdA7o9lzvNoAMH/wbXs08FextTGGeJKnaatU=,tag:A9UntgvPIcappmeM3jsbdA==,type:bool]
- ENC[AES256_GCM,data:3I0AVdM=,iv:q4YKnRIKufREPmwT4sz8plcsOD6iem/tY3NMUV0STBE=,tag:0w4OMKClWTzjKhqsJZT8JA==,type:bool]
this:
    is:
        a:
            nested:
                value: ENC[AES256_GCM,data:oFn5fJS5+slb2sCdLY5SxZ+iWeowWtf4wn9g,iv:MZ7i4tZnfCQhQRUwXV2fYQPIJ0tTUFLiD9xuB+765e8=,tag:ZEjE5jvxE5HkN3mma84pKw==,type:str]
                #ENC[AES256_GCM,data:WwWiKtMsD1shPe5kPHOh2bJqQPGHwxa6GYrR1y14wiid,iv:AZPaRyVDOl100PvBPMeq0lt6/O5ZUhzWX5UmWNABWvM=,tag:PBReK6Ap/VHxncn0G4qtEA==,type:comment]
                #ENC[AES256_GCM,data:eYRaxgs3vGeS96+ZDV8GYrwbvsrMtnWHOtsT2045tD2mlfOD,iv:/RVNEWuBlxhhY8OlJPbS/81QJukXZu1EWnPUQwrcin4=,tag:DybgrXKGWxoRyIQOlc+UMA==,type:comment]
                #ENC[AES256_GCM,data:JXKEWGBg4eeCdeQ=,iv:K5keuEjyekf7a3q7WBOKwljsHGXRdQteJcXeeKvHo28=,tag:60VtIdsy13qSKPIEWHUUNg==,type:comment]
somelist_unencrypted:
- all elements of this list
- remain in clear text
- because of the _unencrypted suffix in the key
nested_unencrypted:
    this:
        is:
            all: going to remain in clear text
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2019-12-10T22:45:53Z'
    mac: ENC[AES256_GCM,data:WDjMv0eWcyPQzZlr3MppeAMQavN88xv5LzI/9wOlg+WPhRoTdrvgFpWowyWvTdUC/i0ybRQRg2u/Wam0kaqzMDpl/E806Gp9hgJcSneqydDJqPiMh+HpXkXWpc70xbYg8/gc1l7eIfSG7rS1dC2t2je60OAIfC/5zAXrL9KH4Ho=,iv:h0hWhb+46upix6K7hZfNNQoiX7WCapiMTv5I/keZsm4=,tag:v7mVbTN3HWmekol5iaO8FA==,type:str]
    pgp:
    -   created_at: '2019-12-10T22:45:53Z'
        enc: |-
            -----BEGIN PGP MESSAGE-----

            wcBMAyUpShfNkFB/AQgAE0MaWAQGbTKY7Xg3fDNtzlvnVBkkQRHsLt5kUTu2nAy4
            sPX0NRXPVF/tAMxr9mI2fRjKnNBXKpOAecNis85D/QEkfflG8/syGkqiJqy9Nqon
            WSm1bNriPfD4PL850688EJe49Xrsz6rVbW5FYZCHMbnPxvmoheMJRxLonW3/eWPy
            IjJ9i6Z7W175mv1y7FELOimdQeelynp4r8bOuuq1BhePB4+wJXihw9n0ovuLklpl
            Kr2iCmIUibSywlEO/LGQT/VXo6R7xgSN2Xg0RwWfflajYHNhVkHlnNYzkACjvdhj
            ph1M5fLGDqPu+ySSe93EyahNhdwKgQ7R9yF8/13+QdLgAeRmrBgh0N54mQQAQyL7
            mH/i4SWi4BjgmeHgUeBL4qcm2avg6OUasVJQHlTeA8D+c3TwKSTRVDijN4GBadYJ
            Z5/vXKOseeBk5JWCnIHC/MtjOkuPt53nvGzi3lYvW+F0FQA=
            =RDyn
            -----END PGP MESSAGE-----
        fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
    -   created_at: '2019-12-10T22:45:53Z'
        enc: |-
            -----BEGIN PGP MESSAGE-----

            wYwDXFUltYFwV4MBBABpm+tFhFhv3A7A/L/p6nL3HXKKhONrgguYgXA/hhSg4/bD
            1Po5pQhCM4yb3gqWewxgVpGNKFr/Gl+kN9eZ3LXp5nEdhei/aQn7BbWkhph5PKt6
            faiEZAL5PNHvktvEQwPsfNJvxe8QT2Z9oFmlueP0n3mCZz3UV9LZHNwOP7XfzdLg
            AeRzUGrZK43KavmIjdgPXcd/4ZW14NPgNeGm8+BO4nD26Fvgh+UiCb0TpBiI7WsX
            HQGUlFjuR6Vd7Q+vg8B/1Ovm3fUw7uCG5Gc2WeB0M+pXaLYS9bCDL9jisGOkD+Ef
            ZAA=
            =mqGc
            -----END PGP MESSAGE-----
        fp: D7229043384BCC60326C6FB9D8720D957C3D3074
    unencrypted_suffix: _unencrypted
    version: 3.5.0
0707010000004A000041ED00000000000000000000000462794F9300000000000000000000000000000000000000000000001400000000sops-3.7.3/examples0707010000004B000041ED00000000000000000000000462794F9300000000000000000000000000000000000000000000001F00000000sops-3.7.3/examples/all_in_one0707010000004C000081A400000000000000000000000162794F9300000013000000000000000000000000000000000000002A00000000sops-3.7.3/examples/all_in_one/.gitignoreconfig/secret.json
0707010000004D000081A400000000000000000000000162794F9300000C7D000000000000000000000000000000000000002A00000000sops-3.7.3/examples/all_in_one/README.rstAll-in-one example
==================
This directory is an example configuration for SOPS inside of a project. We will cover the files used and relevant scripts for developers.

This example is optimized for saving developer time by storing all secrets in a single file (e.g. ``secret.enc.json``).

One downside is any configurations which should be stored side by side might not be.

Getting started
---------------
To use this example, run the following:

.. code:: bash

    # From the `sops` root directory
    # Import the test key
    gpg --import tests/sops_functional_tests_key.asc

    # Navigate to our example directory
    cd examples/all_in_one

    # Decrypt our secrets
    bin/decrypt-config.sh

    # Optionally edit a secret
    # bin/edit-secret.sh config/secret.enc.json

    # Run a script that uses our decrypted secrets
    python main.py

Storage
-------
In both development and production, we will be storing the secrets file unencrypted on disk. This is for a few reasons:

- Can't store file in an encrypted manner because we would need to know the secret to decode it
- Loading it into memory at boot is impractical

  - Requires reimplementing SOPS' decryption logic to multiple languages which increases chance of human error which is bad for security
  - If someone uses an automatic process reloader during development, then it could get expensive with AWS

    - We could cache the results from AWS but those secrets would wind up being stored on disk

As peace of mind, think about this:

- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops --decrypt`` as well.

Files
-----
- ``bin/decrypt-config.sh`` - Script to decrypt secret file
- ``bin/edit-config-file.sh`` - Script to edit a secret file and then decrypt it
- ``config/secret.enc.json`` - Catch-all file containing our secrets
- ``config/secret.json`` - Decrypted catch-all secrets file
- ``config/static.py`` - Configuration file which imports secrets
- ``.gitignore`` - Ignore file for decrypted secret file
- ``main.py`` - Example script

Usage
-----
Development
~~~~~~~~~~~
For development, each developer must have access to the PGP/KMS keys. This means:

- If we are using PGP, then each developer must have the private key installed on their local machine
- If we are using KMS, then each developer must have AWS access to the appropriate key

Testing
~~~~~~~
For testing in a public CI, we can copy ``secret.enc.json`` to ``secret.json``. This will represent the same structure as ``secret.enc.json`` with an additional ``sops`` key but not reveal any secret information.

..

    For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``cp`` rather than ``sops --decrypt``.

For testing in a private CI where we need private information, see the `Production instructions <#production>`_.

Production
~~~~~~~~~~
For production, we have a few options:

- Build an archive (e.g. ``.tar.gz``) in a private CI which contains the secrets and deploy our service via the archive
- Install PGP private key/KMS credentials on production machine, decrypt secrets during deployment process on production machine
0707010000004E000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002300000000sops-3.7.3/examples/all_in_one/bin0707010000004F000081ED00000000000000000000000162794F9300000261000000000000000000000000000000000000003500000000sops-3.7.3/examples/all_in_one/bin/decrypt-config.sh#!/usr/bin/env bash
# Exit on first error
set -e

# Define our secret files
secret_files="secret.enc.json"

# For each of our files in our encrypted config
for file in $secret_files; do
  # Determine src and target for our file
  src_file="config/$file"
  target_file="$(echo "config/$file" | sed -E "s/.enc.json/.json/")"

  # If we only want to copy, then perform a copy
  # DEV: We allow `CONFIG_COPY_ONLY` to handle tests in Travis CI
  if test "$CONFIG_COPY_ONLY" = "TRUE"; then
    cp "$src_file" "$target_file"
  # Otherwise, decrypt it
  else
    sops --decrypt "$src_file" > "$target_file"
  fi
done
07070100000050000081ED00000000000000000000000162794F930000033F000000000000000000000000000000000000003700000000sops-3.7.3/examples/all_in_one/bin/edit-config-file.sh#!/usr/bin/env bash
# Exit on first error
set -e

# Define our secret files
secret_files="secret.enc.json"

# Look up our file
filepath="$1"
if test "$filepath" = ""; then
  echo "Expected \`filepath\` but received nothing" 1>&2
  echo "Usage: $0 <filepath>" 1>&2
  exit 1
fi

# If our file is a secret
filename="$(basename "$filepath")"
if echo "$secret_files" | grep "$filename"; then
  # Load it into SOPS and run our sync script
  sops "$filepath"
  bin/decrypt-config.sh
# Otherwise (it's a normal file)
else
  # Resolve our editor via `sops` logic
  editor="$EDITOR"
  if test "$editor" = ""; then
    editor="$(which vim nano | head -n 1)"
  fi
  if test "$editor" = ""; then
    echo "Expected \`EDITOR\` environment variable to be defined but it was not" 1>&2
    exit 1
  fi

  # Edit our file
  "$editor" "$filepath"
fi
07070100000051000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002600000000sops-3.7.3/examples/all_in_one/config07070100000052000081A400000000000000000000000162794F9300000000000000000000000000000000000000000000003200000000sops-3.7.3/examples/all_in_one/config/__init__.py07070100000053000081A400000000000000000000000162794F930000073B000000000000000000000000000000000000003600000000sops-3.7.3/examples/all_in_one/config/secret.enc.json{
	"github_oauth_token": "ENC[AES256_GCM,data:lVe1fLoILE8e/jiah90=,iv:6g350p9Id8+ssrV5lVEF4TWEnR+JfY84iUsh92/9XIQ=,tag:2qojKddpx5lO1iR2dqLZJg==,type:str]",
	"sops": {
		"kms": null,
		"gcp_kms": null,
		"azure_kv": null,
		"lastmodified": "2019-12-10T22:46:40Z",
		"mac": "ENC[AES256_GCM,data:XUVZm8UI26QEbhlIkQDfEWcyhooh/bGQ2eHOTu/ojbE4SBLRpcLtKHt2Bn8Z/72pLV77FmmSXAIwy8+xvzeTmEAZNRMfjrm0ZulBY1VgJLTrvA4Hh9F7H+Pb95xtc8B9SbFAo65L+xBJJ/e6t8QsV0N4crOoq0Su3OuvqXIVa90=,iv:1Vnq304ic+vPZ0as0xLpaO8dMduMrF3Xh9aAHT+VLiA=,tag:37+ami7Z3tQd1EwZKC7sPg==,type:str]",
		"pgp": [
			{
				"created_at": "2019-12-10T22:46:40Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgATTwZYaZdIVFnJrzujQvk5x/SqVc5VY367UtStph9pxn5\nkSh2lvPrYHtkbpPkvv5Ug5CUwUbofq4rj3YSO3kVSDu5Z3IrAT+EtnK5PECg2JDT\nIXskkjFegQYp4YtC5UUhjIVIALti/9b3g2skVES/IHHlL+BE9AWj93yF24weu54D\nbwF6PQyGRE7HhZ8uUWEKIGTbJbpDzySk06LJSF33uNbKUViRtHuLu4JNFb22sjzy\nMKzITct/4KVlHAkC4ZHpDlsXmtooBJ8Yk7DK2vEM1POk2pE6ZBQbEgtY0hAOMFGB\nfg+j6cvNsLYWyQrjaB9Vsf4+QPDeTWuv5+cJjvgildLgAeQssqFSnZXumtz/VgB1\nm7Ma4Rov4DLgAeHkV+DJ4vTaGavgHuVj+a53Oa9HuX4p1rNV3ZLUyJi6HmNOWjvw\np8QFzrcRyOCN5N3VkrUyEK9l03NczoI9nt7i6r8jBeHWTQA=\n=Uxc1\n-----END PGP MESSAGE-----",
				"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
			},
			{
				"created_at": "2019-12-10T22:46:40Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBAA5g2pnnh43zql0b1bOa6zw0S6uMSLioFy5Or4Z+aEhywpe\n94unRNqABQdqCtowoUpjJ4vEHxQ+dkHPXpYJejolBKPaKpHJHLrdImkC9nb62jJ8\nthWFitj8ELQnHdmSk+t5kvaNj4nWXSuTTiXd2UodXZcHxH4UI4P7A4aZXKTYytLg\nAeRHhnSBOBYD0rMvFIWKLcR94SZE4LrgluHLYODT4l6Ln4fg5uX2CzKsP/KD4+jI\nPDvITr4Sdnh6m6XEctb4whn0OOKXo+Ca5OJ75/+Kb9SzSbA24uXJ1qriELpTt+Hk\nawA=\n=SIw4\n-----END PGP MESSAGE-----",
				"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
			}
		],
		"unencrypted_suffix": "_unencrypted",
		"version": "3.5.0"
	}
}07070100000054000081A400000000000000000000000162794F9300000104000000000000000000000000000000000000003000000000sops-3.7.3/examples/all_in_one/config/static.py# Load in our dependencies
import json

# Load in our secrets
with open('config/secret.json', 'r') as file:
    secret = json.loads(file.read())

# Define our configuration
common = {
    'github_oauth_token': secret['github_oauth_token'],
    'port': 8080,
}
07070100000055000081A400000000000000000000000162794F93000001CA000000000000000000000000000000000000002700000000sops-3.7.3/examples/all_in_one/main.py# Load in our dependencies
from __future__ import absolute_import
from config.static import common


# Define our main function
def main():
    # Output our configuration
    print('Configuration')
    print('-------------')
    for key in common:
        # Example: `port: "8080"`
        print('{key}: "{val}"'.format(key=key, val=common[key]))


# If this script is being invoked directly, then run our main function
if __name__ == '__main__':
    main()
07070100000056000041ED00000000000000000000000462794F9300000000000000000000000000000000000000000000001D00000000sops-3.7.3/examples/per_file07070100000057000081A400000000000000000000000162794F9300000012000000000000000000000000000000000000002800000000sops-3.7.3/examples/per_file/.gitignoreconfig
config.bak
07070100000058000081A400000000000000000000000162794F9300000D83000000000000000000000000000000000000002800000000sops-3.7.3/examples/per_file/README.rstPer-file example
================
This directory is an example configuration for SOPS inside of a project. We will cover the files used and relevant scripts for developers.

This example is optimized for storing sensitive information next to related non-sensitive information (e.g. password next to username).

The downsides include:

- Slowing down developers by requiring usage of SOPS for non-sensitive information
- Losing dynamic configurations that rely on reusing variables (e.g. ``test = {'foo': {'bar': common['foo']['bar'], 'baz': false}}``)

  - There might be work arounds via YAML

Getting started
---------------
To use this example, run the following

.. code:: bash

    # From the `sops` root directory
    # Import the test key
    gpg --import pgp/sops_functional_tests_key.asc

    # Navigate to our example directory
    cd examples/per_file

    # Decrypt our secrets
    bin/decrypt-config.sh

    # Optionally edit a secret
    # bin/edit-secret.sh config.enc/static_github.json

    # Run our script
    python main.py

Storage
-------
In both development and production, we will be storing the secrets file unencrypted on disk. This is for a few reasons:

- Can't store file in an encrypted manner because we would need to know the secret to decode it
- Loading it into memory at boot is impractical

  - Requires reimplementing SOPS' decryption logic to multiple languages which increases chance of human error which is bad for security
  - If someone uses an automatic process reloader during development, then it could get expensive with AWS

    - We could cache the results from AWS but those secrets would wind up being stored on disk

As peace of mind, think about this:

- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops --decrypt`` as well.

Files
-----
- ``bin/decrypt-config.sh`` - Script to decrypt secret file
- ``bin/edit-config-file.sh`` - Script to edit a secret file and then decrypt it
- ``config`` - Directory containing decrypted secrets
- ``config.bak`` - Backup of ``config`` to prevent accidental data loss
- ``config.enc`` - Directory containing encrypted secrets

  - ``static.py`` - Python script to merge together secrets
  - ``static_github.json`` - File containing secrets

- ``.gitignore`` - Ignore file for ``config`` and ``config.bak``
- ``main.py`` - Example script

Usage
-----
Development
~~~~~~~~~~~
For development, each developer must have access to the PGP/KMS keys. This means:

- If we are using PGP, then each developer must have the private key installed on their local machine
- If we are using KMS, then each developer must have AWS access to the appropriate key

Testing
~~~~~~~
For testing in a public CI, we can copy ``config.enc`` to ``config``. The secret files will have structure with an additional ``sops`` key but not reveal any secret information.

..

    For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``ln -s`` rather than ``sops --decrypt``.

For testing in a private CI where we need private information, see the `Production instructions <#production>`_.

Production
~~~~~~~~~~
For production, we have a few options:

- Build an archive (e.g. ``.tar.gz``) in a private CI which contains the secrets and deploy our service via the archive
- Install PGP private key/KMS credentials on production machine, decrypt secrets during deployment process on production machine
07070100000059000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002100000000sops-3.7.3/examples/per_file/bin0707010000005A000081ED00000000000000000000000162794F930000032A000000000000000000000000000000000000003300000000sops-3.7.3/examples/per_file/bin/decrypt-config.sh#!/usr/bin/env bash
# Exit on first error
set -e

# Define our secret extenssion
secret_ext=".json"

# If there is a config directory, then move it to a backup
if test -d config; then
  if test -d config.bak; then
    rm -r config.bak
  fi
  mv config/ config.bak/
fi

# Create our new config directory
mkdir config

# For each of our files in our encrypted config
for src_file in config.enc/*; do
  # Determine target for our file
  src_filename="$(basename "$src_file")"
  target_file="config/$src_filename"

  # If the file is our secret, then decrypt it
  if echo "$src_filename" | grep -E "${secret_ext}$" &&
      test "$CONFIG_COPY_ONLY" != "TRUE"; then
    sops --decrypt "$src_file" > "$target_file"
  # Otherwise, symlink to the original file
  else
    ln -s "../$src_file" "$target_file"
  fi
done
0707010000005B000081ED00000000000000000000000162794F9300000139000000000000000000000000000000000000003500000000sops-3.7.3/examples/per_file/bin/edit-config-file.sh#!/usr/bin/env bash
# Exit on first error
set -e

# Localize our filepath
filepath="$1"
if test "$filepath" = ""; then
  echo "Expected \`filepath\` but received nothing" 1>&2
  echo "Usage: $0 <filepath>" 1>&2
  exit 1
fi

# Load our file into SOPS and run our sync script
sops "$filepath"
bin/decrypt-config.sh
0707010000005C000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002800000000sops-3.7.3/examples/per_file/config.enc0707010000005D000081A400000000000000000000000162794F9300000000000000000000000000000000000000000000003400000000sops-3.7.3/examples/per_file/config.enc/__init__.py0707010000005E000081A400000000000000000000000162794F93000007DF000000000000000000000000000000000000003200000000sops-3.7.3/examples/per_file/config.enc/static.py# Load in our dependencies
import json

# Define our configuration
# DEV: THE FOLLOWING CONFIGURATIONS SHOULD NOT CONTAIN ANY SECRETS
#   THIS FILE IS NOT ENCRYPTED!!
common = {
    'port': 8080,
}

development = {

}

test = {

}

production = {

}

config = {
    'common': common,
    'development': development,
    'test': test,
    'production': production,
}


def walk(item, fn):
    """Traverse dicts and lists to update keys via `fn`"""
    # If we are looking at a dict, then traverse each of its branches
    if isinstance(item, dict):
        for key in item:
            # Walk our value
            walk(item[key], fn)

            # If we are changing our key, then update it
            new_key = fn(key)
            if new_key != key:
                item[new_key] = item[key]
                del item[key]
    # Otherwise, if we are looking at a list, walk each of its items
    elif isinstance(item, list):
        for val in item:
            walk(val, fn)


# Merge all of our static secrets onto our config
# For each of our secrets
secret_files = [
    'config/static_github.json',
]
for secret_file in secret_files:
    with open(secret_file, 'r') as file:
        # Load and parse our JSON
        data = json.loads(file.read())

        # Strip off `_unencrypted` from all keys
        walk(data, lambda key: key.replace('_unencrypted', ''))

        # For each of the environments
        for env_key in data:
            # Load in the respective source and target
            env_src = data[env_key]
            env_target = config[env_key]

            # Merge info between configs
            for key in env_src:
                if key in env_target:
                    raise AssertionError(
                        'Expected "{env_key}.{key}" to not be defined already '
                        'but it was. '
                        'Please verify no configs are using the same key'
                        .format(env_key=env_key, key=key))
            env_target.update(env_src)
0707010000005F000081A400000000000000000000000162794F930000093F000000000000000000000000000000000000003B00000000sops-3.7.3/examples/per_file/config.enc/static_github.json{
	"common": {
		"github_username_unencrypted": "twolfson-dev",
		"github_password": "ENC[AES256_GCM,data:wtyRyrcYh1AaoA==,iv:lJKpAdaRjC4jAl+wIT6ozSZanSIEJwEyJkLi0aFHlGg=,tag:NrrARDWsoWqOyQ+oyALHAA==,type:str]"
	},
	"development": {},
	"test": {
		"github_username_unencrypted": "test-user",
		"github_password": "ENC[AES256_GCM,data:To6eP3uCm+IQo6KCBalF2Q+7zc4=,iv:GZz08Kz9fjCsIk1edHVZRYgSzfF9smzGbllOU6op+z8=,tag:Zrp5RNck7DXCVffX/zmWIQ==,type:str]"
	},
	"production": {
		"github_username_unencrypted": "twolfson",
		"github_password": "ENC[AES256_GCM,data:AZ+Yef+hheVBQWtQ6Ys=,iv:RMd4iwMCiNkt2zPyLBakdA9+HaJA9hh2qUbbdNcemLo=,tag:ejPV/E3dwubZl6s1NyZnSA==,type:str]"
	},
	"sops": {
		"kms": null,
		"gcp_kms": null,
		"azure_kv": null,
		"lastmodified": "2019-12-10T22:47:04Z",
		"mac": "ENC[AES256_GCM,data:rBKvMg8FroPuKB03JEzRvlnONsca7ByBqoTB18kjXHwr7mmnBYLQP4mWiYPRRRaBDaGtDXx3FH+Igyo84vPwNO2eEN94gv1NpeGqPI1vRUwAkCB3UE3hIQN22zXBEwWO4sUPKzERuG8t5dFF7xO4EyzweZlC+nwtyQUQX78ysGo=,iv:vymJO0wSb+/W4LUiZi9+ov9YVy3la+BopXiqd2X/mbA=,tag:Pm7NolfYB7ELKjHaGdbauQ==,type:str]",
		"pgp": [
			{
				"created_at": "2019-12-10T22:47:04Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgAJAeWCsS3nqQ+uDy5gj7Fm7evXn/gY5DW7AWJjkXmHqwx\n+IHVERj9JsfFApoUCPu4Om/KB67uegLWk/GFmAtHcwV2GrsGt1AAIURdNSwYf8P7\nLix9fpRwAUlamXS0wDbGQQyNX3X1rIMu7PnL3KLzvfBQUHaVs3+4GpNXjzUVRGgb\nmjtCTsH3u1VYiHrRz0uoWisPDlvn60fPv0mcYdu17sVpgPnC0qmHh6UJd85dGzfy\nSOisauKYkirOfLSJyVCItFD0GwPVd3mbEcxmIokHmaIznck/Tto3sXcQO4Amc54+\nCjXhk0f/KJbKQQg4mbZMaEMISE+/s3H4mBmk2VADJNLgAeS5csP9GjYrFOAHsgGR\nrDIC4SMd4DTgmuEYSOCh4qLAvJfgmOXA0EoByddCKyrCIHDdyq+lFOQq1Hk+kfl/\nruUZSgCy9OCo5FerieGdW/fWfWsKx2CGKczi0Q/ivuFb2QA=\n=rw7+\n-----END PGP MESSAGE-----",
				"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
			},
			{
				"created_at": "2019-12-10T22:47:04Z",
				"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBACeF7HYThUNm0osc9SEASAnYLB0/qp6IdA6dc0aNHBZxM3Z\nxiOmbGOfImgwWMDGFP3GYFe3NNt/imzy0V+umVWMYxLYb9Hm3yCdVSb6ks9xJwkp\nXWD7vLXXEuVRsLp+IsmMXSRRJ/shBLIORiOMIAiSv92yluNT8PZF3Nv1hA56Y9Lg\nAeR2GEqpdbw6/tIUPMJUHWQf4XZ14DHgkOF9beAN4qqnf1zgsuVA+YmakiS7L5iY\nA/4OMVnCDJBKTD+l91ID5d4k2bQoU+Dm5AmJqm+I6sX6nViQUgyEyjvi+aeOuuG0\nDQA=\n=0KIM\n-----END PGP MESSAGE-----",
				"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
			}
		],
		"unencrypted_suffix": "_unencrypted",
		"version": "3.5.0"
	}
}07070100000060000081A400000000000000000000000162794F9300000377000000000000000000000000000000000000002500000000sops-3.7.3/examples/per_file/main.py# Load in our dependencies
from __future__ import absolute_import
from config.static import config


# Define our main function
def main():
    # Output our configuration
    # DEV: We use custom keys for a custom sort
    print('Configuration')
    print('=============')
    for env_key in ('common', 'development', 'test', 'production'):
        # Example: `Environment: common`
        # Example: `-------------------`
        env_str = 'Environment: {env_key}'.format(env_key=env_key)
        print(env_str)
        print(''.join(['-' for char in env_str]))

        env_config = config[env_key]
        for key in sorted(env_config.keys()):
            # Example: `port: "8080"`
            print('{key}: "{val}"'.format(key=key, val=env_config[key]))
        print('')


# If this script is being invoked directly, then run our main function
if __name__ == '__main__':
    main()
07070100000061000041ED00000000000000000000000562794F9300000000000000000000000000000000000000000000001C00000000sops-3.7.3/functional-tests07070100000062000081A400000000000000000000000162794F9300000401000000000000000000000000000000000000002700000000sops-3.7.3/functional-tests/.sops.yamlcreation_rules:
    - path_regex: test_roundtrip_keygroups.yaml
      key_groups:
      - pgp:
        - FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
      - pgp:
        - D7229043384BCC60326C6FB9D8720D957C3D3074
    - path_regex: test_roundtrip_keygroups_missing_decryption_key.yaml
      key_groups:
      - pgp:
        - FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
      - pgp:
        - B611A2F9F11D0FF82568805119F9B5DAEA91FF86
    - pgp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
destination_rules:
    - s3_bucket: "sops-publish-functional-tests"
      s3_prefix: "functional-test/"
      path_regex: test_encrypt_publish_s3.json
      reencryption_rule:
        pgp: B611A2F9F11D0FF82568805119F9B5DAEA91FF86
    - vault_path: "functional-test/"
      vault_kv_mount_name: "secret/"
      vault_kv_version: 2
      path_regex: test_encrypt_publish_vault.json
    - vault_path: "functional-test-version-1/"
      vault_kv_mount_name: "kv/"
      vault_kv_version: 1
      path_regex: test_encrypt_publish_vault_version_1.json
07070100000063000081A400000000000000000000000162794F93000000EE000000000000000000000000000000000000002700000000sops-3.7.3/functional-tests/Cargo.toml[package]
name = "functional-tests"
version = "0.1.0"
authors = ["Adrian Utrilla <adrianutrilla@gmail.com>"]

[dependencies]
tempdir = "0.3.5"
serde = "1.0"
serde_json = "0.8"
serde_yaml = "0.5"
serde_derive = "1.0"
lazy_static = "0.1.*"
07070100000064000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002000000000sops-3.7.3/functional-tests/bin07070100000065000081A400000000000000000000000162794F9300000000000000000000000000000000000000000000002A00000000sops-3.7.3/functional-tests/bin/editor.rs07070100000066000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002000000000sops-3.7.3/functional-tests/res07070100000067000081A400000000000000000000000162794F930000070B000000000000000000000000000000000000003200000000sops-3.7.3/functional-tests/res/comments.enc.yaml#ENC[AES256_GCM,data:IYA+b4ORDq8u9CBQolipWD4HRqoZyA==,iv:F8ldQqGng+WptHuBkFtjrGM+7sRZCsvd0FHq98lrpAE=,tag:ZHbLU9+CELinf5PhhuIzSQ==,type:comment]
lorem: ENC[AES256_GCM,data:PhmSdTs=,iv:J5ugEWq6RfyNx+5zDXvcTdoQ18YYZkqesDED7LNzou4=,tag:0Qrom6J6aUnZMZzGz5XCxw==,type:str]
#ENC[AES256_GCM,data:HiHCasVRzWUiFxKb3X/AcEeM,iv:bmNg+T91dqGk/CEtVH+FDC53osDCEPmWmJKpLyAU5OM=,tag:bTLDYxQSAfYDCBYccoUokQ==,type:comment]
dolor: ENC[AES256_GCM,data:IgvT,iv:wtPNYbDTARFE810PH6ldOLzCDcAjkB/dzPsZjpgHcko=,tag:zwE8P+AwO1hrHkgF6pTbZw==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    age: []
    lastmodified: '2020-10-07T15:49:13Z'
    mac: ENC[AES256_GCM,data:2dhyKdHYSynjXPwYrn9356wA7vRKw+T5qwBenI2vZrgthpQBOCQG4M6f7eeH3VLTxB4mN4CAchb25dsNRoGr6A38VruaSSAhPco3Rh4AlvKSvXuhgRnzZvNxE/bnHX1D4K5cdTb4FsJg/Ue1l7UcWrlrv1s3H3SwLHP/nf+suD0=,iv:6xBYURjjaQzlUOKOrs2NWOChiNFZVAGPJZQZ59MwX3o=,tag:uXD5VYme+c8eHcCc5TD2YA==,type:str]
    pgp:
    -   created_at: '2019-08-29T21:52:32Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQEMAyUpShfNkFB/AQgAlvpTj0NYqF4mQyIeM7wX2SHLb4U07/flpqDpp2W/30Pz
            AHA7sYrgP0l8BrjT2kwtgCN0cdfoIHJudezrNjANp2P5TbP2b9kYYNxpehzB9PFj
            FixnCS7Zp8WIt1yXr1TX+ANZoXLopVcRbMaQ5OdH7CN1pNQtMR+R3FR3X/IqKxiU
            Do1YLaooRJICUC8LJw2Tb4K+lYnTSqd/HalLGym++ivFvdDB1Ya1GhT1FswXidXK
            IRjsOVbxV0q5VeNOR0zxsheOvuHyCje16c7NXJtATJVWtTFABJB8u7CY5HhZSgq+
            rXJHyLHqVLzJ8E4WqHQkMNUlVcrqAz7glZ6xbAhfI9JeAYk5SuBOQOQ4yvASqH4K
            b0N3+/abluBY7YPqKuRZBiEtmcYlZ+zIHuOTP1rD/7L5VY8CwE5U8SFlEqwM7nQJ
            6/vtl6qngOFjwt34WrhZzUfLPB/wRV/m1Qv2kr0RNA==
            =Ykiw
            -----END PGP MESSAGE-----
        fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
    unencrypted_suffix: _unencrypted
    version: 3.6.1
07070100000068000081A400000000000000000000000162794F9300000043000000000000000000000000000000000000002E00000000sops-3.7.3/functional-tests/res/comments.yaml# first comment in file
lorem: ipsum
# this-is-a-comment
dolor: sit07070100000069000081A400000000000000000000000162794F9300000025000000000000000000000000000000000000003300000000sops-3.7.3/functional-tests/res/comments_list.yamllorem:
- foo
#this-is-a-comment
- bar0707010000006A000081A400000000000000000000000162794F9300000605000000000000000000000000000000000000004300000000sops-3.7.3/functional-tests/res/comments_unencrypted_comments.yaml# first comment in file
lorem: ENC[AES256_GCM,data:qVz4paM=,iv:0oGsaw71i3wZKmlyDl8uDhQT9XLvJt3oIyx514X44K8=,tag:acbMS613StWo1IVnKK+5uQ==,type:str]
# this-is-a-comment
dolor: ENC[AES256_GCM,data:21fI,iv:01LXdHZYwLTeyUB1YWIAM6KF8cPPVsw/RuQO+Ab4pgM=,tag:o1xnCIIoccWzdWxB2kZYKg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2019-08-29T22:42:03Z'
    mac: ENC[AES256_GCM,data:xKkcsqrAHxyqwgv+IVqx52AmrJdC607Dc/Ughna2e2UnnHteXTw7LGt4d0sSlw8LgjaXpa+T6lQ0MgnMPjgTEa20lbtVtauCDdRCnR7Z/Vdk7t7uLl94+STD7C1H6obnOe4fG6c2cUfDNHoeABLetti2ZBZOSZDkWQeCucLys/s=,iv:k9ODJLNYsBedQKMHcgn0KUXPlunOq5jDFH9BeJOyYRE=,tag:d+Ga71V+gijiWLFZ7BhQgg==,type:str]
    pgp:
    -   created_at: '2019-08-29T22:42:03Z'
        enc: |-
            -----BEGIN PGP MESSAGE-----

            wcBMAyUpShfNkFB/AQgAMgUtep3vurVXoOI1h4Ovr3YbYz+gRHAPGMAUdXQdX7az
            fmh7Eq+6Eye2wpnAaogJ2RtIoYO1F/jkoQO74mgaLXNu/gtr8b6Ejc61sQSyjnjg
            N9I50+Bh075TqzToZTo7gOwOlltMjA/UVGD0z+gPHP1MpcVpUvm7C0Ol5L0Co37O
            UvLrQJjw1x6ktAawWokw910iX9usXiTj87fYvaqutNKRBfh1LI0Os2H8C9xpJu7q
            I60NcqF7JQbAaaumRroQpF2K7RI1nt+qTshPqWzDLfjzlrCVPnJlxludqvHUKzR3
            TnJdb4Dsx3o/XEQkZxpl7RZa9SZZuKg3EejTxOj0ZtLgAeRLzLl0gSeMGvkntASq
            NQkM4e1p4AjgJOEqrOCw4h0xtdrg9+UczwVpI4rnibRqJZtEhFHRaupX6leAEi1l
            FMikSjpGSOB55PVMt6+e4ruokoulnq32VRTi07691uEeowA=
            =ft04
            -----END PGP MESSAGE-----
        fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
    unencrypted_suffix: _unencrypted
    version: 3.3.1
0707010000006B000081A400000000000000000000000162794F93000007B5000000000000000000000000000000000000003300000000sops-3.7.3/functional-tests/res/multiple_keys.yamlmessage: ENC[AES256_GCM,data:4soxy1Q=,iv:GGOFFLu8aqMEyoOBdMGdevug7E0R8H0iDyXIKQ5ufzM=,tag:954eipkbSu2U64tx2usiBA==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2019-08-29T22:22:04Z'
    mac: ENC[AES256_GCM,data:Rcyh6DKgpd8WNvLqnPhMRYDGztK/WjNJ+SisCD4qL50wxiYUgwRGs5S9I8kOFm4+8pruOcQyCVIDzyjQHZfM1bKs+CnMJTrp1HDz2vhIKkE8O6EdE5+JbNxwc6ZFQE6YThmx6rz93gMHnDOcrO0XG2guGxTumM5xPkZsEBfpIyQ=,iv:YI0Yc2khjy+q1BxiEBDejgymyAQmBkdyl3fQUm76/1Y=,tag:pf4ZbMy9TrpTA/B2qVEkmw==,type:str]
    pgp:
    -   created_at: '2019-08-29T22:22:04Z'
        enc: |-
            -----BEGIN PGP MESSAGE-----

            wcBMAyUpShfNkFB/AQgAQH8KFrXipAGaHsKjQecpZ4R5PsWg4nqetgsy6FTcAI3j
            KwvBoXapI+2m5aXjAdO4k76ZxGyyXXdwe6ebbuikxCuDl28m86o9xNxbBv5SxbaO
            gTxtUR+swcnYRyfVmkd07HhATXQw7b2q4ERNJAVH9zxJcmzNzP6q9eRjePRWXSjn
            xxzpnS9kQkevuzJXg5dhVZ9HnsuVEthK88c9H3ZZQt/kg3GLaj2XDhFUt9Xy3POl
            3Q2dE8xzVXzg5rF+0n8H0WpiRJ7hbvW6kvAanKNUDeNLY2shmkKiIzh4W0f2Vl5Q
            W1nw23Tga6IQ6mSqLItZB5bYX/C9K33kceV1DYyW+tLgAeTofEdQuueh2myAYmmV
            Wjap4eAq4F/gi+EpuOAS4jDaEhPgY+UkojNLl9ow2EkyrIMMJCKDSzuZcmZhwTYl
            7/RSqGHDHuDX5GvXVgnRiqnoDiPsImCDN5/iNmoSVuG9cgA=
            =wVy4
            -----END PGP MESSAGE-----
        fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
    -   created_at: '2019-08-29T22:22:04Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hIwDXFUltYFwV4MBA/9/qqKDEbT9oZJqB7Z7R1RAz+TStVUzlE9+M0ScFuguBUpd
            7doLhhgBYa9ygti5pnAN4E1SI97xPhflGZLTp4OtdTg5UGOUwh6BM0BZI1ai6dlK
            u4ra0BKOmDzv9SkbTngStXfPPLC9msc6sZe9TER88puqNAZ4DBrTyTtEAmg5wtJe
            AecV7hxDzAGleAdmaTC+WHkvg/EgwY/9PaU1IMf6tCx8O/bS8JQhxmptO1aAm5Vr
            dO/SV2m9QStMafO6OG1aGpy3zkVytlxGavkUK+dPp5fYbRYGQVfUiGpgRRZEZg==
            =NlDf
            -----END PGP MESSAGE-----
        fp: D7229043384BCC60326C6FB9D8720D957C3D3074
    unencrypted_suffix: _unencrypted
    version: 3.3.1
0707010000006C000081A400000000000000000000000162794F9300000456000000000000000000000000000000000000002C00000000sops-3.7.3/functional-tests/res/no_mac.yamlmyapp1: ENC[AES256_GCM,data:NjYyOQ1GyFUhy25XX7cz7CZo4tLt,iv:WeWmnZCG7/pm71VMd37ua3LsZ6NnqQzLAyN8ylfYHb8=,tag:zh2hOpxHZ8ON+sJgB+OnTg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2019-08-29T22:23:01Z'
    pgp:
    -   created_at: '2019-08-29T22:23:00Z'
        enc: |-
            -----BEGIN PGP MESSAGE-----

            wcBMAyUpShfNkFB/AQgABuzcPMJSK+DvnPKZVyYlmDLCM9BobLz/7/F8zyX4O00T
            UN7/0Lc+MwZWUOe7lmTVgorDLcfxLONbLg9nxXO+apbb2gRABRYdggosMFaRUn3+
            m+maKQ4yDTIjIvblVa/olN8reD4Bt037mT52IxTZBLDAnyVw45dRTe9mzJ73eTi7
            1Mk5s6s1ZH52SQHtbY4TSqCnOKodn3UkTjnFmDXMSAzCwBDmK/wv+fDufXSkLpeR
            AGufxo697as2bQ8mpqgPPTvB04MgXwnTngUQ8BfagyOndgTCrU9YMA1gewJnJJj4
            azRP9SOukqtn0c/LpewiOHHk/w00dMLEk0E8DpxMItLgAeQQGaX95PJfl3vuPeRT
            2CA24Qbb4DbgseGimeBG4oS0oCLg4OVDd1tvVtu4+6VGKJr8YvgDk7WpDAK33LoO
            QBWfQ5mPIeAN5GVWJ2Mk1qcSQTlO9LmM1vPiWojLjOEvZAA=
            =5Gax
            -----END PGP MESSAGE-----
        fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
    unencrypted_suffix: _unencrypted
    version: 3.3.1
0707010000006D000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002000000000sops-3.7.3/functional-tests/src0707010000006E000081A400000000000000000000000162794F9300005CEC000000000000000000000000000000000000002700000000sops-3.7.3/functional-tests/src/lib.rsextern crate tempdir;
extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;

#[cfg(test)]
mod tests {
    extern crate serde;
    extern crate serde_json;
    extern crate serde_yaml;

    use std::env;
    use std::fs::File;
    use std::io::{Write, Read};
    use tempdir::TempDir;
    use std::process::Command;
    use serde_yaml::Value;
    use std::path::Path;
    const SOPS_BINARY_PATH: &'static str = "./sops";
    const KMS_KEY: &'static str = "FUNCTIONAL_TEST_KMS_ARN";

    macro_rules! assert_encrypted {
        ($object:expr, $key:expr) => {
            assert!($object.get(&$key).is_some());
            match *$object.get(&$key).unwrap() {
                Value::String(ref s) => {
                   assert!(s.starts_with("ENC["), "Value is not encrypted");
                }
                _ => panic!("Value under key was not a string"),
            }
        }
    }

    lazy_static! {
        static ref TMP_DIR: TempDir = TempDir::new("sops-functional-tests")
            .expect("Unable to create temporary directory");
    }

    fn prepare_temp_file(name: &str, contents: &[u8]) -> String {
        let file_path = TMP_DIR.path().join(name);
        let mut tmp_file = File::create(file_path.clone()).expect("Unable to create temporary file");
        tmp_file.write_all(&contents)
            .expect("Error writing to temporary file");
        file_path.to_string_lossy().into_owned()
    }

    #[test]
    fn encrypt_json_file() {
        let file_path = prepare_temp_file("test_encrypt.json",
                                          b"{
    \"foo\": 2,
    \"bar\": \"baz\"
}");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "sops didn't exit successfully");
        let json = &String::from_utf8_lossy(&output.stdout);
        let data: Value = serde_json::from_str(json).expect("Error parsing sops's JSON output");
        match data.into() {
            Value::Mapping(m) => {
                assert!(m.get(&Value::String("sops".to_owned())).is_some(),
                        "sops metadata branch not found");
                assert_encrypted!(&m, Value::String("foo".to_owned()));
                assert_encrypted!(&m, Value::String("bar".to_owned()));
            }
            _ => panic!("sops's JSON output is not an object"),
        }
    }

    #[test]
    #[ignore]
    fn publish_json_file_s3() {
        let file_path = prepare_temp_file("test_encrypt_publish_s3.json",
                                          b"{
    \"foo\": 2,
    \"bar\": \"baz\"
}");
        assert!(Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops")
            .status
            .success(),
            "SOPS failed to encrypt a file");
        assert!(Command::new(SOPS_BINARY_PATH)
            .arg("publish")
            .arg("--yes")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops")
            .status
            .success(),
            "sops failed to publish a file to S3");

        //TODO: Check that file exists in S3 Bucket
    }

    #[test]
    fn publish_json_file_vault() {
        let file_path = prepare_temp_file("test_encrypt_publish_vault.json",
                                          b"{
    \"foo\": 2,
    \"bar\": \"baz\"
}");
        assert!(Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops")
            .status
            .success(),
            "SOPS failed to encrypt a file");
        assert!(Command::new(SOPS_BINARY_PATH)
            .arg("publish")
            .arg("--yes")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops")
            .status
            .success(),
            "sops failed to publish a file to Vault");

        //TODO: Check that file exists in Vault
    }

    #[test]
    fn publish_json_file_vault_version_1() {
        let file_path = prepare_temp_file("test_encrypt_publish_vault_version_1.json",
                                          b"{
    \"foo\": 2,
    \"bar\": \"baz\"
}");
        assert!(Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops")
            .status
            .success(),
            "SOPS failed to encrypt a file");
        assert!(Command::new(SOPS_BINARY_PATH)
            .arg("publish")
            .arg("--yes")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops")
            .status
            .success(),
            "sops failed to publish a file to Vault");

        //TODO: Check that file exists in Vault
    }

    #[test]
    #[ignore]
    fn encrypt_json_file_kms() {
        let kms_arn = env::var(KMS_KEY).expect("Expected $FUNCTIONAL_TEST_KMS_ARN env var to be set");

        let file_path = prepare_temp_file("test_encrypt_kms.json",
                                          b"{
    \"foo\": 2,
    \"bar\": \"baz\"
}");

        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--kms")
            .arg(kms_arn)
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "sops didn't exit successfully");
        let json = &String::from_utf8_lossy(&output.stdout);
        let data: Value = serde_json::from_str(json).expect("Error parsing sops's JSON output");
        match data.into() {
            Value::Mapping(m) => {
                assert!(m.get(&Value::String("sops".to_owned())).is_some(),
                        "sops metadata branch not found");
                assert_encrypted!(&m, Value::String("foo".to_owned()));
                assert_encrypted!(&m, Value::String("bar".to_owned()));
            }
            _ => panic!("sops's JSON output is not an object"),
        }
    }

    #[test]
    fn encrypt_yaml_file() {
        let file_path = prepare_temp_file("test_encrypt.yaml",
                                          b"foo: 2
bar: baz");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "sops didn't exit successfully");
        let json = &String::from_utf8_lossy(&output.stdout);
        let data: Value = serde_yaml::from_str(&json).expect("Error parsing sops's JSON output");
        match data.into() {
            Value::Mapping(m) => {
                assert!(m.get(&Value::String("sops".to_owned())).is_some(),
                        "sops metadata branch not found");
                assert_encrypted!(&m, Value::String("foo".to_owned()));
                assert_encrypted!(&m, Value::String("bar".to_owned()));
            }
            _ => panic!("sops's YAML output is not a mapping"),
        }
    }

    #[test]
    fn set_json_file_update() {
        let file_path = prepare_temp_file("test_set_update.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
        Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--set")
            .arg(r#"["a"] {"aa": "aaa"}"#)
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        println!("stdout: {}, stderr: {}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        let mut s = String::new();
        File::open(file_path).unwrap().read_to_string(&mut s).unwrap();
        let data: Value = serde_json::from_str(&s).expect("Error parsing sops's JSON output");
        if let Value::Mapping(data) = data {
            let a = data.get(&Value::String("a".to_owned())).unwrap();
            if let &Value::Mapping(ref a) = a {
                assert_encrypted!(&a, Value::String("aa".to_owned()));
                return;
            }
        }
        panic!("Output JSON does not have the expected structure");
    }

    #[test]
    fn set_json_file_insert() {
        let file_path = prepare_temp_file("test_set_insert.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
        Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--set")
            .arg(r#"["c"] {"cc": "ccc"}"#)
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        println!("stdout: {}, stderr: {}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        let mut s = String::new();
        File::open(file_path).unwrap().read_to_string(&mut s).unwrap();
        let data: Value = serde_json::from_str(&s).expect("Error parsing sops's JSON output");
        if let Value::Mapping(data) = data {
            let a = data.get(&Value::String("c".to_owned())).unwrap();
            if let &Value::Mapping(ref a) = a {
                assert_encrypted!(&a, Value::String("cc".to_owned()));
                return;
            }
        }
        panic!("Output JSON does not have the expected structure");
    }


    #[test]
    fn set_yaml_file_update() {
        let file_path = prepare_temp_file("test_set_update.yaml",
                                          r#"a: 2
b: ba"#
                                              .as_bytes());
        Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--set")
            .arg(r#"["a"] {"aa": "aaa"}"#)
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        println!("stdout: {}, stderr: {}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        let mut s = String::new();
        File::open(file_path).unwrap().read_to_string(&mut s).unwrap();
        let data: Value = serde_yaml::from_str(&s).expect("Error parsing sops's JSON output");
        if let Value::Mapping(data) = data {
            let a = data.get(&Value::String("a".to_owned())).unwrap();
            if let &Value::Mapping(ref a) = a {
                assert_encrypted!(&a, Value::String("aa".to_owned()));
                return;
            }
        }
        panic!("Output JSON does not have the expected structure");
    }

    #[test]
    fn set_yaml_file_insert() {
        let file_path = prepare_temp_file("test_set_insert.yaml",
                                          r#"a: 2
b: ba"#
                                              .as_bytes());
        Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--set")
            .arg(r#"["c"] {"cc": "ccc"}"#)
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        println!("stdout: {}, stderr: {}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        let mut s = String::new();
        File::open(file_path).unwrap().read_to_string(&mut s).unwrap();
        let data: Value = serde_yaml::from_str(&s).expect("Error parsing sops's JSON output");
        if let Value::Mapping(data) = data {
            let a = data.get(&Value::String("c".to_owned())).unwrap();
            if let &Value::Mapping(ref a) = a {
                assert_encrypted!(&a, Value::String("cc".to_owned()));
                return;
            }
        }
        panic!("Output YAML does not have the expected structure");
    }

    #[test]
    fn set_yaml_file_string() {
        let file_path = prepare_temp_file("test_set_string.yaml",
                                          r#"a: 2
b: ba"#
                                          .as_bytes());
        Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg("-i")
            .arg("--set")
            .arg(r#"["a"] "aaa""#)
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-d")
            .arg("-i")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        println!("stdout: {}, stderr: {}",
                 String::from_utf8_lossy(&output.stdout),
                 String::from_utf8_lossy(&output.stderr));
        let mut s = String::new();
        File::open(file_path).unwrap().read_to_string(&mut s).unwrap();
        let data: Value = serde_yaml::from_str(&s).expect("Error parsing sops's YAML output");
        if let Value::Mapping(data) = data {
            let a = data.get(&Value::String("a".to_owned())).unwrap();
            assert_eq!(a, &Value::String("aaa".to_owned()));
        } else {
            panic!("Output JSON does not have the expected structure");
        }
    }

    #[test]
    fn decrypt_file_no_mac() {
        let file_path = prepare_temp_file("test_decrypt_no_mac.yaml", include_bytes!("../res/no_mac.yaml"));
        assert!(!Command::new(SOPS_BINARY_PATH)
                    .arg("-d")
                    .arg(file_path.clone())
                    .output()
                    .expect("Error running sops")
                    .status
                    .success(),
                "SOPS allowed decrypting a file with no MAC without --ignore-mac");

        assert!(Command::new(SOPS_BINARY_PATH)
                    .arg("-d")
                    .arg("--ignore-mac")
                    .arg(file_path.clone())
                    .output()
                    .expect("Error running sops")
                    .status
                    .success(),
                "SOPS failed to decrypt a file with no MAC with --ignore-mac passed in");
    }

    #[test]
    fn encrypt_comments() {
        let file_path = "res/comments.yaml";
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "SOPS didn't return successfully");
        assert!(!String::from_utf8_lossy(&output.stdout).contains("first comment in file"), "Comment was not encrypted");
        assert!(!String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not encrypted");
    }

    #[test]
    fn encrypt_comments_list() {
        let file_path = "res/comments_list.yaml";
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "SOPS didn't return successfully");
        assert!(!String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not encrypted");
        assert!(!String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not encrypted");
    }

    #[test]
    fn decrypt_comments() {
        let file_path = "res/comments.enc.yaml";
        let output = Command::new(SOPS_BINARY_PATH)
                    .arg("-d")
                    .arg(file_path.clone())
                    .output()
                    .expect("Error running sops");
        assert!(output.status.success(), "SOPS didn't return successfully");
        assert!(String::from_utf8_lossy(&output.stdout).contains("first comment in file"), "Comment was not decrypted");
        assert!(String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not decrypted");
    }

    #[test]
    fn decrypt_comments_unencrypted_comments() {
        let file_path = "res/comments_unencrypted_comments.yaml";
        let output = Command::new(SOPS_BINARY_PATH)
                    .arg("-d")
                    .arg(file_path.clone())
                    .output()
                    .expect("Error running sops");
        assert!(output.status.success(), "SOPS didn't return successfully");
        assert!(String::from_utf8_lossy(&output.stdout).contains("first comment in file"), "Comment was not decrypted");
        assert!(String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not decrypted");
    }

    #[test]
    fn roundtrip_shamir() {
        // The .sops.yaml file ensures this file is encrypted with two key groups, each with one GPG key
        let file_path = prepare_temp_file("test_roundtrip_keygroups.yaml", "a: secret".as_bytes());
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-i")
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(),
                "SOPS failed to encrypt a file with Shamir Secret Sharing");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-d")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status
                    .success(),
                "SOPS failed to decrypt a file with Shamir Secret Sharing");
        assert!(String::from_utf8_lossy(&output.stdout).contains("secret"));
    }

    #[test]
    fn roundtrip_shamir_missing_decryption_key() {
        // The .sops.yaml file ensures this file is encrypted with two key groups, each with one GPG key,
        // but we don't have one of the private keys
        let file_path = prepare_temp_file("test_roundtrip_keygroups_missing_decryption_key.yaml",
                                          "a: secret".as_bytes());
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-i")
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(),
                "SOPS failed to encrypt a file with Shamir Secret Sharing");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-d")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(!output.status
                    .success(),
                "SOPS succeeded decrypting a file with a missing decrytion key");
    }

    #[test]
    fn test_decrypt_file_multiple_keys() {
        let file_path = prepare_temp_file("test_decrypt_file_multiple_keys.yaml",
                                          include_bytes!("../res/multiple_keys.yaml"));
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-d")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status
                    .success(),
                "SOPS failed to decrypt a file that uses multiple keys");
    }


    #[test]
    fn extract_string() {
        let file_path = prepare_temp_file("test_extract_string.yaml",
                                          "multiline: |\n  multi\n  line".as_bytes());
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-i")
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "SOPS failed to encrypt a file");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--extract")
            .arg("[\"multiline\"]")
            .arg("-d")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status
                    .success(),
                "SOPS failed to extract");

        assert_eq!(output.stdout, b"multi\nline");
    }


    #[test]
    fn roundtrip_binary() {
        let data = b"\"\"{}this_is_binary_data";
        let file_path = prepare_temp_file("test.binary", data);
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-i")
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(),
                "SOPS failed to encrypt a binary file");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-d")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status
                    .success(),
                "SOPS failed to decrypt a binary file");
        assert_eq!(output.stdout, data);
    }

    #[test]
    #[ignore]
    fn roundtrip_kms_encryption_context() {
        let kms_arn = env::var(KMS_KEY).expect("Expected $FUNCTIONAL_TEST_KMS_ARN env var to be set");

        let file_path = prepare_temp_file("test_roundtrip_kms_encryption_context.json",
                                          b"{
    \"foo\": 2,
    \"bar\": \"baz\"
}");

        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--kms")
            .arg(kms_arn)
            .arg("--encryption-context")
            .arg("foo:bar,one:two")
            .arg("-i")
            .arg("-e")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status.success(), "sops didn't exit successfully");

        let output = Command::new(SOPS_BINARY_PATH)
            .arg("-d")
            .arg(file_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status
                    .success(),
                "SOPS failed to decrypt a file with KMS Encryption Context");
        assert!(String::from_utf8_lossy(&output.stdout).contains("foo"));
        assert!(String::from_utf8_lossy(&output.stdout).contains("baz"));
    }

    #[test]
    fn output_flag() {
        let input_path = prepare_temp_file("test_output_flag.binary", b"foo");
        let output_path = Path::join(TMP_DIR.path(), "output_flag.txt");
        let output = Command::new(SOPS_BINARY_PATH)
            .arg("--output")
            .arg(&output_path)
            .arg("-e")
            .arg(input_path.clone())
            .output()
            .expect("Error running sops");
        assert!(output.status
                    .success(),
                "SOPS failed to decrypt a binary file");
        assert_eq!(output.stdout, &[]);
        let mut f = File::open(&output_path).expect("output file not found");

        let mut contents = String::new();
        f.read_to_string(&mut contents)
            .expect("couldn't read output file contents");
        assert_ne!(contents, "", "Output file is empty");
    }
}
0707010000006F000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/gcpkms07070100000070000081A400000000000000000000000162794F9300001551000000000000000000000000000000000000001F00000000sops-3.7.3/gcpkms/keysource.gopackage gcpkms //import "go.mozilla.org/sops/v3/gcpkms"

import (
	"encoding/base64"
	"fmt"
	"google.golang.org/api/option"
	"os"
	"regexp"
	"strings"
	"time"

	"go.mozilla.org/sops/v3/logging"

	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	cloudkms "google.golang.org/api/cloudkms/v1"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("GCPKMS")
}

// MasterKey is a GCP KMS key used to encrypt and decrypt sops' data key.
type MasterKey struct {
	ResourceID   string
	EncryptedKey string
	CreationDate time.Time
}

// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)
}

// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
	key.EncryptedKey = string(enc)
}

// Encrypt takes a sops data key, encrypts it with GCP KMS and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
	cloudkmsService, err := key.createCloudKMSService()
	if err != nil {
		log.WithField("resourceID", key.ResourceID).Info("Encryption failed")
		return fmt.Errorf("Cannot create GCP KMS service: %w", err)
	}
	req := &cloudkms.EncryptRequest{
		Plaintext: base64.StdEncoding.EncodeToString(dataKey),
	}
	resp, err := cloudkmsService.Projects.Locations.KeyRings.CryptoKeys.Encrypt(key.ResourceID, req).Do()
	if err != nil {
		log.WithField("resourceID", key.ResourceID).Info("Encryption failed")
		return fmt.Errorf("Failed to call GCP KMS encryption service: %w", err)
	}
	log.WithField("resourceID", key.ResourceID).Info("Encryption succeeded")
	key.EncryptedKey = resp.Ciphertext
	return nil
}

// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(dataKey)
	}
	return nil
}

// Decrypt decrypts the EncryptedKey field with CGP KMS and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
	cloudkmsService, err := key.createCloudKMSService()
	if err != nil {
		log.WithField("resourceID", key.ResourceID).Info("Decryption failed")
		return nil, fmt.Errorf("Cannot create GCP KMS service: %w", err)
	}

	req := &cloudkms.DecryptRequest{
		Ciphertext: key.EncryptedKey,
	}
	resp, err := cloudkmsService.Projects.Locations.KeyRings.CryptoKeys.Decrypt(key.ResourceID, req).Do()
	if err != nil {
		log.WithField("resourceID", key.ResourceID).Info("Decryption failed")
		return nil, fmt.Errorf("Error decrypting key: %w", err)
	}
	encryptedKey, err := base64.StdEncoding.DecodeString(resp.Plaintext)
	if err != nil {
		log.WithField("resourceID", key.ResourceID).Info("Decryption failed")
		return nil, err
	}
	log.WithField("resourceID", key.ResourceID).Info("Decryption succeeded")
	return encryptedKey, nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}

// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
	return key.ResourceID
}

// NewMasterKeyFromResourceID takes a GCP KMS resource ID string and returns a new MasterKey for that
func NewMasterKeyFromResourceID(resourceID string) *MasterKey {
	k := &MasterKey{}
	resourceID = strings.Replace(resourceID, " ", "", -1)
	k.ResourceID = resourceID
	k.CreationDate = time.Now().UTC()
	return k
}

// MasterKeysFromResourceIDString takes a comma separated list of GCP KMS resource IDs and returns a slice of new MasterKeys for them
func MasterKeysFromResourceIDString(resourceID string) []*MasterKey {
	var keys []*MasterKey
	if resourceID == "" {
		return keys
	}
	for _, s := range strings.Split(resourceID, ",") {
		keys = append(keys, NewMasterKeyFromResourceID(s))
	}
	return keys
}

func (key MasterKey) createCloudKMSService() (*cloudkms.Service, error) {
	re := regexp.MustCompile(`^projects/[^/]+/locations/[^/]+/keyRings/[^/]+/cryptoKeys/[^/]+$`)
	matches := re.FindStringSubmatch(key.ResourceID)
	if matches == nil {
		return nil, fmt.Errorf("No valid resourceId found in %q", key.ResourceID)
	}

	ctx := context.Background()
	var options []option.ClientOption

	if credentials, err := getGoogleCredentials(); err != nil {
		return nil, err
	} else if len(credentials) > 0 {
		options = append(options, option.WithCredentialsJSON(credentials))
	}

	cloudkmsService, err := cloudkms.NewService(ctx, options...)
	if err != nil {
		return nil, err
	}
	return cloudkmsService, nil
}

// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
	out := make(map[string]interface{})
	out["resource_id"] = key.ResourceID
	out["enc"] = key.EncryptedKey
	out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
	return out
}

// getGoogleCredentials looks for a GCP Service Account in the environment
// variable: GOOGLE_CREDENTIALS, set as either a path to a credentials file or directly as the
// variable's value in JSON format.
//
// If not set, will default to use GOOGLE_APPLICATION_CREDENTIALS
func getGoogleCredentials() ([]byte, error) {
	defaultCredentials := os.Getenv("GOOGLE_CREDENTIALS")
	if _, err := os.Stat(defaultCredentials); err == nil {
		return os.ReadFile(defaultCredentials)
	}
	return []byte(defaultCredentials), nil
}
07070100000071000081A400000000000000000000000162794F930000049F000000000000000000000000000000000000002400000000sops-3.7.3/gcpkms/keysource_test.gopackage gcpkms

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestGCPKMSKeySourceFromString(t *testing.T) {
	s := "projects/sops-testing1/locations/global/keyRings/creds/cryptoKeys/key1, projects/sops-testing2/locations/global/keyRings/creds/cryptoKeys/key2"
	ks := MasterKeysFromResourceIDString(s)
	k1 := ks[0]
	k2 := ks[1]
	expectedResourceID1 := "projects/sops-testing1/locations/global/keyRings/creds/cryptoKeys/key1"
	expectedResourceID2 := "projects/sops-testing2/locations/global/keyRings/creds/cryptoKeys/key2"
	if k1.ResourceID != expectedResourceID1 {
		t.Errorf("ResourceID mismatch. Expected %s, found %s", expectedResourceID1, k1.ResourceID)
	}
	if k2.ResourceID != expectedResourceID2 {
		t.Errorf("ResourceID mismatch. Expected %s, found %s", expectedResourceID2, k2.ResourceID)
	}
}

func TestKeyToMap(t *testing.T) {
	key := MasterKey{
		CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
		ResourceID:   "foo",
		EncryptedKey: "this is encrypted",
	}
	assert.Equal(t, map[string]interface{}{
		"resource_id": "foo",
		"enc":         "this is encrypted",
		"created_at":  "2016-10-31T10:00:00Z",
	}, key.ToMap())
}
07070100000072000081A400000000000000000000000162794F930000158C000000000000000000000000000000000000001200000000sops-3.7.3/go.modmodule go.mozilla.org/sops/v3

go 1.17

require (
	cloud.google.com/go/storage v1.22.0
	filippo.io/age v1.0.0
	github.com/Azure/azure-sdk-for-go v63.3.0+incompatible
	github.com/Azure/go-autorest/autorest v0.11.26
	github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
	github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5
	github.com/aws/aws-sdk-go v1.43.43
	github.com/blang/semver v3.5.1+incompatible
	github.com/fatih/color v1.13.0
	github.com/golang/protobuf v1.5.2
	github.com/google/go-cmp v0.5.7
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
	github.com/goware/prefixer v0.0.0-20160118172347-395022866408
	github.com/hashicorp/vault/api v1.5.0
	github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef
	github.com/lib/pq v1.10.5
	github.com/mitchellh/go-homedir v1.1.0
	github.com/mitchellh/go-wordwrap v1.0.1
	github.com/ory/dockertest v3.3.5+incompatible
	github.com/pkg/errors v0.9.1
	github.com/sirupsen/logrus v1.8.1
	github.com/stretchr/testify v1.7.1
	go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a
	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
	golang.org/x/net v0.0.0-20220420153159-1850ba15e1be
	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
	google.golang.org/api v0.74.0
	google.golang.org/grpc v1.45.0
	google.golang.org/protobuf v1.28.0
	gopkg.in/ini.v1 v1.66.4
	gopkg.in/urfave/cli.v1 v1.20.0
	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)

require (
	cloud.google.com/go v0.100.2 // indirect
	cloud.google.com/go/compute v1.5.0 // indirect
	cloud.google.com/go/iam v0.3.0 // indirect
	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
	github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
	github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
	github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
	github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
	github.com/Azure/go-autorest/logger v0.2.1 // indirect
	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
	github.com/Microsoft/go-winio v0.5.2 // indirect
	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
	github.com/armon/go-metrics v0.3.10 // indirect
	github.com/armon/go-radix v1.0.0 // indirect
	github.com/cenkalti/backoff v2.2.1+incompatible // indirect
	github.com/cenkalti/backoff/v3 v3.2.2 // indirect
	github.com/containerd/continuity v0.2.2 // indirect
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/dimchansky/utfbom v1.1.1 // indirect
	github.com/docker/go-connections v0.4.0 // indirect
	github.com/docker/go-units v0.4.0 // indirect
	github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
	github.com/golang/snappy v0.0.4 // indirect
	github.com/googleapis/gax-go/v2 v2.2.0 // indirect
	github.com/googleapis/go-type-adapters v1.0.0 // indirect
	github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
	github.com/hashicorp/errwrap v1.1.0 // indirect
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
	github.com/hashicorp/go-hclog v1.2.0 // indirect
	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
	github.com/hashicorp/go-multierror v1.1.1 // indirect
	github.com/hashicorp/go-plugin v1.4.3 // indirect
	github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
	github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect
	github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3 // indirect
	github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
	github.com/hashicorp/go-sockaddr v1.0.2 // indirect
	github.com/hashicorp/go-uuid v1.0.2 // indirect
	github.com/hashicorp/go-version v1.4.0 // indirect
	github.com/hashicorp/golang-lru v0.5.4 // indirect
	github.com/hashicorp/hcl v1.0.0 // indirect
	github.com/hashicorp/vault/sdk v0.4.1 // indirect
	github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
	github.com/jmespath/go-jmespath v0.4.0 // indirect
	github.com/mattn/go-colorable v0.1.12 // indirect
	github.com/mattn/go-isatty v0.0.14 // indirect
	github.com/mitchellh/copystructure v1.2.0 // indirect
	github.com/mitchellh/go-testing-interface v1.14.1 // indirect
	github.com/mitchellh/mapstructure v1.4.3 // indirect
	github.com/mitchellh/reflectwalk v1.0.2 // indirect
	github.com/oklog/run v1.1.0 // indirect
	github.com/opencontainers/go-digest v1.0.0 // indirect
	github.com/opencontainers/image-spec v1.0.2 // indirect
	github.com/opencontainers/runc v1.1.0 // indirect
	github.com/pierrec/lz4 v2.6.1+incompatible // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	github.com/ryanuber/go-glob v1.0.0 // indirect
	github.com/stretchr/objx v0.3.0 // indirect
	go.opencensus.io v0.23.0 // indirect
	go.uber.org/atomic v1.9.0 // indirect
	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
	golang.org/x/text v0.3.7 // indirect
	golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
	google.golang.org/appengine v1.6.7 // indirect
	google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf // indirect
	gopkg.in/square/go-jose.v2 v2.6.0 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	gotest.tools v2.2.0+incompatible // indirect
)
07070100000073000081A400000000000000000000000162794F9300018C60000000000000000000000000000000000000001200000000sops-3.7.3/go.sumbazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.0 h1:NUV0NNp9nkBuW66BFRLuMgldN60C57ET3dhbwLIYio8=
cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc=
filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtER+FkvCoEwyRCXGqOlmDII=
github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.26 h1:W/MzvoAiFfL5h4nq81wm7axvITgbnOoifXXGkFrgF1g=
github.com/Azure/go-autorest/autorest v0.11.26/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 h1:cSHEbLj0GZeHM1mWG84qEnGFojNEQ83W7cwaPRjcwXU=
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.43.43 h1:1L06qzQvl4aC3Skfh5rV7xVhGHjIZoHcqy16NoyQ1o4=
github.com/aws/aws-sdk-go v1.43.43/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA=
github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk=
github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw=
github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3 h1:geBw3SBrxQq+buvbf4K+Qltv1gjaXJxy8asD4CjGYow=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28=
github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8=
github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k=
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220420153159-1850ba15e1be h1:yx80W7nvY5ySWpaU8UWaj5o9e23YgO9BRhQol7Lc+JI=
golang.org/x/net v0.0.0-20220420153159-1850ba15e1be/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf h1:JTjwKJX9erVpsw17w+OIPP7iAgEkN/r8urhWSunEDTs=
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
07070100000074000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/hcvault07070100000075000081A400000000000000000000000162794F9300001F32000000000000000000000000000000000000002000000000sops-3.7.3/hcvault/keysource.gopackage hcvault

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/hashicorp/vault/api"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3/logging"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("VAULT_TRANSIT")
}

// MasterKey is a Vault Transit backend path used to encrypt and decrypt sops' data key.
type MasterKey struct {
	EncryptedKey string
	KeyName      string
	EnginePath   string
	VaultAddress string
	CreationDate time.Time
}

// NewMasterKeysFromURIs gets lots of keys from lots of URIs
func NewMasterKeysFromURIs(uris string) ([]*MasterKey, error) {
	var keys []*MasterKey
	if uris == "" {
		return keys, nil
	}
	uriList := strings.Split(uris, ",")
	for _, uri := range uriList {
		if uri == "" {
			continue
		}
		key, err := NewMasterKeyFromURI(uri)
		if err != nil {
			return nil, err
		}
		keys = append(keys, key)
	}
	return keys, nil
}

// NewMasterKeyFromURI obtains the vaultAddress the transit backend path and the key name from the full URI of the key
func NewMasterKeyFromURI(uri string) (*MasterKey, error) {
	log.Debugln("Called NewMasterKeyFromURI with uri: ", uri)
	var key *MasterKey
	if uri == "" {
		return key, nil
	}
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	if u.Scheme == "" {
		return nil, fmt.Errorf("missing scheme in vault URL (should be like this: https://vault.example.com:8200/v1/transit/keys/keyName), got: %v", uri)
	}
	enginePath, keyName, err := getBackendAndKeyFromPath(u.RequestURI())
	if err != nil {
		return nil, err
	}
	u.Path = ""
	return NewMasterKey(u.String(), enginePath, keyName), nil

}

func getBackendAndKeyFromPath(fullPath string) (enginePath, keyName string, err error) {
	// Running vault behind a reverse proxy with longer urls seems not to be supported
	// by the vault client api so we have a separate Error for that here.
	if re := regexp.MustCompile(`/[^/]+/v[\d]+/[^/]+/[^/]+/[^/]+`); re.Match([]byte(fullPath)) {
		return "", "", fmt.Errorf("running Vault with a prefixed url is not supported! (Format has to be like https://vault.example.com:8200/v1/transit/keys/keyName)")
	} else if re := regexp.MustCompile(`/v[\d]+/[^/]+/[^/]+/[^/]+`); re.Match([]byte(fullPath)) == false {
		return "", "", fmt.Errorf("vault path does not seem to be formatted correctly: (eg. https://vault.example.com:8200/v1/transit/keys/keyName)")
	}
	fullPath = strings.TrimPrefix(fullPath, "/")
	fullPath = strings.TrimSuffix(fullPath, "/")

	dirs := strings.Split(fullPath, "/")

	keyName = dirs[len(dirs)-1]
	enginePath = path.Join(dirs[1 : len(dirs)-2]...)
	err = nil
	return
}

// NewMasterKey creates a new MasterKey from a vault address, transit backend path and a key name and setting the creation date to the current date
func NewMasterKey(addess, enginePath, keyName string) *MasterKey {
	mk := &MasterKey{
		VaultAddress: addess,
		EnginePath:   enginePath,
		KeyName:      keyName,
		CreationDate: time.Now().UTC(),
	}
	log.Debugln("Created Vault Master Key: ", mk)
	return mk
}

// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)
}

// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
	key.EncryptedKey = string(enc)
}

func vaultClient(address string) (*api.Client, error) {
	cfg := api.DefaultConfig()
	cfg.Address = address
	cli, err := api.NewClient(cfg)
	if err != nil {
		return nil, fmt.Errorf("Cannot create Vault Client: %w", err)
	}
	if cli.Token() != "" {
		return cli, nil
	}
	homePath, err := homedir.Dir()
	if err != nil {
		panic(fmt.Sprintf("error getting user's home directory: %v", err))
	}
	tokenPath := filepath.Join(homePath, ".vault-token")
	f, err := os.Open(tokenPath)
	if os.IsNotExist(err) {
		return cli, nil
	}
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buf := bytes.NewBuffer(nil)
	if _, err := io.Copy(buf, f); err != nil {
		return nil, err
	}
	cli.SetToken(strings.TrimSpace(buf.String()))
	return cli, nil
}

// Encrypt takes a sops data key, encrypts it with Vault Transit and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
	fullPath := path.Join(key.EnginePath, "encrypt", key.KeyName)
	cli, err := vaultClient(key.VaultAddress)
	if err != nil {
		return err
	}
	encoded := base64.StdEncoding.EncodeToString(dataKey)
	payload := make(map[string]interface{})
	payload["plaintext"] = encoded
	raw, err := cli.Logical().Write(fullPath, payload)
	if err != nil {
		log.WithField("Path", fullPath).Info("Encryption failed")
		return err
	}
	if raw == nil || raw.Data == nil {
		return fmt.Errorf("The transit backend %s is empty", fullPath)
	}
	encrypted, ok := raw.Data["ciphertext"]
	if !ok {
		return fmt.Errorf("there's not encrypted data")
	}
	encryptedKey, ok := encrypted.(string)
	if !ok {
		return fmt.Errorf("the ciphertext cannot be casted to string")
	}
	key.EncryptedKey = encryptedKey
	return nil
}

// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(dataKey)
	}
	return nil
}

// Decrypt decrypts the EncryptedKey field with Vault Transit and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
	fullPath := path.Join(key.EnginePath, "decrypt", key.KeyName)
	cli, err := vaultClient(key.VaultAddress)
	if err != nil {
		return nil, err
	}
	payload := make(map[string]interface{})
	payload["ciphertext"] = key.EncryptedKey
	raw, err := cli.Logical().Write(fullPath, payload)
	if err != nil {
		log.WithField("Path", fullPath).Info("Encryption failed")
		return nil, err
	}
	if raw == nil || raw.Data == nil {
		return nil, fmt.Errorf("The transit backend %s is empty", fullPath)
	}
	decrypted, ok := raw.Data["plaintext"]
	if ok != true {
		return nil, fmt.Errorf("there's no decrypted data")
	}
	dataKey, ok := decrypted.(string)
	if ok != true {
		return nil, fmt.Errorf("the plaintest cannot be casted to string")
	}
	result, err := base64.StdEncoding.DecodeString(dataKey)
	if err != nil {
		return nil, fmt.Errorf("Couldn't decode base64 plaintext")
	}
	return result, nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
// This is simply copied from GCPKMS
// TODO: handle key rotation on vault side
func (key *MasterKey) NeedsRotation() bool {
	//TODO: manage rewrapping https://www.vaultproject.io/api/secret/transit/index.html#rewrap-data
	return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}

// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
	return fmt.Sprintf("%s/v1/%s/keys/%s", key.VaultAddress, key.EnginePath, key.KeyName)
}

func (key *MasterKey) createVaultTransitAndKey() error {
	cli, err := vaultClient(key.VaultAddress)
	if err != nil {
		return err
	}
	if err != nil {
		return fmt.Errorf("Cannot create Vault Client: %w", err)
	}
	err = cli.Sys().Mount(key.EnginePath, &api.MountInput{
		Type:        "transit",
		Description: "backend transit used by SOPS",
	})
	if err != nil {
		return err
	}
	path := path.Join(key.EnginePath, "keys", key.KeyName)
	payload := make(map[string]interface{})
	payload["type"] = "rsa-4096"
	_, err = cli.Logical().Write(path, payload)
	if err != nil {
		return err
	}
	_, err = cli.Logical().Read(path)
	if err != nil {
		return err
	}
	return nil
}

// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
	out := make(map[string]interface{})
	out["vault_address"] = key.VaultAddress
	out["key_name"] = key.KeyName
	out["engine_path"] = key.EnginePath
	out["enc"] = key.EncryptedKey
	out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
	return out
}
07070100000076000081A400000000000000000000000162794F9300000FD2000000000000000000000000000000000000002500000000sops-3.7.3/hcvault/keysource_test.gopackage hcvault

import (
	"fmt"
	logger "log"
	"os"
	"testing"
	"time"

	"github.com/hashicorp/vault/api"
	"github.com/ory/dockertest"
	"github.com/stretchr/testify/assert"
)

func TestMain(m *testing.M) {
	// uses a sensible default on windows (tcp/http) and linux/osx (socket)
	pool, err := dockertest.NewPool("")
	if err != nil {
		logger.Fatalf("Could not connect to docker: %s", err)
	}

	// pulls an image, creates a container based on it and runs it
	resource, err := pool.Run("vault", "1.2.2", []string{"VAULT_DEV_ROOT_TOKEN_ID=secret"})
	if err != nil {
		logger.Fatalf("Could not start resource: %s", err)
	}

	vaultAddr := fmt.Sprintf("http://%s", resource.GetHostPort("8200/tcp"))
	os.Setenv("VAULT_ADDR", vaultAddr)
	os.Setenv("VAULT_TOKEN", "secret")
	// exponential backoff-retry, because the application in the container might not be ready to accept connections yet
	if err := pool.Retry(func() error {
		cli, err := api.NewClient(api.DefaultConfig())
		if err != nil {
			return fmt.Errorf("Cannot create Vault Client: %w", err)
		}
		status, err := cli.Sys().InitStatus()
		if err != nil {
			return err
		}
		if status != true {
			return fmt.Errorf("Vault not ready yet")
		}
		return nil
	}); err != nil {
		logger.Fatalf("Could not connect to docker: %s", err)
	}

	key := NewMasterKey(vaultAddr, "sops", "main")
	err = key.createVaultTransitAndKey()
	if err != nil {
		logger.Fatal(err)
	}
	code := 0
	if err == nil {
		code = m.Run()
	}

	// You can't defer this because os.Exit doesn't care for defer
	if err := pool.Purge(resource); err != nil {
		logger.Fatalf("Could not purge resource: %s", err)
	}

	os.Exit(code)
}

func TestKeyToMap(t *testing.T) {
	key := MasterKey{
		CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
		VaultAddress: "http://127.0.0.1:8200",
		EnginePath:   "foo",
		KeyName:      "bar",
		EncryptedKey: "this is encrypted",
	}
	assert.Equal(t, map[string]interface{}{
		"vault_address": "http://127.0.0.1:8200",
		"engine_path":   "foo",
		"key_name":      "bar",
		"enc":           "this is encrypted",
		"created_at":    "2016-10-31T10:00:00Z",
	}, key.ToMap())
}

func TestEncryptionDecryption(t *testing.T) {
	dataKey := []byte("super very Secret Key!!!")
	key := MasterKey{
		VaultAddress: os.Getenv("VAULT_ADDR"),
		EnginePath:   "sops",
		KeyName:      "main",
	}
	err := key.Encrypt(dataKey)
	if err != nil {
		fmt.Println(err)
		t.Fail()
		return
	}
	decrypted, err := key.Decrypt()
	if err != nil {
		fmt.Println(err)
		t.Fail()
		return
	}
	assert.Equal(t, dataKey, decrypted)
}

func TestNewMasterKeyFromURI(t *testing.T) {
	uri1 := "https://vault.example.com:8200/v1/transit/keys/keyName"
	uri2 := "https://vault.me.com/v1/super42/bestmarket/keys/slig"
	uri3 := "http://127.0.0.1:12121/v1/transit/keys/dev"

	mk1 := &MasterKey{
		VaultAddress: "https://vault.example.com:8200",
		EnginePath:   "transit",
		KeyName:      "keyName",
	}
	mk2 := &MasterKey{
		VaultAddress: "https://vault.me.com",
		EnginePath:   "super42/bestmarket",
		KeyName:      "slig",
	}
	mk3 := &MasterKey{
		VaultAddress: "http://127.0.0.1:12121",
		EnginePath:   "transit",
		KeyName:      "dev",
	}
	genMk1, err := NewMasterKeyFromURI(uri1)
	if err != nil {
		log.Errorln(err)
		t.Fail()
	}

	genMk2, err := NewMasterKeyFromURI(uri2)
	if err != nil {
		log.Errorln(err)
		t.Fail()
	}

	genMk3, err := NewMasterKeyFromURI(uri3)
	if err != nil {
		log.Errorln(err)
		t.Fail()
	}

	if assert.NotNil(t, genMk1) {
		mk1.CreationDate = genMk1.CreationDate
		assert.Equal(t, mk1, genMk1)
	}
	if assert.NotNil(t, genMk2) {
		mk2.CreationDate = genMk2.CreationDate
		assert.Equal(t, mk2, genMk2)
	}
	if assert.NotNil(t, genMk3) {
		mk3.CreationDate = genMk3.CreationDate
		assert.Equal(t, mk3, genMk3)
	}

	badURIs := []string{
		"vault.me/keys/dev/mykey",
		"http://127.0.0.1:12121/v1/keys/dev",
		"tcp://127.0.0.1:12121/v1/keys/dev",
	}
	for _, uri := range badURIs {
		if _, err = NewMasterKeyFromURI(uri); err == nil {
			log.Errorf("Should be a invalid uri: %s", uri)
			t.Fail()
		}
	}

}
07070100000077000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001000000000sops-3.7.3/keys07070100000078000081A400000000000000000000000162794F930000017D000000000000000000000000000000000000001800000000sops-3.7.3/keys/keys.gopackage keys

// MasterKey provides a way of securing the key used to encrypt the Tree by encrypting and decrypting said key.
type MasterKey interface {
	Encrypt(dataKey []byte) error
	EncryptIfNeeded(dataKey []byte) error
	EncryptedDataKey() []byte
	SetEncryptedDataKey([]byte)
	Decrypt() ([]byte, error)
	NeedsRotation() bool
	ToString() string
	ToMap() map[string]interface{}
}
07070100000079000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001600000000sops-3.7.3/keyservice0707010000007A000081A400000000000000000000000162794F9300000422000000000000000000000000000000000000002000000000sops-3.7.3/keyservice/client.gopackage keyservice

import (
	"golang.org/x/net/context"

	"google.golang.org/grpc"
)

// LocalClient is a key service client that performs all operations locally
type LocalClient struct {
	Server KeyServiceServer
}

// NewLocalClient creates a new local client
func NewLocalClient() LocalClient {
	return LocalClient{Server{}}
}

// NewCustomLocalClient creates a new local client with a non-default backing
// KeyServiceServer implementation
func NewCustomLocalClient(server KeyServiceServer) LocalClient {
	return LocalClient{Server: server}
}

// Decrypt processes a decrypt request locally
// See keyservice/server.go for more details
func (c LocalClient) Decrypt(ctx context.Context,
	req *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) {
	return c.Server.Decrypt(ctx, req)
}

// Encrypt processes an encrypt request locally
// See keyservice/server.go for more details
func (c LocalClient) Encrypt(ctx context.Context,
	req *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) {
	return c.Server.Encrypt(ctx, req)
}
0707010000007B000081A400000000000000000000000162794F930000070F000000000000000000000000000000000000002400000000sops-3.7.3/keyservice/keyservice.go/*
Package keyservice implements a gRPC API that can be used by SOPS to encrypt and decrypt the data key using remote
master keys.
*/
package keyservice

import (
	"fmt"

	"go.mozilla.org/sops/v3/age"
	"go.mozilla.org/sops/v3/azkv"
	"go.mozilla.org/sops/v3/gcpkms"
	"go.mozilla.org/sops/v3/hcvault"
	"go.mozilla.org/sops/v3/keys"
	"go.mozilla.org/sops/v3/kms"
	"go.mozilla.org/sops/v3/pgp"
)

// KeyFromMasterKey converts a SOPS internal MasterKey to an RPC Key that can be serialized with Protocol Buffers
func KeyFromMasterKey(mk keys.MasterKey) Key {
	switch mk := mk.(type) {
	case *pgp.MasterKey:
		return Key{
			KeyType: &Key_PgpKey{
				PgpKey: &PgpKey{
					Fingerprint: mk.Fingerprint,
				},
			},
		}
	case *gcpkms.MasterKey:
		return Key{
			KeyType: &Key_GcpKmsKey{
				GcpKmsKey: &GcpKmsKey{
					ResourceId: mk.ResourceID,
				},
			},
		}
	case *hcvault.MasterKey:
		return Key{
			KeyType: &Key_VaultKey{
				VaultKey: &VaultKey{
					VaultAddress: mk.VaultAddress,
					EnginePath:   mk.EnginePath,
					KeyName:      mk.KeyName,
				},
			},
		}
	case *kms.MasterKey:
		ctx := make(map[string]string)
		for k, v := range mk.EncryptionContext {
			ctx[k] = *v
		}
		return Key{
			KeyType: &Key_KmsKey{
				KmsKey: &KmsKey{
					Arn:        mk.Arn,
					Role:       mk.Role,
					Context:    ctx,
					AwsProfile: mk.AwsProfile,
				},
			},
		}
	case *azkv.MasterKey:
		return Key{
			KeyType: &Key_AzureKeyvaultKey{
				AzureKeyvaultKey: &AzureKeyVaultKey{
					VaultUrl: mk.VaultURL,
					Name:     mk.Name,
					Version:  mk.Version,
				},
			},
		}
	case *age.MasterKey:
		return Key{
			KeyType: &Key_AgeKey{
				AgeKey: &AgeKey{
					Recipient: mk.Recipient,
				},
			},
		}
	default:
		panic(fmt.Sprintf("Tried to convert unknown MasterKey type %T to keyservice.Key", mk))
	}
}
0707010000007C000081A400000000000000000000000162794F93000089E3000000000000000000000000000000000000002700000000sops-3.7.3/keyservice/keyservice.pb.go// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.23.0
// 	protoc        v3.13.0
// source: keyservice/keyservice.proto

package keyservice

import (
	context "context"
	proto "github.com/golang/protobuf/proto"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4

type Key struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	// Types that are assignable to KeyType:
	//	*Key_KmsKey
	//	*Key_PgpKey
	//	*Key_GcpKmsKey
	//	*Key_AzureKeyvaultKey
	//	*Key_VaultKey
	//	*Key_AgeKey
	KeyType isKey_KeyType `protobuf_oneof:"key_type"`
}

func (x *Key) Reset() {
	*x = Key{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Key) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Key) ProtoMessage() {}

func (x *Key) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Key.ProtoReflect.Descriptor instead.
func (*Key) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{0}
}

func (m *Key) GetKeyType() isKey_KeyType {
	if m != nil {
		return m.KeyType
	}
	return nil
}

func (x *Key) GetKmsKey() *KmsKey {
	if x, ok := x.GetKeyType().(*Key_KmsKey); ok {
		return x.KmsKey
	}
	return nil
}

func (x *Key) GetPgpKey() *PgpKey {
	if x, ok := x.GetKeyType().(*Key_PgpKey); ok {
		return x.PgpKey
	}
	return nil
}

func (x *Key) GetGcpKmsKey() *GcpKmsKey {
	if x, ok := x.GetKeyType().(*Key_GcpKmsKey); ok {
		return x.GcpKmsKey
	}
	return nil
}

func (x *Key) GetAzureKeyvaultKey() *AzureKeyVaultKey {
	if x, ok := x.GetKeyType().(*Key_AzureKeyvaultKey); ok {
		return x.AzureKeyvaultKey
	}
	return nil
}

func (x *Key) GetVaultKey() *VaultKey {
	if x, ok := x.GetKeyType().(*Key_VaultKey); ok {
		return x.VaultKey
	}
	return nil
}

func (x *Key) GetAgeKey() *AgeKey {
	if x, ok := x.GetKeyType().(*Key_AgeKey); ok {
		return x.AgeKey
	}
	return nil
}

type isKey_KeyType interface {
	isKey_KeyType()
}

type Key_KmsKey struct {
	KmsKey *KmsKey `protobuf:"bytes,1,opt,name=kms_key,json=kmsKey,proto3,oneof"`
}

type Key_PgpKey struct {
	PgpKey *PgpKey `protobuf:"bytes,2,opt,name=pgp_key,json=pgpKey,proto3,oneof"`
}

type Key_GcpKmsKey struct {
	GcpKmsKey *GcpKmsKey `protobuf:"bytes,3,opt,name=gcp_kms_key,json=gcpKmsKey,proto3,oneof"`
}

type Key_AzureKeyvaultKey struct {
	AzureKeyvaultKey *AzureKeyVaultKey `protobuf:"bytes,4,opt,name=azure_keyvault_key,json=azureKeyvaultKey,proto3,oneof"`
}

type Key_VaultKey struct {
	VaultKey *VaultKey `protobuf:"bytes,5,opt,name=vault_key,json=vaultKey,proto3,oneof"`
}

type Key_AgeKey struct {
	AgeKey *AgeKey `protobuf:"bytes,6,opt,name=age_key,json=ageKey,proto3,oneof"`
}

func (*Key_KmsKey) isKey_KeyType() {}

func (*Key_PgpKey) isKey_KeyType() {}

func (*Key_GcpKmsKey) isKey_KeyType() {}

func (*Key_AzureKeyvaultKey) isKey_KeyType() {}

func (*Key_VaultKey) isKey_KeyType() {}

func (*Key_AgeKey) isKey_KeyType() {}

type PgpKey struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Fingerprint string `protobuf:"bytes,1,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
}

func (x *PgpKey) Reset() {
	*x = PgpKey{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *PgpKey) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*PgpKey) ProtoMessage() {}

func (x *PgpKey) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[1]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use PgpKey.ProtoReflect.Descriptor instead.
func (*PgpKey) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{1}
}

func (x *PgpKey) GetFingerprint() string {
	if x != nil {
		return x.Fingerprint
	}
	return ""
}

type KmsKey struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Arn        string            `protobuf:"bytes,1,opt,name=arn,proto3" json:"arn,omitempty"`
	Role       string            `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
	Context    map[string]string `protobuf:"bytes,3,rep,name=context,proto3" json:"context,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
	AwsProfile string            `protobuf:"bytes,4,opt,name=aws_profile,json=awsProfile,proto3" json:"aws_profile,omitempty"`
}

func (x *KmsKey) Reset() {
	*x = KmsKey{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[2]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *KmsKey) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*KmsKey) ProtoMessage() {}

func (x *KmsKey) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[2]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use KmsKey.ProtoReflect.Descriptor instead.
func (*KmsKey) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{2}
}

func (x *KmsKey) GetArn() string {
	if x != nil {
		return x.Arn
	}
	return ""
}

func (x *KmsKey) GetRole() string {
	if x != nil {
		return x.Role
	}
	return ""
}

func (x *KmsKey) GetContext() map[string]string {
	if x != nil {
		return x.Context
	}
	return nil
}

func (x *KmsKey) GetAwsProfile() string {
	if x != nil {
		return x.AwsProfile
	}
	return ""
}

type GcpKmsKey struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
}

func (x *GcpKmsKey) Reset() {
	*x = GcpKmsKey{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[3]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *GcpKmsKey) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*GcpKmsKey) ProtoMessage() {}

func (x *GcpKmsKey) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[3]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use GcpKmsKey.ProtoReflect.Descriptor instead.
func (*GcpKmsKey) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{3}
}

func (x *GcpKmsKey) GetResourceId() string {
	if x != nil {
		return x.ResourceId
	}
	return ""
}

type VaultKey struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	VaultAddress string `protobuf:"bytes,1,opt,name=vault_address,json=vaultAddress,proto3" json:"vault_address,omitempty"`
	EnginePath   string `protobuf:"bytes,2,opt,name=engine_path,json=enginePath,proto3" json:"engine_path,omitempty"`
	KeyName      string `protobuf:"bytes,3,opt,name=key_name,json=keyName,proto3" json:"key_name,omitempty"`
}

func (x *VaultKey) Reset() {
	*x = VaultKey{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[4]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *VaultKey) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*VaultKey) ProtoMessage() {}

func (x *VaultKey) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[4]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use VaultKey.ProtoReflect.Descriptor instead.
func (*VaultKey) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{4}
}

func (x *VaultKey) GetVaultAddress() string {
	if x != nil {
		return x.VaultAddress
	}
	return ""
}

func (x *VaultKey) GetEnginePath() string {
	if x != nil {
		return x.EnginePath
	}
	return ""
}

func (x *VaultKey) GetKeyName() string {
	if x != nil {
		return x.KeyName
	}
	return ""
}

type AzureKeyVaultKey struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	VaultUrl string `protobuf:"bytes,1,opt,name=vault_url,json=vaultUrl,proto3" json:"vault_url,omitempty"`
	Name     string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
	Version  string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
}

func (x *AzureKeyVaultKey) Reset() {
	*x = AzureKeyVaultKey{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[5]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *AzureKeyVaultKey) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*AzureKeyVaultKey) ProtoMessage() {}

func (x *AzureKeyVaultKey) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[5]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use AzureKeyVaultKey.ProtoReflect.Descriptor instead.
func (*AzureKeyVaultKey) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{5}
}

func (x *AzureKeyVaultKey) GetVaultUrl() string {
	if x != nil {
		return x.VaultUrl
	}
	return ""
}

func (x *AzureKeyVaultKey) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}

func (x *AzureKeyVaultKey) GetVersion() string {
	if x != nil {
		return x.Version
	}
	return ""
}

type AgeKey struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Recipient string `protobuf:"bytes,1,opt,name=recipient,proto3" json:"recipient,omitempty"`
}

func (x *AgeKey) Reset() {
	*x = AgeKey{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[6]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *AgeKey) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*AgeKey) ProtoMessage() {}

func (x *AgeKey) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[6]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use AgeKey.ProtoReflect.Descriptor instead.
func (*AgeKey) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{6}
}

func (x *AgeKey) GetRecipient() string {
	if x != nil {
		return x.Recipient
	}
	return ""
}

type EncryptRequest struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Key       *Key   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
	Plaintext []byte `protobuf:"bytes,2,opt,name=plaintext,proto3" json:"plaintext,omitempty"`
}

func (x *EncryptRequest) Reset() {
	*x = EncryptRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[7]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *EncryptRequest) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*EncryptRequest) ProtoMessage() {}

func (x *EncryptRequest) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[7]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use EncryptRequest.ProtoReflect.Descriptor instead.
func (*EncryptRequest) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{7}
}

func (x *EncryptRequest) GetKey() *Key {
	if x != nil {
		return x.Key
	}
	return nil
}

func (x *EncryptRequest) GetPlaintext() []byte {
	if x != nil {
		return x.Plaintext
	}
	return nil
}

type EncryptResponse struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Ciphertext []byte `protobuf:"bytes,1,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"`
}

func (x *EncryptResponse) Reset() {
	*x = EncryptResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[8]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *EncryptResponse) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*EncryptResponse) ProtoMessage() {}

func (x *EncryptResponse) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[8]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use EncryptResponse.ProtoReflect.Descriptor instead.
func (*EncryptResponse) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{8}
}

func (x *EncryptResponse) GetCiphertext() []byte {
	if x != nil {
		return x.Ciphertext
	}
	return nil
}

type DecryptRequest struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Key        *Key   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
	Ciphertext []byte `protobuf:"bytes,2,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"`
}

func (x *DecryptRequest) Reset() {
	*x = DecryptRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[9]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *DecryptRequest) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*DecryptRequest) ProtoMessage() {}

func (x *DecryptRequest) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[9]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use DecryptRequest.ProtoReflect.Descriptor instead.
func (*DecryptRequest) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{9}
}

func (x *DecryptRequest) GetKey() *Key {
	if x != nil {
		return x.Key
	}
	return nil
}

func (x *DecryptRequest) GetCiphertext() []byte {
	if x != nil {
		return x.Ciphertext
	}
	return nil
}

type DecryptResponse struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Plaintext []byte `protobuf:"bytes,1,opt,name=plaintext,proto3" json:"plaintext,omitempty"`
}

func (x *DecryptResponse) Reset() {
	*x = DecryptResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_keyservice_keyservice_proto_msgTypes[10]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *DecryptResponse) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*DecryptResponse) ProtoMessage() {}

func (x *DecryptResponse) ProtoReflect() protoreflect.Message {
	mi := &file_keyservice_keyservice_proto_msgTypes[10]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use DecryptResponse.ProtoReflect.Descriptor instead.
func (*DecryptResponse) Descriptor() ([]byte, []int) {
	return file_keyservice_keyservice_proto_rawDescGZIP(), []int{10}
}

func (x *DecryptResponse) GetPlaintext() []byte {
	if x != nil {
		return x.Plaintext
	}
	return nil
}

var File_keyservice_keyservice_proto protoreflect.FileDescriptor

var file_keyservice_keyservice_proto_rawDesc = []byte{
	0x0a, 0x1b, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6b, 0x65, 0x79,
	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x02,
	0x0a, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x6b, 0x6d, 0x73, 0x5f, 0x6b, 0x65, 0x79,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x48,
	0x00, 0x52, 0x06, 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x70, 0x67, 0x70,
	0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x50, 0x67, 0x70,
	0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x06, 0x70, 0x67, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a,
	0x0b, 0x67, 0x63, 0x70, 0x5f, 0x6b, 0x6d, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01,
	0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x48, 0x00,
	0x52, 0x09, 0x67, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x12, 0x61,
	0x7a, 0x75, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6b, 0x65,
	0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b,
	0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x10, 0x61, 0x7a,
	0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x28,
	0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28,
	0x0b, 0x32, 0x09, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x08,
	0x76, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x61, 0x67, 0x65, 0x5f,
	0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x41, 0x67, 0x65, 0x4b,
	0x65, 0x79, 0x48, 0x00, 0x52, 0x06, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08,
	0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2a, 0x0a, 0x06, 0x50, 0x67, 0x70, 0x4b,
	0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
	0x72, 0x69, 0x6e, 0x74, 0x22, 0xbb, 0x01, 0x0a, 0x06, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12,
	0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72,
	0x6e, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
	0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
	0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x2e,
	0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6f,
	0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x77, 0x73, 0x5f, 0x70, 0x72, 0x6f,
	0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x77, 0x73, 0x50,
	0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
	0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
	0x38, 0x01, 0x22, 0x2c, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12,
	0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
	0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64,
	0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d,
	0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
	0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
	0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68,
	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61,
	0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a,
	0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65,
	0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01,
	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12,
	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
	0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
	0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06,
	0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69,
	0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70,
	0x69, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52,
	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
	0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c,
	0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
	0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f,
	0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
	0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20,
	0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22,
	0x48, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
	0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04,
	0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70,
	0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63,
	0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63,
	0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
	0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
	0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65,
	0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72,
	0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71,
	0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65,
	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72,
	0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71,
	0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65,
	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_keyservice_keyservice_proto_rawDescOnce sync.Once
	file_keyservice_keyservice_proto_rawDescData = file_keyservice_keyservice_proto_rawDesc
)

func file_keyservice_keyservice_proto_rawDescGZIP() []byte {
	file_keyservice_keyservice_proto_rawDescOnce.Do(func() {
		file_keyservice_keyservice_proto_rawDescData = protoimpl.X.CompressGZIP(file_keyservice_keyservice_proto_rawDescData)
	})
	return file_keyservice_keyservice_proto_rawDescData
}

var file_keyservice_keyservice_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_keyservice_keyservice_proto_goTypes = []interface{}{
	(*Key)(nil),              // 0: Key
	(*PgpKey)(nil),           // 1: PgpKey
	(*KmsKey)(nil),           // 2: KmsKey
	(*GcpKmsKey)(nil),        // 3: GcpKmsKey
	(*VaultKey)(nil),         // 4: VaultKey
	(*AzureKeyVaultKey)(nil), // 5: AzureKeyVaultKey
	(*AgeKey)(nil),           // 6: AgeKey
	(*EncryptRequest)(nil),   // 7: EncryptRequest
	(*EncryptResponse)(nil),  // 8: EncryptResponse
	(*DecryptRequest)(nil),   // 9: DecryptRequest
	(*DecryptResponse)(nil),  // 10: DecryptResponse
	nil,                      // 11: KmsKey.ContextEntry
}
var file_keyservice_keyservice_proto_depIdxs = []int32{
	2,  // 0: Key.kms_key:type_name -> KmsKey
	1,  // 1: Key.pgp_key:type_name -> PgpKey
	3,  // 2: Key.gcp_kms_key:type_name -> GcpKmsKey
	5,  // 3: Key.azure_keyvault_key:type_name -> AzureKeyVaultKey
	4,  // 4: Key.vault_key:type_name -> VaultKey
	6,  // 5: Key.age_key:type_name -> AgeKey
	11, // 6: KmsKey.context:type_name -> KmsKey.ContextEntry
	0,  // 7: EncryptRequest.key:type_name -> Key
	0,  // 8: DecryptRequest.key:type_name -> Key
	7,  // 9: KeyService.Encrypt:input_type -> EncryptRequest
	9,  // 10: KeyService.Decrypt:input_type -> DecryptRequest
	8,  // 11: KeyService.Encrypt:output_type -> EncryptResponse
	10, // 12: KeyService.Decrypt:output_type -> DecryptResponse
	11, // [11:13] is the sub-list for method output_type
	9,  // [9:11] is the sub-list for method input_type
	9,  // [9:9] is the sub-list for extension type_name
	9,  // [9:9] is the sub-list for extension extendee
	0,  // [0:9] is the sub-list for field type_name
}

func init() { file_keyservice_keyservice_proto_init() }
func file_keyservice_keyservice_proto_init() {
	if File_keyservice_keyservice_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_keyservice_keyservice_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Key); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*PgpKey); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*KmsKey); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*GcpKmsKey); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*VaultKey); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*AzureKeyVaultKey); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*AgeKey); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*EncryptRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*EncryptResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*DecryptRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_keyservice_keyservice_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*DecryptResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	file_keyservice_keyservice_proto_msgTypes[0].OneofWrappers = []interface{}{
		(*Key_KmsKey)(nil),
		(*Key_PgpKey)(nil),
		(*Key_GcpKmsKey)(nil),
		(*Key_AzureKeyvaultKey)(nil),
		(*Key_VaultKey)(nil),
		(*Key_AgeKey)(nil),
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_keyservice_keyservice_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   12,
			NumExtensions: 0,
			NumServices:   1,
		},
		GoTypes:           file_keyservice_keyservice_proto_goTypes,
		DependencyIndexes: file_keyservice_keyservice_proto_depIdxs,
		MessageInfos:      file_keyservice_keyservice_proto_msgTypes,
	}.Build()
	File_keyservice_keyservice_proto = out.File
	file_keyservice_keyservice_proto_rawDesc = nil
	file_keyservice_keyservice_proto_goTypes = nil
	file_keyservice_keyservice_proto_depIdxs = nil
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6

// KeyServiceClient is the client API for KeyService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type KeyServiceClient interface {
	Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error)
	Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error)
}

type keyServiceClient struct {
	cc grpc.ClientConnInterface
}

func NewKeyServiceClient(cc grpc.ClientConnInterface) KeyServiceClient {
	return &keyServiceClient{cc}
}

func (c *keyServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) {
	out := new(EncryptResponse)
	err := c.cc.Invoke(ctx, "/KeyService/Encrypt", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *keyServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) {
	out := new(DecryptResponse)
	err := c.cc.Invoke(ctx, "/KeyService/Decrypt", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// KeyServiceServer is the server API for KeyService service.
type KeyServiceServer interface {
	Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error)
	Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error)
}

// UnimplementedKeyServiceServer can be embedded to have forward compatible implementations.
type UnimplementedKeyServiceServer struct {
}

func (*UnimplementedKeyServiceServer) Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method Encrypt not implemented")
}
func (*UnimplementedKeyServiceServer) Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method Decrypt not implemented")
}

func RegisterKeyServiceServer(s *grpc.Server, srv KeyServiceServer) {
	s.RegisterService(&_KeyService_serviceDesc, srv)
}

func _KeyService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(EncryptRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(KeyServiceServer).Encrypt(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/KeyService/Encrypt",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(KeyServiceServer).Encrypt(ctx, req.(*EncryptRequest))
	}
	return interceptor(ctx, in, info, handler)
}

func _KeyService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(DecryptRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(KeyServiceServer).Decrypt(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/KeyService/Decrypt",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(KeyServiceServer).Decrypt(ctx, req.(*DecryptRequest))
	}
	return interceptor(ctx, in, info, handler)
}

var _KeyService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "KeyService",
	HandlerType: (*KeyServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "Encrypt",
			Handler:    _KeyService_Encrypt_Handler,
		},
		{
			MethodName: "Decrypt",
			Handler:    _KeyService_Decrypt_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "keyservice/keyservice.proto",
}
0707010000007D000081A400000000000000000000000162794F9300000409000000000000000000000000000000000000002700000000sops-3.7.3/keyservice/keyservice.protosyntax = "proto3";

message Key {
	oneof key_type {
		KmsKey kms_key = 1;
		PgpKey pgp_key = 2;
		GcpKmsKey gcp_kms_key = 3;
		AzureKeyVaultKey azure_keyvault_key = 4;
		VaultKey vault_key = 5;
		AgeKey age_key = 6;
	}
}

message PgpKey {
	string fingerprint = 1;
}

message KmsKey {
	string arn = 1;
	string role = 2;
	map<string, string> context = 3;
	string aws_profile = 4;
}

message GcpKmsKey {
	string resource_id = 1;
}

message VaultKey {
	string vault_address = 1;
	string engine_path = 2;
	string key_name = 3;
}

message AzureKeyVaultKey {
	string vault_url = 1;
	string name = 2;
	string version = 3;
}

message AgeKey {
	string recipient = 1;
}

message EncryptRequest {
	Key key = 1;
	bytes plaintext = 2;
}

message EncryptResponse {
	bytes ciphertext = 1;
}

message DecryptRequest {
	Key key = 1;
	bytes ciphertext = 2;
}

message DecryptResponse {
	bytes plaintext = 1;
}

service KeyService {
	rpc Encrypt (EncryptRequest) returns (EncryptResponse) {}
	rpc Decrypt (DecryptRequest) returns (DecryptResponse) {}
}
0707010000007E000081A400000000000000000000000162794F93000022F9000000000000000000000000000000000000002000000000sops-3.7.3/keyservice/server.gopackage keyservice

import (
	"fmt"

	"go.mozilla.org/sops/v3/age"
	"go.mozilla.org/sops/v3/azkv"
	"go.mozilla.org/sops/v3/gcpkms"
	"go.mozilla.org/sops/v3/hcvault"
	"go.mozilla.org/sops/v3/kms"
	"go.mozilla.org/sops/v3/pgp"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// Server is a key service server that uses SOPS MasterKeys to fulfill requests
type Server struct {
	// Prompt indicates whether the server should prompt before decrypting or encrypting data
	Prompt bool
}

func (ks *Server) encryptWithPgp(key *PgpKey, plaintext []byte) ([]byte, error) {
	pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
	err := pgpKey.Encrypt(plaintext)
	if err != nil {
		return nil, err
	}
	return []byte(pgpKey.EncryptedKey), nil
}

func (ks *Server) encryptWithKms(key *KmsKey, plaintext []byte) ([]byte, error) {
	kmsKey := kmsKeyToMasterKey(key)
	err := kmsKey.Encrypt(plaintext)
	if err != nil {
		return nil, err
	}
	return []byte(kmsKey.EncryptedKey), nil
}

func (ks *Server) encryptWithGcpKms(key *GcpKmsKey, plaintext []byte) ([]byte, error) {
	gcpKmsKey := gcpkms.MasterKey{
		ResourceID: key.ResourceId,
	}
	err := gcpKmsKey.Encrypt(plaintext)
	if err != nil {
		return nil, err
	}
	return []byte(gcpKmsKey.EncryptedKey), nil
}

func (ks *Server) encryptWithAzureKeyVault(key *AzureKeyVaultKey, plaintext []byte) ([]byte, error) {
	azkvKey := azkv.MasterKey{
		VaultURL: key.VaultUrl,
		Name:     key.Name,
		Version:  key.Version,
	}
	err := azkvKey.Encrypt(plaintext)
	if err != nil {
		return nil, err
	}
	return []byte(azkvKey.EncryptedKey), nil
}

func (ks *Server) encryptWithVault(key *VaultKey, plaintext []byte) ([]byte, error) {
	vaultKey := hcvault.MasterKey{
		VaultAddress: key.VaultAddress,
		EnginePath:   key.EnginePath,
		KeyName:      key.KeyName,
	}
	err := vaultKey.Encrypt(plaintext)
	if err != nil {
		return nil, err
	}
	return []byte(vaultKey.EncryptedKey), nil
}

func (ks *Server) encryptWithAge(key *AgeKey, plaintext []byte) ([]byte, error) {
	ageKey := age.MasterKey{
		Recipient: key.Recipient,
	}

	if err := ageKey.Encrypt(plaintext); err != nil {
		return nil, err
	}

	return []byte(ageKey.EncryptedKey), nil
}

func (ks *Server) decryptWithPgp(key *PgpKey, ciphertext []byte) ([]byte, error) {
	pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
	pgpKey.EncryptedKey = string(ciphertext)
	plaintext, err := pgpKey.Decrypt()
	return []byte(plaintext), err
}

func (ks *Server) decryptWithKms(key *KmsKey, ciphertext []byte) ([]byte, error) {
	kmsKey := kmsKeyToMasterKey(key)
	kmsKey.EncryptedKey = string(ciphertext)
	plaintext, err := kmsKey.Decrypt()
	return []byte(plaintext), err
}

func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte, error) {
	gcpKmsKey := gcpkms.MasterKey{
		ResourceID: key.ResourceId,
	}
	gcpKmsKey.EncryptedKey = string(ciphertext)
	plaintext, err := gcpKmsKey.Decrypt()
	return []byte(plaintext), err
}

func (ks *Server) decryptWithAzureKeyVault(key *AzureKeyVaultKey, ciphertext []byte) ([]byte, error) {
	azkvKey := azkv.MasterKey{
		VaultURL: key.VaultUrl,
		Name:     key.Name,
		Version:  key.Version,
	}
	azkvKey.EncryptedKey = string(ciphertext)
	plaintext, err := azkvKey.Decrypt()
	return []byte(plaintext), err
}

func (ks *Server) decryptWithVault(key *VaultKey, ciphertext []byte) ([]byte, error) {
	vaultKey := hcvault.MasterKey{
		VaultAddress: key.VaultAddress,
		EnginePath:   key.EnginePath,
		KeyName:      key.KeyName,
	}
	vaultKey.EncryptedKey = string(ciphertext)
	plaintext, err := vaultKey.Decrypt()
	return []byte(plaintext), err
}

func (ks *Server) decryptWithAge(key *AgeKey, ciphertext []byte) ([]byte, error) {
	ageKey := age.MasterKey{
		Recipient: key.Recipient,
	}
	ageKey.EncryptedKey = string(ciphertext)
	plaintext, err := ageKey.Decrypt()
	return []byte(plaintext), err
}

// Encrypt takes an encrypt request and encrypts the provided plaintext with the provided key, returning the encrypted
// result
func (ks Server) Encrypt(ctx context.Context,
	req *EncryptRequest) (*EncryptResponse, error) {
	key := *req.Key
	var response *EncryptResponse
	switch k := key.KeyType.(type) {
	case *Key_PgpKey:
		ciphertext, err := ks.encryptWithPgp(k.PgpKey, req.Plaintext)
		if err != nil {
			return nil, err
		}
		response = &EncryptResponse{
			Ciphertext: ciphertext,
		}
	case *Key_KmsKey:
		ciphertext, err := ks.encryptWithKms(k.KmsKey, req.Plaintext)
		if err != nil {
			return nil, err
		}
		response = &EncryptResponse{
			Ciphertext: ciphertext,
		}
	case *Key_GcpKmsKey:
		ciphertext, err := ks.encryptWithGcpKms(k.GcpKmsKey, req.Plaintext)
		if err != nil {
			return nil, err
		}
		response = &EncryptResponse{
			Ciphertext: ciphertext,
		}
	case *Key_AzureKeyvaultKey:
		ciphertext, err := ks.encryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Plaintext)
		if err != nil {
			return nil, err
		}
		response = &EncryptResponse{
			Ciphertext: ciphertext,
		}
	case *Key_VaultKey:
		ciphertext, err := ks.encryptWithVault(k.VaultKey, req.Plaintext)
		if err != nil {
			return nil, err
		}
		response = &EncryptResponse{
			Ciphertext: ciphertext,
		}
	case *Key_AgeKey:
		ciphertext, err := ks.encryptWithAge(k.AgeKey, req.Plaintext)
		if err != nil {
			return nil, err
		}
		response = &EncryptResponse{
			Ciphertext: ciphertext,
		}
	case nil:
		return nil, status.Errorf(codes.NotFound, "Must provide a key")
	default:
		return nil, status.Errorf(codes.NotFound, "Unknown key type")
	}
	if ks.Prompt {
		err := ks.prompt(key, "encrypt")
		if err != nil {
			return nil, err
		}
	}
	return response, nil
}

func keyToString(key Key) string {
	switch k := key.KeyType.(type) {
	case *Key_PgpKey:
		return fmt.Sprintf("PGP key with fingerprint %s", k.PgpKey.Fingerprint)
	case *Key_KmsKey:
		return fmt.Sprintf("AWS KMS key with ARN %s", k.KmsKey.Arn)
	case *Key_GcpKmsKey:
		return fmt.Sprintf("GCP KMS key with resource ID %s", k.GcpKmsKey.ResourceId)
	case *Key_AzureKeyvaultKey:
		return fmt.Sprintf("Azure Key Vault key with URL %s/keys/%s/%s", k.AzureKeyvaultKey.VaultUrl, k.AzureKeyvaultKey.Name, k.AzureKeyvaultKey.Version)
	case *Key_VaultKey:
		return fmt.Sprintf("Hashicorp Vault key with URI %s/v1/%s/keys/%s", k.VaultKey.VaultAddress, k.VaultKey.EnginePath, k.VaultKey.KeyName)
	default:
		return fmt.Sprintf("Unknown key type")
	}
}

func (ks Server) prompt(key Key, requestType string) error {
	keyString := keyToString(key)
	var response string
	for response != "y" && response != "n" {
		fmt.Printf("\nReceived %s request using %s. Respond to request? (y/n): ", requestType, keyString)
		_, err := fmt.Scanln(&response)
		if err != nil {
			return err
		}
	}
	if response == "n" {
		return grpc.Errorf(codes.PermissionDenied, "Request rejected by user")
	}
	return nil
}

// Decrypt takes a decrypt request and decrypts the provided ciphertext with the provided key, returning the decrypted
// result
func (ks Server) Decrypt(ctx context.Context,
	req *DecryptRequest) (*DecryptResponse, error) {
	key := *req.Key
	var response *DecryptResponse
	switch k := key.KeyType.(type) {
	case *Key_PgpKey:
		plaintext, err := ks.decryptWithPgp(k.PgpKey, req.Ciphertext)
		if err != nil {
			return nil, err
		}
		response = &DecryptResponse{
			Plaintext: plaintext,
		}
	case *Key_KmsKey:
		plaintext, err := ks.decryptWithKms(k.KmsKey, req.Ciphertext)
		if err != nil {
			return nil, err
		}
		response = &DecryptResponse{
			Plaintext: plaintext,
		}
	case *Key_GcpKmsKey:
		plaintext, err := ks.decryptWithGcpKms(k.GcpKmsKey, req.Ciphertext)
		if err != nil {
			return nil, err
		}
		response = &DecryptResponse{
			Plaintext: plaintext,
		}
	case *Key_AzureKeyvaultKey:
		plaintext, err := ks.decryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Ciphertext)
		if err != nil {
			return nil, err
		}
		response = &DecryptResponse{
			Plaintext: plaintext,
		}
	case *Key_VaultKey:
		plaintext, err := ks.decryptWithVault(k.VaultKey, req.Ciphertext)
		if err != nil {
			return nil, err
		}
		response = &DecryptResponse{
			Plaintext: plaintext,
		}
	case *Key_AgeKey:
		plaintext, err := ks.decryptWithAge(k.AgeKey, req.Ciphertext)
		if err != nil {
			return nil, err
		}
		response = &DecryptResponse{
			Plaintext: plaintext,
		}
	case nil:
		return nil, grpc.Errorf(codes.NotFound, "Must provide a key")
	default:
		return nil, grpc.Errorf(codes.NotFound, "Unknown key type")
	}
	if ks.Prompt {
		err := ks.prompt(key, "decrypt")
		if err != nil {
			return nil, err
		}
	}
	return response, nil
}

func kmsKeyToMasterKey(key *KmsKey) kms.MasterKey {
	ctx := make(map[string]*string)
	for k, v := range key.Context {
		value := v // Allocate a new string to prevent the pointer below from referring to only the last iteration value
		ctx[k] = &value
	}
	return kms.MasterKey{
		Arn:               key.Arn,
		Role:              key.Role,
		EncryptionContext: ctx,
		AwsProfile:        key.AwsProfile,
	}
}
0707010000007F000081A400000000000000000000000162794F9300000A08000000000000000000000000000000000000002500000000sops-3.7.3/keyservice/server_test.gopackage keyservice

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"testing"
)

func TestKmsKeyToMasterKey(t *testing.T) {

	cases := []struct {
		description        string
		expectedArn        string
		expectedRole       string
		expectedCtx        map[string]string
		expectedAwsProfile string
	}{
		{
			description:        "empty context",
			expectedArn:        "arn:aws:kms:eu-west-1:123456789012:key/d5c90a06-f824-4628-922b-12424571ed4d",
			expectedRole:       "ExampleRole",
			expectedCtx:        map[string]string{},
			expectedAwsProfile: "",
		},
		{
			description:  "context with one key-value pair",
			expectedArn:  "arn:aws:kms:eu-west-1:123456789012:key/d5c90a06-f824-4628-922b-12424571ed4d",
			expectedRole: "",
			expectedCtx: map[string]string{
				"firstKey": "first value",
			},
			expectedAwsProfile: "ExampleProfile",
		},
		{
			description:  "context with three key-value pairs",
			expectedArn:  "arn:aws:kms:eu-west-1:123456789012:key/d5c90a06-f824-4628-922b-12424571ed4d",
			expectedRole: "",
			expectedCtx: map[string]string{
				"firstKey":  "first value",
				"secondKey": "second value",
				"thirdKey":  "third value",
			},
			expectedAwsProfile: "",
		},
	}

	for _, c := range cases {

		t.Run(c.description, func(t *testing.T) {

			inputCtx := make(map[string]string)
			for k, v := range c.expectedCtx {
				inputCtx[k] = v
			}

			key := &KmsKey{
				Arn:        c.expectedArn,
				Role:       c.expectedRole,
				Context:    inputCtx,
				AwsProfile: c.expectedAwsProfile,
			}

			masterKey := kmsKeyToMasterKey(key)
			foundCtx := masterKey.EncryptionContext

			for k := range c.expectedCtx {
				require.Containsf(t, foundCtx, k, "Context does not contain expected key '%s'", k)
			}
			for k := range foundCtx {
				require.Containsf(t, c.expectedCtx, k, "Context contains an unexpected key '%s' which cannot be found from expected map", k)
			}
			for k, expected := range c.expectedCtx {
				foundVal := *foundCtx[k]
				assert.Equalf(t, expected, foundVal, "Context key '%s' value '%s' does not match expected value '%s'", k, foundVal, expected)
			}
			assert.Equalf(t, c.expectedArn, masterKey.Arn, "Expected ARN to be '%s', but found '%s'", c.expectedArn, masterKey.Arn)
			assert.Equalf(t, c.expectedRole, masterKey.Role, "Expected Role to be '%s', but found '%s'", c.expectedRole, masterKey.Role)
			assert.Equalf(t, c.expectedAwsProfile, masterKey.AwsProfile, "Expected AWS profile to be '%s', but found '%s'", c.expectedAwsProfile, masterKey.AwsProfile)
		})
	}
}
07070100000080000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/kms07070100000081000081A400000000000000000000000162794F930000229C000000000000000000000000000000000000001C00000000sops-3.7.3/kms/keysource.go/*
Package kms contains an implementation of the go.mozilla.org/sops/v3.MasterKey interface that encrypts and decrypts the
data key using AWS KMS with the AWS Go SDK.
*/
package kms //import "go.mozilla.org/sops/v3/kms"

import (
	"encoding/base64"
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"go.mozilla.org/sops/v3/logging"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/kms/kmsiface"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/sirupsen/logrus"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("AWSKMS")
}

// this needs to be a global var for unit tests to work (mockKMS redefines
// it in keysource_test.go)
var kmsSvc kmsiface.KMSAPI
var isMocked bool

// MasterKey is a AWS KMS key used to encrypt and decrypt sops' data key.
type MasterKey struct {
	Arn               string
	Role              string
	EncryptedKey      string
	CreationDate      time.Time
	EncryptionContext map[string]*string
	AwsProfile        string
}

// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)
}

// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
	key.EncryptedKey = string(enc)
}

// Encrypt takes a sops data key, encrypts it with KMS and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
	// isMocked is set by unit test to indicate that the KMS service
	// has already been initialized. it's ugly, but it works.
	if kmsSvc == nil || !isMocked {
		sess, err := key.createSession()
		if err != nil {
			log.WithField("arn", key.Arn).Info("Encryption failed")
			return fmt.Errorf("Failed to create session: %w", err)
		}
		kmsSvc = kms.New(sess)
	}
	out, err := kmsSvc.Encrypt(&kms.EncryptInput{Plaintext: dataKey, KeyId: &key.Arn, EncryptionContext: key.EncryptionContext})
	if err != nil {
		log.WithField("arn", key.Arn).Info("Encryption failed")
		return fmt.Errorf("Failed to call KMS encryption service: %w", err)
	}
	key.EncryptedKey = base64.StdEncoding.EncodeToString(out.CiphertextBlob)
	log.WithField("arn", key.Arn).Info("Encryption succeeded")
	return nil
}

// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(dataKey)
	}
	return nil
}

// Decrypt decrypts the EncryptedKey field with AWS KMS and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
	k, err := base64.StdEncoding.DecodeString(key.EncryptedKey)
	if err != nil {
		log.WithField("arn", key.Arn).Info("Decryption failed")
		return nil, fmt.Errorf("Error base64-decoding encrypted data key: %s", err)
	}
	// isMocked is set by unit test to indicate that the KMS service
	// has already been initialized. it's ugly, but it works.
	if kmsSvc == nil || !isMocked {
		sess, err := key.createSession()
		if err != nil {
			log.WithField("arn", key.Arn).Info("Decryption failed")
			return nil, fmt.Errorf("Error creating AWS session: %w", err)
		}
		kmsSvc = kms.New(sess)
	}
	decrypted, err := kmsSvc.Decrypt(&kms.DecryptInput{CiphertextBlob: k, EncryptionContext: key.EncryptionContext})
	if err != nil {
		log.WithField("arn", key.Arn).Info("Decryption failed")
		return nil, fmt.Errorf("Error decrypting key: %w", err)
	}
	log.WithField("arn", key.Arn).Info("Decryption succeeded")
	return decrypted.Plaintext, nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}

// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
	return key.Arn
}

// NewMasterKey creates a new MasterKey from an ARN, role and context, setting the creation date to the current date
func NewMasterKey(arn string, role string, context map[string]*string) *MasterKey {
	return &MasterKey{
		Arn:               arn,
		Role:              role,
		EncryptionContext: context,
		CreationDate:      time.Now().UTC(),
	}
}

// NewMasterKeyFromArn takes an ARN string and returns a new MasterKey for that ARN
func NewMasterKeyFromArn(arn string, context map[string]*string, awsProfile string) *MasterKey {
	k := &MasterKey{}
	arn = strings.Replace(arn, " ", "", -1)
	roleIndex := strings.Index(arn, "+arn:aws:iam::")
	if roleIndex > 0 {
		k.Arn = arn[:roleIndex]
		k.Role = arn[roleIndex+1:]
	} else {
		k.Arn = arn
	}
	k.EncryptionContext = context
	k.CreationDate = time.Now().UTC()
	k.AwsProfile = awsProfile
	return k
}

// MasterKeysFromArnString takes a comma separated list of AWS KMS ARNs and returns a slice of new MasterKeys for those ARNs
func MasterKeysFromArnString(arn string, context map[string]*string, awsProfile string) []*MasterKey {
	var keys []*MasterKey
	if arn == "" {
		return keys
	}
	for _, s := range strings.Split(arn, ",") {
		keys = append(keys, NewMasterKeyFromArn(s, context, awsProfile))
	}
	return keys
}

func (key MasterKey) createStsSession(config aws.Config, sess *session.Session) (*session.Session, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	stsRoleSessionNameRe, err := regexp.Compile("[^a-zA-Z0-9=,.@-]+")
	if err != nil {
		return nil, fmt.Errorf("Failed to compile STS role session name regex: %w", err)
	}
	sanitizedHostname := stsRoleSessionNameRe.ReplaceAllString(hostname, "")
	stsService := sts.New(sess)
	name := "sops@" + sanitizedHostname

	// Make sure the name is no longer than 64 characters (role session name length limit from AWS)
	roleSessionNameLengthLimit := 64
	if len(name) >= roleSessionNameLengthLimit {
		name = name[:roleSessionNameLengthLimit]
	}

	out, err := stsService.AssumeRole(&sts.AssumeRoleInput{
		RoleArn: &key.Role, RoleSessionName: &name})
	if err != nil {
		return nil, fmt.Errorf("Failed to assume role %q: %w", key.Role, err)
	}
	config.Credentials = credentials.NewStaticCredentials(*out.Credentials.AccessKeyId,
		*out.Credentials.SecretAccessKey, *out.Credentials.SessionToken)
	sess, err = session.NewSession(&config)
	if err != nil {
		return nil, fmt.Errorf("Failed to create new aws session: %w", err)
	}
	return sess, nil
}

func (key MasterKey) createSession() (*session.Session, error) {
	re := regexp.MustCompile(`^arn:aws[\w-]*:kms:(.+):[0-9]+:(key|alias)/.+$`)
	matches := re.FindStringSubmatch(key.Arn)
	if matches == nil {
		return nil, fmt.Errorf("No valid ARN found in %q", key.Arn)
	}

	config := aws.Config{Region: aws.String(matches[1])}

	opts := session.Options{
		Profile:                 key.AwsProfile,
		Config:                  config,
		AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
		SharedConfigState:       session.SharedConfigEnable,
	}
	sess, err := session.NewSessionWithOptions(opts)
	if err != nil {
		return nil, err
	}
	if key.Role != "" {
		return key.createStsSession(config, sess)
	}
	return sess, nil
}

// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
	out := make(map[string]interface{})
	out["arn"] = key.Arn
	if key.Role != "" {
		out["role"] = key.Role
	}
	out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
	out["enc"] = key.EncryptedKey
	if key.EncryptionContext != nil {
		outcontext := make(map[string]string)
		for k, v := range key.EncryptionContext {
			outcontext[k] = *v
		}
		out["context"] = outcontext
	}
	return out
}

// ParseKMSContext takes either a KMS context map or a comma-separated list of KMS context key:value pairs and returns a map
func ParseKMSContext(in interface{}) map[string]*string {
	nonStringValueWarning := "Encryption context contains a non-string value, context will not be used"
	out := make(map[string]*string)

	switch in := in.(type) {
	case map[string]interface{}:
		if len(in) == 0 {
			return nil
		}
		for k, v := range in {
			value, ok := v.(string)
			if !ok {
				log.Warn(nonStringValueWarning)
				return nil
			}
			out[k] = &value
		}
	case map[interface{}]interface{}:
		if len(in) == 0 {
			return nil
		}
		for k, v := range in {
			key, ok := k.(string)
			if !ok {
				log.Warn(nonStringValueWarning)
				return nil
			}
			value, ok := v.(string)
			if !ok {
				log.Warn(nonStringValueWarning)
				return nil
			}
			out[key] = &value
		}
	case string:
		if in == "" {
			return nil
		}
		for _, kv := range strings.Split(in, ",") {
			kv := strings.Split(kv, ":")
			if len(kv) != 2 {
				log.Warn(nonStringValueWarning)
				return nil
			}
			out[kv[0]] = &kv[1]
		}
	}
	return out
}
07070100000082000081A400000000000000000000000162794F9300001042000000000000000000000000000000000000002100000000sops-3.7.3/kms/keysource_test.gopackage kms

import (
	"bytes"
	"testing"
	"testing/quick"
	"time"

	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"go.mozilla.org/sops/v3/kms/mocks"
)

func TestKMS(t *testing.T) {
	mockKMS := &mocks.KMSAPI{}
	defer mockKMS.AssertExpectations(t)
	kmsSvc = mockKMS
	isMocked = true
	encryptOutput := &kms.EncryptOutput{}
	decryptOutput := &kms.DecryptOutput{}
	mockKMS.On("Encrypt", mock.AnythingOfType("*kms.EncryptInput")).Return(encryptOutput, nil).Run(func(args mock.Arguments) {
		encryptOutput.CiphertextBlob = args.Get(0).(*kms.EncryptInput).Plaintext
	})
	mockKMS.On("Decrypt", mock.AnythingOfType("*kms.DecryptInput")).Return(decryptOutput, nil).Run(func(args mock.Arguments) {
		decryptOutput.Plaintext = args.Get(0).(*kms.DecryptInput).CiphertextBlob
	})
	k := MasterKey{Arn: "arn:aws:kms:us-east-1:927034868273:key/e9fc75db-05e9-44c1-9c35-633922bac347", Role: "", EncryptedKey: ""}
	f := func(x []byte) bool {
		err := k.Encrypt(x)
		if err != nil {
			log.Println(err)
		}
		v, err := k.Decrypt()
		if err != nil {
			log.Println(err)
		}
		return bytes.Equal(v, x)
	}
	config := quick.Config{}
	if testing.Short() {
		config.MaxCount = 10
	}
	if err := quick.Check(f, &config); err != nil {
		t.Error(err)
	}
}

func TestKMSKeySourceFromString(t *testing.T) {
	s := "arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e+arn:aws:iam::927034868273:role/sops-dev, arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d"
	ks := MasterKeysFromArnString(s, nil, "foo")
	k1 := ks[0]
	k2 := ks[1]
	expectedArn1 := "arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e"
	expectedRole1 := "arn:aws:iam::927034868273:role/sops-dev"
	if k1.Arn != expectedArn1 {
		t.Errorf("ARN mismatch. Expected %s, found %s", expectedArn1, k1.Arn)
	}
	if k1.Role != expectedRole1 {
		t.Errorf("Role mismatch. Expected %s, found %s", expectedRole1, k1.Role)
	}
	expectedArn2 := "arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d"
	expectedRole2 := ""
	if k2.Arn != expectedArn2 {
		t.Errorf("ARN mismatch. Expected %s, found %s", expectedArn2, k2.Arn)
	}
	if k2.Role != expectedRole2 {
		t.Errorf("Role mismatch. Expected empty role, found %s.", k2.Role)
	}
}

func TestParseEncryptionContext(t *testing.T) {
	value1 := "value1"
	value2 := "value2"
	// map from YAML
	var yamlmap = map[interface{}]interface{}{
		"key1": value1,
		"key2": value2,
	}
	assert.Equal(t, ParseKMSContext(yamlmap), map[string]*string{
		"key1": &value1,
		"key2": &value2,
	})
	assert.Nil(t, ParseKMSContext(map[interface{}]interface{}{}))
	assert.Nil(t, ParseKMSContext(map[interface{}]interface{}{
		"key1": 1,
	}))
	assert.Nil(t, ParseKMSContext(map[interface{}]interface{}{
		1: "value",
	}))
	// map from JSON
	var jsonmap = map[string]interface{}{
		"key1": value1,
		"key2": value2,
	}
	assert.Equal(t, ParseKMSContext(jsonmap), map[string]*string{
		"key1": &value1,
		"key2": &value2,
	})
	assert.Nil(t, ParseKMSContext(map[string]interface{}{}))
	assert.Nil(t, ParseKMSContext(map[string]interface{}{
		"key1": 1,
	}))
	// sops 2.0.x formatted encryption context as a comma-separated list of key:value pairs
	assert.Equal(t, ParseKMSContext("key1:value1,key2:value2"), map[string]*string{
		"key1": &value1,
		"key2": &value2,
	})
	assert.Equal(t, ParseKMSContext("key1:value1"), map[string]*string{
		"key1": &value1,
	})
	assert.Nil(t, ParseKMSContext("key1,key2:value2"))
	assert.Nil(t, ParseKMSContext("key1"))
}

func TestKeyToMap(t *testing.T) {
	value1 := "value1"
	value2 := "value2"
	key := MasterKey{
		CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
		Arn:          "foo",
		Role:         "bar",
		EncryptedKey: "this is encrypted",
		EncryptionContext: map[string]*string{
			"key1": &value1,
			"key2": &value2,
		},
	}
	assert.Equal(t, map[string]interface{}{
		"arn":        "foo",
		"role":       "bar",
		"enc":        "this is encrypted",
		"created_at": "2016-10-31T10:00:00Z",
		"context": map[string]string{
			"key1": value1,
			"key2": value2,
		},
	}, key.ToMap())
}
07070100000083000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001500000000sops-3.7.3/kms/mocks07070100000084000081A400000000000000000000000162794F930001B9D8000000000000000000000000000000000000001F00000000sops-3.7.3/kms/mocks/KMSAPI.go// Code generated by mockery v1.0.0. DO NOT EDIT.

package mocks

import (
	context "context"

	kms "github.com/aws/aws-sdk-go/service/kms"

	mock "github.com/stretchr/testify/mock"

	request "github.com/aws/aws-sdk-go/aws/request"
)

// KMSAPI is an autogenerated mock type for the KMSAPI type
type KMSAPI struct {
	mock.Mock
}

// CancelKeyDeletion provides a mock function with given fields: _a0
func (_m *KMSAPI) CancelKeyDeletion(_a0 *kms.CancelKeyDeletionInput) (*kms.CancelKeyDeletionOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.CancelKeyDeletionOutput
	if rf, ok := ret.Get(0).(func(*kms.CancelKeyDeletionInput) *kms.CancelKeyDeletionOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CancelKeyDeletionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.CancelKeyDeletionInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CancelKeyDeletionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CancelKeyDeletionRequest(_a0 *kms.CancelKeyDeletionInput) (*request.Request, *kms.CancelKeyDeletionOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.CancelKeyDeletionInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.CancelKeyDeletionOutput
	if rf, ok := ret.Get(1).(func(*kms.CancelKeyDeletionInput) *kms.CancelKeyDeletionOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.CancelKeyDeletionOutput)
		}
	}

	return r0, r1
}

// CancelKeyDeletionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CancelKeyDeletionWithContext(_a0 context.Context, _a1 *kms.CancelKeyDeletionInput, _a2 ...request.Option) (*kms.CancelKeyDeletionOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.CancelKeyDeletionOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.CancelKeyDeletionInput, ...request.Option) *kms.CancelKeyDeletionOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CancelKeyDeletionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.CancelKeyDeletionInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ConnectCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) ConnectCustomKeyStore(_a0 *kms.ConnectCustomKeyStoreInput) (*kms.ConnectCustomKeyStoreOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ConnectCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(*kms.ConnectCustomKeyStoreInput) *kms.ConnectCustomKeyStoreOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ConnectCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ConnectCustomKeyStoreInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ConnectCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ConnectCustomKeyStoreRequest(_a0 *kms.ConnectCustomKeyStoreInput) (*request.Request, *kms.ConnectCustomKeyStoreOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ConnectCustomKeyStoreInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ConnectCustomKeyStoreOutput
	if rf, ok := ret.Get(1).(func(*kms.ConnectCustomKeyStoreInput) *kms.ConnectCustomKeyStoreOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ConnectCustomKeyStoreOutput)
		}
	}

	return r0, r1
}

// ConnectCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ConnectCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.ConnectCustomKeyStoreInput, _a2 ...request.Option) (*kms.ConnectCustomKeyStoreOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ConnectCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ConnectCustomKeyStoreInput, ...request.Option) *kms.ConnectCustomKeyStoreOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ConnectCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ConnectCustomKeyStoreInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateAlias provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateAlias(_a0 *kms.CreateAliasInput) (*kms.CreateAliasOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.CreateAliasOutput
	if rf, ok := ret.Get(0).(func(*kms.CreateAliasInput) *kms.CreateAliasOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateAliasOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.CreateAliasInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateAliasRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateAliasRequest(_a0 *kms.CreateAliasInput) (*request.Request, *kms.CreateAliasOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.CreateAliasInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.CreateAliasOutput
	if rf, ok := ret.Get(1).(func(*kms.CreateAliasInput) *kms.CreateAliasOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.CreateAliasOutput)
		}
	}

	return r0, r1
}

// CreateAliasWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateAliasWithContext(_a0 context.Context, _a1 *kms.CreateAliasInput, _a2 ...request.Option) (*kms.CreateAliasOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.CreateAliasOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateAliasInput, ...request.Option) *kms.CreateAliasOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateAliasOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateAliasInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateCustomKeyStore(_a0 *kms.CreateCustomKeyStoreInput) (*kms.CreateCustomKeyStoreOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.CreateCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(*kms.CreateCustomKeyStoreInput) *kms.CreateCustomKeyStoreOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.CreateCustomKeyStoreInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateCustomKeyStoreRequest(_a0 *kms.CreateCustomKeyStoreInput) (*request.Request, *kms.CreateCustomKeyStoreOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.CreateCustomKeyStoreInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.CreateCustomKeyStoreOutput
	if rf, ok := ret.Get(1).(func(*kms.CreateCustomKeyStoreInput) *kms.CreateCustomKeyStoreOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.CreateCustomKeyStoreOutput)
		}
	}

	return r0, r1
}

// CreateCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.CreateCustomKeyStoreInput, _a2 ...request.Option) (*kms.CreateCustomKeyStoreOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.CreateCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateCustomKeyStoreInput, ...request.Option) *kms.CreateCustomKeyStoreOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateCustomKeyStoreInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateGrant provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateGrant(_a0 *kms.CreateGrantInput) (*kms.CreateGrantOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.CreateGrantOutput
	if rf, ok := ret.Get(0).(func(*kms.CreateGrantInput) *kms.CreateGrantOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateGrantOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.CreateGrantInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateGrantRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateGrantRequest(_a0 *kms.CreateGrantInput) (*request.Request, *kms.CreateGrantOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.CreateGrantInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.CreateGrantOutput
	if rf, ok := ret.Get(1).(func(*kms.CreateGrantInput) *kms.CreateGrantOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.CreateGrantOutput)
		}
	}

	return r0, r1
}

// CreateGrantWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateGrantWithContext(_a0 context.Context, _a1 *kms.CreateGrantInput, _a2 ...request.Option) (*kms.CreateGrantOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.CreateGrantOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateGrantInput, ...request.Option) *kms.CreateGrantOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateGrantOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateGrantInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateKey provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateKey(_a0 *kms.CreateKeyInput) (*kms.CreateKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.CreateKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.CreateKeyInput) *kms.CreateKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.CreateKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// CreateKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateKeyRequest(_a0 *kms.CreateKeyInput) (*request.Request, *kms.CreateKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.CreateKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.CreateKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.CreateKeyInput) *kms.CreateKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.CreateKeyOutput)
		}
	}

	return r0, r1
}

// CreateKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateKeyWithContext(_a0 context.Context, _a1 *kms.CreateKeyInput, _a2 ...request.Option) (*kms.CreateKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.CreateKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateKeyInput, ...request.Option) *kms.CreateKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.CreateKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// Decrypt provides a mock function with given fields: _a0
func (_m *KMSAPI) Decrypt(_a0 *kms.DecryptInput) (*kms.DecryptOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DecryptOutput
	if rf, ok := ret.Get(0).(func(*kms.DecryptInput) *kms.DecryptOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DecryptOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DecryptInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DecryptRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DecryptRequest(_a0 *kms.DecryptInput) (*request.Request, *kms.DecryptOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DecryptInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DecryptOutput
	if rf, ok := ret.Get(1).(func(*kms.DecryptInput) *kms.DecryptOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DecryptOutput)
		}
	}

	return r0, r1
}

// DecryptWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DecryptWithContext(_a0 context.Context, _a1 *kms.DecryptInput, _a2 ...request.Option) (*kms.DecryptOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DecryptOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DecryptInput, ...request.Option) *kms.DecryptOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DecryptOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DecryptInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DeleteAlias provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteAlias(_a0 *kms.DeleteAliasInput) (*kms.DeleteAliasOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DeleteAliasOutput
	if rf, ok := ret.Get(0).(func(*kms.DeleteAliasInput) *kms.DeleteAliasOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DeleteAliasOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DeleteAliasInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DeleteAliasRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteAliasRequest(_a0 *kms.DeleteAliasInput) (*request.Request, *kms.DeleteAliasOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DeleteAliasInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DeleteAliasOutput
	if rf, ok := ret.Get(1).(func(*kms.DeleteAliasInput) *kms.DeleteAliasOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DeleteAliasOutput)
		}
	}

	return r0, r1
}

// DeleteAliasWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DeleteAliasWithContext(_a0 context.Context, _a1 *kms.DeleteAliasInput, _a2 ...request.Option) (*kms.DeleteAliasOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DeleteAliasOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DeleteAliasInput, ...request.Option) *kms.DeleteAliasOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DeleteAliasOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DeleteAliasInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DeleteCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteCustomKeyStore(_a0 *kms.DeleteCustomKeyStoreInput) (*kms.DeleteCustomKeyStoreOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DeleteCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(*kms.DeleteCustomKeyStoreInput) *kms.DeleteCustomKeyStoreOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DeleteCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DeleteCustomKeyStoreInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DeleteCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteCustomKeyStoreRequest(_a0 *kms.DeleteCustomKeyStoreInput) (*request.Request, *kms.DeleteCustomKeyStoreOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DeleteCustomKeyStoreInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DeleteCustomKeyStoreOutput
	if rf, ok := ret.Get(1).(func(*kms.DeleteCustomKeyStoreInput) *kms.DeleteCustomKeyStoreOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DeleteCustomKeyStoreOutput)
		}
	}

	return r0, r1
}

// DeleteCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DeleteCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.DeleteCustomKeyStoreInput, _a2 ...request.Option) (*kms.DeleteCustomKeyStoreOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DeleteCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DeleteCustomKeyStoreInput, ...request.Option) *kms.DeleteCustomKeyStoreOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DeleteCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DeleteCustomKeyStoreInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DeleteImportedKeyMaterial provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteImportedKeyMaterial(_a0 *kms.DeleteImportedKeyMaterialInput) (*kms.DeleteImportedKeyMaterialOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DeleteImportedKeyMaterialOutput
	if rf, ok := ret.Get(0).(func(*kms.DeleteImportedKeyMaterialInput) *kms.DeleteImportedKeyMaterialOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DeleteImportedKeyMaterialOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DeleteImportedKeyMaterialInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DeleteImportedKeyMaterialRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteImportedKeyMaterialRequest(_a0 *kms.DeleteImportedKeyMaterialInput) (*request.Request, *kms.DeleteImportedKeyMaterialOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DeleteImportedKeyMaterialInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DeleteImportedKeyMaterialOutput
	if rf, ok := ret.Get(1).(func(*kms.DeleteImportedKeyMaterialInput) *kms.DeleteImportedKeyMaterialOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DeleteImportedKeyMaterialOutput)
		}
	}

	return r0, r1
}

// DeleteImportedKeyMaterialWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DeleteImportedKeyMaterialWithContext(_a0 context.Context, _a1 *kms.DeleteImportedKeyMaterialInput, _a2 ...request.Option) (*kms.DeleteImportedKeyMaterialOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DeleteImportedKeyMaterialOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DeleteImportedKeyMaterialInput, ...request.Option) *kms.DeleteImportedKeyMaterialOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DeleteImportedKeyMaterialOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DeleteImportedKeyMaterialInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DescribeCustomKeyStores provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeCustomKeyStores(_a0 *kms.DescribeCustomKeyStoresInput) (*kms.DescribeCustomKeyStoresOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DescribeCustomKeyStoresOutput
	if rf, ok := ret.Get(0).(func(*kms.DescribeCustomKeyStoresInput) *kms.DescribeCustomKeyStoresOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DescribeCustomKeyStoresOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DescribeCustomKeyStoresInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DescribeCustomKeyStoresRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeCustomKeyStoresRequest(_a0 *kms.DescribeCustomKeyStoresInput) (*request.Request, *kms.DescribeCustomKeyStoresOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DescribeCustomKeyStoresInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DescribeCustomKeyStoresOutput
	if rf, ok := ret.Get(1).(func(*kms.DescribeCustomKeyStoresInput) *kms.DescribeCustomKeyStoresOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DescribeCustomKeyStoresOutput)
		}
	}

	return r0, r1
}

// DescribeCustomKeyStoresWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DescribeCustomKeyStoresWithContext(_a0 context.Context, _a1 *kms.DescribeCustomKeyStoresInput, _a2 ...request.Option) (*kms.DescribeCustomKeyStoresOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DescribeCustomKeyStoresOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DescribeCustomKeyStoresInput, ...request.Option) *kms.DescribeCustomKeyStoresOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DescribeCustomKeyStoresOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DescribeCustomKeyStoresInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DescribeKey provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeKey(_a0 *kms.DescribeKeyInput) (*kms.DescribeKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DescribeKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.DescribeKeyInput) *kms.DescribeKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DescribeKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DescribeKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DescribeKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeKeyRequest(_a0 *kms.DescribeKeyInput) (*request.Request, *kms.DescribeKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DescribeKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DescribeKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.DescribeKeyInput) *kms.DescribeKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DescribeKeyOutput)
		}
	}

	return r0, r1
}

// DescribeKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DescribeKeyWithContext(_a0 context.Context, _a1 *kms.DescribeKeyInput, _a2 ...request.Option) (*kms.DescribeKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DescribeKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DescribeKeyInput, ...request.Option) *kms.DescribeKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DescribeKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DescribeKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DisableKey provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKey(_a0 *kms.DisableKeyInput) (*kms.DisableKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DisableKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.DisableKeyInput) *kms.DisableKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DisableKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DisableKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DisableKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKeyRequest(_a0 *kms.DisableKeyInput) (*request.Request, *kms.DisableKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DisableKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DisableKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.DisableKeyInput) *kms.DisableKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DisableKeyOutput)
		}
	}

	return r0, r1
}

// DisableKeyRotation provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKeyRotation(_a0 *kms.DisableKeyRotationInput) (*kms.DisableKeyRotationOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DisableKeyRotationOutput
	if rf, ok := ret.Get(0).(func(*kms.DisableKeyRotationInput) *kms.DisableKeyRotationOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DisableKeyRotationOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DisableKeyRotationInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DisableKeyRotationRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKeyRotationRequest(_a0 *kms.DisableKeyRotationInput) (*request.Request, *kms.DisableKeyRotationOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DisableKeyRotationInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DisableKeyRotationOutput
	if rf, ok := ret.Get(1).(func(*kms.DisableKeyRotationInput) *kms.DisableKeyRotationOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DisableKeyRotationOutput)
		}
	}

	return r0, r1
}

// DisableKeyRotationWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DisableKeyRotationWithContext(_a0 context.Context, _a1 *kms.DisableKeyRotationInput, _a2 ...request.Option) (*kms.DisableKeyRotationOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DisableKeyRotationOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DisableKeyRotationInput, ...request.Option) *kms.DisableKeyRotationOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DisableKeyRotationOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DisableKeyRotationInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DisableKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DisableKeyWithContext(_a0 context.Context, _a1 *kms.DisableKeyInput, _a2 ...request.Option) (*kms.DisableKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DisableKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DisableKeyInput, ...request.Option) *kms.DisableKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DisableKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DisableKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DisconnectCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) DisconnectCustomKeyStore(_a0 *kms.DisconnectCustomKeyStoreInput) (*kms.DisconnectCustomKeyStoreOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.DisconnectCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(*kms.DisconnectCustomKeyStoreInput) *kms.DisconnectCustomKeyStoreOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DisconnectCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.DisconnectCustomKeyStoreInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// DisconnectCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DisconnectCustomKeyStoreRequest(_a0 *kms.DisconnectCustomKeyStoreInput) (*request.Request, *kms.DisconnectCustomKeyStoreOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.DisconnectCustomKeyStoreInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.DisconnectCustomKeyStoreOutput
	if rf, ok := ret.Get(1).(func(*kms.DisconnectCustomKeyStoreInput) *kms.DisconnectCustomKeyStoreOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.DisconnectCustomKeyStoreOutput)
		}
	}

	return r0, r1
}

// DisconnectCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DisconnectCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.DisconnectCustomKeyStoreInput, _a2 ...request.Option) (*kms.DisconnectCustomKeyStoreOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.DisconnectCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.DisconnectCustomKeyStoreInput, ...request.Option) *kms.DisconnectCustomKeyStoreOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.DisconnectCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.DisconnectCustomKeyStoreInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// EnableKey provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKey(_a0 *kms.EnableKeyInput) (*kms.EnableKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.EnableKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.EnableKeyInput) *kms.EnableKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.EnableKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.EnableKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// EnableKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKeyRequest(_a0 *kms.EnableKeyInput) (*request.Request, *kms.EnableKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.EnableKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.EnableKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.EnableKeyInput) *kms.EnableKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.EnableKeyOutput)
		}
	}

	return r0, r1
}

// EnableKeyRotation provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKeyRotation(_a0 *kms.EnableKeyRotationInput) (*kms.EnableKeyRotationOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.EnableKeyRotationOutput
	if rf, ok := ret.Get(0).(func(*kms.EnableKeyRotationInput) *kms.EnableKeyRotationOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.EnableKeyRotationOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.EnableKeyRotationInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// EnableKeyRotationRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKeyRotationRequest(_a0 *kms.EnableKeyRotationInput) (*request.Request, *kms.EnableKeyRotationOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.EnableKeyRotationInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.EnableKeyRotationOutput
	if rf, ok := ret.Get(1).(func(*kms.EnableKeyRotationInput) *kms.EnableKeyRotationOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.EnableKeyRotationOutput)
		}
	}

	return r0, r1
}

// EnableKeyRotationWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) EnableKeyRotationWithContext(_a0 context.Context, _a1 *kms.EnableKeyRotationInput, _a2 ...request.Option) (*kms.EnableKeyRotationOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.EnableKeyRotationOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.EnableKeyRotationInput, ...request.Option) *kms.EnableKeyRotationOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.EnableKeyRotationOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.EnableKeyRotationInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// EnableKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) EnableKeyWithContext(_a0 context.Context, _a1 *kms.EnableKeyInput, _a2 ...request.Option) (*kms.EnableKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.EnableKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.EnableKeyInput, ...request.Option) *kms.EnableKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.EnableKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.EnableKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// Encrypt provides a mock function with given fields: _a0
func (_m *KMSAPI) Encrypt(_a0 *kms.EncryptInput) (*kms.EncryptOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.EncryptOutput
	if rf, ok := ret.Get(0).(func(*kms.EncryptInput) *kms.EncryptOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.EncryptOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.EncryptInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// EncryptRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) EncryptRequest(_a0 *kms.EncryptInput) (*request.Request, *kms.EncryptOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.EncryptInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.EncryptOutput
	if rf, ok := ret.Get(1).(func(*kms.EncryptInput) *kms.EncryptOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.EncryptOutput)
		}
	}

	return r0, r1
}

// EncryptWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) EncryptWithContext(_a0 context.Context, _a1 *kms.EncryptInput, _a2 ...request.Option) (*kms.EncryptOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.EncryptOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.EncryptInput, ...request.Option) *kms.EncryptOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.EncryptOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.EncryptInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKey provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKey(_a0 *kms.GenerateDataKeyInput) (*kms.GenerateDataKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GenerateDataKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyInput) *kms.GenerateDataKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyPair provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPair(_a0 *kms.GenerateDataKeyPairInput) (*kms.GenerateDataKeyPairOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GenerateDataKeyPairOutput
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairInput) *kms.GenerateDataKeyPairOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyPairOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyPairRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPairRequest(_a0 *kms.GenerateDataKeyPairInput) (*request.Request, *kms.GenerateDataKeyPairOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GenerateDataKeyPairOutput
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairInput) *kms.GenerateDataKeyPairOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GenerateDataKeyPairOutput)
		}
	}

	return r0, r1
}

// GenerateDataKeyPairWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyPairWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyPairInput, _a2 ...request.Option) (*kms.GenerateDataKeyPairOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GenerateDataKeyPairOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyPairInput, ...request.Option) *kms.GenerateDataKeyPairOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyPairOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyPairInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyPairWithoutPlaintext provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPairWithoutPlaintext(_a0 *kms.GenerateDataKeyPairWithoutPlaintextInput) (*kms.GenerateDataKeyPairWithoutPlaintextOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GenerateDataKeyPairWithoutPlaintextOutput
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) *kms.GenerateDataKeyPairWithoutPlaintextOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyPairWithoutPlaintextOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyPairWithoutPlaintextRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPairWithoutPlaintextRequest(_a0 *kms.GenerateDataKeyPairWithoutPlaintextInput) (*request.Request, *kms.GenerateDataKeyPairWithoutPlaintextOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GenerateDataKeyPairWithoutPlaintextOutput
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) *kms.GenerateDataKeyPairWithoutPlaintextOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GenerateDataKeyPairWithoutPlaintextOutput)
		}
	}

	return r0, r1
}

// GenerateDataKeyPairWithoutPlaintextWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyPairWithoutPlaintextWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyPairWithoutPlaintextInput, _a2 ...request.Option) (*kms.GenerateDataKeyPairWithoutPlaintextOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GenerateDataKeyPairWithoutPlaintextOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyPairWithoutPlaintextInput, ...request.Option) *kms.GenerateDataKeyPairWithoutPlaintextOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyPairWithoutPlaintextOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyPairWithoutPlaintextInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyRequest(_a0 *kms.GenerateDataKeyInput) (*request.Request, *kms.GenerateDataKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GenerateDataKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyInput) *kms.GenerateDataKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GenerateDataKeyOutput)
		}
	}

	return r0, r1
}

// GenerateDataKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyInput, _a2 ...request.Option) (*kms.GenerateDataKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GenerateDataKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyInput, ...request.Option) *kms.GenerateDataKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyWithoutPlaintext provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyWithoutPlaintext(_a0 *kms.GenerateDataKeyWithoutPlaintextInput) (*kms.GenerateDataKeyWithoutPlaintextOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GenerateDataKeyWithoutPlaintextOutput
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyWithoutPlaintextInput) *kms.GenerateDataKeyWithoutPlaintextOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyWithoutPlaintextOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyWithoutPlaintextInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateDataKeyWithoutPlaintextRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyWithoutPlaintextRequest(_a0 *kms.GenerateDataKeyWithoutPlaintextInput) (*request.Request, *kms.GenerateDataKeyWithoutPlaintextOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyWithoutPlaintextInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GenerateDataKeyWithoutPlaintextOutput
	if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyWithoutPlaintextInput) *kms.GenerateDataKeyWithoutPlaintextOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GenerateDataKeyWithoutPlaintextOutput)
		}
	}

	return r0, r1
}

// GenerateDataKeyWithoutPlaintextWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyWithoutPlaintextWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyWithoutPlaintextInput, _a2 ...request.Option) (*kms.GenerateDataKeyWithoutPlaintextOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GenerateDataKeyWithoutPlaintextOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyWithoutPlaintextInput, ...request.Option) *kms.GenerateDataKeyWithoutPlaintextOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateDataKeyWithoutPlaintextOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyWithoutPlaintextInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateMac provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateMac(_a0 *kms.GenerateMacInput) (*kms.GenerateMacOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GenerateMacOutput
	if rf, ok := ret.Get(0).(func(*kms.GenerateMacInput) *kms.GenerateMacOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateMacOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GenerateMacInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateMacRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateMacRequest(_a0 *kms.GenerateMacInput) (*request.Request, *kms.GenerateMacOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GenerateMacInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GenerateMacOutput
	if rf, ok := ret.Get(1).(func(*kms.GenerateMacInput) *kms.GenerateMacOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GenerateMacOutput)
		}
	}

	return r0, r1
}

// GenerateMacWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateMacWithContext(_a0 context.Context, _a1 *kms.GenerateMacInput, _a2 ...request.Option) (*kms.GenerateMacOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GenerateMacOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateMacInput, ...request.Option) *kms.GenerateMacOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateMacOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateMacInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateRandom provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateRandom(_a0 *kms.GenerateRandomInput) (*kms.GenerateRandomOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GenerateRandomOutput
	if rf, ok := ret.Get(0).(func(*kms.GenerateRandomInput) *kms.GenerateRandomOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateRandomOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GenerateRandomInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GenerateRandomRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateRandomRequest(_a0 *kms.GenerateRandomInput) (*request.Request, *kms.GenerateRandomOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GenerateRandomInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GenerateRandomOutput
	if rf, ok := ret.Get(1).(func(*kms.GenerateRandomInput) *kms.GenerateRandomOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GenerateRandomOutput)
		}
	}

	return r0, r1
}

// GenerateRandomWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateRandomWithContext(_a0 context.Context, _a1 *kms.GenerateRandomInput, _a2 ...request.Option) (*kms.GenerateRandomOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GenerateRandomOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateRandomInput, ...request.Option) *kms.GenerateRandomOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GenerateRandomOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateRandomInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetKeyPolicy provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyPolicy(_a0 *kms.GetKeyPolicyInput) (*kms.GetKeyPolicyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GetKeyPolicyOutput
	if rf, ok := ret.Get(0).(func(*kms.GetKeyPolicyInput) *kms.GetKeyPolicyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetKeyPolicyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GetKeyPolicyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetKeyPolicyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyPolicyRequest(_a0 *kms.GetKeyPolicyInput) (*request.Request, *kms.GetKeyPolicyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GetKeyPolicyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GetKeyPolicyOutput
	if rf, ok := ret.Get(1).(func(*kms.GetKeyPolicyInput) *kms.GetKeyPolicyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GetKeyPolicyOutput)
		}
	}

	return r0, r1
}

// GetKeyPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetKeyPolicyWithContext(_a0 context.Context, _a1 *kms.GetKeyPolicyInput, _a2 ...request.Option) (*kms.GetKeyPolicyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GetKeyPolicyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GetKeyPolicyInput, ...request.Option) *kms.GetKeyPolicyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetKeyPolicyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GetKeyPolicyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetKeyRotationStatus provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyRotationStatus(_a0 *kms.GetKeyRotationStatusInput) (*kms.GetKeyRotationStatusOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GetKeyRotationStatusOutput
	if rf, ok := ret.Get(0).(func(*kms.GetKeyRotationStatusInput) *kms.GetKeyRotationStatusOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetKeyRotationStatusOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GetKeyRotationStatusInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetKeyRotationStatusRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyRotationStatusRequest(_a0 *kms.GetKeyRotationStatusInput) (*request.Request, *kms.GetKeyRotationStatusOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GetKeyRotationStatusInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GetKeyRotationStatusOutput
	if rf, ok := ret.Get(1).(func(*kms.GetKeyRotationStatusInput) *kms.GetKeyRotationStatusOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GetKeyRotationStatusOutput)
		}
	}

	return r0, r1
}

// GetKeyRotationStatusWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetKeyRotationStatusWithContext(_a0 context.Context, _a1 *kms.GetKeyRotationStatusInput, _a2 ...request.Option) (*kms.GetKeyRotationStatusOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GetKeyRotationStatusOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GetKeyRotationStatusInput, ...request.Option) *kms.GetKeyRotationStatusOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetKeyRotationStatusOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GetKeyRotationStatusInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetParametersForImport provides a mock function with given fields: _a0
func (_m *KMSAPI) GetParametersForImport(_a0 *kms.GetParametersForImportInput) (*kms.GetParametersForImportOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GetParametersForImportOutput
	if rf, ok := ret.Get(0).(func(*kms.GetParametersForImportInput) *kms.GetParametersForImportOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetParametersForImportOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GetParametersForImportInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetParametersForImportRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetParametersForImportRequest(_a0 *kms.GetParametersForImportInput) (*request.Request, *kms.GetParametersForImportOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GetParametersForImportInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GetParametersForImportOutput
	if rf, ok := ret.Get(1).(func(*kms.GetParametersForImportInput) *kms.GetParametersForImportOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GetParametersForImportOutput)
		}
	}

	return r0, r1
}

// GetParametersForImportWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetParametersForImportWithContext(_a0 context.Context, _a1 *kms.GetParametersForImportInput, _a2 ...request.Option) (*kms.GetParametersForImportOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GetParametersForImportOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GetParametersForImportInput, ...request.Option) *kms.GetParametersForImportOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetParametersForImportOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GetParametersForImportInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetPublicKey provides a mock function with given fields: _a0
func (_m *KMSAPI) GetPublicKey(_a0 *kms.GetPublicKeyInput) (*kms.GetPublicKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.GetPublicKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.GetPublicKeyInput) *kms.GetPublicKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetPublicKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.GetPublicKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetPublicKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetPublicKeyRequest(_a0 *kms.GetPublicKeyInput) (*request.Request, *kms.GetPublicKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.GetPublicKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.GetPublicKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.GetPublicKeyInput) *kms.GetPublicKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.GetPublicKeyOutput)
		}
	}

	return r0, r1
}

// GetPublicKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetPublicKeyWithContext(_a0 context.Context, _a1 *kms.GetPublicKeyInput, _a2 ...request.Option) (*kms.GetPublicKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.GetPublicKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.GetPublicKeyInput, ...request.Option) *kms.GetPublicKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.GetPublicKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.GetPublicKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ImportKeyMaterial provides a mock function with given fields: _a0
func (_m *KMSAPI) ImportKeyMaterial(_a0 *kms.ImportKeyMaterialInput) (*kms.ImportKeyMaterialOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ImportKeyMaterialOutput
	if rf, ok := ret.Get(0).(func(*kms.ImportKeyMaterialInput) *kms.ImportKeyMaterialOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ImportKeyMaterialOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ImportKeyMaterialInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ImportKeyMaterialRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ImportKeyMaterialRequest(_a0 *kms.ImportKeyMaterialInput) (*request.Request, *kms.ImportKeyMaterialOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ImportKeyMaterialInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ImportKeyMaterialOutput
	if rf, ok := ret.Get(1).(func(*kms.ImportKeyMaterialInput) *kms.ImportKeyMaterialOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ImportKeyMaterialOutput)
		}
	}

	return r0, r1
}

// ImportKeyMaterialWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ImportKeyMaterialWithContext(_a0 context.Context, _a1 *kms.ImportKeyMaterialInput, _a2 ...request.Option) (*kms.ImportKeyMaterialOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ImportKeyMaterialOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ImportKeyMaterialInput, ...request.Option) *kms.ImportKeyMaterialOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ImportKeyMaterialOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ImportKeyMaterialInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListAliases provides a mock function with given fields: _a0
func (_m *KMSAPI) ListAliases(_a0 *kms.ListAliasesInput) (*kms.ListAliasesOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ListAliasesOutput
	if rf, ok := ret.Get(0).(func(*kms.ListAliasesInput) *kms.ListAliasesOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListAliasesOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ListAliasesInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListAliasesPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListAliasesPages(_a0 *kms.ListAliasesInput, _a1 func(*kms.ListAliasesOutput, bool) bool) error {
	ret := _m.Called(_a0, _a1)

	var r0 error
	if rf, ok := ret.Get(0).(func(*kms.ListAliasesInput, func(*kms.ListAliasesOutput, bool) bool) error); ok {
		r0 = rf(_a0, _a1)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListAliasesPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListAliasesPagesWithContext(_a0 context.Context, _a1 *kms.ListAliasesInput, _a2 func(*kms.ListAliasesOutput, bool) bool, _a3 ...request.Option) error {
	_va := make([]interface{}, len(_a3))
	for _i := range _a3 {
		_va[_i] = _a3[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1, _a2)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 error
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListAliasesInput, func(*kms.ListAliasesOutput, bool) bool, ...request.Option) error); ok {
		r0 = rf(_a0, _a1, _a2, _a3...)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListAliasesRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListAliasesRequest(_a0 *kms.ListAliasesInput) (*request.Request, *kms.ListAliasesOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ListAliasesInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ListAliasesOutput
	if rf, ok := ret.Get(1).(func(*kms.ListAliasesInput) *kms.ListAliasesOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ListAliasesOutput)
		}
	}

	return r0, r1
}

// ListAliasesWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListAliasesWithContext(_a0 context.Context, _a1 *kms.ListAliasesInput, _a2 ...request.Option) (*kms.ListAliasesOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ListAliasesOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListAliasesInput, ...request.Option) *kms.ListAliasesOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListAliasesOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ListAliasesInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListGrants provides a mock function with given fields: _a0
func (_m *KMSAPI) ListGrants(_a0 *kms.ListGrantsInput) (*kms.ListGrantsResponse, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ListGrantsResponse
	if rf, ok := ret.Get(0).(func(*kms.ListGrantsInput) *kms.ListGrantsResponse); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListGrantsResponse)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ListGrantsInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListGrantsPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListGrantsPages(_a0 *kms.ListGrantsInput, _a1 func(*kms.ListGrantsResponse, bool) bool) error {
	ret := _m.Called(_a0, _a1)

	var r0 error
	if rf, ok := ret.Get(0).(func(*kms.ListGrantsInput, func(*kms.ListGrantsResponse, bool) bool) error); ok {
		r0 = rf(_a0, _a1)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListGrantsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListGrantsPagesWithContext(_a0 context.Context, _a1 *kms.ListGrantsInput, _a2 func(*kms.ListGrantsResponse, bool) bool, _a3 ...request.Option) error {
	_va := make([]interface{}, len(_a3))
	for _i := range _a3 {
		_va[_i] = _a3[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1, _a2)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 error
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListGrantsInput, func(*kms.ListGrantsResponse, bool) bool, ...request.Option) error); ok {
		r0 = rf(_a0, _a1, _a2, _a3...)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListGrantsRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListGrantsRequest(_a0 *kms.ListGrantsInput) (*request.Request, *kms.ListGrantsResponse) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ListGrantsInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ListGrantsResponse
	if rf, ok := ret.Get(1).(func(*kms.ListGrantsInput) *kms.ListGrantsResponse); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ListGrantsResponse)
		}
	}

	return r0, r1
}

// ListGrantsWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListGrantsWithContext(_a0 context.Context, _a1 *kms.ListGrantsInput, _a2 ...request.Option) (*kms.ListGrantsResponse, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ListGrantsResponse
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListGrantsInput, ...request.Option) *kms.ListGrantsResponse); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListGrantsResponse)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ListGrantsInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListKeyPolicies provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeyPolicies(_a0 *kms.ListKeyPoliciesInput) (*kms.ListKeyPoliciesOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ListKeyPoliciesOutput
	if rf, ok := ret.Get(0).(func(*kms.ListKeyPoliciesInput) *kms.ListKeyPoliciesOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListKeyPoliciesOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ListKeyPoliciesInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListKeyPoliciesPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListKeyPoliciesPages(_a0 *kms.ListKeyPoliciesInput, _a1 func(*kms.ListKeyPoliciesOutput, bool) bool) error {
	ret := _m.Called(_a0, _a1)

	var r0 error
	if rf, ok := ret.Get(0).(func(*kms.ListKeyPoliciesInput, func(*kms.ListKeyPoliciesOutput, bool) bool) error); ok {
		r0 = rf(_a0, _a1)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListKeyPoliciesPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListKeyPoliciesPagesWithContext(_a0 context.Context, _a1 *kms.ListKeyPoliciesInput, _a2 func(*kms.ListKeyPoliciesOutput, bool) bool, _a3 ...request.Option) error {
	_va := make([]interface{}, len(_a3))
	for _i := range _a3 {
		_va[_i] = _a3[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1, _a2)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 error
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeyPoliciesInput, func(*kms.ListKeyPoliciesOutput, bool) bool, ...request.Option) error); ok {
		r0 = rf(_a0, _a1, _a2, _a3...)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListKeyPoliciesRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeyPoliciesRequest(_a0 *kms.ListKeyPoliciesInput) (*request.Request, *kms.ListKeyPoliciesOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ListKeyPoliciesInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ListKeyPoliciesOutput
	if rf, ok := ret.Get(1).(func(*kms.ListKeyPoliciesInput) *kms.ListKeyPoliciesOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ListKeyPoliciesOutput)
		}
	}

	return r0, r1
}

// ListKeyPoliciesWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListKeyPoliciesWithContext(_a0 context.Context, _a1 *kms.ListKeyPoliciesInput, _a2 ...request.Option) (*kms.ListKeyPoliciesOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ListKeyPoliciesOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeyPoliciesInput, ...request.Option) *kms.ListKeyPoliciesOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListKeyPoliciesOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ListKeyPoliciesInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListKeys provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeys(_a0 *kms.ListKeysInput) (*kms.ListKeysOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ListKeysOutput
	if rf, ok := ret.Get(0).(func(*kms.ListKeysInput) *kms.ListKeysOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListKeysOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ListKeysInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListKeysPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListKeysPages(_a0 *kms.ListKeysInput, _a1 func(*kms.ListKeysOutput, bool) bool) error {
	ret := _m.Called(_a0, _a1)

	var r0 error
	if rf, ok := ret.Get(0).(func(*kms.ListKeysInput, func(*kms.ListKeysOutput, bool) bool) error); ok {
		r0 = rf(_a0, _a1)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListKeysPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListKeysPagesWithContext(_a0 context.Context, _a1 *kms.ListKeysInput, _a2 func(*kms.ListKeysOutput, bool) bool, _a3 ...request.Option) error {
	_va := make([]interface{}, len(_a3))
	for _i := range _a3 {
		_va[_i] = _a3[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1, _a2)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 error
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeysInput, func(*kms.ListKeysOutput, bool) bool, ...request.Option) error); ok {
		r0 = rf(_a0, _a1, _a2, _a3...)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// ListKeysRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeysRequest(_a0 *kms.ListKeysInput) (*request.Request, *kms.ListKeysOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ListKeysInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ListKeysOutput
	if rf, ok := ret.Get(1).(func(*kms.ListKeysInput) *kms.ListKeysOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ListKeysOutput)
		}
	}

	return r0, r1
}

// ListKeysWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListKeysWithContext(_a0 context.Context, _a1 *kms.ListKeysInput, _a2 ...request.Option) (*kms.ListKeysOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ListKeysOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeysInput, ...request.Option) *kms.ListKeysOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListKeysOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ListKeysInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListResourceTags provides a mock function with given fields: _a0
func (_m *KMSAPI) ListResourceTags(_a0 *kms.ListResourceTagsInput) (*kms.ListResourceTagsOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ListResourceTagsOutput
	if rf, ok := ret.Get(0).(func(*kms.ListResourceTagsInput) *kms.ListResourceTagsOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListResourceTagsOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ListResourceTagsInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListResourceTagsRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListResourceTagsRequest(_a0 *kms.ListResourceTagsInput) (*request.Request, *kms.ListResourceTagsOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ListResourceTagsInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ListResourceTagsOutput
	if rf, ok := ret.Get(1).(func(*kms.ListResourceTagsInput) *kms.ListResourceTagsOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ListResourceTagsOutput)
		}
	}

	return r0, r1
}

// ListResourceTagsWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListResourceTagsWithContext(_a0 context.Context, _a1 *kms.ListResourceTagsInput, _a2 ...request.Option) (*kms.ListResourceTagsOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ListResourceTagsOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListResourceTagsInput, ...request.Option) *kms.ListResourceTagsOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListResourceTagsOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ListResourceTagsInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListRetirableGrants provides a mock function with given fields: _a0
func (_m *KMSAPI) ListRetirableGrants(_a0 *kms.ListRetirableGrantsInput) (*kms.ListGrantsResponse, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ListGrantsResponse
	if rf, ok := ret.Get(0).(func(*kms.ListRetirableGrantsInput) *kms.ListGrantsResponse); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListGrantsResponse)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ListRetirableGrantsInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ListRetirableGrantsRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListRetirableGrantsRequest(_a0 *kms.ListRetirableGrantsInput) (*request.Request, *kms.ListGrantsResponse) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ListRetirableGrantsInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ListGrantsResponse
	if rf, ok := ret.Get(1).(func(*kms.ListRetirableGrantsInput) *kms.ListGrantsResponse); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ListGrantsResponse)
		}
	}

	return r0, r1
}

// ListRetirableGrantsWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListRetirableGrantsWithContext(_a0 context.Context, _a1 *kms.ListRetirableGrantsInput, _a2 ...request.Option) (*kms.ListGrantsResponse, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ListGrantsResponse
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ListRetirableGrantsInput, ...request.Option) *kms.ListGrantsResponse); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ListGrantsResponse)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ListRetirableGrantsInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// PutKeyPolicy provides a mock function with given fields: _a0
func (_m *KMSAPI) PutKeyPolicy(_a0 *kms.PutKeyPolicyInput) (*kms.PutKeyPolicyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.PutKeyPolicyOutput
	if rf, ok := ret.Get(0).(func(*kms.PutKeyPolicyInput) *kms.PutKeyPolicyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.PutKeyPolicyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.PutKeyPolicyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// PutKeyPolicyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) PutKeyPolicyRequest(_a0 *kms.PutKeyPolicyInput) (*request.Request, *kms.PutKeyPolicyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.PutKeyPolicyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.PutKeyPolicyOutput
	if rf, ok := ret.Get(1).(func(*kms.PutKeyPolicyInput) *kms.PutKeyPolicyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.PutKeyPolicyOutput)
		}
	}

	return r0, r1
}

// PutKeyPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) PutKeyPolicyWithContext(_a0 context.Context, _a1 *kms.PutKeyPolicyInput, _a2 ...request.Option) (*kms.PutKeyPolicyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.PutKeyPolicyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.PutKeyPolicyInput, ...request.Option) *kms.PutKeyPolicyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.PutKeyPolicyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.PutKeyPolicyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ReEncrypt provides a mock function with given fields: _a0
func (_m *KMSAPI) ReEncrypt(_a0 *kms.ReEncryptInput) (*kms.ReEncryptOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ReEncryptOutput
	if rf, ok := ret.Get(0).(func(*kms.ReEncryptInput) *kms.ReEncryptOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ReEncryptOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ReEncryptInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ReEncryptRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ReEncryptRequest(_a0 *kms.ReEncryptInput) (*request.Request, *kms.ReEncryptOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ReEncryptInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ReEncryptOutput
	if rf, ok := ret.Get(1).(func(*kms.ReEncryptInput) *kms.ReEncryptOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ReEncryptOutput)
		}
	}

	return r0, r1
}

// ReEncryptWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ReEncryptWithContext(_a0 context.Context, _a1 *kms.ReEncryptInput, _a2 ...request.Option) (*kms.ReEncryptOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ReEncryptOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ReEncryptInput, ...request.Option) *kms.ReEncryptOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ReEncryptOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ReEncryptInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ReplicateKey provides a mock function with given fields: _a0
func (_m *KMSAPI) ReplicateKey(_a0 *kms.ReplicateKeyInput) (*kms.ReplicateKeyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ReplicateKeyOutput
	if rf, ok := ret.Get(0).(func(*kms.ReplicateKeyInput) *kms.ReplicateKeyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ReplicateKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ReplicateKeyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ReplicateKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ReplicateKeyRequest(_a0 *kms.ReplicateKeyInput) (*request.Request, *kms.ReplicateKeyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ReplicateKeyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ReplicateKeyOutput
	if rf, ok := ret.Get(1).(func(*kms.ReplicateKeyInput) *kms.ReplicateKeyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ReplicateKeyOutput)
		}
	}

	return r0, r1
}

// ReplicateKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ReplicateKeyWithContext(_a0 context.Context, _a1 *kms.ReplicateKeyInput, _a2 ...request.Option) (*kms.ReplicateKeyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ReplicateKeyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ReplicateKeyInput, ...request.Option) *kms.ReplicateKeyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ReplicateKeyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ReplicateKeyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// RetireGrant provides a mock function with given fields: _a0
func (_m *KMSAPI) RetireGrant(_a0 *kms.RetireGrantInput) (*kms.RetireGrantOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.RetireGrantOutput
	if rf, ok := ret.Get(0).(func(*kms.RetireGrantInput) *kms.RetireGrantOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.RetireGrantOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.RetireGrantInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// RetireGrantRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) RetireGrantRequest(_a0 *kms.RetireGrantInput) (*request.Request, *kms.RetireGrantOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.RetireGrantInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.RetireGrantOutput
	if rf, ok := ret.Get(1).(func(*kms.RetireGrantInput) *kms.RetireGrantOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.RetireGrantOutput)
		}
	}

	return r0, r1
}

// RetireGrantWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) RetireGrantWithContext(_a0 context.Context, _a1 *kms.RetireGrantInput, _a2 ...request.Option) (*kms.RetireGrantOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.RetireGrantOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.RetireGrantInput, ...request.Option) *kms.RetireGrantOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.RetireGrantOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.RetireGrantInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// RevokeGrant provides a mock function with given fields: _a0
func (_m *KMSAPI) RevokeGrant(_a0 *kms.RevokeGrantInput) (*kms.RevokeGrantOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.RevokeGrantOutput
	if rf, ok := ret.Get(0).(func(*kms.RevokeGrantInput) *kms.RevokeGrantOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.RevokeGrantOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.RevokeGrantInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// RevokeGrantRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) RevokeGrantRequest(_a0 *kms.RevokeGrantInput) (*request.Request, *kms.RevokeGrantOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.RevokeGrantInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.RevokeGrantOutput
	if rf, ok := ret.Get(1).(func(*kms.RevokeGrantInput) *kms.RevokeGrantOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.RevokeGrantOutput)
		}
	}

	return r0, r1
}

// RevokeGrantWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) RevokeGrantWithContext(_a0 context.Context, _a1 *kms.RevokeGrantInput, _a2 ...request.Option) (*kms.RevokeGrantOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.RevokeGrantOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.RevokeGrantInput, ...request.Option) *kms.RevokeGrantOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.RevokeGrantOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.RevokeGrantInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ScheduleKeyDeletion provides a mock function with given fields: _a0
func (_m *KMSAPI) ScheduleKeyDeletion(_a0 *kms.ScheduleKeyDeletionInput) (*kms.ScheduleKeyDeletionOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.ScheduleKeyDeletionOutput
	if rf, ok := ret.Get(0).(func(*kms.ScheduleKeyDeletionInput) *kms.ScheduleKeyDeletionOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ScheduleKeyDeletionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.ScheduleKeyDeletionInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// ScheduleKeyDeletionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ScheduleKeyDeletionRequest(_a0 *kms.ScheduleKeyDeletionInput) (*request.Request, *kms.ScheduleKeyDeletionOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.ScheduleKeyDeletionInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.ScheduleKeyDeletionOutput
	if rf, ok := ret.Get(1).(func(*kms.ScheduleKeyDeletionInput) *kms.ScheduleKeyDeletionOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.ScheduleKeyDeletionOutput)
		}
	}

	return r0, r1
}

// ScheduleKeyDeletionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ScheduleKeyDeletionWithContext(_a0 context.Context, _a1 *kms.ScheduleKeyDeletionInput, _a2 ...request.Option) (*kms.ScheduleKeyDeletionOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.ScheduleKeyDeletionOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.ScheduleKeyDeletionInput, ...request.Option) *kms.ScheduleKeyDeletionOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.ScheduleKeyDeletionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.ScheduleKeyDeletionInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// Sign provides a mock function with given fields: _a0
func (_m *KMSAPI) Sign(_a0 *kms.SignInput) (*kms.SignOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.SignOutput
	if rf, ok := ret.Get(0).(func(*kms.SignInput) *kms.SignOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.SignOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.SignInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// SignRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) SignRequest(_a0 *kms.SignInput) (*request.Request, *kms.SignOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.SignInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.SignOutput
	if rf, ok := ret.Get(1).(func(*kms.SignInput) *kms.SignOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.SignOutput)
		}
	}

	return r0, r1
}

// SignWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) SignWithContext(_a0 context.Context, _a1 *kms.SignInput, _a2 ...request.Option) (*kms.SignOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.SignOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.SignInput, ...request.Option) *kms.SignOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.SignOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.SignInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// TagResource provides a mock function with given fields: _a0
func (_m *KMSAPI) TagResource(_a0 *kms.TagResourceInput) (*kms.TagResourceOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.TagResourceOutput
	if rf, ok := ret.Get(0).(func(*kms.TagResourceInput) *kms.TagResourceOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.TagResourceOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.TagResourceInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// TagResourceRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) TagResourceRequest(_a0 *kms.TagResourceInput) (*request.Request, *kms.TagResourceOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.TagResourceInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.TagResourceOutput
	if rf, ok := ret.Get(1).(func(*kms.TagResourceInput) *kms.TagResourceOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.TagResourceOutput)
		}
	}

	return r0, r1
}

// TagResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) TagResourceWithContext(_a0 context.Context, _a1 *kms.TagResourceInput, _a2 ...request.Option) (*kms.TagResourceOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.TagResourceOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.TagResourceInput, ...request.Option) *kms.TagResourceOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.TagResourceOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.TagResourceInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UntagResource provides a mock function with given fields: _a0
func (_m *KMSAPI) UntagResource(_a0 *kms.UntagResourceInput) (*kms.UntagResourceOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.UntagResourceOutput
	if rf, ok := ret.Get(0).(func(*kms.UntagResourceInput) *kms.UntagResourceOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UntagResourceOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.UntagResourceInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UntagResourceRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UntagResourceRequest(_a0 *kms.UntagResourceInput) (*request.Request, *kms.UntagResourceOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.UntagResourceInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.UntagResourceOutput
	if rf, ok := ret.Get(1).(func(*kms.UntagResourceInput) *kms.UntagResourceOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.UntagResourceOutput)
		}
	}

	return r0, r1
}

// UntagResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UntagResourceWithContext(_a0 context.Context, _a1 *kms.UntagResourceInput, _a2 ...request.Option) (*kms.UntagResourceOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.UntagResourceOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.UntagResourceInput, ...request.Option) *kms.UntagResourceOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UntagResourceOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.UntagResourceInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdateAlias provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateAlias(_a0 *kms.UpdateAliasInput) (*kms.UpdateAliasOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.UpdateAliasOutput
	if rf, ok := ret.Get(0).(func(*kms.UpdateAliasInput) *kms.UpdateAliasOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdateAliasOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.UpdateAliasInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdateAliasRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateAliasRequest(_a0 *kms.UpdateAliasInput) (*request.Request, *kms.UpdateAliasOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.UpdateAliasInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.UpdateAliasOutput
	if rf, ok := ret.Get(1).(func(*kms.UpdateAliasInput) *kms.UpdateAliasOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.UpdateAliasOutput)
		}
	}

	return r0, r1
}

// UpdateAliasWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdateAliasWithContext(_a0 context.Context, _a1 *kms.UpdateAliasInput, _a2 ...request.Option) (*kms.UpdateAliasOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.UpdateAliasOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdateAliasInput, ...request.Option) *kms.UpdateAliasOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdateAliasOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdateAliasInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdateCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateCustomKeyStore(_a0 *kms.UpdateCustomKeyStoreInput) (*kms.UpdateCustomKeyStoreOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.UpdateCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(*kms.UpdateCustomKeyStoreInput) *kms.UpdateCustomKeyStoreOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdateCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.UpdateCustomKeyStoreInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdateCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateCustomKeyStoreRequest(_a0 *kms.UpdateCustomKeyStoreInput) (*request.Request, *kms.UpdateCustomKeyStoreOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.UpdateCustomKeyStoreInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.UpdateCustomKeyStoreOutput
	if rf, ok := ret.Get(1).(func(*kms.UpdateCustomKeyStoreInput) *kms.UpdateCustomKeyStoreOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.UpdateCustomKeyStoreOutput)
		}
	}

	return r0, r1
}

// UpdateCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdateCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.UpdateCustomKeyStoreInput, _a2 ...request.Option) (*kms.UpdateCustomKeyStoreOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.UpdateCustomKeyStoreOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdateCustomKeyStoreInput, ...request.Option) *kms.UpdateCustomKeyStoreOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdateCustomKeyStoreOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdateCustomKeyStoreInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdateKeyDescription provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateKeyDescription(_a0 *kms.UpdateKeyDescriptionInput) (*kms.UpdateKeyDescriptionOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.UpdateKeyDescriptionOutput
	if rf, ok := ret.Get(0).(func(*kms.UpdateKeyDescriptionInput) *kms.UpdateKeyDescriptionOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdateKeyDescriptionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.UpdateKeyDescriptionInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdateKeyDescriptionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateKeyDescriptionRequest(_a0 *kms.UpdateKeyDescriptionInput) (*request.Request, *kms.UpdateKeyDescriptionOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.UpdateKeyDescriptionInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.UpdateKeyDescriptionOutput
	if rf, ok := ret.Get(1).(func(*kms.UpdateKeyDescriptionInput) *kms.UpdateKeyDescriptionOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.UpdateKeyDescriptionOutput)
		}
	}

	return r0, r1
}

// UpdateKeyDescriptionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdateKeyDescriptionWithContext(_a0 context.Context, _a1 *kms.UpdateKeyDescriptionInput, _a2 ...request.Option) (*kms.UpdateKeyDescriptionOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.UpdateKeyDescriptionOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdateKeyDescriptionInput, ...request.Option) *kms.UpdateKeyDescriptionOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdateKeyDescriptionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdateKeyDescriptionInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdatePrimaryRegion provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdatePrimaryRegion(_a0 *kms.UpdatePrimaryRegionInput) (*kms.UpdatePrimaryRegionOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.UpdatePrimaryRegionOutput
	if rf, ok := ret.Get(0).(func(*kms.UpdatePrimaryRegionInput) *kms.UpdatePrimaryRegionOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdatePrimaryRegionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.UpdatePrimaryRegionInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// UpdatePrimaryRegionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdatePrimaryRegionRequest(_a0 *kms.UpdatePrimaryRegionInput) (*request.Request, *kms.UpdatePrimaryRegionOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.UpdatePrimaryRegionInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.UpdatePrimaryRegionOutput
	if rf, ok := ret.Get(1).(func(*kms.UpdatePrimaryRegionInput) *kms.UpdatePrimaryRegionOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.UpdatePrimaryRegionOutput)
		}
	}

	return r0, r1
}

// UpdatePrimaryRegionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdatePrimaryRegionWithContext(_a0 context.Context, _a1 *kms.UpdatePrimaryRegionInput, _a2 ...request.Option) (*kms.UpdatePrimaryRegionOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.UpdatePrimaryRegionOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdatePrimaryRegionInput, ...request.Option) *kms.UpdatePrimaryRegionOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.UpdatePrimaryRegionOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdatePrimaryRegionInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// Verify provides a mock function with given fields: _a0
func (_m *KMSAPI) Verify(_a0 *kms.VerifyInput) (*kms.VerifyOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.VerifyOutput
	if rf, ok := ret.Get(0).(func(*kms.VerifyInput) *kms.VerifyOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.VerifyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.VerifyInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// VerifyMac provides a mock function with given fields: _a0
func (_m *KMSAPI) VerifyMac(_a0 *kms.VerifyMacInput) (*kms.VerifyMacOutput, error) {
	ret := _m.Called(_a0)

	var r0 *kms.VerifyMacOutput
	if rf, ok := ret.Get(0).(func(*kms.VerifyMacInput) *kms.VerifyMacOutput); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.VerifyMacOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(*kms.VerifyMacInput) error); ok {
		r1 = rf(_a0)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// VerifyMacRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) VerifyMacRequest(_a0 *kms.VerifyMacInput) (*request.Request, *kms.VerifyMacOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.VerifyMacInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.VerifyMacOutput
	if rf, ok := ret.Get(1).(func(*kms.VerifyMacInput) *kms.VerifyMacOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.VerifyMacOutput)
		}
	}

	return r0, r1
}

// VerifyMacWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) VerifyMacWithContext(_a0 context.Context, _a1 *kms.VerifyMacInput, _a2 ...request.Option) (*kms.VerifyMacOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.VerifyMacOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.VerifyMacInput, ...request.Option) *kms.VerifyMacOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.VerifyMacOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.VerifyMacInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// VerifyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) VerifyRequest(_a0 *kms.VerifyInput) (*request.Request, *kms.VerifyOutput) {
	ret := _m.Called(_a0)

	var r0 *request.Request
	if rf, ok := ret.Get(0).(func(*kms.VerifyInput) *request.Request); ok {
		r0 = rf(_a0)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*request.Request)
		}
	}

	var r1 *kms.VerifyOutput
	if rf, ok := ret.Get(1).(func(*kms.VerifyInput) *kms.VerifyOutput); ok {
		r1 = rf(_a0)
	} else {
		if ret.Get(1) != nil {
			r1 = ret.Get(1).(*kms.VerifyOutput)
		}
	}

	return r0, r1
}

// VerifyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) VerifyWithContext(_a0 context.Context, _a1 *kms.VerifyInput, _a2 ...request.Option) (*kms.VerifyOutput, error) {
	_va := make([]interface{}, len(_a2))
	for _i := range _a2 {
		_va[_i] = _a2[_i]
	}
	var _ca []interface{}
	_ca = append(_ca, _a0, _a1)
	_ca = append(_ca, _va...)
	ret := _m.Called(_ca...)

	var r0 *kms.VerifyOutput
	if rf, ok := ret.Get(0).(func(context.Context, *kms.VerifyInput, ...request.Option) *kms.VerifyOutput); ok {
		r0 = rf(_a0, _a1, _a2...)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(*kms.VerifyOutput)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(context.Context, *kms.VerifyInput, ...request.Option) error); ok {
		r1 = rf(_a0, _a1, _a2...)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}
07070100000085000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/logging07070100000086000081A400000000000000000000000162794F9300000467000000000000000000000000000000000000001E00000000sops-3.7.3/logging/logging.gopackage logging

import (
	"fmt"

	"github.com/fatih/color"
	"github.com/sirupsen/logrus"
)

func init() {
	Loggers = make(map[string]*logrus.Logger)
}

// TextFormatter extends the standard logrus TextFormatter and adds a field to specify the logger's name
type TextFormatter struct {
	LoggerName string
	logrus.TextFormatter
}

// Format formats a log entry onto bytes
func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	bytes, err := f.TextFormatter.Format(entry)
	name := color.New(color.Bold).Sprintf("[%s]", f.LoggerName)
	return []byte(fmt.Sprintf("%s\t %s", name, bytes)), err
}

// NewLogger is the constructor for a new Logger object with the given name
func NewLogger(name string) *logrus.Logger {
	log := logrus.New()
	log.SetLevel(logrus.WarnLevel)
	log.Formatter = &TextFormatter{
		LoggerName: name,
	}
	Loggers[name] = log
	return log
}

// SetLevel sets the given level for all current Loggers
func SetLevel(level logrus.Level) {
	for k := range Loggers {
		Loggers[k].SetLevel(level)
	}
}

// Loggers is the runtime map of logger name to logger object
var Loggers map[string]*logrus.Logger
07070100000087000081A400000000000000000000000162794F9300000270000000000000000000000000000000000000002100000000sops-3.7.3/make_download_page.sh#!/usr/bin/env bash
[ ! -d dist ] && mkdir dist
echo -e "<html>\n<head><title>Sops download page></title>\n<body>\n<h1>Sops download page</h1>\n<h2><a href="https://go.mozilla.org/sops/">go.mozilla.org/sops</a></h2>\n<table>" > index.html
IFS=$'\n'
for dist in $(aws s3 ls s3://go.mozilla.org/sops/dist/ | grep -P "deb|rpm"); do
	ts=$(echo $dist|awk '{print $1,$2}')
	size=$(echo $dist|awk '{print $3}')
	pkg=$(echo $dist|awk '{print $4}')
	echo -e "<tr><td>$ts</td><td>$size</td><td><a href=\"https://go.mozilla.org/sops/dist/$pkg\">$pkg</a></td></tr>" >> index.html
done
echo -e "</table>\n</body>\n</html>" >> index.html
07070100000088000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/pgp07070100000089000081A400000000000000000000000162794F930000296E000000000000000000000000000000000000001C00000000sops-3.7.3/pgp/keysource.go/*
Package pgp contains an implementation of the go.mozilla.org/sops/v3.MasterKey interface that encrypts and decrypts the
data key by first trying with the github.com/ProtonMail/go-crypto/openpgp package and if that fails, by calling the "gpg" binary.
*/
package pgp //import "go.mozilla.org/sops/v3/pgp"

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strings"
	"time"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/ProtonMail/go-crypto/openpgp/armor"
	"github.com/howeyc/gopass"
	"github.com/sirupsen/logrus"
	gpgagent "go.mozilla.org/gopgagent"
	"go.mozilla.org/sops/v3/logging"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("PGP")
}

// MasterKey is a PGP key used to securely store sops' data key by encrypting it and decrypting it
type MasterKey struct {
	Fingerprint  string
	EncryptedKey string
	CreationDate time.Time
}

// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
	return []byte(key.EncryptedKey)
}

// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
	key.EncryptedKey = string(enc)
}

func gpgBinary() string {
	binary := "gpg"
	if envBinary := os.Getenv("SOPS_GPG_EXEC"); envBinary != "" {
		binary = envBinary
	}
	return binary
}

func (key *MasterKey) encryptWithGPGBinary(dataKey []byte) error {
	fingerprint := key.Fingerprint
	if offset := len(fingerprint) - 16; offset > 0 {
		fingerprint = fingerprint[offset:]
	}
	args := []string{
		"--no-default-recipient",
		"--yes",
		"--encrypt",
		"-a",
		"-r",
		key.Fingerprint,
		"--trusted-key",
		fingerprint,
		"--no-encrypt-to",
	}
	cmd := exec.Command(gpgBinary(), args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdin = bytes.NewReader(dataKey)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("gpg binary failed with error: %s, %s", err, stderr.String())
	}
	key.EncryptedKey = stdout.String()
	return nil
}

func getKeyFromKeyServer(fingerprint string) (openpgp.Entity, error) {
	log.Warn("Deprecation Warning: GPG key fetching from a keyserver within sops will be removed in a future version of sops. See https://github.com/mozilla/sops/issues/727 for more information.")

	url := fmt.Sprintf("https://keys.openpgp.org/vks/v1/by-fingerprint/%s", fingerprint)
	resp, err := http.Get(url)
	if err != nil {
		return openpgp.Entity{}, fmt.Errorf("error getting key from keyserver: %s", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return openpgp.Entity{}, fmt.Errorf("keyserver returned non-200 status code %s", resp.Status)
	}
	ents, err := openpgp.ReadArmoredKeyRing(resp.Body)
	if err != nil {
		return openpgp.Entity{}, fmt.Errorf("could not read entities: %s", err)
	}
	return *ents[0], nil
}

func (key *MasterKey) getPubKey() (openpgp.Entity, error) {
	ring, err := key.pubRing()
	if err == nil {
		fingerprints := key.fingerprintMap(ring)
		entity, ok := fingerprints[key.Fingerprint]
		if ok {
			return entity, nil
		}
	}
	entity, err := getKeyFromKeyServer(key.Fingerprint)
	if err != nil {
		return openpgp.Entity{},
			fmt.Errorf("key with fingerprint %s is not available "+
				"in keyring and could not be retrieved from keyserver", key.Fingerprint)
	}
	return entity, nil
}

func (key *MasterKey) encryptWithCryptoOpenPGP(dataKey []byte) error {
	entity, err := key.getPubKey()
	if err != nil {
		return err
	}
	encbuf := new(bytes.Buffer)
	armorbuf, err := armor.Encode(encbuf, "PGP MESSAGE", nil)
	if err != nil {
		return err
	}
	plaintextbuf, err := openpgp.Encrypt(armorbuf, []*openpgp.Entity{&entity}, nil, &openpgp.FileHints{IsBinary: true}, nil)
	if err != nil {
		return err
	}
	_, err = plaintextbuf.Write(dataKey)
	if err != nil {
		return err
	}
	err = plaintextbuf.Close()
	if err != nil {
		return err
	}
	err = armorbuf.Close()
	if err != nil {
		return err
	}
	bytes, err := ioutil.ReadAll(encbuf)
	if err != nil {
		return err
	}
	key.EncryptedKey = string(bytes)
	return nil
}

// Encrypt encrypts the data key with the PGP key with the same fingerprint as the MasterKey. It looks for PGP public keys in $PGPHOME/pubring.gpg.
func (key *MasterKey) Encrypt(dataKey []byte) error {
	openpgpErr := key.encryptWithCryptoOpenPGP(dataKey)
	if openpgpErr == nil {
		log.WithField("fingerprint", key.Fingerprint).Info("Encryption succeeded")
		return nil
	}
	binaryErr := key.encryptWithGPGBinary(dataKey)
	if binaryErr == nil {
		log.WithField("fingerprint", key.Fingerprint).Info("Encryption succeeded")
		return nil
	}
	log.WithField("fingerprint", key.Fingerprint).Info("Encryption failed")
	return fmt.Errorf(
		`could not encrypt data key with PGP key: github.com/ProtonMail/go-crypto/openpgp error: %v; GPG binary error: %v`,
		openpgpErr, binaryErr)
}

// EncryptIfNeeded encrypts the data key with PGP only if it's needed, that is, if it hasn't been encrypted already
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
	if key.EncryptedKey == "" {
		return key.Encrypt(dataKey)
	}
	return nil
}

func (key *MasterKey) decryptWithGPGBinary() ([]byte, error) {
	args := []string{
		"--use-agent",
		"-d",
	}
	cmd := exec.Command(gpgBinary(), args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdin = strings.NewReader(key.EncryptedKey)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		return nil, err
	}
	return stdout.Bytes(), nil
}

func (key *MasterKey) decryptWithCryptoOpenpgp() ([]byte, error) {
	ring, err := key.secRing()
	if err != nil {
		return nil, fmt.Errorf("Could not load secring: %s", err)
	}
	block, err := armor.Decode(strings.NewReader(key.EncryptedKey))
	if err != nil {
		return nil, fmt.Errorf("Armor decoding failed: %s", err)
	}
	md, err := openpgp.ReadMessage(block.Body, ring, key.passphrasePrompt(), nil)
	if err != nil {
		return nil, fmt.Errorf("Reading PGP message failed: %s", err)
	}
	if b, err := ioutil.ReadAll(md.UnverifiedBody); err == nil {
		return b, nil
	}
	return nil, fmt.Errorf("The key could not be decrypted with any of the PGP entries")
}

// Decrypt uses PGP to obtain the data key from the EncryptedKey store in the MasterKey and returns it
func (key *MasterKey) Decrypt() ([]byte, error) {
	dataKey, openpgpErr := key.decryptWithCryptoOpenpgp()
	if openpgpErr == nil {
		log.WithField("fingerprint", key.Fingerprint).Info("Decryption succeeded")
		return dataKey, nil
	}
	dataKey, binaryErr := key.decryptWithGPGBinary()
	if binaryErr == nil {
		log.WithField("fingerprint", key.Fingerprint).Info("Decryption succeeded")
		return dataKey, nil
	}
	log.WithField("fingerprint", key.Fingerprint).Info("Decryption failed")
	return nil, fmt.Errorf(
		`could not decrypt data key with PGP key: github.com/ProtonMail/go-crypto/openpgp error: %v; GPG binary error: %v`,
		openpgpErr, binaryErr)
}

// NeedsRotation returns whether the data key needs to be rotated or not
func (key *MasterKey) NeedsRotation() bool {
	return time.Since(key.CreationDate).Hours() > 24*30*6
}

// ToString returns the string representation of the key, i.e. its fingerprint
func (key *MasterKey) ToString() string {
	return key.Fingerprint
}

func (key *MasterKey) gpgHome() string {
	dir := os.Getenv("GNUPGHOME")
	if dir == "" {
		usr, err := user.Current()
		if err != nil {
			return path.Join(os.Getenv("HOME"), "/.gnupg")
		}
		return path.Join(usr.HomeDir, ".gnupg")
	}
	return dir
}

// NewMasterKeyFromFingerprint takes a PGP fingerprint and returns a new MasterKey with that fingerprint
func NewMasterKeyFromFingerprint(fingerprint string) *MasterKey {
	return &MasterKey{
		Fingerprint:  strings.Replace(fingerprint, " ", "", -1),
		CreationDate: time.Now().UTC(),
	}
}

// MasterKeysFromFingerprintString takes a comma separated list of PGP fingerprints and returns a slice of new MasterKeys with those fingerprints
func MasterKeysFromFingerprintString(fingerprint string) []*MasterKey {
	var keys []*MasterKey
	if fingerprint == "" {
		return keys
	}
	for _, s := range strings.Split(fingerprint, ",") {
		keys = append(keys, NewMasterKeyFromFingerprint(s))
	}
	return keys
}

func (key *MasterKey) loadRing(path string) (openpgp.EntityList, error) {
	f, err := os.Open(path)
	if err != nil {
		return openpgp.EntityList{}, err
	}
	defer f.Close()
	keyring, err := openpgp.ReadKeyRing(f)
	if err != nil {
		return keyring, err
	}
	return keyring, nil
}

func (key *MasterKey) secRing() (openpgp.EntityList, error) {
	return key.loadRing(key.gpgHome() + "/secring.gpg")
}

func (key *MasterKey) pubRing() (openpgp.EntityList, error) {
	return key.loadRing(key.gpgHome() + "/pubring.gpg")
}

func (key *MasterKey) fingerprintMap(ring openpgp.EntityList) map[string]openpgp.Entity {
	fps := make(map[string]openpgp.Entity)
	for _, entity := range ring {
		fp := strings.ToUpper(hex.EncodeToString(entity.PrimaryKey.Fingerprint[:]))
		if entity != nil {
			fps[fp] = *entity
		}
	}
	return fps
}

func (key *MasterKey) passphrasePrompt() func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
	callCounter := 0
	maxCalls := 3
	return func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
		if callCounter >= maxCalls {
			return nil, fmt.Errorf("function passphrasePrompt called too many times")
		}
		callCounter++

		conn, err := gpgagent.NewConn()
		if err == gpgagent.ErrNoAgent {
			log.Infof("gpg-agent not found, continuing with manual passphrase " +
				"input...")
			fmt.Print("Enter PGP key passphrase: ")
			pass, err := gopass.GetPasswd()
			if err != nil {
				return nil, err
			}
			for _, k := range keys {
				k.PrivateKey.Decrypt(pass)
			}
			return pass, err
		}
		if err != nil {
			return nil, fmt.Errorf("Could not establish connection with gpg-agent: %s", err)
		}
		defer conn.Close()
		for _, k := range keys {
			req := gpgagent.PassphraseRequest{
				CacheKey: k.PublicKey.KeyIdShortString(),
				Prompt:   "Passphrase",
				Desc:     fmt.Sprintf("Unlock key %s to decrypt sops's key", k.PublicKey.KeyIdShortString()),
			}
			pass, err := conn.GetPassphrase(&req)
			if err != nil {
				return nil, fmt.Errorf("gpg-agent passphrase request errored: %s", err)
			}
			k.PrivateKey.Decrypt([]byte(pass))
			return []byte(pass), nil
		}
		return nil, fmt.Errorf("No key to unlock")
	}
}

// ToMap converts the MasterKey into a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
	out := make(map[string]interface{})
	out["fp"] = key.Fingerprint
	out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
	out["enc"] = key.EncryptedKey
	return out
}
0707010000008A000081A400000000000000000000000162794F93000005F2000000000000000000000000000000000000002100000000sops-3.7.3/pgp/keysource_test.gopackage pgp

import (
	"bytes"
	"testing"
	"testing/quick"

	"github.com/stretchr/testify/assert"
)

func TestPGP(t *testing.T) {
	key := NewMasterKeyFromFingerprint("FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4")
	f := func(x []byte) bool {
		if x == nil || len(x) == 0 {
			return true
		}
		if err := key.Encrypt(x); err != nil {
			t.Errorf("Failed to encrypt: %#v err: %w", x, err)
			return false
		}
		k, err := key.Decrypt()
		if err != nil {
			t.Errorf("Failed to decrypt: %#v err: %w", x, err)
			return false
		}
		return bytes.Equal(x, k)
	}
	if err := quick.Check(f, nil); err != nil {
		t.Error(err)
	}
}

func TestPGPKeySourceFromString(t *testing.T) {
	s := "C8C5 2C0A B2A4 8174 01E8  12C8 F3CC 3233 3FAD 9F1E, C8C5 2C0A B2A4 8174 01E8  12C8 F3CC 3233 3FAD 9F1E"
	ks := MasterKeysFromFingerprintString(s)
	expected := "C8C52C0AB2A4817401E812C8F3CC32333FAD9F1E"
	if ks[0].Fingerprint != expected {
		t.Errorf("Fingerprint does not match. Got %s, expected %s", ks[0].Fingerprint, expected)
	}

	if ks[1].Fingerprint != expected {
		t.Error("Fingerprint does not match")
	}
}

func TestRetrievePGPKey(t *testing.T) {
	// Requires a key available in https://keys.openpgp.org/ *with identity information* (that is, an email address).
	// See https://keys.openpgp.org/about/faq#verify-multiple for details about identity information.
	// We use the key of release@mozilla.com for here.
	fingerprint := "14F26682D0916CDD81E37B6D61B7B526D98F0353"
	_, err := getKeyFromKeyServer(fingerprint)
	assert.NoError(t, err)
}
0707010000008B000081A400000000000000000000000162794F9300002861000000000000000000000000000000000000002D00000000sops-3.7.3/pgp/sops_functional_tests_key.asc-----BEGIN PGP PRIVATE KEY BLOCK-----

lQOYBF1oQV0BCAC1iFfE7H3uu0hbWbRYVMoz5zZ91ACHETCOMVxN8GOG4SV0l8aQ
wmK9QWkYxhi52LnicVD3D7Uy75+J3zkvEDQ15C0AZ8UHXp4JlSQuXpFhrOhfYUF/
6pr/QexT+hQjOacvY4qfnj4xKa/AGdv5vPIygtQumE6r3GhEVAxQ1GSwtCWSU3Zl
3Uqf7S8kDvJTemtR2UkVfpXcMd4AmMKgt7fVhPO8eFotqTLPvz/iClzER+q61fLA
d1rP9YlmY46MJp/PffPicWdJiKv2i6ynKcIwkrQyP6V2ZzYi/gAhNJst3ZlMfsiN
ekCtcow9Bn44uxW3U8W02FNQSNyn6V6QPDIXABEBAAEAB/0Z8kQSlkzE97QhXm0g
/PQuaVCdY9UJeSMBXTvDZhBhAcLf6yZLStq1uz4sIiWm6+ZcX8mXQ9b90fMceoaK
sVxiYYaEcCXgu5zcuMTu8xRWK30bzjkARrDjEByZFNLrr/yzO3KKWvdVAToou77N
xLxct4df+46vEMs/DOulDUkxBOjlkprlq8xSG/6vuo7rJKUylsS4s5+y+EJCfm0m
8C94IIOt42ANObDUziUHCFNhCKSUs92rL7HXfcMG6L16UrSpJ3yLNvTI34PgRydv
ppu6DAFNeqsJ6oINSWXEqjfMHK7Ly9oyF2bkB2VKoapAdz6YGJydrODhFrThcuJk
+pY9BADKnXtYvDRPoTsfRYgZewtBxf3ccGUjoS9YCC3salWuPEWnal2yI0YRwZNE
iirOFGKH6jh/fxtFZNPXuYb7MJzFqVOcARz6USCvR1va2kMZzQEOKwxOXqIYYMVh
Uwz9++QugqcBLHw9YUFmH/DsRaL4zP4H8cX5O1TALFo3aC/EHQQA5VzUDupcpRLP
gF6dCgT2GyajgRoUFU7Brq82+HJDBDhHMB+3VWJhsC9DkTMh/RtPOuLb41K0OZ//
acoXo0QjsLsBx+hNqWC0oosqaoXiUyhbmEukvlURm5uHThX9n5BZIKhiCft/NYNO
yb+OBgYFHN11BMUVyhMR7be2mlJ4EMMD/jd9WQIoHQQ6BKMNOlc6BGu4KsMv/+fF
KV4xnJKrWjJxwri0FsOYLS2qkgbSAXjxLqZWx4UylmJh1HSAyjTghY0zQEf2oDKd
0DKN8Y42aawh1AolIfDbYOampw5tBzI2/WYOksGRFCwjCidL3pNd03W9dBmNbBRc
tVKLG/kt4JwCL0y0U1NPUFMgRnVuY3Rpb25hbCBUZXN0cyBLZXkgMSAoaHR0cHM6
Ly9naXRodWIuY29tL21vemlsbGEvc29wcy8pIDxzZWNvcHNAbW96aWxsYS5jb20+
iQFOBBMBCAA4FiEE+8e54qT5KJrAwdSEPRbO5KJzgbQFAl1oQV0CGwMFCwkIBwIG
FQoJCAsCBBYCAwECHgECF4AACgkQPRbO5KJzgbTDcQf7Bp7e2zY9pBBXTgDASQl3
1SSHp9WkRUV5iqPVC9iPCELggteBGMwIpbDlobc6O8/06foxWctTUaaciPBo2+je
WFTO+DNvB7oXIArqr5673QHLh6jEABBjyt91rvta2wYF1XJBgxpui9aLICsCptFN
IRvHeKUrXBI4fG5z3CDs/EOoY8K/AAYJUF+ERtmvmisiE/m20UpbYRmkBJy25c89
Wcn12I1SUJA3H3hGwvZCYp8hY1HPxxQUtU+DZBIpryi0xQqExGAlYqck7G03F+AD
7/csaT1LEdCtWRLNwE8UkvfUF6liF0SgzxFo1pp3gBU4swds9yO9wNe12JY/M5A/
BJ0DmARdaEFdAQgAtun8JhSpNAKvOXwWX2nFhnMXTJp4viMhlAZEdmMXEi27B2DM
/nRzldjxGZoNUBSVbJNj2kx5ZUDl0o6eOpChvRaGuCOpYqOuSQvD8FnX0NgQULwu
TZ+MawsaezktJEjDSBM1R6uASeJwDZj4hcUnPgyAIESajPdowEkEjdYt261fGOLL
cVoVdtqzOMBkLVdrK/FD1kGR9jnSlKEYDV9DveBUBQGdqkgWXjS5BKcae07viC6x
Ma9AJS4pizyDALB2k0HQOelZNihOGXYUuvkcs2Fivl0Tk3OCfH9XDvFehbYRHmkR
DoMuKUDSzdy6tFBAkL0CPlXAWI6kQklaBEp19QARAQABAAf7BX7YLYi3YLGn9BEv
VuSFo7l3fLyzXfsOOjVJ/0iQ2+H12Y3l+ssi4eCntb40IjDMIHv5JwjfKNSfUwkn
5diMk3LGz2d64lTKmrU4yNLaMhMbwmE0/u4JOPoXbJZWLd3lyBeTpTiY3R9pgG8V
IGfA+xNDEjUdc5jHU+edtGk37X6l6uL3OANS/MyTRdVNr28Gv/upXmJs/NbvTost
1hsU89gaDjkfsWhdhiuCHR9bqoyot/Vgvpt1NxzfV4SQGVFeph8yCGvSRBS8zXuZ
FtmzACs0j2aOMSucAGogEoD158OpXSNfdmZ1nCswlo1yqP6+ir8mr2DTRgMtxPQa
N49b4QQAxVTwRZ6+qiSCz/GJPq7qASGG4RIr87gPzxaHmznQhKIx6LEMjX/+NU6c
94A8aZY/oN7f3rr8apIA+cAHbAwFGpbc7ke1Cgy/m/eJZNUxWPT/YBjZ4V+41Uat
viGrbmS9B4QulOpF2Ng6LcOc4dggxTPAW/CYd5T2FImr1qYjjWkEAO1Lss00LY5o
5I4QqgM0OeeBEOO8LiSDmjKgOvtsmJ6+dA4x3rYgI8smFMsvtyrcb75k6EdZazgN
YSI4sU3WceWbrtdVr1glP38CBMupnFvg8KwbjSFV8vNqVBHCXShUxnHmlOW+UVqy
CxjJf0RTOhLEY5DIRwQB0H8P30dYOfatBADaGIbs/6+1RulKpHwW/c3+XOlaTZrT
UhNjuccj7Y9IspYD+6crNkQvAri60AoDfIiO5aTk8rSYpGwB1vEmnUVmNPvRF958
GV3pyCOv/pkmnpS+4w+akiJsSHX3jqqp5fb/xd6ukUX95VgSymuJ+ya49G8B0jj6
bw7B4S2M39+Xdkg2iQE2BBgBCAAgFiEE+8e54qT5KJrAwdSEPRbO5KJzgbQFAl1o
QV0CGwwACgkQPRbO5KJzgbS7zwgAndbf532OXo9HwPH+yQQmzQCLDFL6P4V7LcFr
rydYItTEhxqI3tbb96MKXRAt+G5Mw6JjRkWhwzbU3jE7D7XBMHw7GriTTU9QltNH
g7VUpSSaiTfVcSNErzsaqbjbA7jMs7VWzOq4LZo6Efy8UDKg5qcqLFaTQrzQZYNH
NfM+kLAiUPU8m7vwmz6oJWsjHkQKUhKhHptlpwMwdHkoacqDO0x2H6H91l/PnDm4
ZG6FybJtcjr98i+p52/XOo81nLgX7tcFS3nrN9HNdgKg1ZW3yrzg8NOaFCVA8qLD
gLk//M3qDixOxiurECkFrMvt/bDxEGpN5GVy550MmyUZQrkuqg==
=VjGL
-----END PGP PRIVATE KEY BLOCK-----
-----BEGIN PGP PRIVATE KEY BLOCK-----

lQHYBF1oQYgBBADPuVP6Jdk/J/TbNa9dXirp/zzwK18ZqNudNqQGN3H+2aSgxXwL
wlRfzy7rB3CU6Ewjzk9EVYeYztTIkGHL0JZ1CCTiBJArlHO0bHQQ7CPeKPkhIhkj
eA8yu9dcU77oYC2xbwgf43KYzfMKSGEybg+sBO+bH+Y6paJK54V2cuS3GwARAQAB
AAP+Jjf5BXtVP1OAr5xvCYS77JWzhpTUSIpS7dgR0br91GAC9DmhmyBEGeSqwz95
LUyYRbY9y1rZOfpEGCrIc5GLPOQytO9XMIzaS3dpzfGhla/spaKN4vJDvIOl+ruT
bInDdCRSmqXCfm2478OhOquc0H0a46eSmoaYeKdE3E8QZiECANxUL/dFk5j8NyPo
ZcwXw9Mv0A8UrynRcqht3Scti9k7dbsHylcObM305LFdcoNnSfNAIJhxfjbiXyGW
vwT2/qMCAPFatq3gvVjy6wKKylioi5cVwbLv9L+OaRXdR/Dy2bh/t3ujnsliV4+R
f7k3rHOQeaMLTnyfcz8AenL5IOe8RSkCANFpBgyzxCcV48Mm+FWDxjrSJ4/msRnN
gxqAPRrdpm7e1uebtBkPh4ch4oCW5/lLsRN23LUVIXYJRwyFfRjehCio0rRTU09Q
UyBGdW5jdGlvbmFsIFRlc3RzIEtleSAyIChodHRwczovL2dpdGh1Yi5jb20vbW96
aWxsYS9zb3BzLykgPHNlY29wc0Btb3ppbGxhLmNvbT6IzgQTAQgAOBYhBNcikEM4
S8xgMmxvudhyDZV8PTB0BQJdaEGIAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
AAoJENhyDZV8PTB0R2cD/2YwaJ43iGueaAzByFnl+mUEBQJ4HhH4p7BIdx6B9AjE
3yLe8I4dqqYXxyZzaJ9d+KiqxJBT0l1GXt3H5M32yDJZqzXB9PTWP3yx8+Q1CuCs
7EL/bhJD1/sLdumVc77bmQtcI9NSiYyPzN/2ZqtV5RU14Loh24VFEjuHGvO0jI3+
nQHYBF1oQYgBBAChXi00fmpEs0Jiq0zOyYm9i749VoOsNReoB/5ix1QCimwVZKe1
D37IP5Qqysxy+LIQc4lJ+Q8foNOx1Aev5+TDyv+iU82D9xr9uPLLbA82k3AZ04Or
BjrZ/Yt1NZhuaHzciZCPpmqzF9kqVqAZc+vMiKZL1WZjS7O1FwaidY1vXwARAQAB
AAP+L0wUQeOfsD0+gv8khyPJTJZOD1pxQ6NYKLcXF8rG0+vQnECha098YKNKAXTp
kfVU8795iQYIKcQQ6Hl2O1fj1AxJE/iZYrqfm7UZz3bQ7ROSsAEPZ5GDOjKfbwsz
E6bWVH+PhS1azlvtTs9JezUtK0Wl9s+81FOrZtnUUskmWtECAMNNs9ujUt6GHv/J
NXVaSmk1z8QXitPHbAJLDMj4xVDysJWZV95eplC+RUSiLz5HeP2AQgh1D9Rv2bA5
c7OcJ3kCANOEkA0hVpXCI0FKrsihOf0NUOaAtS6CQNFlaIkrLwssJQY8pGYbRfRa
3krNJPyOlXmezV2/CsX3EqA9KXXen5cB/iSmMJO4WndGJTe7YzUEnnY/P2TKg1fN
s6v5Lf39j5Ll8V5rVDT7ApAw0IKS8fzpbdHP0HcizutlF6l44YaAXMGfhoi2BBgB
CAAgFiEE1yKQQzhLzGAybG+52HINlXw9MHQFAl1oQYgCGwwACgkQ2HINlXw9MHTD
HwQAv+ui718AT2hw2pK9JaNuTxjllrH+KPMlrov0P8oXHPCohC5cxM5sJ6tCQ0qH
XyeWoDE8V31btqFVAQyrr0wy0gntl1L/trnwMHoP8a/xa0RHNk5C7hmcuhTHbQey
JNbiRJZpCIZ1OyrF17+q6u9YBPjwqp8KrJ/0ryy2kyb7ZRM=
=+tJ6
-----END PGP PRIVATE KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBF1oQV0BCAC1iFfE7H3uu0hbWbRYVMoz5zZ91ACHETCOMVxN8GOG4SV0l8aQ
wmK9QWkYxhi52LnicVD3D7Uy75+J3zkvEDQ15C0AZ8UHXp4JlSQuXpFhrOhfYUF/
6pr/QexT+hQjOacvY4qfnj4xKa/AGdv5vPIygtQumE6r3GhEVAxQ1GSwtCWSU3Zl
3Uqf7S8kDvJTemtR2UkVfpXcMd4AmMKgt7fVhPO8eFotqTLPvz/iClzER+q61fLA
d1rP9YlmY46MJp/PffPicWdJiKv2i6ynKcIwkrQyP6V2ZzYi/gAhNJst3ZlMfsiN
ekCtcow9Bn44uxW3U8W02FNQSNyn6V6QPDIXABEBAAG0U1NPUFMgRnVuY3Rpb25h
bCBUZXN0cyBLZXkgMSAoaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvc29wcy8p
IDxzZWNvcHNAbW96aWxsYS5jb20+iQFOBBMBCAA4FiEE+8e54qT5KJrAwdSEPRbO
5KJzgbQFAl1oQV0CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQPRbO5KJz
gbTDcQf7Bp7e2zY9pBBXTgDASQl31SSHp9WkRUV5iqPVC9iPCELggteBGMwIpbDl
obc6O8/06foxWctTUaaciPBo2+jeWFTO+DNvB7oXIArqr5673QHLh6jEABBjyt91
rvta2wYF1XJBgxpui9aLICsCptFNIRvHeKUrXBI4fG5z3CDs/EOoY8K/AAYJUF+E
RtmvmisiE/m20UpbYRmkBJy25c89Wcn12I1SUJA3H3hGwvZCYp8hY1HPxxQUtU+D
ZBIpryi0xQqExGAlYqck7G03F+AD7/csaT1LEdCtWRLNwE8UkvfUF6liF0SgzxFo
1pp3gBU4swds9yO9wNe12JY/M5A/BLkBDQRdaEFdAQgAtun8JhSpNAKvOXwWX2nF
hnMXTJp4viMhlAZEdmMXEi27B2DM/nRzldjxGZoNUBSVbJNj2kx5ZUDl0o6eOpCh
vRaGuCOpYqOuSQvD8FnX0NgQULwuTZ+MawsaezktJEjDSBM1R6uASeJwDZj4hcUn
PgyAIESajPdowEkEjdYt261fGOLLcVoVdtqzOMBkLVdrK/FD1kGR9jnSlKEYDV9D
veBUBQGdqkgWXjS5BKcae07viC6xMa9AJS4pizyDALB2k0HQOelZNihOGXYUuvkc
s2Fivl0Tk3OCfH9XDvFehbYRHmkRDoMuKUDSzdy6tFBAkL0CPlXAWI6kQklaBEp1
9QARAQABiQE2BBgBCAAgFiEE+8e54qT5KJrAwdSEPRbO5KJzgbQFAl1oQV0CGwwA
CgkQPRbO5KJzgbS7zwgAndbf532OXo9HwPH+yQQmzQCLDFL6P4V7LcFrrydYItTE
hxqI3tbb96MKXRAt+G5Mw6JjRkWhwzbU3jE7D7XBMHw7GriTTU9QltNHg7VUpSSa
iTfVcSNErzsaqbjbA7jMs7VWzOq4LZo6Efy8UDKg5qcqLFaTQrzQZYNHNfM+kLAi
UPU8m7vwmz6oJWsjHkQKUhKhHptlpwMwdHkoacqDO0x2H6H91l/PnDm4ZG6FybJt
cjr98i+p52/XOo81nLgX7tcFS3nrN9HNdgKg1ZW3yrzg8NOaFCVA8qLDgLk//M3q
DixOxiurECkFrMvt/bDxEGpN5GVy550MmyUZQrkuqg==
=Zs2s
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----

mI0EXWhBiAEEAM+5U/ol2T8n9Ns1r11eKun/PPArXxmo2502pAY3cf7ZpKDFfAvC
VF/PLusHcJToTCPOT0RVh5jO1MiQYcvQlnUIJOIEkCuUc7RsdBDsI94o+SEiGSN4
DzK711xTvuhgLbFvCB/jcpjN8wpIYTJuD6wE75sf5jqlokrnhXZy5LcbABEBAAG0
U1NPUFMgRnVuY3Rpb25hbCBUZXN0cyBLZXkgMiAoaHR0cHM6Ly9naXRodWIuY29t
L21vemlsbGEvc29wcy8pIDxzZWNvcHNAbW96aWxsYS5jb20+iM4EEwEIADgWIQTX
IpBDOEvMYDJsb7nYcg2VfD0wdAUCXWhBiAIbAwULCQgHAgYVCgkICwIEFgIDAQIe
AQIXgAAKCRDYcg2VfD0wdEdnA/9mMGieN4hrnmgMwchZ5fplBAUCeB4R+KewSHce
gfQIxN8i3vCOHaqmF8cmc2ifXfioqsSQU9JdRl7dx+TN9sgyWas1wfT01j98sfPk
NQrgrOxC/24SQ9f7C3bplXO+25kLXCPTUomMj8zf9marVeUVNeC6IduFRRI7hxrz
tIyN/riNBF1oQYgBBAChXi00fmpEs0Jiq0zOyYm9i749VoOsNReoB/5ix1QCimwV
ZKe1D37IP5Qqysxy+LIQc4lJ+Q8foNOx1Aev5+TDyv+iU82D9xr9uPLLbA82k3AZ
04OrBjrZ/Yt1NZhuaHzciZCPpmqzF9kqVqAZc+vMiKZL1WZjS7O1FwaidY1vXwAR
AQABiLYEGAEIACAWIQTXIpBDOEvMYDJsb7nYcg2VfD0wdAUCXWhBiAIbDAAKCRDY
cg2VfD0wdMMfBAC/66LvXwBPaHDakr0lo25PGOWWsf4o8yWui/Q/yhcc8KiELlzE
zmwnq0JDSodfJ5agMTxXfVu2oVUBDKuvTDLSCe2XUv+2ufAweg/xr/FrREc2TkLu
GZy6FMdtB7Ik1uJElmkIhnU7KsXXv6rq71gE+PCqnwqsn/SvLLaTJvtlEw==
=PafV
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQENBF1oQaYBCADsCw223WDj2ISnkZQJ07NS7ER/ft2Tz6FPzsMqz5JmGlwQantH
MzjqNGE5du1TBK+yCIuzn+P/iokmOdFjkH20OHHCEmgBQPQ2WxpR4Bc/jDvswoL2
2amknIYStf+SgCwtNPT51RS7/5brN5pVn4GjDYg+IRLk33bxz1kN3933olkMFHJF
hrr1rcE1uxt3j2CEPzfKAUyviKXkSl41IAopE2e69zsVg0YaensTnE/hY45r1q0W
ABXoJLg4H6UtHZWWGg7vmtQYCpiiUIaWZ3YB449Ur4jxXeIZPUqhliqOoUk8a7En
1ZC1hGLw0jaENeaHywVGi9ZqABmOVYmvItI3ABEBAAG0U1NPUFMgRnVuY3Rpb25h
bCBUZXN0cyBLZXkgMyAoaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvc29wcy8p
IDxzZWNvcHNAbW96aWxsYS5jb20+iQFOBBMBCAA4FiEEthGi+fEdD/glaIBRGfm1
2uqR/4YFAl1oQaYCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQGfm12uqR
/4Yc3QgA2WcMlCCUB4uyyvKq1a8ZTBli0YzIxT+KslL+PbOxfhoxglxPRClB0/l6
stG3RD0UXKq9cTe9f5nI96XJRqUzePfKtixSieirNTZswiHIYifYfg460gXYAIsV
deztRGxEaMUVq6vajkA69yGbi2nExaQrPQAgcOoyEcGhsQVi5M3rbIGIJtK/K777
lAvWskMWjEL/VIQ3qHJapBwhtyK8pKiQ1U2ssvXSP15RouUU/8PdD6d82dLlLfBm
eiyQCeJYQyR/geJAvhyBO+Jy+SS2RNCE6FGirACzRh3SSLpKAvieoMHqYzav8kMh
zSMa5230SKpartbsgFu7t39zsc3t6LkBDQRdaEGmAQgA60kJghTuxvZaNjXCZBnj
0+z+NSnfhdPaGgdUStXDCbzsO2l/6kvlWMwwS3VuDwGjx1vLWt9KWxmL85+qLE2H
FuRpH4sJFa+0HraY9SP1UxgO5ydommRKdLDw5iQIaJACMSsMAHHOQCJ9VuNE1gt8
gOb5J902gRahgIKpGrrmtI6x3Zmu1ryKrBH9Ln60kYvrU14ANPYtIvOwqCNxeU4C
9q1q4/b73RXg2Gri8alGb2HMIaeK6u7i5MlEOqkQof2FkmGg3nEw/6Cvcnf4jesJ
ibKa69vC6n39ZJ0eRhmhr3tw3MP76Laayhqq8T+Ffog68A3Oe+i6+f7PyLe0oaHA
uQARAQABiQE2BBgBCAAgFiEEthGi+fEdD/glaIBRGfm12uqR/4YFAl1oQaYCGwwA
CgkQGfm12uqR/4b5lwgAmVCa0XYeco6Ec+Iz0CLBvNXDFH/KsP/ypWK5duzZRKeb
D30cDwQHUFWH5WivGZ5nJ+Rs9zkD7a07omMTRmCsrjD4I3xDGMTU23l+gBSC5+9R
B6bOi81ngH3OLaSbeh2t21PDEf57M94WFNlw2LVgMvZ6S4rs7I4FZgm75h4EGGuY
It1l8SqNWcKDm9Kz/qG0lqeSGGFnQqmSBFH0Vb0hus/XErU2r3fQr1lDj0VKpOIO
J0Ys9rmI6yEPTi+GhFr1bHKwZMinz5lcHnOl8xye48tsrOtHMGN17/B6hUUGzd+W
TphrOfnfTO1YCkg1nEB5E2Raj/KV+ohqPvjE+KhE7Q==
=gqhE
-----END PGP PUBLIC KEY BLOCK-----
0707010000008C000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/publish0707010000008D000081A400000000000000000000000162794F93000004EC000000000000000000000000000000000000001A00000000sops-3.7.3/publish/gcs.gopackage publish

import (
	"context"
	"fmt"

	"cloud.google.com/go/storage"
)

// GCSDestination represents the Google Cloud Storage destination
type GCSDestination struct {
	gcsBucket string
	gcsPrefix string
}

// NewGCSDestination is the constructor for a Google Cloud Storage destination
func NewGCSDestination(gcsBucket, gcsPrefix string) *GCSDestination {
	return &GCSDestination{gcsBucket, gcsPrefix}
}

// Path returns a the GCS path for a file within this GCS Destination
func (gcsd *GCSDestination) Path(fileName string) string {
	return fmt.Sprintf("gcs://%s/%s%s", gcsd.gcsBucket, gcsd.gcsPrefix, fileName)
}

// Upload uploads contents to a file in GCS
func (gcsd *GCSDestination) Upload(fileContents []byte, fileName string) error {
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		return err
	}
	wc := client.Bucket(gcsd.gcsBucket).Object(gcsd.gcsPrefix + fileName).NewWriter(ctx)
	defer wc.Close()
	_, err = wc.Write(fileContents)
	if err != nil {
		return err
	}
	return nil
}

// Returns NotImplementedError
func (gcsd *GCSDestination) UploadUnencrypted(data map[string]interface{}, fileName string) error {
	return &NotImplementedError{"GCS does not support uploading the unencrypted file contents."}
}
0707010000008E000081A400000000000000000000000162794F93000001E4000000000000000000000000000000000000001E00000000sops-3.7.3/publish/publish.gopackage publish

import "fmt"

// Destination represents actions which all destination types
// must implement in order to be used by SOPS
type Destination interface {
	Upload(fileContents []byte, fileName string) error
	UploadUnencrypted(data map[string]interface{}, fileName string) error
	Path(fileName string) string
}

type NotImplementedError struct {
	message string
}

func (e *NotImplementedError) Error() string {
	return fmt.Sprintf("NotImplementedError: %s", e.message)
}
0707010000008F000081A400000000000000000000000162794F9300000557000000000000000000000000000000000000001900000000sops-3.7.3/publish/s3.gopackage publish

import (
	"bytes"
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

// S3Destination is the AWS S3 implementation of the Destination interface
type S3Destination struct {
	s3Bucket string
	s3Prefix string
}

// NewS3Destination is the constructor for an S3 Destination
func NewS3Destination(s3Bucket, s3Prefix string) *S3Destination {
	return &S3Destination{s3Bucket, s3Prefix}
}

// Path returns the S3 path of a file in an S3 Destination (bucket)
func (s3d *S3Destination) Path(fileName string) string {
	return fmt.Sprintf("s3://%s/%s%s", s3d.s3Bucket, s3d.s3Prefix, fileName)
}

// Upload uploads contents to a file in an S3 Destination (bucket)
func (s3d *S3Destination) Upload(fileContents []byte, fileName string) error {
	sess := session.Must(session.NewSession())
	svc := s3.New(sess)
	input := &s3.PutObjectInput{
		Body:   aws.ReadSeekCloser(bytes.NewReader(fileContents)),
		Bucket: aws.String(s3d.s3Bucket),
		Key:    aws.String(s3d.s3Prefix + fileName),
	}
	_, err := svc.PutObject(input)
	if err != nil {
		return err
	}
	return nil
}

// Returns NotImplementedError
func (s3d *S3Destination) UploadUnencrypted(data map[string]interface{}, fileName string) error {
	return &NotImplementedError{"S3 does not support uploading the unencrypted file contents."}
}
07070100000090000081A400000000000000000000000162794F9300000A34000000000000000000000000000000000000001C00000000sops-3.7.3/publish/vault.gopackage publish

import (
	"fmt"
	"strings"

	"github.com/google/go-cmp/cmp"
	vault "github.com/hashicorp/vault/api"
	"go.mozilla.org/sops/v3/logging"

	"github.com/sirupsen/logrus"
)

var log *logrus.Logger

func init() {
	log = logging.NewLogger("PUBLISH")
}

type VaultDestination struct {
	vaultAddress string
	vaultPath    string
	kvMountName  string
	kvVersion    int
}

func NewVaultDestination(vaultAddress, vaultPath, kvMountName string, kvVersion int) *VaultDestination {
	if !strings.HasSuffix(vaultPath, "/") {
		vaultPath = vaultPath + "/"
	}
	if kvMountName == "" {
		kvMountName = "secret/"
	}
	if !strings.HasSuffix(kvMountName, "/") {
		kvMountName = kvMountName + "/"
	}
	if kvVersion != 1 && kvVersion != 2 {
		kvVersion = 2
	}
	return &VaultDestination{vaultAddress, vaultPath, kvMountName, kvVersion}
}

func (vaultd *VaultDestination) getAddress() string {
	if vaultd.vaultAddress != "" {
		return vaultd.vaultAddress
	}
	return vault.DefaultConfig().Address
}

func (vaultd *VaultDestination) Path(fileName string) string {
	return fmt.Sprintf("%s/v1/%s", vaultd.getAddress(), vaultd.secretsPath(fileName))
}

func (vaultd *VaultDestination) secretsPath(fileName string) string {
	if vaultd.kvVersion == 1 {
		return fmt.Sprintf("%s%s%s", vaultd.kvMountName, vaultd.vaultPath, fileName)
	}
	return fmt.Sprintf("%sdata/%s%s", vaultd.kvMountName, vaultd.vaultPath, fileName)
}

// Returns NotImplementedError
func (vaultd *VaultDestination) Upload(fileContents []byte, fileName string) error {
	return &NotImplementedError{"Vault does not support uploading encrypted sops files directly."}
}

func (vaultd *VaultDestination) UploadUnencrypted(data map[string]interface{}, fileName string) error {
	client, err := vault.NewClient(nil)
	if err != nil {
		return err
	}
	if vaultd.vaultAddress != "" {
		err = client.SetAddress(vaultd.vaultAddress)
		if err != nil {
			return err
		}
	}

	secretsPath := vaultd.secretsPath(fileName)
	existingSecret, err := client.Logical().Read(secretsPath)
	if err != nil {
		log.Warnf("Cannot check if destination secret already exists in %s. New version will be created even if the data has not been changed.", secretsPath)
	}
	if existingSecret != nil && cmp.Equal(data, existingSecret.Data["data"]) {
		log.Infof("Secret in %s is already up-to-date.\n", secretsPath)
		return nil
	}

	secretsData := make(map[string]interface{})

	if vaultd.kvVersion == 1 {
		secretsData = data
	} else if vaultd.kvVersion == 2 {
		secretsData["data"] = data
	}

	_, err = client.Logical().Write(secretsPath, secretsData)
	if err != nil {
		return err
	}

	return nil
}
07070100000091000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/shamir07070100000092000081A400000000000000000000000162794F9300003E31000000000000000000000000000000000000001A00000000sops-3.7.3/shamir/LICENSEMozilla Public License, version 2.0

1. Definitions

1.1. "Contributor"

     means each individual or legal entity that creates, contributes to the
     creation of, or owns Covered Software.

1.2. "Contributor Version"

     means the combination of the Contributions of others (if any) used by a
     Contributor and that particular Contributor's Contribution.

1.3. "Contribution"

     means Covered Software of a particular Contributor.

1.4. "Covered Software"

     means Source Code Form to which the initial Contributor has attached the
     notice in Exhibit A, the Executable Form of such Source Code Form, and
     Modifications of such Source Code Form, in each case including portions
     thereof.

1.5. "Incompatible With Secondary Licenses"
     means

     a. that the initial Contributor has attached the notice described in
        Exhibit B to the Covered Software; or

     b. that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the terms of
        a Secondary License.

1.6. "Executable Form"

     means any form of the work other than Source Code Form.

1.7. "Larger Work"

     means a work that combines Covered Software with other material, in a
     separate file or files, that is not Covered Software.

1.8. "License"

     means this document.

1.9. "Licensable"

     means having the right to grant, to the maximum extent possible, whether
     at the time of the initial grant or subsequently, any and all of the
     rights conveyed by this License.

1.10. "Modifications"

     means any of the following:

     a. any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered Software; or

     b. any new file in Source Code Form that contains any Covered Software.

1.11. "Patent Claims" of a Contributor

      means any patent claim(s), including without limitation, method,
      process, and apparatus claims, in any patent Licensable by such
      Contributor that would be infringed, but for the grant of the License,
      by the making, using, selling, offering for sale, having made, import,
      or transfer of either its Contributions or its Contributor Version.

1.12. "Secondary License"

      means either the GNU General Public License, Version 2.0, the GNU Lesser
      General Public License, Version 2.1, the GNU Affero General Public
      License, Version 3.0, or any later versions of those licenses.

1.13. "Source Code Form"

      means the form of the work preferred for making modifications.

1.14. "You" (or "Your")

      means an individual or a legal entity exercising rights under this
      License. For legal entities, "You" includes any entity that controls, is
      controlled by, or is under common control with You. For purposes of this
      definition, "control" means (a) the power, direct or indirect, to cause
      the direction or management of such entity, whether by contract or
      otherwise, or (b) ownership of more than fifty percent (50%) of the
      outstanding shares or beneficial ownership of such entity.


2. License Grants and Conditions

2.1. Grants

     Each Contributor hereby grants You a world-wide, royalty-free,
     non-exclusive license:

     a. under intellectual property rights (other than patent or trademark)
        Licensable by such Contributor to use, reproduce, make available,
        modify, display, perform, distribute, and otherwise exploit its
        Contributions, either on an unmodified basis, with Modifications, or
        as part of a Larger Work; and

     b. under Patent Claims of such Contributor to make, use, sell, offer for
        sale, have made, import, and otherwise transfer either its
        Contributions or its Contributor Version.

2.2. Effective Date

     The licenses granted in Section 2.1 with respect to any Contribution
     become effective for each Contribution on the date the Contributor first
     distributes such Contribution.

2.3. Limitations on Grant Scope

     The licenses granted in this Section 2 are the only rights granted under
     this License. No additional rights or licenses will be implied from the
     distribution or licensing of Covered Software under this License.
     Notwithstanding Section 2.1(b) above, no patent license is granted by a
     Contributor:

     a. for any code that a Contributor has removed from Covered Software; or

     b. for infringements caused by: (i) Your and any other third party's
        modifications of Covered Software, or (ii) the combination of its
        Contributions with other software (except as part of its Contributor
        Version); or

     c. under Patent Claims infringed by Covered Software in the absence of
        its Contributions.

     This License does not grant any rights in the trademarks, service marks,
     or logos of any Contributor (except as may be necessary to comply with
     the notice requirements in Section 3.4).

2.4. Subsequent Licenses

     No Contributor makes additional grants as a result of Your choice to
     distribute the Covered Software under a subsequent version of this
     License (see Section 10.2) or under the terms of a Secondary License (if
     permitted under the terms of Section 3.3).

2.5. Representation

     Each Contributor represents that the Contributor believes its
     Contributions are its original creation(s) or it has sufficient rights to
     grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

     This License is not intended to limit any rights You have under
     applicable copyright doctrines of fair use, fair dealing, or other
     equivalents.

2.7. Conditions

     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
     Section 2.1.


3. Responsibilities

3.1. Distribution of Source Form

     All distribution of Covered Software in Source Code Form, including any
     Modifications that You create or to which You contribute, must be under
     the terms of this License. You must inform recipients that the Source
     Code Form of the Covered Software is governed by the terms of this
     License, and how they can obtain a copy of this License. You may not
     attempt to alter or restrict the recipients' rights in the Source Code
     Form.

3.2. Distribution of Executable Form

     If You distribute Covered Software in Executable Form then:

     a. such Covered Software must also be made available in Source Code Form,
        as described in Section 3.1, and You must inform recipients of the
        Executable Form how they can obtain a copy of such Source Code Form by
        reasonable means in a timely manner, at a charge no more than the cost
        of distribution to the recipient; and

     b. You may distribute such Executable Form under the terms of this
        License, or sublicense it under different terms, provided that the
        license for the Executable Form does not attempt to limit or alter the
        recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

     You may create and distribute a Larger Work under terms of Your choice,
     provided that You also comply with the requirements of this License for
     the Covered Software. If the Larger Work is a combination of Covered
     Software with a work governed by one or more Secondary Licenses, and the
     Covered Software is not Incompatible With Secondary Licenses, this
     License permits You to additionally distribute such Covered Software
     under the terms of such Secondary License(s), so that the recipient of
     the Larger Work may, at their option, further distribute the Covered
     Software under the terms of either this License or such Secondary
     License(s).

3.4. Notices

     You may not remove or alter the substance of any license notices
     (including copyright notices, patent notices, disclaimers of warranty, or
     limitations of liability) contained within the Source Code Form of the
     Covered Software, except that You may alter any license notices to the
     extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

     You may choose to offer, and to charge a fee for, warranty, support,
     indemnity or liability obligations to one or more recipients of Covered
     Software. However, You may do so only on Your own behalf, and not on
     behalf of any Contributor. You must make it absolutely clear that any
     such warranty, support, indemnity, or liability obligation is offered by
     You alone, and You hereby agree to indemnify every Contributor for any
     liability incurred by such Contributor as a result of warranty, support,
     indemnity or liability terms You offer. You may include additional
     disclaimers of warranty and limitations of liability specific to any
     jurisdiction.

4. Inability to Comply Due to Statute or Regulation

   If it is impossible for You to comply with any of the terms of this License
   with respect to some or all of the Covered Software due to statute,
   judicial order, or regulation then You must: (a) comply with the terms of
   this License to the maximum extent possible; and (b) describe the
   limitations and the code they affect. Such description must be placed in a
   text file included with all distributions of the Covered Software under
   this License. Except to the extent prohibited by statute or regulation,
   such description must be sufficiently detailed for a recipient of ordinary
   skill to be able to understand it.

5. Termination

5.1. The rights granted under this License will terminate automatically if You
     fail to comply with any of its terms. However, if You become compliant,
     then the rights granted under this License from a particular Contributor
     are reinstated (a) provisionally, unless and until such Contributor
     explicitly and finally terminates Your grants, and (b) on an ongoing
     basis, if such Contributor fails to notify You of the non-compliance by
     some reasonable means prior to 60 days after You have come back into
     compliance. Moreover, Your grants from a particular Contributor are
     reinstated on an ongoing basis if such Contributor notifies You of the
     non-compliance by some reasonable means, this is the first time You have
     received notice of non-compliance with this License from such
     Contributor, and You become compliant prior to 30 days after Your receipt
     of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
     infringement claim (excluding declaratory judgment actions,
     counter-claims, and cross-claims) alleging that a Contributor Version
     directly or indirectly infringes any patent, then the rights granted to
     You by any and all Contributors for the Covered Software under Section
     2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
     license agreements (excluding distributors and resellers) which have been
     validly granted by You or Your distributors under this License prior to
     termination shall survive termination.

6. Disclaimer of Warranty

   Covered Software is provided under this License on an "as is" basis,
   without warranty of any kind, either expressed, implied, or statutory,
   including, without limitation, warranties that the Covered Software is free
   of defects, merchantable, fit for a particular purpose or non-infringing.
   The entire risk as to the quality and performance of the Covered Software
   is with You. Should any Covered Software prove defective in any respect,
   You (not any Contributor) assume the cost of any necessary servicing,
   repair, or correction. This disclaimer of warranty constitutes an essential
   part of this License. No use of  any Covered Software is authorized under
   this License except under this disclaimer.

7. Limitation of Liability

   Under no circumstances and under no legal theory, whether tort (including
   negligence), contract, or otherwise, shall any Contributor, or anyone who
   distributes Covered Software as permitted above, be liable to You for any
   direct, indirect, special, incidental, or consequential damages of any
   character including, without limitation, damages for lost profits, loss of
   goodwill, work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses, even if such party shall have been
   informed of the possibility of such damages. This limitation of liability
   shall not apply to liability for death or personal injury resulting from
   such party's negligence to the extent applicable law prohibits such
   limitation. Some jurisdictions do not allow the exclusion or limitation of
   incidental or consequential damages, so this exclusion and limitation may
   not apply to You.

8. Litigation

   Any litigation relating to this License may be brought only in the courts
   of a jurisdiction where the defendant maintains its principal place of
   business and such litigation shall be governed by laws of that
   jurisdiction, without reference to its conflict-of-law provisions. Nothing
   in this Section shall prevent a party's ability to bring cross-claims or
   counter-claims.

9. Miscellaneous

   This License represents the complete agreement concerning the subject
   matter hereof. If any provision of this License is held to be
   unenforceable, such provision shall be reformed only to the extent
   necessary to make it enforceable. Any law or regulation which provides that
   the language of a contract shall be construed against the drafter shall not
   be used to construe this License against a Contributor.


10. Versions of the License

10.1. New Versions

      Mozilla Foundation is the license steward. Except as provided in Section
      10.3, no one other than the license steward has the right to modify or
      publish new versions of this License. Each version will be given a
      distinguishing version number.

10.2. Effect of New Versions

      You may distribute the Covered Software under the terms of the version
      of the License under which You originally received the Covered Software,
      or under the terms of any subsequent version published by the license
      steward.

10.3. Modified Versions

      If you create software not governed by this License, and you want to
      create a new license for such software, you may create and use a
      modified version of this License if you rename the license and remove
      any references to the name of the license steward (except to note that
      such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
      Licenses If You choose to distribute Source Code Form that is
      Incompatible With Secondary Licenses under the terms of this version of
      the License, the notice described in Exhibit B of this License must be
      attached.

Exhibit A - Source Code Form License Notice

      This Source Code Form is subject to the
      terms of the Mozilla Public License, v.
      2.0. If a copy of the MPL was not
      distributed with this file, You can
      obtain one at
      http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice

      This Source Code Form is "Incompatible
      With Secondary Licenses", as defined by
      the Mozilla Public License, v. 2.0.
07070100000093000081A400000000000000000000000162794F9300001685000000000000000000000000000000000000001C00000000sops-3.7.3/shamir/README.mdForked from [Vault](https://github.com/hashicorp/vault/tree/master/shamir)

## How it works

We want to split a secret into parts.

Any two points on the cartesian plane define a line. Three points define a
parabola. Four points define a cubic curve, and so on. In general, `n` points
define an function of degree `(n - 1)`. If our secret was somehow an function of
degree `(n - 1)`, we could just compute `n` different points of that function
and give `n` different people one point each. In order to recover the secret,
then we'd need all the `n` points. If we wanted to, we could compute more than n
points, but even then still only `n` points out of our whole set of computed
points would be required to recover the function.

A concrete example: our secret is the function `y = 2x + 1`. This function is of
degree 1, so we need at least 2 points to define it. For example, let's set
`x = 1`, `x = 2` and `x = 3`. From this follows that `y = 3`, `y = 5` and
`y = 7`, respectively. Now, with the information that our secret is of degree 1,
we can use any 2 of the 3 points we computed to recover our original function.
For example, let's use the points `x = 1; y = 3` and `x = 2; y = 5`.
We know that first degree functions are lines, defined by their slope and their
intersection point with the y axis. We can easily compute the slope given our
two points: it's the change in `y` divided by the change in `x`:
`(5 - 3)/(2 - 1) = 2`. Now, knowing the slope we can compute the intersection
point with the `y` axis by "working our way back". We know that at `x = 1`,
`y` equals `3`, so naturally because the slope is `2`, at `x = 0`, `y` must be
`1`.

## Lagrange interpolation

The method we've used for this isn't very general: it only works for polynomials
of degree 1. Lagrange interpolation is a more general way that lets us obtain
the function of degree `(n - 1)` that passes through `n` arbitrary points.

Understanding how to perform Lagrange interpolation isn't really necessary to
understand Shamir's Secret Sharing: it's enough to know that there's only one
function of degree `(n - 1)` that passes through `n` given points and that
computing this function given the points is computationally efficient.

But for those interested, here's an explanation:

Let's say our points are `(x_0, y_0),...,(x_j, y_j),...,(x_(n-1), y_(n-1))`.
Then, the Lagrange polynomial `L(x)`, the polynomial we're looking for, is
defined as follows:

`L(x) = sum from j=0 to j=(n-1) of {y_j * l_j(x)}`

and `l_j(x) = product from m=0 to m=(n-1) except when m=j of {(x - x_m)/(x_j - x_m)}`

A concrete example, with 3 points:

```
x_0 = 1   y_0 = 1
x_1 = 2   y_1 = 4
x_2 = 3   y_2 = 9
```

Let's apply the formula:

```
L(x) =
        y_0 * l_0(x) +
        y_1 * l_1(x) +
        y_2 * l_2(x)
```

Substitute `y_j` for the actual value:

```
L(x) =
        1 * l_0(x) +
        4 * l_1(x) +
        9 * l_2(x)
```

Replace `l_j(x)`:

```
l_0(x) = (x - 2)/(1 - 2) * (x - 3)/(1 - 3) =  0.5x^2 - 2.5x + 3
l_1(x) = (x - 1)/(2 - 1) * (x - 3)/(2 - 3) = -   x^2 +   4x - 3
l_2(x) = (x - 1)/(3 - 1) * (x - 2)/(3 - 2) =  0.5x^2 - 1.5x + 1
```

```

L(x) =
        1 * ( 0.5x^2 - 2.5x + 3) +
        4 * (   -x^2 +   4x - 3) +
        9 * ( 0.5x^2 - 1.5x + 1)
```

```

L(x) =
        ( 0.5x^2 -  2.5x +  3) +
        (  -4x^2 +   16x - 12) +
        ( 4.5x^2 - 13.5x +  9)
     =       x^2 +    0x +  0
     = x^2
```

So the polynomial we were looking for is `y = x^2`.


## Splitting a secret

So we have the ability of splitting a function into parts, but in the context
of computing we generally want to split a number, not a function. For this,
let's define a function of degree `threshold`. `threshold` is the amount of
parts we want to require in order to recover the secret. Let's set the parameter
of degree zero to our secret `S` and make the rest of the parameters random:

`y = ax^(threshold) + bx^(threshold-1) + ... + zx^1 + S`

With `a, b, ...` random.

Then, we want to generate our parts. For this, we evaluate our function at as
many points as we want parts. For example, say our secret is 123, we want 5
parts and a threshold of 2. Because the threshold is 2, we're going to need a
polynomial of degree 2:

`y = ax^2 + bx + 123`

We randomly set `a = 7` and `b = 1`:

`y = 7x^2 + x + 123`

Because we want 5 parts, we need to compute 5 points:

```
x = 0 -> y = 123 # woops! This is the secret itself. Let's not use that one.
x = 1 -> y = 131
x = 2 -> y = 153
x = 3 -> y = 189
x = 4 -> y = 239
x = 5 -> y = 303
```

And that's it. Each of the computed points is one part of the secret.

## Combining a secret

Now that we have our parts, we have to define a way to recover them. Using
the example from the previous section, we only need any two points out of the
five we created to recover the secret, because we set the threshold to two.
So with any two of the five points we created, we can recover the original
polynomial, and because the secret is the free term in the polynomial, we can
recover the secret.

## Finite fields

In the previous examples we've only used integers, and this unfortunately has
a flaw. First of all, it's impossible to uniformly sample integers to get
random coefficients for our generated polynomial. Additionally, if we don't
operate in a finite field, information about the secret is leaked for every part
someone recovers.

For these reasons, Vault's implementation of Shamir's Secret Sharing uses finite
field arithmetic, specifically in GF(2^8), with 229 as the generator. GF(2^8)
has 256 elements, so using this we can only split one byte at a time. This is
not a problem, though, as we can just split each byte in our secret
independently. This implementation uses tables to speed up the execution of
finite field arithmetic.
07070100000094000081A400000000000000000000000162794F93000020C6000000000000000000000000000000000000001C00000000sops-3.7.3/shamir/shamir.gopackage shamir

// Some comments in this file were written by @autrilla
// The code was written by HashiCorp as part of Vault.

// This implementation of Shamir's Secret Sharing matches the definition
// of the scheme. Other tools used, such as GF(2^8) arithmetic, Lagrange
// interpolation and Horner's method also match their definitions and should
// therefore be correct.
// More information about Shamir's Secret Sharing and Lagrange interpolation
// can be found in README.md

import (
	"crypto/rand"
	"crypto/subtle"
	"fmt"
	mathrand "math/rand"
	"time"
)

const (
	// ShareOverhead is the byte size overhead of each share
	// when using Split on a secret. This is caused by appending
	// a one byte tag to the share.
	ShareOverhead = 1
)

// polynomial represents a polynomial of arbitrary degree
type polynomial struct {
	coefficients []uint8
}

// makePolynomial constructs a random polynomial of the given
// degree but with the provided intercept value.
func makePolynomial(intercept, degree uint8) (polynomial, error) {
	// Create a wrapper
	p := polynomial{
		coefficients: make([]byte, degree+1),
	}

	// Ensure the intercept is set
	p.coefficients[0] = intercept

	// Assign random co-efficients to the polynomial
	if _, err := rand.Read(p.coefficients[1:]); err != nil {
		return p, err
	}

	return p, nil
}

// evaluate returns the value of the polynomial for the given x
// Uses Horner's method <https://en.wikipedia.org/wiki/Horner%27s_method> to
// evaluate the polynomial at point x
func (p *polynomial) evaluate(x uint8) uint8 {
	// Special case the origin
	if x == 0 {
		return p.coefficients[0]
	}

	// Compute the polynomial value using Horner's method.
	degree := len(p.coefficients) - 1
	out := p.coefficients[degree]
	for i := degree - 1; i >= 0; i-- {
		coeff := p.coefficients[i]
		out = add(mult(out, x), coeff)
	}
	return out
}

// interpolatePolynomial takes N sample points and returns
// the value at a given x using a lagrange interpolation.
// An implementation of Lagrange interpolation
// <https://en.wikipedia.org/wiki/Lagrange_polynomial>
// For this particular implementation, x is always 0
func interpolatePolynomial(xSamples, ySamples []uint8, x uint8) uint8 {
	limit := len(xSamples)
	var result, basis uint8
	for i := 0; i < limit; i++ {
		basis = 1
		for j := 0; j < limit; j++ {
			if i == j {
				continue
			}
			num := add(x, xSamples[j])
			denom := add(xSamples[i], xSamples[j])
			term := div(num, denom)
			basis = mult(basis, term)
		}
		group := mult(ySamples[i], basis)
		result = add(result, group)
	}
	return result
}

// div divides two numbers in GF(2^8)
// GF(2^8) division using log/exp tables
func div(a, b uint8) uint8 {
	if b == 0 {
		// leaks some timing information but we don't care anyways as this
		// should never happen, hence the panic
		panic("divide by zero")
	}

	var goodVal, zero uint8
	logA := logTable[a]
	logB := logTable[b]
	diff := (int(logA) - int(logB)) % 255
	if diff < 0 {
		diff += 255
	}

	ret := expTable[diff]

	// Ensure we return zero if a is zero but aren't subject to timing attacks
	goodVal = ret

	if subtle.ConstantTimeByteEq(a, 0) == 1 {
		ret = zero
	} else {
		ret = goodVal
	}

	return ret
}

// mult multiplies two numbers in GF(2^8)
// GF(2^8) multiplication using log/exp tables
func mult(a, b uint8) (out uint8) {
	var goodVal, zero uint8
	log_a := logTable[a]
	log_b := logTable[b]
	sum := (int(log_a) + int(log_b)) % 255

	ret := expTable[sum]

	// Ensure we return zero if either a or b are zero but aren't subject to
	// timing attacks
	goodVal = ret

	if subtle.ConstantTimeByteEq(a, 0) == 1 {
		ret = zero
	} else {
		ret = goodVal
	}

	if subtle.ConstantTimeByteEq(b, 0) == 1 {
		ret = zero
	} else {
		// This operation does not do anything logically useful. It
		// only ensures a constant number of assignments to thwart
		// timing attacks.
		goodVal = zero
	}

	return ret
}

// add combines two numbers in GF(2^8)
// This can also be used for subtraction since it is symmetric.
func add(a, b uint8) uint8 {
	return a ^ b
}

// Split takes an arbitrarily long secret and generates a `parts`
// number of shares, `threshold` of which are required to reconstruct
// the secret. The parts and threshold must be at least 2, and less
// than 256. The returned shares are each one byte longer than the secret
// as they attach a tag used to reconstruct the secret.
func Split(secret []byte, parts, threshold int) ([][]byte, error) {
	// Sanity check the input
	if parts < threshold {
		return nil, fmt.Errorf("parts cannot be less than threshold")
	}
	if parts > 255 {
		return nil, fmt.Errorf("parts cannot exceed 255")
	}
	if threshold < 2 {
		return nil, fmt.Errorf("threshold must be at least 2")
	}
	if threshold > 255 {
		return nil, fmt.Errorf("threshold cannot exceed 255")
	}
	if len(secret) == 0 {
		return nil, fmt.Errorf("cannot split an empty secret")
	}

	// Generate random x coordinates for computing points. I don't know
	// why random x coordinates are used, and I also don't know why
	// a non-cryptographically secure source of randomness is used.
	// As far as I know the x coordinates do not need to be random.

	mathrand.Seed(time.Now().UnixNano())
	xCoordinates := mathrand.Perm(255)

	// Allocate the output array, initialize the final byte
	// of the output with the offset. The representation of each
	// output is {y1, y2, .., yN, x}.
	out := make([][]byte, parts)
	for idx := range out {
		// Store the x coordinate for each part as its last byte
		// Add 1 to the xCoordinate because if the x coordinate is 0,
		// then the result of evaluating the polynomial at that point
		// will be our secret
		out[idx] = make([]byte, len(secret)+1)
		out[idx][len(secret)] = uint8(xCoordinates[idx]) + 1
	}

	// Construct a random polynomial for each byte of the secret.
	// Because we are using a field of size 256, we can only represent
	// a single byte as the intercept of the polynomial, so we must
	// use a new polynomial for each byte.
	for idx, val := range secret {
		// Create a random polynomial for each point.
		// This polynomial crosses the y axis at `val`.
		p, err := makePolynomial(val, uint8(threshold-1))
		if err != nil {
			return nil, fmt.Errorf("failed to generate polynomial: %w", err)
		}

		// Generate a `parts` number of (x,y) pairs
		// We cheat by encoding the x value once as the final index,
		// so that it only needs to be stored once.
		for i := 0; i < parts; i++ {
			// Add 1 to the xCoordinate because if it's 0,
			// then the result of p.evaluate(x) will be our secret
			x := uint8(xCoordinates[i]) + 1
			// Evaluate the polynomial at x
			y := p.evaluate(x)
			out[i][idx] = y
		}
	}

	// Return the encoded secrets
	return out, nil
}

// Combine is used to reverse a Split and reconstruct a secret
// once a `threshold` number of parts are available.
func Combine(parts [][]byte) ([]byte, error) {
	// Verify enough parts provided
	if len(parts) < 2 {
		return nil, fmt.Errorf("less than two parts cannot be used to reconstruct the secret")
	}

	// Verify the parts are all the same length
	firstPartLen := len(parts[0])
	if firstPartLen < 2 {
		return nil, fmt.Errorf("parts must be at least two bytes")
	}
	for i := 1; i < len(parts); i++ {
		if len(parts[i]) != firstPartLen {
			return nil, fmt.Errorf("all parts must be the same length")
		}
	}

	// Create a buffer to store the reconstructed secret
	secret := make([]byte, firstPartLen-1)

	// Buffer to store the samples
	xSamples := make([]uint8, len(parts))
	ySamples := make([]uint8, len(parts))

	// Set the x value for each sample and ensure no x_sample values are the same,
	// otherwise div() can be unhappy
	// Check that we don't have any duplicate parts, that is, two or
	// more parts with the same x coordinate.
	checkMap := map[byte]bool{}
	for i, part := range parts {
		samp := part[firstPartLen-1]
		if exists := checkMap[samp]; exists {
			return nil, fmt.Errorf("duplicate part detected")
		}
		checkMap[samp] = true
		xSamples[i] = samp
	}

	// Reconstruct each byte
	for idx := range secret {
		// Set the y value for each sample
		for i, part := range parts {
			ySamples[i] = part[idx]
		}

		// Use Lagrange interpolation to retrieve the free term
		// of the original polynomial
		val := interpolatePolynomial(xSamples, ySamples, 0)

		// Evaluate the 0th value to get the intercept
		secret[idx] = val
	}
	return secret, nil
}
07070100000095000081A400000000000000000000000162794F9300000E01000000000000000000000000000000000000002100000000sops-3.7.3/shamir/shamir_test.gopackage shamir

import (
	"bytes"
	"testing"
)

func TestSplit_invalid(t *testing.T) {
	secret := []byte("test")

	if _, err := Split(secret, 0, 0); err == nil {
		t.Fatalf("expect error")
	}

	if _, err := Split(secret, 2, 3); err == nil {
		t.Fatalf("expect error")
	}

	if _, err := Split(secret, 1000, 3); err == nil {
		t.Fatalf("expect error")
	}

	if _, err := Split(secret, 10, 1); err == nil {
		t.Fatalf("expect error")
	}

	if _, err := Split(nil, 3, 2); err == nil {
		t.Fatalf("expect error")
	}
}

func TestSplit(t *testing.T) {
	secret := []byte("test")

	out, err := Split(secret, 5, 3)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if len(out) != 5 {
		t.Fatalf("bad: %v", out)
	}

	for _, share := range out {
		if len(share) != len(secret)+1 {
			t.Fatalf("bad: %v", out)
		}
	}
}

func TestCombine_invalid(t *testing.T) {
	// Not enough parts
	if _, err := Combine(nil); err == nil {
		t.Fatalf("should err")
	}

	// Mis-match in length
	parts := [][]byte{
		[]byte("foo"),
		[]byte("ba"),
	}
	if _, err := Combine(parts); err == nil {
		t.Fatalf("should err")
	}

	//Too short
	parts = [][]byte{
		[]byte("f"),
		[]byte("b"),
	}
	if _, err := Combine(parts); err == nil {
		t.Fatalf("should err")
	}

	parts = [][]byte{
		[]byte("foo"),
		[]byte("foo"),
	}
	if _, err := Combine(parts); err == nil {
		t.Fatalf("should err")
	}
}

func TestCombine(t *testing.T) {
	secret := []byte("test")

	out, err := Split(secret, 5, 3)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// There is 5*4*3 possible choices,
	// we will just brute force try them all
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if j == i {
				continue
			}
			for k := 0; k < 5; k++ {
				if k == i || k == j {
					continue
				}
				parts := [][]byte{out[i], out[j], out[k]}
				recomb, err := Combine(parts)
				if err != nil {
					t.Fatalf("err: %v", err)
				}

				if !bytes.Equal(recomb, secret) {
					t.Errorf("parts: (i:%d, j:%d, k:%d) %v", i, j, k, parts)
					t.Fatalf("bad: %v %v", recomb, secret)
				}
			}
		}
	}
}

func TestField_Add(t *testing.T) {
	if out := add(16, 16); out != 0 {
		t.Fatalf("Bad: %v 16", out)
	}

	if out := add(3, 4); out != 7 {
		t.Fatalf("Bad: %v 7", out)
	}
}

func TestField_Mult(t *testing.T) {
	if out := mult(3, 7); out != 9 {
		t.Fatalf("Bad: %v 9", out)
	}

	if out := mult(3, 0); out != 0 {
		t.Fatalf("Bad: %v 0", out)
	}

	if out := mult(0, 3); out != 0 {
		t.Fatalf("Bad: %v 0", out)
	}
}

func TestField_Divide(t *testing.T) {
	if out := div(0, 7); out != 0 {
		t.Fatalf("Bad: %v 0", out)
	}

	if out := div(3, 3); out != 1 {
		t.Fatalf("Bad: %v 1", out)
	}

	if out := div(6, 3); out != 2 {
		t.Fatalf("Bad: %v 2", out)
	}
}

func TestPolynomial_Random(t *testing.T) {
	p, err := makePolynomial(42, 2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if p.coefficients[0] != 42 {
		t.Fatalf("bad: %v", p.coefficients)
	}
}

func TestPolynomial_Eval(t *testing.T) {
	p, err := makePolynomial(42, 1)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	if out := p.evaluate(0); out != 42 {
		t.Fatalf("bad: %v", out)
	}

	out := p.evaluate(1)
	exp := add(42, mult(1, p.coefficients[1]))
	if out != exp {
		t.Fatalf("bad: %v %v %v", out, exp, p.coefficients)
	}
}

func TestInterpolate_Rand(t *testing.T) {
	for i := 0; i < 256; i++ {
		p, err := makePolynomial(uint8(i), 2)
		if err != nil {
			t.Fatalf("err: %v", err)
		}

		xVals := []uint8{1, 2, 3}
		yVals := []uint8{p.evaluate(1), p.evaluate(2), p.evaluate(3)}
		out := interpolatePolynomial(xVals, yVals, 0)
		if out != uint8(i) {
			t.Fatalf("Bad: %v %d", out, i)
		}
	}
}
07070100000096000081A400000000000000000000000162794F9300000DB9000000000000000000000000000000000000001C00000000sops-3.7.3/shamir/tables.gopackage shamir

// Tables taken from http://www.samiam.org/galois.html
// They use 0xe5 (229) as the generator

var (
	// logTable provides the log(X)/log(g) at each index X
	logTable = [256]uint8{
		0x00, 0xff, 0xc8, 0x08, 0x91, 0x10, 0xd0, 0x36,
		0x5a, 0x3e, 0xd8, 0x43, 0x99, 0x77, 0xfe, 0x18,
		0x23, 0x20, 0x07, 0x70, 0xa1, 0x6c, 0x0c, 0x7f,
		0x62, 0x8b, 0x40, 0x46, 0xc7, 0x4b, 0xe0, 0x0e,
		0xeb, 0x16, 0xe8, 0xad, 0xcf, 0xcd, 0x39, 0x53,
		0x6a, 0x27, 0x35, 0x93, 0xd4, 0x4e, 0x48, 0xc3,
		0x2b, 0x79, 0x54, 0x28, 0x09, 0x78, 0x0f, 0x21,
		0x90, 0x87, 0x14, 0x2a, 0xa9, 0x9c, 0xd6, 0x74,
		0xb4, 0x7c, 0xde, 0xed, 0xb1, 0x86, 0x76, 0xa4,
		0x98, 0xe2, 0x96, 0x8f, 0x02, 0x32, 0x1c, 0xc1,
		0x33, 0xee, 0xef, 0x81, 0xfd, 0x30, 0x5c, 0x13,
		0x9d, 0x29, 0x17, 0xc4, 0x11, 0x44, 0x8c, 0x80,
		0xf3, 0x73, 0x42, 0x1e, 0x1d, 0xb5, 0xf0, 0x12,
		0xd1, 0x5b, 0x41, 0xa2, 0xd7, 0x2c, 0xe9, 0xd5,
		0x59, 0xcb, 0x50, 0xa8, 0xdc, 0xfc, 0xf2, 0x56,
		0x72, 0xa6, 0x65, 0x2f, 0x9f, 0x9b, 0x3d, 0xba,
		0x7d, 0xc2, 0x45, 0x82, 0xa7, 0x57, 0xb6, 0xa3,
		0x7a, 0x75, 0x4f, 0xae, 0x3f, 0x37, 0x6d, 0x47,
		0x61, 0xbe, 0xab, 0xd3, 0x5f, 0xb0, 0x58, 0xaf,
		0xca, 0x5e, 0xfa, 0x85, 0xe4, 0x4d, 0x8a, 0x05,
		0xfb, 0x60, 0xb7, 0x7b, 0xb8, 0x26, 0x4a, 0x67,
		0xc6, 0x1a, 0xf8, 0x69, 0x25, 0xb3, 0xdb, 0xbd,
		0x66, 0xdd, 0xf1, 0xd2, 0xdf, 0x03, 0x8d, 0x34,
		0xd9, 0x92, 0x0d, 0x63, 0x55, 0xaa, 0x49, 0xec,
		0xbc, 0x95, 0x3c, 0x84, 0x0b, 0xf5, 0xe6, 0xe7,
		0xe5, 0xac, 0x7e, 0x6e, 0xb9, 0xf9, 0xda, 0x8e,
		0x9a, 0xc9, 0x24, 0xe1, 0x0a, 0x15, 0x6b, 0x3a,
		0xa0, 0x51, 0xf4, 0xea, 0xb2, 0x97, 0x9e, 0x5d,
		0x22, 0x88, 0x94, 0xce, 0x19, 0x01, 0x71, 0x4c,
		0xa5, 0xe3, 0xc5, 0x31, 0xbb, 0xcc, 0x1f, 0x2d,
		0x3b, 0x52, 0x6f, 0xf6, 0x2e, 0x89, 0xf7, 0xc0,
		0x68, 0x1b, 0x64, 0x04, 0x06, 0xbf, 0x83, 0x38}

	// expTable provides the anti-log or exponentiation value
	// for the equivalent index
	expTable = [256]uint8{
		0x01, 0xe5, 0x4c, 0xb5, 0xfb, 0x9f, 0xfc, 0x12,
		0x03, 0x34, 0xd4, 0xc4, 0x16, 0xba, 0x1f, 0x36,
		0x05, 0x5c, 0x67, 0x57, 0x3a, 0xd5, 0x21, 0x5a,
		0x0f, 0xe4, 0xa9, 0xf9, 0x4e, 0x64, 0x63, 0xee,
		0x11, 0x37, 0xe0, 0x10, 0xd2, 0xac, 0xa5, 0x29,
		0x33, 0x59, 0x3b, 0x30, 0x6d, 0xef, 0xf4, 0x7b,
		0x55, 0xeb, 0x4d, 0x50, 0xb7, 0x2a, 0x07, 0x8d,
		0xff, 0x26, 0xd7, 0xf0, 0xc2, 0x7e, 0x09, 0x8c,
		0x1a, 0x6a, 0x62, 0x0b, 0x5d, 0x82, 0x1b, 0x8f,
		0x2e, 0xbe, 0xa6, 0x1d, 0xe7, 0x9d, 0x2d, 0x8a,
		0x72, 0xd9, 0xf1, 0x27, 0x32, 0xbc, 0x77, 0x85,
		0x96, 0x70, 0x08, 0x69, 0x56, 0xdf, 0x99, 0x94,
		0xa1, 0x90, 0x18, 0xbb, 0xfa, 0x7a, 0xb0, 0xa7,
		0xf8, 0xab, 0x28, 0xd6, 0x15, 0x8e, 0xcb, 0xf2,
		0x13, 0xe6, 0x78, 0x61, 0x3f, 0x89, 0x46, 0x0d,
		0x35, 0x31, 0x88, 0xa3, 0x41, 0x80, 0xca, 0x17,
		0x5f, 0x53, 0x83, 0xfe, 0xc3, 0x9b, 0x45, 0x39,
		0xe1, 0xf5, 0x9e, 0x19, 0x5e, 0xb6, 0xcf, 0x4b,
		0x38, 0x04, 0xb9, 0x2b, 0xe2, 0xc1, 0x4a, 0xdd,
		0x48, 0x0c, 0xd0, 0x7d, 0x3d, 0x58, 0xde, 0x7c,
		0xd8, 0x14, 0x6b, 0x87, 0x47, 0xe8, 0x79, 0x84,
		0x73, 0x3c, 0xbd, 0x92, 0xc9, 0x23, 0x8b, 0x97,
		0x95, 0x44, 0xdc, 0xad, 0x40, 0x65, 0x86, 0xa2,
		0xa4, 0xcc, 0x7f, 0xec, 0xc0, 0xaf, 0x91, 0xfd,
		0xf7, 0x4f, 0x81, 0x2f, 0x5b, 0xea, 0xa8, 0x1c,
		0x02, 0xd1, 0x98, 0x71, 0xed, 0x25, 0xe3, 0x24,
		0x06, 0x68, 0xb3, 0x93, 0x2c, 0x6f, 0x3e, 0x6c,
		0x0a, 0xb8, 0xce, 0xae, 0x74, 0xb1, 0x42, 0xb4,
		0x1e, 0xd3, 0x49, 0xe9, 0x9c, 0xc8, 0xc6, 0xc7,
		0x22, 0x6e, 0xdb, 0x20, 0xbf, 0x43, 0x51, 0x52,
		0x66, 0xb2, 0x76, 0x60, 0xda, 0xc5, 0xf3, 0xf6,
		0xaa, 0xcd, 0x9a, 0xa0, 0x75, 0x54, 0x0e, 0x01}
)
07070100000097000081A400000000000000000000000162794F93000000E4000000000000000000000000000000000000002100000000sops-3.7.3/shamir/tables_test.gopackage shamir

import "testing"

func TestTables(t *testing.T) {
	for i := 1; i < 256; i++ {
		logV := logTable[i]
		expV := expTable[logV]
		if expV != uint8(i) {
			t.Fatalf("bad: %d log: %d exp: %d", i, logV, expV)
		}
	}
}
07070100000098000081A400000000000000000000000162794F9300005E78000000000000000000000000000000000000001300000000sops-3.7.3/sops.go/*
Package sops manages JSON, YAML and BINARY documents to be encrypted or decrypted.

This package should not be used directly. Instead, Sops users should install the
command line client via `go get -u go.mozilla.org/sops/v3/cmd/sops`, or use the
decryption helper provided at `go.mozilla.org/sops/v3/decrypt`.

We do not guarantee API stability for any package other than `go.mozilla.org/sops/v3/decrypt`.

A Sops document is a Tree composed of a data branch with arbitrary key/value pairs
and a metadata branch with encryption and integrity information.

In JSON and YAML formats, the structure of the cleartext tree is preserved, keys are
stored in cleartext and only values are encrypted. Keeping the values in cleartext
provides better readability when storing Sops documents in version controls, and allows
for merging competing changes on documents. This is a major difference between Sops
and other encryption tools that store documents as encrypted blobs.

In BINARY format, the cleartext data is treated as a single blob and the encrypted
document is in JSON format with a single `data` key and a single encrypted value.

Sops allows operators to encrypt their documents with multiple master keys. Each of
the master key defined in the document is able to decrypt it, allowing users to
share documents amongst themselves without sharing keys, or using a PGP key as a
backup for KMS.

In practice, this is achieved by generating a data key for each document that is used
to encrypt all values, and encrypting the data with each master key defined. Being
able to decrypt the data key gives access to the document.

The integrity of each document is guaranteed by calculating a Message Authentication Code
(MAC) that is stored encrypted by the data key. When decrypting a document, the MAC should
be recalculated and compared with the MAC stored in the document to verify that no
fraudulent changes have been applied. The MAC covers keys and values as well as their
ordering.
*/
package sops //import "go.mozilla.org/sops/v3"

import (
	"crypto/rand"
	"crypto/sha512"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"go.mozilla.org/sops/v3/audit"
	"go.mozilla.org/sops/v3/keys"
	"go.mozilla.org/sops/v3/keyservice"
	"go.mozilla.org/sops/v3/logging"
	"go.mozilla.org/sops/v3/shamir"
	"golang.org/x/net/context"
)

// DefaultUnencryptedSuffix is the default suffix a TreeItem key has to end with for sops to leave its Value unencrypted
const DefaultUnencryptedSuffix = "_unencrypted"

type sopsError string

func (e sopsError) Error() string {
	return string(e)
}

// MacMismatch occurs when the computed MAC does not match the expected ones
const MacMismatch = sopsError("MAC mismatch")

// MetadataNotFound occurs when the input file is malformed and doesn't have sops metadata in it
const MetadataNotFound = sopsError("sops metadata not found")

var log *logrus.Logger

func init() {
	log = logging.NewLogger("SOPS")
}

// Cipher provides a way to encrypt and decrypt the data key used to encrypt and decrypt sops files, so that the
// data key can be stored alongside the encrypted content. A Cipher must be able to decrypt the values it encrypts.
type Cipher interface {
	// Encrypt takes a plaintext, a key and additional data and returns the plaintext encrypted with the key, using the
	// additional data for authentication
	Encrypt(plaintext interface{}, key []byte, additionalData string) (ciphertext string, err error)
	// Encrypt takes a ciphertext, a key and additional data and returns the ciphertext encrypted with the key, using
	// the additional data for authentication
	Decrypt(ciphertext string, key []byte, additionalData string) (plaintext interface{}, err error)
}

// Comment represents a comment in the sops tree for the file formats that actually support them.
type Comment struct {
	Value string
}

// TreeItem is an item inside sops's tree
type TreeItem struct {
	Key   interface{}
	Value interface{}
}

// TreeBranch is a branch inside sops's tree. It is a slice of TreeItems and is therefore ordered
type TreeBranch []TreeItem

// TreeBranches is a collection of TreeBranch
// Trees usually have more than one branch
type TreeBranches []TreeBranch

func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
	switch component := path[0].(type) {
	case int:
		if len(path) == 1 {
			return []interface{}{
				leaf,
			}
		}
		return []interface{}{
			valueFromPathAndLeaf(path[1:], leaf),
		}
	default:
		if len(path) == 1 {
			return TreeBranch{
				TreeItem{
					Key:   component,
					Value: leaf,
				},
			}
		}
		return TreeBranch{
			TreeItem{
				Key:   component,
				Value: valueFromPathAndLeaf(path[1:], leaf),
			},
		}
	}
}

func set(branch interface{}, path []interface{}, value interface{}) interface{} {
	switch branch := branch.(type) {
	case TreeBranch:
		for i, item := range branch {
			if item.Key == path[0] {
				if len(path) == 1 {
					branch[i].Value = value
				} else {
					branch[i].Value = set(item.Value, path[1:], value)
				}
				return branch
			}
		}
		// Not found, need to add the next path entry to the branch
		if len(path) == 1 {
			return append(branch, TreeItem{Key: path[0], Value: value})
		}
		return valueFromPathAndLeaf(path, value)
	case []interface{}:
		position := path[0].(int)
		if len(path) == 1 {
			if position >= len(branch) {
				return append(branch, value)
			}
			branch[position] = value
		} else {
			if position >= len(branch) {
				branch = append(branch, valueFromPathAndLeaf(path[1:], value))
			}
			branch[position] = set(branch[position], path[1:], value)
		}
		return branch
	default:
		return valueFromPathAndLeaf(path, value)
	}
}

// Set sets a value on a given tree for the specified path
func (branch TreeBranch) Set(path []interface{}, value interface{}) TreeBranch {
	return set(branch, path, value).(TreeBranch)
}

// Tree is the data structure used by sops to represent documents internally
type Tree struct {
	Metadata Metadata
	Branches TreeBranches
	// FilePath is the path of the file this struct represents
	FilePath string
}

// Truncate truncates the tree to the path specified
func (branch TreeBranch) Truncate(path []interface{}) (interface{}, error) {
	log.WithField("path", path).Info("Truncating tree")
	var current interface{} = branch
	for _, component := range path {
		switch component := component.(type) {
		case string:
			found := false
			for _, item := range current.(TreeBranch) {
				if item.Key == component {
					current = item.Value
					found = true
					break
				}
			}
			if !found {
				return nil, fmt.Errorf("component ['%s'] not found", component)
			}
		case int:
			if reflect.ValueOf(current).Kind() != reflect.Slice {
				return nil, fmt.Errorf("component [%d] is integer, but tree part is not a slice", component)
			}
			if reflect.ValueOf(current).Len() <= component {
				return nil, fmt.Errorf("component [%d] accesses out of bounds", component)
			}
			current = reflect.ValueOf(current).Index(component).Interface()
		}
	}
	return current, nil
}

func (branch TreeBranch) walkValue(in interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (interface{}, error) {
	switch in := in.(type) {
	case string:
		return onLeaves(in, path)
	case []byte:
		return onLeaves(string(in), path)
	case int:
		return onLeaves(in, path)
	case bool:
		return onLeaves(in, path)
	case float64:
		return onLeaves(in, path)
	case Comment:
		return onLeaves(in, path)
	case TreeBranch:
		return branch.walkBranch(in, path, onLeaves)
	case []interface{}:
		return branch.walkSlice(in, path, onLeaves)
	case nil:
		// the value returned remains the same since it doesn't make
		// sense to encrypt or decrypt a nil value
		return nil, nil
	default:
		return nil, fmt.Errorf("Cannot walk value, unknown type: %T", in)
	}
}

func (branch TreeBranch) walkSlice(in []interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) ([]interface{}, error) {
	for i, v := range in {
		newV, err := branch.walkValue(v, path, onLeaves)
		if err != nil {
			return nil, err
		}
		in[i] = newV
	}
	return in, nil
}

func (branch TreeBranch) walkBranch(in TreeBranch, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (TreeBranch, error) {
	for i, item := range in {
		if _, ok := item.Key.(Comment); ok {
			enc, err := branch.walkValue(item.Key, path, onLeaves)
			if err != nil {
				return nil, err
			}
			if encComment, ok := enc.(Comment); ok {
				in[i].Key = encComment
				continue
			} else if comment, ok := enc.(string); ok {
				in[i].Key = Comment{Value: comment}
				continue
			} else {
				return nil, fmt.Errorf("walkValue of Comment should be either Comment or string, was %T", enc)
			}
		}
		key, ok := item.Key.(string)
		if !ok {
			return nil, fmt.Errorf("Tree contains a non-string key (type %T): %s. Only string keys are"+
				"supported", item.Key, item.Key)
		}
		newV, err := branch.walkValue(item.Value, append(path, key), onLeaves)
		if err != nil {
			return nil, err
		}
		in[i].Value = newV
	}
	return in, nil
}

// Encrypt walks over the tree and encrypts all values with the provided cipher,
// except those whose key ends with the UnencryptedSuffix specified on the
// Metadata struct, those not ending with EncryptedSuffix, if EncryptedSuffix
// is provided (by default it is not), those not matching EncryptedRegex,
// if EncryptedRegex is provided (by default it is not) or those matching
// UnencryptedRegex, if UnencryptedRegex is provided (by default it is not).
// If encryption is successful, it returns the MAC for the encrypted tree.
func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) {
	audit.SubmitEvent(audit.EncryptEvent{
		File: tree.FilePath,
	})
	hash := sha512.New()
	walk := func(branch TreeBranch) error {
		_, err := branch.walkBranch(branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
			// Only add to MAC if not a comment
			if _, ok := in.(Comment); !ok {
				bytes, err := ToBytes(in)
				if err != nil {
					return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err)
				}
				hash.Write(bytes)
			}
			encrypted := true
			if tree.Metadata.UnencryptedSuffix != "" {
				for _, v := range path {
					if strings.HasSuffix(v, tree.Metadata.UnencryptedSuffix) {
						encrypted = false
						break
					}
				}
			}
			if tree.Metadata.EncryptedSuffix != "" {
				encrypted = false
				for _, v := range path {
					if strings.HasSuffix(v, tree.Metadata.EncryptedSuffix) {
						encrypted = true
						break
					}
				}
			}
			if tree.Metadata.UnencryptedRegex != "" {
				for _, p := range path {
					matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p))
					if matched {
						encrypted = false
						break
					}
				}
			}
			if tree.Metadata.EncryptedRegex != "" {
				encrypted = false
				for _, p := range path {
					matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p))
					if matched {
						encrypted = true
						break
					}
				}
			}
			if encrypted {
				var err error
				pathString := strings.Join(path, ":") + ":"
				in, err = cipher.Encrypt(in, key, pathString)
				if err != nil {
					return nil, fmt.Errorf("Could not encrypt value: %s", err)
				}
			}
			return in, nil
		})
		return err
	}

	for _, branch := range tree.Branches {
		err := walk(branch)
		if err != nil {
			return "", fmt.Errorf("Error walking tree: %s", err)
		}
	}
	return fmt.Sprintf("%X", hash.Sum(nil)), nil
}

// Decrypt walks over the tree and decrypts all values with the provided cipher,
// except those whose key ends with the UnencryptedSuffix specified on the Metadata struct,
// those not ending with EncryptedSuffix, if EncryptedSuffix is provided (by default it is not),
// those not matching EncryptedRegex, if EncryptedRegex is provided (by default it is not),
// or those matching UnencryptedRegex, if UnencryptedRegex is provided (by default it is not).
// If decryption is successful, it returns the MAC for the decrypted tree.
func (tree Tree) Decrypt(key []byte, cipher Cipher) (string, error) {
	log.Debug("Decrypting tree")
	audit.SubmitEvent(audit.DecryptEvent{
		File: tree.FilePath,
	})
	hash := sha512.New()
	walk := func(branch TreeBranch) error {
		_, err := branch.walkBranch(branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
			encrypted := true
			if tree.Metadata.UnencryptedSuffix != "" {
				for _, p := range path {
					if strings.HasSuffix(p, tree.Metadata.UnencryptedSuffix) {
						encrypted = false
						break
					}
				}
			}
			if tree.Metadata.EncryptedSuffix != "" {
				encrypted = false
				for _, p := range path {
					if strings.HasSuffix(p, tree.Metadata.EncryptedSuffix) {
						encrypted = true
						break
					}
				}
			}
			if tree.Metadata.UnencryptedRegex != "" {
				for _, p := range path {
					matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p))
					if matched {
						encrypted = false
						break
					}
				}
			}
			if tree.Metadata.EncryptedRegex != "" {
				encrypted = false
				for _, p := range path {
					matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p))
					if matched {
						encrypted = true
						break
					}
				}
			}
			var v interface{}
			if encrypted {
				var err error
				pathString := strings.Join(path, ":") + ":"
				if c, ok := in.(Comment); ok {
					v, err = cipher.Decrypt(c.Value, key, pathString)
					if err != nil {
						// Assume the comment was not encrypted in the first place
						log.WithField("comment", c.Value).
							Warn("Found possibly unencrypted comment in file. " +
								"This is to be expected if the file being " +
								"decrypted was created with an older version of " +
								"SOPS.")
						v = c
					}
				} else {
					v, err = cipher.Decrypt(in.(string), key, pathString)
					if err != nil {
						return nil, fmt.Errorf("Could not decrypt value: %s", err)
					}
				}
			} else {
				v = in
			}
			// Only add to MAC if not a comment
			if _, ok := v.(Comment); !ok {
				bytes, err := ToBytes(v)
				if err != nil {
					return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err)
				}
				hash.Write(bytes)
			}
			return v, nil
		})
		return err
	}
	for _, branch := range tree.Branches {
		err := walk(branch)
		if err != nil {
			return "", fmt.Errorf("Error walking tree: %s", err)
		}
	}
	return fmt.Sprintf("%X", hash.Sum(nil)), nil
}

// GenerateDataKey generates a new random data key and encrypts it with all MasterKeys.
func (tree Tree) GenerateDataKey() ([]byte, []error) {
	newKey := make([]byte, 32)
	_, err := rand.Read(newKey)
	if err != nil {
		return nil, []error{fmt.Errorf("Could not generate random key: %s", err)}
	}
	return newKey, tree.Metadata.UpdateMasterKeys(newKey)
}

// GenerateDataKeyWithKeyServices generates a new random data key and encrypts it with all MasterKeys.
func (tree *Tree) GenerateDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient) ([]byte, []error) {
	newKey := make([]byte, 32)
	_, err := rand.Read(newKey)
	if err != nil {
		return nil, []error{fmt.Errorf("Could not generate random key: %s", err)}
	}
	return newKey, tree.Metadata.UpdateMasterKeysWithKeyServices(newKey, svcs)
}

// Metadata holds information about a file encrypted by sops
type Metadata struct {
	LastModified              time.Time
	UnencryptedSuffix         string
	EncryptedSuffix           string
	UnencryptedRegex          string
	EncryptedRegex            string
	MessageAuthenticationCode string
	Version                   string
	KeyGroups                 []KeyGroup
	// ShamirThreshold is the number of key groups required to recover the
	// original data key
	ShamirThreshold int
	// DataKey caches the decrypted data key so it doesn't have to be decrypted with a master key every time it's needed
	DataKey []byte
}

// KeyGroup is a slice of SOPS MasterKeys that all encrypt the same part of the data key
type KeyGroup []keys.MasterKey

// EncryptedFileLoader is the interface for loading of encrypted files. It provides a
// way to load encrypted SOPS files into the internal SOPS representation. Because it
// loads encrypted files, the returned data structure already contains all SOPS
// metadata.
type EncryptedFileLoader interface {
	LoadEncryptedFile(in []byte) (Tree, error)
}

// PlainFileLoader is the interface for loading of plain text files. It provides a
// way to load unencrypted files into SOPS. Because the files it loads are
// unencrypted, the returned data structure does not contain any metadata.
type PlainFileLoader interface {
	LoadPlainFile(in []byte) (TreeBranches, error)
}

// EncryptedFileEmitter is the interface for emitting encrypting files. It provides a
// way to emit encrypted files from the internal SOPS representation.
type EncryptedFileEmitter interface {
	EmitEncryptedFile(Tree) ([]byte, error)
}

// PlainFileEmitter is the interface for emitting plain text files. It provides a way
// to emit plain text files from the internal SOPS representation so that they can be
// shown
type PlainFileEmitter interface {
	EmitPlainFile(TreeBranches) ([]byte, error)
}

// ValueEmitter is the interface for emitting a value. It provides a way to emit
// values from the internal SOPS representation so that they can be shown
type ValueEmitter interface {
	EmitValue(interface{}) ([]byte, error)
}

// Store is used to interact with files, both encrypted and unencrypted.
type Store interface {
	EncryptedFileLoader
	PlainFileLoader
	EncryptedFileEmitter
	PlainFileEmitter
	ValueEmitter
}

// MasterKeyCount returns the number of master keys available
func (m *Metadata) MasterKeyCount() int {
	count := 0
	for _, group := range m.KeyGroups {
		count += len(group)
	}
	return count
}

// UpdateMasterKeysWithKeyServices encrypts the data key with all master keys using the provided key services
func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyservice.KeyServiceClient) (errs []error) {
	if len(svcs) == 0 {
		return []error{
			fmt.Errorf("no key services provided, cannot update master keys"),
		}
	}
	var parts [][]byte
	if len(m.KeyGroups) == 1 {
		// If there's only one key group, we can't do Shamir. All keys
		// in the group encrypt the whole data key.
		parts = append(parts, dataKey)
	} else {
		var err error
		if m.ShamirThreshold == 0 {
			m.ShamirThreshold = len(m.KeyGroups)
		}
		log.WithFields(logrus.Fields{
			"quorum": m.ShamirThreshold,
			"parts":  len(m.KeyGroups),
		}).Info("Splitting data key with Shamir Secret Sharing")
		parts, err = shamir.Split(dataKey, len(m.KeyGroups), int(m.ShamirThreshold))
		if err != nil {
			errs = append(errs, fmt.Errorf("could not split data key into parts for Shamir: %s", err))
			return
		}
		if len(parts) != len(m.KeyGroups) {
			errs = append(errs, fmt.Errorf("not enough parts obtained from Shamir: need %d, got %d", len(m.KeyGroups), len(parts)))
			return
		}
	}
	for i, group := range m.KeyGroups {
		part := parts[i]
		for _, key := range group {
			svcKey := keyservice.KeyFromMasterKey(key)
			var keyErrs []error
			encrypted := false
			for _, svc := range svcs {
				rsp, err := svc.Encrypt(context.Background(), &keyservice.EncryptRequest{
					Key:       &svcKey,
					Plaintext: part,
				})
				if err != nil {
					keyErrs = append(keyErrs, fmt.Errorf("failed to encrypt new data key with master key %q: %w", key.ToString(), err))
					continue
				}
				key.SetEncryptedDataKey(rsp.Ciphertext)
				encrypted = true
				// Only need to encrypt the key successfully with one service
				break
			}
			if !encrypted {
				errs = append(errs, keyErrs...)
			}
		}
	}
	m.DataKey = dataKey
	return
}

// UpdateMasterKeys encrypts the data key with all master keys
func (m *Metadata) UpdateMasterKeys(dataKey []byte) (errs []error) {
	return m.UpdateMasterKeysWithKeyServices(dataKey, []keyservice.KeyServiceClient{
		keyservice.NewLocalClient(),
	})
}

// GetDataKeyWithKeyServices retrieves the data key, asking KeyServices to decrypt it with each
// MasterKey in the Metadata's KeySources until one of them succeeds.
func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient) ([]byte, error) {
	if m.DataKey != nil {
		return m.DataKey, nil
	}
	getDataKeyErr := getDataKeyError{
		RequiredSuccessfulKeyGroups: m.ShamirThreshold,
		GroupResults:                make([]error, len(m.KeyGroups)),
	}
	var parts [][]byte
	for i, group := range m.KeyGroups {
		part, err := decryptKeyGroup(group, svcs)
		if err == nil {
			parts = append(parts, part)
		}
		getDataKeyErr.GroupResults[i] = err
	}
	var dataKey []byte
	if len(m.KeyGroups) > 1 {
		if len(parts) < m.ShamirThreshold {
			return nil, &getDataKeyErr
		}
		var err error
		dataKey, err = shamir.Combine(parts)
		if err != nil {
			return nil, fmt.Errorf("could not get data key from shamir parts: %s", err)
		}
	} else {
		if len(parts) != 1 {
			return nil, &getDataKeyErr
		}
		dataKey = parts[0]
	}
	log.Info("Data key recovered successfully")
	m.DataKey = dataKey
	return dataKey, nil
}

// decryptKeyGroup tries to decrypt the contents of the provided KeyGroup with
// any of the MasterKeys in the KeyGroup with any of the provided key services,
// returning as soon as one key service succeeds.
func decryptKeyGroup(group KeyGroup, svcs []keyservice.KeyServiceClient) ([]byte, error) {
	var keyErrs []error
	for _, key := range group {
		part, err := decryptKey(key, svcs)
		if err != nil {
			keyErrs = append(keyErrs, err)
		} else {
			return part, nil
		}
	}
	return nil, decryptKeyErrors(keyErrs)
}

// decryptKey tries to decrypt the contents of the provided MasterKey with any
// of the key services, returning as soon as one key service succeeds.
func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient) ([]byte, error) {
	svcKey := keyservice.KeyFromMasterKey(key)
	var part []byte
	decryptErr := decryptKeyError{
		keyName: key.ToString(),
	}
	for _, svc := range svcs {
		// All keys in a key group encrypt the same part, so as soon
		// as we decrypt it successfully with one key, we need to
		// proceed with the next group
		var err error
		if part == nil {
			var rsp *keyservice.DecryptResponse
			rsp, err = svc.Decrypt(
				context.Background(),
				&keyservice.DecryptRequest{
					Ciphertext: key.EncryptedDataKey(),
					Key:        &svcKey,
				})
			if err == nil {
				part = rsp.Plaintext
			}
		}
		decryptErr.errs = append(decryptErr.errs, err)
	}
	if part != nil {
		return part, nil
	}
	return nil, &decryptErr
}

// GetDataKey retrieves the data key from the first MasterKey in the Metadata's KeySources that's able to return it,
// using the local KeyService
func (m Metadata) GetDataKey() ([]byte, error) {
	return m.GetDataKeyWithKeyServices([]keyservice.KeyServiceClient{
		keyservice.NewLocalClient(),
	})
}

// ToBytes converts a string, int, float or bool to a byte representation.
func ToBytes(in interface{}) ([]byte, error) {
	switch in := in.(type) {
	case string:
		return []byte(in), nil
	case int:
		return []byte(strconv.Itoa(in)), nil
	case float64:
		return []byte(strconv.FormatFloat(in, 'f', -1, 64)), nil
	case bool:
		return []byte(strings.Title(strconv.FormatBool(in))), nil
	case []byte:
		return in, nil
	case Comment:
		return ToBytes(in.Value)
	default:
		return nil, fmt.Errorf("Could not convert unknown type %T to bytes", in)
	}
}

// EmitAsMap will emit the tree branches as a map. This is used by the publish
// command for writing decrypted trees to various destinations. Should only be
// used for outputting to data structures in code.
func EmitAsMap(in TreeBranches) (map[string]interface{}, error) {
	data := map[string]interface{}{}

	for _, branch := range in {
		for _, item := range branch {
			if _, ok := item.Key.(Comment); ok {
				continue
			}
			val, err := encodeValueForMap(item.Value)
			if err != nil {
				return nil, err
			}
			data[item.Key.(string)] = val
		}
	}

	return data, nil
}

func encodeValueForMap(v interface{}) (interface{}, error) {
	switch v := v.(type) {
	case TreeBranch:
		return EmitAsMap([]TreeBranch{v})
	default:
		return v, nil
	}
}
07070100000099000081A400000000000000000000000162794F930000377F000000000000000000000000000000000000001800000000sops-3.7.3/sops_test.gopackage sops

import (
	"bytes"
	"fmt"
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
)

type reverseCipher struct{}

// reverse returns its argument string reversed rune-wise left to right.
func reverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

func (c reverseCipher) Encrypt(value interface{}, key []byte, path string) (string, error) {
	b, err := ToBytes(value)
	if err != nil {
		return "", err
	}
	return reverse(string(b)), nil
}
func (c reverseCipher) Decrypt(value string, key []byte, path string) (plaintext interface{}, err error) {
	if value == "error" {
		return nil, fmt.Errorf("Error")
	}
	return reverse(value), nil
}

func TestUnencryptedSuffix(t *testing.T) {
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foo_unencrypted",
				Value: "bar",
			},
			TreeItem{
				Key: "bar_unencrypted",
				Value: TreeBranch{
					TreeItem{
						Key:   "foo",
						Value: "bar",
					},
				},
			},
		},
	}
	tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: "_unencrypted"}}
	expected := TreeBranch{
		TreeItem{
			Key:   "foo_unencrypted",
			Value: "bar",
		},
		TreeItem{
			Key: "bar_unencrypted",
			Value: TreeBranch{
				TreeItem{
					Key:   "foo",
					Value: "bar",
				},
			},
		},
	}
	cipher := reverseCipher{}
	_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Encrypting the tree failed: %s", err)
	}
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
	}
	_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Decrypting the tree failed: %s", err)
	}
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
	}
}

func TestEncryptedSuffix(t *testing.T) {
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foo_encrypted",
				Value: "bar",
			},
			TreeItem{
				Key: "bar",
				Value: TreeBranch{
					TreeItem{
						Key:   "foo",
						Value: "bar",
					},
				},
			},
		},
	}
	tree := Tree{Branches: branches, Metadata: Metadata{EncryptedSuffix: "_encrypted"}}
	expected := TreeBranch{
		TreeItem{
			Key:   "foo_encrypted",
			Value: "rab",
		},
		TreeItem{
			Key: "bar",
			Value: TreeBranch{
				TreeItem{
					Key:   "foo",
					Value: "bar",
				},
			},
		},
	}
	cipher := reverseCipher{}
	_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Encrypting the tree failed: %s", err)
	}
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
	}
	_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Decrypting the tree failed: %s", err)
	}
	expected[0].Value = "bar"
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
	}
}

func TestEncryptedRegex(t *testing.T) {
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "enc:foo",
				Value: "bar",
			},
			TreeItem{
				Key: "bar",
				Value: TreeBranch{
					TreeItem{
						Key:   "foo",
						Value: "bar",
					},
				},
			},
		},
	}
	tree := Tree{Branches: branches, Metadata: Metadata{EncryptedRegex: "^enc:"}}
	expected := TreeBranch{
		TreeItem{
			Key:   "enc:foo",
			Value: "rab",
		},
		TreeItem{
			Key: "bar",
			Value: TreeBranch{
				TreeItem{
					Key:   "foo",
					Value: "bar",
				},
			},
		},
	}
	cipher := reverseCipher{}
	_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Encrypting the tree failed: %s", err)
	}
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
	}
	_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Decrypting the tree failed: %s", err)
	}
	expected[0].Value = "bar"
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
	}
}

func TestUnencryptedRegex(t *testing.T) {
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "dec:foo",
				Value: "bar",
			},
			TreeItem{
				Key: "dec:bar",
				Value: TreeBranch{
					TreeItem{
						Key:   "foo",
						Value: "bar",
					},
				},
			},
		},
	}
	tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedRegex: "^dec:"}}
	expected := TreeBranch{
		TreeItem{
			Key:   "dec:foo",
			Value: "bar",
		},
		TreeItem{
			Key: "dec:bar",
			Value: TreeBranch{
				TreeItem{
					Key:   "foo",
					Value: "bar",
				},
			},
		},
	}
	cipher := reverseCipher{}

	_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Encrypting the tree failed: %s", err)
	}
	// expected[1].Value[] = "bar"
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
	}
	_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
	if err != nil {
		t.Errorf("Decrypting the tree failed: %s", err)
	}
	if !reflect.DeepEqual(tree.Branches[0], expected) {
		t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
	}
}

type MockCipher struct{}

func (m MockCipher) Encrypt(value interface{}, key []byte, path string) (string, error) {
	return "a", nil
}

func (m MockCipher) Decrypt(value string, key []byte, path string) (interface{}, error) {
	return "a", nil
}

func TestEncrypt(t *testing.T) {
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foo",
				Value: "bar",
			},
			TreeItem{
				Key: "baz",
				Value: TreeBranch{
					TreeItem{
						Key:   "bar",
						Value: 5,
					},
				},
			},
			TreeItem{
				Key:   "bar",
				Value: false,
			},
			TreeItem{
				Key:   "foobar",
				Value: 2.12,
			},
			TreeItem{
				Key:   "barfoo",
				Value: nil,
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo2",
				Value: "bar",
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo3",
				Value: "bar",
			},
		},
	}
	expected := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foo",
				Value: "a",
			},
			TreeItem{
				Key: "baz",
				Value: TreeBranch{
					TreeItem{
						Key:   "bar",
						Value: "a",
					},
				},
			},
			TreeItem{
				Key:   "bar",
				Value: "a",
			},
			TreeItem{
				Key:   "foobar",
				Value: "a",
			},
			TreeItem{
				Key:   "barfoo",
				Value: nil,
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo2",
				Value: "a",
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo3",
				Value: "a",
			},
		},
	}
	tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: DefaultUnencryptedSuffix}}
	tree.Encrypt(bytes.Repeat([]byte{'f'}, 32), MockCipher{})
	if !reflect.DeepEqual(tree.Branches, expected) {
		t.Errorf("%s does not equal expected tree: %s", tree.Branches, expected)
	}
}

func TestDecrypt(t *testing.T) {
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foo",
				Value: "bar",
			},
			TreeItem{
				Key: "baz",
				Value: TreeBranch{
					TreeItem{
						Key:   "bar",
						Value: "5",
					},
				},
			},
			TreeItem{
				Key:   "bar",
				Value: "false",
			},
			TreeItem{
				Key:   "foobar",
				Value: "2.12",
			},
			TreeItem{
				Key:   "barfoo",
				Value: nil,
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo",
				Value: "bar",
			},
			TreeItem{
				Key: "baz",
				Value: TreeBranch{
					TreeItem{
						Key:   "bar",
						Value: "6",
					},
				},
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo3",
				Value: "bar",
			},
		},
	}
	expected := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foo",
				Value: "a",
			},
			TreeItem{
				Key: "baz",
				Value: TreeBranch{
					TreeItem{
						Key:   "bar",
						Value: "a",
					},
				},
			},
			TreeItem{
				Key:   "bar",
				Value: "a",
			},
			TreeItem{
				Key:   "foobar",
				Value: "a",
			},
			TreeItem{
				Key:   "barfoo",
				Value: nil,
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo",
				Value: "a",
			},
			TreeItem{
				Key: "baz",
				Value: TreeBranch{
					TreeItem{
						Key:   "bar",
						Value: "a",
					},
				},
			},
		},
		TreeBranch{
			TreeItem{
				Key:   "foo3",
				Value: "a",
			},
		},
	}
	tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: DefaultUnencryptedSuffix}}
	tree.Decrypt(bytes.Repeat([]byte{'f'}, 32), MockCipher{})
	if !reflect.DeepEqual(tree.Branches, expected) {
		t.Errorf("%s does not equal expected tree: %s", tree.Branches[0], expected)
	}
}

func TestTruncateTree(t *testing.T) {
	tree := TreeBranch{
		TreeItem{
			Key:   "foo",
			Value: 2,
		},
		TreeItem{
			Key: "bar",
			Value: TreeBranch{
				TreeItem{
					Key: "foobar",
					Value: []int{
						1,
						2,
						3,
						4,
					},
				},
			},
		},
	}
	expected := 3
	result, err := tree.Truncate([]interface{}{
		"bar",
		"foobar",
		2,
	})
	assert.Equal(t, nil, err)
	assert.Equal(t, expected, result)
}

func TestEncryptComments(t *testing.T) {
	tree := Tree{
		Branches: TreeBranches{
			TreeBranch{
				TreeItem{
					Key:   Comment{"foo"},
					Value: nil,
				},
				TreeItem{
					Key: "list",
					Value: []interface{}{
						"1",
						Comment{"bar"},
						"2",
					},
				},
			},
		},
		Metadata: Metadata{
			UnencryptedSuffix: DefaultUnencryptedSuffix,
		},
	}
	tree.Encrypt(bytes.Repeat([]byte{'f'}, 32), reverseCipher{})
	assert.Equal(t, "oof", tree.Branches[0][0].Key.(Comment).Value)
	assert.Equal(t, "rab", tree.Branches[0][1].Value.([]interface{})[1])
}

func TestDecryptComments(t *testing.T) {
	tree := Tree{
		Branches: TreeBranches{
			TreeBranch{
				TreeItem{
					Key:   Comment{"oof"},
					Value: nil,
				},
				TreeItem{
					Key: "list",
					Value: []interface{}{
						"1",
						Comment{"rab"},
						"2",
					},
				},
				TreeItem{
					Key:   "list",
					Value: nil,
				},
			},
		},
		Metadata: Metadata{
			UnencryptedSuffix: DefaultUnencryptedSuffix,
		},
	}
	tree.Decrypt(bytes.Repeat([]byte{'f'}, 32), reverseCipher{})
	assert.Equal(t, "foo", tree.Branches[0][0].Key.(Comment).Value)
	assert.Equal(t, "bar", tree.Branches[0][1].Value.([]interface{})[1])
}

func TestDecryptUnencryptedComments(t *testing.T) {
	tree := Tree{
		Branches: TreeBranches{
			TreeBranch{
				TreeItem{
					// We use `error` to simulate an error decrypting, the fake cipher will error in this case
					Key:   Comment{"error"},
					Value: nil,
				},
			},
		},
		Metadata: Metadata{},
	}
	tree.Decrypt(bytes.Repeat([]byte{'f'}, 32), reverseCipher{})
	assert.Equal(t, "error", tree.Branches[0][0].Key.(Comment).Value)
}

func TestSetNewKey(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key: "foo",
			Value: TreeBranch{
				TreeItem{
					Key: "bar",
					Value: TreeBranch{
						TreeItem{
							Key:   "baz",
							Value: "foobar",
						},
					},
				},
			},
		},
	}
	set := branch.Set([]interface{}{"foo", "bar", "foo"}, "hello")
	assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[1].Value)
}

func TestSetArrayDeepNew(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key: "foo",
			Value: []interface{}{
				"one",
				"two",
			},
		},
	}
	set := branch.Set([]interface{}{"foo", 2, "bar"}, "hello")
	assert.Equal(t, "hello", set[0].Value.([]interface{})[2].(TreeBranch)[0].Value)
}

func TestSetNewKeyDeep(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key:   "foo",
			Value: "bar",
		},
	}
	set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
	assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
}

func TestSetNewKeyOnEmptyBranch(t *testing.T) {
	branch := TreeBranch{}
	set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
	assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
}

func TestSetArray(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key: "foo",
			Value: []interface{}{
				"one",
				"two",
				"three",
			},
		},
	}
	set := branch.Set([]interface{}{"foo", 0}, "uno")
	assert.Equal(t, "uno", set[0].Value.([]interface{})[0])
}

func TestSetArrayNew(t *testing.T) {
	branch := TreeBranch{}
	set := branch.Set([]interface{}{"foo", 0, 0}, "uno")
	assert.Equal(t, "uno", set[0].Value.([]interface{})[0].([]interface{})[0])
}

func TestSetExisting(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key:   "foo",
			Value: "foobar",
		},
	}
	set := branch.Set([]interface{}{"foo"}, "bar")
	assert.Equal(t, "bar", set[0].Value)
}

func TestSetArrayLeafNewItem(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key:   "array",
			Value: []interface{}{},
		},
	}
	set := branch.Set([]interface{}{"array", 2}, "hello")
	assert.Equal(t, TreeBranch{
		TreeItem{
			Key: "array",
			Value: []interface{}{
				"hello",
			},
		},
	}, set)
}

func TestSetArrayNonLeaf(t *testing.T) {
	branch := TreeBranch{
		TreeItem{
			Key: "array",
			Value: []interface{}{
				1,
			},
		},
	}
	set := branch.Set([]interface{}{"array", 0, "hello"}, "hello")
	assert.Equal(t, TreeBranch{
		TreeItem{
			Key: "array",
			Value: []interface{}{
				TreeBranch{
					TreeItem{
						Key:   "hello",
						Value: "hello",
					},
				},
			},
		},
	}, set)
}

func TestEmitAsMap(t *testing.T) {
	expected := map[string]interface{}{
		"foobar": "barfoo",
		"number": 42,
		"foo": map[string]interface{}{
			"bar": map[string]interface{}{
				"baz": "foobar",
			},
		},
	}
	branches := TreeBranches{
		TreeBranch{
			TreeItem{
				Key:   "foobar",
				Value: "barfoo",
			},
			TreeItem{
				Key:   "number",
				Value: 42,
			},
		},
		TreeBranch{
			TreeItem{
				Key: "foo",
				Value: TreeBranch{
					TreeItem{
						Key: "bar",
						Value: TreeBranch{
							TreeItem{
								Key:   "baz",
								Value: "foobar",
							},
						},
					},
				},
			},
		},
	}

	data, err := EmitAsMap(branches)

	if assert.NoError(t, err) {
		assert.Equal(t, expected, data)
	}
}
0707010000009A000041ED00000000000000000000000662794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/stores0707010000009B000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001900000000sops-3.7.3/stores/dotenv0707010000009C000081A400000000000000000000000162794F9300001278000000000000000000000000000000000000002200000000sops-3.7.3/stores/dotenv/store.gopackage dotenv //import "go.mozilla.org/sops/v3/stores/dotenv"

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/stores"
)

// SopsPrefix is the prefix for all metadatada entry keys
const SopsPrefix = "sops_"

// Store handles storage of dotenv data
type Store struct {
}

// LoadEncryptedFile loads an encrypted file's bytes onto a sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
	branches, err := store.LoadPlainFile(in)
	if err != nil {
		return sops.Tree{}, err
	}

	var resultBranch sops.TreeBranch
	mdMap := make(map[string]interface{})
	for _, item := range branches[0] {
		switch key := item.Key.(type) {
		case string:
			if strings.HasPrefix(key, SopsPrefix) {
				key = key[len(SopsPrefix):]
				mdMap[key] = item.Value
			} else {
				resultBranch = append(resultBranch, item)
			}
		case sops.Comment:
			resultBranch = append(resultBranch, item)
		default:
			panic(fmt.Sprintf("Unexpected type: %T (value %#v)", key, key))
		}
	}

	metadata, err := mapToMetadata(mdMap)
	if err != nil {
		return sops.Tree{}, err
	}
	internalMetadata, err := metadata.ToInternal()
	if err != nil {
		return sops.Tree{}, err
	}

	return sops.Tree{
		Branches: sops.TreeBranches{
			resultBranch,
		},
		Metadata: internalMetadata,
	}, nil
}

// LoadPlainFile returns the contents of a plaintext file loaded onto a
// sops runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
	var branches sops.TreeBranches
	var branch sops.TreeBranch

	for _, line := range bytes.Split(in, []byte("\n")) {
		if len(line) == 0 {
			continue
		}
		if line[0] == '#' {
			branch = append(branch, sops.TreeItem{
				Key:   sops.Comment{string(line[1:])},
				Value: nil,
			})
		} else {
			pos := bytes.Index(line, []byte("="))
			if pos == -1 {
				return nil, fmt.Errorf("invalid dotenv input line: %s", line)
			}
			branch = append(branch, sops.TreeItem{
				Key:   string(line[:pos]),
				Value: strings.Replace(string(line[pos+1:]), "\\n", "\n", -1),
			})
		}
	}

	branches = append(branches, branch)
	return branches, nil
}

// EmitEncryptedFile returns the encrypted file's bytes corresponding to a sops
// runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
	metadata := stores.MetadataFromInternal(in.Metadata)
	mdItems, err := metadataToMap(metadata)
	if err != nil {
		return nil, err
	}
	for key, value := range mdItems {
		if value == nil {
			continue
		}
		in.Branches[0] = append(in.Branches[0], sops.TreeItem{Key: SopsPrefix + key, Value: value})
	}
	return store.EmitPlainFile(in.Branches)
}

// EmitPlainFile returns the plaintext file's bytes corresponding to a sops
// runtime object
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
	buffer := bytes.Buffer{}
	for _, item := range in[0] {
		if isComplexValue(item.Value) {
			return nil, fmt.Errorf("cannot use complex value in dotenv file: %s", item.Value)
		}
		var line string
		if comment, ok := item.Key.(sops.Comment); ok {
			line = fmt.Sprintf("#%s\n", comment.Value)
		} else {
			value := strings.Replace(item.Value.(string), "\n", "\\n", -1)
			line = fmt.Sprintf("%s=%s\n", item.Key, value)
		}
		buffer.WriteString(line)
	}
	return buffer.Bytes(), nil
}

// EmitValue returns a single value as bytes
func (Store) EmitValue(v interface{}) ([]byte, error) {
	if s, ok := v.(string); ok {
		return []byte(s), nil
	}
	return nil, fmt.Errorf("the dotenv store only supports emitting strings, got %T", v)
}

// EmitExample returns the bytes corresponding to an example Flat Tree runtime object
func (store *Store) EmitExample() []byte {
	bytes, err := store.EmitPlainFile(stores.ExampleFlatTree.Branches)
	if err != nil {
		panic(err)
	}
	return bytes
}

func metadataToMap(md stores.Metadata) (map[string]interface{}, error) {
	var mdMap map[string]interface{}
	inrec, err := json.Marshal(md)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(inrec, &mdMap)
	if err != nil {
		return nil, err
	}
	flat := stores.Flatten(mdMap)
	for k, v := range flat {
		if s, ok := v.(string); ok {
			flat[k] = strings.Replace(s, "\n", "\\n", -1)
		}
	}
	return flat, nil
}

func mapToMetadata(m map[string]interface{}) (stores.Metadata, error) {
	for k, v := range m {
		if s, ok := v.(string); ok {
			m[k] = strings.Replace(s, "\\n", "\n", -1)
		}
	}
	m = stores.Unflatten(m)
	var md stores.Metadata
	inrec, err := json.Marshal(m)
	if err != nil {
		return md, err
	}
	err = json.Unmarshal(inrec, &md)
	return md, err
}

func isComplexValue(v interface{}) bool {
	switch v.(type) {
	case []interface{}:
		return true
	case sops.TreeBranch:
		return true
	}
	return false
}
0707010000009D000081A400000000000000000000000162794F930000048A000000000000000000000000000000000000002700000000sops-3.7.3/stores/dotenv/store_test.gopackage dotenv

import (
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"go.mozilla.org/sops/v3"
)

var PLAIN = []byte(strings.TrimLeft(`
VAR1=val1
VAR2=val2
#comment
VAR3_unencrypted=val3
VAR4=val4\nval4
`, "\n"))

var BRANCH = sops.TreeBranch{
	sops.TreeItem{
		Key:   "VAR1",
		Value: "val1",
	},
	sops.TreeItem{
		Key:   "VAR2",
		Value: "val2",
	},
	sops.TreeItem{
		Key:   sops.Comment{"comment"},
		Value: nil,
	},
	sops.TreeItem{
		Key:   "VAR3_unencrypted",
		Value: "val3",
	},
	sops.TreeItem{
		Key:   "VAR4",
		Value: "val4\nval4",
	},
}

func TestLoadPlainFile(t *testing.T) {
	branches, err := (&Store{}).LoadPlainFile(PLAIN)
	assert.Nil(t, err)
	assert.Equal(t, BRANCH, branches[0])
}
func TestEmitPlainFile(t *testing.T) {
	branches := sops.TreeBranches{
		BRANCH,
	}
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, PLAIN, bytes)
}

func TestEmitValueString(t *testing.T) {
	bytes, err := (&Store{}).EmitValue("hello")
	assert.Nil(t, err)
	assert.Equal(t, []byte("hello"), bytes)
}

func TestEmitValueNonstring(t *testing.T) {
	_, err := (&Store{}).EmitValue(BRANCH)
	assert.NotNil(t, err)
}
0707010000009E000081A400000000000000000000000162794F93000013C4000000000000000000000000000000000000001D00000000sops-3.7.3/stores/flatten.gopackage stores

import (
	"fmt"
	"strconv"
	"strings"
)

const mapSeparator = "__map_"
const listSeparator = "__list_"

// flattenAndMerge flattens the provided value and merges into the
// into map using prefix
func flattenAndMerge(into map[string]interface{}, prefix string, value interface{}) {
	flattenedValue := flattenValue(value)
	if flattenedValue, ok := flattenedValue.(map[string]interface{}); ok {
		for flatK, flatV := range flattenedValue {
			into[prefix+flatK] = flatV
		}
	} else {
		into[prefix] = value
	}
}

func flattenValue(value interface{}) interface{} {
	var output interface{}
	switch value := value.(type) {
	case map[string]interface{}:
		newMap := make(map[string]interface{})
		for k, v := range value {
			flattenAndMerge(newMap, mapSeparator+k, v)
		}
		output = newMap
	case []interface{}:
		newMap := make(map[string]interface{})
		for i, v := range value {
			flattenAndMerge(newMap, listSeparator+fmt.Sprintf("%d", i), v)
		}
		output = newMap
	default:
		output = value
	}
	return output
}

// Flatten flattens a map with potentially nested maps into a flat
// map. Only string keys are allowed on both the top-level map and
// child maps.
func Flatten(in map[string]interface{}) map[string]interface{} {
	newMap := make(map[string]interface{})
	for k, v := range in {
		if flat, ok := flattenValue(v).(map[string]interface{}); ok {
			for flatK, flatV := range flat {
				newMap[k+flatK] = flatV
			}
		} else {
			newMap[k] = v
		}
	}
	return newMap
}

type token interface{}

type mapToken struct {
	key string
}

type listToken struct {
	position int
}

// tokenize converts a path generated by Flatten to be used as a key
// in the flattened map, and converts it to a slice of tokens
func tokenize(path string) []token {
	const (
		StateNormal = 0
		StateMap    = iota
		StateList   = iota
	)
	var tokens []token
	state := StateNormal
	lastTokenEnd := 0
	i := 0
	finishPrevToken := func() {
		var t token
		switch state {
		case StateNormal:
			t = mapToken{path[lastTokenEnd:i]}
		case StateMap:
			t = mapToken{path[lastTokenEnd+len(mapSeparator) : i]}
		case StateList:
			pos, _ := strconv.Atoi(path[lastTokenEnd+len(listSeparator) : i])
			t = listToken{pos}
		}
		lastTokenEnd = i
		tokens = append(tokens, t)
	}
	for i < len(path) {
		if strings.HasPrefix(path[i:], mapSeparator) {
			finishPrevToken()
			state = StateMap
			i += len(mapSeparator)
		} else if strings.HasPrefix(path[i:], listSeparator) {
			finishPrevToken()
			state = StateList
			i += len(listSeparator)
		} else {
			i++
		}
	}
	finishPrevToken()
	return tokens
}

// unflatten takes the currentNode, currentToken, nextToken and value
// and populates currentNode such that currentToken can be considered
// processed. It inspects nextToken to decide what type to allocate
// and assign under currentNode.
func unflatten(currentNode interface{}, currentToken, nextToken token, value interface{}) interface{} {
	switch currentToken := currentToken.(type) {
	case mapToken:
		currentNode := currentNode.(map[string]interface{})
		switch nextToken := nextToken.(type) {
		case mapToken:
			if _, ok := currentNode[currentToken.key]; !ok {
				currentNode[currentToken.key] = make(map[string]interface{})
			}
			next := currentNode[currentToken.key].(map[string]interface{})
			return next
		case listToken:
			if _, ok := currentNode[currentToken.key]; !ok {
				currentNode[currentToken.key] = make([]interface{}, nextToken.position+1)
			}
			next := currentNode[currentToken.key].([]interface{})
			if nextToken.position >= len(next) {
				// Grow the slice and reassign it
				newNext := make([]interface{}, nextToken.position+1)
				copy(newNext, next)
				next = newNext
				currentNode[currentToken.key] = next
			}
			return next
		default:
			currentNode[currentToken.key] = value
		}
	case listToken:
		currentNode := currentNode.([]interface{})
		switch nextToken := nextToken.(type) {
		case mapToken:
			if currentNode[currentToken.position] == nil {
				currentNode[currentToken.position] = make(map[string]interface{})
			}
			next := currentNode[currentToken.position].(map[string]interface{})
			return next
		case listToken:
			if currentNode[currentToken.position] == nil {
				currentNode[currentToken.position] = make([]interface{}, nextToken.position+1)
			}
			next := currentNode[currentToken.position].([]interface{})
			if nextToken.position >= len(next) {
				// Grow the slice and reassign it
				newNext := make([]interface{}, nextToken.position+1)
				copy(newNext, next)
				next = newNext
				currentNode[currentToken.position] = next
			}
			return next
		default:
			currentNode[currentToken.position] = value
		}
	}
	return nil
}

// Unflatten unflattens a map flattened by Flatten
func Unflatten(in map[string]interface{}) map[string]interface{} {
	newMap := make(map[string]interface{})
	for k, v := range in {
		var current interface{} = newMap
		tokens := append(tokenize(k), nil)
		for i := 0; i < len(tokens)-1; i++ {
			current = unflatten(current, tokens[i], tokens[i+1], v)
		}
	}
	return newMap
}
0707010000009F000081A400000000000000000000000162794F9300000D66000000000000000000000000000000000000002200000000sops-3.7.3/stores/flatten_test.gopackage stores

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFlat(t *testing.T) {
	input := map[string]interface{}{
		"foo": "bar",
	}
	expected := map[string]interface{}{
		"foo": "bar",
	}
	flattened := Flatten(input)
	assert.Equal(t, expected, flattened)
	unflattened := Unflatten(flattened)
	assert.Equal(t, input, unflattened)
}

func TestMap(t *testing.T) {
	input := map[string]interface{}{
		"foo": map[string]interface{}{
			"bar": 0,
			"baz": 0,
		},
	}
	expected := map[string]interface{}{
		"foo" + mapSeparator + "bar": 0,
		"foo" + mapSeparator + "baz": 0,
	}
	flattened := Flatten(input)
	assert.Equal(t, expected, flattened)
	unflattened := Unflatten(flattened)
	assert.Equal(t, input, unflattened)
}

func TestFlattenMapMoreNesting(t *testing.T) {
	input := map[string]interface{}{
		"foo": map[string]interface{}{
			"bar": map[string]interface{}{
				"baz": 0,
			},
		},
	}
	expected := map[string]interface{}{
		"foo" + mapSeparator + "bar" + mapSeparator + "baz": 0,
	}
	flattened := Flatten(input)
	assert.Equal(t, expected, flattened)
	unflattened := Unflatten(flattened)
	assert.Equal(t, input, unflattened)
}

func TestFlattenList(t *testing.T) {
	input := map[string]interface{}{
		"foo": []interface{}{
			0,
		},
	}
	expected := map[string]interface{}{
		"foo" + listSeparator + "0": 0,
	}
	flattened := Flatten(input)
	assert.Equal(t, expected, flattened)
	unflattened := Unflatten(flattened)
	assert.Equal(t, input, unflattened)
}

func TestFlattenListWithMap(t *testing.T) {
	input := map[string]interface{}{
		"foo": []interface{}{
			map[string]interface{}{
				"bar": 0,
			},
		},
	}
	expected := map[string]interface{}{
		"foo" + listSeparator + "0" + mapSeparator + "bar": 0,
	}
	flattened := Flatten(input)
	assert.Equal(t, expected, flattened)
	unflattened := Unflatten(flattened)
	assert.Equal(t, input, unflattened)
}

func TestFlatten(t *testing.T) {
	input := map[string]interface{}{
		"foo": "bar",
		"baz": map[string]interface{}{
			"foo": 2,
			"bar": map[string]interface{}{
				"foo": 2,
			},
		},
		"qux": []interface{}{
			"hello", 1, 2,
		},
	}
	expected := map[string]interface{}{
		"foo":                        "bar",
		"baz" + mapSeparator + "foo": 2,
		"baz" + mapSeparator + "bar" + mapSeparator + "foo": 2,
		"qux" + listSeparator + "0":                         "hello",
		"qux" + listSeparator + "1":                         1,
		"qux" + listSeparator + "2":                         2,
	}
	flattened := Flatten(input)
	assert.Equal(t, expected, flattened)
	unflattened := Unflatten(flattened)
	assert.Equal(t, input, unflattened)
}

func TestTokenizeFlat(t *testing.T) {
	input := "bar"
	expected := []token{mapToken{"bar"}}
	tokenized := tokenize(input)
	assert.Equal(t, expected, tokenized)
}

func TestTokenizeMap(t *testing.T) {
	input := "bar" + mapSeparator + "foo"
	expected := []token{mapToken{"bar"}, mapToken{"foo"}}
	tokenized := tokenize(input)
	assert.Equal(t, expected, tokenized)
}

func TestTokenizeList(t *testing.T) {
	input := "bar" + listSeparator + "10"
	expected := []token{mapToken{"bar"}, listToken{10}}
	tokenized := tokenize(input)
	assert.Equal(t, expected, tokenized)
}

func TestTokenizeNested(t *testing.T) {
	input := "bar" + listSeparator + "10" + mapSeparator + "baz"
	expected := []token{mapToken{"bar"}, listToken{10}, mapToken{"baz"}}
	tokenized := tokenize(input)
	assert.Equal(t, expected, tokenized)
}
070701000000A0000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001600000000sops-3.7.3/stores/ini070701000000A1000081A400000000000000000000000162794F9300001C7E000000000000000000000000000000000000001F00000000sops-3.7.3/stores/ini/store.gopackage ini //import "go.mozilla.org/sops/v3/stores/ini"

import (
	"bytes"
	"encoding/json"
	"fmt"

	"strconv"
	"strings"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/stores"
	"gopkg.in/ini.v1"
)

// Store handles storage of ini data.
type Store struct {
}

func (store Store) encodeTree(branches sops.TreeBranches) ([]byte, error) {
	iniFile := ini.Empty()
	for _, branch := range branches {
		for _, item := range branch {
			if _, ok := item.Key.(sops.Comment); ok {
				continue
			}
			section, err := iniFile.NewSection(item.Key.(string))
			if err != nil {
				return nil, fmt.Errorf("Error encoding section %s: %s", item.Key, err)
			}
			itemTree, ok := item.Value.(sops.TreeBranch)
			if !ok {
				return nil, fmt.Errorf("Error encoding section: Section values should always be TreeBranches")
			}

			first := 0
			if len(itemTree) > 0 {
				if sectionComment, ok := itemTree[0].Key.(sops.Comment); ok {
					section.Comment = sectionComment.Value
					first = 1
				}
			}

			var lastItem *ini.Key
			for i := first; i < len(itemTree); i++ {
				keyVal := itemTree[i]
				if comment, ok := keyVal.Key.(sops.Comment); ok {
					if lastItem != nil {
						lastItem.Comment = comment.Value
					}
				} else {
					lastItem, err = section.NewKey(keyVal.Key.(string), store.valToString(keyVal.Value))
					if err != nil {
						return nil, fmt.Errorf("Error encoding key: %s", err)
					}
				}
			}
		}
	}
	var buffer bytes.Buffer
	iniFile.WriteTo(&buffer)
	return buffer.Bytes(), nil
}

func (store Store) stripCommentChar(comment string) string {
	if strings.HasPrefix(comment, ";") {
		comment = strings.TrimLeft(comment, "; ")
	} else if strings.HasPrefix(comment, "#") {
		comment = strings.TrimLeft(comment, "# ")
	}
	return comment
}

func (store Store) valToString(v interface{}) string {
	switch v := v.(type) {
	case fmt.Stringer:
		return v.String()
	case float64:
		return strconv.FormatFloat(v, 'f', 6, 64)
	case bool:
		return strconv.FormatBool(v)
	default:
		return fmt.Sprintf("%s", v)
	}
}

func (store Store) iniFromTreeBranches(branches sops.TreeBranches) ([]byte, error) {
	return store.encodeTree(branches)
}

func (store Store) treeBranchesFromIni(in []byte) (sops.TreeBranches, error) {
	iniFile, err := ini.Load(in)
	if err != nil {
		return nil, err
	}
	var branch sops.TreeBranch
	for _, section := range iniFile.Sections() {

		item, err := store.treeItemFromSection(section)
		if err != nil {
			return sops.TreeBranches{branch}, err
		}
		branch = append(branch, item)
	}
	return sops.TreeBranches{branch}, nil
}

func (store Store) treeItemFromSection(section *ini.Section) (sops.TreeItem, error) {
	var sectionItem sops.TreeItem
	sectionItem.Key = section.Name()
	var items sops.TreeBranch

	if section.Comment != "" {
		items = append(items, sops.TreeItem{
			Key: sops.Comment{
				Value: store.stripCommentChar(section.Comment),
			},
			Value: nil,
		})
	}

	for _, key := range section.Keys() {
		item := sops.TreeItem{Key: key.Name(), Value: key.Value()}
		items = append(items, item)
		if key.Comment != "" {
			items = append(items, sops.TreeItem{
				Key: sops.Comment{
					Value: store.stripCommentChar(key.Comment),
				},
				Value: nil,
			})
		}
	}
	sectionItem.Value = items
	return sectionItem, nil
}

// LoadEncryptedFile loads encrypted INI file's bytes onto a sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
	iniFileOuter, err := ini.Load(in)
	if err != nil {
		return sops.Tree{}, err
	}

	sopsSection, err := iniFileOuter.GetSection("sops")
	if err != nil {
		return sops.Tree{}, sops.MetadataNotFound
	}

	metadataHolder, err := store.iniSectionToMetadata(sopsSection)
	if err != nil {
		return sops.Tree{}, err
	}

	metadata, err := metadataHolder.ToInternal()
	if err != nil {
		return sops.Tree{}, err
	}
	// After that, we load the whole file into a map.
	branches, err := store.treeBranchesFromIni(in)
	if err != nil {
		return sops.Tree{}, fmt.Errorf("Could not unmarshal input data: %s", err)
	}
	// Discard metadata, as we already loaded it.
	for bi, branch := range branches {
		for s, sectionBranch := range branch {
			if sectionBranch.Key == "sops" {
				branch = append(branch[:s], branch[s+1:]...)
				branches[bi] = branch
			}
		}
	}
	return sops.Tree{
		Branches: branches,
		Metadata: metadata,
	}, nil
}

func (store *Store) iniSectionToMetadata(sopsSection *ini.Section) (stores.Metadata, error) {

	metadataHash := make(map[string]interface{})
	for k, v := range sopsSection.KeysHash() {
		metadataHash[k] = strings.Replace(v, "\\n", "\n", -1)
	}
	m := stores.Unflatten(metadataHash)
	var md stores.Metadata
	inrec, err := json.Marshal(m)
	if err != nil {
		return md, err
	}
	err = json.Unmarshal(inrec, &md)
	return md, err
}

// LoadPlainFile loads a plaintext INI file's bytes onto a sops.TreeBranches runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
	branches, err := store.treeBranchesFromIni(in)
	if err != nil {
		return branches, fmt.Errorf("Could not unmarshal input data: %s", err)
	}
	return branches, nil
}

// EmitEncryptedFile returns encrypted INI file bytes corresponding to a sops.Tree
// runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {

	metadata := stores.MetadataFromInternal(in.Metadata)
	newBranch, err := store.encodeMetadataToIniBranch(metadata)
	if err != nil {
		return nil, err
	}
	sectionItem := sops.TreeItem{Key: "sops", Value: newBranch}
	branch := sops.TreeBranch{sectionItem}

	in.Branches = append(in.Branches, branch)

	out, err := store.iniFromTreeBranches(in.Branches)
	if err != nil {
		return nil, fmt.Errorf("Error marshaling to ini: %s", err)
	}
	return out, nil
}

func (store *Store) encodeMetadataToIniBranch(md stores.Metadata) (sops.TreeBranch, error) {
	var mdMap map[string]interface{}
	inrec, err := json.Marshal(md)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(inrec, &mdMap)
	if err != nil {
		return nil, err
	}
	flat := stores.Flatten(mdMap)
	for k, v := range flat {
		if s, ok := v.(string); ok {
			flat[k] = strings.Replace(s, "\n", "\\n", -1)
		}
	}
	if err != nil {
		return nil, err
	}
	branch := sops.TreeBranch{}
	for key, value := range flat {
		if value == nil {
			continue
		}
		branch = append(branch, sops.TreeItem{Key: key, Value: value})
	}
	return branch, nil
}

// EmitPlainFile returns the plaintext INI file bytes corresponding to a sops.TreeBranches object
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
	out, err := store.iniFromTreeBranches(in)
	if err != nil {
		return nil, fmt.Errorf("Error marshaling to ini: %s", err)
	}
	return out, nil
}

func (store Store) encodeValue(v interface{}) ([]byte, error) {
	switch v := v.(type) {
	case sops.TreeBranches:
		return store.encodeTree(v)
	default:
		return json.Marshal(v)
	}
}

// EmitValue returns a single value encoded in a generic interface{} as bytes
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
	return store.encodeValue(v)
}

// EmitExample returns the plaintext INI file bytes corresponding to the SimpleTree example
func (store *Store) EmitExample() []byte {
	bytes, err := store.EmitPlainFile(stores.ExampleSimpleTree.Branches)
	if err != nil {
		panic(err)
	}
	return bytes
}
070701000000A2000081A400000000000000000000000162794F9300000ABD000000000000000000000000000000000000002400000000sops-3.7.3/stores/ini/store_test.gopackage ini

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"go.mozilla.org/sops/v3"
)

func TestDecodeIni(t *testing.T) {
	in := `
; last modified 1 April 2001 by John Doe
[owner]
name=John Doe
organization=Acme Widgets Inc.

[database]
; use IP address in case network name resolution is not working
server=192.0.2.62     
port=143
file="payroll.dat"
`
	expected := sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key:   "DEFAULT",
				Value: sops.TreeBranch(nil),
			},
			sops.TreeItem{
				Key: "owner",
				Value: sops.TreeBranch{
					sops.TreeItem{
						Key:   sops.Comment{Value: "last modified 1 April 2001 by John Doe"},
						Value: nil,
					},
					sops.TreeItem{
						Key:   "name",
						Value: "John Doe",
					},
					sops.TreeItem{
						Key:   "organization",
						Value: "Acme Widgets Inc.",
					},
				},
			},
			sops.TreeItem{
				Key: "database",
				Value: sops.TreeBranch{
					sops.TreeItem{
						Key:   "server",
						Value: "192.0.2.62",
					},
					sops.TreeItem{
						Key:   sops.Comment{Value: "use IP address in case network name resolution is not working"},
						Value: nil,
					},
					sops.TreeItem{
						Key:   "port",
						Value: "143",
					},
					sops.TreeItem{
						Key:   "file",
						Value: "payroll.dat",
					},
				},
			},
		},
	}
	branch, err := Store{}.treeBranchesFromIni([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestEncodeSimpleIni(t *testing.T) {
	branches := sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key: "DEFAULT",
				Value: sops.TreeBranch{
					sops.TreeItem{
						Key:   "foo",
						Value: "bar",
					},
					sops.TreeItem{
						Key:   "baz",
						Value: "3.0",
					},
					sops.TreeItem{
						Key:   "qux",
						Value: "false",
					},
				},
			},
		},
	}
	out, err := Store{}.iniFromTreeBranches(branches)
	assert.Nil(t, err)
	expected, _ := Store{}.treeBranchesFromIni(out)
	assert.Equal(t, expected, branches)
}

func TestEncodeIniWithEscaping(t *testing.T) {
	branches := sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key: "DEFAULT",
				Value: sops.TreeBranch{
					sops.TreeItem{
						Key:   "foo\\bar",
						Value: "value",
					},
					sops.TreeItem{
						Key:   "a_key_with\"quotes\"",
						Value: "4.0",
					},
					sops.TreeItem{
						Key:   "baz\\\\foo",
						Value: "2.0",
					},
				},
			},
		},
	}
	out, err := Store{}.iniFromTreeBranches(branches)
	assert.Nil(t, err)
	expected, _ := Store{}.treeBranchesFromIni(out)
	assert.Equal(t, expected, branches)
}

func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
	data := []byte(`hello=2`)
	store := Store{}
	_, err := store.LoadEncryptedFile(data)
	assert.Equal(t, sops.MetadataNotFound, err)
}
070701000000A3000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002500000000sops-3.7.3/stores/ini/test_resources070701000000A4000081A400000000000000000000000162794F9300000C03000000000000000000000000000000000000003200000000sops-3.7.3/stores/ini/test_resources/example.json{
    "example_key": "ENC[AES256_GCM,data:Xjen3YMQYCBfTU8VjA==,iv:1NUKversqeQiuTmAkZuyd6UY2AWiBS4owa4QHnwKOBM=,tag:ZO+Aln5DQ5Qm/QV2uBdahA==,type:str]", 
    "example_array": [
        "ENC[AES256_GCM,data:XJR0qvZuifm8j1TIQB8=,iv:QUkwy1dp0RU0PKEAw/VxVe1ZsQ972c8gPMJoVKgMfuw=,tag:LGdwC5nTua4rSe0dLbbA1Q==,type:str]", 
        "ENC[AES256_GCM,data:7bhghzi5GN/mMqh1vHU=,iv:X5vrd9X7ItIG/RVCn0T7RFhUrTb2YItr3i97EVk9nOY=,tag:vrM058PPOGmWOGPThOP5Ew==,type:str]"
    ], 
    "example_number": "ENC[AES256_GCM,data:w9etQN5r8iCz,iv:YF+1uUlMa4I1C7A0ELpVuMa1yK042uEMhp8y6HiCTDE=,tag:Dmh+AV9sh+ir0M1Txe+v2A==,type:float]", 
    "example_booleans": [
        "ENC[AES256_GCM,data:/hltsg==,iv:pbAtZ9i8rxFpaFlbwE1KOA+k/TVx5dm0tDtH94GCEVc=,tag:yz3d1pQu9zy5Ra9z2kDcoA==,type:bool]", 
        "ENC[AES256_GCM,data:HEei0+s=,iv:hgKT5eiYdHn5AqWdNji7vRKfabln90VbLmJqt7A480E=,tag:6pfezzTX/eULebF+32Z2+w==,type:bool]"
    ], 
    "sops": {
        "lastmodified": "2016-08-04T23:30:35Z", 
        "attention": "This section contains key material that should only be modified with extra care. See `sops -h`.", 
        "unencrypted_suffix": "_unencrypted", 
        "mac": "ENC[AES256_GCM,data:EK1LkVgW5CBEsGgGc7RkfZlzqWrP2fZe3kG7HbkJ5JFd591oUkbQ6I2uPImkcxf7HjiEHzKPPF5QvNg3+rUxgw6S8pQtumhDbFrfDi8GDS2VVvPR+0fnc2fR5PMGm36bOaQFDNSmgyJzKhMmNL+MtRhH+fMUnHhrnxuN3wfLr4w=,iv:xbNK6wRDVT4xhrP+vP2RIy+uNjZSSzqEJZPOdShn96o=,tag:vT4akR5X6qx5/wJ4dncxtg==,type:str]", 
        "version": "1.13", 
        "kms": [
            {
                "created_at": "2016-08-04T23:30:35Z", 
                "enc": "AQECAHgFEiO2dNygC3Rz8PhERCc8Sfhak4g81FUPqQJ0OBcAKgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDPKe5R67LMN3+xAkygIBEIA7F8noZukawV3VLQ/yH3Ep7Ptx8weLFUgVf/ZI6xqSMNvEHIr4+vf2xjBiAyrEF8u/n9nm9PWAdKHszFM=", 
                "arn": "arn:aws:kms:us-east-1:927034868273:key/e9fc75db-05e9-44c1-9c35-633922bac347"
            }
        ], 
        "pgp": [
            {
                "fp": "E5297818703249D0C60E19E6824612478D1A4CCD", 
                "created_at": "2016-08-04T23:30:35Z", 
                "enc": "-----BEGIN PGP MESSAGE-----\nVersion: GnuPG v1\n\nhQIMA2X8rvoeiASBARAAurTEVS82kqadk68f5ZlwR176S148WYTYxFp5oMC7cVD7\n42+Eo9RzaxbHeO5n7XKX0SDOUUeCucFl8fwuDUV1iDIx4/u5HgWXxDuvWoNe5cAL\n4LBS1Er2ZBVAdU0WHZ/8USuZLhSu7ucAHOvqNpHzPT6gkuBUYLQKOu0c+onWHqVO\n1DhfkTtvphotZ0ZBBR099t5N8ofD0W2+SM268A9/bB5yQcK9Ig/KxZBrfmMQm7zx\n9hLVQhcBmj0OQG37K4/SXGwjrQFarh6lm+FuZM0Q+GI+OARoAKdpZOnPEhXKE6un\nSEa69rh5FKVM/XRp2/QVZEakzRtq3gi9CtYL2sNr7KEnCvxt/v2pEc6evfIvxWTc\nT8MWdk48FkVjdsJ34sNiIM8msstnYorse8RZny9gcLE+A5lsRavo2QPL4GADyHF8\n7kwijSVDd08nByTBMMEPpMozUFhzF8QuVZPD+siuUvi+Bned9MmqgGMfvhS0Kf38\nMZFy5C6e38VGEX3IrWChvzbBm/M3fjs1fPVDShHfk1MYsCU9sXNQMQVewWE0s/em\nklycIL3hywd4N9z1MVW2hBpRrC247PtGQRKGoB9qbKtSgjTtgM7bo1vYekeY1tjr\nBGTHNFV+FBqFih16u/rGVzIaBsf5lLL/RtpaFZx1OWHMd9XjQpRrHhjOMpQ8tjvS\nXgGH59vv/9GNZ+Rix1QF+iMD84sfkyyguGKwg+TC3m275v+HIO1NvNdU6oS3O/Xq\nBCBV3yYAUwcrWUPWCuSUHJbuHKJEI1ymXUu8+RUElPyi/5JEhW+J1WlVPvnG1Xk=\n=Q/XA\n-----END PGP MESSAGE-----\n"
            }
        ]
    }
}070701000000A5000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001700000000sops-3.7.3/stores/json070701000000A6000081A400000000000000000000000162794F93000022AF000000000000000000000000000000000000002000000000sops-3.7.3/stores/json/store.gopackage json //import "go.mozilla.org/sops/v3/stores/json"

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/stores"
)

// Store handles storage of JSON data.
type Store struct {
}

// BinaryStore handles storage of binary data in a JSON envelope.
type BinaryStore struct {
	store Store
}

// LoadEncryptedFile loads an encrypted json file onto a sops.Tree object
func (store BinaryStore) LoadEncryptedFile(in []byte) (sops.Tree, error) {
	return store.store.LoadEncryptedFile(in)
}

// LoadPlainFile loads a plaintext json file onto a sops.Tree encapsulated
// within a sops.TreeBranches object
func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
	return sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key:   "data",
				Value: string(in),
			},
		},
	}, nil
}

// EmitEncryptedFile produces an encrypted json file's bytes from its corresponding sops.Tree object
func (store BinaryStore) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
	return store.store.EmitEncryptedFile(in)
}

// EmitPlainFile produces plaintext json file's bytes from its corresponding sops.TreeBranches object
func (store BinaryStore) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
	// JSON stores a single object per file
	for _, item := range in[0] {
		if item.Key == "data" {
			return []byte(item.Value.(string)), nil
		}
	}
	return nil, fmt.Errorf("No binary data found in tree")
}

// EmitValue extracts a value from a generic interface{} object representing a structured set
// of binary files
func (store BinaryStore) EmitValue(v interface{}) ([]byte, error) {
	return nil, fmt.Errorf("Binary files are not structured and extracting a single value is not possible")
}

// EmitExample returns the example's plaintext json file bytes
func (store BinaryStore) EmitExample() []byte {
	return []byte("Welcome to SOPS! Edit this file as you please!")
}

func (store Store) sliceFromJSONDecoder(dec *json.Decoder) ([]interface{}, error) {
	var slice []interface{}
	for {
		t, err := dec.Token()
		if err != nil {
			return slice, err
		}
		if delim, ok := t.(json.Delim); ok && delim.String() == "]" {
			return slice, nil
		} else if ok && delim.String() == "{" {
			item, err := store.treeBranchFromJSONDecoder(dec)
			if err != nil {
				return slice, err
			}
			slice = append(slice, item)
		} else if ok && delim.String() == "[" {
			item, err := store.sliceFromJSONDecoder(dec)
			if err != nil {
				return slice, err
			}
			slice = append(slice, item)
		} else {
			slice = append(slice, t)
		}
	}
}

var errEndOfObject = fmt.Errorf("End of object")

func (store Store) treeItemFromJSONDecoder(dec *json.Decoder) (sops.TreeItem, error) {
	var item sops.TreeItem
	key, err := dec.Token()
	if err != nil && err != io.EOF {
		return item, err
	}
	if k, ok := key.(string); ok {
		item.Key = k
	} else if d, ok := key.(json.Delim); ok && d.String() == "}" {
		return item, errEndOfObject
	} else {
		return item, fmt.Errorf("Expected JSON object key, got %s of type %T instead", key, key)
	}
	value, err := dec.Token()
	if err != nil {
		return item, err
	}
	if delim, ok := value.(json.Delim); ok {
		if delim.String() == "[" {
			v, err := store.sliceFromJSONDecoder(dec)
			if err != nil {
				return item, err
			}
			item.Value = v
		}
		if delim.String() == "{" {
			v, err := store.treeBranchFromJSONDecoder(dec)
			if err != nil {
				return item, err
			}
			item.Value = v
		}
	} else {
		item.Value = value
	}
	return item, nil

}

func (store Store) treeBranchFromJSONDecoder(dec *json.Decoder) (sops.TreeBranch, error) {
	var tree sops.TreeBranch
	for {
		item, err := store.treeItemFromJSONDecoder(dec)
		if err == io.EOF {
			return tree, nil
		}
		if err == errEndOfObject {
			return tree, nil
		}
		if err != nil {
			return tree, err
		}
		tree = append(tree, item)
	}
}

func (store Store) encodeValue(v interface{}) ([]byte, error) {
	switch v := v.(type) {
	case sops.TreeBranch:
		return store.encodeTree(v)
	case []interface{}:
		return store.encodeArray(v)
	default:
		return json.Marshal(v)
	}
}

func (store Store) encodeArray(array []interface{}) ([]byte, error) {
	out := "["
	for i, item := range array {
		if _, ok := item.(sops.Comment); ok {
			continue
		}
		v, err := store.encodeValue(item)
		if err != nil {
			return nil, err
		}
		out += string(v)
		if i != len(array)-1 {
			out += ","
		}
	}
	out += "]"
	return []byte(out), nil
}

func (store Store) encodeTree(tree sops.TreeBranch) ([]byte, error) {
	out := "{"
	for i, item := range tree {
		if _, ok := item.Key.(sops.Comment); ok {
			continue
		}
		v, err := store.encodeValue(item.Value)
		if err != nil {
			return nil, fmt.Errorf("Error encoding value %s: %s", v, err)
		}
		k, err := json.Marshal(item.Key.(string))
		if err != nil {
			return nil, fmt.Errorf("Error encoding key %s: %s", k, err)
		}
		out += string(k) + `: ` + string(v)
		if i != len(tree)-1 {
			out += ","
		}
	}
	return []byte(out + "}"), nil
}

func (store Store) jsonFromTreeBranch(branch sops.TreeBranch) ([]byte, error) {
	out, err := store.encodeTree(branch)
	if err != nil {
		return nil, err
	}
	return store.reindentJSON(out)
}

func (store Store) treeBranchFromJSON(in []byte) (sops.TreeBranch, error) {
	dec := json.NewDecoder(bytes.NewReader(in))
	dec.Token()
	return store.treeBranchFromJSONDecoder(dec)
}

func (store Store) reindentJSON(in []byte) ([]byte, error) {
	var out bytes.Buffer
	err := json.Indent(&out, in, "", "\t")
	return out.Bytes(), err
}

// LoadEncryptedFile loads an encrypted secrets file onto a sops.Tree object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
	// Because we don't know what fields the input file will have, we have to
	// load the file in two steps.
	// First, we load the file's metadata, the structure of which is known.
	metadataHolder := stores.SopsFile{}
	err := json.Unmarshal(in, &metadataHolder)
	if err != nil {
		if err, ok := err.(*json.UnmarshalTypeError); ok {
			if err.Value == "number" && err.Struct == "Metadata" && err.Field == "version" {
				return sops.Tree{},
					fmt.Errorf("SOPS versions higher than 2.0.10 can not automatically decrypt JSON files " +
						"created with SOPS 1.x. In order to be able to decrypt this file, you can either edit it " +
						"manually and make sure the JSON value under `sops -> version` is a string and not a " +
						"number, or you can rotate the file's key with any version of SOPS between 2.0 and 2.0.10 " +
						"using `sops -r your_file.json`")
			}
		}
		return sops.Tree{}, fmt.Errorf("Error unmarshalling input json: %s", err)
	}
	if metadataHolder.Metadata == nil {
		return sops.Tree{}, sops.MetadataNotFound
	}
	metadata, err := metadataHolder.Metadata.ToInternal()
	if err != nil {
		return sops.Tree{}, err
	}
	// After that, we load the whole file into a map.
	branch, err := store.treeBranchFromJSON(in)
	if err != nil {
		return sops.Tree{}, fmt.Errorf("Could not unmarshal input data: %s", err)
	}
	// Discard metadata, as we already loaded it.
	for i, item := range branch {
		if item.Key == "sops" {
			branch = append(branch[:i], branch[i+1:]...)
		}
	}
	return sops.Tree{
		Branches: sops.TreeBranches{
			branch,
		},
		Metadata: metadata,
	}, nil
}

// LoadPlainFile loads plaintext json file bytes onto a sops.TreeBranches object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
	branch, err := store.treeBranchFromJSON(in)
	if err != nil {
		return nil, fmt.Errorf("Could not unmarshal input data: %s", err)
	}
	return sops.TreeBranches{
		branch,
	}, nil
}

// EmitEncryptedFile returns the encrypted bytes of the json file corresponding to a
// sops.Tree runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
	tree := append(in.Branches[0], sops.TreeItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)})
	out, err := store.jsonFromTreeBranch(tree)
	if err != nil {
		return nil, fmt.Errorf("Error marshaling to json: %s", err)
	}
	return out, nil
}

// EmitPlainFile returns the plaintext bytes of the json file corresponding to a
// sops.TreeBranches runtime object
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
	out, err := store.jsonFromTreeBranch(in[0])
	if err != nil {
		return nil, fmt.Errorf("Error marshaling to json: %s", err)
	}
	return out, nil
}

// EmitValue returns bytes corresponding to a single encoded value
// in a generic interface{} object
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
	s, err := store.encodeValue(v)
	if err != nil {
		return nil, err
	}
	return store.reindentJSON(s)
}

// EmitExample returns the bytes corresponding to an example complex tree
func (store *Store) EmitExample() []byte {
	bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
	if err != nil {
		panic(err)
	}
	return bytes
}
070701000000A7000081A400000000000000000000000162794F9300001B0E000000000000000000000000000000000000002500000000sops-3.7.3/stores/json/store_test.gopackage json

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"go.mozilla.org/sops/v3"
)

func TestDecodeJSON(t *testing.T) {
	in := `
{
   "glossary":{
      "title":"example glossary",
      "GlossDiv":{
         "title":"S",
         "GlossList":{
            "GlossEntry":{
               "ID":"SGML",
               "SortAs":"SGML",
               "GlossTerm":"Standard Generalized Markup Language",
               "Acronym":"SGML",
               "Abbrev":"ISO 8879:1986",
               "GlossDef":{
                  "para":"A meta-markup language, used to create markup languages such as DocBook.",
                  "GlossSeeAlso":[
                     "GML",
                     "XML"
                  ]
               },
               "GlossSee":"markup"
            }
         }
      }
   }
}`
	expected := sops.TreeBranch{
		sops.TreeItem{
			Key: "glossary",
			Value: sops.TreeBranch{
				sops.TreeItem{
					Key:   "title",
					Value: "example glossary",
				},
				sops.TreeItem{
					Key: "GlossDiv",
					Value: sops.TreeBranch{
						sops.TreeItem{
							Key:   "title",
							Value: "S",
						},
						sops.TreeItem{
							Key: "GlossList",
							Value: sops.TreeBranch{
								sops.TreeItem{
									Key: "GlossEntry",
									Value: sops.TreeBranch{
										sops.TreeItem{
											Key:   "ID",
											Value: "SGML",
										},
										sops.TreeItem{
											Key:   "SortAs",
											Value: "SGML",
										},
										sops.TreeItem{
											Key:   "GlossTerm",
											Value: "Standard Generalized Markup Language",
										},
										sops.TreeItem{
											Key:   "Acronym",
											Value: "SGML",
										},
										sops.TreeItem{
											Key:   "Abbrev",
											Value: "ISO 8879:1986",
										},
										sops.TreeItem{
											Key: "GlossDef",
											Value: sops.TreeBranch{
												sops.TreeItem{
													Key:   "para",
													Value: "A meta-markup language, used to create markup languages such as DocBook.",
												},
												sops.TreeItem{
													Key: "GlossSeeAlso",
													Value: []interface{}{
														"GML",
														"XML",
													},
												},
											},
										},
										sops.TreeItem{
											Key:   "GlossSee",
											Value: "markup",
										},
									},
								},
							},
						},
					},
				},
			},
		},
	}
	branch, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestDecodeSimpleJSONObject(t *testing.T) {
	in := `{"foo": "bar", "baz": 2}`
	expected := sops.TreeBranch{
		sops.TreeItem{
			Key:   "foo",
			Value: "bar",
		},
		sops.TreeItem{
			Key:   "baz",
			Value: 2.0,
		},
	}
	branch, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestDecodeNumber(t *testing.T) {
	in := `42`
	_, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.NotNil(t, err)
}

func TestDecodeNestedJSONObject(t *testing.T) {
	in := `{"foo": {"foo": "bar"}}`
	expected := sops.TreeBranch{
		sops.TreeItem{
			Key: "foo",
			Value: sops.TreeBranch{
				sops.TreeItem{
					Key:   "foo",
					Value: "bar",
				},
			},
		},
	}
	branch, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestDecodeJSONWithArray(t *testing.T) {
	in := `{"foo": {"foo": [1, 2, 3]}, "bar": "baz"}`
	expected := sops.TreeBranch{
		sops.TreeItem{
			Key: "foo",
			Value: sops.TreeBranch{
				sops.TreeItem{
					Key:   "foo",
					Value: []interface{}{1.0, 2.0, 3.0},
				},
			},
		},
		sops.TreeItem{
			Key:   "bar",
			Value: "baz",
		},
	}
	branch, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestDecodeJSONArrayOfObjects(t *testing.T) {
	in := `{"foo": [{"bar": "foo"}, {"foo": "bar"}]}`
	expected := sops.TreeBranch{
		sops.TreeItem{
			Key: "foo",
			Value: []interface{}{
				sops.TreeBranch{
					sops.TreeItem{
						Key:   "bar",
						Value: "foo",
					},
				},
				sops.TreeBranch{
					sops.TreeItem{
						Key:   "foo",
						Value: "bar",
					},
				},
			},
		},
	}
	branch, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestDecodeJSONArrayOfArrays(t *testing.T) {
	in := `{"foo": [[["foo", {"bar": "foo"}]]]}`
	expected := sops.TreeBranch{
		sops.TreeItem{
			Key: "foo",
			Value: []interface{}{
				[]interface{}{
					[]interface{}{
						"foo",
						sops.TreeBranch{
							sops.TreeItem{
								Key:   "bar",
								Value: "foo",
							},
						},
					},
				},
			},
		},
	}
	branch, err := Store{}.treeBranchFromJSON([]byte(in))
	assert.Nil(t, err)
	assert.Equal(t, expected, branch)
}

func TestEncodeSimpleJSON(t *testing.T) {
	branch := sops.TreeBranch{
		sops.TreeItem{
			Key:   "foo",
			Value: "bar",
		},
		sops.TreeItem{
			Key:   "foo",
			Value: 3.0,
		},
		sops.TreeItem{
			Key:   "bar",
			Value: false,
		},
	}
	out, err := Store{}.jsonFromTreeBranch(branch)
	assert.Nil(t, err)
	expected, _ := Store{}.treeBranchFromJSON(out)
	assert.Equal(t, expected, branch)
}

func TestEncodeJSONWithEscaping(t *testing.T) {
	branch := sops.TreeBranch{
		sops.TreeItem{
			Key:   "foo\\bar",
			Value: "value",
		},
		sops.TreeItem{
			Key:   "a_key_with\"quotes\"",
			Value: 4.0,
		},
		sops.TreeItem{
			Key:   "baz\\\\foo",
			Value: 2.0,
		},
	}
	out, err := Store{}.jsonFromTreeBranch(branch)
	assert.Nil(t, err)
	expected, _ := Store{}.treeBranchFromJSON(out)
	assert.Equal(t, expected, branch)
}

func TestEncodeJSONArrayOfObjects(t *testing.T) {
	tree := sops.Tree{
		Branches: sops.TreeBranches{
			sops.TreeBranch{
				sops.TreeItem{
					Key: "foo",
					Value: []interface{}{
						sops.TreeBranch{
							sops.TreeItem{
								Key:   "foo",
								Value: 3,
							},
							sops.TreeItem{
								Key:   "bar",
								Value: false,
							},
						},
						2,
					},
				},
			},
		},
	}
	expected := `{
	"foo": [
		{
			"foo": 3,
			"bar": false
		},
		2
	]
}`
	store := Store{}
	out, err := store.EmitPlainFile(tree.Branches)
	assert.Nil(t, err)
	assert.Equal(t, expected, string(out))
}

func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
	data := []byte(`{"hello": 2}`)
	store := Store{}
	_, err := store.LoadEncryptedFile(data)
	assert.Equal(t, sops.MetadataNotFound, err)
}

func TestLoadJSONFormattedBinaryFile(t *testing.T) {
	// This is JSON data, but we want SOPS to interpret it as binary,
	// e.g. because the --input-type binary flag was provided.
	data := []byte(`{"hello": 2}`)
	store := BinaryStore{}
	branches, err := store.LoadPlainFile(data)
	assert.Nil(t, err)
	assert.Equal(t, "data", branches[0][0].Key)
}

func TestEmitValueString(t *testing.T) {
	bytes, err := (&Store{}).EmitValue("hello")
	assert.Nil(t, err)
	assert.Equal(t, []byte("\"hello\""), bytes)
}
070701000000A8000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002600000000sops-3.7.3/stores/json/test_resources070701000000A9000081A400000000000000000000000162794F9300000C03000000000000000000000000000000000000003300000000sops-3.7.3/stores/json/test_resources/example.json{
    "example_key": "ENC[AES256_GCM,data:Xjen3YMQYCBfTU8VjA==,iv:1NUKversqeQiuTmAkZuyd6UY2AWiBS4owa4QHnwKOBM=,tag:ZO+Aln5DQ5Qm/QV2uBdahA==,type:str]", 
    "example_array": [
        "ENC[AES256_GCM,data:XJR0qvZuifm8j1TIQB8=,iv:QUkwy1dp0RU0PKEAw/VxVe1ZsQ972c8gPMJoVKgMfuw=,tag:LGdwC5nTua4rSe0dLbbA1Q==,type:str]", 
        "ENC[AES256_GCM,data:7bhghzi5GN/mMqh1vHU=,iv:X5vrd9X7ItIG/RVCn0T7RFhUrTb2YItr3i97EVk9nOY=,tag:vrM058PPOGmWOGPThOP5Ew==,type:str]"
    ], 
    "example_number": "ENC[AES256_GCM,data:w9etQN5r8iCz,iv:YF+1uUlMa4I1C7A0ELpVuMa1yK042uEMhp8y6HiCTDE=,tag:Dmh+AV9sh+ir0M1Txe+v2A==,type:float]", 
    "example_booleans": [
        "ENC[AES256_GCM,data:/hltsg==,iv:pbAtZ9i8rxFpaFlbwE1KOA+k/TVx5dm0tDtH94GCEVc=,tag:yz3d1pQu9zy5Ra9z2kDcoA==,type:bool]", 
        "ENC[AES256_GCM,data:HEei0+s=,iv:hgKT5eiYdHn5AqWdNji7vRKfabln90VbLmJqt7A480E=,tag:6pfezzTX/eULebF+32Z2+w==,type:bool]"
    ], 
    "sops": {
        "lastmodified": "2016-08-04T23:30:35Z", 
        "attention": "This section contains key material that should only be modified with extra care. See `sops -h`.", 
        "unencrypted_suffix": "_unencrypted", 
        "mac": "ENC[AES256_GCM,data:EK1LkVgW5CBEsGgGc7RkfZlzqWrP2fZe3kG7HbkJ5JFd591oUkbQ6I2uPImkcxf7HjiEHzKPPF5QvNg3+rUxgw6S8pQtumhDbFrfDi8GDS2VVvPR+0fnc2fR5PMGm36bOaQFDNSmgyJzKhMmNL+MtRhH+fMUnHhrnxuN3wfLr4w=,iv:xbNK6wRDVT4xhrP+vP2RIy+uNjZSSzqEJZPOdShn96o=,tag:vT4akR5X6qx5/wJ4dncxtg==,type:str]", 
        "version": "1.13", 
        "kms": [
            {
                "created_at": "2016-08-04T23:30:35Z", 
                "enc": "AQECAHgFEiO2dNygC3Rz8PhERCc8Sfhak4g81FUPqQJ0OBcAKgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDPKe5R67LMN3+xAkygIBEIA7F8noZukawV3VLQ/yH3Ep7Ptx8weLFUgVf/ZI6xqSMNvEHIr4+vf2xjBiAyrEF8u/n9nm9PWAdKHszFM=", 
                "arn": "arn:aws:kms:us-east-1:927034868273:key/e9fc75db-05e9-44c1-9c35-633922bac347"
            }
        ], 
        "pgp": [
            {
                "fp": "E5297818703249D0C60E19E6824612478D1A4CCD", 
                "created_at": "2016-08-04T23:30:35Z", 
                "enc": "-----BEGIN PGP MESSAGE-----\nVersion: GnuPG v1\n\nhQIMA2X8rvoeiASBARAAurTEVS82kqadk68f5ZlwR176S148WYTYxFp5oMC7cVD7\n42+Eo9RzaxbHeO5n7XKX0SDOUUeCucFl8fwuDUV1iDIx4/u5HgWXxDuvWoNe5cAL\n4LBS1Er2ZBVAdU0WHZ/8USuZLhSu7ucAHOvqNpHzPT6gkuBUYLQKOu0c+onWHqVO\n1DhfkTtvphotZ0ZBBR099t5N8ofD0W2+SM268A9/bB5yQcK9Ig/KxZBrfmMQm7zx\n9hLVQhcBmj0OQG37K4/SXGwjrQFarh6lm+FuZM0Q+GI+OARoAKdpZOnPEhXKE6un\nSEa69rh5FKVM/XRp2/QVZEakzRtq3gi9CtYL2sNr7KEnCvxt/v2pEc6evfIvxWTc\nT8MWdk48FkVjdsJ34sNiIM8msstnYorse8RZny9gcLE+A5lsRavo2QPL4GADyHF8\n7kwijSVDd08nByTBMMEPpMozUFhzF8QuVZPD+siuUvi+Bned9MmqgGMfvhS0Kf38\nMZFy5C6e38VGEX3IrWChvzbBm/M3fjs1fPVDShHfk1MYsCU9sXNQMQVewWE0s/em\nklycIL3hywd4N9z1MVW2hBpRrC247PtGQRKGoB9qbKtSgjTtgM7bo1vYekeY1tjr\nBGTHNFV+FBqFih16u/rGVzIaBsf5lLL/RtpaFZx1OWHMd9XjQpRrHhjOMpQ8tjvS\nXgGH59vv/9GNZ+Rix1QF+iMD84sfkyyguGKwg+TC3m275v+HIO1NvNdU6oS3O/Xq\nBCBV3yYAUwcrWUPWCuSUHJbuHKJEI1ymXUu8+RUElPyi/5JEhW+J1WlVPvnG1Xk=\n=Q/XA\n-----END PGP MESSAGE-----\n"
            }
        ]
    }
}070701000000AA000081A400000000000000000000000162794F9300003E2A000000000000000000000000000000000000001C00000000sops-3.7.3/stores/stores.go/*
Package stores acts as a layer between the internal representation of encrypted files and the encrypted files
themselves.

Subpackages implement serialization and deserialization to multiple formats.

This package defines the structure SOPS files should have and conversions to and from the internal representation. Part
of the purpose of this package is to make it easy to change the SOPS file format while remaining backwards-compatible.
*/
package stores

import (
	"time"

	"fmt"

	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/age"
	"go.mozilla.org/sops/v3/azkv"
	"go.mozilla.org/sops/v3/gcpkms"
	"go.mozilla.org/sops/v3/hcvault"
	"go.mozilla.org/sops/v3/kms"
	"go.mozilla.org/sops/v3/pgp"
)

// SopsFile is a struct used by the stores as a helper to unmarshal the SOPS metadata
type SopsFile struct {
	// Metadata is a pointer so we can easily tell when the field is not present
	// in the SOPS file by checking for nil. This way we can show the user a
	// helpful error message indicating that the metadata wasn't found, instead
	// of showing a cryptic parsing error
	Metadata *Metadata `yaml:"sops" json:"sops" ini:"sops"`
}

// Metadata is stored in SOPS encrypted files, and it contains the information necessary to decrypt the file.
// This struct is just used for serialization, and SOPS uses another struct internally, sops.Metadata. It exists
// in order to allow the binary format to stay backwards compatible over time, but at the same time allow the internal
// representation SOPS uses to change over time.
type Metadata struct {
	ShamirThreshold           int         `yaml:"shamir_threshold,omitempty" json:"shamir_threshold,omitempty"`
	KeyGroups                 []keygroup  `yaml:"key_groups,omitempty" json:"key_groups,omitempty"`
	KMSKeys                   []kmskey    `yaml:"kms" json:"kms"`
	GCPKMSKeys                []gcpkmskey `yaml:"gcp_kms" json:"gcp_kms"`
	AzureKeyVaultKeys         []azkvkey   `yaml:"azure_kv" json:"azure_kv"`
	VaultKeys                 []vaultkey  `yaml:"hc_vault" json:"hc_vault"`
	AgeKeys                   []agekey    `yaml:"age" json:"age"`
	LastModified              string      `yaml:"lastmodified" json:"lastmodified"`
	MessageAuthenticationCode string      `yaml:"mac" json:"mac"`
	PGPKeys                   []pgpkey    `yaml:"pgp" json:"pgp"`
	UnencryptedSuffix         string      `yaml:"unencrypted_suffix,omitempty" json:"unencrypted_suffix,omitempty"`
	EncryptedSuffix           string      `yaml:"encrypted_suffix,omitempty" json:"encrypted_suffix,omitempty"`
	UnencryptedRegex          string      `yaml:"unencrypted_regex,omitempty" json:"unencrypted_regex,omitempty"`
	EncryptedRegex            string      `yaml:"encrypted_regex,omitempty" json:"encrypted_regex,omitempty"`
	Version                   string      `yaml:"version" json:"version"`
}

type keygroup struct {
	PGPKeys           []pgpkey    `yaml:"pgp,omitempty" json:"pgp,omitempty"`
	KMSKeys           []kmskey    `yaml:"kms,omitempty" json:"kms,omitempty"`
	GCPKMSKeys        []gcpkmskey `yaml:"gcp_kms,omitempty" json:"gcp_kms,omitempty"`
	AzureKeyVaultKeys []azkvkey   `yaml:"azure_kv,omitempty" json:"azure_kv,omitempty"`
	VaultKeys         []vaultkey  `yaml:"hc_vault" json:"hc_vault"`
	AgeKeys           []agekey    `yaml:"age" json:"age"`
}

type pgpkey struct {
	CreatedAt        string `yaml:"created_at" json:"created_at"`
	EncryptedDataKey string `yaml:"enc" json:"enc"`
	Fingerprint      string `yaml:"fp" json:"fp"`
}

type kmskey struct {
	Arn              string             `yaml:"arn" json:"arn"`
	Role             string             `yaml:"role,omitempty" json:"role,omitempty"`
	Context          map[string]*string `yaml:"context,omitempty" json:"context,omitempty"`
	CreatedAt        string             `yaml:"created_at" json:"created_at"`
	EncryptedDataKey string             `yaml:"enc" json:"enc"`
	AwsProfile       string             `yaml:"aws_profile" json:"aws_profile"`
}

type gcpkmskey struct {
	ResourceID       string `yaml:"resource_id" json:"resource_id"`
	CreatedAt        string `yaml:"created_at" json:"created_at"`
	EncryptedDataKey string `yaml:"enc" json:"enc"`
}

type vaultkey struct {
	VaultAddress     string `yaml:"vault_address" json:"vault_address"`
	EnginePath       string `yaml:"engine_path" json:"engine_path"`
	KeyName          string `yaml:"key_name" json:"key_name"`
	CreatedAt        string `yaml:"created_at" json:"created_at"`
	EncryptedDataKey string `yaml:"enc" json:"enc"`
}

type azkvkey struct {
	VaultURL         string `yaml:"vault_url" json:"vault_url"`
	Name             string `yaml:"name" json:"name"`
	Version          string `yaml:"version" json:"version"`
	CreatedAt        string `yaml:"created_at" json:"created_at"`
	EncryptedDataKey string `yaml:"enc" json:"enc"`
}

type agekey struct {
	Recipient        string `yaml:"recipient" json:"recipient"`
	EncryptedDataKey string `yaml:"enc" json:"enc"`
}

// MetadataFromInternal converts an internal SOPS metadata representation to a representation appropriate for storage
func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata {
	var m Metadata
	m.LastModified = sopsMetadata.LastModified.Format(time.RFC3339)
	m.UnencryptedSuffix = sopsMetadata.UnencryptedSuffix
	m.EncryptedSuffix = sopsMetadata.EncryptedSuffix
	m.UnencryptedRegex = sopsMetadata.UnencryptedRegex
	m.EncryptedRegex = sopsMetadata.EncryptedRegex
	m.MessageAuthenticationCode = sopsMetadata.MessageAuthenticationCode
	m.Version = sopsMetadata.Version
	m.ShamirThreshold = sopsMetadata.ShamirThreshold
	if len(sopsMetadata.KeyGroups) == 1 {
		group := sopsMetadata.KeyGroups[0]
		m.PGPKeys = pgpKeysFromGroup(group)
		m.KMSKeys = kmsKeysFromGroup(group)
		m.GCPKMSKeys = gcpkmsKeysFromGroup(group)
		m.VaultKeys = vaultKeysFromGroup(group)
		m.AzureKeyVaultKeys = azkvKeysFromGroup(group)
		m.AgeKeys = ageKeysFromGroup(group)
	} else {
		for _, group := range sopsMetadata.KeyGroups {
			m.KeyGroups = append(m.KeyGroups, keygroup{
				KMSKeys:           kmsKeysFromGroup(group),
				PGPKeys:           pgpKeysFromGroup(group),
				GCPKMSKeys:        gcpkmsKeysFromGroup(group),
				VaultKeys:         vaultKeysFromGroup(group),
				AzureKeyVaultKeys: azkvKeysFromGroup(group),
				AgeKeys:           ageKeysFromGroup(group),
			})
		}
	}
	return m
}

func pgpKeysFromGroup(group sops.KeyGroup) (keys []pgpkey) {
	for _, key := range group {
		switch key := key.(type) {
		case *pgp.MasterKey:
			keys = append(keys, pgpkey{
				Fingerprint:      key.Fingerprint,
				EncryptedDataKey: key.EncryptedKey,
				CreatedAt:        key.CreationDate.Format(time.RFC3339),
			})
		}
	}
	return
}

func kmsKeysFromGroup(group sops.KeyGroup) (keys []kmskey) {
	for _, key := range group {
		switch key := key.(type) {
		case *kms.MasterKey:
			keys = append(keys, kmskey{
				Arn:              key.Arn,
				CreatedAt:        key.CreationDate.Format(time.RFC3339),
				EncryptedDataKey: key.EncryptedKey,
				Context:          key.EncryptionContext,
				Role:             key.Role,
				AwsProfile:       key.AwsProfile,
			})
		}
	}
	return
}

func gcpkmsKeysFromGroup(group sops.KeyGroup) (keys []gcpkmskey) {
	for _, key := range group {
		switch key := key.(type) {
		case *gcpkms.MasterKey:
			keys = append(keys, gcpkmskey{
				ResourceID:       key.ResourceID,
				CreatedAt:        key.CreationDate.Format(time.RFC3339),
				EncryptedDataKey: key.EncryptedKey,
			})
		}
	}
	return
}

func vaultKeysFromGroup(group sops.KeyGroup) (keys []vaultkey) {
	for _, key := range group {
		switch key := key.(type) {
		case *hcvault.MasterKey:
			keys = append(keys, vaultkey{
				VaultAddress:     key.VaultAddress,
				EnginePath:       key.EnginePath,
				KeyName:          key.KeyName,
				CreatedAt:        key.CreationDate.Format(time.RFC3339),
				EncryptedDataKey: key.EncryptedKey,
			})
		}
	}
	return
}

func azkvKeysFromGroup(group sops.KeyGroup) (keys []azkvkey) {
	for _, key := range group {
		switch key := key.(type) {
		case *azkv.MasterKey:
			keys = append(keys, azkvkey{
				VaultURL:         key.VaultURL,
				Name:             key.Name,
				Version:          key.Version,
				CreatedAt:        key.CreationDate.Format(time.RFC3339),
				EncryptedDataKey: key.EncryptedKey,
			})
		}
	}
	return
}

func ageKeysFromGroup(group sops.KeyGroup) (keys []agekey) {
	for _, key := range group {
		switch key := key.(type) {
		case *age.MasterKey:
			keys = append(keys, agekey{
				Recipient:        key.Recipient,
				EncryptedDataKey: key.EncryptedKey,
			})
		}
	}
	return
}

// ToInternal converts a storage-appropriate Metadata struct to a SOPS internal representation
func (m *Metadata) ToInternal() (sops.Metadata, error) {
	lastModified, err := time.Parse(time.RFC3339, m.LastModified)
	if err != nil {
		return sops.Metadata{}, err
	}
	groups, err := m.internalKeygroups()
	if err != nil {
		return sops.Metadata{}, err
	}

	cryptRuleCount := 0
	if m.UnencryptedSuffix != "" {
		cryptRuleCount++
	}
	if m.EncryptedSuffix != "" {
		cryptRuleCount++
	}
	if m.UnencryptedRegex != "" {
		cryptRuleCount++
	}
	if m.EncryptedRegex != "" {
		cryptRuleCount++
	}

	if cryptRuleCount > 1 {
		return sops.Metadata{}, fmt.Errorf("Cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex or unencrypted_regex in the same file")
	}

	if cryptRuleCount == 0 {
		m.UnencryptedSuffix = sops.DefaultUnencryptedSuffix
	}
	return sops.Metadata{
		KeyGroups:                 groups,
		ShamirThreshold:           m.ShamirThreshold,
		Version:                   m.Version,
		MessageAuthenticationCode: m.MessageAuthenticationCode,
		UnencryptedSuffix:         m.UnencryptedSuffix,
		EncryptedSuffix:           m.EncryptedSuffix,
		UnencryptedRegex:          m.UnencryptedRegex,
		EncryptedRegex:            m.EncryptedRegex,
		LastModified:              lastModified,
	}, nil
}

func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey, azkvKeys []azkvkey, vaultKeys []vaultkey, ageKeys []agekey) (sops.KeyGroup, error) {
	var internalGroup sops.KeyGroup
	for _, kmsKey := range kmsKeys {
		k, err := kmsKey.toInternal()
		if err != nil {
			return nil, err
		}
		internalGroup = append(internalGroup, k)
	}
	for _, gcpKmsKey := range gcpKmsKeys {
		k, err := gcpKmsKey.toInternal()
		if err != nil {
			return nil, err
		}
		internalGroup = append(internalGroup, k)
	}
	for _, azkvKey := range azkvKeys {
		k, err := azkvKey.toInternal()
		if err != nil {
			return nil, err
		}
		internalGroup = append(internalGroup, k)
	}
	for _, vaultKey := range vaultKeys {
		k, err := vaultKey.toInternal()
		if err != nil {
			return nil, err
		}
		internalGroup = append(internalGroup, k)
	}
	for _, pgpKey := range pgpKeys {
		k, err := pgpKey.toInternal()
		if err != nil {
			return nil, err
		}
		internalGroup = append(internalGroup, k)
	}
	for _, ageKey := range ageKeys {
		k, err := ageKey.toInternal()
		if err != nil {
			return nil, err
		}
		internalGroup = append(internalGroup, k)
	}
	return internalGroup, nil
}

func (m *Metadata) internalKeygroups() ([]sops.KeyGroup, error) {
	var internalGroups []sops.KeyGroup
	if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 || len(m.AzureKeyVaultKeys) > 0 || len(m.VaultKeys) > 0 || len(m.AgeKeys) > 0 {
		internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys, m.AzureKeyVaultKeys, m.VaultKeys, m.AgeKeys)
		if err != nil {
			return nil, err
		}
		internalGroups = append(internalGroups, internalGroup)
		return internalGroups, nil
	} else if len(m.KeyGroups) > 0 {
		for _, group := range m.KeyGroups {
			internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys, group.AzureKeyVaultKeys, group.VaultKeys, group.AgeKeys)
			if err != nil {
				return nil, err
			}
			internalGroups = append(internalGroups, internalGroup)
		}
		return internalGroups, nil
	} else {
		return nil, fmt.Errorf("No keys found in file")
	}
}

func (kmsKey *kmskey) toInternal() (*kms.MasterKey, error) {
	creationDate, err := time.Parse(time.RFC3339, kmsKey.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &kms.MasterKey{
		Role:              kmsKey.Role,
		EncryptionContext: kmsKey.Context,
		EncryptedKey:      kmsKey.EncryptedDataKey,
		CreationDate:      creationDate,
		Arn:               kmsKey.Arn,
		AwsProfile:        kmsKey.AwsProfile,
	}, nil
}

func (gcpKmsKey *gcpkmskey) toInternal() (*gcpkms.MasterKey, error) {
	creationDate, err := time.Parse(time.RFC3339, gcpKmsKey.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &gcpkms.MasterKey{
		ResourceID:   gcpKmsKey.ResourceID,
		EncryptedKey: gcpKmsKey.EncryptedDataKey,
		CreationDate: creationDate,
	}, nil
}

func (azkvKey *azkvkey) toInternal() (*azkv.MasterKey, error) {
	creationDate, err := time.Parse(time.RFC3339, azkvKey.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &azkv.MasterKey{
		VaultURL:     azkvKey.VaultURL,
		Name:         azkvKey.Name,
		Version:      azkvKey.Version,
		EncryptedKey: azkvKey.EncryptedDataKey,
		CreationDate: creationDate,
	}, nil
}

func (vaultKey *vaultkey) toInternal() (*hcvault.MasterKey, error) {
	creationDate, err := time.Parse(time.RFC3339, vaultKey.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &hcvault.MasterKey{
		VaultAddress: vaultKey.VaultAddress,
		EnginePath:   vaultKey.EnginePath,
		KeyName:      vaultKey.KeyName,
		CreationDate: creationDate,
		EncryptedKey: vaultKey.EncryptedDataKey,
	}, nil
}

func (pgpKey *pgpkey) toInternal() (*pgp.MasterKey, error) {
	creationDate, err := time.Parse(time.RFC3339, pgpKey.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &pgp.MasterKey{
		EncryptedKey: pgpKey.EncryptedDataKey,
		CreationDate: creationDate,
		Fingerprint:  pgpKey.Fingerprint,
	}, nil
}

func (ageKey *agekey) toInternal() (*age.MasterKey, error) {
	return &age.MasterKey{
		EncryptedKey: ageKey.EncryptedDataKey,
		Recipient:    ageKey.Recipient,
	}, nil
}

// ExampleComplexTree is an example sops.Tree object exhibiting complex relationships
var ExampleComplexTree = sops.Tree{
	Branches: sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key:   "hello",
				Value: `Welcome to SOPS! Edit this file as you please!`,
			},
			sops.TreeItem{
				Key:   "example_key",
				Value: "example_value",
			},
			sops.TreeItem{
				Key:   sops.Comment{Value: " Example comment"},
				Value: nil,
			},
			sops.TreeItem{
				Key: "example_array",
				Value: []interface{}{
					"example_value1",
					"example_value2",
				},
			},
			sops.TreeItem{
				Key:   "example_number",
				Value: 1234.56789,
			},
			sops.TreeItem{
				Key:   "example_booleans",
				Value: []interface{}{true, false},
			},
		},
	},
}

// ExampleSimpleTree is an example sops.Tree object exhibiting only simple relationships
// with only one nested branch and only simple string values
var ExampleSimpleTree = sops.Tree{
	Branches: sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key: "Welcome!",
				Value: sops.TreeBranch{
					sops.TreeItem{
						Key:   sops.Comment{Value: " This is an example file."},
						Value: nil,
					},
					sops.TreeItem{
						Key:   "hello",
						Value: "Welcome to SOPS! Edit this file as you please!",
					},
					sops.TreeItem{
						Key:   "example_key",
						Value: "example_value",
					},
				},
			},
		},
	},
}

// ExampleFlatTree is an example sops.Tree object exhibiting only simple relationships
// with no nested branches and only simple string values
var ExampleFlatTree = sops.Tree{
	Branches: sops.TreeBranches{
		sops.TreeBranch{
			sops.TreeItem{
				Key:   sops.Comment{Value: " This is an example file."},
				Value: nil,
			},
			sops.TreeItem{
				Key:   "hello",
				Value: "Welcome to SOPS! Edit this file as you please!",
			},
			sops.TreeItem{
				Key:   "example_key",
				Value: "example_value",
			},
			sops.TreeItem{
				Key:   "example_multiline",
				Value: "foo\nbar\nbaz",
			},
		},
	},
}
070701000000AB000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001700000000sops-3.7.3/stores/yaml070701000000AC000081A400000000000000000000000162794F9300002C07000000000000000000000000000000000000002000000000sops-3.7.3/stores/yaml/store.gopackage yaml //import "go.mozilla.org/sops/v3/stores/yaml"

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"gopkg.in/yaml.v3"
	"go.mozilla.org/sops/v3"
	"go.mozilla.org/sops/v3/stores"
)

// Store handles storage of YAML data
type Store struct {
}

func (store Store) appendCommentToList(comment string, list []interface{}) []interface{} {
	if comment != "" {
		for _, commentLine := range strings.Split(comment, "\n") {
			if commentLine != "" {
				list = append(list, sops.Comment{
					Value: commentLine[1:],
				})
			}
		}
	}
	return list
}

func (store Store) appendCommentToMap(comment string, branch sops.TreeBranch) sops.TreeBranch {
	if comment != "" {
		for _, commentLine := range strings.Split(comment, "\n") {
			if commentLine != "" {
				branch = append(branch, sops.TreeItem{
					Key: sops.Comment{
						Value: commentLine[1:],
					},
					Value: nil,
				})
			}
		}
	}
	return branch
}

func (store Store) nodeToTreeValue(node *yaml.Node, commentsWereHandled bool) (interface{}, error) {
	switch node.Kind {
	case yaml.DocumentNode:
		panic("documents should never be passed here")
	case yaml.SequenceNode:
		var result []interface{}
		if !commentsWereHandled {
			result = store.appendCommentToList(node.HeadComment, result)
			result = store.appendCommentToList(node.LineComment, result)
		}
		for _, item := range node.Content {
			result = store.appendCommentToList(item.HeadComment, result)
			result = store.appendCommentToList(item.LineComment, result)
			val, err := store.nodeToTreeValue(item, true)
			if err != nil {
				return nil, err
			}
			result = append(result, val)
			result = store.appendCommentToList(item.FootComment, result)
		}
		if !commentsWereHandled {
			result = store.appendCommentToList(node.FootComment, result)
		}
		return result, nil
	case yaml.MappingNode:
		branch := make(sops.TreeBranch, 0)
		return store.appendYamlNodeToTreeBranch(node, branch, commentsWereHandled)
	case yaml.ScalarNode:
		var result interface{}
		node.Decode(&result)
		return result, nil
	case yaml.AliasNode:
		return store.nodeToTreeValue(node.Alias, false);
	}
	return nil, nil
}

func (store Store) appendYamlNodeToTreeBranch(node *yaml.Node, branch sops.TreeBranch, commentsWereHandled bool) (sops.TreeBranch, error) {
	var err error
	if !commentsWereHandled {
		branch = store.appendCommentToMap(node.HeadComment, branch)
		branch = store.appendCommentToMap(node.LineComment, branch)
	}
	switch node.Kind {
	case yaml.DocumentNode:
		for _, item := range node.Content {
			branch, err = store.appendYamlNodeToTreeBranch(item, branch, false)
			if err != nil {
				return nil, err
			}
		}
	case yaml.SequenceNode:
		return nil, fmt.Errorf("YAML documents that are sequences are not supported")
	case yaml.MappingNode:
		for i := 0; i < len(node.Content); i += 2 {
			key := node.Content[i]
			value := node.Content[i + 1]
			branch = store.appendCommentToMap(key.HeadComment, branch)
			branch = store.appendCommentToMap(key.LineComment, branch)
			handleValueComments := value.Kind == yaml.ScalarNode || value.Kind == yaml.AliasNode
			if handleValueComments {
				branch = store.appendCommentToMap(value.HeadComment, branch)
				branch = store.appendCommentToMap(value.LineComment, branch)
			}
			var keyValue interface{}
			key.Decode(&keyValue)
			valueTV, err := store.nodeToTreeValue(value, handleValueComments)
			if err != nil {
				return nil, err
			}
			branch = append(branch, sops.TreeItem{
				Key:   keyValue,
				Value: valueTV,
			})
			if handleValueComments {
				branch = store.appendCommentToMap(value.FootComment, branch)
			}
			branch = store.appendCommentToMap(key.FootComment, branch)
		}
	case yaml.ScalarNode:
		// A empty document with a document start marker without comments results in null
		if node.ShortTag() == "!!null" {
			return branch, nil
		}
		return nil, fmt.Errorf("YAML documents that are values are not supported")
	case yaml.AliasNode:
		branch, err = store.appendYamlNodeToTreeBranch(node.Alias, branch, false)
	}
	if !commentsWereHandled {
		branch = store.appendCommentToMap(node.FootComment, branch)
	}
	return branch, nil
}

func (store Store) yamlDocumentNodeToTreeBranch(in yaml.Node) (sops.TreeBranch, error) {
	branch := make(sops.TreeBranch, 0)
	return store.appendYamlNodeToTreeBranch(&in, branch, false)
}

func (store *Store) addCommentsHead(node *yaml.Node, comments []string) []string {
	if len(comments) > 0 {
		comment := "#" + strings.Join(comments, "\n#")
		if len(node.HeadComment) > 0 {
			node.HeadComment = comment + "\n" + node.HeadComment
		} else {
			node.HeadComment = comment
		}
	}
	return nil
}

func (store *Store) addCommentsFoot(node *yaml.Node, comments []string) []string {
	if len(comments) > 0 {
		comment := "#" + strings.Join(comments, "\n#")
		if len(node.FootComment) > 0 {
			node.FootComment += "\n" + comment
		} else {
			node.FootComment = comment
		}
	}
	return nil
}

func (store *Store) treeValueToNode(in interface{}) *yaml.Node {
	switch in := in.(type) {
	case sops.TreeBranch:
		var mapping = &yaml.Node{}
		mapping.Kind = yaml.MappingNode
		store.appendTreeBranch(in, mapping)
		return mapping
	case []interface{}:
		var sequence = &yaml.Node{}
		sequence.Kind = yaml.SequenceNode
		store.appendSequence(in, sequence)
		return sequence
	default:
		var valueNode = &yaml.Node{}
		valueNode.Encode(in)
		return valueNode
	}
}

func (store *Store) appendSequence(in []interface{}, sequence *yaml.Node) {
	var comments []string
	var beginning bool = true
	for _, item := range in {
		if comment, ok := item.(sops.Comment); ok {
			comments = append(comments, comment.Value)
		} else {
			if beginning {
				comments = store.addCommentsHead(sequence, comments)
				beginning = false
			}
			itemNode := store.treeValueToNode(item)
			comments = store.addCommentsHead(itemNode, comments)
			sequence.Content = append(sequence.Content, itemNode)
		}
	}
	if len(comments) > 0 {
		if beginning {
			comments = store.addCommentsHead(sequence, comments)
		} else {
			comments = store.addCommentsFoot(sequence.Content[len(sequence.Content) - 1], comments)
		}
	}
}

func (store *Store) appendTreeBranch(branch sops.TreeBranch, mapping *yaml.Node) {
	var comments []string
	var beginning bool = true
	for _, item := range branch {
		if comment, ok := item.Key.(sops.Comment); ok {
			comments = append(comments, comment.Value)
		} else {
			if beginning {
				comments = store.addCommentsHead(mapping, comments)
				beginning = false
			}
			var keyNode = &yaml.Node{}
			keyNode.Encode(item.Key)
			comments = store.addCommentsHead(keyNode, comments)
			valueNode := store.treeValueToNode(item.Value)
			mapping.Content = append(mapping.Content, keyNode, valueNode)
		}
	}
	if len(comments) > 0 {
		if beginning {
			comments = store.addCommentsHead(mapping, comments)
		} else {
			comments = store.addCommentsFoot(mapping.Content[len(mapping.Content) - 1], comments)
		}
	}
}

// LoadEncryptedFile loads the contents of an encrypted yaml file onto a
// sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
	// Because we don't know what fields the input file will have, we have to
	// load the file in two steps.
	// First, we load the file's metadata, the structure of which is known.
	metadataHolder := stores.SopsFile{}
	err := yaml.Unmarshal(in, &metadataHolder)
	if err != nil {
		return sops.Tree{}, fmt.Errorf("Error unmarshalling input yaml: %s", err)
	}
	if metadataHolder.Metadata == nil {
		return sops.Tree{}, sops.MetadataNotFound
	}
	metadata, err := metadataHolder.Metadata.ToInternal()
	if err != nil {
		return sops.Tree{}, err
	}
	var data yaml.Node
	if err := yaml.Unmarshal(in, &data); err != nil {
		return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
	}
	var branches sops.TreeBranches
	d := yaml.NewDecoder(bytes.NewReader(in))
	for true {
		var data yaml.Node
		err := d.Decode(&data)
		if err == io.EOF {
			break
		}
		if err != nil {
			return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
		}

		branch, err := store.yamlDocumentNodeToTreeBranch(data)
		if err != nil {
			return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
		}

		for i, elt := range branch {
			if elt.Key == "sops" { // Erase
				branch = append(branch[:i], branch[i+1:]...)
			}
		}
		branches = append(branches, branch)
	}
	return sops.Tree{
		Branches: branches,
		Metadata: metadata,
	}, nil
}

// LoadPlainFile loads the contents of a plaintext yaml file onto a
// sops.Tree runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
	var branches sops.TreeBranches
	d := yaml.NewDecoder(bytes.NewReader(in))
	for true {
		var data yaml.Node
		err := d.Decode(&data)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
		}

		branch, err := store.yamlDocumentNodeToTreeBranch(data)
		if err != nil {
			return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
		}
		branches = append(branches, branch)
	}
	return branches, nil
}

// EmitEncryptedFile returns the encrypted bytes of the yaml file corresponding to a
// sops.Tree runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
    var b bytes.Buffer
	e := yaml.NewEncoder(io.Writer(&b))
	e.SetIndent(4)
	for _, branch := range in.Branches {
		// Document root
		var doc = yaml.Node{}
		doc.Kind = yaml.DocumentNode
		// Add global mapping
		var mapping = yaml.Node{}
		mapping.Kind = yaml.MappingNode
		doc.Content = append(doc.Content, &mapping)
		// Create copy of branch with metadata appended
		branch = append(sops.TreeBranch(nil), branch...)
		branch = append(branch, sops.TreeItem{
			Key: "sops",
			Value: stores.MetadataFromInternal(in.Metadata),
		})
		// Marshal branch to global mapping node
		store.appendTreeBranch(branch, &mapping)
		// Encode YAML
		err := e.Encode(&doc)
		if err != nil {
			return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
		}
	}
	e.Close()
	return b.Bytes(), nil
}

// EmitPlainFile returns the plaintext bytes of the yaml file corresponding to a
// sops.TreeBranches runtime object
func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) {
    var b bytes.Buffer
	e := yaml.NewEncoder(io.Writer(&b))
	e.SetIndent(4)
	for _, branch := range branches {
		// Document root
		var doc = yaml.Node{}
		doc.Kind = yaml.DocumentNode
		// Add global mapping
		var mapping = yaml.Node{}
		mapping.Kind = yaml.MappingNode
		// Marshal branch to global mapping node
		store.appendTreeBranch(branch, &mapping)
		doc.Content = append(doc.Content, &mapping)
		// Encode YAML
		err := e.Encode(&doc)
		if err != nil {
			return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
		}
	}
	e.Close()
	return b.Bytes(), nil
}

// EmitValue returns bytes corresponding to a single encoded value
// in a generic interface{} object
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
	n := store.treeValueToNode(v)
	return yaml.Marshal(n)
}

// EmitExample returns the bytes corresponding to an example complex tree
func (store *Store) EmitExample() []byte {
	bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
	if err != nil {
		panic(err)
	}
	return bytes
}
070701000000AD000081A400000000000000000000000162794F930000170C000000000000000000000000000000000000002500000000sops-3.7.3/stores/yaml/store_test.gopackage yaml

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"go.mozilla.org/sops/v3"
)

var PLAIN = []byte(`---
# comment 0
key1: value
key1_a: value
# ^ comment 1
---
key2: value2`)

var PLAIN_0 = []byte(`# comment 0
key1: value
key1_a: value
# ^ comment 1
`)

var BRANCHES = sops.TreeBranches{
	sops.TreeBranch{
		sops.TreeItem{
			Key:   sops.Comment{" comment 0"},
			Value: nil,
		},
		sops.TreeItem{
			Key:   "key1",
			Value: "value",
		},
		sops.TreeItem{
			Key:   "key1_a",
			Value: "value",
		},
		sops.TreeItem{
			Key:   sops.Comment{" ^ comment 1"},
			Value: nil,
		},
	},
	sops.TreeBranch{
		sops.TreeItem{
			Key:   "key2",
			Value: "value2",
		},
	},
}

var COMMENT_1 = []byte(`# test
a:
    b: null
    # foo
`)

var COMMENT_2 = []byte(`a:
    # foo
    b: null
`)

var COMMENT_3_IN = []byte(`## Configuration for prometheus-node-exporter subchart
##
prometheus-node-exporter:
  podLabels:
    ## Add the 'node-exporter' label to be used by serviceMonitor to match standard common usage in rules and grafana dashboards
    ##

    jobLabel: node-exporter
  extraArgs:
    - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
    - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
`)
var COMMENT_3_OUT = []byte(`## Configuration for prometheus-node-exporter subchart
##
prometheus-node-exporter:
    podLabels:
        ## Add the 'node-exporter' label to be used by serviceMonitor to match standard common usage in rules and grafana dashboards
        ##
        jobLabel: node-exporter
    extraArgs:
        - --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
        - --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
`)

var COMMENT_4 = []byte(`# foo
`)

var COMMENT_5 = []byte(`# foo
---
key: value
`)

// The following is a regression test for https://github.com/mozilla/sops/issues/865
var COMMENT_6 = []byte(`a:
    - a
    # I no longer get duplicated
    - {}
`)

var COMMENT_6_BRANCHES = sops.TreeBranches{
	sops.TreeBranch{
		sops.TreeItem{
			Key:   "a",
			Value: []interface{}{
				"a",
				sops.Comment{" I no longer get duplicated"},
				sops.TreeBranch{},
			},
		},
	},
}

func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
	data := []byte(`hello: 2`)
	_, err := (&Store{}).LoadEncryptedFile(data)
	assert.Equal(t, sops.MetadataNotFound, err)
}

func TestLoadPlainFile(t *testing.T) {
	branches, err := (&Store{}).LoadPlainFile(PLAIN)
	assert.Nil(t, err)
	assert.Equal(t, BRANCHES, branches)
}

func TestComment1(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile(COMMENT_1)
	assert.Nil(t, err)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, string(COMMENT_1), string(bytes))
	assert.Equal(t, COMMENT_1, bytes)
}

func TestComment2(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile(COMMENT_2)
	assert.Nil(t, err)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, string(COMMENT_2), string(bytes))
	assert.Equal(t, COMMENT_2, bytes)
}

func TestComment3(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile(COMMENT_3_IN)
	assert.Nil(t, err)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, string(COMMENT_3_OUT), string(bytes))
	assert.Equal(t, COMMENT_3_OUT, bytes)
}

/* TODO: re-enable once https://github.com/go-yaml/yaml/pull/690 is merged
func TestComment4(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile(COMMENT_4)
	assert.Nil(t, err)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, string(COMMENT_4), string(bytes))
	assert.Equal(t, COMMENT_4, bytes)
}

func TestComment5(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile(COMMENT_5)
	assert.Nil(t, err)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, string(COMMENT_5), string(bytes))
	assert.Equal(t, COMMENT_5, bytes)
}
*/

func TestEmpty(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile([]byte(``))
	assert.Nil(t, err)
	assert.Equal(t, len(branches), 0)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, ``, string(bytes))
}

/* TODO: re-enable once https://github.com/go-yaml/yaml/pull/690 is merged
func TestEmpty2(t *testing.T) {
	// First iteration: load and store
	branches, err := (&Store{}).LoadPlainFile([]byte(`---`))
	assert.Nil(t, err)
	assert.Equal(t, len(branches), 1)
	assert.Equal(t, len(branches[0]), 0)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, ``, string(bytes))
}
*/

func TestEmpty3(t *testing.T) {
	branches, err := (&Store{}).LoadPlainFile([]byte("{}\n"))
	assert.Nil(t, err)
	assert.Equal(t, len(branches), 1)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, "{}\n", string(bytes))
}

func TestComment6(t *testing.T) {
	branches, err := (&Store{}).LoadPlainFile(COMMENT_6)
	assert.Nil(t, err)
	assert.Equal(t, COMMENT_6_BRANCHES, branches)
	bytes, err := (&Store{}).EmitPlainFile(branches)
	assert.Nil(t, err)
	assert.Equal(t, string(COMMENT_6), string(bytes))
	assert.Equal(t, COMMENT_6, bytes)
}

func TestEmitValue(t *testing.T) {
	// First iteration: load and store
	bytes, err := (&Store{}).EmitValue(BRANCHES[0])
	assert.Nil(t, err)
	assert.Equal(t, string(PLAIN_0), string(bytes))
	assert.Equal(t, PLAIN_0, bytes)
}
070701000000AE000081ED00000000000000000000000162794F930000016D000000000000000000000000000000000000001300000000sops-3.7.3/test.sh#!/usr/bin/env bash

set -e
echo "" > coverage.txt

failed=0

for d in $(go list ./... | grep -v vendor); do
    go test -race -coverprofile=profile.out -covermode=atomic $d && true
    rc=$?
    if [ $rc != 0 ]; then
      failed=$rc
    fi
    if [ -f profile.out ]; then
        cat profile.out >> coverage.txt
        rm profile.out
    fi
done

exit ${failed}
070701000000AF000081A400000000000000000000000162794F930000115E000000000000000000000000000000000000001900000000sops-3.7.3/usererrors.gopackage sops

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/fatih/color"
	"github.com/goware/prefixer"
	wordwrap "github.com/mitchellh/go-wordwrap"
)

// UserError is a well-formatted error for the purpose of being displayed to
// the end user.
type UserError interface {
	error
	UserError() string
}

var statusSuccess = color.New(color.FgGreen).Sprint("SUCCESS")
var statusFailed = color.New(color.FgRed).Sprint("FAILED")

type getDataKeyError struct {
	RequiredSuccessfulKeyGroups int
	GroupResults                []error
}

func (err *getDataKeyError) successfulKeyGroups() int {
	n := 0
	for _, r := range err.GroupResults {
		if r == nil {
			n++
		}
	}
	return n
}

func (err *getDataKeyError) Error() string {
	return fmt.Sprintf("Error getting data key: %d successful groups "+
		"required, got %d", err.RequiredSuccessfulKeyGroups,
		err.successfulKeyGroups())
}

func (err *getDataKeyError) UserError() string {
	var groupErrs []string
	for i, res := range err.GroupResults {
		groupErr := decryptGroupError{
			err:       res,
			groupName: fmt.Sprintf("%d", i),
		}
		groupErrs = append(groupErrs, groupErr.UserError())
	}
	var trailer string
	if err.RequiredSuccessfulKeyGroups == 0 {
		trailer = "Recovery failed because no master key was able to decrypt " +
			"the file. In order for SOPS to recover the file, at least one key " +
			"has to be successful, but none were."
	} else {
		trailer = fmt.Sprintf("Recovery failed because the file was "+
			"encrypted with a Shamir threshold of %d, but only %d part(s) "+
			"were successfully recovered, one for each successful key group. "+
			"In order for SOPS to recover the file, at least %d groups have "+
			"to be successful. In order for a group to be successful, "+
			"decryption has to succeed with any of the keys in that key group.",
			err.RequiredSuccessfulKeyGroups, err.successfulKeyGroups(),
			err.RequiredSuccessfulKeyGroups)
	}
	trailer = wordwrap.WrapString(trailer, 75)
	return fmt.Sprintf("Failed to get the data key required to "+
		"decrypt the SOPS file.\n\n%s\n\n%s",
		strings.Join(groupErrs, "\n\n"), trailer)
}

type decryptGroupError struct {
	groupName string
	err       error
}

func (r *decryptGroupError) Error() string {
	return fmt.Sprintf("could not decrypt group %s: %s", r.groupName, r.err)
}

func (r *decryptGroupError) UserError() string {
	var status string
	if r.err == nil {
		status = statusSuccess
	} else {
		status = statusFailed
	}
	header := fmt.Sprintf(`Group %s: %s`, r.groupName, status)
	if r.err == nil {
		return header
	}
	message := r.err.Error()
	if userError, ok := r.err.(UserError); ok {
		message = userError.UserError()
	}
	reader := prefixer.New(strings.NewReader(message), "  ")
	// Safe to ignore this error, as reading from a strings.Reader can't fail
	errMsg, _ := ioutil.ReadAll(reader)
	return fmt.Sprintf("%s\n%s", header, string(errMsg))
}

type decryptKeyErrors []error

func (e decryptKeyErrors) Error() string {
	return fmt.Sprintf("error decrypting key: %s", []error(e))
}

func (e decryptKeyErrors) UserError() string {
	var errStrs []string
	for _, err := range []error(e) {
		if userErr, ok := err.(UserError); ok {
			errStrs = append(errStrs, userErr.UserError())
		} else {
			errStrs = append(errStrs, err.Error())
		}
	}
	return strings.Join(errStrs, "\n\n")
}

type decryptKeyError struct {
	keyName string
	errs    []error
}

func (e *decryptKeyError) isSuccessful() bool {
	for _, err := range e.errs {
		if err == nil {
			return true
		}
	}
	return false
}

func (e *decryptKeyError) Error() string {
	return fmt.Sprintf("error decrypting key %s: %s", e.keyName, e.errs)
}

func (e *decryptKeyError) UserError() string {
	var status string
	if e.isSuccessful() {
		status = statusSuccess
	} else {
		status = statusFailed
	}
	header := fmt.Sprintf("%s: %s", e.keyName, status)
	if e.isSuccessful() {
		return header
	}
	var errMessages []string
	for _, err := range e.errs {
		wrappedErr := wordwrap.WrapString(err.Error(), 60)
		reader := prefixer.New(strings.NewReader(wrappedErr), "  | ")
		// Safe to ignore this error, as reading from a strings.Reader can't fail
		errMsg, _ := ioutil.ReadAll(reader)
		errMsg[0] = '-'
		errMessages = append(errMessages, string(errMsg))
	}
	joinedMsgs := strings.Join(errMessages, "\n\n")
	reader := prefixer.New(strings.NewReader(joinedMsgs), "  ")
	errMsg, _ := ioutil.ReadAll(reader)
	return fmt.Sprintf("%s\n%s", header, string(errMsg))
}
070701000000B0000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001600000000sops-3.7.3/validation070701000000B1000081A400000000000000000000000162794F93000000A0000000000000000000000000000000000000002100000000sops-3.7.3/validation/Cargo.toml[package]
name = "validation"
version = "0.1.0"
authors = ["Adrian Utrilla <adrianutrilla@gmail.com>"]

[dependencies]
serde_json = "0.8.0"
yaml-rust = "0.3.3"
070701000000B2000081A400000000000000000000000162794F9300000314000000000000000000000000000000000000002300000000sops-3.7.3/validation/example.json{
    "glossary": {
        "title": "example glossary",
        "GlossDiv": {
            "title": "S",
            "GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
                    "SortAs": "SGML",
                    "GlossTerm": "Standard Generalized Markup Language",
                    "Acronym": "SGML",
                    "Abbrev": "ISO 8879:1986",
                    "GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
                        "GlossSeeAlso": [
                            "GML",
                            "XML"
                        ]
                    },
                    "GlossSee": "markup"
                }
            }
        }
    }
}
070701000000B3000081A400000000000000000000000162794F93000000B7000000000000000000000000000000000000002300000000sops-3.7.3/validation/example.yamlname: Martin
job: Developer
skill: Elite
foods:
    - Apple
    - Orange
    - Strawberry
    - Mango
languages:
    perl: Elite
    python: Elite
    pascal: Lame
foo: |
  bar
  baz
070701000000B4000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001A00000000sops-3.7.3/validation/src070701000000B5000081A400000000000000000000000162794F9300000E76000000000000000000000000000000000000002200000000sops-3.7.3/validation/src/main.rsuse std::rc::Rc;
use std::process::{Stdio, Command};
use std::io::{Write, Read};
use std::fs::{OpenOptions, File};
use std::str;
use std::env;
extern crate serde_json;

use serde_json::Value;

extern crate yaml_rust;
use yaml_rust::{YamlLoader, YamlEmitter};

fn main() {}

fn run_sops_and_return_output(command: &mut Command, filename: &str) -> String {
    let mut child = command.stdout(Stdio::piped())
        .arg(filename)
        .spawn()
        .expect("Could not start sops python process");
    let output = child.wait_with_output().expect("Could not retrieve sops's output");
    if !output.status.success() {
        panic!("sops did not exit successfully!");
    }
    return String::from_utf8(output.stdout).expect("Could not decode sops's output as utf-8");
}

fn get_sops_python() -> Command {
    let sops_python_path = env::var("SOPS_PYTHON_PATH")
        .expect("SOPS_PYTHON_PATH environment variable missing");
    let mut cmd = Command::new("python");
    cmd.arg(sops_python_path);
    cmd
}

fn encrypt_with_sops_python(plaintext: &str) -> String {
    let mut child = get_sops_python();
    let child = child.arg("-e");
    return run_sops_and_return_output(child, plaintext);
}

fn decrypt_with_sops_python(ciphertext: &str) -> String {
    let mut child = get_sops_python();
    let child = child.arg("-d");
    return run_sops_and_return_output(child, ciphertext);
}

fn validate_json_file(input_file_name: &str,
                      encrypt: fn(&str) -> String,
                      decrypt: fn(&str) -> String) {
    let output_file_name = "temp.json";
    let mut input = String::new();
    File::open(input_file_name).unwrap().read_to_string(&mut input);
    let input_value: Value = serde_json::from_str(&input).expect("Could not decode input json");
    let encrypted_output = encrypt(input_file_name);
    let mut output_file = OpenOptions::new()
        .write(true)
        .create(true)
        .open(output_file_name)
        .expect("Could not open output file");
    output_file.write_all(encrypted_output.as_bytes()).expect("Could not write to output file");
    let decryption = decrypt(output_file_name);
    let output_value: Value = serde_json::from_str(&decryption).unwrap();
    std::fs::remove_file(output_file_name).expect("Could not remove output file");
    assert_eq!(input_value, output_value);
}

fn validate_yaml_file(input_file_name: &str,
                      encrypt: fn(&str) -> String,
                      decrypt: fn(&str) -> String) {
    let output_file_name = "temp.yaml";
    let mut input = String::new();
    File::open(input_file_name).unwrap().read_to_string(&mut input);
    let input_value = YamlLoader::load_from_str(&input).expect("Could not decode input yaml");
    let encrypted_output = encrypt(input_file_name);
    let mut output_file = OpenOptions::new()
        .write(true)
        .create(true)
        .open(output_file_name)
        .expect("Could not open output file");
    output_file.write_all(encrypted_output.as_bytes()).expect("Could not write to output file");
    let decryption = decrypt(output_file_name);
    let output_value = YamlLoader::load_from_str(&decryption)
        .expect("Could not decode output yaml");
    std::fs::remove_file(output_file_name).expect("Could not remove output file");
    assert_eq!(input_value, output_value);
}

#[test]
fn validate_python_json() {
    validate_json_file("example.json",
                       encrypt_with_sops_python,
                       decrypt_with_sops_python);
}


#[test]
fn validate_python_yaml() {
    validate_yaml_file("example.yaml",
                       encrypt_with_sops_python,
                       decrypt_with_sops_python);
}
070701000000B6000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/version070701000000B7000081A400000000000000000000000162794F93000009AF000000000000000000000000000000000000001E00000000sops-3.7.3/version/version.gopackage version

import (
	"bufio"
	"fmt"
	"net/http"
	"strings"

	"github.com/blang/semver"
	"gopkg.in/urfave/cli.v1"
)

// Version represents the value of the current semantic version
const Version = "3.7.3"

// PrintVersion handles the version command for sops
func PrintVersion(c *cli.Context) {
	out := fmt.Sprintf("%s %s", c.App.Name, c.App.Version)
	upstreamVersion, err := RetrieveLatestVersionFromUpstream()
	if err != nil {
		out += fmt.Sprintf("\n[warning] failed to retrieve latest version from upstream: %v\n", err)
	}
	outdated, err := AIsNewerThanB(upstreamVersion, Version)
	if err != nil {
		out += fmt.Sprintf("\n[warning] failed to compare current version with latest: %v\n", err)
	}
	if outdated {
		out += fmt.Sprintf("\n[info] sops %s is available, update with `go get -u go.mozilla.org/sops/v3/cmd/sops`\n", upstreamVersion)
	} else {
		out += " (latest)\n"
	}
	fmt.Fprintf(c.App.Writer, "%s", out)
}

// AIsNewerThanB takes 2 semver strings are returns true
// is the A is newer than B, false otherwise
func AIsNewerThanB(A, B string) (bool, error) {
	if strings.HasPrefix(B, "1.") {
		// sops 1.0 doesn't use the semver format, which will
		// fail the call to `make` below. Since we now we're
		// more recent than 1.X anyway, return true right away
		return true, nil
	}
	vA, err := semver.Make(A)
	if err != nil {
		return false, err
	}
	vB, err := semver.Make(B)
	if err != nil {
		return false, err
	}
	if vA.Compare(vB) > 0 {
		// vA is newer than vB
		return true, nil
	}
	return false, nil
}

// RetrieveLatestVersionFromUpstream gets the latest version from the source code at Github
func RetrieveLatestVersionFromUpstream() (string, error) {
	resp, err := http.Get("https://raw.githubusercontent.com/mozilla/sops/master/version/version.go")
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, `const Version = "`) {
			comps := strings.Split(line, `"`)
			if len(comps) < 2 {
				return "", fmt.Errorf("Failed to parse version from upstream source")
			}
			// try to parse the version as semver
			_, err := semver.Make(comps[1])
			if err != nil {
				return "", fmt.Errorf("Retrieved version %q does not match semver format: %w", comps[1], err)
			}
			return comps[1], nil
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}
	return "", fmt.Errorf("Version information not found in upstream file")
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1674 blocks
openSUSE Build Service is sponsored by