From 897a3667f48267b45f82bac7848fc14daf6f71e4 Mon Sep 17 00:00:00 2001 From: Jernej Kos Date: Thu, 28 Apr 2022 18:30:58 +0200 Subject: [PATCH] go/common/sgx/aesm: Add support for newer methods --- go/common/sgx/aesm/aesm.go | 149 ++++++++++++++++++++++++++++++++++++ go/common/sgx/aesm/types.go | 99 ++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 go/common/sgx/aesm/types.go diff --git a/go/common/sgx/aesm/aesm.go b/go/common/sgx/aesm/aesm.go index 16ff0b49ddb..29029c1c87a 100644 --- a/go/common/sgx/aesm/aesm.go +++ b/go/common/sgx/aesm/aesm.go @@ -234,3 +234,152 @@ func (c *Client) GetQuote( return quote, nil } + +// GetAttestationKeyIDs returns the available attestation keys. +func (c *Client) GetAttestationKeyIDs(ctx context.Context) ([]*AttestationKeyID, error) { + // Request the number of attestation keys first. + timeout := uint32(localAESMTimeout.Nanoseconds() / 1000) + resp, err := c.transact(ctx, &Request{ + GetSupportedAttKeyIDNumReq: &Request_GetSupportedAttKeyIDNumRequest{ + Timeout: &timeout, + }, + }) + if err != nil { + return nil, err + } + if resp.GetSupportedAttKeyIDNumRes == nil { + return nil, errMalformedResponse + } + if errCode := resp.GetSupportedAttKeyIDNumRes.GetErrorCode(); errCode != 0 { + return nil, fmt.Errorf("aesm: get supported attestation key count: error %d", errCode) + } + keyCount := resp.GetSupportedAttKeyIDNumRes.GetAttKeyIdNum() + + // Request the attestation key IDs. + const sgxKeyIDSize = 256 + bufSize := uint32(keyCount * sgxKeyIDSize) + resp, err = c.transact(ctx, &Request{ + GetSupportedAttKeyIDsReq: &Request_GetSupportedAttKeyIDsRequest{ + BufSize: &bufSize, + Timeout: &timeout, + }, + }) + if err != nil { + return nil, err + } + if resp.GetSupportedAttKeyIDsRes == nil { + return nil, errMalformedResponse + } + if errCode := resp.GetSupportedAttKeyIDsRes.GetErrorCode(); errCode != 0 { + return nil, fmt.Errorf("aesm: get supported attestation key IDs: error %d", errCode) + } + keyIDsBuf := resp.GetSupportedAttKeyIDsRes.GetAttKeyIds() + if len(keyIDsBuf) != int(bufSize) { + return nil, errMalformedResponse + } + keyIDs := make([]*AttestationKeyID, 0, keyCount) + for i := 0; i < int(keyCount); i++ { + offset := i * sgxKeyIDSize + + var keyID AttestationKeyID + if err = keyID.UnmarshalBinary(keyIDsBuf[offset : offset+sgxKeyIDSize]); err != nil { + // Skip bad keys. + continue + } + keyID.Index = uint32(i) + keyIDs = append(keyIDs, &keyID) + } + + return keyIDs, nil +} + +// GetTargetInfo retrieves the target enclave information for QE. +func (c *Client) GetTargetInfo(ctx context.Context, keyID *AttestationKeyID) ([]byte, error) { + // First we need to determine the public key size so that we can pass it back to the same daemon + // that reported it. This is stupid, but it is how it is. + timeout := uint32(localAESMTimeout.Nanoseconds() / 1000) + noPubKey := false + resp, err := c.transact(ctx, &Request{ + InitQuoteExReq: &Request_InitQuoteExRequest{ + AttKeyId: keyID.raw, + BPubKeyId: &noPubKey, + Timeout: &timeout, + }, + }) + if err != nil { + return nil, err + } + if resp.InitQuoteExRes == nil { + return nil, errMalformedResponse + } + if errCode := resp.InitQuoteExRes.GetErrorCode(); errCode != 0 { + return nil, fmt.Errorf("aesm: get public key size: error %d", errCode) + } + pubKeySize := resp.InitQuoteExRes.GetPubKeyIdSize() + + // Now get the actual target info. + yesPubKey := true + resp, err = c.transact(ctx, &Request{ + InitQuoteExReq: &Request_InitQuoteExRequest{ + AttKeyId: keyID.raw, + BPubKeyId: &yesPubKey, + BufSize: &pubKeySize, + Timeout: &timeout, + }, + }) + if err != nil { + return nil, err + } + if resp.InitQuoteExRes == nil { + return nil, errMalformedResponse + } + if errCode := resp.InitQuoteExRes.GetErrorCode(); errCode != 0 { + return nil, fmt.Errorf("aesm: get QE target info: error %d", errCode) + } + + return resp.InitQuoteExRes.GetTargetInfo(), nil +} + +// GetQuoteEx exchanges the report for an attestation quote. +func (c *Client) GetQuoteEx(ctx context.Context, keyID *AttestationKeyID, report []byte) ([]byte, error) { + // First we need to determine the quote size so that we can pass it back to the same daemon that + // reported it. This is stupid, but it is how it is. + timeout := uint32(localAESMTimeout.Nanoseconds() / 1000) + resp, err := c.transact(ctx, &Request{ + GetQuoteSizeExReq: &Request_GetQuoteSizeExRequest{ + AttKeyId: keyID.raw, + Timeout: &timeout, + }, + }) + if err != nil { + return nil, err + } + if resp.GetQuoteSizeExRes == nil { + return nil, errMalformedResponse + } + if errCode := resp.GetQuoteSizeExRes.GetErrorCode(); errCode != 0 { + return nil, fmt.Errorf("aesm: get quote size: error %d", errCode) + } + quoteSize := resp.GetQuoteSizeExRes.GetQuoteSize() + + // Then request the quote itself. + resp, err = c.transact(ctx, &Request{ + GetQuoteExReq: &Request_GetQuoteExRequest{ + Report: report, + AttKeyId: keyID.raw, + BufSize: "eSize, + Timeout: &timeout, + }, + }) + if err != nil { + return nil, err + } + if resp.GetQuoteExRes == nil { + return nil, errMalformedResponse + } + if errCode := resp.GetQuoteExRes.GetErrorCode(); errCode != 0 { + return nil, fmt.Errorf("aesm: get quote: error %d", errCode) + } + + return resp.GetQuoteExRes.GetQuote(), nil +} diff --git a/go/common/sgx/aesm/types.go b/go/common/sgx/aesm/types.go new file mode 100644 index 00000000000..a096eca47be --- /dev/null +++ b/go/common/sgx/aesm/types.go @@ -0,0 +1,99 @@ +package aesm + +import ( + "encoding/binary" + "fmt" + + "github.com/oasisprotocol/oasis-core/go/common/sgx" +) + +// AttestationKeyType is the attestation key type. +type AttestationKeyType uint32 + +const ( + // AttestationKeyEPIDUnlinkable is the unlinkable EPID attestation key type. + AttestationKeyEPIDUnlinkable AttestationKeyType = 0 + // AttestationKeyEPIDLinkable is the unlinkable EPID attestation key type. + AttestationKeyEPIDLinkable AttestationKeyType = 1 + // AttestationKeyECDSA_P256 is the ECDSA-P256 attestation key type. + AttestationKeyECDSA_P256 AttestationKeyType = 2 +) + +// String returns a string representation of the attestation key type. +func (kt AttestationKeyType) String() string { + switch kt { + case AttestationKeyEPIDUnlinkable: + return "EPID-unlinkable" + case AttestationKeyEPIDLinkable: + return "EPID-linkable" + case AttestationKeyECDSA_P256: + return "ECDSA-P256" + default: + return "[unknown]" + } +} + +// AttestationKeyID is the parsed attestation key ID. +type AttestationKeyID struct { + // Index is the key index. + Index uint32 + + // Type is the attestation key type. + Type AttestationKeyType + + // MrSigner is the MRSIGNER of the Quoting Enclave. + MrSigner sgx.MrSigner + + // raw contains the raw representation of the attestation key ID which is needed in AESM API. + raw []byte +} + +// String returns a string representation of the attestation key identifier. +func (ak AttestationKeyID) String() string { + return fmt.Sprintf("", ak.Index, ak.Type, ak.MrSigner) +} + +// UnmarshalBinary decodes a binary marshaled attestation key identifier. +func (ak *AttestationKeyID) UnmarshalBinary(data []byte) error { + // See common/inc/sgx_quote.h in Intel SDK for details. + const ( + // Offset of mrsigner_length in sgx_att_key_id_ext_t. + sgxOffsetMrSignerLen = 4 + // Offset of mrsigner in sgx_att_key_id_ext_t. + sgxOffsetMrSigner = 6 + // Size of mrsigner in sgx_att_key_id_ext_t. + sgxSizeMrSigner = 48 + // Offset of algorithm_id in sgx_att_key_id_ext_t. + sgxOffsetAlgorithmID = 154 + // Size of the sgx_att_key_id_ext_t structure. + sgxSizeKeyID = 158 + ) + + if len(data) < sgxSizeKeyID { + return fmt.Errorf("malformed attestation key ID (size: %d expected: %d)", len(data), sgxSizeKeyID) + } + + mrSignerLen := int(binary.LittleEndian.Uint16(data[sgxOffsetMrSignerLen : sgxOffsetMrSignerLen+2])) + if mrSignerLen != sgx.MrSignerSize { + return fmt.Errorf("unsupported MRSIGNER size (got: %d expected: %d)", mrSignerLen, sgx.MrSignerSize) + } + + var mrSigner sgx.MrSigner + if err := mrSigner.UnmarshalBinary(data[sgxOffsetMrSigner : sgxOffsetMrSigner+mrSignerLen]); err != nil { + return fmt.Errorf("bad MRSIGNER: %w", err) + } + + algID := binary.LittleEndian.Uint32(data[sgxOffsetAlgorithmID : sgxOffsetAlgorithmID+4]) + switch AttestationKeyType(algID) { + case AttestationKeyEPIDUnlinkable, AttestationKeyEPIDLinkable, AttestationKeyECDSA_P256: + default: + return fmt.Errorf("unsupported key algorithm: %d", algID) + } + + ak.Index = 0 + ak.Type = AttestationKeyType(algID) + ak.MrSigner = mrSigner + ak.raw = data + + return nil +}