Skip to content

Commit

Permalink
Merge pull request #32 from Amnesic-Systems/improve-verify-tool
Browse files Browse the repository at this point in the history
Handle all of attestation in `veil-verify`.
  • Loading branch information
NullHypothesis authored Nov 30, 2024
2 parents ecc7652 + 4b95be8 commit 27286cc
Show file tree
Hide file tree
Showing 613 changed files with 122,668 additions and 278 deletions.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.vscode
cmd/veil/veil
cmd/veil-verify/veil-verify
cover.html
cover.out
veil.tar
veil.eif
*.tar
*.eif
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
prog = veil
prog_dir = cmd/veil
verify_prog = veil-verify
verify_prog_dir = cmd/veil-verify
godeps = go.mod go.sum $(shell find cmd internal -name "*.go" -type f)

image_tag := $(prog)
Expand Down Expand Up @@ -81,10 +83,15 @@ $(prog): $(godeps)
-ldflags="-s -w" \
-buildvcs=false \
-o $(prog)
@echo "$(prog_dir)/$(prog)"
@-sha1sum "$(prog_dir)/$(prog)"

$(verify_prog): $(godeps)
@go build -C $(verify_prog_dir) -o $(verify_prog)
@-sha1sum "$(verify_prog_dir)/$(verify_prog)"

.PHONY: clean
clean:
rm -f $(prog_dir)/$(prog)
rm -f $(verify_prog_dir)/$(verify_prog)
rm -f $(cover_out) $(cover_html)
rm -f $(image_tar) $(image_eif)
145 changes: 145 additions & 0 deletions cmd/veil-verify/attestation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package main

import (
"bytes"
"context"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strings"

"github.com/fatih/color"

"github.com/Amnesic-Systems/veil/internal/enclave"
"github.com/Amnesic-Systems/veil/internal/enclave/nitro"
"github.com/Amnesic-Systems/veil/internal/enclave/noop"
"github.com/Amnesic-Systems/veil/internal/errs"
"github.com/Amnesic-Systems/veil/internal/httpx"
"github.com/Amnesic-Systems/veil/internal/nonce"
"github.com/Amnesic-Systems/veil/internal/util"
)

var (
errFailedToAttest = errors.New("failed to attest enclave")
errFailedToConvert = errors.New("failed to convert measurements to PCR")
)

func attestEnclave(
ctx context.Context,
cfg *config,
pcrs enclave.PCR,
) (err error) {
defer errs.WrapErr(&err, errFailedToAttest)

// Generate a nonce to ensure that the attestation document is fresh.
nonce, err := nonce.New()
if err != nil {
return err
}

// Request the enclave's attestation document. We don't verify HTTPS
// certificates because authentication is happening via the attestation
// document.
client := httpx.NewUnauthClient()
url := cfg.addr + "/enclave/attestation?nonce=" + nonce.URLEncode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("got status %d from enclave", resp.StatusCode)
}

// Parse the attestation document.
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()
var rawDoc enclave.RawDocument
if err := json.Unmarshal(body, &rawDoc); err != nil {
return err
}

// Verify the attestation document, which provides assurance that we are
// talking to an enclave. The nonce provides assurance that we are talking
// to an alive enclave (instead of a replayed attestation document).
var attester enclave.Attester = nitro.NewAttester()
if cfg.testing {
attester = noop.NewAttester()
}
doc, err := attester.Verify(&rawDoc, nonce)
if err != nil {
return err
}

// Delete empty PCR values from the attestation document. This is not
// ideal; we should either have the rest of the code tolerate empty PCR
// values or fix the nsm package, so it doesn't return empty PCR values.
empty := make([]byte, sha512.Size384)
for i, pcr := range doc.PCRs {
if bytes.Equal(pcr, empty) {
delete(doc.PCRs, i)
}
}

// Verify the attestation document's PCR values, which provide assurance
// that the remote enclave's image and kernel match the local copy.
if !pcrs.Equal(doc.PCRs) {
log.Printf("Expected PCRs:\n%sbut got PCRs:\n%s", pcrs, doc.PCRs)
color.Red("Enclave's code DOES NOT match local code!")
} else {
color.Green("Enclave's code matches local code!")
}

return nil
}

func toPCR(jsonMsmts []byte) (_ enclave.PCR, err error) {
defer errs.WrapErr(&err, errFailedToConvert)

// This structs represents the JSON-encoded measurements of the enclave
// image. The JSON tags must match the output of the nitro-cli command
// line tool. An example:
//
// {
// "Measurements": {
// "HashAlgorithm": "Sha384 { ... }",
// "PCR0": "8b927cf0bbf2d668a8c24c69afd23bff2dda713b4f0d70195205950f9a5a1fbb7089ad937e3025ee8d5a084f3d6c9126",
// "PCR1": "4b4d5b3661b3efc12920900c80e126e4ce783c522de6c02a2a5bf7af3a2b9327b86776f188e4be1c1c404a129dbda493",
// "PCR2": "22d2194eb27a7cda42e66dd5b91ef13e5a153d797c04ae179e59bef1c93438d6ad0365c175c119230e36d0f8d6b6b59e"
// }
// }
m := struct {
Measurements struct {
HashAlgorithm string `json:"HashAlgorithm"`
PCR0 string `json:"PCR0"`
PCR1 string `json:"PCR1"`
PCR2 string `json:"PCR2"`
} `json:"Measurements"`
}{}
if err := json.Unmarshal(jsonMsmts, &m); err != nil {
return nil, err
}

const want = "sha384"
got := strings.ToLower(m.Measurements.HashAlgorithm)
if !strings.HasPrefix(got, want) {
return nil, fmt.Errorf("expected hash algorithm %q but got %q", want, got)
}

return enclave.PCR{
0: util.Must(hex.DecodeString(m.Measurements.PCR0)),
1: util.Must(hex.DecodeString(m.Measurements.PCR1)),
2: util.Must(hex.DecodeString(m.Measurements.PCR2)),
}, nil
}
62 changes: 62 additions & 0 deletions cmd/veil-verify/attestation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"testing"

"github.com/Amnesic-Systems/veil/internal/enclave"
"github.com/stretchr/testify/require"
)

func TestToPCR(t *testing.T) {
cases := []struct {
name string
in []byte
wantPCRs enclave.PCR
wantErr bool
}{
{
name: "invalid json",
in: []byte("invalid"),
wantErr: true,
},
{
name: "invalid hash",
in: []byte(`{
"Measurements": {
"HashAlgorithm": "Sha512 { ... }",
"PCR0": "616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",
"PCR1": "626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262",
"PCR2": "636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363"
}
}`),
wantErr: true,
},
{
name: "invalid PCR value",
in: []byte(`{
"Measurements": {
"HashAlgorithm": "Sha512 { ... }",
"PCR0": "foobar",
}
}`),
wantErr: true,
},
{
name: "valid",
in: []byte(validPCRs),
wantPCRs: enclave.PCR{
0: []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
1: []byte("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
2: []byte("cccccccccccccccccccccccccccccccccccccccccccccccc"),
},
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
gotPCRs, err := toPCR(c.in)
require.Equal(t, c.wantErr, err != nil)
require.True(t, gotPCRs.Equal(c.wantPCRs))
})
}
}
Loading

0 comments on commit 27286cc

Please sign in to comment.