Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
NullHypothesis committed Oct 19, 2024
1 parent 98d54a1 commit 2ad281a
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 41 deletions.
12 changes: 5 additions & 7 deletions internal/enclave/attester.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
// See page 65 of the AWS Nitro Enclaves user guide for reference:
// https://docs.aws.amazon.com/pdfs/enclaves/latest/user/enclaves-user.pdf
const userDataLen = 1024
const (
typeNoop = "noop"
typeNitro = "nitro"
)

var (
errPCRMismatch = errors.New("platform configuration registers differ")

Check failure on line 18 in internal/enclave/attester.go

View workflow job for this annotation

GitHub Actions / Lint

var `errPCRMismatch` is unused (unused)
Expand All @@ -35,17 +39,11 @@ type AuxInfo struct {
Nonce [userDataLen]byte `json:"public_key"`
}

func ToAuxField(s []byte) [userDataLen]byte {
var a [userDataLen]byte
copy(a[:], s)
return a
}

// Attester defines functions for the creation and verification of attestation
// documents. Making this an interface helps with testing: It allows us to
// implement a dummy attester that works without the AWS Nitro hypervisor.
type Attester interface {
Type() string
Attest(*AuxInfo) (*AttestationDoc, error)
Verify(Attestation, *nonce.Nonce) (*AuxInfo, error)
Verify(*AttestationDoc, *nonce.Nonce) (*AuxInfo, error)
}
50 changes: 29 additions & 21 deletions internal/enclave/attester_nitro.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ func NewNitroAttester() (attester Attester, err error) {
}

func (*NitroAttester) Type() string {
return "nitro"
return typeNitro
}

func (a *NitroAttester) Attest(aux *AuxInfo) (_ *AttestationDoc, err error) {
defer errs.Wrap(&err, "failed to verify attestation document")
defer errs.Wrap(&err, "failed to create attestation document")

if aux == nil {
return nil, errors.New("aux info is nil")
Expand All @@ -51,47 +51,55 @@ func (a *NitroAttester) Attest(aux *AuxInfo) (_ *AttestationDoc, err error) {
return nil, err
}
if resp.Attestation == nil || resp.Attestation.Document == nil {
return nil, errors.New("not good")
return nil, errors.New("required fields missing in attestation response")
}

return &AttestationDoc{
Type: "nitro",
Type: typeNitro,
Doc: resp.Attestation.Document,
}, nil
}

func (*NitroAttester) Verify(a Attestation, ourNonce *nonce.Nonce) (_ *AuxInfo, err error) {
func (a *NitroAttester) Verify(doc *AttestationDoc, ourNonce *nonce.Nonce) (_ *AuxInfo, err error) {
defer errs.Wrap(&err, "failed to verify attestation document")

// First, verify the remote enclave's attestation document.
if doc == nil {
return nil, errors.New("attestation document is nil")
}
if doc.Type != a.Type() {
return nil, errors.New("attestation document type mismatch")
}

// First, verify the attestation document.
opts := nitrite.VerifyOptions{CurrentTime: time.Now().UTC()}
their, err := nitrite.Verify(a, opts)
their, err := nitrite.Verify(doc.Doc, opts)
if err != nil {
return nil, err
}

// Verify that the remote enclave's PCR values (e.g., the image ID) are
// identical to ours.
ourPCRs, err := getPCRs()
if err != nil {
return nil, err
}
if !ourPCRs.Equal(their.Document.PCRs) {
return nil, errPCRMismatch
}
// ourPCRs, err := getPCRs()
// if err != nil {
// return nil, err
// }
// if !ourPCRs.Equal(their.Document.PCRs) {
// return nil, errPCRMismatch
// }

// Verify that the remote enclave's attestation document contains the nonce
// that we asked it to embed.
theirNonce, err := nonce.FromSlice(their.Document.Nonce)
// Verify that the attestation document contains the nonce that we may have
// asked it to embed.
docNonce, err := nonce.FromSlice(their.Document.Nonce)
if err != nil {
return nil, err
}
if *ourNonce != *theirNonce {
if ourNonce != nil && *ourNonce != *docNonce {
return nil, errNonceMismatch
}

return &AuxInfo{
Nonce: [1024]byte(their.Document.Nonce),
UserData: [1024]byte(their.Document.UserData),
PublicKey: [1024]byte(their.Document.PublicKey),
Nonce: [userDataLen]byte(their.Document.Nonce),
UserData: [userDataLen]byte(their.Document.UserData),
PublicKey: [userDataLen]byte(their.Document.PublicKey),
}, nil
}
96 changes: 92 additions & 4 deletions internal/enclave/attester_nitro_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ package enclave
import (
"testing"

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

func TestNitroAttester(t *testing.T) {
func getNonce(t *testing.T) [userDataLen]byte {
n, err := nonce.New()
require.NoError(t, err)
return ToAuxField(n.ToSlice())
}

func TestNitroAttest(t *testing.T) {
if !IsEnclave() {
t.Skip("skipping test; not running in an enclave")
}

attester, err := NewNitroAttester()
require.NoError(t, err)

Expand All @@ -19,15 +26,96 @@ func TestNitroAttester(t *testing.T) {
aux *AuxInfo
wantErr bool
}{
{
name: "nil aux info",
wantErr: true,
},
{
name: "empty aux info",
aux: &AuxInfo{},
},
{
name: "aux info with nonce",
aux: &AuxInfo{
Nonce: getNonce(t),
},
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
doc, err := attester.Attest(c.aux)
if c.wantErr {
require.NotNil(t, err)
return
}
require.Equal(t, doc.Type, typeNitro)
})
}
}

func TestNitroVerify(t *testing.T) {
if !IsEnclave() {
t.Skip("skipping test; not running in an enclave")
}

attester, err := NewNitroAttester()
require.NoError(t, err)

getDoc := func(t *testing.T, n *nonce.Nonce) *AttestationDoc {
doc, err := attester.Attest(&AuxInfo{Nonce: ToAuxField(n.ToSlice())})
require.NoError(t, err)
return doc
}
testNonce := util.Must(nonce.New())

cases := []struct {
name string
doc *AttestationDoc
nonce *nonce.Nonce
wantErr bool
}{
{
name: "nil document and nonce",
wantErr: true,
},
{
name: "document type mismatch",
doc: &AttestationDoc{Type: "foo"},
wantErr: true,
},
{
name: "invalid document",
doc: &AttestationDoc{
Type: typeNitro,
Doc: []byte("foobar"),
},
wantErr: true,
},
{
name: "nonce mismatch",
doc: getDoc(t, util.Must(nonce.New())),
nonce: util.Must(nonce.New()),
wantErr: true,
},
{
name: "no nonce",
doc: getDoc(t, util.Must(nonce.New())),
},
{
name: "valid document and nonce",
doc: getDoc(t, testNonce),
nonce: testNonce,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
_, err := attester.Attest(c.aux)
require.Equal(t, c.wantErr, err != nil)
_, err := attester.Verify(c.doc, c.nonce)
if c.wantErr {
require.Error(t, err)
return
}
})
}
}
8 changes: 4 additions & 4 deletions internal/enclave/attester_noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func NewNoopAttester() Attester {
}

func (*NoopAttester) Type() string {
return "noop"
return typeNoop
}

func (*NoopAttester) Attest(aux *AuxInfo) (*AttestationDoc, error) {
Expand All @@ -23,14 +23,14 @@ func (*NoopAttester) Attest(aux *AuxInfo) (*AttestationDoc, error) {
return nil, err
}
return &AttestationDoc{
Type: "noop",
Type: typeNoop,
Doc: a,
}, nil
}

func (*NoopAttester) Verify(a Attestation, n *nonce.Nonce) (*AuxInfo, error) {
func (*NoopAttester) Verify(a *AttestationDoc, n *nonce.Nonce) (*AuxInfo, error) {
var aux = new(AuxInfo)
if err := json.Unmarshal(a, &aux); err != nil {
if err := json.Unmarshal(a.Doc, &aux); err != nil {
return nil, err
}
return aux, nil
Expand Down
13 changes: 8 additions & 5 deletions internal/enclave/attester_noop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,26 @@ func TestSuccessfulVerification(t *testing.T) {
// "Age": float64(42),
// }
origAux = &AuxInfo{
PublicKey: [1024]byte{'a', 'b', 'c'},
UserData: [1024]byte{'d', 'e', 'f'},
Nonce: [1024]byte{'g', 'h', 'i'},
PublicKey: [userDataLen]byte{'a', 'b', 'c'},
UserData: [userDataLen]byte{'d', 'e', 'f'},
Nonce: [userDataLen]byte{'g', 'h', 'i'},
}
)

attestation, err := a.Attest(origAux)
require.Nil(t, err)

aux, err := a.Verify(attestation.Doc, &nonce.Nonce{})
aux, err := a.Verify(attestation, &nonce.Nonce{})
require.Nil(t, err)
require.Equal(t, origAux, aux)
}

func TestFailedVerification(t *testing.T) {
var a = NewNoopAttester()

_, err := a.Verify([]byte(`"foo": "bar`), &nonce.Nonce{})
_, err := a.Verify(&AttestationDoc{
Type: typeNoop,
Doc: []byte(`"foo": "bar`),
}, &nonce.Nonce{})
require.NotNil(t, err)
}
6 changes: 6 additions & 0 deletions internal/enclave/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ func IsEnclave() bool {
}
return false
}

func ToAuxField(s []byte) [userDataLen]byte {
var a [userDataLen]byte
copy(a[:], s)
return a
}
4 changes: 4 additions & 0 deletions internal/nonce/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func (n *Nonce) URLEncode() string {
)
}

func (n *Nonce) ToSlice() []byte {
return n[:]
}

// New creates a new nonce.
func New() (*Nonce, error) {
var newNonce Nonce
Expand Down

0 comments on commit 2ad281a

Please sign in to comment.