Skip to content

Commit

Permalink
Merge pull request #269 from capeprivacy/chris/key-tests
Browse files Browse the repository at this point in the history
Add tests and authz token for Cape Key
  • Loading branch information
ChrisFriesen authored Apr 13, 2023
2 parents c420fe5 + b355151 commit faa9f82
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 57 deletions.
27 changes: 18 additions & 9 deletions cmd/cape/cmd/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"crypto/x509"
"encoding/pem"
"fmt"

"github.com/spf13/cobra"

Expand All @@ -30,6 +29,7 @@ func init() {
rootCmd.AddCommand(keyCmd)

keyCmd.PersistentFlags().StringSliceP("pcr", "p", []string{""}, "pass multiple PCRs to validate against, used while getting key for the first time")
keyCmd.PersistentFlags().StringP("token", "t", "", "authorization token to use")
}

func key(cmd *cobra.Command, args []string) error {
Expand All @@ -38,17 +38,22 @@ func key(cmd *cobra.Command, args []string) error {
return UserError{Msg: "error retrieving pcr flags", Err: err}
}

token, err := getAuthToken()
if err != nil {
return err
token, _ := cmd.Flags().GetString("token")
if token == "" {
t, err := authToken()
if err != nil {
return err
}

token = t
}

keyReq, err := GetKeyRequest(pcrSlice, token)
if err != nil {
return err
}

capeKey, err := sdk.Key(keyReq)
capeKey, err := keyFunc(keyReq)
if err != nil {
return err
}
Expand All @@ -57,18 +62,20 @@ func key(cmd *cobra.Command, args []string) error {
// ...but NOTE that Cape will only support decryption if envelope encryption is used.
p, err := x509.ParsePKIXPublicKey(capeKey)
if err != nil {
return err
return UserError{Msg: "error: key in unexpected format", Err: err}
}

m, err := x509.MarshalPKIXPublicKey(p)
if err != nil {
return err
return UserError{Msg: "error: key in unexpected format", Err: err}
}

fmt.Println(string(pem.EncodeToMemory(&pem.Block{
if _, err := cmd.OutOrStdout().Write(pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: m,
})))
})); err != nil {
return err
}

return nil
}
Expand All @@ -83,3 +90,5 @@ func GetKeyRequest(pcrSlice []string, token string) (sdk.KeyRequest, error) {
PcrSlice: pcrSlice,
}, nil
}

var keyFunc = sdk.Key
77 changes: 77 additions & 0 deletions cmd/cape/cmd/key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package cmd

import (
"errors"
"testing"

"github.com/capeprivacy/cli/sdk"
)

func TestKeyNoArgs(t *testing.T) {
cmd, stdout, stderr := getCmd()
cmd.SetArgs([]string{"key"})

want := `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoM2iIWF9ocxYGsSUnyt9
P7NLq3gv39uNJCdIGee/y8EQHhFEg6cJONPJP60E/3Zt4hnrYh4a4lx7rV0aWks5
KxpQi6LPP98sUKLkZO/ZTcY5Ugtn7FAQNj19ohtI39c2WCgxUB/1IR485jE1SLFn
x351mcog4V3pdU6THK1ZQTNhkonsLwyaP5TzpKySpz+OlgOBNDxqm6iRb7BQrc/w
hYj8Fpfj92m83cWk+jhlqUQwjMZ3b0B9jmSfzUNmEZEng/+Bw9hFpMH48LOsAHwg
z5tC1RhuGI5Is6VaKUeKbnptZQREIcXcs2857h+1i6EVW11shn4IRpOl3nvFoU+t
SDwpOQXs7oFcsEWz+qhpknMcQfd/fv/z4FSUuvStzlNO6bsGm8KBNtXLjTbhK4V7
Q44KcYulow/Dp4Rq3Pf+ZHgoqpfqujspWV1Sh++u6rPCte8lMozEIVd1scaCWw9S
w1id8sguJTfgccx1HBbp76q0U2zojfQf+EAyMHwN0/4JnqZ1mJZPhi9nGpnINZuy
wBsNPjtORYiDYLdLY7VL/O/tXsX03uVKfu6mQZNxhOSR2sD6AoEi/LaECMM+L96Q
EhOGvy7wILr1Zjc6KlUksXKOlXeKhJ0xxwcBWMznJG82WzeNczQ14I+I9RdkLUtP
vbU0SB7H7aX/bxqvQ+MOwS8CAwEAAQ==
-----END PUBLIC KEY-----
`

keyFunc = func(keyReq sdk.KeyRequest) ([]byte, error) {
return []byte("0\x82\x02\"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xa0͢!a}\xa1\xccX\x1aĔ\x9f+}?\xb3K\xabx/\xdfۍ$'H\x19\xe7\xbf\xcb\xc1\x10\x1e\x11D\x83\xa7\t8\xd3\xc9?\xad\x04\xffvm\xe2\x19\xebb\x1e\x1a\xe2\\{\xad]\x1aZK9+\x1aP\x8b\xa2\xcf?\xdf,P\xa2\xe4d\xef\xd9M\xc69R\vg\xecP\x106=}\xa2\x1bH\xdf\xd76X(1P\x1f\xf5!\x1e<\xe615H\xb1g\xc7~u\x99\xca \xe1]\xe9uN\x93\x1c\xadYA3a\x92\x89\xec/\f\x9a?\x94\U000e4b12\xa7?\x8e\x96\x03\x814<j\x9b\xa8\x91o\xb0P\xad\xcf\xf0\x85\x88\xfc\x16\x97\xe3\xf7i\xbc\xddŤ\xfa8e\xa9D0\x8c\xc6wo@}\x8ed\x9f\xcdCf\x11\x91'\x83\xff\x81\xc3\xd8E\xa4\xc1\xf8\xf0\xb3\xac\x00| ϛB\xd5\x18n\x18\x8eH\xb3\xa5Z)G\x8anzme\x04D!\xc5ܳo9\xee\x1f\xb5\x8b\xa1\x15[]l\x86~\bF\x93\xa5\xde{šO\xadH<)9\x05\xec\xee\x81\\\xb0E\xb3\xfa\xa8i\x92s\x1cA\xf7\x7f~\xff\xf3\xe0T\x94\xba\xf4\xad\xceSN\xe9\xbb\x06\x9b\u00816\xd5ˍ6\xe1+\x85{C\x8e\nq\x8b\xa5\xa3\x0fç\x84j\xdc\xf7\xfedx(\xaa\x97\xea\xba;)Y]R\x87\xef\xae\xea\xb3µ\xef%2\x8c\xc4!Wu\xb1Ƃ[\x0fR\xc3X\x9d\xf2\xc8.%7\xe0q\xccu\x1c\x16\xe9華Sl\xe8\x8d\xf4\x1f\xf8@20|\r\xd3\xfe\t\x9e\xa6u\x98\x96O\x86/g\x1a\x99\xc85\x9b\xb2\xc0\x1b\r>;NE\x88\x83`\xb7Kc\xb5K\xfc\xef\xed^\xc5\xf4\xde\xe5J~\xee\xa6A\x93q\x84\xe4\x91\xda\xc0\xfa\x02\x81\"\xfc\xb6\x84\b\xc3>/ސ\x12\x13\x86\xbf.\xf0 \xba\xf5f7:*U$\xb1r\x8e\x95w\x8a\x84\x9d1\xc7\a\x01X\xcc\xe7$o6[7\x8ds45\xe0\x8f\x88\xf5\x17d-KO\xbd\xb54H\x1e\xc7\xed\xa5\xffo\x1a\xafC\xe3\x0e\xc1/\x02\x03\x01\x00\x01"), nil
}
authToken = func() (string, error) {
return "you're you", nil
}
defer func() {
keyFunc = sdk.Key
authToken = getAuthToken
}()

if err := cmd.Execute(); err != nil {
t.Fatalf("received unexpected error: %s", err)
}

if got, want := stderr.String(), ""; got != want {
t.Fatalf("didn't get expected stderr, got %s, wanted %s", got, want)
}

if got, want := stdout.String(), want; got != want {
t.Fatalf("didn't get expected stdout, got %s, wanted %s", got, want)
}
}

func TestKeyInvalidFormat(t *testing.T) {
cmd, _, _ := getCmd()
cmd.SetArgs([]string{"key"})

keyFunc = func(keyReq sdk.KeyRequest) ([]byte, error) {
return []byte("-----BEGIN PUBLIC KEY-----\nTestKey\n-----END PUBLIC KEY-----\n"), nil
}
authToken = func() (string, error) {
return "you're you", nil
}
defer func() {
keyFunc = sdk.Key
authToken = getAuthToken
}()

err := cmd.Execute()
if err == nil {
t.Fatalf("expected an error: %s", err)
}

if !errors.As(err, &UserError{}) {
t.Fatalf("expected different error: %s", err)
}
}
66 changes: 62 additions & 4 deletions mocks/mocks.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,69 @@
package mocks

import "github.com/capeprivacy/attest/attest"
import (
"github.com/capeprivacy/attest/attest"
"github.com/capeprivacy/cli"
"github.com/capeprivacy/cli/entities"
)

type MockVerifier struct {
type Verifier struct {
VerifyFn func(attestation []byte, nonce []byte) (*attest.AttestationDoc, error)
}

func (m MockVerifier) Verify(attestation []byte, nonce []byte) (*attest.AttestationDoc, error) {
return m.VerifyFn(attestation, nonce)
func (v Verifier) Verify(attestation []byte, nonce []byte) (*attest.AttestationDoc, error) {
if v.VerifyFn != nil {
return v.VerifyFn(attestation, nonce)
}
return &attest.AttestationDoc{}, nil
}

type Protocol struct {
WriteStartFn func(req entities.StartRequest) error
ReadAttestationDocFn func() ([]byte, error)
ReadRunResultsFn func() (*cli.RunResult, error)
WriteBinaryFn func(b []byte) error
WriteFunctionInfoFn func(name string, public bool) error
ReadDeploymentResultsFn func() (*entities.SetDeploymentIDRequest, error)
}

func (p Protocol) WriteStart(req entities.StartRequest) error {
if p.WriteStartFn != nil {
return p.WriteStartFn(req)
}
return nil
}

func (p Protocol) ReadAttestationDoc() ([]byte, error) {
if p.ReadAttestationDocFn != nil {
return p.ReadAttestationDocFn()
}
return []byte{}, nil
}

func (p Protocol) ReadRunResults() (*cli.RunResult, error) {
if p.ReadRunResultsFn != nil {
return p.ReadRunResultsFn()
}
return &cli.RunResult{}, nil
}

func (p Protocol) WriteBinary(b []byte) error {
if p.WriteBinaryFn != nil {
return p.WriteBinaryFn(b)
}
return nil
}

func (p Protocol) WriteFunctionInfo(name string, public bool) error {
if p.WriteFunctionInfoFn != nil {
return p.WriteFunctionInfoFn(name, public)
}
return nil
}

func (p Protocol) ReadDeploymentResults() (*entities.SetDeploymentIDRequest, error) {
if p.ReadDeploymentResultsFn != nil {
return p.ReadDeploymentResultsFn()
}
return &entities.SetDeploymentIDRequest{}, nil
}
2 changes: 2 additions & 0 deletions pcrs/pcrs_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build integration

package pcrs

import (
Expand Down
12 changes: 5 additions & 7 deletions sdk/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func Key(keyReq KeyRequest) ([]byte, error) {
// If the key file isn't present we download it, but log this error anyway in case something else happened.
log.Debugf("Unable to open cape key file: %s", err)

capeKey, err = downloadAndSaveKey(keyReq)
capeKey, err = downloadAndSaveKey(keyReq, attest.NewVerifier())
if err != nil {
return nil, err
}
Expand All @@ -48,10 +48,10 @@ func Key(keyReq KeyRequest) ([]byte, error) {
return capeKey, nil
}

func downloadAndSaveKey(keyReq KeyRequest) ([]byte, error) {
func downloadAndSaveKey(keyReq KeyRequest, verifier Verifier) ([]byte, error) {
log.Debug("Downloading cape key...")

_, userData, err := ConnectAndAttest(keyReq)
_, userData, err := ConnectAndAttest(keyReq, verifier)
if err != nil {
log.Println("failed to attest")
return nil, err
Expand All @@ -71,7 +71,7 @@ func downloadAndSaveKey(keyReq KeyRequest) ([]byte, error) {
}

// TODO: Run, deploy and test could use this function.
func ConnectAndAttest(keyReq KeyRequest) (*attest.AttestationDoc, *AttestationUserData, error) {
func ConnectAndAttest(keyReq KeyRequest, verifier Verifier) (*attest.AttestationDoc, *AttestationUserData, error) {
endpoint := fmt.Sprintf("%s/v1/key", keyReq.URL)

authProtocolType := "cape.runtime"
Expand All @@ -91,7 +91,7 @@ func ConnectAndAttest(keyReq KeyRequest) (*attest.AttestationDoc, *AttestationUs
return nil, nil, err
}

p := getProtocol(conn)
p := getProtocolFn(conn)

req := entities.StartRequest{Nonce: nonce}
log.Debug("\n> Sending Nonce and Auth Token")
Expand All @@ -107,8 +107,6 @@ func ConnectAndAttest(keyReq KeyRequest) (*attest.AttestationDoc, *AttestationUs
return nil, nil, err
}

verifier := attest.NewVerifier()

log.Debug("< Auth Completed. Received Attestation Document")
doc, err := verifier.Verify(attestDoc, nonce)
if err != nil {
Expand Down
109 changes: 109 additions & 0 deletions sdk/key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package sdk

import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"

"github.com/gorilla/websocket"

"github.com/capeprivacy/attest/attest"
"github.com/capeprivacy/cli/mocks"
)

func TestKeyNotPresent(t *testing.T) {
dir := t.TempDir()
file := "testKey.pub.der"
want := []byte("key")

// Set up attestation
userData := AttestationUserData{
CapeKey: want,
}
data, err := json.Marshal(userData)
if err != nil {
t.Errorf("unable to set up attestation user data: %s", err)
}
doc := attest.AttestationDoc{
UserData: data,
}

verifier := mocks.Verifier{
VerifyFn: func(attestation []byte, nonce []byte) (*attest.AttestationDoc, error) {
return &doc, nil
},
}

getProtocolFn = func(ws *websocket.Conn) protocol {
return mocks.Protocol{}
}
defer func() {
getProtocolFn = getProtocol
}()

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}

_, err := upgrader.Upgrade(w, r, nil)
if err != nil {
t.Fatal(err)
}
}))
defer s.Close()

req := KeyRequest{
ConfigDir: dir,
CapeKeyFile: file,
URL: wsURL(s.URL),
}

got, err := downloadAndSaveKey(req, verifier)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}

if !bytes.Equal(want, got) {
t.Fatalf("Key does not match, want: %s, got: %s", want, got)
}

savedKey, err := readFile(dir, file)
if err != nil {
t.Fatalf("Unexpected error reading file %s", err)
}

if !bytes.Equal(want, savedKey) {
t.Fatalf("Saved key does not match, want: %s, got: %s", want, got)
}
}

func TestKeyPresent(t *testing.T) {
dir := t.TempDir()
file := "testKey.pub.der"

want := []byte("-----BEGIN PUBLIC KEY-----\nTestKey\n-----END PUBLIC KEY-----")
err := os.WriteFile(filepath.Join(dir, file), want, os.ModePerm)
if err != nil {
t.Fatalf("Unable to setup key file err: %s", err)
}

req := KeyRequest{
ConfigDir: dir,
CapeKeyFile: file,
}

got, err := Key(req)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}

if !bytes.Equal(want, got) {
t.Fatalf("Key retrieved from file does not match, want: %s, got: %s", want, got)
}
}
Loading

0 comments on commit faa9f82

Please sign in to comment.