Skip to content

Commit

Permalink
go/common/sgx/aesm: Add support for newer methods
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Apr 29, 2022
1 parent 56532b1 commit 897a366
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 0 deletions.
149 changes: 149 additions & 0 deletions go/common/sgx/aesm/aesm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: &quoteSize,
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
}
99 changes: 99 additions & 0 deletions go/common/sgx/aesm/types.go
Original file line number Diff line number Diff line change
@@ -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("<AttestationKeyID index=%d type=%s MRSIGNER=%s>", 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
}

0 comments on commit 897a366

Please sign in to comment.