mirror of
https://github.com/matter-labs/vault-auth-tee.git
synced 2025-07-20 15:23:56 +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