From b155b6f50ad9d3a128897cc3e1ac03f6b4069c38 Mon Sep 17 00:00:00 2001 From: Benjamin DeCoste Date: Mon, 27 Mar 2023 17:38:05 -0300 Subject: [PATCH] Add support for signed output from runtime (#256) --- cape.go | 17 +++++++++++-- cmd/cape/cmd/run.go | 14 +++++++++-- sdk/key.go | 7 +++--- sdk/run.go | 59 +++++++++++++++++++++++++++++++++------------ 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/cape.go b/cape.go index f549884..0788d34 100644 --- a/cape.go +++ b/cape.go @@ -1,6 +1,19 @@ package cli +import "github.com/capeprivacy/attest/attest" + +type Checksums struct { + Input []byte `json:"input"` + Function []byte `json:"function"` + Output []byte `json:"output"` +} + type RunResult struct { - Type string `json:"type"` - Message []byte `json:"message"` + Type string `json:"type"` + Message []byte `json:"message"` + Checksums Checksums `json:"checksums"` + SignedChecksums []byte `json:"signed_checksums"` + + DecodedAttestationDocument *attest.AttestationDoc `json:"decoded_attestation_document"` + RawAttestationDocument []byte `json:"raw_attestation_document"` } diff --git a/cmd/cape/cmd/run.go b/cmd/cape/cmd/run.go index 2464ae6..38a72cb 100644 --- a/cmd/cape/cmd/run.go +++ b/cmd/cape/cmd/run.go @@ -13,6 +13,8 @@ import ( "github.com/spf13/cobra" + "github.com/capeprivacy/attest/attest" + "github.com/capeprivacy/cli" "github.com/capeprivacy/cli/entities" @@ -228,8 +230,16 @@ func runPlain(result cli.RunResult) error { func runJSON(result cli.RunResult) error { return json.NewEncoder(os.Stdout).Encode(struct { - Output string `json:"output"` + Output string `json:"output"` + Checksums cli.Checksums `json:"checksums"` + SignedResults []byte `json:"signed_results"` + AttestationDoc *attest.AttestationDoc `json:"attestation_doc"` + RawAttestationDoc []byte `json:"raw_attestation_doc"` }{ - Output: string(result.Message), + Output: string(result.Message), + Checksums: result.Checksums, + SignedResults: result.SignedChecksums, + AttestationDoc: result.DecodedAttestationDocument, + RawAttestationDoc: result.RawAttestationDocument, }) } diff --git a/sdk/key.go b/sdk/key.go index 952144a..89f0d58 100644 --- a/sdk/key.go +++ b/sdk/key.go @@ -15,9 +15,10 @@ import ( ) type AttestationUserData struct { - FuncChecksum []byte `json:"func_checksum"` - KeyChecksum []byte `json:"key_checksum"` - CapeKey []byte `json:"key"` + FuncChecksum []byte `json:"func_checksum"` + KeyChecksum []byte `json:"key_checksum"` + CapeKey []byte `json:"key"` + SignatureVerificationKey []byte `json:"signature_verification_public_key"` } type KeyRequest struct { diff --git a/sdk/run.go b/sdk/run.go index c4eb02f..0dc905d 100644 --- a/sdk/run.go +++ b/sdk/run.go @@ -1,6 +1,10 @@ package sdk import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" "encoding/json" "fmt" "reflect" @@ -12,7 +16,7 @@ import ( "github.com/capeprivacy/cli" "github.com/capeprivacy/attest/attest" - "github.com/capeprivacy/cli/crypto" + capeCrypto "github.com/capeprivacy/cli/crypto" "github.com/capeprivacy/cli/entities" "github.com/capeprivacy/cli/pcrs" ) @@ -30,6 +34,11 @@ type RunRequest struct { Insecure bool } +type attestationDoc struct { + decoded *attest.AttestationDoc + raw []byte +} + // Run loads the given function into a secure enclave and invokes it on the given data, then returns the result. func Run(req RunRequest) (*cli.RunResult, error) { conn, doc, err := connect(req.URL, req.FunctionID, req.FunctionAuth, req.FuncChecksum, req.KeyChecksum, req.PcrSlice, req.Insecure) @@ -43,7 +52,7 @@ func Run(req RunRequest) (*cli.RunResult, error) { return invoke(doc, conn, req.Data) } -func connect(url string, functionID string, functionAuth entities.FunctionAuth, funcChecksum []byte, keyChecksum []byte, pcrSlice []string, insecure bool) (*websocket.Conn, *attest.AttestationDoc, error) { +func connect(url string, functionID string, functionAuth entities.FunctionAuth, funcChecksum []byte, keyChecksum []byte, pcrSlice []string, insecure bool) (*websocket.Conn, *attestationDoc, error) { endpoint := fmt.Sprintf("%s/v1/run/%s", url, functionID) authProtocolType := "cape.runtime" @@ -54,7 +63,7 @@ func connect(url string, functionID string, functionAuth entities.FunctionAuth, return nil, nil, err } - nonce, err := crypto.GetNonce() + nonce, err := capeCrypto.GetNonce() if err != nil { return nil, nil, err } @@ -114,20 +123,14 @@ func connect(url string, functionID string, functionAuth entities.FunctionAuth, return nil, nil, fmt.Errorf("returned key policy checksum did not match provided, got: %x, want %x", userData.KeyChecksum, keyChecksum) } - return conn, doc, nil + return conn, &attestationDoc{ + decoded: doc, + raw: attestDoc, + }, nil } -func invoke(doc *attest.AttestationDoc, conn *websocket.Conn, data []byte) (*cli.RunResult, error) { - if doc == nil { - log.Error("missing attestation document, you may need to run cape.Connect()") - return nil, errors.New("missing attestation document") - } - if conn == nil { - log.Error("missing websocket connection, you may need to run cape.Connect()") - return nil, errors.New("no active connection") - } - - encryptedData, err := crypto.LocalEncrypt(*doc, data) +func invoke(attestDoc *attestationDoc, conn *websocket.Conn, data []byte) (*cli.RunResult, error) { + encryptedData, err := capeCrypto.LocalEncrypt(*attestDoc.decoded, data) if err != nil { log.Println("error encrypting") return nil, err @@ -146,6 +149,32 @@ func invoke(doc *attest.AttestationDoc, conn *websocket.Conn, data []byte) (*cli return nil, err } log.Debugf("< Received Function Results.") + resData.DecodedAttestationDocument = attestDoc.decoded + resData.RawAttestationDocument = attestDoc.raw + + log.Debugf("* Verifying Function Results.") + + // TODO -- connect is already doing this + var ud AttestationUserData + if err := json.Unmarshal(attestDoc.decoded.UserData, &ud); err != nil { + return nil, err + } + + publicKey, err := x509.ParsePKCS1PublicKey(ud.SignatureVerificationKey) + if err != nil { + return nil, err + } + + c := sha256.New() + if err := json.NewEncoder(c).Encode(resData.Checksums); err != nil { + return nil, err + } + + if err := rsa.VerifyPSS(publicKey, crypto.SHA256, c.Sum(nil), resData.SignedChecksums, nil); err != nil { + return nil, err + } + + log.Debugf("* Function Results Verified") return resData, nil }