mirror of
https://github.com/matter-labs/vault-auth-tee.git
synced 2025-07-21 07:43:57 +02:00
feat: initial commit
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
commit
c2411a45a7
29 changed files with 6542 additions and 0 deletions
4
.github/CODEOWNERS
vendored
Normal file
4
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Each line is a file pattern followed by one or more owners.
|
||||||
|
# Owners will be automatically notified about new PRs and
|
||||||
|
# an owner's approval is required to merge to protected branches.
|
||||||
|
* @haraldh @thomasknauth
|
41
.github/workflows/container.yml
vendored
Normal file
41
.github/workflows/container.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
name: Container
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
push_to_registry:
|
||||||
|
name: Build and push containers image to GitHub Packages
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Set up env
|
||||||
|
run: echo "repository_owner=${GITHUB_REPOSITORY_OWNER,,}" >>${GITHUB_ENV}
|
||||||
|
- name: Build and Push Container
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{env.repository_owner}}/${{ github.event.repository.name }}:latest
|
||||||
|
matterlabsrobot/${{ github.event.repository.name }}:latest
|
||||||
|
push: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
|
||||||
|
|
40
.github/workflows/go.yml
vendored
Normal file
40
.github/workflows/go.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# This workflow will build a golang project
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
|
||||||
|
|
||||||
|
name: Go
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Prep
|
||||||
|
run: |
|
||||||
|
wget -qO - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add -
|
||||||
|
sudo bash -c 'echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main" > /etc/apt/sources.list.d/intel-sgx.list'
|
||||||
|
sudo apt -o Acquire::Retries=3 update
|
||||||
|
sudo apt -o Acquire::Retries=3 install -y --no-install-recommends \
|
||||||
|
libsgx-headers \
|
||||||
|
libsgx-enclave-common \
|
||||||
|
libsgx-urts \
|
||||||
|
libsgx-dcap-quote-verify \
|
||||||
|
libsgx-dcap-quote-verify-dev
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: go test -v ./...
|
72
.github/workflows/nix.yml
vendored
Normal file
72
.github/workflows/nix.yml
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
name: nix
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths:
|
||||||
|
- '**.nix'
|
||||||
|
- 'go.mod'
|
||||||
|
- 'go.sum'
|
||||||
|
- 'flake.lock'
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths:
|
||||||
|
- '**.nix'
|
||||||
|
- 'go.mod'
|
||||||
|
- 'go.sum'
|
||||||
|
- 'flake.lock'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ github.token }}
|
||||||
|
- run: nix flake check -L --show-trace --keep-going
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ github.token }}
|
||||||
|
- run: nix fmt
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ github.token }}
|
||||||
|
- uses: cachix/cachix-action@v12
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
name: haraldh
|
||||||
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
- run: nix build -L .
|
||||||
|
|
||||||
|
develop:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ github.token }}
|
||||||
|
- uses: cachix/cachix-action@v12
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
name: haraldh
|
||||||
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
- run: nix develop -L -c go test ./...
|
18
.github/workflows/secrets_scanner.yaml
vendored
Normal file
18
.github/workflows/secrets_scanner.yaml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
name: Leaked Secrets Scan
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
TruffleHog:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: TruffleHog OSS
|
||||||
|
uses: trufflesecurity/trufflehog@6914dacde37c95874645cc208ce63a58c888cc6c # v3.60.4
|
||||||
|
with:
|
||||||
|
path: ./
|
||||||
|
base: ${{ github.event.repository.default_branch }}
|
||||||
|
head: HEAD
|
||||||
|
extra_args: --debug --only-verified
|
||||||
|
|
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
/.idea
|
41
CONTRIBUTING.md
Normal file
41
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Contribution Guidelines
|
||||||
|
|
||||||
|
Hello! Thanks for your interest in joining the mission to accelerate the mass adoption of crypto for personal
|
||||||
|
sovereignty! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!
|
||||||
|
|
||||||
|
## Ways to contribute
|
||||||
|
|
||||||
|
There are many ways to contribute to the project:
|
||||||
|
|
||||||
|
1. Open issues: if you find a bug, have something you believe needs to be fixed, or have an idea for a feature, please
|
||||||
|
open an issue.
|
||||||
|
2. Add color to existing issues: provide screenshots, code snippets, and whatever you think would be helpful to resolve
|
||||||
|
issues.
|
||||||
|
3. Resolve issues: either by showing an issue isn't a problem and the current state is ok as is or by fixing the problem
|
||||||
|
and opening a PR.
|
||||||
|
4. Report security issues, see [our security policy](SECURITY.md).
|
||||||
|
5. [Join the team!](https://matterlabs.notion.site/Shape-the-future-of-Ethereum-at-Matter-Labs-dfb3b5a037044bb3a8006af2eb0575e0)
|
||||||
|
|
||||||
|
## Fixing issues
|
||||||
|
|
||||||
|
To contribute code fixing issues, please fork the repo, fix an issue, commit, add documentation as per the PR template,
|
||||||
|
and the repo's maintainers will review the PR.
|
||||||
|
[here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork)
|
||||||
|
for guidance how to work with PRs created from a fork.
|
||||||
|
|
||||||
|
## Licenses
|
||||||
|
|
||||||
|
If you contribute to this project, your contributions will be made to the project under the Mozilla Public License 2.0
|
||||||
|
license.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
We aim to make it as easy as possible to contribute to the mission. This is still WIP, and we're happy for contributions
|
||||||
|
and suggestions here too.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
Be polite and respectful.
|
||||||
|
|
||||||
|
### Thank you
|
||||||
|
|
53
Dockerfile
Normal file
53
Dockerfile
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
FROM docker.io/ubuntu:20.04 AS pluginbuilder
|
||||||
|
|
||||||
|
ARG VERSION=1.20.4
|
||||||
|
ARG CGO_ENABLED=1
|
||||||
|
ARG BUILD_TAGS="default"
|
||||||
|
ENV JOBS=2
|
||||||
|
RUN set -eux; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update -y; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl; \
|
||||||
|
:
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
curl -fsSLo /usr/share/keyrings/intel.asc https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key; \
|
||||||
|
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel.asc] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main" > /etc/apt/sources.list.d/intel-sgx.list; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get update; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
rsync \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libprotobuf-dev \
|
||||||
|
protobuf-compiler \
|
||||||
|
clang \
|
||||||
|
libsgx-headers \
|
||||||
|
libsgx-dcap-quote-verify-dev \
|
||||||
|
; \
|
||||||
|
:
|
||||||
|
|
||||||
|
RUN mkdir /goroot && mkdir /go
|
||||||
|
RUN curl https://storage.googleapis.com/golang/go${VERSION}.linux-amd64.tar.gz \
|
||||||
|
| tar xvzf - -C /goroot --strip-components=1
|
||||||
|
ENV GOPATH /go
|
||||||
|
ENV GOROOT /goroot
|
||||||
|
ENV PATH $GOROOT/bin:$GOPATH/bin:$PATH
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/root/.cache --mount=type=cache,target=/go --mount=type=bind,target=/data \
|
||||||
|
set -eux; \
|
||||||
|
mkdir -p /go/src/github.com/matter-labs/vault-auth-tee; \
|
||||||
|
cd /go/src/github.com/matter-labs/vault-auth-tee; \
|
||||||
|
rsync -a --delete-after /data/ ./ ; \
|
||||||
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o vault-auth-tee; \
|
||||||
|
mkdir -p /opt/vault/plugins; \
|
||||||
|
cp vault-auth-tee /opt/vault/plugins/vault-auth-tee; \
|
||||||
|
:
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
WORKDIR /opt/vault/plugins
|
||||||
|
|
||||||
|
COPY --from=pluginbuilder /opt/vault/plugins/vault-auth-tee /opt/vault/plugins/vault-auth-tee
|
366
LICENSE
Normal file
366
LICENSE
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
Copyright (c) 2015 HashiCorp, Inc.
|
||||||
|
Copyright (c) 2023 Matter Labs
|
||||||
|
|
||||||
|
Mozilla 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.
|
||||||
|
|
96
README.md
Normal file
96
README.md
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
# vault-auth-tee
|
||||||
|
TEE remote attestation plugin for Hashicorp Vault
|
||||||
|
|
||||||
|
# ⚠️☢️☣️ WARNING: not yet for production use ☣️☢️⚠️
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
All of the code is licensed under the Mozilla Public License 2.0 unless otherwise specified.
|
||||||
|
Most of the vault plugin code is based on the vault `builtin/credential/cert` plugin.
|
||||||
|
|
||||||
|
## Build Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ wget -qO - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add -
|
||||||
|
$ sudo bash -c 'echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main" > /etc/apt/sources.list.d/intel-sgx.list'
|
||||||
|
$ sudo apt update
|
||||||
|
$ sudo apt install -y --no-install-recommends \
|
||||||
|
libsgx-headers \
|
||||||
|
libsgx-enclave-common \
|
||||||
|
libsgx-urts \
|
||||||
|
libsgx-dcap-quote-verify \
|
||||||
|
libsgx-dcap-quote-verify-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
`Create` or `Update` via the `${plugin}/tees/$name` endpoint
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "TEE_role_name",
|
||||||
|
"token_policies": "policy1,policy2,...",
|
||||||
|
"types": "sgx",
|
||||||
|
"sgx_mrsigner": "298037d88782e022e019b3020745b78aa40ed95c77da4bf7f3253d3a44c4fd7e",
|
||||||
|
"sgx_mrenclave": "18946b3547d3ca036f4df7b516857e28fd512d69fed3411dc660537912faabf8",
|
||||||
|
"sgx_isv_prodid": 0,
|
||||||
|
"sgx_min_isv_svn": 0,
|
||||||
|
"sgx_allowed_tcb_levels": "Ok,ConfigNeeded,OutOfDate,OutOfDateConfigNeeded,SwHardeningNeeded,ConfigAndSwHardeningNeeded"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* At least one of `sgx_mrsigner` or `sgx_mrenclave` must be set. If both are set, both are used for matching.
|
||||||
|
* `sgx_isv_prodid` is optional and defaults to `0`.
|
||||||
|
* `sgx_min_isv_svn` is optional and defaults to `0`.
|
||||||
|
* `sgx_allowed_tcb_levels` is optional and defaults to `Ok`.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
- Client TEE generates a self-signed TLS client certificate
|
||||||
|
- Client TEE generates an attestation report, which includes the hash of the public key of the client certificate (in case of SGX, a sha256 sum of the public key)
|
||||||
|
- Client TEE fetches all collateral material via e.g. Intel DCAP ([`tee_qv_get_collateral`](https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/4cb5c8b81f126f9aa3ee921d7980a909a9bd676d/QuoteVerification/dcap_quoteverify/inc/sgx_dcap_quoteverify.h#L234-L238))
|
||||||
|
- Client TEE sends POST request with a TLS connection using the client certificate
|
||||||
|
to Vault via the `${plugin}/login` endpoint with the name, attestation report and the attestation collateral material
|
||||||
|
- An optional challenge can be included in the POST request, which is then included in the attestation report of the vault response
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "The name of the TEE role to authenticate against.",
|
||||||
|
"quote": "The quote Base64 encoded.",
|
||||||
|
"collateral": "The collateral Json string encoded.",
|
||||||
|
"challenge": "An optional challenge hex encoded."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The response contains the Vault token and, if a challenge was included,
|
||||||
|
the vault attestation report, which must contain the challenge bytes in the report_data of the quote.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"client_token": "The Vault token.",
|
||||||
|
"....": "...."
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"quote": "The vault quote Base64 encoded.",
|
||||||
|
"collateral": "The vault collateral Json string encoded."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Collateral Json encoding
|
||||||
|
|
||||||
|
See [sgx_ql_lib_common.h](https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/4cb5c8b81f126f9aa3ee921d7980a909a9bd676d/QuoteGeneration/quote_wrapper/common/inc/sgx_ql_lib_common.h#L202-L227)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"major_version": uint16,
|
||||||
|
"minor_version": uint16,
|
||||||
|
"tee_type": uint32,
|
||||||
|
"pck_crl_issuer_chain": []byte,
|
||||||
|
"root_ca_crl": []byte,
|
||||||
|
"pck_crl": []byte,
|
||||||
|
"tcb_info_issuer_chain": []byte,
|
||||||
|
"tcb_info": []byte,
|
||||||
|
"qe_identity_issuer_chain": []byte,
|
||||||
|
"qe_identity": []byte
|
||||||
|
}
|
||||||
|
```
|
70
SECURITY.md
Normal file
70
SECURITY.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
We truly appreciate efforts to discover and disclose security issues responsibly!
|
||||||
|
|
||||||
|
## Vulnerabilities
|
||||||
|
|
||||||
|
We take an impact-first approach instead of a rules-first approach. Therefore, if you believe you found the impactful
|
||||||
|
issue, please email us at
|
||||||
|
[security@matterlabs.dev](mailto:security@matterlabs.dev).
|
||||||
|
|
||||||
|
### PGP Key
|
||||||
|
|
||||||
|
The following PGP key may be used to communicate sensitive information to developers:
|
||||||
|
|
||||||
|
Fingerprint: `5FED B2D0 EA2C 4906 DD66 71D7 A2C5 0B40 CE3C F297`
|
||||||
|
|
||||||
|
```
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQINBGEBmQkBEAD6tlkBEZFMvR8kOgxXX857nC2+oTik6TopJz4uCskuqDaeldMy
|
||||||
|
l+26BBzLkIeO1loS+bzVgnNFJRrGt9gv98MzNEHJVv6D7GsSLlUX/pz7Lxn0J4ry
|
||||||
|
o5XIk3MQTCUBdaXGs6GBLl5Xe8o+zNj4MKd4zjgDLinITNlE/YZCDsXyvYS3YFTQ
|
||||||
|
cwaUTNlawkKgw4BLaEqwB2JuyEhI9wx5X7ibjFL32sWMolYsNAlzFQzM09HCurTn
|
||||||
|
q0DYau9kPJARcEk9/DK2iq0z3gMCQ8iRTDaOWd8IbSP3HxcEoM5j5ZVAlULmjmUE
|
||||||
|
StDaMPLj0Kh01Tesh/j+vjchPXHT0n4zqi1+KOesAOk7SIwLadHfQMTpkU7G2fR1
|
||||||
|
BrA5MtlzY+4Rm6o7qu3dpZ+Nc4iM3FUnaQRpvn4g5nTh8vjG94OCzX8DXWrCKyxx
|
||||||
|
amCs9PLDYOpx84fXYv4frkWpKh2digDSUGKhoHaOSnqyyvu3BNWXBCQZJ20rqEIu
|
||||||
|
sXOQMxWIoWCOOPRRvrHrKDA2hpoKjs3pGsProfpVRzb9702jhWpTfbDp9WjQlFtX
|
||||||
|
2ZIDxlwAxcugClgrp5JiUxvhg2A9lDNwCF7r1e68uNv5usBZQVKPJmnvS2nWgKy8
|
||||||
|
x9oJsnwrEjxwiRHd34UvfMkwY9RENSJ+NoXqBdS7Lwz4m6vgbzq6K56WPQARAQAB
|
||||||
|
tCRaa1N5bmMgU2VjdXJpdHkgPHNlY3VyaXR5QHprc3luYy5pbz6JAk4EEwEKADgW
|
||||||
|
IQRf7bLQ6ixJBt1mcdeixQtAzjzylwUCYQGZCQIbAwULCQgHAgYVCgkICwIEFgID
|
||||||
|
AQIeAQIXgAAKCRCixQtAzjzyl5y8EAC/T3oq88Dak2b+5TlWdU2Gpm6924eAqlMt
|
||||||
|
y1KksDezzNQUlPiCUVllpin2PIjU/S+yzMWKXJA04LoVkEPfPOWjAaavLOjRumxu
|
||||||
|
MR6P2dVUg1InqzYVsJuRhKSpeexzNA5qO2BPM7/I2Iea1IoJPjogGbfXCo0r5kne
|
||||||
|
KU7a5GEa9eDHxpHTsbphQe2vpQ1239mUJrFpzAvILn6jV1tawMn5pNCXbsa8l6l2
|
||||||
|
gtlyQPdOQECy77ZJxrgzaUBcs/RPzUGhwA/qNuvpF0whaCvZuUFMVuCTEu5LZka2
|
||||||
|
I9Rixy+3jqBeONBgb+Fiz5phbiMX33M9JQwGONFaxdvpFTerLwPK2N1T8zcufa01
|
||||||
|
ypzkWGheScFZemBxUwXwK4x579wjsnfrY11w0p1jtDgPTnLlXUA2mom4+7MyXPg0
|
||||||
|
F75qh6vU1pdXaCVkruFgPVtIw+ccw2AxD50iZQ943ZERom9k165dR9+QxOVMXQ4P
|
||||||
|
VUxsFZWvK70/s8TLjsGljvSdSOa85iEUqSqh0AlCwIAxLMiDwh5s/ZgiHoIM6Xih
|
||||||
|
oCpuZyK9p0dn+DF/XkgAZ/S91PesMye3cGm6M5r0tS26aoc2Pk6X37Hha1pRALwo
|
||||||
|
MOHyaGjc/jjcXXxv6o55ALrOrzS0LQmLZ+EHuteCT15kmeY3kqYJ3og62KgiDvew
|
||||||
|
dKHENvg7d7kCDQRhAZleARAA6uD6WfdqGeKV5i170+kLsxR3QGav0qGNAbxpSJyn
|
||||||
|
iHQ8u7mQk3S+ziwN2AAopfBk1je+vCWtEGC3+DWRRfJSjLbtaBG8e6kLP3/cGA75
|
||||||
|
qURz6glTG4nl5fcEAa6B1st0OxjVWiSLX3g/yjz8lznQb9awuRjdeHMnyx5DsJUN
|
||||||
|
d+Iu5KxGupQvKGOMKivSvC8VWk9taaQRpRF+++6stLCDk3ZtlxiopMs3X2jAp6xG
|
||||||
|
sOBbix1cv9BTsfaiL7XDL/gviqBPXYY5L42x6+jnPo5lROfnlLYkWrv6KZr7HD4k
|
||||||
|
tRXeaSwxLD2EkUyb16Jpp0be/ofvBtITGUDDLCGBiaXtx/v8d52MARjsyLJSYloj
|
||||||
|
1yiW01LfAiWHUC4z5jl2T7E7sicrlLH1M8Z6WbuqjdeaYwtfyPA2YCKr/3fn6pIo
|
||||||
|
D+pYaBSESmhA92P+XVaf5y2BZ6Qf8LveDpWwsVGdBGh9T0raA1ooe1GESLjmIjUa
|
||||||
|
z5AeQ/uXL5Md9I6bpMUUJYQiH19RPcFlJriI3phXyyf6Wlkk8oVEeCWyzcmw+x1V
|
||||||
|
deRTvE2x4WIwKGLXRNjin2j1AP7vU2HaNwlPrLijqdyi68+0irRQONoH7Qonr4ca
|
||||||
|
xWgL+pAaa3dWxf0xqK7uZFp4aTVWlr2uXtV/eaUtLmGMCU0jnjb109wg5L0F7WRT
|
||||||
|
PfEAEQEAAYkCNgQYAQoAIBYhBF/tstDqLEkG3WZx16LFC0DOPPKXBQJhAZleAhsM
|
||||||
|
AAoJEKLFC0DOPPKXAAEP/jK7ch9GkoaYlsuqY/aHtxEwVddUDOxjyn3FMDoln85L
|
||||||
|
/n8AmLQb2bcpKSqpaJwMbmfEyr5MDm8xnsBTfx3u6kgaLOWfKxjLQ6PM7kgIMdi4
|
||||||
|
bfaRRuSEI1/R6c/hNpiGnzAeeexldH1we+eH1IVmh4crdat49S2xh7Qlv9ahvgsP
|
||||||
|
LfKl3rJ+aaX/Ok0AHzhvSfhFpPr1gAaGeaRt+rhlZsx2QyG4Ez8p2nDAcAzPiB3T
|
||||||
|
73ENoBIX6mTPfPm1UgrRyFKBqtUzAodz66j3r6ebBlWzIRg8iZenVMAxzjINAsxN
|
||||||
|
w1Bzfgsi5ZespfsSlmEaa7jJkqqDuEcLa2YuiFAue7Euqwz1aGeq1GfTicQioSCb
|
||||||
|
Ur/LGyz2Mj3ykbaP8p5mFVcUN51yQy6OcpvR/W1DfRT9SHFT/bCf9ixsjB2HlZGo
|
||||||
|
uxPJowwqmMgHd755ZzPDUM9YDgLI1yXdcYshObv3Wq537JAxnZJCGRK4Y8SwrMSh
|
||||||
|
8WRxlaM0AGWXiJFIDD4bQPIdnF3X8w0cGWE5Otkb8mMHOT+rFTVlDODwm1zF6oIG
|
||||||
|
PTwfVrpiZBwiUtfJol1exr/MzSPyGoJnYs3cRf2E3O+D1LbcR8w0LbjGuUy38Piz
|
||||||
|
ZO/vCeyJ3JZC5kE8nD+XBA4idwzh0BKEfH9t+WchQ3Up9rxyzLyQamoqt5Xby4pY
|
||||||
|
=xkM3
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
```
|
||||||
|
|
65
flake.lock
generated
Normal file
65
flake.lock
generated
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694102001,
|
||||||
|
"narHash": "sha256-vky6VPK1n1od6vXbqzOXnekrQpTL4hbPAwUhT5J9c9E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "9e21c80adf67ebcb077d75bd5e7d724d21eeafd6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mynixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694694534,
|
||||||
|
"narHash": "sha256-8xmRrzjyT3ZWt+sCegaKRx+PIkB1W69G+CBz3Atf35A=",
|
||||||
|
"owner": "haraldh",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "15d456e58b39f691d4815101723d2a767853f8da",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "haraldh",
|
||||||
|
"ref": "intel-dcap-openssl",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1694499547,
|
||||||
|
"narHash": "sha256-R7xMz1Iia6JthWRHDn36s/E248WB1/je62ovC/dUVKI=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e5f018cf150e29aac26c61dac0790ea023c46b24",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-23.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"mynixpkgs": "mynixpkgs",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
52
flake.nix
Normal file
52
flake.nix
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
description = "vault auth plugin for remote attestation of TEEs";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
# for libsgx-dcap-quote-verify
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
|
||||||
|
mynixpkgs.url =
|
||||||
|
"github:haraldh/nixpkgs/intel-dcap-openssl";
|
||||||
|
gitignore = {
|
||||||
|
url = "github:hercules-ci/gitignore.nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, gitignore, mynixpkgs, ... }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
mypkgs = import mynixpkgs { inherit system; };
|
||||||
|
bin = pkgs.buildGoModule {
|
||||||
|
buildInputs = with mypkgs; [ sgx-sdk libsgx-dcap-quote-verify ];
|
||||||
|
|
||||||
|
CGO_CFLAGS =
|
||||||
|
"-I${mypkgs.libsgx-dcap-quote-verify.dev}/include -I${mypkgs.sgx-sdk}/include";
|
||||||
|
LDFLAGS = "-L${mypkgs.libsgx-dcap-quote-verify.dev}/lib";
|
||||||
|
|
||||||
|
name = "vault-auth-tee";
|
||||||
|
src = gitignore.lib.gitignoreSource ./.;
|
||||||
|
vendorSha256 = "sha256-9l1EVnWIJ+FdIcEic14M/B2BLD/Ffj+dCkompa06KJQ=";
|
||||||
|
};
|
||||||
|
dockerImage = pkgs.dockerTools.buildImage {
|
||||||
|
name = "vault-auth-tee";
|
||||||
|
tag = "latest";
|
||||||
|
copyToRoot = [
|
||||||
|
bin
|
||||||
|
# pkgs.vault
|
||||||
|
];
|
||||||
|
#config = { Cmd = [ "${bin}/bin/vault" ]; };
|
||||||
|
};
|
||||||
|
in
|
||||||
|
with pkgs; {
|
||||||
|
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixpkgs-fmt;
|
||||||
|
packages.x86_64-linux = {
|
||||||
|
inherit bin dockerImage;
|
||||||
|
default = bin;
|
||||||
|
};
|
||||||
|
devShells.x86_64-linux.default = mkShell {
|
||||||
|
inputsFrom = [ bin ];
|
||||||
|
buildInputs = with pkgs; [ dive go_1_19 gotools mypkgs.sgx-sdk mypkgs.libsgx-dcap-quote-verify ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
229
go.mod
Normal file
229
go.mod
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
module github.com/matter-labs/vault-auth-tee
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/hashicorp/go-hclog v1.5.0
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.2
|
||||||
|
github.com/hashicorp/vault v1.14.1
|
||||||
|
github.com/hashicorp/vault/api v1.9.2
|
||||||
|
github.com/hashicorp/vault/sdk v0.9.2-0.20230704151349-7522ca248f90
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
|
golang.org/x/crypto v0.14.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go/compute v1.19.3 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
|
cloud.google.com/go/iam v1.0.1 // indirect
|
||||||
|
cloud.google.com/go/kms v1.10.2 // indirect
|
||||||
|
cloud.google.com/go/monitoring v1.13.0 // indirect
|
||||||
|
github.com/Azure/azure-sdk-for-go v67.2.0+incompatible // indirect
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // 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/BurntSushi/toml v1.2.1 // indirect
|
||||||
|
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
|
||||||
|
github.com/Jeffail/gabs v1.1.1 // indirect
|
||||||
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
|
github.com/Masterminds/semver v1.5.0 // indirect
|
||||||
|
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||||
|
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20230626094100-7e9e0395ebec // indirect
|
||||||
|
github.com/aliyun/alibaba-cloud-sdk-go v1.62.301 // indirect
|
||||||
|
github.com/armon/go-metrics v0.4.1 // indirect
|
||||||
|
github.com/armon/go-radix v1.0.0 // indirect
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||||
|
github.com/aws/aws-sdk-go v1.44.268 // indirect
|
||||||
|
github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||||
|
github.com/boombuler/barcode v1.0.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect
|
||||||
|
github.com/circonus-labs/circonusllhist v0.1.3 // indirect
|
||||||
|
github.com/cloudflare/circl v1.3.3 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba // indirect
|
||||||
|
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect
|
||||||
|
github.com/digitalocean/godo v1.7.5 // indirect
|
||||||
|
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||||
|
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 // indirect
|
||||||
|
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
|
||||||
|
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||||
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-openapi/analysis v0.20.0 // indirect
|
||||||
|
github.com/go-openapi/errors v0.20.1 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||||
|
github.com/go-openapi/loads v0.20.2 // indirect
|
||||||
|
github.com/go-openapi/runtime v0.19.24 // indirect
|
||||||
|
github.com/go-openapi/spec v0.20.3 // indirect
|
||||||
|
github.com/go-openapi/strfmt v0.20.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
|
github.com/go-openapi/validate v0.20.2 // indirect
|
||||||
|
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
|
github.com/go-test/deep v1.1.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||||
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/google/go-metrics-stackdriver v0.2.0 // indirect
|
||||||
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.4 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.9.1 // indirect
|
||||||
|
github.com/gophercloud/gophercloud v0.1.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/eventlogger v0.2.1 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
github.com/hashicorp/go-discover v0.0.0-20210818145131-c573d69da192 // indirect
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/v2 v2.0.9 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.7-1 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7 // indirect
|
||||||
|
github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.7 // indirect
|
||||||
|
github.com/hashicorp/go-memdb v1.3.3 // indirect
|
||||||
|
github.com/hashicorp/go-msgpack v1.1.5 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-plugin v1.4.9 // indirect
|
||||||
|
github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a // indirect
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/awsutil v0.2.3 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/mlock v0.1.3 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/reloadutil v0.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||||
|
github.com/hashicorp/hcp-sdk-go v0.23.0 // indirect
|
||||||
|
github.com/hashicorp/mdns v1.0.4 // indirect
|
||||||
|
github.com/hashicorp/raft v1.3.10 // indirect
|
||||||
|
github.com/hashicorp/raft-autopilot v0.2.0 // indirect
|
||||||
|
github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c // indirect
|
||||||
|
github.com/hashicorp/raft-snapshot v1.0.4 // indirect
|
||||||
|
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect
|
||||||
|
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||||
|
github.com/huandu/xstrings v1.4.0 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.15 // indirect
|
||||||
|
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f // indirect
|
||||||
|
github.com/jefferai/jsonx v1.0.0 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.16.5 // indirect
|
||||||
|
github.com/linode/linodego v0.7.1 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
|
github.com/miekg/dns v1.1.43 // indirect
|
||||||
|
github.com/mitchellh/cli v1.1.2 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect
|
||||||
|
github.com/oklog/run v1.1.0 // indirect
|
||||||
|
github.com/okta/okta-sdk-golang/v2 v2.12.1 // indirect
|
||||||
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
|
github.com/oracle/oci-go-sdk/v60 v60.0.0 // indirect
|
||||||
|
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
|
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||||
|
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||||
|
github.com/pires/go-proxyproto v0.6.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/posener/complete v1.2.3 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
|
github.com/pquerna/otp v1.2.1-0.20191009055518-468c2dd2b58d // indirect
|
||||||
|
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
|
github.com/rboyer/safeio v0.2.1 // indirect
|
||||||
|
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect
|
||||||
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
|
github.com/sasha-s/go-deadlock v0.2.0 // indirect
|
||||||
|
github.com/sethvargo/go-limiter v0.7.1 // indirect
|
||||||
|
github.com/shirou/gopsutil/v3 v3.22.6 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
|
github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect
|
||||||
|
github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go v1.0.162 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||||
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c // indirect
|
||||||
|
github.com/vmware/govmomi v0.18.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
go.etcd.io/bbolt v1.3.7 // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.11.6 // indirect
|
||||||
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
golang.org/x/net v0.17.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.8.0 // indirect
|
||||||
|
golang.org/x/sync v0.2.0 // indirect
|
||||||
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
|
golang.org/x/term v0.13.0 // indirect
|
||||||
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
google.golang.org/api v0.124.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230525154841-bd750badd5c6 // indirect
|
||||||
|
google.golang.org/grpc v1.56.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||||
|
gopkg.in/resty.v1 v1.12.0 // indirect
|
||||||
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
k8s.io/api v0.27.2 // indirect
|
||||||
|
k8s.io/apimachinery v0.27.2 // indirect
|
||||||
|
k8s.io/client-go v0.27.2 // indirect
|
||||||
|
k8s.io/klog/v2 v2.90.1 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
|
||||||
|
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
|
||||||
|
nhooyr.io/websocket v1.8.7 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
|
)
|
36
main.go
Normal file
36
main.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/hashicorp/vault/api"
|
||||||
|
"github.com/hashicorp/vault/sdk/plugin"
|
||||||
|
|
||||||
|
"github.com/matter-labs/vault-auth-tee/tee"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(os.Args[1:])
|
||||||
|
|
||||||
|
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||||
|
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
|
||||||
|
|
||||||
|
if err := plugin.ServeMultiplex(&plugin.ServeOpts{
|
||||||
|
BackendFactoryFunc: tee.Factory,
|
||||||
|
// set the TLSProviderFunc so that the plugin maintains backwards
|
||||||
|
// compatibility with Vault versions that don’t support plugin AutoMTLS
|
||||||
|
TLSProviderFunc: tlsProviderFunc,
|
||||||
|
}); err != nil {
|
||||||
|
logger := hclog.New(&hclog.LoggerOptions{})
|
||||||
|
|
||||||
|
logger.Error("plugin shutting down", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
530
ratee/sgxquote.go
Normal file
530
ratee/sgxquote.go
Normal file
|
@ -0,0 +1,530 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package ratee
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lsgx_dcap_quoteverify -ldl
|
||||||
|
/*
|
||||||
|
#include <stdlib.h> // for malloc/free
|
||||||
|
#include <sgx_dcap_quoteverify.h>
|
||||||
|
#include <sgx_quote.h>
|
||||||
|
|
||||||
|
sgx_ql_qv_supplemental_t *allocSupp() { return (sgx_ql_qv_supplemental_t*) malloc(sizeof(sgx_ql_qv_supplemental_t)); }
|
||||||
|
void freeSupp(sgx_ql_qv_supplemental_t * * p) { free(p); }
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TeeQvCollateral struct {
|
||||||
|
MajorVersion uint16 `json:"major_version"`
|
||||||
|
MinorVersion uint16 `json:"minor_version"`
|
||||||
|
TeeType uint32 `json:"tee_type"`
|
||||||
|
PckCrlIssuerChain []byte `json:"pck_crl_issuer_chain"`
|
||||||
|
RootCaCrl []byte `json:"root_ca_crl"`
|
||||||
|
PckCrl []byte `json:"pck_crl"`
|
||||||
|
TcbInfoIssuerChain []byte `json:"tcb_info_issuer_chain"`
|
||||||
|
TcbInfo []byte `json:"tcb_info"`
|
||||||
|
QeIdentityIssuerChain []byte `json:"qe_identity_issuer_chain"`
|
||||||
|
QeIdentity []byte `json:"qe_identity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Quote struct {
|
||||||
|
Version [2]byte `json:"version"`
|
||||||
|
KeyType [2]byte `json:"key_type"`
|
||||||
|
Reserved [4]byte `json:"reserved"`
|
||||||
|
QeSvn [2]byte `json:"qe_svn"`
|
||||||
|
PceSvn [2]byte `json:"pce_svn"`
|
||||||
|
QeVendorId [16]byte `json:"qe_vendor_id"`
|
||||||
|
UserData [20]byte `json:"user_data"`
|
||||||
|
ReportBody struct {
|
||||||
|
Cpusvn [16]byte `json:"cpusvn"`
|
||||||
|
Miscselect [4]byte `json:"miscselect"`
|
||||||
|
Reserved1 [28]byte `json:"reserved1"`
|
||||||
|
Features [8]byte `json:"features"`
|
||||||
|
Xfrm [8]byte `json:"xfrm"`
|
||||||
|
MrEnclave [32]byte `json:"mrenclave"`
|
||||||
|
Reserved2 [32]byte `json:"reserved2"`
|
||||||
|
MrSigner [32]byte `json:"mrsigner"`
|
||||||
|
Reserved3 [96]byte `json:"reserved3"`
|
||||||
|
IsvProdid [2]byte `json:"isv_prodid"`
|
||||||
|
IsvSvn [2]byte `json:"isv_svn"`
|
||||||
|
Reserved4 [60]byte `json:"reserved4"`
|
||||||
|
ReportData [64]byte `json:"reportdata"`
|
||||||
|
} `json:"report_body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuoteVerificationResult struct {
|
||||||
|
VerificationResult SgxQlQvResult
|
||||||
|
CollateralExpired bool
|
||||||
|
EarliestExpirationDate int64
|
||||||
|
Advisory string
|
||||||
|
Quote Quote
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertCollateral converts TeeQvCollateral to sgx_ql_qve_collateral_t
|
||||||
|
func convertQveCollateral(coll C.sgx_ql_qve_collateral_t) TeeQvCollateral {
|
||||||
|
var data = TeeQvCollateral{
|
||||||
|
TeeType: uint32(coll.tee_type),
|
||||||
|
PckCrlIssuerChain: C.GoBytes(unsafe.Pointer(coll.pck_crl_issuer_chain), C.int(coll.pck_crl_issuer_chain_size)),
|
||||||
|
RootCaCrl: C.GoBytes(unsafe.Pointer(coll.root_ca_crl), C.int(coll.root_ca_crl_size)),
|
||||||
|
PckCrl: C.GoBytes(unsafe.Pointer(coll.pck_crl), C.int(coll.pck_crl_size)),
|
||||||
|
TcbInfoIssuerChain: C.GoBytes(unsafe.Pointer(coll.tcb_info_issuer_chain), C.int(coll.tcb_info_issuer_chain_size)),
|
||||||
|
TcbInfo: C.GoBytes(unsafe.Pointer(coll.tcb_info), C.int(coll.tcb_info_size)),
|
||||||
|
QeIdentityIssuerChain: C.GoBytes(unsafe.Pointer(coll.qe_identity_issuer_chain), C.int(coll.qe_identity_issuer_chain_size)),
|
||||||
|
QeIdentity: C.GoBytes(unsafe.Pointer(coll.qe_identity), C.int(coll.qe_identity_size)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack needed due to unnamed union and struct at the beginning
|
||||||
|
var pver = (*[2]uint16)(unsafe.Pointer(&coll))
|
||||||
|
data.MajorVersion = pver[0]
|
||||||
|
data.MinorVersion = pver[1]
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertCollateral converts TeeQvCollateral to sgx_ql_qve_collateral_t
|
||||||
|
func convertCollateral(coll TeeQvCollateral) C.sgx_ql_qve_collateral_t {
|
||||||
|
var data = C.sgx_ql_qve_collateral_t{
|
||||||
|
tee_type: C.uint32_t(coll.TeeType),
|
||||||
|
pck_crl_issuer_chain: (*C.char)(C.CBytes(coll.PckCrlIssuerChain)),
|
||||||
|
pck_crl_issuer_chain_size: C.uint32_t(len(coll.PckCrlIssuerChain)),
|
||||||
|
root_ca_crl: (*C.char)(C.CBytes(coll.RootCaCrl)),
|
||||||
|
root_ca_crl_size: C.uint32_t(len(coll.RootCaCrl)),
|
||||||
|
pck_crl: (*C.char)(C.CBytes(coll.PckCrl)),
|
||||||
|
pck_crl_size: C.uint32_t(len(coll.PckCrl)),
|
||||||
|
tcb_info_issuer_chain: (*C.char)(C.CBytes(coll.TcbInfoIssuerChain)),
|
||||||
|
tcb_info_issuer_chain_size: C.uint32_t(len(coll.TcbInfoIssuerChain)),
|
||||||
|
tcb_info: (*C.char)(C.CBytes(coll.TcbInfo)),
|
||||||
|
tcb_info_size: C.uint32_t(len(coll.TcbInfo)),
|
||||||
|
qe_identity_issuer_chain: (*C.char)(C.CBytes(coll.QeIdentityIssuerChain)),
|
||||||
|
qe_identity_issuer_chain_size: C.uint32_t(len(coll.QeIdentityIssuerChain)),
|
||||||
|
qe_identity: (*C.char)(C.CBytes(coll.QeIdentity)),
|
||||||
|
qe_identity_size: C.uint32_t(len(coll.QeIdentity)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack needed due to unnamed union and struct at the beginning
|
||||||
|
var pver = (*[2]uint16)(unsafe.Pointer(&data))
|
||||||
|
pver[0] = coll.MajorVersion
|
||||||
|
pver[1] = coll.MinorVersion
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
type SgxQlQvResult uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
SgxQlQvResultOk = SgxQlQvResult(C.SGX_QL_QV_RESULT_OK)
|
||||||
|
SgxQlQvResultConfigNeeded = SgxQlQvResult(C.SGX_QL_QV_RESULT_CONFIG_NEEDED)
|
||||||
|
SgxQlQvResultOutOfDate = SgxQlQvResult(C.SGX_QL_QV_RESULT_OUT_OF_DATE)
|
||||||
|
SgxQlQvResultOutOfDateConfigNeeded = SgxQlQvResult(C.SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED)
|
||||||
|
SgxQlQvResultInvalidSignature = SgxQlQvResult(C.SGX_QL_QV_RESULT_INVALID_SIGNATURE)
|
||||||
|
SgxQlQvResultRevoked = SgxQlQvResult(C.SGX_QL_QV_RESULT_REVOKED)
|
||||||
|
SgxQlQvResultUnspecified = SgxQlQvResult(C.SGX_QL_QV_RESULT_UNSPECIFIED)
|
||||||
|
SgxQlQvResultSwHardeningNeeded = SgxQlQvResult(C.SGX_QL_QV_RESULT_SW_HARDENING_NEEDED)
|
||||||
|
SgxQlQvResultConfigAndSwHardeningNeeded = SgxQlQvResult(C.SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrEmptyReport is returned by VerifyRemoteReport if reportBytes is empty.
|
||||||
|
ErrEmptyReport = errors.New("empty report")
|
||||||
|
ErrSgxQlErrorUnexpected = errors.New("SGX_QL_ERROR_UNEXPECTED")
|
||||||
|
ErrSgxQlErrorInvalidParameter = errors.New("SGX_QL_ERROR_INVALID_PARAMETER")
|
||||||
|
ErrSgxQlErrorOutOfMemory = errors.New("SGX_QL_ERROR_OUT_OF_MEMORY")
|
||||||
|
ErrSgxQlErrorEcdsaIdMismatch = errors.New("SGX_QL_ERROR_ECDSA_ID_MISMATCH")
|
||||||
|
ErrSgxQlPathnameBufferOverflowError = errors.New("SGX_QL_PATHNAME_BUFFER_OVERFLOW_ERROR")
|
||||||
|
ErrSgxQlFileAccessError = errors.New("SGX_QL_FILE_ACCESS_ERROR")
|
||||||
|
ErrSgxQlErrorStoredKey = errors.New("SGX_QL_ERROR_STORED_KEY")
|
||||||
|
ErrSgxQlErrorPubKeyIdMismatch = errors.New("SGX_QL_ERROR_PUB_KEY_ID_MISMATCH")
|
||||||
|
ErrSgxQlErrorInvalidPceSigScheme = errors.New("SGX_QL_ERROR_INVALID_PCE_SIG_SCHEME")
|
||||||
|
ErrSgxQlAttKeyBlobError = errors.New("SGX_QL_ATT_KEY_BLOB_ERROR")
|
||||||
|
ErrSgxQlUnsupportedAttKeyId = errors.New("SGX_QL_UNSUPPORTED_ATT_KEY_ID")
|
||||||
|
ErrSgxQlUnsupportedLoadingPolicy = errors.New("SGX_QL_UNSUPPORTED_LOADING_POLICY")
|
||||||
|
ErrSgxQlInterfaceUnavailable = errors.New("SGX_QL_INTERFACE_UNAVAILABLE")
|
||||||
|
ErrSgxQlPlatformLibUnavailable = errors.New("SGX_QL_PLATFORM_LIB_UNAVAILABLE")
|
||||||
|
ErrSgxQlAttKeyNotInitialized = errors.New("SGX_QL_ATT_KEY_NOT_INITIALIZED")
|
||||||
|
ErrSgxQlAttKeyCertDataInvalid = errors.New("SGX_QL_ATT_KEY_CERT_DATA_INVALID")
|
||||||
|
ErrSgxQlNoPlatformCertData = errors.New("SGX_QL_NO_PLATFORM_CERT_DATA")
|
||||||
|
ErrSgxQlOutOfEpc = errors.New("SGX_QL_OUT_OF_EPC")
|
||||||
|
ErrSgxQlErrorReport = errors.New("SGX_QL_ERROR_REPORT")
|
||||||
|
ErrSgxQlEnclaveLost = errors.New("SGX_QL_ENCLAVE_LOST")
|
||||||
|
ErrSgxQlInvalidReport = errors.New("SGX_QL_INVALID_REPORT")
|
||||||
|
ErrSgxQlEnclaveLoadError = errors.New("SGX_QL_ENCLAVE_LOAD_ERROR")
|
||||||
|
ErrSgxQlUnableToGenerateQeReport = errors.New("SGX_QL_UNABLE_TO_GENERATE_QE_REPORT")
|
||||||
|
ErrSgxQlKeyCertifcationError = errors.New("SGX_QL_KEY_CERTIFCATION_ERROR")
|
||||||
|
ErrSgxQlNetworkError = errors.New("SGX_QL_NETWORK_ERROR")
|
||||||
|
ErrSgxQlMessageError = errors.New("SGX_QL_MESSAGE_ERROR")
|
||||||
|
ErrSgxQlNoQuoteCollateralData = errors.New("SGX_QL_NO_QUOTE_COLLATERAL_DATA")
|
||||||
|
ErrSgxQlQuoteCertificationDataUnsupported = errors.New("SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED")
|
||||||
|
ErrSgxQlQuoteFormatUnsupported = errors.New("SGX_QL_QUOTE_FORMAT_UNSUPPORTED")
|
||||||
|
ErrSgxQlUnableToGenerateReport = errors.New("SGX_QL_UNABLE_TO_GENERATE_REPORT")
|
||||||
|
ErrSgxQlQeReportInvalidSignature = errors.New("SGX_QL_QE_REPORT_INVALID_SIGNATURE")
|
||||||
|
ErrSgxQlQeReportUnsupportedFormat = errors.New("SGX_QL_QE_REPORT_UNSUPPORTED_FORMAT")
|
||||||
|
ErrSgxQlPckCertUnsupportedFormat = errors.New("SGX_QL_PCK_CERT_UNSUPPORTED_FORMAT")
|
||||||
|
ErrSgxQlPckCertChainError = errors.New("SGX_QL_PCK_CERT_CHAIN_ERROR")
|
||||||
|
ErrSgxQlTcbinfoUnsupportedFormat = errors.New("SGX_QL_TCBINFO_UNSUPPORTED_FORMAT")
|
||||||
|
ErrSgxQlTcbinfoMismatch = errors.New("SGX_QL_TCBINFO_MISMATCH")
|
||||||
|
ErrSgxQlQeidentityUnsupportedFormat = errors.New("SGX_QL_QEIDENTITY_UNSUPPORTED_FORMAT")
|
||||||
|
ErrSgxQlQeidentityMismatch = errors.New("SGX_QL_QEIDENTITY_MISMATCH")
|
||||||
|
ErrSgxQlTcbOutOfDate = errors.New("SGX_QL_TCB_OUT_OF_DATE")
|
||||||
|
ErrSgxQlTcbOutOfDateConfigurationNeeded = errors.New("SGX_QL_TCB_OUT_OF_DATE_CONFIGURATION_NEEDED")
|
||||||
|
ErrSgxQlSgxEnclaveIdentityOutOfDate = errors.New("SGX_QL_SGX_ENCLAVE_IDENTITY_OUT_OF_DATE")
|
||||||
|
ErrSgxQlSgxEnclaveReportIsvsvnOutOfDate = errors.New("SGX_QL_SGX_ENCLAVE_REPORT_ISVSVN_OUT_OF_DATE")
|
||||||
|
ErrSgxQlQeIdentityOutOfDate = errors.New("SGX_QL_QE_IDENTITY_OUT_OF_DATE")
|
||||||
|
ErrSgxQlSgxTcbInfoExpired = errors.New("SGX_QL_SGX_TCB_INFO_EXPIRED")
|
||||||
|
ErrSgxQlSgxPckCertChainExpired = errors.New("SGX_QL_SGX_PCK_CERT_CHAIN_EXPIRED")
|
||||||
|
ErrSgxQlSgxCrlExpired = errors.New("SGX_QL_SGX_CRL_EXPIRED")
|
||||||
|
ErrSgxQlSgxSigningCertChainExpired = errors.New("SGX_QL_SGX_SIGNING_CERT_CHAIN_EXPIRED")
|
||||||
|
ErrSgxQlSgxEnclaveIdentityExpired = errors.New("SGX_QL_SGX_ENCLAVE_IDENTITY_EXPIRED")
|
||||||
|
ErrSgxQlPckRevoked = errors.New("SGX_QL_PCK_REVOKED")
|
||||||
|
ErrSgxQlTcbRevoked = errors.New("SGX_QL_TCB_REVOKED")
|
||||||
|
ErrSgxQlTcbConfigurationNeeded = errors.New("SGX_QL_TCB_CONFIGURATION_NEEDED")
|
||||||
|
ErrSgxQlUnableToGetCollateral = errors.New("SGX_QL_UNABLE_TO_GET_COLLATERAL")
|
||||||
|
ErrSgxQlErrorInvalidPrivilege = errors.New("SGX_QL_ERROR_INVALID_PRIVILEGE")
|
||||||
|
ErrSgxQlNoQveIdentityData = errors.New("SGX_QL_NO_QVE_IDENTITY_DATA")
|
||||||
|
ErrSgxQlCrlUnsupportedFormat = errors.New("SGX_QL_CRL_UNSUPPORTED_FORMAT")
|
||||||
|
ErrSgxQlQeidentityChainError = errors.New("SGX_QL_QEIDENTITY_CHAIN_ERROR")
|
||||||
|
ErrSgxQlTcbinfoChainError = errors.New("SGX_QL_TCBINFO_CHAIN_ERROR")
|
||||||
|
ErrSgxQlErrorQvlQveMismatch = errors.New("SGX_QL_ERROR_QVL_QVE_MISMATCH")
|
||||||
|
ErrSgxQlTcbSwHardeningNeeded = errors.New("SGX_QL_TCB_SW_HARDENING_NEEDED")
|
||||||
|
ErrSgxQlTcbConfigurationAndSwHardeningNeeded = errors.New("SGX_QL_TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED")
|
||||||
|
ErrSgxQlUnsupportedMode = errors.New("SGX_QL_UNSUPPORTED_MODE")
|
||||||
|
ErrSgxQlNoDevice = errors.New("SGX_QL_NO_DEVICE")
|
||||||
|
ErrSgxQlServiceUnavailable = errors.New("SGX_QL_SERVICE_UNAVAILABLE")
|
||||||
|
ErrSgxQlNetworkFailure = errors.New("SGX_QL_NETWORK_FAILURE")
|
||||||
|
ErrSgxQlServiceTimeout = errors.New("SGX_QL_SERVICE_TIMEOUT")
|
||||||
|
ErrSgxQlErrorBusy = errors.New("SGX_QL_ERROR_BUSY")
|
||||||
|
ErrSgxQlUnknownMessageResponse = errors.New("SGX_QL_UNKNOWN_MESSAGE_RESPONSE")
|
||||||
|
ErrSgxQlPersistentStorageError = errors.New("SGX_QL_PERSISTENT_STORAGE_ERROR")
|
||||||
|
ErrSgxQlErrorMessageParsingError = errors.New("SGX_QL_ERROR_MESSAGE_PARSING_ERROR")
|
||||||
|
ErrSgxQlPlatformUnknown = errors.New("SGX_QL_PLATFORM_UNKNOWN")
|
||||||
|
ErrSgxQlUnknownApiVersion = errors.New("SGX_QL_UNKNOWN_API_VERSION")
|
||||||
|
ErrSgxQlCertsUnavailable = errors.New("SGX_QL_CERTS_UNAVAILABLE")
|
||||||
|
ErrSgxQlQveidentityMismatch = errors.New("SGX_QL_QVEIDENTITY_MISMATCH")
|
||||||
|
ErrSgxQlQveOutOfDate = errors.New("SGX_QL_QVE_OUT_OF_DATE")
|
||||||
|
ErrSgxQlPswNotAvailable = errors.New("SGX_QL_PSW_NOT_AVAILABLE")
|
||||||
|
ErrSgxQlCollateralVersionNotSupported = errors.New("SGX_QL_COLLATERAL_VERSION_NOT_SUPPORTED")
|
||||||
|
ErrSgxQlTdxModuleMismatch = errors.New("SGX_QL_TDX_MODULE_MISMATCH")
|
||||||
|
ErrSgxQlQeidentityNotFound = errors.New("SGX_QL_QEIDENTITY_NOT_FOUND")
|
||||||
|
ErrSgxQlTcbinfoNotFound = errors.New("SGX_QL_TCBINFO_NOT_FOUND")
|
||||||
|
ErrSgxQlInternalServerError = errors.New("SGX_QL_INTERNAL_SERVER_ERROR")
|
||||||
|
ErrSgxQlSupplementalDataVersionNotSupported = errors.New("SGX_QL_SUPPLEMENTAL_DATA_VERSION_NOT_SUPPORTED")
|
||||||
|
ErrSgxQlRootCaUntrusted = errors.New("SGX_QL_ROOT_CA_UNTRUSTED")
|
||||||
|
ErrSgxQlTcbNotSupported = errors.New("SGX_QL_TCB_NOT_SUPPORTED")
|
||||||
|
)
|
||||||
|
|
||||||
|
// convert an SGX quote library error to a go error
|
||||||
|
func sgx2Error(err uint32) error {
|
||||||
|
switch err {
|
||||||
|
case C.SGX_QL_ERROR_UNEXPECTED:
|
||||||
|
return ErrSgxQlErrorUnexpected
|
||||||
|
case C.SGX_QL_ERROR_INVALID_PARAMETER:
|
||||||
|
return ErrSgxQlErrorInvalidParameter
|
||||||
|
case C.SGX_QL_ERROR_OUT_OF_MEMORY:
|
||||||
|
return ErrSgxQlErrorOutOfMemory
|
||||||
|
case C.SGX_QL_ERROR_ECDSA_ID_MISMATCH:
|
||||||
|
return ErrSgxQlErrorEcdsaIdMismatch
|
||||||
|
case C.SGX_QL_PATHNAME_BUFFER_OVERFLOW_ERROR:
|
||||||
|
return ErrSgxQlPathnameBufferOverflowError
|
||||||
|
case C.SGX_QL_FILE_ACCESS_ERROR:
|
||||||
|
return ErrSgxQlFileAccessError
|
||||||
|
case C.SGX_QL_ERROR_STORED_KEY:
|
||||||
|
return ErrSgxQlErrorStoredKey
|
||||||
|
case C.SGX_QL_ERROR_PUB_KEY_ID_MISMATCH:
|
||||||
|
return ErrSgxQlErrorPubKeyIdMismatch
|
||||||
|
case C.SGX_QL_ERROR_INVALID_PCE_SIG_SCHEME:
|
||||||
|
return ErrSgxQlErrorInvalidPceSigScheme
|
||||||
|
case C.SGX_QL_ATT_KEY_BLOB_ERROR:
|
||||||
|
return ErrSgxQlAttKeyBlobError
|
||||||
|
case C.SGX_QL_UNSUPPORTED_ATT_KEY_ID:
|
||||||
|
return ErrSgxQlUnsupportedAttKeyId
|
||||||
|
case C.SGX_QL_UNSUPPORTED_LOADING_POLICY:
|
||||||
|
return ErrSgxQlUnsupportedLoadingPolicy
|
||||||
|
case C.SGX_QL_INTERFACE_UNAVAILABLE:
|
||||||
|
return ErrSgxQlInterfaceUnavailable
|
||||||
|
case C.SGX_QL_PLATFORM_LIB_UNAVAILABLE:
|
||||||
|
return ErrSgxQlPlatformLibUnavailable
|
||||||
|
case C.SGX_QL_ATT_KEY_NOT_INITIALIZED:
|
||||||
|
return ErrSgxQlAttKeyNotInitialized
|
||||||
|
case C.SGX_QL_ATT_KEY_CERT_DATA_INVALID:
|
||||||
|
return ErrSgxQlAttKeyCertDataInvalid
|
||||||
|
case C.SGX_QL_NO_PLATFORM_CERT_DATA:
|
||||||
|
return ErrSgxQlNoPlatformCertData
|
||||||
|
case C.SGX_QL_OUT_OF_EPC:
|
||||||
|
return ErrSgxQlOutOfEpc
|
||||||
|
case C.SGX_QL_ERROR_REPORT:
|
||||||
|
return ErrSgxQlErrorReport
|
||||||
|
case C.SGX_QL_ENCLAVE_LOST:
|
||||||
|
return ErrSgxQlEnclaveLost
|
||||||
|
case C.SGX_QL_INVALID_REPORT:
|
||||||
|
return ErrSgxQlInvalidReport
|
||||||
|
case C.SGX_QL_ENCLAVE_LOAD_ERROR:
|
||||||
|
return ErrSgxQlEnclaveLoadError
|
||||||
|
case C.SGX_QL_UNABLE_TO_GENERATE_QE_REPORT:
|
||||||
|
return ErrSgxQlUnableToGenerateQeReport
|
||||||
|
case C.SGX_QL_KEY_CERTIFCATION_ERROR:
|
||||||
|
return ErrSgxQlKeyCertifcationError
|
||||||
|
case C.SGX_QL_NETWORK_ERROR:
|
||||||
|
return ErrSgxQlNetworkError
|
||||||
|
case C.SGX_QL_MESSAGE_ERROR:
|
||||||
|
return ErrSgxQlMessageError
|
||||||
|
case C.SGX_QL_NO_QUOTE_COLLATERAL_DATA:
|
||||||
|
return ErrSgxQlNoQuoteCollateralData
|
||||||
|
case C.SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED:
|
||||||
|
return ErrSgxQlQuoteCertificationDataUnsupported
|
||||||
|
case C.SGX_QL_QUOTE_FORMAT_UNSUPPORTED:
|
||||||
|
return ErrSgxQlQuoteFormatUnsupported
|
||||||
|
case C.SGX_QL_UNABLE_TO_GENERATE_REPORT:
|
||||||
|
return ErrSgxQlUnableToGenerateReport
|
||||||
|
case C.SGX_QL_QE_REPORT_INVALID_SIGNATURE:
|
||||||
|
return ErrSgxQlQeReportInvalidSignature
|
||||||
|
case C.SGX_QL_QE_REPORT_UNSUPPORTED_FORMAT:
|
||||||
|
return ErrSgxQlQeReportUnsupportedFormat
|
||||||
|
case C.SGX_QL_PCK_CERT_UNSUPPORTED_FORMAT:
|
||||||
|
return ErrSgxQlPckCertUnsupportedFormat
|
||||||
|
case C.SGX_QL_PCK_CERT_CHAIN_ERROR:
|
||||||
|
return ErrSgxQlPckCertChainError
|
||||||
|
case C.SGX_QL_TCBINFO_UNSUPPORTED_FORMAT:
|
||||||
|
return ErrSgxQlTcbinfoUnsupportedFormat
|
||||||
|
case C.SGX_QL_TCBINFO_MISMATCH:
|
||||||
|
return ErrSgxQlTcbinfoMismatch
|
||||||
|
case C.SGX_QL_QEIDENTITY_UNSUPPORTED_FORMAT:
|
||||||
|
return ErrSgxQlQeidentityUnsupportedFormat
|
||||||
|
case C.SGX_QL_QEIDENTITY_MISMATCH:
|
||||||
|
return ErrSgxQlQeidentityMismatch
|
||||||
|
case C.SGX_QL_TCB_OUT_OF_DATE:
|
||||||
|
return ErrSgxQlTcbOutOfDate
|
||||||
|
case C.SGX_QL_TCB_OUT_OF_DATE_CONFIGURATION_NEEDED:
|
||||||
|
return ErrSgxQlTcbOutOfDateConfigurationNeeded
|
||||||
|
case C.SGX_QL_SGX_ENCLAVE_IDENTITY_OUT_OF_DATE:
|
||||||
|
return ErrSgxQlSgxEnclaveIdentityOutOfDate
|
||||||
|
case C.SGX_QL_SGX_ENCLAVE_REPORT_ISVSVN_OUT_OF_DATE:
|
||||||
|
return ErrSgxQlSgxEnclaveReportIsvsvnOutOfDate
|
||||||
|
case C.SGX_QL_QE_IDENTITY_OUT_OF_DATE:
|
||||||
|
return ErrSgxQlQeIdentityOutOfDate
|
||||||
|
case C.SGX_QL_SGX_TCB_INFO_EXPIRED:
|
||||||
|
return ErrSgxQlSgxTcbInfoExpired
|
||||||
|
case C.SGX_QL_SGX_PCK_CERT_CHAIN_EXPIRED:
|
||||||
|
return ErrSgxQlSgxPckCertChainExpired
|
||||||
|
case C.SGX_QL_SGX_CRL_EXPIRED:
|
||||||
|
return ErrSgxQlSgxCrlExpired
|
||||||
|
case C.SGX_QL_SGX_SIGNING_CERT_CHAIN_EXPIRED:
|
||||||
|
return ErrSgxQlSgxSigningCertChainExpired
|
||||||
|
case C.SGX_QL_SGX_ENCLAVE_IDENTITY_EXPIRED:
|
||||||
|
return ErrSgxQlSgxEnclaveIdentityExpired
|
||||||
|
case C.SGX_QL_PCK_REVOKED:
|
||||||
|
return ErrSgxQlPckRevoked
|
||||||
|
case C.SGX_QL_TCB_REVOKED:
|
||||||
|
return ErrSgxQlTcbRevoked
|
||||||
|
case C.SGX_QL_TCB_CONFIGURATION_NEEDED:
|
||||||
|
return ErrSgxQlTcbConfigurationNeeded
|
||||||
|
case C.SGX_QL_UNABLE_TO_GET_COLLATERAL:
|
||||||
|
return ErrSgxQlUnableToGetCollateral
|
||||||
|
case C.SGX_QL_ERROR_INVALID_PRIVILEGE:
|
||||||
|
return ErrSgxQlErrorInvalidPrivilege
|
||||||
|
case C.SGX_QL_NO_QVE_IDENTITY_DATA:
|
||||||
|
return ErrSgxQlNoQveIdentityData
|
||||||
|
case C.SGX_QL_CRL_UNSUPPORTED_FORMAT:
|
||||||
|
return ErrSgxQlCrlUnsupportedFormat
|
||||||
|
case C.SGX_QL_QEIDENTITY_CHAIN_ERROR:
|
||||||
|
return ErrSgxQlQeidentityChainError
|
||||||
|
case C.SGX_QL_TCBINFO_CHAIN_ERROR:
|
||||||
|
return ErrSgxQlTcbinfoChainError
|
||||||
|
case C.SGX_QL_ERROR_QVL_QVE_MISMATCH:
|
||||||
|
return ErrSgxQlErrorQvlQveMismatch
|
||||||
|
case C.SGX_QL_TCB_SW_HARDENING_NEEDED:
|
||||||
|
return ErrSgxQlTcbSwHardeningNeeded
|
||||||
|
case C.SGX_QL_TCB_CONFIGURATION_AND_SW_HARDENING_NEEDED:
|
||||||
|
return ErrSgxQlTcbConfigurationAndSwHardeningNeeded
|
||||||
|
case C.SGX_QL_UNSUPPORTED_MODE:
|
||||||
|
return ErrSgxQlUnsupportedMode
|
||||||
|
case C.SGX_QL_NO_DEVICE:
|
||||||
|
return ErrSgxQlNoDevice
|
||||||
|
case C.SGX_QL_SERVICE_UNAVAILABLE:
|
||||||
|
return ErrSgxQlServiceUnavailable
|
||||||
|
case C.SGX_QL_NETWORK_FAILURE:
|
||||||
|
return ErrSgxQlNetworkFailure
|
||||||
|
case C.SGX_QL_SERVICE_TIMEOUT:
|
||||||
|
return ErrSgxQlServiceTimeout
|
||||||
|
case C.SGX_QL_ERROR_BUSY:
|
||||||
|
return ErrSgxQlErrorBusy
|
||||||
|
case C.SGX_QL_UNKNOWN_MESSAGE_RESPONSE:
|
||||||
|
return ErrSgxQlUnknownMessageResponse
|
||||||
|
case C.SGX_QL_PERSISTENT_STORAGE_ERROR:
|
||||||
|
return ErrSgxQlPersistentStorageError
|
||||||
|
case C.SGX_QL_ERROR_MESSAGE_PARSING_ERROR:
|
||||||
|
return ErrSgxQlErrorMessageParsingError
|
||||||
|
case C.SGX_QL_PLATFORM_UNKNOWN:
|
||||||
|
return ErrSgxQlPlatformUnknown
|
||||||
|
case C.SGX_QL_UNKNOWN_API_VERSION:
|
||||||
|
return ErrSgxQlUnknownApiVersion
|
||||||
|
case C.SGX_QL_CERTS_UNAVAILABLE:
|
||||||
|
return ErrSgxQlCertsUnavailable
|
||||||
|
case C.SGX_QL_QVEIDENTITY_MISMATCH:
|
||||||
|
return ErrSgxQlQveidentityMismatch
|
||||||
|
case C.SGX_QL_QVE_OUT_OF_DATE:
|
||||||
|
return ErrSgxQlQveOutOfDate
|
||||||
|
case C.SGX_QL_PSW_NOT_AVAILABLE:
|
||||||
|
return ErrSgxQlPswNotAvailable
|
||||||
|
case C.SGX_QL_COLLATERAL_VERSION_NOT_SUPPORTED:
|
||||||
|
return ErrSgxQlCollateralVersionNotSupported
|
||||||
|
case C.SGX_QL_TDX_MODULE_MISMATCH:
|
||||||
|
return ErrSgxQlTdxModuleMismatch
|
||||||
|
case C.SGX_QL_QEIDENTITY_NOT_FOUND:
|
||||||
|
return ErrSgxQlQeidentityNotFound
|
||||||
|
case C.SGX_QL_TCBINFO_NOT_FOUND:
|
||||||
|
return ErrSgxQlTcbinfoNotFound
|
||||||
|
case C.SGX_QL_INTERNAL_SERVER_ERROR:
|
||||||
|
return ErrSgxQlInternalServerError
|
||||||
|
case C.SGX_QL_SUPPLEMENTAL_DATA_VERSION_NOT_SUPPORTED:
|
||||||
|
return ErrSgxQlSupplementalDataVersionNotSupported
|
||||||
|
case C.SGX_QL_ROOT_CA_UNTRUSTED:
|
||||||
|
return ErrSgxQlRootCaUntrusted
|
||||||
|
case C.SGX_QL_TCB_NOT_SUPPORTED:
|
||||||
|
return ErrSgxQlTcbNotSupported
|
||||||
|
}
|
||||||
|
return ErrSgxQlErrorUnexpected
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyRemoteReportSGXCollateral(reportBytes []byte, pQuoteCollateral *C.sgx_ql_qve_collateral_t, expirationCheckDate int64) (*QuoteVerificationResult, error) {
|
||||||
|
if len(reportBytes) == 0 {
|
||||||
|
return nil, ErrEmptyReport
|
||||||
|
}
|
||||||
|
|
||||||
|
var collateralExpirationStatus uint32
|
||||||
|
var quoteVerificationResult uint32
|
||||||
|
|
||||||
|
var pSuppData = C.allocSupp()
|
||||||
|
var suppDataDesc = C.tee_supp_data_descriptor_t{
|
||||||
|
major_version: 0,
|
||||||
|
data_size: C.uint(unsafe.Sizeof(C.sgx_ql_qv_supplemental_t{})),
|
||||||
|
p_data: (*C.uchar)(unsafe.Pointer(pSuppData)),
|
||||||
|
}
|
||||||
|
|
||||||
|
var pReportBytes = C.CBytes(reportBytes)
|
||||||
|
|
||||||
|
res := uint32(C.tee_verify_quote(
|
||||||
|
(*C.uint8_t)(pReportBytes),
|
||||||
|
C.uint32_t(len(reportBytes)),
|
||||||
|
(*C.uint8_t)(unsafe.Pointer(pQuoteCollateral)),
|
||||||
|
C.time_t(expirationCheckDate),
|
||||||
|
(*C.uint32_t)(&collateralExpirationStatus),
|
||||||
|
(*C.sgx_ql_qv_result_t)("eVerificationResult),
|
||||||
|
nil,
|
||||||
|
&suppDataDesc,
|
||||||
|
))
|
||||||
|
|
||||||
|
if res != C.SGX_QL_SUCCESS {
|
||||||
|
return nil, sgx2Error(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
var quote = Quote{}
|
||||||
|
var byteReader = bytes.NewReader(reportBytes)
|
||||||
|
err := binary.Read(byteReader, binary.BigEndian, "e)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = QuoteVerificationResult{
|
||||||
|
VerificationResult: SgxQlQvResult(quoteVerificationResult),
|
||||||
|
CollateralExpired: collateralExpirationStatus != 0,
|
||||||
|
EarliestExpirationDate: int64(pSuppData.earliest_expiration_date),
|
||||||
|
Quote: quote,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SgxVerifyRemoteReport verifies the SGX attestation report.
|
||||||
|
// It needs to connect to servers to collect the collateral material.
|
||||||
|
func SgxVerifyRemoteReport(reportBytes []byte, expirationCheckDate int64) (*QuoteVerificationResult, error) {
|
||||||
|
if len(reportBytes) == 0 {
|
||||||
|
return nil, ErrEmptyReport
|
||||||
|
}
|
||||||
|
|
||||||
|
var pQuoteCollateral *C.uint8_t = nil
|
||||||
|
var collateralSize C.uint32_t
|
||||||
|
res := uint32(C.tee_qv_get_collateral(
|
||||||
|
(*C.uint8_t)(&reportBytes[0]),
|
||||||
|
C.uint32_t(len(reportBytes)),
|
||||||
|
(**C.uint8_t)(&pQuoteCollateral),
|
||||||
|
(*C.uint32_t)(&collateralSize),
|
||||||
|
))
|
||||||
|
|
||||||
|
defer C.tee_qv_free_collateral(pQuoteCollateral)
|
||||||
|
|
||||||
|
if res != C.SGX_QL_SUCCESS {
|
||||||
|
return nil, sgx2Error(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
return verifyRemoteReportSGXCollateral(reportBytes, (*C.sgx_ql_qve_collateral_t)(unsafe.Pointer(pQuoteCollateral)), expirationCheckDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SgxVerifyRemoteReportCollateral verifies the report along with the collateral material.
|
||||||
|
// It does not need to start an SGX enclave, nor does it need to connect to any server.
|
||||||
|
func SgxVerifyRemoteReportCollateral(reportBytes []byte, collateral TeeQvCollateral, expirationCheckDate int64) (*QuoteVerificationResult, error) {
|
||||||
|
var quoteCollateral = convertCollateral(collateral)
|
||||||
|
|
||||||
|
return verifyRemoteReportSGXCollateral(reportBytes, "eCollateral, expirationCheckDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SgxGetCollateral(reportBytes []byte) (*TeeQvCollateral, error) {
|
||||||
|
var pQuoteCollateral *C.uint8_t = nil
|
||||||
|
var collateralSize C.uint32_t
|
||||||
|
res := uint32(C.tee_qv_get_collateral(
|
||||||
|
(*C.uint8_t)(&reportBytes[0]),
|
||||||
|
C.uint32_t(len(reportBytes)),
|
||||||
|
(**C.uint8_t)(&pQuoteCollateral),
|
||||||
|
(*C.uint32_t)(&collateralSize),
|
||||||
|
))
|
||||||
|
|
||||||
|
defer C.tee_qv_free_collateral(pQuoteCollateral)
|
||||||
|
|
||||||
|
if res != C.SGX_QL_SUCCESS {
|
||||||
|
return nil, sgx2Error(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
coll := convertQveCollateral(*(*C.sgx_ql_qve_collateral_t)(unsafe.Pointer(pQuoteCollateral)))
|
||||||
|
|
||||||
|
return &coll, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sgxGramineGetQuote(reportData []byte) ([]byte, error) {
|
||||||
|
// open "/dev/attestation/user_report_data" and write reportData
|
||||||
|
if err := os.WriteFile("/dev/attestation/user_report_data", reportData, 0600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// open "/dev/attestation/quote" and read quote
|
||||||
|
quote, err := os.ReadFile("/dev/attestation/quote")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return quote, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SgxGetQuote(reportData []byte) ([]byte, error) {
|
||||||
|
if len(reportData) > 64 {
|
||||||
|
reportData = reportData[:64]
|
||||||
|
}
|
||||||
|
if len(reportData) < 64 {
|
||||||
|
reportData = append(reportData, make([]byte, 64-len(reportData))...)
|
||||||
|
}
|
||||||
|
// only support Gramine for now
|
||||||
|
|
||||||
|
// check if "/dev/attestation/user_report_data" and "/dev/attestation/quote" exist
|
||||||
|
if _, err := os.Stat("/dev/attestation/user_report_data"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat("/dev/attestation/quote"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sgxGramineGetQuote(reportData)
|
||||||
|
}
|
1491
ratee/sgxquote_test.go
Normal file
1491
ratee/sgxquote_test.go
Normal file
File diff suppressed because one or more lines are too long
6
renovate.json
Normal file
6
renovate.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:base"
|
||||||
|
]
|
||||||
|
}
|
61
tee/backend.go
Normal file
61
tee/backend.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package tee
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/matter-labs/vault-auth-tee/version"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
)
|
||||||
|
|
||||||
|
const operationPrefixTee = "tee"
|
||||||
|
|
||||||
|
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
b := Backend()
|
||||||
|
if err := b.Setup(ctx, conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Backend() *backend {
|
||||||
|
var b backend
|
||||||
|
b.Backend = &framework.Backend{
|
||||||
|
Help: backendHelp,
|
||||||
|
PathsSpecial: &logical.Paths{
|
||||||
|
Unauthenticated: []string{
|
||||||
|
"login",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Paths: []*framework.Path{
|
||||||
|
pathInfo(&b),
|
||||||
|
pathLogin(&b),
|
||||||
|
pathListTees(&b),
|
||||||
|
pathTees(&b),
|
||||||
|
},
|
||||||
|
AuthRenew: b.loginPathWrapper(b.pathLoginRenew),
|
||||||
|
BackendType: logical.TypeCredential,
|
||||||
|
RunningVersion: "v" + version.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
type backend struct {
|
||||||
|
*framework.Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
const backendHelp = `
|
||||||
|
The "tee" credential provider allows authentication using
|
||||||
|
remote attestation verification together with TLS client certificates.
|
||||||
|
A client connects to Vault and uses the "login" endpoint to generate a client token.
|
||||||
|
|
||||||
|
Trusted execution environments are configured using the "tees/" endpoint
|
||||||
|
by a user with root access. Authentication is then done
|
||||||
|
by supplying the attestation report, the attestation collateral
|
||||||
|
and the client certificate for "login".
|
||||||
|
`
|
284
tee/backend_test.go
Normal file
284
tee/backend_test.go
Normal file
File diff suppressed because one or more lines are too long
40
tee/path_info.go
Normal file
40
tee/path_info.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package tee
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
|
||||||
|
"github.com/matter-labs/vault-auth-tee/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pathInfo(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "info",
|
||||||
|
HelpSynopsis: "Display information about the plugin",
|
||||||
|
HelpDescription: `
|
||||||
|
|
||||||
|
Displays information about the plugin, such as the plugin version and where to
|
||||||
|
get help.
|
||||||
|
|
||||||
|
`,
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.ReadOperation: b.pathInfoRead,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathInfoRead corresponds to READ auth/tee/info.
|
||||||
|
func (b *backend) pathInfoRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
||||||
|
return &logical.Response{
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"name": version.Name,
|
||||||
|
"version": version.Version,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
417
tee/path_login.go
Normal file
417
tee/path_login.go
Normal file
|
@ -0,0 +1,417 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package tee
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/cidrutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/policyutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
|
||||||
|
"github.com/matter-labs/vault-auth-tee/ratee"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeNowFunc = time.Now
|
||||||
|
|
||||||
|
func pathLogin(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "login",
|
||||||
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
|
OperationPrefix: operationPrefixTee,
|
||||||
|
OperationVerb: "login",
|
||||||
|
},
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"name": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "The name of the tee role to authenticate against.",
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "The type of the TEE.",
|
||||||
|
},
|
||||||
|
"quote": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "The quote Base64 encoded.",
|
||||||
|
},
|
||||||
|
"collateral": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "The collateral Json encoded.",
|
||||||
|
},
|
||||||
|
"challenge": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "Hex encoded bytes to include in the attestation report of the vault quote",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.UpdateOperation: b.loginPathWrapper(b.pathLogin),
|
||||||
|
logical.AliasLookaheadOperation: b.pathLoginAliasLookahead,
|
||||||
|
logical.ResolveRoleOperation: b.loginPathWrapper(b.pathLoginResolveRole),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) loginPathWrapper(wrappedOp func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error)) framework.OperationFunc {
|
||||||
|
return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
return wrappedOp(ctx, req, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathLoginResolveRole(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
quoteBase64 := data.Get("quote").(string)
|
||||||
|
if quoteBase64 == "" {
|
||||||
|
return nil, fmt.Errorf("missing quote")
|
||||||
|
}
|
||||||
|
|
||||||
|
quoteBytes, err := base64.StdEncoding.DecodeString(quoteBase64)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("quote decode error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var quote = ratee.Quote{}
|
||||||
|
var byteReader = bytes.NewReader(quoteBytes)
|
||||||
|
err = binary.Read(byteReader, binary.BigEndian, "e)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("quote decode error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := req.Storage.List(ctx, "tee/")
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("no TEE was matched by this request"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rb := quote.ReportBody
|
||||||
|
|
||||||
|
mrSignerHex := hex.EncodeToString(rb.MrSigner[:])
|
||||||
|
mrEnclaveHex := hex.EncodeToString(rb.MrEnclave[:])
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
entry, err := b.Tee(ctx, req.Storage, strings.TrimPrefix(name, "tee/"))
|
||||||
|
if err != nil {
|
||||||
|
b.Logger().Error("failed to load trusted tee", "name", name, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry == nil {
|
||||||
|
// This could happen when the name was provided and the tee doesn't exist,
|
||||||
|
// or just if between the LIST and the GET the tee was deleted.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.SgxMrsigner != "" && entry.SgxMrsigner != mrSignerHex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry.SgxMrenclave != "" && entry.SgxMrenclave != mrEnclaveHex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry.SgxIsvProdid != int(binary.LittleEndian.Uint16(rb.IsvProdid[:])) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return logical.ResolveRoleResponse(name)
|
||||||
|
}
|
||||||
|
return logical.ErrorResponse("no TEE was matched by this request"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathLoginAliasLookahead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
if name == "" {
|
||||||
|
return nil, fmt.Errorf("missing name")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &logical.Response{
|
||||||
|
Auth: &logical.Auth{
|
||||||
|
Alias: &logical.Alias{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashPublicKey256(pub interface{}) ([]byte, error) {
|
||||||
|
pubBytes, err := x509.MarshalPKIXPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := sha256.Sum256(pubBytes)
|
||||||
|
return result[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Contains[T comparable](s []T, e T) bool {
|
||||||
|
for _, v := range s {
|
||||||
|
if v == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
name := data.Get("name").(string)
|
||||||
|
if name == "" {
|
||||||
|
return nil, fmt.Errorf("missing name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow constraining the login request to a single TeeEntry
|
||||||
|
entry, err := b.Tee(ctx, req.Storage, strings.TrimPrefix(name, "tee/"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("no TEE matching for this login name; additionally got errors during verification: %v", err)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry == nil {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("no TEE matching for this login name")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the connection state
|
||||||
|
if req.Connection == nil || req.Connection.ConnState == nil {
|
||||||
|
return logical.ErrorResponse("tls connection required"), nil
|
||||||
|
}
|
||||||
|
connState := req.Connection.ConnState
|
||||||
|
|
||||||
|
if connState.PeerCertificates == nil || len(connState.PeerCertificates) == 0 {
|
||||||
|
return logical.ErrorResponse("client certificate must be supplied"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCert := connState.PeerCertificates[0]
|
||||||
|
|
||||||
|
// verify self-signed certificate
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
roots.AddCert(clientCert)
|
||||||
|
_, err = clientCert.Verify(x509.VerifyOptions{Roots: roots})
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("client certificate must be self-signed"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entry.TokenBoundCIDRs) > 0 {
|
||||||
|
if req.Connection == nil {
|
||||||
|
b.Logger().Warn("token bound CIDRs found but no connection information available for validation")
|
||||||
|
return nil, logical.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
if !cidrutil.RemoteAddrIsOk(req.Connection.RemoteAddr, entry.TokenBoundCIDRs) {
|
||||||
|
return nil, logical.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientCert.PublicKey == nil {
|
||||||
|
return logical.ErrorResponse("no public key found in client certificate"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hashClientPk, err := hashPublicKey256(clientCert.PublicKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("error hashing public key"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
teeType := data.Get("type").(string)
|
||||||
|
|
||||||
|
if _, ok := entry.Types[teeType]; !ok {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("type `%s` not supported for `%s`", teeType, name)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
quote := data.Get("quote").(string)
|
||||||
|
quoteBytes, err := base64.StdEncoding.DecodeString(quote)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("quote decode error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a quick check of the quote before doing the expensive verification
|
||||||
|
var quoteStart = ratee.Quote{}
|
||||||
|
var byteReader = bytes.NewReader(quoteBytes)
|
||||||
|
err = binary.Read(byteReader, binary.BigEndian, "eStart)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("quote decode error"), nil
|
||||||
|
}
|
||||||
|
reportBody := quoteStart.ReportBody
|
||||||
|
|
||||||
|
if !bytes.Equal(reportBody.ReportData[:32], hashClientPk) {
|
||||||
|
return logical.ErrorResponse("client certificate's hashed public key not in report data of attestation quote report"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mrSignerHex := hex.EncodeToString(reportBody.MrSigner[:])
|
||||||
|
mrEnclaveHex := hex.EncodeToString(reportBody.MrEnclave[:])
|
||||||
|
|
||||||
|
if entry.SgxMrsigner != "" && entry.SgxMrsigner != mrSignerHex {
|
||||||
|
return logical.ErrorResponse("`sgx_mrsigner` does not match"), nil
|
||||||
|
}
|
||||||
|
if entry.SgxMrenclave != "" && entry.SgxMrenclave != mrEnclaveHex {
|
||||||
|
return logical.ErrorResponse("`sgx_mrenclave` does not match"), nil
|
||||||
|
}
|
||||||
|
if entry.SgxIsvProdid != int(binary.LittleEndian.Uint16(reportBody.IsvProdid[:])) {
|
||||||
|
return logical.ErrorResponse("`sgx_isv_prodid` does not match"), nil
|
||||||
|
}
|
||||||
|
if entry.SgxMinIsvSvn > int(binary.LittleEndian.Uint16(reportBody.IsvSvn[:])) {
|
||||||
|
return logical.ErrorResponse("`sgx_isv_svn` too low"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the collateral
|
||||||
|
jsonCollateralBlob := data.Get("collateral").(string)
|
||||||
|
var collateral ratee.TeeQvCollateral
|
||||||
|
err = json.Unmarshal([]byte(jsonCollateralBlob), &collateral)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("collateral unmarshal error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the actual remote attestation verification
|
||||||
|
result, err := ratee.SgxVerifyRemoteReportCollateral(quoteBytes, collateral, timeNowFunc().Unix())
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("sgx verify error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.CollateralExpired {
|
||||||
|
return logical.ErrorResponse("collateral expired"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.VerificationResult != ratee.SgxQlQvResultOk {
|
||||||
|
if entry.SgxAllowedTcbLevels[result.VerificationResult] != true {
|
||||||
|
return logical.ErrorResponse("invalid TCB state %v", result.VerificationResult), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skid := base64.StdEncoding.EncodeToString(clientCert.SubjectKeyId)
|
||||||
|
akid := base64.StdEncoding.EncodeToString(clientCert.AuthorityKeyId)
|
||||||
|
pkid := base64.StdEncoding.EncodeToString(hashClientPk)
|
||||||
|
|
||||||
|
expirationDate := time.Unix(result.EarliestExpirationDate, 0)
|
||||||
|
metadata := map[string]string{
|
||||||
|
"tee_name": entry.Name,
|
||||||
|
"collateral_expiration_date": expirationDate.Format(time.RFC3339),
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := &logical.Auth{
|
||||||
|
InternalData: map[string]interface{}{
|
||||||
|
"subject_key_id": skid,
|
||||||
|
"authority_key_id": akid,
|
||||||
|
"hash_public_key": pkid,
|
||||||
|
},
|
||||||
|
Alias: &logical.Alias{
|
||||||
|
Name: entry.Name,
|
||||||
|
},
|
||||||
|
DisplayName: entry.DisplayName,
|
||||||
|
Metadata: metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.PopulateTokenAuth(auth)
|
||||||
|
|
||||||
|
now := timeNowFunc()
|
||||||
|
|
||||||
|
if !now.Add(auth.TTL).After(expirationDate) {
|
||||||
|
auth.TTL = expirationDate.Sub(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !now.Add(auth.MaxTTL).After(expirationDate) {
|
||||||
|
auth.MaxTTL = expirationDate.Sub(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
respData := make(map[string]interface{})
|
||||||
|
|
||||||
|
challenge := data.Get("challenge").(string)
|
||||||
|
if challenge != "" {
|
||||||
|
challengeBytes, err := hex.DecodeString(challenge)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("challenge decode error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ourQuote, err := ratee.SgxGetQuote(challengeBytes)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("vault quote error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
quoteBase64 := base64.StdEncoding.EncodeToString(ourQuote)
|
||||||
|
|
||||||
|
respData["quote"] = quoteBase64
|
||||||
|
|
||||||
|
collateral, err := ratee.SgxGetCollateral(ourQuote)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("vault collateral error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
collateralJson, err := json.Marshal(collateral)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("vault collateral json error"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
respData["collateral"] = string(collateralJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &logical.Response{
|
||||||
|
Auth: auth,
|
||||||
|
Data: respData,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
clientCerts := req.Connection.ConnState.PeerCertificates
|
||||||
|
if len(clientCerts) == 0 {
|
||||||
|
return logical.ErrorResponse("no client certificate found"), nil
|
||||||
|
}
|
||||||
|
hashClientPk, err := hashPublicKey256(clientCerts[0].PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("error hashing public key"), nil
|
||||||
|
}
|
||||||
|
skid := base64.StdEncoding.EncodeToString(clientCerts[0].SubjectKeyId)
|
||||||
|
akid := base64.StdEncoding.EncodeToString(clientCerts[0].AuthorityKeyId)
|
||||||
|
pkid := base64.StdEncoding.EncodeToString(hashClientPk)
|
||||||
|
|
||||||
|
// Certificate should not only match a registered tee policy.
|
||||||
|
// Also, the identity of the certificate presented should match the identity of the certificate used during login
|
||||||
|
if req.Auth.InternalData["subject_key_id"] != skid && req.Auth.InternalData["authority_key_id"] != akid && req.Auth.InternalData["hash_public_key"] != pkid {
|
||||||
|
return nil, fmt.Errorf("client identity during renewal not matching client identity used during login")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the tee and use its TTL
|
||||||
|
tee, err := b.Tee(ctx, req.Storage, req.Auth.Metadata["tee_name"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tee == nil {
|
||||||
|
// User no longer exists, do not renew
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !policyutil.EquivalentPolicies(tee.TokenPolicies, req.Auth.TokenPolicies) {
|
||||||
|
return nil, fmt.Errorf("policies have changed, not renewing")
|
||||||
|
}
|
||||||
|
|
||||||
|
expirationDate, err := time.Parse(time.RFC3339, req.Auth.Metadata["collateral_expiration_date"])
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse("error parsing `collateral_expiration_date` metadata"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
now := timeNowFunc()
|
||||||
|
|
||||||
|
if expirationDate.Before(now) {
|
||||||
|
return logical.ErrorResponse("Collateral expired"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &logical.Response{Auth: req.Auth}
|
||||||
|
|
||||||
|
fmt.Errorf("XXXXXXXX: tee.TokenTTL: %v\n", tee.TokenTTL)
|
||||||
|
|
||||||
|
if now.Add(tee.TokenTTL).After(expirationDate) {
|
||||||
|
resp.Auth.TTL = tee.TokenTTL
|
||||||
|
} else {
|
||||||
|
resp.Auth.TTL = expirationDate.Sub(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
if now.Add(tee.TokenMaxTTL).After(expirationDate) {
|
||||||
|
resp.Auth.MaxTTL = tee.TokenMaxTTL
|
||||||
|
} else {
|
||||||
|
resp.Auth.MaxTTL = expirationDate.Sub(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Auth.Period = tee.TokenPeriod
|
||||||
|
return resp, nil
|
||||||
|
}
|
145
tee/path_login_test.go
Normal file
145
tee/path_login_test.go
Normal file
File diff suppressed because one or more lines are too long
331
tee/path_tees.go
Normal file
331
tee/path_tees.go
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package tee
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/tokenutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
|
||||||
|
"github.com/matter-labs/vault-auth-tee/ratee"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pathListTees(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "tees/?",
|
||||||
|
|
||||||
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
|
OperationPrefix: operationPrefixTee,
|
||||||
|
OperationSuffix: "tees",
|
||||||
|
Navigation: true,
|
||||||
|
ItemType: "Tee",
|
||||||
|
},
|
||||||
|
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.ListOperation: b.pathTeeList,
|
||||||
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: pathTeeHelpSyn,
|
||||||
|
HelpDescription: pathTeeHelpDesc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathTees(b *backend) *framework.Path {
|
||||||
|
p := &framework.Path{
|
||||||
|
Pattern: "tees/" + framework.GenericNameRegex("name"),
|
||||||
|
|
||||||
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
|
OperationPrefix: operationPrefixTee,
|
||||||
|
OperationSuffix: "tee",
|
||||||
|
Action: "Create",
|
||||||
|
ItemType: "Tee",
|
||||||
|
},
|
||||||
|
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"name": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: "The name of the Tee, which passes remote attestation verification",
|
||||||
|
},
|
||||||
|
|
||||||
|
"types": {
|
||||||
|
Type: framework.TypeCommaStringSlice,
|
||||||
|
Description: "The types of the TEE.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"sgx_mrsigner": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `The SGX mrsigner hex value to check the attestation report against`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"sgx_mrenclave": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `The SGX mrenclave hex value to check the attestation report against`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"sgx_isv_prodid": {
|
||||||
|
Type: framework.TypeInt,
|
||||||
|
Description: `The SGX isv_prodid value to check the attestation report against`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"sgx_min_isv_svn": {
|
||||||
|
Type: framework.TypeInt,
|
||||||
|
Description: `The SGX minimum isv_svn value to check the attestation report against`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"sgx_allowed_tcb_levels": {
|
||||||
|
Type: framework.TypeCommaStringSlice,
|
||||||
|
Description: `A comma seperated list of allowed SGX TCB states.
|
||||||
|
Allowed values are: ConfigNeeded, OutOfDate, OutOfDateConfigNeeded, SwHardeningNeeded, ConfigAndSwHardeningNeeded`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"display_name": {
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: `The display name to use for clients using this certificate.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.DeleteOperation: b.pathTeeDelete,
|
||||||
|
logical.ReadOperation: b.pathTeeRead,
|
||||||
|
logical.UpdateOperation: b.pathTeeWrite,
|
||||||
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: pathTeeHelpSyn,
|
||||||
|
HelpDescription: pathTeeHelpDesc,
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenutil.AddTokenFields(p.Fields)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) Tee(ctx context.Context, s logical.Storage, n string) (*TeeEntry, error) {
|
||||||
|
entry, err := s.Get(ctx, "tee/"+strings.ToLower(n))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if entry == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result TeeEntry
|
||||||
|
if err := entry.DecodeJSON(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTeeDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
err := req.Storage.Delete(ctx, "tee/"+strings.ToLower(d.Get("name").(string)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTeeList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
tees, err := req.Storage.List(ctx, "tee/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return logical.ListResponse(tees), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTeeRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
tee, err := b.Tee(ctx, req.Storage, strings.ToLower(d.Get("name").(string)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tee == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"display_name": tee.DisplayName,
|
||||||
|
"types": tee.Types,
|
||||||
|
"sgx_mrsigner": tee.SgxMrsigner,
|
||||||
|
"sgx_mrenclave": tee.SgxMrenclave,
|
||||||
|
"sgx_isv_prodid": tee.SgxIsvProdid,
|
||||||
|
"sgx_min_isv_svn": tee.SgxMinIsvSvn,
|
||||||
|
"sgx_allowed_tcb_levels": tee.SgxAllowedTcbLevels,
|
||||||
|
}
|
||||||
|
tee.PopulateTokenData(data)
|
||||||
|
|
||||||
|
return &logical.Response{
|
||||||
|
Data: data,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathTeeWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
|
name := strings.ToLower(d.Get("name").(string))
|
||||||
|
|
||||||
|
tee, err := b.Tee(ctx, req.Storage, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tee == nil {
|
||||||
|
tee = &TeeEntry{
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get non tokenutil fields
|
||||||
|
if displayNameRaw, ok := d.GetOk("display_name"); ok {
|
||||||
|
tee.DisplayName = displayNameRaw.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if teeTypes, ok := d.GetOk("types"); ok {
|
||||||
|
tee.Types = make(map[string]bool)
|
||||||
|
handled := make(map[string]bool)
|
||||||
|
for _, t := range teeTypes.([]string) {
|
||||||
|
// only SGX supported for now
|
||||||
|
if _, ok = handled[t]; ok {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("duplicate TEE type `%s`", t)), nil
|
||||||
|
}
|
||||||
|
if t == "sgx" {
|
||||||
|
tee.Types[t] = true
|
||||||
|
handled[t] = true
|
||||||
|
response, err := handleSGXConfig(d, tee)
|
||||||
|
if response != nil || err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("invalid TEE type `%s`", t)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return logical.ErrorResponse("missing TEE types"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tokenutil fields
|
||||||
|
if err := tee.ParseTokenFields(req, d); err != nil {
|
||||||
|
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp logical.Response
|
||||||
|
|
||||||
|
systemDefaultTTL := b.System().DefaultLeaseTTL()
|
||||||
|
if tee.TokenTTL > systemDefaultTTL {
|
||||||
|
resp.AddWarning(fmt.Sprintf("Given ttl of %d seconds is greater than current mount/system default of %d seconds", tee.TokenTTL/time.Second, systemDefaultTTL/time.Second))
|
||||||
|
}
|
||||||
|
systemMaxTTL := b.System().MaxLeaseTTL()
|
||||||
|
if tee.TokenMaxTTL > systemMaxTTL {
|
||||||
|
resp.AddWarning(fmt.Sprintf("Given max_ttl of %d seconds is greater than current mount/system default of %d seconds", tee.TokenMaxTTL/time.Second, systemMaxTTL/time.Second))
|
||||||
|
}
|
||||||
|
if tee.TokenMaxTTL != 0 && tee.TokenTTL > tee.TokenMaxTTL {
|
||||||
|
return logical.ErrorResponse("ttl should be shorter than max_ttl"), nil
|
||||||
|
}
|
||||||
|
if tee.TokenPeriod > systemMaxTTL {
|
||||||
|
resp.AddWarning(fmt.Sprintf("Given period of %d seconds is greater than the backend's maximum TTL of %d seconds", tee.TokenPeriod/time.Second, systemMaxTTL/time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default the display name to the certificate name if not given
|
||||||
|
if tee.DisplayName == "" {
|
||||||
|
tee.DisplayName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
entry, err := logical.StorageEntryJSON("tee/"+name, tee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := req.Storage.Put(ctx, entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.Warnings) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSGXConfig(d *framework.FieldData, tee *TeeEntry) (*logical.Response, error) {
|
||||||
|
if sgxMrsignerRaw, ok := d.GetOk("sgx_mrsigner"); ok && sgxMrsignerRaw.(string) != "" {
|
||||||
|
tee.SgxMrsigner = strings.ToLower(sgxMrsignerRaw.(string))
|
||||||
|
b, err := hex.DecodeString(tee.SgxMrsigner)
|
||||||
|
if err != nil || len(b) != 32 {
|
||||||
|
return logical.ErrorResponse("`sgx_mrsigner` must be 32 byte hex encoded"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sgxMrenclaveRaw, ok := d.GetOk("sgx_mrenclave"); ok && sgxMrenclaveRaw.(string) != "" {
|
||||||
|
tee.SgxMrenclave = strings.ToLower(sgxMrenclaveRaw.(string))
|
||||||
|
b, err := hex.DecodeString(tee.SgxMrenclave)
|
||||||
|
if err != nil || len(b) != 32 {
|
||||||
|
return logical.ErrorResponse("`sgx_mrenclave` must be 32 byte hex encoded"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tee.SgxMrsigner == "" && tee.SgxMrenclave == "" {
|
||||||
|
return logical.ErrorResponse("either `sgx_mrsigner` or `sgx_mrenclave` must be set"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sgxIsvProdidRaw, ok := d.GetOk("sgx_isv_prodid"); ok {
|
||||||
|
tee.SgxIsvProdid = sgxIsvProdidRaw.(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sgxMinIsvSvnRaw, ok := d.GetOk("sgx_min_isv_svn"); ok {
|
||||||
|
tee.SgxMinIsvSvn = sgxMinIsvSvnRaw.(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sgxAllowedTcbLevelsRaw, ok := d.GetOk("sgx_allowed_tcb_levels"); ok {
|
||||||
|
tee.SgxAllowedTcbLevels = make(map[ratee.SgxQlQvResult]bool)
|
||||||
|
for _, v := range sgxAllowedTcbLevelsRaw.([]string) {
|
||||||
|
var state ratee.SgxQlQvResult
|
||||||
|
switch v {
|
||||||
|
case "Ok":
|
||||||
|
state = ratee.SgxQlQvResultOk
|
||||||
|
case "ConfigNeeded":
|
||||||
|
state = ratee.SgxQlQvResultConfigNeeded
|
||||||
|
case "OutOfDate":
|
||||||
|
state = ratee.SgxQlQvResultOutOfDate
|
||||||
|
case "OutOfDateConfigNeeded":
|
||||||
|
state = ratee.SgxQlQvResultOutOfDateConfigNeeded
|
||||||
|
case "SwHardeningNeeded":
|
||||||
|
state = ratee.SgxQlQvResultSwHardeningNeeded
|
||||||
|
case "ConfigAndSwHardeningNeeded":
|
||||||
|
state = ratee.SgxQlQvResultConfigAndSwHardeningNeeded
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse("invalid sgx_allowed_tcb_levels value"), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
tee.SgxAllowedTcbLevels[state] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TeeEntry struct {
|
||||||
|
tokenutil.TokenParams
|
||||||
|
|
||||||
|
Name string
|
||||||
|
DisplayName string
|
||||||
|
Types map[string]bool
|
||||||
|
SgxMrsigner string
|
||||||
|
SgxMrenclave string
|
||||||
|
SgxIsvProdid int
|
||||||
|
SgxMinIsvSvn int
|
||||||
|
SgxAllowedTcbLevels map[ratee.SgxQlQvResult]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathTeeHelpSyn = `
|
||||||
|
Manage TEE remote attestation parameters used for authentication.`
|
||||||
|
|
||||||
|
const pathTeeHelpDesc = `
|
||||||
|
This endpoint allows you to create, read, update, and delete TEEs
|
||||||
|
that are allowed to authenticate.
|
||||||
|
|
||||||
|
Deleting a TEE will not revoke auth for prior authenticated connections.
|
||||||
|
To do this, do a revoke on "login". If you don't need to revoke login immediately,
|
||||||
|
then the next renew will cause the lease to expire.
|
||||||
|
`
|
22
tee/test-fixtures/keys/cert.pem
Normal file
22
tee/test-fixtures/keys/cert.pem
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDtTCCAp2gAwIBAgIUf+jhKTFBnqSs34II0WS1L4QsbbAwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzQxWhcNMjUw
|
||||||
|
MTA1MTAyODExWjAbMRkwFwYDVQQDExBjZXJ0LmV4YW1wbGUuY29tMIIBIjANBgkq
|
||||||
|
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxS
|
||||||
|
TRAVnygAftetT8puHflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGn
|
||||||
|
SgMld6ZWRhNheZhA6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmi
|
||||||
|
YYMiIWplidMmMO5NTRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5
|
||||||
|
donyqtnaHuIJGuUdy54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVG
|
||||||
|
B+5+AAGF5iuHC3N2DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABo4H1
|
||||||
|
MIHyMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUm++e
|
||||||
|
HpyM3p708bgZJuRYEdX1o+UwHwYDVR0jBBgwFoAUncSzT/6HMexyuiU9/7EgHu+o
|
||||||
|
k5swOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4x
|
||||||
|
OjgyMDAvdjEvcGtpL2NhMCEGA1UdEQQaMBiCEGNlcnQuZXhhbXBsZS5jb22HBH8A
|
||||||
|
AAEwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3Br
|
||||||
|
aS9jcmwwDQYJKoZIhvcNAQELBQADggEBABsuvmPSNjjKTVN6itWzdQy+SgMIrwfs
|
||||||
|
X1Yb9Lefkkwmp9ovKFNQxa4DucuCuzXcQrbKwWTfHGgR8ct4rf30xCRoA7dbQWq4
|
||||||
|
aYqNKFWrRaBRAaaYZ/O1ApRTOrXqRx9Eqr0H1BXLsoAq+mWassL8sf6siae+CpwA
|
||||||
|
KqBko5G0dNXq5T4i2LQbmoQSVetIrCJEeMrU+idkuqfV2h1BQKgSEhFDABjFdTCN
|
||||||
|
QDAHsEHsi2M4/jRW9fqEuhHSDfl2n7tkFUI8wTHUUCl7gXwweJ4qtaSXIwKXYzNj
|
||||||
|
xqKHA8Purc1Yfybz4iE1JCROi9fInKlzr5xABq8nb9Qc/J9DIQM+Xmk=
|
||||||
|
-----END CERTIFICATE-----
|
27
tee/test-fixtures/keys/key.pem
Normal file
27
tee/test-fixtures/keys/key.pem
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxSTRAVnygAftetT8pu
|
||||||
|
HflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGnSgMld6ZWRhNheZhA
|
||||||
|
6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmiYYMiIWplidMmMO5N
|
||||||
|
TRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5donyqtnaHuIJGuUd
|
||||||
|
y54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVGB+5+AAGF5iuHC3N2
|
||||||
|
DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABAoIBAHR7fFV0eAGaopsX
|
||||||
|
9OD0TUGlsephBXb43g0GYHfJ/1Ew18w9oaxszJEqkl+PB4W3xZ3yG3e8ZomxDOhF
|
||||||
|
RreF2WgG5xOfhDogMwu6NodbArfgnAvoC6JnW3qha8HMP4F500RFVyCRcd6A3Frd
|
||||||
|
rFtaZn/UyCsBAN8/zkwPeYHayo7xX6d9kzgRl9HluEX5PXI5+3uiBDUiM085gkLI
|
||||||
|
5Cmadh9fMdjfhDXI4x2JYmILpp/9Nlc/krB15s5n1MPNtn3yL0TI0tWp0WlwDCV7
|
||||||
|
oUm1SfIM0F1fXGFyFDcqwoIr6JCQgXk6XtTg31YhH1xgUIclUVdtHqmAwAbLdIhQ
|
||||||
|
GAiHn2kCgYEAwD4pZ8HfpiOG/EHNoWsMATc/5yC7O8F9WbvcHZQIymLY4v/7HKZb
|
||||||
|
VyOR6UQ5/O2cztSGIuKSF6+OK1C34lOyCuTSOTFrjlgEYtLIXjdGLfFdtOO8GRQR
|
||||||
|
akVXdwuzNAjTBaH5eXbG+NKcjmCvZL48dQVlfDTVulzFGbcsVTHIMQUCgYEA7IQI
|
||||||
|
FVsKnY3KqpyGqXq92LMcsT3XgW6X1BIIV+YhJ5AFUFkFrjrbXs94/8XyLfi0xBQy
|
||||||
|
efK+8g5sMs7koF8LyZEcAXWZJQduaKB71hoLlRaU4VQkL/dl2B6VFmAII/CsRCYh
|
||||||
|
r9RmDN2PF/mp98Ih9dpC1VqcCDRGoTYsd7jLalMCgYAMgH5k1wDaZxkSMp1S0AlZ
|
||||||
|
0uP+/evvOOgT+9mWutfPgZolOQx1koQCKLgGeX9j6Xf3I28NubpSfAI84uTyfQrp
|
||||||
|
FnRtb79U5Hh0jMynA+U2e6niZ6UF5H41cQj9Hu+qhKBkj2IP+h96cwfnYnZFkPGR
|
||||||
|
kqZE65KyqfHPeFATwkcImQKBgCdrfhlpGiTWXCABhKQ8s+WpPLAB2ahV8XJEKyXT
|
||||||
|
UlVQuMIChGLcpnFv7P/cUxf8asx/fUY8Aj0/0CLLvulHziQjTmKj4gl86pb/oIQ3
|
||||||
|
xRRtNhU0O+/OsSfLORgIm3K6C0w0esregL/GMbJSR1TnA1gBr7/1oSnw5JC8Ab9W
|
||||||
|
injHAoGAJT1MGAiQrhlt9GCGe6Ajw4omdbY0wS9NXefnFhf7EwL0es52ezZ28zpU
|
||||||
|
2LXqSFbtann5CHgpSLxiMYPDIf+er4xgg9Bz34tz1if1rDfP2Qrxdrpr4jDnrGT3
|
||||||
|
gYC2qCpvVD9RRUMKFfnJTfl5gMQdBW/LINkHtJ82snAeLl3gjQ4=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
302
tee/test_responder.go
Normal file
302
tee/test_responder.go
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
|
||||||
|
// Package ocsp implements an OCSP responder based on a generic storage backend.
|
||||||
|
// It provides a couple of sample implementations.
|
||||||
|
// Because OCSP responders handle high query volumes, we have to be careful
|
||||||
|
// about how much logging we do. Error-level logs are reserved for problems
|
||||||
|
// internal to the server, that can be fixed by an administrator. Any type of
|
||||||
|
// incorrect input from a user should be logged and Info or below. For things
|
||||||
|
// that are logged on every request, Debug is the appropriate level.
|
||||||
|
//
|
||||||
|
// From https://github.com/cloudflare/cfssl/blob/master/ocsp/responder.go
|
||||||
|
|
||||||
|
package tee
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
malformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
|
||||||
|
internalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
|
||||||
|
unauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
|
||||||
|
|
||||||
|
// ErrNotFound indicates the request OCSP response was not found. It is used to
|
||||||
|
// indicate that the responder should reply with unauthorizedErrorResponse.
|
||||||
|
ErrNotFound = errors.New("Request OCSP Response not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Source represents the logical source of OCSP responses, i.e.,
|
||||||
|
// the logic that actually chooses a response based on a request. In
|
||||||
|
// order to create an actual responder, wrap one of these in a Responder
|
||||||
|
// object and pass it to http.Handle. By default the Responder will set
|
||||||
|
// the headers Cache-Control to "max-age=(response.NextUpdate-now), public, no-transform, must-revalidate",
|
||||||
|
// Last-Modified to response.ThisUpdate, Expires to response.NextUpdate,
|
||||||
|
// ETag to the SHA256 hash of the response, and Content-Type to
|
||||||
|
// application/ocsp-response. If you want to override these headers,
|
||||||
|
// or set extra headers, your source should return a http.Header
|
||||||
|
// with the headers you wish to set. If you don'log want to set any
|
||||||
|
// extra headers you may return nil instead.
|
||||||
|
type Source interface {
|
||||||
|
Response(*ocsp.Request) ([]byte, http.Header, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An InMemorySource is a map from serialNumber -> der(response)
|
||||||
|
type InMemorySource map[string][]byte
|
||||||
|
|
||||||
|
// Response looks up an OCSP response to provide for a given request.
|
||||||
|
// InMemorySource looks up a response purely based on serial number,
|
||||||
|
// without regard to what issuer the request is asking for.
|
||||||
|
func (src InMemorySource) Response(request *ocsp.Request) ([]byte, http.Header, error) {
|
||||||
|
response, present := src[request.SerialNumber.String()]
|
||||||
|
if !present {
|
||||||
|
return nil, nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return response, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats is a basic interface that allows users to record information
|
||||||
|
// about returned responses
|
||||||
|
type Stats interface {
|
||||||
|
ResponseStatus(ocsp.ResponseStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
type logger interface {
|
||||||
|
Log(args ...any)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Responder object provides the HTTP logic to expose a
|
||||||
|
// Source of OCSP responses.
|
||||||
|
type Responder struct {
|
||||||
|
log logger
|
||||||
|
Source Source
|
||||||
|
stats Stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResponder instantiates a Responder with the give Source.
|
||||||
|
func NewResponder(t logger, source Source, stats Stats) *Responder {
|
||||||
|
return &Responder{
|
||||||
|
Source: source,
|
||||||
|
stats: stats,
|
||||||
|
log: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func overrideHeaders(response http.ResponseWriter, headers http.Header) {
|
||||||
|
for k, v := range headers {
|
||||||
|
if len(v) == 1 {
|
||||||
|
response.Header().Set(k, v[0])
|
||||||
|
} else if len(v) > 1 {
|
||||||
|
response.Header().Del(k)
|
||||||
|
for _, e := range v {
|
||||||
|
response.Header().Add(k, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashToString contains mappings for the only hash functions
|
||||||
|
// x/crypto/ocsp supports
|
||||||
|
var hashToString = map[crypto.Hash]string{
|
||||||
|
crypto.SHA1: "SHA1",
|
||||||
|
crypto.SHA256: "SHA256",
|
||||||
|
crypto.SHA384: "SHA384",
|
||||||
|
crypto.SHA512: "SHA512",
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Responder can process both GET and POST requests. The mapping
|
||||||
|
// from an OCSP request to an OCSP response is done by the Source;
|
||||||
|
// the Responder simply decodes the request, and passes back whatever
|
||||||
|
// response is provided by the source.
|
||||||
|
// Note: The caller must use http.StripPrefix to strip any path components
|
||||||
|
// (including '/') on GET requests.
|
||||||
|
// Do not use this responder in conjunction with http.NewServeMux, because the
|
||||||
|
// default handler will try to canonicalize path components by changing any
|
||||||
|
// strings of repeated '/' into a single '/', which will break the base64
|
||||||
|
// encoding.
|
||||||
|
func (rs *Responder) ServeHTTP(response http.ResponseWriter, request *http.Request) {
|
||||||
|
// By default we set a 'max-age=0, no-cache' Cache-Control header, this
|
||||||
|
// is only returned to the client if a valid authorized OCSP response
|
||||||
|
// is not found or an error is returned. If a response if found the header
|
||||||
|
// will be altered to contain the proper max-age and modifiers.
|
||||||
|
response.Header().Add("Cache-Control", "max-age=0, no-cache")
|
||||||
|
// Read response from request
|
||||||
|
var requestBody []byte
|
||||||
|
var err error
|
||||||
|
switch request.Method {
|
||||||
|
case "GET":
|
||||||
|
base64Request, err := url.QueryUnescape(request.URL.Path)
|
||||||
|
if err != nil {
|
||||||
|
rs.log.Log("Error decoding URL:", request.URL.Path)
|
||||||
|
response.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// url.QueryUnescape not only unescapes %2B escaping, but it additionally
|
||||||
|
// turns the resulting '+' into a space, which makes base64 decoding fail.
|
||||||
|
// So we go back afterwards and turn ' ' back into '+'. This means we
|
||||||
|
// accept some malformed input that includes ' ' or %20, but that's fine.
|
||||||
|
base64RequestBytes := []byte(base64Request)
|
||||||
|
for i := range base64RequestBytes {
|
||||||
|
if base64RequestBytes[i] == ' ' {
|
||||||
|
base64RequestBytes[i] = '+'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// In certain situations a UA may construct a request that has a double
|
||||||
|
// slash between the host name and the base64 request body due to naively
|
||||||
|
// constructing the request URL. In that case strip the leading slash
|
||||||
|
// so that we can still decode the request.
|
||||||
|
if len(base64RequestBytes) > 0 && base64RequestBytes[0] == '/' {
|
||||||
|
base64RequestBytes = base64RequestBytes[1:]
|
||||||
|
}
|
||||||
|
requestBody, err = base64.StdEncoding.DecodeString(string(base64RequestBytes))
|
||||||
|
if err != nil {
|
||||||
|
rs.log.Log("Error decoding base64 from URL", string(base64RequestBytes))
|
||||||
|
response.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "POST":
|
||||||
|
requestBody, err = ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
rs.log.Log("Problem reading body of POST", err)
|
||||||
|
response.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
response.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b64Body := base64.StdEncoding.EncodeToString(requestBody)
|
||||||
|
rs.log.Log("Received OCSP request", b64Body)
|
||||||
|
|
||||||
|
// All responses after this point will be OCSP.
|
||||||
|
// We could check for the content type of the request, but that
|
||||||
|
// seems unnecessariliy restrictive.
|
||||||
|
response.Header().Add("Content-Type", "application/ocsp-response")
|
||||||
|
|
||||||
|
// Parse response as an OCSP request
|
||||||
|
// XXX: This fails if the request contains the nonce extension.
|
||||||
|
// We don'log intend to support nonces anyway, but maybe we
|
||||||
|
// should return unauthorizedRequest instead of malformed.
|
||||||
|
ocspRequest, err := ocsp.ParseRequest(requestBody)
|
||||||
|
if err != nil {
|
||||||
|
rs.log.Log("Error decoding request body", b64Body)
|
||||||
|
response.WriteHeader(http.StatusBadRequest)
|
||||||
|
response.Write(malformedRequestErrorResponse)
|
||||||
|
if rs.stats != nil {
|
||||||
|
rs.stats.ResponseStatus(ocsp.Malformed)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up OCSP response from source
|
||||||
|
ocspResponse, headers, err := rs.Source.Response(ocspRequest)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrNotFound {
|
||||||
|
rs.log.Log("No response found for request: serial %x, request body %s",
|
||||||
|
ocspRequest.SerialNumber, b64Body)
|
||||||
|
response.Write(unauthorizedErrorResponse)
|
||||||
|
if rs.stats != nil {
|
||||||
|
rs.stats.ResponseStatus(ocsp.Unauthorized)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rs.log.Log("Error retrieving response for request: serial %x, request body %s, error",
|
||||||
|
ocspRequest.SerialNumber, b64Body, err)
|
||||||
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
response.Write(internalErrorErrorResponse)
|
||||||
|
if rs.stats != nil {
|
||||||
|
rs.stats.ResponseStatus(ocsp.InternalError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedResponse, err := ocsp.ParseResponse(ocspResponse, nil)
|
||||||
|
if err != nil {
|
||||||
|
rs.log.Log("Error parsing response for serial %x",
|
||||||
|
ocspRequest.SerialNumber, err)
|
||||||
|
response.Write(internalErrorErrorResponse)
|
||||||
|
if rs.stats != nil {
|
||||||
|
rs.stats.ResponseStatus(ocsp.InternalError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write OCSP response to response
|
||||||
|
response.Header().Add("Last-Modified", parsedResponse.ThisUpdate.Format(time.RFC1123))
|
||||||
|
response.Header().Add("Expires", parsedResponse.NextUpdate.Format(time.RFC1123))
|
||||||
|
now := time.Now()
|
||||||
|
maxAge := 0
|
||||||
|
if now.Before(parsedResponse.NextUpdate) {
|
||||||
|
maxAge = int(parsedResponse.NextUpdate.Sub(now) / time.Second)
|
||||||
|
} else {
|
||||||
|
// TODO(#530): we want max-age=0 but this is technically an authorized OCSP response
|
||||||
|
// (despite being stale) and 5019 forbids attaching no-cache
|
||||||
|
maxAge = 0
|
||||||
|
}
|
||||||
|
response.Header().Set(
|
||||||
|
"Cache-Control",
|
||||||
|
fmt.Sprintf(
|
||||||
|
"max-age=%d, public, no-transform, must-revalidate",
|
||||||
|
maxAge,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
responseHash := sha256.Sum256(ocspResponse)
|
||||||
|
response.Header().Add("ETag", fmt.Sprintf("\"%X\"", responseHash))
|
||||||
|
|
||||||
|
if headers != nil {
|
||||||
|
overrideHeaders(response, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 7232 says that a 304 response must contain the above
|
||||||
|
// headers if they would also be sent for a 200 for the same
|
||||||
|
// request, so we have to wait until here to do this
|
||||||
|
if etag := request.Header.Get("If-None-Match"); etag != "" {
|
||||||
|
if etag == fmt.Sprintf("\"%X\"", responseHash) {
|
||||||
|
response.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.WriteHeader(http.StatusOK)
|
||||||
|
response.Write(ocspResponse)
|
||||||
|
if rs.stats != nil {
|
||||||
|
rs.stats.ResponseStatus(ocsp.Success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2014 CloudFlare Inc.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
7
version/version.go
Normal file
7
version/version.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Matter Labs
|
||||||
|
|
||||||
|
package version
|
||||||
|
|
||||||
|
const Version = "0.1.0+dev"
|
||||||
|
const Name = "vault-auth-tee"
|
Loading…
Add table
Add a link
Reference in a new issue