diff --git a/internal/enclave/attester.go b/internal/enclave/attester.go index 19d1092..69c906b 100644 --- a/internal/enclave/attester.go +++ b/internal/enclave/attester.go @@ -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") @@ -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) } diff --git a/internal/enclave/attester_nitro.go b/internal/enclave/attester_nitro.go index 2a8d1b5..2e342b9 100644 --- a/internal/enclave/attester_nitro.go +++ b/internal/enclave/attester_nitro.go @@ -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") @@ -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 } diff --git a/internal/enclave/attester_nitro_test.go b/internal/enclave/attester_nitro_test.go index 512c383..a9c6784 100644 --- a/internal/enclave/attester_nitro_test.go +++ b/internal/enclave/attester_nitro_test.go @@ -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) @@ -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 + } }) } } diff --git a/internal/enclave/attester_noop.go b/internal/enclave/attester_noop.go index 5df614a..7af5184 100644 --- a/internal/enclave/attester_noop.go +++ b/internal/enclave/attester_noop.go @@ -14,7 +14,7 @@ func NewNoopAttester() Attester { } func (*NoopAttester) Type() string { - return "noop" + return typeNoop } func (*NoopAttester) Attest(aux *AuxInfo) (*AttestationDoc, error) { @@ -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 diff --git a/internal/enclave/attester_noop_test.go b/internal/enclave/attester_noop_test.go index 7d333b0..e5f2ea6 100644 --- a/internal/enclave/attester_noop_test.go +++ b/internal/enclave/attester_noop_test.go @@ -16,16 +16,16 @@ 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) } @@ -33,6 +33,9 @@ func TestSuccessfulVerification(t *testing.T) { 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) } diff --git a/internal/enclave/util.go b/internal/enclave/util.go index cdcadc9..a11ad8c 100644 --- a/internal/enclave/util.go +++ b/internal/enclave/util.go @@ -9,3 +9,9 @@ func IsEnclave() bool { } return false } + +func ToAuxField(s []byte) [userDataLen]byte { + var a [userDataLen]byte + copy(a[:], s) + return a +} diff --git a/internal/nonce/nonce.go b/internal/nonce/nonce.go index 7e96061..09ec21b 100644 --- a/internal/nonce/nonce.go +++ b/internal/nonce/nonce.go @@ -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