-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
runtime: Add conditional SGX attestation parsing for rofl.Register txs
- Loading branch information
Showing
3 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
runtime: Add conditional SGX attestation parsing for rofl.Register txs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package client | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"reflect" | ||
"unsafe" | ||
|
||
"github.com/oasisprotocol/oasis-core/go/common/cbor" | ||
"github.com/oasisprotocol/oasis-core/go/common/crypto/signature" | ||
"github.com/oasisprotocol/oasis-core/go/common/sgx" | ||
"github.com/oasisprotocol/oasis-core/go/common/sgx/pcs" | ||
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/rofl" | ||
|
||
"github.com/oasisprotocol/nexus/coreapi/v24.0/common/node" | ||
) | ||
|
||
// This file contains custom serialization logic for `rofl.Register` transactions. | ||
// It handles the parsing and enhanced processing of attestation data in rofl.Register transactions. | ||
// The goal is to produce a structured representation of the attestation and embed it into the transaction body. | ||
|
||
type Header struct { | ||
Version uint16 `json:"version"` | ||
TeeType pcs.TeeType `json:"tee_type"` | ||
QEVendorID []byte `json:"qe_vendor_id"` | ||
AttestationKeyType pcs.AttestationKeyType `json:"attestation_key_type"` | ||
} | ||
|
||
type ReportBody struct { | ||
ReportData []byte `json:"report_data"` | ||
EnclaveIdentity sgx.EnclaveIdentity `json:"enclave_identity"` | ||
} | ||
|
||
// Quote is an enclave quote. | ||
type EnclaveQuote struct { | ||
Header Header `json:"header"` | ||
ReportBody ReportBody `json:"report_body"` | ||
Signature pcs.QuoteSignature `json:"signature"` | ||
} | ||
|
||
// Parsed version of <TODO LINK>. | ||
type ParsedPSCQuoteBundle struct { | ||
TCB pcs.TCBBundle `json:"tcb"` | ||
Quote EnclaveQuote `json:"quote"` | ||
} | ||
|
||
// Matches: <TODO LINK>. | ||
type ParsedPCSQuote struct { | ||
PCS *ParsedPSCQuoteBundle `json:"pcs"` | ||
} | ||
|
||
// Matches: <TODO LINK> with updated Quote field. | ||
type ParsedAttestation struct { | ||
// Quote is the parsed quote. | ||
Quote ParsedPCSQuote `json:"quote"` | ||
|
||
// Height is the runtime's view of the consensus layer height at the time of attestation. | ||
Height uint64 `json:"height"` | ||
|
||
// Signature is the signature of the attestation by the enclave (RAK). | ||
Signature signature.RawSignature `json:"signature"` | ||
} | ||
|
||
// Serialize the transaction body with enhanced attestation parsing for SGX hardware. | ||
// If the CapabilityTEE's hardware type is SGX, attempts to parse the attestation field, | ||
// replacing it with a structured SGXAttestation. If parsing fails or the hardware type | ||
// is not SGX, the original transaction body is returned unchanged. | ||
func extractPCSQuote(untyped *map[string]interface{}) (*map[string]interface{}, error) { | ||
raw, err := json.Marshal(untyped) | ||
if err != nil { | ||
return untyped, fmt.Errorf("failed to marshal untyped body: %w", err) | ||
} | ||
var body rofl.Register | ||
if err := json.Unmarshal(raw, &body); err != nil { | ||
return untyped, fmt.Errorf("failed to unmarshal rofl.Register body: %w", err) | ||
} | ||
|
||
// If not SGX attestation, return original. | ||
if uint8(body.EndorsedCapability.CapabilityTEE.Hardware) != uint8(node.TEEHardwareIntelSGX) { | ||
return untyped, fmt.Errorf("not an SGX attestation") | ||
} | ||
|
||
// Try parsing the SGX Attestation. | ||
var sa node.SGXAttestation | ||
if err := cbor.Unmarshal(body.EndorsedCapability.CapabilityTEE.Attestation, &sa); err != nil { | ||
return untyped, fmt.Errorf("failed to unmarshal SGX attestation: %w", err) | ||
} | ||
|
||
// Try parsing the PCS Quote. (We don't try parsing the IAS quote since it's deprecated, so we mostly care for PCS). | ||
var pcsQuote pcs.Quote | ||
if sa.Quote.PCS == nil { | ||
return untyped, fmt.Errorf("missing PCS quote") | ||
} | ||
if err := pcsQuote.UnmarshalBinary(sa.Quote.PCS.Quote); err != nil { | ||
return untyped, fmt.Errorf("failed to unmarshal PCS quote: %w", err) | ||
} | ||
|
||
// Pick ReportBody from the PCS quote (TODO: add method to oasis-core to expose this). | ||
val := reflect.ValueOf(&pcsQuote).Elem() | ||
field := val.FieldByName("reportBody") | ||
if !field.IsValid() { | ||
return untyped, fmt.Errorf("failed to extract PCS report body") | ||
} | ||
fieldValue := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem() | ||
reportBody, ok := fieldValue.Interface().(pcs.ReportBody) | ||
if !ok { | ||
return untyped, fmt.Errorf("failed to extract PCS report body interface") | ||
} | ||
|
||
// Update the body with the parsed data. | ||
parsedRegister := struct { | ||
rofl.Register | ||
// Override Attestation field. | ||
EndorsedCapability struct { | ||
CapabilityTEE struct { | ||
node.CapabilityTEE | ||
Attestation ParsedAttestation `json:"attestation"` | ||
} `json:"capability_tee"` | ||
NodeEndorsement signature.Signature `json:"node_endorsement"` | ||
} `json:"ect"` //nolint: misspell | ||
}{ | ||
Register: body, | ||
} | ||
parsedRegister.EndorsedCapability.CapabilityTEE.Attestation = ParsedAttestation{ | ||
Quote: ParsedPCSQuote{ | ||
PCS: &ParsedPSCQuoteBundle{ | ||
TCB: sa.Quote.PCS.TCB, // PCS is not null, otherwise we wouldn't have reached this point. | ||
Quote: EnclaveQuote{ | ||
Header: Header{ | ||
Version: pcsQuote.Header().Version(), | ||
TeeType: pcsQuote.Header().TeeType(), | ||
QEVendorID: pcsQuote.Header().QEVendorID(), | ||
AttestationKeyType: pcsQuote.Header().AttestationKeyType(), | ||
}, | ||
ReportBody: ReportBody{ | ||
ReportData: reportBody.ReportData(), | ||
EnclaveIdentity: reportBody.AsEnclaveIdentity(), | ||
}, | ||
Signature: pcsQuote.Signature(), | ||
}, | ||
}, | ||
}, | ||
Height: sa.Height, | ||
Signature: sa.Signature, | ||
} | ||
|
||
raw, err = json.Marshal(parsedRegister) | ||
if err != nil { | ||
return untyped, fmt.Errorf("failed to marshal parsed body: %w", err) | ||
} | ||
var newrt *map[string]interface{} | ||
if err := json.Unmarshal(raw, &newrt); err != nil { | ||
return untyped, fmt.Errorf("failed to unmarshal parsed body: %w", err) | ||
} | ||
return newrt, nil | ||
} |