Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add QEMU-based Intel TDX provisioner #5861

Merged
merged 8 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .buildkite/rust/test_generic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ unset OASIS_UNSAFE_SKIP_AVR_VERIFY
# Run the build and tests
#########################
pushd $src_dir
CARGO_TARGET_DIR="${CARGO_TARGET_DIR}/default" cargo build --release --all --locked --exclude simple-keyvalue
CARGO_TARGET_DIR="${CARGO_TARGET_DIR}/default" cargo build --release --workspace --locked \
--exclude simple-keyvalue

cargo fmt -- --check
CARGO_TARGET_DIR="${CARGO_TARGET_DIR}/default" cargo test --all --locked --exclude simple-keyvalue

CARGO_TARGET_DIR="${CARGO_TARGET_DIR}/default" cargo test --workspace --locked \
--exclude simple-keyvalue
popd
1 change: 1 addition & 0 deletions .changelog/5861.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add QEMU-based Intel TDX provisioner
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ members = [
"tests/runtimes/simple-keymanager",
"tests/runtimes/simple-rofl",
]
exclude = [
# Example TDX runtime.
"tests/runtimes/simple-rofl-tdx",
]
resolver = "2"

[profile.release]
Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,19 @@ fmt: $(fmt-targets)
# Lint code, commits and documentation.
lint-targets := lint-rust lint-go lint-git lint-md lint-changelog lint-docs lint-go-mod-tidy

lint-rust:
@$(ECHO) "$(CYAN)*** Running cargo clippy linters...$(OFF)"
@cargo clippy --all-features -- -D warnings \
CARGO_CLIPPY_FLAGS := -D warnings \
-A clippy::upper-case-acronyms \
-A clippy::borrowed-box \
-A clippy::ptr-arg \
-A clippy::large_enum_variant \
-A clippy::field-reassign-with-default

lint-rust:
@$(ECHO) "$(CYAN)*** Running cargo clippy linters...$(OFF)"
@cargo clippy -- $(CARGO_CLIPPY_FLAGS)
@cargo clippy --features debug-mock-sgx -- $(CARGO_CLIPPY_FLAGS)
@cargo clippy --features tdx -- $(CARGO_CLIPPY_FLAGS)

lint-go:
@$(MAKE) -C go lint

Expand Down
6 changes: 3 additions & 3 deletions docs/oasis-node/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ oasis_storage_failures | Counter | Number of storage failures. | call | [storage
oasis_storage_latency | Summary | Storage call latency (seconds). | call | [storage/api](https://github.com/oasisprotocol/oasis-core/tree/master/go/storage/api/metrics.go)
oasis_storage_successes | Counter | Number of storage successes. | call | [storage/api](https://github.com/oasisprotocol/oasis-core/tree/master/go/storage/api/metrics.go)
oasis_storage_value_size | Summary | Storage call value size (bytes). | call | [storage/api](https://github.com/oasisprotocol/oasis-core/tree/master/go/storage/api/metrics.go)
oasis_tee_attestations_failed | Counter | Number of failed TEE attestations. | runtime | [runtime/host/sgx](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/host/sgx/metrics.go)
oasis_tee_attestations_performed | Counter | Number of TEE attestations performed. | runtime | [runtime/host/sgx](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/host/sgx/metrics.go)
oasis_tee_attestations_successful | Counter | Number of successful TEE attestations. | runtime | [runtime/host/sgx](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/host/sgx/metrics.go)
oasis_tee_attestations_failed | Counter | Number of failed TEE attestations. | runtime, kind | [runtime/host/sgx/common](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/host/sgx/common/metrics.go)
oasis_tee_attestations_performed | Counter | Number of TEE attestations performed. | runtime, kind | [runtime/host/sgx/common](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/host/sgx/common/metrics.go)
oasis_tee_attestations_successful | Counter | Number of successful TEE attestations. | runtime, kind | [runtime/host/sgx/common](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/host/sgx/common/metrics.go)
oasis_txpool_accepted_transactions | Counter | Number of accepted transactions (passing check tx). | runtime | [runtime/txpool](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/txpool/metrics.go)
oasis_txpool_local_queue_size | Gauge | Size of the local transactions schedulable queue (number of entries). | runtime | [runtime/txpool](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/txpool/metrics.go)
oasis_txpool_pending_check_size | Gauge | Size of the pending to be checked queue (number of entries). | runtime | [runtime/txpool](https://github.com/oasisprotocol/oasis-core/tree/master/go/runtime/txpool/metrics.go)
Expand Down
1 change: 1 addition & 0 deletions go/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ linters-settings:
- github.com/stretchr
- github.com/tidwall/btree
- github.com/tyler-smith/go-bip39
- github.com/mdlayher/vsock

linters:
disable-all: true
Expand Down
10 changes: 10 additions & 0 deletions go/common/crypto/hash/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"encoding/hex"
"errors"
"hash"
"io"

cmtbytes "github.com/cometbft/cometbft/libs/bytes"

Expand Down Expand Up @@ -149,6 +150,15 @@ func NewFromBytes(data ...[]byte) (h Hash) {
return
}

// NewFromReader creates a new hash by hashing data from the provided reader until EOF.
func NewFromReader(reader io.Reader) (Hash, error) {
b := NewBuilder()
if _, err := io.Copy(b, reader); err != nil {
return Hash{}, err
}
return b.Build(), nil
}

// LoadFromHexBytes creates a new hash by loading it from the given CometBFT
// HexBytes byte array.
func LoadFromHexBytes(data cmtbytes.HexBytes) (h Hash) {
Expand Down
19 changes: 2 additions & 17 deletions go/common/sgx/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,10 @@ func (m *MrEnclave) UnmarshalHex(text string) error {
// FromSgxs derives a MrEnclave from r, under the assumption that r will
// provide the entire `.sgxs` file.
func (m *MrEnclave) FromSgxs(r io.Reader) error {
// A `.sgxs` file's SHA256 digest is conveniently the MRENCLAVE.
var buf [32768]byte

h := sha256.New()
readLoop:
for {
l, err := r.Read(buf[:])
if l > 0 {
_, _ = h.Write(buf[:l])
}
switch err {
case nil:
case io.EOF:
break readLoop
default:
return fmt.Errorf("sgx: failed to read .sgxs: %w", err)
}
if _, err := io.Copy(h, r); err != nil {
return fmt.Errorf("sgx: failed to read sgxs: %w", err)
}

sum := h.Sum(nil)
return m.UnmarshalBinary(sum)
}
Expand Down
25 changes: 12 additions & 13 deletions go/runtime/host/sgx/tcb_cache.go → go/common/sgx/pcs/cache.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sgx
package pcs

import (
"bytes"
Expand All @@ -8,7 +8,6 @@ import (

"github.com/oasisprotocol/oasis-core/go/common/logging"
"github.com/oasisprotocol/oasis-core/go/common/persistent"
"github.com/oasisprotocol/oasis-core/go/common/sgx/pcs"
)

const (
Expand All @@ -18,23 +17,23 @@ const (
tcbCacheSlowRefreshInterval = 24 * time.Hour
)

func readBundleMinTimestamp(bundle *pcs.TCBBundle) (time.Time, error) {
func readBundleMinTimestamp(bundle *TCBBundle) (time.Time, error) {
var err error
var info pcs.TCBInfo
var info TCBInfo
if err = json.Unmarshal(bundle.TCBInfo.TCBInfo, &info); err != nil {
return time.Time{}, fmt.Errorf("could not unmarshal TCB bundle info: %w", err)
}
var bundleUpdate time.Time
if bundleUpdate, err = time.Parse(pcs.TimestampFormat, info.NextUpdate); err != nil {
if bundleUpdate, err = time.Parse(TimestampFormat, info.NextUpdate); err != nil {
return time.Time{}, fmt.Errorf("unreadable TCB bundle info next update timestamp: %w", err)
}

var identity pcs.QEIdentity
var identity QEIdentity
if err = json.Unmarshal(bundle.QEIdentity.EnclaveIdentity, &identity); err != nil {
return time.Time{}, fmt.Errorf("could not unmarshal TCB bundle QE identity: %w", err)
}
var identityUpdate time.Time
if identityUpdate, err = time.Parse(pcs.TimestampFormat, identity.NextUpdate); err != nil {
if identityUpdate, err = time.Parse(TimestampFormat, identity.NextUpdate); err != nil {
return time.Time{}, fmt.Errorf("unreadable TCB bundle QE identity next update timestamp: %w", err)
}

Expand All @@ -45,10 +44,10 @@ func readBundleMinTimestamp(bundle *pcs.TCBBundle) (time.Time, error) {
}

type tcbBundleCache struct {
Bundle *pcs.TCBBundle `json:"bundle"`
FMSPC []byte `json:"fmspc"`
ExpectedExpiry time.Time `json:"expected_expiry"`
LastUpdate time.Time `json:"last_update"`
Bundle *TCBBundle `json:"bundle"`
FMSPC []byte `json:"fmspc"`
ExpectedExpiry time.Time `json:"expected_expiry"`
LastUpdate time.Time `json:"last_update"`
}

type tcbCache struct {
Expand All @@ -57,7 +56,7 @@ type tcbCache struct {
now func() time.Time
}

func (tc *tcbCache) check(fmspc []byte) (*pcs.TCBBundle, bool) {
func (tc *tcbCache) check(fmspc []byte) (*TCBBundle, bool) {
var err error

// Check if we have a copy in the local store.
Expand Down Expand Up @@ -98,7 +97,7 @@ func (tc *tcbCache) check(fmspc []byte) (*pcs.TCBBundle, bool) {
return stored.Bundle, refresh
}

func (tc *tcbCache) cache(tcbBundle *pcs.TCBBundle, fmspc []byte) {
func (tc *tcbCache) cache(tcbBundle *TCBBundle, fmspc []byte) {
expectedExpiry, err := readBundleMinTimestamp(tcbBundle)
if err != nil {
tc.logger.Error("could not determine next update timestamp from TCB bundle",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sgx
package pcs

import (
"encoding/json"
Expand All @@ -10,7 +10,6 @@ import (

"github.com/oasisprotocol/oasis-core/go/common/logging"
"github.com/oasisprotocol/oasis-core/go/common/persistent"
"github.com/oasisprotocol/oasis-core/go/common/sgx/pcs"
)

const loggerModule = "runtime/host/sgx/tests"
Expand All @@ -23,7 +22,7 @@ func (ft *fakeTime) get() time.Time {
return ft.now
}

func testStorageRoundtrip(t *testing.T, store *persistent.ServiceStore, bundle *pcs.TCBBundle) {
func testStorageRoundtrip(t *testing.T, store *persistent.ServiceStore, bundle *TCBBundle) {
require := require.New(t)
fmspc := []byte("fmspc")

Expand All @@ -34,7 +33,7 @@ func testStorageRoundtrip(t *testing.T, store *persistent.ServiceStore, bundle *
require.EqualValues(cached, bundle, "tcbCache.check")
}

func testFMSPCInvalidation(t *testing.T, store *persistent.ServiceStore, bundle *pcs.TCBBundle) {
func testFMSPCInvalidation(t *testing.T, store *persistent.ServiceStore, bundle *TCBBundle) {
require := require.New(t)
fmspc := []byte("fmspc")
expiryTime, err := readBundleMinTimestamp(bundle)
Expand All @@ -44,7 +43,7 @@ func testFMSPCInvalidation(t *testing.T, store *persistent.ServiceStore, bundle
}
tcbCache := newMockTcbCache(store, logging.GetLogger(loggerModule), ft.get)

var cached *pcs.TCBBundle
var cached *TCBBundle
var refresh bool

// Cache initial and check.
Expand All @@ -64,7 +63,7 @@ func testFMSPCInvalidation(t *testing.T, store *persistent.ServiceStore, bundle
require.False(refresh, "tcbCache.check 3")
}

func testCheckIntervals(t *testing.T, store *persistent.ServiceStore, bundle *pcs.TCBBundle) {
func testCheckIntervals(t *testing.T, store *persistent.ServiceStore, bundle *TCBBundle) {
require := require.New(t)
fmspc := []byte("fmspc")
expiryTime, err := readBundleMinTimestamp(bundle)
Expand Down Expand Up @@ -149,21 +148,21 @@ func TestTCBCache(t *testing.T) {
rawQEIdentity, err := os.ReadFile("testdata/qe_identity_v2.json") // From PCS V4 response.
require.NoError(err, "Read test vector")

var tcbInfo pcs.SignedTCBInfo
var tcbInfo SignedTCBInfo
err = json.Unmarshal(rawTCBInfo, &tcbInfo)
require.NoError(err, "Parse TCB info")

var qeIdentity pcs.SignedQEIdentity
var qeIdentity SignedQEIdentity
err = json.Unmarshal(rawQEIdentity, &qeIdentity)
require.NoError(err, "Parse QE identity")

tcbBundle := pcs.TCBBundle{
tcbBundle := TCBBundle{
TCBInfo: tcbInfo,
QEIdentity: qeIdentity,
Certificates: rawCerts,
}

for name, fun := range map[string]func(*testing.T, *persistent.ServiceStore, *pcs.TCBBundle){
for name, fun := range map[string]func(*testing.T, *persistent.ServiceStore, *TCBBundle){
"StorageRoundtrip": testStorageRoundtrip,
"CheckIntervals": testCheckIntervals,
"FMSPCInvalidation": testFMSPCInvalidation,
Expand Down
30 changes: 23 additions & 7 deletions go/common/sgx/pcs/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import (
const (
pcsAPISubscriptionKeyHeader = "Ocp-Apim-Subscription-Key"
pcsAPITimeout = 10 * time.Second
pcsAPIBaseURL = "https://api.trustedservices.intel.com/sgx"
pcsAPIGetPCKCertificatePath = "/certification/v4/pckcert"
pcsAPIGetRevocationListPath = "/certification/v4/pckcrl"
pcsAPIGetTCBInfoPath = "/certification/v4/tcb"
pcsAPIGetQEIdentityPath = "/certification/v4/qe/identity"
pcsAPIBaseURL = "https://api.trustedservices.intel.com"
pcsAPIGetPCKCertificatePath = "/sgx/certification/v4/pckcert"
pcsAPIGetRevocationListPath = "/sgx/certification/v4/pckcrl"
pcsAPIGetSgxTCBInfoPath = "/sgx/certification/v4/tcb"
pcsAPIGetTdxTCBInfoPath = "/tdx/certification/v4/tcb"
pcsAPIGetSgxQEIdentityPath = "/sgx/certification/v4/qe/identity"
pcsAPIGetTdxQEIdentityPath = "/tdx/certification/v4/qe/identity"
pcsAPICertChainHeader = "TCB-Info-Issuer-Chain"
pcsAPIPCKIIssuerChainHeader = "SGX-PCK-Certificate-Issuer-Chain"
)
Expand Down Expand Up @@ -88,8 +90,22 @@ func (hc *httpClient) getUrl(p string) *url.URL { // nolint: revive
return &u
}

func (hc *httpClient) GetTCBBundle(ctx context.Context, fmspc []byte, update UpdateType) (*TCBBundle, error) {
var tcbBundle TCBBundle
func (hc *httpClient) GetTCBBundle(ctx context.Context, teeType TeeType, fmspc []byte, update UpdateType) (*TCBBundle, error) {
var (
tcbBundle TCBBundle
pcsAPIGetTCBInfoPath string
pcsAPIGetQEIdentityPath string
)
switch teeType {
case TeeTypeSGX:
pcsAPIGetTCBInfoPath = pcsAPIGetSgxTCBInfoPath
pcsAPIGetQEIdentityPath = pcsAPIGetSgxQEIdentityPath
case TeeTypeTDX:
pcsAPIGetTCBInfoPath = pcsAPIGetTdxTCBInfoPath
pcsAPIGetQEIdentityPath = pcsAPIGetTdxQEIdentityPath
default:
return nil, fmt.Errorf("pcs: unsupported TEE type: %s", teeType)
}

// First fetch TCB info.
u := hc.getUrl(pcsAPIGetTCBInfoPath)
Expand Down
2 changes: 1 addition & 1 deletion go/common/sgx/pcs/pcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const (
// Client is an Intel SGX PCS client interface.
type Client interface {
// GetTCBBundle retrieves the signed TCB artifacts needed to verify a quote.
GetTCBBundle(ctx context.Context, fmspc []byte, update UpdateType) (*TCBBundle, error)
GetTCBBundle(ctx context.Context, teeType TeeType, fmspc []byte, update UpdateType) (*TCBBundle, error)

// GetPCKCertificateChain retrieves the PCK certificate chain for the given platform data or PPID.
//
Expand Down
Loading
Loading