diff --git a/.buildkite/rust/test_generic.sh b/.buildkite/rust/test_generic.sh index 76410a3b207..7151b0cc605 100755 --- a/.buildkite/rust/test_generic.sh +++ b/.buildkite/rust/test_generic.sh @@ -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 diff --git a/.changelog/5861.feature.md b/.changelog/5861.feature.md new file mode 100644 index 00000000000..6fc500db9b6 --- /dev/null +++ b/.changelog/5861.feature.md @@ -0,0 +1 @@ +Add QEMU-based Intel TDX provisioner diff --git a/Cargo.lock b/Cargo.lock index 8369d1e3afa..3866d85940c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -489,6 +489,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.9.1" @@ -1501,9 +1507,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -1686,6 +1692,19 @@ dependencies = [ "void", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "7.1.3" @@ -1886,9 +1905,11 @@ dependencies = [ "intrusive-collections", "jsonrpc", "lazy_static", + "libc", "log", "lru", "mbedtls", + "nix 0.29.0", "num-bigint 0.4.6", "num-derive", "num-traits", @@ -1918,6 +1939,7 @@ dependencies = [ "tiny-keccak 2.0.2", "tokio", "tokio-retry", + "vsock", "x25519-dalek", "x509-parser 0.16.0", "yasna 0.5.2", @@ -3481,6 +3503,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vsock" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8b4d00e672f147fc86a09738fadb1445bd1c0a40542378dfb82909deeee688" +dependencies = [ + "libc", + "nix 0.29.0", +] + [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index d3c3d424092..32176414f27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/Makefile b/Makefile index 91793c3bb96..ce28f2aa4bf 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/docs/oasis-node/metrics.md b/docs/oasis-node/metrics.md index 41328232f06..666d26935a4 100644 --- a/docs/oasis-node/metrics.md +++ b/docs/oasis-node/metrics.md @@ -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 | [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 | [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 | [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) diff --git a/go/.golangci.yml b/go/.golangci.yml index 92b2e505d5b..5bdeabb5940 100644 --- a/go/.golangci.yml +++ b/go/.golangci.yml @@ -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 diff --git a/go/runtime/host/sgx/tcb_cache.go b/go/common/sgx/pcs/cache.go similarity index 82% rename from go/runtime/host/sgx/tcb_cache.go rename to go/common/sgx/pcs/cache.go index c9bd299ae25..f79cf284b27 100644 --- a/go/runtime/host/sgx/tcb_cache.go +++ b/go/common/sgx/pcs/cache.go @@ -1,4 +1,4 @@ -package sgx +package pcs import ( "bytes" @@ -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 ( @@ -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) } @@ -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 { @@ -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. @@ -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", diff --git a/go/runtime/host/sgx/tcb_cache_test.go b/go/common/sgx/pcs/cache_test.go similarity index 94% rename from go/runtime/host/sgx/tcb_cache_test.go rename to go/common/sgx/pcs/cache_test.go index afcca221dbd..8631eadadf4 100644 --- a/go/runtime/host/sgx/tcb_cache_test.go +++ b/go/common/sgx/pcs/cache_test.go @@ -1,4 +1,4 @@ -package sgx +package pcs import ( "encoding/json" @@ -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" @@ -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") @@ -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) @@ -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. @@ -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) @@ -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, diff --git a/go/common/sgx/pcs/http.go b/go/common/sgx/pcs/http.go index 71bf6defdd4..050e394cb28 100644 --- a/go/common/sgx/pcs/http.go +++ b/go/common/sgx/pcs/http.go @@ -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" ) @@ -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) diff --git a/go/common/sgx/pcs/pcs.go b/go/common/sgx/pcs/pcs.go index 2ffecd3c609..0bf07e15503 100644 --- a/go/common/sgx/pcs/pcs.go +++ b/go/common/sgx/pcs/pcs.go @@ -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. // diff --git a/go/common/sgx/pcs/quote.go b/go/common/sgx/pcs/quote.go index 8db58718a8d..a5a754327f6 100644 --- a/go/common/sgx/pcs/quote.go +++ b/go/common/sgx/pcs/quote.go @@ -37,6 +37,9 @@ const ( // DefaultMinTCBEvaluationDataNumber is the default minimum TCB evaluation data number. DefaultMinTCBEvaluationDataNumber = 12 // As of 2022-08-01. + + quoteVersionV3 = 3 + quoteVersionV4 = 4 ) // Quote is an enclave quote. @@ -46,15 +49,16 @@ type Quote struct { signature QuoteSignature } -const ( - quoteVersionV3 = 3 - quoteVersionV4 = 4 -) - // UnmarshalBinary decodes a Quote from a byte array. func (q *Quote) UnmarshalBinary(data []byte) error { + _, err := q.UnmarshalBinaryWithTrailing(data, false) + return err +} + +// UnmarshalBinaryWithTrailing decodes a Quote from a byte array, optionally allowing trailing data. +func (q *Quote) UnmarshalBinaryWithTrailing(data []byte, allowTrailing bool) (int, error) { if len(data) < quoteHeaderLen+reportBodySgxLen+quoteSigSizeLen { - return fmt.Errorf("pcs/quote: invalid quote length") + return 0, fmt.Errorf("pcs/quote: invalid quote length") } // Quote Header. @@ -64,22 +68,22 @@ func (q *Quote) UnmarshalBinary(data []byte) error { case quoteVersionV3: var qh QuoteHeaderV3 if err := qh.UnmarshalBinary(data[offset : offset+quoteHeaderLen]); err != nil { - return err + return 0, err } q.header = &qh case quoteVersionV4: var qh QuoteHeaderV4 if err := qh.UnmarshalBinary(data[offset : offset+quoteHeaderLen]); err != nil { - return err + return 0, err } q.header = &qh default: - return fmt.Errorf("pcs/quote: unsupported quote version %d", version) + return 0, fmt.Errorf("pcs/quote: unsupported quote version %d", version) } offset += quoteHeaderLen if !bytes.Equal(q.header.QEVendorID(), QEVendorID_Intel) { - return fmt.Errorf("pcs/quote: unsupported QE vendor: %X", q.header.QEVendorID()) + return 0, fmt.Errorf("pcs/quote: unsupported QE vendor: %X", q.header.QEVendorID()) } // Report body. @@ -87,18 +91,18 @@ func (q *Quote) UnmarshalBinary(data []byte) error { case TeeTypeSGX: var report SgxReport if err := report.UnmarshalBinary(data[offset : offset+reportBodySgxLen]); err != nil { - return err + return 0, err } q.reportBody = &report offset += reportBodySgxLen case TeeTypeTDX: if len(data) < offset+reportBodyTdLen+quoteSigSizeLen { - return fmt.Errorf("pcs/quote: invalid quote length") + return 0, fmt.Errorf("pcs/quote: invalid quote length") } var report TdReport if err := report.UnmarshalBinary(data[offset : offset+reportBodyTdLen]); err != nil { - return err + return 0, err } q.reportBody = &report offset += reportBodyTdLen @@ -107,8 +111,11 @@ func (q *Quote) UnmarshalBinary(data []byte) error { // Quote Signature Length. sigLen := int(binary.LittleEndian.Uint32(data[offset:])) offset += quoteSigSizeLen - if len(data) != offset+sigLen { - return fmt.Errorf("pcs/quote: unexpected trailing data") + if len(data) < offset+sigLen { + return 0, fmt.Errorf("pcs/quote: unexpected trailing data") + } + if !allowTrailing && len(data) != offset+sigLen { + return 0, fmt.Errorf("pcs/quote: unexpected trailing data") } // Quote Signature. @@ -116,14 +123,15 @@ func (q *Quote) UnmarshalBinary(data []byte) error { case AttestationKeyECDSA_P256: var qs QuoteSignatureECDSA_P256 if err := qs.UnmarshalBinary(q.header.Version(), data[offset:offset+sigLen]); err != nil { - return err + return 0, err } q.signature = &qs default: - return fmt.Errorf("pcs/quote: unsupported attestation key type: %s", q.header.AttestationKeyType()) + return 0, fmt.Errorf("pcs/quote: unsupported attestation key type: %s", q.header.AttestationKeyType()) } + offset += sigLen - return nil + return offset, nil } // Verify verifies the quote. @@ -195,6 +203,11 @@ func (q *Quote) Verify(policy *QuotePolicy, ts time.Time, tcb *TCBBundle) (*sgx. }, nil } +// Header returns the quote header. +func (q *Quote) Header() QuoteHeader { + return q.header +} + // Signature returns the quote signature. func (q *Quote) Signature() QuoteSignature { return q.signature @@ -208,6 +221,18 @@ const ( TeeTypeTDX TeeType = 0x00000081 ) +// String returns a string representation of the TEE type. +func (tt TeeType) String() string { + switch tt { + case TeeTypeSGX: + return "sgx" + case TeeTypeTDX: + return "tdx" + default: + return fmt.Sprintf("[unknown: %d]", tt) + } +} + // QuoteHeader is the quote header interface. type QuoteHeader interface { // Version returns the quote version. diff --git a/go/common/sgx/pcs/quote_test.go b/go/common/sgx/pcs/quote_test.go index 01947a43890..97ee170c158 100644 --- a/go/common/sgx/pcs/quote_test.go +++ b/go/common/sgx/pcs/quote_test.go @@ -368,6 +368,23 @@ func TestQuoteV4_TDX_ECDSA_P256_OutOfDate(t *testing.T) { require.ErrorContains(err, "TCB level not supported", "Verify quote signature") // The quote is out of date. } +func TestQuoteV4_TDX_ECDSA_P256_TrailingData(t *testing.T) { + require := require.New(t) + + rawQuote, err := os.ReadFile("testdata/quote_v4_tdx_ecdsa_p256_trailing.bin") + require.NoError(err, "Read test vector") + + var quote Quote + err = quote.UnmarshalBinary(rawQuote) + require.Error(err, "Parse quote (trailing data not allowed)") + + size, err := quote.UnmarshalBinaryWithTrailing(rawQuote, true) + require.NoError(err, "Parse quote (trailing data allowed)") + + err = quote.UnmarshalBinary(rawQuote[:size]) + require.NoError(err, "Parse quote (trimmed)") +} + func FuzzQuoteUnmarshal(f *testing.F) { // Seed corpus. raw1, _ := os.ReadFile("testdata/quote_v3_ecdsa_p256_pck_chain.bin") diff --git a/go/common/sgx/pcs/service.go b/go/common/sgx/pcs/service.go new file mode 100644 index 00000000000..05bc2463902 --- /dev/null +++ b/go/common/sgx/pcs/service.go @@ -0,0 +1,168 @@ +package pcs + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/oasisprotocol/oasis-core/go/common/logging" + "github.com/oasisprotocol/oasis-core/go/common/persistent" +) + +// serviceStoreName is the service name for the common store to use for SGX-related persistent data. +const serviceStoreName = "runtime_host_sgx" + +// QuoteService is a service for resolving raw quotes into quote bundles that include all of the +// needed collateral. +type QuoteService interface { + // ResolveQuote resolves a given raw quote into a full bundle with the required collateral. + ResolveQuote(ctx context.Context, rawQuote []byte, quotePolicy *QuotePolicy) (*QuoteBundle, error) +} + +type cachingQuoteService struct { + client Client + cache *tcbCache + logger *logging.Logger +} + +// NewCachingQuoteService creates a new caching quote service. +func NewCachingQuoteService( + client Client, + store *persistent.CommonStore, + logger *logging.Logger, +) QuoteService { + serviceStore := store.GetServiceStore(serviceStoreName) + + return &cachingQuoteService{ + client: client, + cache: newTcbCache(serviceStore, logger), + logger: logger, + } +} + +func (qs *cachingQuoteService) verifyBundle(quote Quote, quotePolicy *QuotePolicy, tcbBundle *TCBBundle, which string) error { + if tcbBundle == nil { + return fmt.Errorf("nil bundle is not valid") + } + _, err := quote.Verify(quotePolicy, time.Now(), tcbBundle) + var tcbErr *TCBOutOfDateError + switch { + case err == nil: + return nil + case errors.As(err, &tcbErr): + qs.logger.Error("TCB is not up to date", + "which", which, + "kind", tcbErr.Kind, + "tcb_status", tcbErr.Status.String(), + "advisory_ids", tcbErr.AdvisoryIDs, + ) + return tcbErr + default: + return fmt.Errorf("quote verification failed (%s bundle): %w", which, err) + } +} + +func (qs *cachingQuoteService) ResolveQuote(ctx context.Context, rawQuote []byte, quotePolicy *QuotePolicy) (*QuoteBundle, error) { + var quote Quote + size, err := quote.UnmarshalBinaryWithTrailing(rawQuote, true) + if err != nil { + return nil, fmt.Errorf("failed to parse quote: %w", err) + } + + // Check what information we need to retrieve based on what is in the quote. + sig, ok := quote.Signature().(*QuoteSignatureECDSA_P256) + if !ok { + return nil, fmt.Errorf("unsupported attestation key type: %s", sig.AttestationKeyType()) + } + + switch sig.CertificationData().(type) { + case *CertificationData_PCKCertificateChain: + // We have a PCK certificate chain and so are good to go. + case *CertificationData_PPID: + // We have a PPID, need to retrieve PCK certificate first. + // TODO: Fetch PCK certificate based on PPID and include it in the quote, replacing the + // PPID certification data with the PCK certificate chain certification data. + // e.g. sp.GetPCKCertificateChain(ctx, nil, data.PPID, data.CPUSVN, data.PCESVN, data.PCEID) + // + // Due to aesmd QuoteEx APIs not supporting certification data this currently + // cannot be easily implemented. Instead we rely on a quote provider to be installed. + return nil, fmt.Errorf("PPID certification data not yet supported; please install a quote provider") + default: + return nil, fmt.Errorf("unsupported certification data type: %s", sig.CertificationData().CertificationDataType()) + } + + // Verify PCK certificate and extract the information required to get the TCB bundle. + pckInfo, err := sig.VerifyPCK(time.Now()) + if err != nil { + return nil, fmt.Errorf("PCK verification failed: %w", err) + } + + // Verify the quote so we can catch errors early (the runtime and later consensus layer will + // also do their own verification). + // Check bundles in order: fresh first, then cached, then try downloading again if there was + // no scheduled refresh this time. + getTcbBundle := func(update UpdateType) (*TCBBundle, error) { + var fresh *TCBBundle + + cached, refresh := qs.cache.check(pckInfo.FMSPC) + if refresh { + if fresh, err = qs.client.GetTCBBundle(ctx, quote.Header().TeeType(), pckInfo.FMSPC, update); err != nil { + qs.logger.Warn("error downloading TCB refresh", + "err", err, + "update", update, + ) + } + if err = qs.verifyBundle(quote, quotePolicy, fresh, "fresh"); err == nil { + qs.cache.cache(fresh, pckInfo.FMSPC) + return fresh, nil + } + qs.logger.Warn("error verifying downloaded TCB refresh", + "err", err, + "update", update, + ) + } + + if err = qs.verifyBundle(quote, quotePolicy, cached, "cached"); err == nil { + return cached, nil + } + + // If downloaded already, don't try again but just return the last error. + if refresh { + qs.logger.Warn("error verifying cached TCB", + "err", err, + "update", update, + ) + return nil, fmt.Errorf("both fresh and cached TCB bundles failed verification, cached error: %w", err) + } + + // If not downloaded yet this time round, try forcing. Any errors are fatal. + if fresh, err = qs.client.GetTCBBundle(ctx, quote.Header().TeeType(), pckInfo.FMSPC, update); err != nil { + qs.logger.Warn("error downloading TCB", + "err", err, + "update", update, + ) + return nil, err + } + if err = qs.verifyBundle(quote, quotePolicy, fresh, "downloaded"); err != nil { + return nil, err + } + qs.cache.cache(fresh, pckInfo.FMSPC) + return fresh, nil + } + var tcbBundle *TCBBundle + for _, update := range []UpdateType{UpdateEarly, UpdateStandard} { + if tcbBundle, err = getTcbBundle(update); err == nil { + break + } + } + if err != nil { + return nil, err + } + + // Prepare quote structure. + return &QuoteBundle{ + Quote: rawQuote[:size], // Trim quote as it may contain extra data. + TCB: *tcbBundle, + }, nil +} diff --git a/go/common/sgx/pcs/testdata/quote_v4_tdx_ecdsa_p256_trailing.bin b/go/common/sgx/pcs/testdata/quote_v4_tdx_ecdsa_p256_trailing.bin new file mode 100644 index 00000000000..17ce874a178 Binary files /dev/null and b/go/common/sgx/pcs/testdata/quote_v4_tdx_ecdsa_p256_trailing.bin differ diff --git a/go/go.mod b/go/go.mod index 40af2a95b44..9657000be12 100644 --- a/go/go.mod +++ b/go/go.mod @@ -35,6 +35,7 @@ require ( github.com/ipfs/go-log/v2 v2.5.1 github.com/libp2p/go-libp2p v0.36.3 github.com/libp2p/go-libp2p-pubsub v0.12.0 + github.com/mdlayher/vsock v1.2.1 github.com/multiformats/go-multiaddr v0.13.0 github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 @@ -132,6 +133,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mdlayher/socket v0.4.1 // indirect github.com/miekg/dns v1.1.62 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect diff --git a/go/go.sum b/go/go.sum index 27684c7da5e..16a350dda85 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1362,6 +1362,10 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= +github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= diff --git a/go/oasis-node/cmd/node/node_control.go b/go/oasis-node/cmd/node/node_control.go index d9d57cecf34..0ba1a96f897 100644 --- a/go/oasis-node/cmd/node/node_control.go +++ b/go/oasis-node/cmd/node/node_control.go @@ -333,17 +333,9 @@ func (n *Node) getRuntimeStatus(ctx context.Context) (map[common.Namespace]contr } // Fetch provisioner type. - _, provisioner, err := rt.Host() - switch { - case err != nil: - n.logger.Error("failed to fetch host configuration", - "err", err, - ) - status.Provisioner = "pending" - case provisioner != nil: + status.Provisioner = "none" + if _, provisioner, err := rt.Host(); err == nil { status.Provisioner = provisioner.Name() - default: - status.Provisioner = "none" } runtimes[rt.ID()] = status diff --git a/go/oasis-test-runner/scenario/e2e/runtime/helpers_runtime.go b/go/oasis-test-runner/scenario/e2e/runtime/helpers_runtime.go index 7fe2529d786..a842228c3f2 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime/helpers_runtime.go +++ b/go/oasis-test-runner/scenario/e2e/runtime/helpers_runtime.go @@ -160,7 +160,7 @@ func (sc *Scenario) EnsureActiveVersionForComputeWorker(ctx context.Context, nod } provisioner := status.Runtimes[rt.ID()].Provisioner - if provisioner != "sandbox" && provisioner != "sgx" { + if provisioner == "none" { return fmt.Errorf("%s: unexpected runtime provisioner for runtime '%s': %s", node.Name, rt.ID(), provisioner) } diff --git a/go/runtime/bundle/bundle.go b/go/runtime/bundle/bundle.go index ebba2783a9a..8b27bd4a258 100644 --- a/go/runtime/bundle/bundle.go +++ b/go/runtime/bundle/bundle.go @@ -66,8 +66,23 @@ func (bnd *Bundle) Validate() error { needFiles = append(needFiles, []bundleFile{ { - descr: fmt.Sprintf("%s: TDX VM image", id), - fn: tdx.Image, + descr: fmt.Sprintf("%s: TDX virtual firmware", id), + fn: tdx.Firmware, + }, + { + descr: fmt.Sprintf("%s: TDX kernel image", id), + fn: tdx.Kernel, + optional: true, + }, + { + descr: fmt.Sprintf("%s: TDX initrd image", id), + fn: tdx.InitRD, + optional: true, + }, + { + descr: fmt.Sprintf("%s: TDX VM stage 2 image", id), + fn: tdx.Stage2Image, + optional: true, }, }..., ) diff --git a/go/runtime/bundle/manifest.go b/go/runtime/bundle/manifest.go index b987d1298cf..a462847bf97 100644 --- a/go/runtime/bundle/manifest.go +++ b/go/runtime/bundle/manifest.go @@ -139,16 +139,73 @@ func (s *SGXMetadata) Validate() error { // TDXMetadata is the TDX specific manifest metadata. type TDXMetadata struct { - // Image is the name of the VM image file. - Image string `json:"image"` - - // TODO: We may also need the corresponding measurements if these cannot be computed. + // Firmware is the name of the virtual firmware file. It should rarely change and multiple + // components may use the same firmware. + Firmware string `json:"firmware"` + // Kernel is the name of the kernel image file. It should rarely change and multiple components + // may use the same kernel. + Kernel string `json:"kernel,omitempty"` + // InitRD is the name of the initial RAM disk image file. It should rarely change and multiple + // components may use the same initrd. + InitRD string `json:"initrd,omitempty"` + // ExtraKernelOptions are the extra kernel options to pass to the kernel after any of the + // default options. Note that kernel options affect TD measurements. + ExtraKernelOptions []string `json:"extra_kernel_options,omitempty"` + + // Stage2Image is the name of the stage 2 VM image file. + Stage2Image string `json:"stage2_image,omitempty"` + + // Resources are the requested VM resources. + Resources TDXResources `json:"resources,omitempty"` } // Validate validates the TDX metadata structure for well-formedness. func (t *TDXMetadata) Validate() error { - if t.Image == "" { - return fmt.Errorf("VM image must be set") + if t.Firmware == "" { + return fmt.Errorf("firmware must be set") + } + if !t.HasKernel() && t.HasStage2() { + return fmt.Errorf("kernel must be set if stage 2 image is set") + } + if !t.HasKernel() && t.HasInitRD() { + return fmt.Errorf("kernel must be set if initrd image is set") + } + if err := t.Resources.Validate(); err != nil { + return err + } + return nil +} + +// HasKernel returns true iff the TDX metadata indicates there is a kernel present. +func (t *TDXMetadata) HasKernel() bool { + return t.Kernel != "" +} + +// HasInitRD returns true iff the TDX metadata indicates there is an initial RAM disk image present. +func (t *TDXMetadata) HasInitRD() bool { + return t.InitRD != "" +} + +// HasStage2 returns true iff the TDX metadata indicates there is a stage 2 image present. +func (t *TDXMetadata) HasStage2() bool { + return t.Stage2Image != "" +} + +// TDXResources are the requested VM resources for TDX VMs. +type TDXResources struct { + // Memory is the requested VM memory amount in megabytes. + Memory uint64 `json:"memory"` + // CPUCount is the requested number of vCPUs. + CPUCount uint8 `json:"cpus"` +} + +// Validate validates the VM resources. +func (r *TDXResources) Validate() error { + if r.Memory < 16 { + return fmt.Errorf("memory limit must be at least 16M") + } + if r.CPUCount < 1 { + return fmt.Errorf("vCPU count must be at least 1") } return nil } diff --git a/go/runtime/host/composite/composite.go b/go/runtime/host/composite/composite.go index e32e93350b3..4e07801b045 100644 --- a/go/runtime/host/composite/composite.go +++ b/go/runtime/host/composite/composite.go @@ -5,6 +5,8 @@ package composite import ( "context" "fmt" + "slices" + "strings" "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/logging" @@ -215,7 +217,18 @@ func (p *provisioner) NewRuntime(cfg host.Config) (host.Runtime, error) { // Implements host.Provisioner. func (p *provisioner) Name() string { - return "composite" + if len(p.kinds) == 0 { + return "composite{}" + } + + atoms := make([]string, 0, len(p.kinds)) + for kind, provisioner := range p.kinds { + atoms = append(atoms, kind.String()+": "+provisioner.Name()) + } + // Ensure deterministic order. + slices.Sort(atoms) + + return "composite{" + strings.Join(atoms, ", ") + "}" } // NewProvisioner returns a composite provisioner that dispatches to the actual provisioner based diff --git a/go/runtime/host/host.go b/go/runtime/host/host.go index 1f8483b96a2..d3ac72cda05 100644 --- a/go/runtime/host/host.go +++ b/go/runtime/host/host.go @@ -3,6 +3,7 @@ package host import ( "context" + "fmt" "path/filepath" "github.com/oasisprotocol/oasis-core/go/common" @@ -33,6 +34,18 @@ type Config struct { LocalConfig map[string]interface{} } +// GetComponent ensures that only a single component is configured for this runtime and returns it. +func (cfg *Config) GetComponent() (*bundle.Component, error) { + if numComps := len(cfg.Components); numComps != 1 { + return nil, fmt.Errorf("expected a single component (got %d)", numComps) + } + comp := cfg.Bundle.Manifest.GetComponentByID(cfg.Components[0]) + if comp == nil { + return nil, fmt.Errorf("component '%s' not available", cfg.Components[0]) + } + return comp, nil +} + // RuntimeBundle is a exploded runtime bundle ready for execution. type RuntimeBundle struct { *bundle.Bundle diff --git a/go/runtime/host/logger.go b/go/runtime/host/logger.go index af4aa8fcd58..5744a0bbc8f 100644 --- a/go/runtime/host/logger.go +++ b/go/runtime/host/logger.go @@ -1,6 +1,7 @@ package host import ( + "bytes" "encoding/json" "strings" @@ -11,7 +12,7 @@ import ( // Max number of bytes to buffer in the runtime log wrapper, i.e. roughly // the longest expected valid log line from the runtime. -const maxLogBufferSize = 10_000_000 +const maxLogBufferSize = 10_000 // RuntimeLogWrapper is a Writer that interprets data written to it as JSON-formatted // runtime logs, and re-logs the messages as oasis-node logs. For example, it @@ -76,6 +77,9 @@ func (w *RuntimeLogWrapper) rtLogger(module string) *logging.Logger { } func (w RuntimeLogWrapper) processLogLine(line []byte) { + // Trim extra whitespace. + line = bytes.TrimSpace(line) + // Interpret line as JSON. var m map[string]interface{} if err := json.Unmarshal(line, &m); err != nil { diff --git a/go/runtime/host/logger_test.go b/go/runtime/host/logger_test.go index 67a48d91a31..afef530d0b1 100644 --- a/go/runtime/host/logger_test.go +++ b/go/runtime/host/logger_test.go @@ -49,7 +49,7 @@ func TestRuntimeLogWrapper(t *testing.T) { `{"level":"info","module":"runtime","msg":"My info\\nwith a newline","ts":"2022"}`, `{"level":"debug","module":"runtime/dispatcher","msg":"My debug","ts":"2022-04-27"}`, `{"err":"some explanation","level":"error","module":"runtime/protocol","msg":"My error","ts":"2022-04-28"}`, - `{"level":"warn","module":"runtime","msg":"\\t\\tRandom crap","ts":"[^"]+"}`, + `{"level":"warn","module":"runtime","msg":"Random crap","ts":"[^"]+"}`, `{"level":"info","module":"runtime/foo","msg":"Should be recovered","ts":"2022"}`, ``, // Because we split on newline and the last log entry ends with a newline } diff --git a/go/runtime/host/sandbox/sandbox.go b/go/runtime/host/sandbox/sandbox.go index 4943422933a..5a937d06611 100644 --- a/go/runtime/host/sandbox/sandbox.go +++ b/go/runtime/host/sandbox/sandbox.go @@ -68,6 +68,7 @@ type Config struct { // HostInitializerParams contains parameters for the HostInitializer function. type HostInitializerParams struct { Runtime host.Runtime + Config *host.Config Version version.Version Process process.Process Connection protocol.Connection @@ -395,6 +396,7 @@ func (r *sandboxedRuntime) startProcess() (err error) { hp := &HostInitializerParams{ Runtime: r, + Config: &r.rtCfg, Version: *rtVersion, Process: p, Connection: pc, @@ -612,19 +614,17 @@ func (r *sandboxedRuntime) manager() { // DefaultGetSandboxConfig is the default function for generating sandbox configuration. func DefaultGetSandboxConfig(logger *logging.Logger, sandboxBinaryPath string) GetSandboxConfigFunc { return func(hostCfg host.Config, conn Connector, _ string) (process.Config, error) { - if numComps := len(hostCfg.Components); numComps != 1 { - return process.Config{}, fmt.Errorf("expected a single component (got %d)", numComps) - } - comp := hostCfg.Bundle.Manifest.GetComponentByID(hostCfg.Components[0]) - if comp == nil { - return process.Config{}, fmt.Errorf("component '%s' not available", hostCfg.Components[0]) + comp, err := hostCfg.GetComponent() + if err != nil { + return process.Config{}, err } logWrapper := host.NewRuntimeLogWrapper( logger, "runtime_id", hostCfg.Bundle.Manifest.ID, "runtime_name", hostCfg.Bundle.Manifest.Name, - "component", comp.Kind, + "component", comp.ID(), + "provisioner", "sandbox", ) us, ok := conn.(*UnixSocketConnector) if !ok { diff --git a/go/runtime/host/sgx/common/common.go b/go/runtime/host/sgx/common/common.go new file mode 100644 index 00000000000..7ab8f6367e8 --- /dev/null +++ b/go/runtime/host/sgx/common/common.go @@ -0,0 +1,200 @@ +// Package common implements common SGX functions. +package common + +import ( + "context" + "fmt" + "time" + + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/identity" + "github.com/oasisprotocol/oasis-core/go/common/logging" + "github.com/oasisprotocol/oasis-core/go/common/node" + "github.com/oasisprotocol/oasis-core/go/common/sgx/pcs" + sgxQuote "github.com/oasisprotocol/oasis-core/go/common/sgx/quote" + consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" + registry "github.com/oasisprotocol/oasis-core/go/registry/api" + "github.com/oasisprotocol/oasis-core/go/runtime/bundle/component" + "github.com/oasisprotocol/oasis-core/go/runtime/host" + "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" + "github.com/oasisprotocol/oasis-core/go/runtime/host/sandbox" +) + +// GetQuotePolicy fetches the quote policy for the given manifest/component. In case the policy is +// not available, return the fallback policy. +func GetQuotePolicy( + ctx context.Context, + rtCfg *host.Config, + cb consensus.Backend, + fallbackPolicy *sgxQuote.Policy, +) (*sgxQuote.Policy, error) { + comp, err := rtCfg.GetComponent() + if err != nil { + return nil, err + } + + switch comp.Kind { + case component.RONL: + // Load RONL policy from the consensus layer. + rt, err := cb.Registry().GetRuntime(ctx, ®istry.GetRuntimeQuery{ + Height: consensus.HeightLatest, + ID: rtCfg.Bundle.Manifest.ID, + IncludeSuspended: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to query runtime descriptor: %w", err) + } + if d := rt.DeploymentForVersion(rtCfg.Bundle.Manifest.Version); d != nil { + var sc node.SGXConstraints + if err = cbor.Unmarshal(d.TEE, &sc); err != nil { + return nil, fmt.Errorf("malformed runtime SGX constraints: %w", err) + } + + return sc.Policy, nil + } + return fallbackPolicy, nil + case component.ROFL: + // Always use fallback policy for ROFL components. + return fallbackPolicy, nil + default: + // No policy. + return fallbackPolicy, nil + } +} + +// EndorseCapabilityTEE endorses the given CapabilityTEE and submits the signed endorsement to the +// runtime over the given connection. +func EndorseCapabilityTEE( + ctx context.Context, + identity *identity.Identity, + capabilityTEE *node.CapabilityTEE, + conn protocol.Connection, + logger *logging.Logger, +) { + ri, err := conn.GetInfo() + if err != nil { + logger.Error("failed to get host information, not endorsing local component", + "err", err, + ) + return + } + if !ri.Features.EndorsedCapabilityTEE { + logger.Debug("runtime does not support endorsed TEE capabilities, skipping endorsement") + return + } + + // Endorse CapabilityTEE by signing it under the proper domain separation context. + nodeSignature, err := signature.Sign( + identity.NodeSigner, + node.EndorseCapabilityTEESignatureContext, + cbor.Marshal(capabilityTEE), + ) + if err != nil { + logger.Error("failed to sign endorsement of local component", + "err", err, + ) + return + } + + _, err = conn.Call(ctx, &protocol.Body{ + RuntimeCapabilityTEEUpdateEndorsementRequest: &protocol.RuntimeCapabilityTEEUpdateEndorsementRequest{ + EndorsedCapabilityTEE: node.EndorsedCapabilityTEE{ + CapabilityTEE: *capabilityTEE, + NodeEndorsement: *nodeSignature, + }, + }, + }) + if err != nil { + logger.Error("failed to update endorsement of local component", + "err", err, + ) + return + } + + logger.Debug("successfully updated component's TEE capability endorsement") +} + +// UpdateRuntimeQuote sends the given quote bundle to the runtime so it can be configured for remote +// attestation purposes. The runtime responds with a signed attestation. +func UpdateRuntimeQuote(ctx context.Context, conn protocol.Connection, quote *pcs.QuoteBundle) ([]byte, error) { + // Prepare quote structure. + q := sgxQuote.Quote{ + PCS: quote, + } + + // Call the runtime with the quote and TCB bundle. + rspBody, err := conn.Call( + ctx, + &protocol.Body{ + RuntimeCapabilityTEERakQuoteRequest: &protocol.RuntimeCapabilityTEERakQuoteRequest{ + Quote: q, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("error while configuring quote: %w", err) + } + rsp := rspBody.RuntimeCapabilityTEERakQuoteResponse + if rsp == nil { + return nil, fmt.Errorf("unexpected response from runtime") + } + + return cbor.Marshal(node.SGXAttestation{ + Versioned: cbor.NewVersioned(node.LatestSGXAttestationVersion), + Quote: q, + Height: rsp.Height, + Signature: rsp.Signature, + }), nil +} + +// AttestationWorker is the periodic re-attestation worker loop. +// +// It should be started in its own goroutine. +func AttestationWorker( + interval time.Duration, + logger *logging.Logger, + hp *sandbox.HostInitializerParams, + updateCapabilityFunc func(context.Context, *sandbox.HostInitializerParams) (*node.CapabilityTEE, error), +) { + t := time.NewTicker(interval) + defer t.Stop() + + logger = logger.With("runtime_id", hp.Runtime.ID()) + + // Get the event emitter. + eventEmitter, _ := hp.Runtime.(host.RuntimeEventEmitter) + + for { + select { + case <-hp.Process.Wait(): + // Process has terminated. + return + case <-t.C: + // Re-attest based on the configured interval. + case <-hp.NotifyUpdateCapabilityTEE: + // Re-attest when explicitly requested. Also reset the periodic ticker to make sure we + // don't needlessly re-attest too often. + t.Reset(interval) + } + + // Update CapabilityTEE. + logger.Info("regenerating CapabilityTEE") + + capabilityTEE, err := updateCapabilityFunc(context.Background(), hp) + if err != nil { + logger.Error("failed to regenerate CapabilityTEE", + "err", err, + ) + continue + } + + // Emit event about the updated CapabilityTEE. + if eventEmitter != nil { + eventEmitter.EmitEvent(&host.Event{Updated: &host.UpdatedEvent{ + Version: hp.Version, + CapabilityTEE: capabilityTEE, + }}) + } + } +} diff --git a/go/runtime/host/sgx/metrics.go b/go/runtime/host/sgx/common/metrics.go similarity index 82% rename from go/runtime/host/sgx/metrics.go rename to go/runtime/host/sgx/common/metrics.go index 0af6546d1fa..8aa48123606 100644 --- a/go/runtime/host/sgx/metrics.go +++ b/go/runtime/host/sgx/common/metrics.go @@ -1,10 +1,11 @@ -package sgx +package common import ( "sync" "github.com/prometheus/client_golang/prometheus" + "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/metrics" ) @@ -45,12 +46,14 @@ var ( metricsOnce sync.Once ) -// updateAttestationMetrics updates the attestation metrics if metrics are enabled. -func updateAttestationMetrics(runtime string, err error) { +// UpdateAttestationMetrics updates the attestation metrics if metrics are enabled. +func UpdateAttestationMetrics(runtimeID common.Namespace, err error) { if !metrics.Enabled() { return } + runtime := runtimeID.String() + teeAttestationsPerformed.With(prometheus.Labels{"runtime": runtime}).Inc() if err != nil { teeAttestationsFailed.With(prometheus.Labels{"runtime": runtime}).Inc() @@ -59,8 +62,8 @@ func updateAttestationMetrics(runtime string, err error) { } } -// initMetrics registers the metrics collectors if metrics are enabled. -func initMetrics() { +// InitMetrics registers the metrics collectors if metrics are enabled. +func InitMetrics() { if !metrics.Enabled() { return } diff --git a/go/runtime/host/sgx/ecdsa.go b/go/runtime/host/sgx/ecdsa.go index ebcec744352..b024c83d7d4 100644 --- a/go/runtime/host/sgx/ecdsa.go +++ b/go/runtime/host/sgx/ecdsa.go @@ -2,30 +2,23 @@ package sgx import ( "context" - "errors" "fmt" - "time" - "github.com/oasisprotocol/oasis-core/go/common" - "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/common/sgx/aesm" "github.com/oasisprotocol/oasis-core/go/common/sgx/pcs" - sgxQuote "github.com/oasisprotocol/oasis-core/go/common/sgx/quote" - "github.com/oasisprotocol/oasis-core/go/common/version" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" + "github.com/oasisprotocol/oasis-core/go/runtime/host" "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" + sgxCommon "github.com/oasisprotocol/oasis-core/go/runtime/host/sgx/common" ) type teeStateECDSA struct { - teeStateImplCommon - key *aesm.AttestationKeyID - - tcbCache *tcbCache + qs pcs.QuoteService + cfg *host.Config } -func (ec *teeStateECDSA) Init(ctx context.Context, sp *sgxProvisioner, runtimeID common.Namespace, version version.Version) ([]byte, error) { +func (ec *teeStateECDSA) Init(ctx context.Context, sp *sgxProvisioner, cfg *host.Config) ([]byte, error) { // Check whether the consensus layer even supports ECDSA attestations. regParams, err := sp.consensus.Registry().ConsensusParameters(ctx, consensus.HeightLatest) if err != nil { @@ -59,178 +52,31 @@ func (ec *teeStateECDSA) Init(ctx context.Context, sp *sgxProvisioner, runtimeID return nil, err } - ec.runtimeID = runtimeID - ec.version = version ec.key = key - - ec.tcbCache = newTcbCache(sp.serviceStore, sp.logger) + ec.qs = pcs.NewCachingQuoteService(sp.pcs, sp.store, sp.logger) + ec.cfg = cfg return targetInfo, nil } -func (ec *teeStateECDSA) verifyBundle(quote pcs.Quote, quotePolicy *pcs.QuotePolicy, tcbBundle *pcs.TCBBundle, sp *sgxProvisioner, which string) error { - if tcbBundle == nil { - return fmt.Errorf("nil bundle is not valid") - } - _, err := quote.Verify(quotePolicy, time.Now(), tcbBundle) - var tcbErr *pcs.TCBOutOfDateError - switch { - case err == nil: - return nil - case errors.As(err, &tcbErr): - sp.logger.Error("TCB is not up to date", - "which", which, - "kind", tcbErr.Kind, - "tcb_status", tcbErr.Status.String(), - "advisory_ids", tcbErr.AdvisoryIDs, - ) - return tcbErr - default: - return fmt.Errorf("quote verification failed (%s bundle): %w", which, err) - } -} - func (ec *teeStateECDSA) Update(ctx context.Context, sp *sgxProvisioner, conn protocol.Connection, report []byte, _ string) ([]byte, error) { rawQuote, err := sp.aesm.GetQuoteEx(ctx, ec.key, report) if err != nil { return nil, fmt.Errorf("failed to get quote: %w", err) } - var quote pcs.Quote - if err = quote.UnmarshalBinary(rawQuote); err != nil { - return nil, fmt.Errorf("failed to parse quote: %w", err) - } - - // Check what information we need to retrieve based on what is in the quote. - qs, ok := quote.Signature().(*pcs.QuoteSignatureECDSA_P256) - if !ok { - return nil, fmt.Errorf("unsupported attestation key type: %s", qs.AttestationKeyType()) - } - - switch qs.CertificationData().(type) { - case *pcs.CertificationData_PCKCertificateChain: - // We have a PCK certificate chain and so are good to go. - case *pcs.CertificationData_PPID: - // We have a PPID, need to retrieve PCK certificate first. - // TODO: Fetch PCK certificate based on PPID and include it in the quote, replacing the - // PPID certification data with the PCK certificate chain certification data. - // e.g. sp.pcs.GetPCKCertificateChain(ctx, nil, data.PPID, data.CPUSVN, data.PCESVN, data.PCEID) - // - // Due to aesmd QuoteEx APIs not supporting certification data this currently - // cannot be easily implemented. Instead we rely on a quote provider to be installed. - return nil, fmt.Errorf("PPID certification data not yet supported; please install a quote provider") - default: - return nil, fmt.Errorf("unsupported certification data type: %s", qs.CertificationData().CertificationDataType()) - } - - // Verify PCK certificate and extract the information required to get the TCB bundle. - pckInfo, err := qs.VerifyPCK(time.Now()) - if err != nil { - return nil, fmt.Errorf("PCK verification failed: %w", err) - } - - // Get current quote policy from the consensus layer. - var quotePolicy *pcs.QuotePolicy - var policies *sgxQuote.Policy - policies, err = ec.getQuotePolicies(ctx, sp) + quotePolicy, err := sgxCommon.GetQuotePolicy(ctx, ec.cfg, sp.consensus, nil) if err != nil { return nil, err } - if policies != nil { - quotePolicy = policies.PCS + var pcsQuotePolicy *pcs.QuotePolicy + if quotePolicy != nil { + pcsQuotePolicy = quotePolicy.PCS } - // Verify the quote so we can catch errors early (the runtime and later consensus layer will - // also do their own verification). - // Check bundles in order: fresh first, then cached, then try downloading again if there was - // no scheduled refresh this time. - getTcbBundle := func(update pcs.UpdateType) (*pcs.TCBBundle, error) { - var fresh *pcs.TCBBundle - - cached, refresh := ec.tcbCache.check(pckInfo.FMSPC) - if refresh { - if fresh, err = sp.pcs.GetTCBBundle(ctx, pckInfo.FMSPC, update); err != nil { - sp.logger.Warn("error downloading TCB refresh", - "err", err, - "update", update, - ) - } - if err = ec.verifyBundle(quote, quotePolicy, fresh, sp, "fresh"); err == nil { - ec.tcbCache.cache(fresh, pckInfo.FMSPC) - return fresh, nil - } - sp.logger.Warn("error verifying downloaded TCB refresh", - "err", err, - "update", update, - ) - } - - if err = ec.verifyBundle(quote, quotePolicy, cached, sp, "cached"); err == nil { - return cached, nil - } - - // If downloaded already, don't try again but just return the last error. - if refresh { - sp.logger.Warn("error verifying cached TCB", - "err", err, - "update", update, - ) - return nil, fmt.Errorf("both fresh and cached TCB bundles failed verification, cached error: %w", err) - } - - // If not downloaded yet this time round, try forcing. Any errors are fatal. - if fresh, err = sp.pcs.GetTCBBundle(ctx, pckInfo.FMSPC, update); err != nil { - sp.logger.Warn("error downloading TCB", - "err", err, - "update", update, - ) - return nil, err - } - if err = ec.verifyBundle(quote, quotePolicy, fresh, sp, "downloaded"); err != nil { - return nil, err - } - ec.tcbCache.cache(fresh, pckInfo.FMSPC) - return fresh, nil - } - var tcbBundle *pcs.TCBBundle - for _, update := range []pcs.UpdateType{pcs.UpdateEarly, pcs.UpdateStandard} { - if tcbBundle, err = getTcbBundle(update); err == nil { - break - } - } + quoteBundle, err := ec.qs.ResolveQuote(ctx, rawQuote, pcsQuotePolicy) if err != nil { return nil, err } - - // Prepare quote structure. - q := sgxQuote.Quote{ - PCS: &pcs.QuoteBundle{ - Quote: rawQuote, - TCB: *tcbBundle, - }, - } - - // Call the runtime with the quote and TCB bundle. - rspBody, err := conn.Call( - ctx, - &protocol.Body{ - RuntimeCapabilityTEERakQuoteRequest: &protocol.RuntimeCapabilityTEERakQuoteRequest{ - Quote: q, - }, - }, - ) - if err != nil { - return nil, fmt.Errorf("error while configuring quote: %w", err) - } - rsp := rspBody.RuntimeCapabilityTEERakQuoteResponse - if rsp == nil { - return nil, fmt.Errorf("unexpected response from runtime") - } - - return cbor.Marshal(node.SGXAttestation{ - Versioned: cbor.NewVersioned(node.LatestSGXAttestationVersion), - Quote: q, - Height: rsp.Height, - Signature: rsp.Signature, - }), nil + return sgxCommon.UpdateRuntimeQuote(ctx, conn, quoteBundle) } diff --git a/go/runtime/host/sgx/epid.go b/go/runtime/host/sgx/epid.go index 4bd323f6d11..4fcad54c80e 100644 --- a/go/runtime/host/sgx/epid.go +++ b/go/runtime/host/sgx/epid.go @@ -6,20 +6,19 @@ import ( "fmt" "time" - "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/common/node" cmnIAS "github.com/oasisprotocol/oasis-core/go/common/sgx/ias" sgxQuote "github.com/oasisprotocol/oasis-core/go/common/sgx/quote" - "github.com/oasisprotocol/oasis-core/go/common/version" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" ias "github.com/oasisprotocol/oasis-core/go/ias/api" + "github.com/oasisprotocol/oasis-core/go/runtime/host" "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" + sgxCommon "github.com/oasisprotocol/oasis-core/go/runtime/host/sgx/common" ) type teeStateEPID struct { - teeStateImplCommon - + cfg *host.Config epidGID uint32 // prevIAS is the index of the IAS server that was used for the last successful attestation. @@ -28,14 +27,13 @@ type teeStateEPID struct { prevIAS int } -func (ep *teeStateEPID) Init(ctx context.Context, sp *sgxProvisioner, runtimeID common.Namespace, version version.Version) ([]byte, error) { +func (ep *teeStateEPID) Init(ctx context.Context, sp *sgxProvisioner, cfg *host.Config) ([]byte, error) { qi, err := sp.aesm.InitQuote(ctx) if err != nil { return nil, fmt.Errorf("error while getting quote info from AESMD: %w", err) } - ep.runtimeID = runtimeID - ep.version = version + ep.cfg = cfg ep.epidGID = binary.LittleEndian.Uint32(qi.GID[:]) return qi.TargetInfo, nil @@ -109,23 +107,21 @@ func (ep *teeStateEPID) update( return nil, fmt.Errorf("error while getting quote: %w", err) } - // Get current quote policy from the consensus layer. - var quotePolicy *cmnIAS.QuotePolicy - var policies *sgxQuote.Policy - policies, err = ep.getQuotePolicies(ctx, sp) + quotePolicy, err := sgxCommon.GetQuotePolicy(ctx, ep.cfg, sp.consensus, nil) if err != nil { return nil, err } - if policies != nil { - quotePolicy = policies.IAS + var minTCBEvaluationDataNumber uint32 + if quotePolicy != nil && quotePolicy.IAS != nil { + minTCBEvaluationDataNumber = quotePolicy.IAS.MinTCBEvaluationDataNumber } evidence := ias.Evidence{ - RuntimeID: ep.runtimeID, + RuntimeID: ep.cfg.Bundle.Manifest.ID, Quote: quote, Nonce: nonce, EarlyTCBUpdate: true, - MinTCBEvaluationDataNumber: quotePolicy.MinTCBEvaluationDataNumber, + MinTCBEvaluationDataNumber: minTCBEvaluationDataNumber, } // First try with early updating. If that fails, fall back to normal. @@ -148,11 +144,11 @@ func (ep *teeStateEPID) update( if decErr != nil { return nil, fmt.Errorf("unable to decode AVR: %w", decErr) } - if avr.TCBEvaluationDataNumber < quotePolicy.MinTCBEvaluationDataNumber { + if avr.TCBEvaluationDataNumber < minTCBEvaluationDataNumber { return nil, fmt.Errorf( "AVR TCB data evaluation number invalid (%v < %v)", avr.TCBEvaluationDataNumber, - quotePolicy.MinTCBEvaluationDataNumber, + minTCBEvaluationDataNumber, ) } diff --git a/go/runtime/host/sgx/mock.go b/go/runtime/host/sgx/mock.go index 0e952bd7b80..8da65d04854 100644 --- a/go/runtime/host/sgx/mock.go +++ b/go/runtime/host/sgx/mock.go @@ -5,19 +5,16 @@ import ( "fmt" "time" - "github.com/oasisprotocol/oasis-core/go/common" - "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/common/sgx/pcs" - sgxQuote "github.com/oasisprotocol/oasis-core/go/common/sgx/quote" - "github.com/oasisprotocol/oasis-core/go/common/version" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" + "github.com/oasisprotocol/oasis-core/go/runtime/host" "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" + sgxCommon "github.com/oasisprotocol/oasis-core/go/runtime/host/sgx/common" ) type teeStateMock struct{} -func (ec *teeStateMock) Init(ctx context.Context, sp *sgxProvisioner, _ common.Namespace, _ version.Version) ([]byte, error) { +func (ec *teeStateMock) Init(ctx context.Context, sp *sgxProvisioner, _ *host.Config) ([]byte, error) { // Check whether the consensus layer even supports ECDSA attestations. regParams, err := sp.consensus.Registry().ConsensusParameters(ctx, consensus.HeightLatest) if err != nil { @@ -56,40 +53,14 @@ func (ec *teeStateMock) Update(ctx context.Context, sp *sgxProvisioner, conn pro return nil, fmt.Errorf("PCK verification failed: %w", err) } - tcbBundle, err := sp.pcs.GetTCBBundle(ctx, pckInfo.FMSPC, pcs.UpdateStandard) + tcbBundle, err := sp.pcs.GetTCBBundle(ctx, pcs.TeeTypeSGX, pckInfo.FMSPC, pcs.UpdateStandard) if err != nil { return nil, err } - // Prepare quote structure. - q := sgxQuote.Quote{ - PCS: &pcs.QuoteBundle{ - Quote: rawQuote, - TCB: *tcbBundle, - }, + quoteBundle := &pcs.QuoteBundle{ + Quote: rawQuote, + TCB: *tcbBundle, } - - // Call the runtime with the quote and TCB bundle. - rspBody, err := conn.Call( - ctx, - &protocol.Body{ - RuntimeCapabilityTEERakQuoteRequest: &protocol.RuntimeCapabilityTEERakQuoteRequest{ - Quote: q, - }, - }, - ) - if err != nil { - return nil, fmt.Errorf("error while configuring quote: %w", err) - } - rsp := rspBody.RuntimeCapabilityTEERakQuoteResponse - if rsp == nil { - return nil, fmt.Errorf("unexpected response from runtime") - } - - return cbor.Marshal(node.SGXAttestation{ - Versioned: cbor.NewVersioned(node.LatestSGXAttestationVersion), - Quote: q, - Height: rsp.Height, - Signature: rsp.Signature, - }), nil + return sgxCommon.UpdateRuntimeQuote(ctx, conn, quoteBundle) } diff --git a/go/runtime/host/sgx/sgx.go b/go/runtime/host/sgx/sgx.go index f1f7a2afd27..b85875be13c 100644 --- a/go/runtime/host/sgx/sgx.go +++ b/go/runtime/host/sgx/sgx.go @@ -11,9 +11,6 @@ import ( "sync" "time" - "github.com/oasisprotocol/oasis-core/go/common" - "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/identity" "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/common/node" @@ -22,7 +19,6 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/sgx/aesm" "github.com/oasisprotocol/oasis-core/go/common/sgx/pcs" "github.com/oasisprotocol/oasis-core/go/common/sgx/sigstruct" - "github.com/oasisprotocol/oasis-core/go/common/version" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" ias "github.com/oasisprotocol/oasis-core/go/ias/api" cmdFlags "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/flags" @@ -31,6 +27,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" "github.com/oasisprotocol/oasis-core/go/runtime/host/sandbox" "github.com/oasisprotocol/oasis-core/go/runtime/host/sandbox/process" + sgxCommon "github.com/oasisprotocol/oasis-core/go/runtime/host/sgx/common" ) const ( @@ -40,9 +37,6 @@ const ( sandboxMountRuntime = "/runtime" sandboxMountSignature = "/runtime.sig" - // The service name for the common store to use for SGX-related persistent data. - serviceStoreName = "runtime_host_sgx" - // Runtime RAK initialization timeout. // // This can take a long time in deployments that run multiple nodes on a single machine, all @@ -92,17 +86,14 @@ type Config struct { type teeStateImpl interface { // Init initializes the TEE state and returns the QE target info. - Init(ctx context.Context, sp *sgxProvisioner, runtimeID common.Namespace, version version.Version) ([]byte, error) + Init(ctx context.Context, sp *sgxProvisioner, cfg *host.Config) ([]byte, error) // Update updates the TEE state and returns a new attestation. Update(ctx context.Context, sp *sgxProvisioner, conn protocol.Connection, report []byte, nonce string) ([]byte, error) } type teeState struct { - runtimeID common.Namespace - version version.Version - eventEmitter host.RuntimeEventEmitter - + cfg *host.Config insecureMock bool impl teeStateImpl @@ -121,18 +112,18 @@ func (ts *teeState) init(ctx context.Context, sp *sgxProvisioner) ([]byte, error // When insecure mock SGX is enabled, use mock implementation. if ts.insecureMock { ts.impl = &teeStateMock{} - return ts.impl.Init(ctx, sp, ts.runtimeID, ts.version) + return ts.impl.Init(ctx, sp, ts.cfg) } // Try ECDSA first. If it fails, try EPID. implECDSA := &teeStateECDSA{} - if targetInfo, err = implECDSA.Init(ctx, sp, ts.runtimeID, ts.version); err != nil { + if targetInfo, err = implECDSA.Init(ctx, sp, ts.cfg); err != nil { sp.logger.Debug("ECDSA attestation initialization failed, trying EPID", "err", err, ) implEPID := &teeStateEPID{} - if targetInfo, err = implEPID.Init(ctx, sp, ts.runtimeID, ts.version); err != nil { + if targetInfo, err = implEPID.Init(ctx, sp, ts.cfg); err != nil { return nil, err } ts.impl = implEPID @@ -147,7 +138,7 @@ func (ts *teeState) updateTargetInfo(ctx context.Context, sp *sgxProvisioner) ([ if ts.impl == nil { return nil, fmt.Errorf("not initialized") } - return ts.impl.Init(ctx, sp, ts.runtimeID, ts.version) + return ts.impl.Init(ctx, sp, ts.cfg) } func (ts *teeState) update(ctx context.Context, sp *sgxProvisioner, conn protocol.Connection, report []byte, nonce string) ([]byte, error) { @@ -157,7 +148,7 @@ func (ts *teeState) update(ctx context.Context, sp *sgxProvisioner, conn protoco attestation, err := ts.impl.Update(ctx, sp, conn, report, nonce) - updateAttestationMetrics(ts.runtimeID.String(), err) + sgxCommon.UpdateAttestationMetrics(ts.cfg.Bundle.Manifest.ID, err) return attestation, err } @@ -173,9 +164,9 @@ type sgxProvisioner struct { aesm *aesm.Client consensus consensus.Backend identity *identity.Identity + store *persistent.CommonStore - logger *logging.Logger - serviceStore *persistent.ServiceStore + logger *logging.Logger } func (s *sgxProvisioner) loadEnclaveBinaries(rtCfg host.Config, comp *bundle.Component) ([]byte, []byte, error) { @@ -243,12 +234,9 @@ func (s *sgxProvisioner) discoverSGXDevice() (string, error) { } func (s *sgxProvisioner) getSandboxConfig(rtCfg host.Config, conn sandbox.Connector, runtimeDir string) (process.Config, error) { - if numComps := len(rtCfg.Components); numComps != 1 { - return process.Config{}, fmt.Errorf("expected a single component (got %d)", numComps) - } - comp := rtCfg.Bundle.Manifest.GetComponentByID(rtCfg.Components[0]) - if comp == nil { - return process.Config{}, fmt.Errorf("component '%s' not available", rtCfg.Components[0]) + comp, err := rtCfg.GetComponent() + if err != nil { + return process.Config{}, err } us, ok := conn.(*sandbox.UnixSocketConnector) @@ -302,7 +290,8 @@ func (s *sgxProvisioner) getSandboxConfig(rtCfg host.Config, conn sandbox.Connec s.logger, "runtime_id", rtCfg.Bundle.Manifest.ID, "runtime_name", rtCfg.Bundle.Manifest.Name, - "component", comp.Kind, + "component", comp.ID(), + "provisioner", s.Name(), ) args := []string{ @@ -339,7 +328,7 @@ func (s *sgxProvisioner) hostInitializer(ctx context.Context, hp *sandbox.HostIn // Initialize TEE. var err error var ts *teeState - if ts, err = s.initCapabilityTEE(ctx, hp.Runtime, hp.Connection, hp.Version); err != nil { + if ts, err = s.initCapabilityTEE(ctx, hp.Config, hp.Connection); err != nil { return nil, fmt.Errorf("failed to initialize TEE: %w", err) } var capabilityTEE *node.CapabilityTEE @@ -347,7 +336,10 @@ func (s *sgxProvisioner) hostInitializer(ctx context.Context, hp *sandbox.HostIn return nil, fmt.Errorf("failed to initialize TEE: %w", err) } - go s.attestationWorker(ts, hp) + // Start periodic re-attestation worker. + go sgxCommon.AttestationWorker(s.cfg.RuntimeAttestInterval, s.logger, hp, func(ctx context.Context, hp *sandbox.HostInitializerParams) (*node.CapabilityTEE, error) { + return s.updateCapabilityTEE(ctx, ts, hp.Connection) + }) return &host.StartedEvent{ Version: hp.Version, @@ -355,16 +347,12 @@ func (s *sgxProvisioner) hostInitializer(ctx context.Context, hp *sandbox.HostIn }, nil } -func (s *sgxProvisioner) initCapabilityTEE(ctx context.Context, rt host.Runtime, conn protocol.Connection, version version.Version) (*teeState, error) { +func (s *sgxProvisioner) initCapabilityTEE(ctx context.Context, cfg *host.Config, conn protocol.Connection) (*teeState, error) { ctx, cancel := context.WithTimeout(ctx, runtimeRAKTimeout) defer cancel() ts := teeState{ - runtimeID: rt.ID(), - version: version, - // We know that the runtime implementation provided by sandbox runtime provisioner - // implements the RuntimeEventEmitter interface. - eventEmitter: rt.(host.RuntimeEventEmitter), + cfg: cfg, insecureMock: s.cfg.InsecureMock, } @@ -431,93 +419,11 @@ func (s *sgxProvisioner) updateCapabilityTEE(ctx context.Context, ts *teeState, } // Endorse TEE capability to support authenticated inter-component EnclaveRPC. - s.endorseCapabilityTEE(ctx, capabilityTEE, conn) + sgxCommon.EndorseCapabilityTEE(ctx, s.identity, capabilityTEE, conn, s.logger) return capabilityTEE, nil } -func (s *sgxProvisioner) endorseCapabilityTEE(ctx context.Context, capabilityTEE *node.CapabilityTEE, conn protocol.Connection) { - ri, err := conn.GetInfo() - if err != nil { - s.logger.Error("failed to get host information, not endorsing local component", - "err", err, - ) - return - } - if !ri.Features.EndorsedCapabilityTEE { - s.logger.Debug("runtime does not support endorsed TEE capabilities, skipping endorsement") - return - } - - // Endorse CapabilityTEE by signing it under the proper domain separation context. - nodeSignature, err := signature.Sign( - s.identity.NodeSigner, - node.EndorseCapabilityTEESignatureContext, - cbor.Marshal(capabilityTEE), - ) - if err != nil { - s.logger.Error("failed to sign endorsement of local component", - "err", err, - ) - return - } - - _, err = conn.Call(ctx, &protocol.Body{ - RuntimeCapabilityTEEUpdateEndorsementRequest: &protocol.RuntimeCapabilityTEEUpdateEndorsementRequest{ - EndorsedCapabilityTEE: node.EndorsedCapabilityTEE{ - CapabilityTEE: *capabilityTEE, - NodeEndorsement: *nodeSignature, - }, - }, - }) - if err != nil { - s.logger.Error("failed to update endorsement of local component", - "err", err, - ) - return - } - - s.logger.Debug("successfully updated component's TEE capability endorsement") -} - -func (s *sgxProvisioner) attestationWorker(ts *teeState, hp *sandbox.HostInitializerParams) { - t := time.NewTicker(s.cfg.RuntimeAttestInterval) - defer t.Stop() - - logger := s.logger.With("runtime_id", ts.runtimeID) - - for { - select { - case <-hp.Process.Wait(): - // Process has terminated. - return - case <-t.C: - // Re-attest based on the configured interval. - case <-hp.NotifyUpdateCapabilityTEE: - // Re-attest when explicitly requested. Also reset the periodic ticker to make sure we - // don't needlessly re-attest too often. - t.Reset(s.cfg.RuntimeAttestInterval) - } - - // Update CapabilityTEE. - logger.Info("regenerating CapabilityTEE") - - capabilityTEE, err := s.updateCapabilityTEE(context.Background(), ts, hp.Connection) - if err != nil { - logger.Error("failed to regenerate CapabilityTEE", - "err", err, - ) - continue - } - - // Emit event about the updated CapabilityTEE. - ts.eventEmitter.EmitEvent(&host.Event{Updated: &host.UpdatedEvent{ - Version: hp.Version, - CapabilityTEE: capabilityTEE, - }}) - } -} - // Implements host.Provisioner. func (s *sgxProvisioner) NewRuntime(cfg host.Config) (host.Runtime, error) { // Make sure to return an error early if the SGX runtime loader is not configured. @@ -540,17 +446,17 @@ func New(cfg Config) (host.Provisioner, error) { cfg.RuntimeAttestInterval = defaultRuntimeAttestInterval } - initMetrics() + sgxCommon.InitMetrics() s := &sgxProvisioner{ - cfg: cfg, - ias: cfg.IAS, - pcs: cfg.PCS, - aesm: aesm.NewClient(aesmdSocketPath), - consensus: cfg.Consensus, - identity: cfg.Identity, - logger: logging.GetLogger("runtime/host/sgx"), - serviceStore: cfg.CommonStore.GetServiceStore(serviceStoreName), + cfg: cfg, + ias: cfg.IAS, + pcs: cfg.PCS, + aesm: aesm.NewClient(aesmdSocketPath), + consensus: cfg.Consensus, + identity: cfg.Identity, + store: cfg.CommonStore, + logger: logging.GetLogger("runtime/host/sgx"), } p, err := sandbox.New(sandbox.Config{ GetSandboxConfig: s.getSandboxConfig, diff --git a/go/runtime/host/sgx/tee_common.go b/go/runtime/host/sgx/tee_common.go deleted file mode 100644 index 34357d19980..00000000000 --- a/go/runtime/host/sgx/tee_common.go +++ /dev/null @@ -1,40 +0,0 @@ -package sgx - -import ( - "context" - "fmt" - - "github.com/oasisprotocol/oasis-core/go/common" - "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/node" - sgxQuote "github.com/oasisprotocol/oasis-core/go/common/sgx/quote" - "github.com/oasisprotocol/oasis-core/go/common/version" - consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" - registry "github.com/oasisprotocol/oasis-core/go/registry/api" -) - -type teeStateImplCommon struct { - runtimeID common.Namespace - version version.Version -} - -// getQuotePolicies gets the current quote policies from the consensus layer. -func (tsc *teeStateImplCommon) getQuotePolicies(ctx context.Context, sp *sgxProvisioner) (*sgxQuote.Policy, error) { - rt, err := sp.consensus.Registry().GetRuntime(ctx, ®istry.GetRuntimeQuery{ - Height: consensus.HeightLatest, - ID: tsc.runtimeID, - IncludeSuspended: true, - }) - if err != nil { - return nil, fmt.Errorf("failed to query runtime descriptor: %w", err) - } - if d := rt.DeploymentForVersion(tsc.version); d != nil { - var sc node.SGXConstraints - if err = cbor.Unmarshal(d.TEE, &sc); err != nil { - return nil, fmt.Errorf("malformed runtime SGX constraints: %w", err) - } - - return sc.Policy, nil - } - return nil, nil -} diff --git a/go/runtime/host/tdx/qemu.go b/go/runtime/host/tdx/qemu.go new file mode 100644 index 00000000000..1a8c90e0ffd --- /dev/null +++ b/go/runtime/host/tdx/qemu.go @@ -0,0 +1,361 @@ +package tdx + +import ( + "context" + "fmt" + "net" + "strings" + "sync" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/mdlayher/vsock" + + "github.com/oasisprotocol/oasis-core/go/common/identity" + "github.com/oasisprotocol/oasis-core/go/common/logging" + "github.com/oasisprotocol/oasis-core/go/common/node" + "github.com/oasisprotocol/oasis-core/go/common/persistent" + "github.com/oasisprotocol/oasis-core/go/common/sgx/pcs" + sgxQuote "github.com/oasisprotocol/oasis-core/go/common/sgx/quote" + consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" + "github.com/oasisprotocol/oasis-core/go/runtime/bundle/component" + "github.com/oasisprotocol/oasis-core/go/runtime/host" + "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" + "github.com/oasisprotocol/oasis-core/go/runtime/host/sandbox" + "github.com/oasisprotocol/oasis-core/go/runtime/host/sandbox/process" + sgxCommon "github.com/oasisprotocol/oasis-core/go/runtime/host/sgx/common" +) + +const ( + // defaultQemuSystemPath is the default QEMU system binary path. + defaultQemuSystemPath = "/usr/bin/qemu-system-x86_64" + // defaultStartCid is the default start CID. + defaultStartCid = 0xA5150000 + // defaultRuntimeAttestInterval is the default runtime (re-)attestation interval. + defaultRuntimeAttestInterval = 2 * time.Hour + + // vsockPortRHP is the VSOCK port used for the Runtime-Host Protocol. + vsockPortRHP = 1 + + runtimeConnectTimeout = 30 * time.Second +) + +// QemuConfig is the configuration of the QEMU-based TDX runtime provisioner. +type QemuConfig struct { + // HostInfo provides information about the host environment. + HostInfo *protocol.HostInfo + + // CommonStore is a handle to the node's common persistent store. + CommonStore *persistent.CommonStore + + // PCS is the Intel Provisioning Certification Service client. + PCS pcs.Client + // Consensus is the consensus layer backend. + Consensus consensus.Backend + // Identity is the node identity. + Identity *identity.Identity + + // RuntimeAttestInterval is the interval for periodic runtime re-attestation. If not specified + // a default will be used. + RuntimeAttestInterval time.Duration +} + +// QemuExtraConfig is the per-runtime QEMU-specific extra configuration. +type QemuExtraConfig struct { + // CID is the VSOCK cid to use for this runtime. If zero, the CID is automatically assigned. + CID uint32 +} + +type qemuProvisioner struct { + sync.Mutex + + cfg QemuConfig + + sandbox host.Provisioner + pcs pcs.Client + qs pcs.QuoteService + consensus consensus.Backend + identity *identity.Identity + + nextCid uint32 + + logger *logging.Logger +} + +// Implements host.Provisioner. +func (q *qemuProvisioner) NewRuntime(cfg host.Config) (host.Runtime, error) { + q.Lock() + defer q.Unlock() + + // Assign CID if not explicitly configured. + if cfg.Extra == nil { + cfg.Extra = &QemuExtraConfig{ + CID: q.nextCid, + } + q.nextCid++ + } + return q.sandbox.NewRuntime(cfg) +} + +// Implements host.Provisioner. +func (q *qemuProvisioner) Name() string { + return "tdx-qemu" +} + +func (q *qemuProvisioner) getSandboxConfig(rtCfg host.Config, conn sandbox.Connector, _ string) (process.Config, error) { + comp, err := rtCfg.GetComponent() + if err != nil { + return process.Config{}, err + } + if comp.TEEKind() != component.TEEKindTDX { + return process.Config{}, fmt.Errorf("component '%s' is not a TDX component", comp.ID()) + } + + cid := rtCfg.Extra.(*QemuExtraConfig).CID + + // Configure the CID on the VSOCK connector. + vs, ok := conn.(*vsockConnector) + if !ok { + return process.Config{}, fmt.Errorf("VSOCK connector is required") + } + vs.cid = cid + + bnd := rtCfg.Bundle + tdxCfg := comp.TDX + resources := tdxCfg.Resources + firmware := bnd.ExplodedPath(comp.ID(), tdxCfg.Firmware) + + cfg := process.Config{ + Path: defaultQemuSystemPath, + Args: []string{ + "-accel", "kvm", + "-m", fmt.Sprintf("%d", resources.Memory), + "-smp", fmt.Sprintf("%d", resources.CPUCount), + "-name", fmt.Sprintf("oasis-%s-%s", bnd.Manifest.ID, comp.ID()), + "-cpu", "host", + "-machine", "q35,kernel_irqchip=split,confidential-guest-support=tdx,hpet=off", + "-bios", firmware, + "-nographic", + "-nodefaults", + // Serial port. + "-serial", "stdio", + "-device", "virtio-serial,max_ports=1", + // TDX remote attestation via VSOCK. + "-object", `{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type": "vsock", "cid":"2","port":"4050"}}`, + // VSOCK. + "-device", fmt.Sprintf("vhost-vsock-pci,guest-cid=%d", cid), + }, + } + + // Configure kernel when one is available. We can set up TDs that only include the virtual + // firmware for special-purpose locked down TDs. + if tdxCfg.HasKernel() { + kernelImage := bnd.ExplodedPath(comp.ID(), tdxCfg.Kernel) + + cfg.Args = append(cfg.Args, "-kernel", kernelImage) + if tdxCfg.HasInitRD() { + initrdImage := bnd.ExplodedPath(comp.ID(), tdxCfg.InitRD) + + cfg.Args = append(cfg.Args, "-initrd", initrdImage) + } + + // Configure stage 2 image. + if tdxCfg.HasStage2() { + stage2Image := bnd.ExplodedPath(comp.ID(), tdxCfg.Stage2Image) + + cfg.Args = append(cfg.Args, + // Stage 2 disk. + "-drive", fmt.Sprintf("format=raw,file=%s,if=none,id=virtio-disk1,read-only=on", stage2Image), + "-device", "virtio-blk-pci,drive=virtio-disk1", + ) + } + + // Append any specified extra kernel options. + if len(tdxCfg.ExtraKernelOptions) > 0 { + cfg.Args = append(cfg.Args, + "-append", strings.Join(tdxCfg.ExtraKernelOptions, " "), + ) + } + } + + // Configure network access. + switch comp.IsNetworkAllowed() { + case true: + cfg.Args = append(cfg.Args, + "-netdev", "user,id=nic0_td", + ) + cfg.AllowNetwork = true + case false: + cfg.Args = append(cfg.Args, + "-netdev", "user,id=nic0_td,restrict=y", + ) + } + cfg.Args = append(cfg.Args, + "-device", "virtio-net-pci,netdev=nic0_td", + ) + + // Logging. + logWrapper := host.NewRuntimeLogWrapper( + q.logger, + "runtime_id", rtCfg.Bundle.Manifest.ID, + "runtime_name", rtCfg.Bundle.Manifest.Name, + "component", comp.ID(), + "provisioner", q.Name(), + ) + cfg.Stdout = logWrapper + cfg.Stderr = logWrapper + + return cfg, nil +} + +func (q *qemuProvisioner) updateCapabilityTEE(ctx context.Context, hp *sandbox.HostInitializerParams) (cap *node.CapabilityTEE, aerr error) { + defer func() { + sgxCommon.UpdateAttestationMetrics(hp.Runtime.ID(), aerr) + }() + + // Issue the RAK report request which will return the full quote in TDX since the attestation + // flow is handled transparently via the TDX guest device and VSOCK communication to qgsd + // running on the host. + rspRep, err := hp.Connection.Call( + ctx, + &protocol.Body{ + RuntimeCapabilityTEERakReportRequest: &protocol.Empty{}, + }, + ) + if err != nil { + return nil, fmt.Errorf("error while requesting worker quote and public RAK: %w", err) + } + rakPub := rspRep.RuntimeCapabilityTEERakReportResponse.RakPub + rekPub := rspRep.RuntimeCapabilityTEERakReportResponse.RekPub + rawQuote := rspRep.RuntimeCapabilityTEERakReportResponse.Report + + // Prepare the quote policy for local verification. In case a policy is not available or it + // indicates that TDX is not supported, use the fallback policy so we can provision something. + fallbackPolicy := &sgxQuote.Policy{ + PCS: &pcs.QuotePolicy{ + TCBValidityPeriod: 30, + MinTCBEvaluationDataNumber: 17, + TDX: &pcs.TdxQuotePolicy{}, + }, + } + quotePolicy, err := sgxCommon.GetQuotePolicy(ctx, hp.Config, q.consensus, fallbackPolicy) + if err != nil { + return nil, err + } + if quotePolicy.PCS == nil { + quotePolicy = fallbackPolicy + } + if quotePolicy.PCS.TDX == nil { + quotePolicy.PCS.TDX = fallbackPolicy.PCS.TDX + } + + // Resolve the quote and fetch required collateral. + quoteBundle, err := q.qs.ResolveQuote(ctx, rawQuote, quotePolicy.PCS) + if err != nil { + return nil, fmt.Errorf("error while resolving quote: %w", err) + } + + attestation, err := sgxCommon.UpdateRuntimeQuote(ctx, hp.Connection, quoteBundle) + if err != nil { + return nil, err + } + + capabilityTEE := &node.CapabilityTEE{ + Hardware: node.TEEHardwareIntelSGX, + RAK: rakPub, + REK: rekPub, + Attestation: attestation, + } + + // Endorse TEE capability to support authenticated inter-component EnclaveRPC. + sgxCommon.EndorseCapabilityTEE(ctx, q.identity, capabilityTEE, hp.Connection, q.logger) + + return capabilityTEE, nil +} + +func (q *qemuProvisioner) hostInitializer(ctx context.Context, hp *sandbox.HostInitializerParams) (*host.StartedEvent, error) { + capabilityTEE, err := q.updateCapabilityTEE(ctx, hp) + if err != nil { + return nil, err + } + + // Start periodic re-attestation worker. + go sgxCommon.AttestationWorker(q.cfg.RuntimeAttestInterval, q.logger, hp, q.updateCapabilityTEE) + + return &host.StartedEvent{ + Version: hp.Version, + CapabilityTEE: capabilityTEE, + }, nil +} + +// NewQemu creates a new QEMU-based TDX runtime provisioner. +func NewQemu(cfg QemuConfig) (host.Provisioner, error) { + // Use a default RuntimeAttestInterval if none was provided. + if cfg.RuntimeAttestInterval == 0 { + cfg.RuntimeAttestInterval = defaultRuntimeAttestInterval + } + + sgxCommon.InitMetrics() + + q := &qemuProvisioner{ + cfg: cfg, + pcs: cfg.PCS, + consensus: cfg.Consensus, + identity: cfg.Identity, + nextCid: defaultStartCid, // TODO: Could also include the local PID. + logger: logging.GetLogger("runtime/host/tdx/qemu"), + } + p, err := sandbox.New(sandbox.Config{ + Connector: newVsockConnector, + GetSandboxConfig: q.getSandboxConfig, + HostInfo: cfg.HostInfo, + HostInitializer: q.hostInitializer, + InsecureNoSandbox: true, // No sandbox is needed for TDX. + Logger: q.logger, + }) + if err != nil { + return nil, err + } + q.sandbox = p + + q.qs = pcs.NewCachingQuoteService(cfg.PCS, cfg.CommonStore, q.logger) + + return q, nil +} + +// vsockConnector is a VSOCK-based connector. +type vsockConnector struct { + cid uint32 +} + +func newVsockConnector(_ *logging.Logger, _ string, _ bool) (sandbox.Connector, error) { + return &vsockConnector{}, nil +} + +// ConfigureSandbox configures the process sandbox if needed. +func (vs *vsockConnector) ConfigureSandbox(_ *process.Config) error { + return nil +} + +// Connect establishes a connection to the runtime. +func (vs *vsockConnector) Connect(_ process.Process) (net.Conn, error) { + var ( + conn *vsock.Conn + err error + ) + op := func() error { + conn, err = vsock.Dial(vs.cid, vsockPortRHP, nil) + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), runtimeConnectTimeout) + defer cancel() + sched := backoff.NewConstantBackOff(1 * time.Second) + if err = backoff.Retry(op, backoff.WithContext(sched, ctx)); err != nil { + return nil, fmt.Errorf("failed to connect to the runtime VM: %w", err) + } + return conn, nil +} + +// Close releases any resources associated with the connector. +func (vs *vsockConnector) Close() {} diff --git a/go/runtime/host/tdx/tdx.go b/go/runtime/host/tdx/tdx.go new file mode 100644 index 00000000000..ac322055b53 --- /dev/null +++ b/go/runtime/host/tdx/tdx.go @@ -0,0 +1,2 @@ +// Package tdx implements the TDX runtime provisioner. +package tdx diff --git a/go/runtime/registry/config.go b/go/runtime/registry/config.go index 0bc34c8ba6e..b0fb71c011b 100644 --- a/go/runtime/registry/config.go +++ b/go/runtime/registry/config.go @@ -30,6 +30,7 @@ import ( hostProtocol "github.com/oasisprotocol/oasis-core/go/runtime/host/protocol" hostSandbox "github.com/oasisprotocol/oasis-core/go/runtime/host/sandbox" hostSgx "github.com/oasisprotocol/oasis-core/go/runtime/host/sgx" + hostTdx "github.com/oasisprotocol/oasis-core/go/runtime/host/tdx" ) const ( @@ -179,6 +180,14 @@ func newConfig( //nolint: gocyclo ConsensusChainContext: chainCtx, } + // Create the PCS client. + pc, err := pcs.NewHTTPClient(&pcs.HTTPClientConfig{ + // TODO: Support configuring the API key. + }) + if err != nil { + return nil, fmt.Errorf("failed to create PCS HTTP client: %w", err) + } + // Register provisioners based on the configured provisioner. var insecureNoSandbox bool sandboxBinary := config.GlobalConfig.Runtime.SandboxBinary @@ -238,15 +247,6 @@ func newConfig( //nolint: gocyclo // SGX may be needed, but we don't have a loader configured. break default: - // Configure the provided SGX loader. - var pc pcs.Client - pc, err = pcs.NewHTTPClient(&pcs.HTTPClientConfig{ - // TODO: Support configuring the API key. - }) - if err != nil { - return nil, fmt.Errorf("failed to create PCS HTTP client: %w", err) - } - // Configure mock SGX if configured and we are in a debug mode. insecureMock := runtimeEnv == rtConfig.RuntimeEnvironmentSGXMock if insecureMock && !cmdFlags.DebugDontBlameOasis() { @@ -274,6 +274,20 @@ func newConfig( //nolint: gocyclo return nil, fmt.Errorf("unsupported runtime provisioner: %s", p) } + // Configure TDX provisioner. + // TODO: Allow provisioner selection in the future, currently we only have QEMU. + provisioners[component.TEEKindTDX], err = hostTdx.NewQemu(hostTdx.QemuConfig{ + HostInfo: hostInfo, + CommonStore: commonStore, + PCS: pc, + Consensus: consensus, + Identity: identity, + RuntimeAttestInterval: attestInterval, + }) + if err != nil { + return nil, fmt.Errorf("failed to create TDX runtime provisioner: %w", err) + } + // Configure optional load balancing. for tee, rp := range provisioners { provisioners[tee] = hostLoadBalance.New(rp, hostLoadBalance.Config{ diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5bd45dac137..d297b90852f 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -63,6 +63,11 @@ lru = "0.12.4" async-trait = "0.1.82" cfg-if = "1.0" +# TDX dependencies. +libc = { version = "0.2.158", optional = true } +nix = { version = "0.29.0", features = ["mount", "signal"], optional = true } +vsock = { version = "0.5.1", optional = true } + [target.'cfg(not(target_env = "sgx"))'.dependencies.tokio] version = "1.40.0" features = ["full"] @@ -75,6 +80,8 @@ tendermint-testgen = "0.39.1" [features] default = [] +# Enables build for TDX. +tdx = ["dep:vsock", "dep:libc", "dep:nix"] # Enables debug-level logging in release builds. debug-logging = ["slog/max_level_debug", "slog/release_max_level_debug"] # Enables mock SGX in non-SGX builds. diff --git a/runtime/src/attestation.rs b/runtime/src/attestation.rs index c97f7475b13..82b04b73908 100644 --- a/runtime/src/attestation.rs +++ b/runtime/src/attestation.rs @@ -6,8 +6,8 @@ use slog::{info, Logger}; use crate::{ common::{ - crypto::signature::Signer, logger::get_logger, namespace::Namespace, sgx::Quote, - version::Version, + crypto::signature::Signer, logger::get_logger, namespace::Namespace, panic::AbortOnPanic, + sgx::Quote, version::Version, }, consensus::{ registry::{EndorsedCapabilityTEE, SGXAttestation, ATTESTATION_SIGNATURE_CONTEXT}, @@ -76,17 +76,20 @@ impl Handler { } fn target_info_init(&self, target_info: Vec) -> Result { + // Make sure to abort the process on panics during attestation process. + let _guard = AbortOnPanic; + info!(self.logger, "Initializing the runtime target info"); self.identity.init_target_info(target_info)?; Ok(Body::RuntimeCapabilityTEERakInitResponse {}) } fn report_init(&self) -> Result { - info!(self.logger, "Initializing the runtime key report"); - let (rak_pub, rek_pub, report, nonce) = self.identity.init_report(); + // Make sure to abort the process on panics during attestation process. + let _guard = AbortOnPanic; - let report: &[u8] = report.as_ref(); - let report = report.to_vec(); + info!(self.logger, "Initializing the runtime key report"); + let (rak_pub, rek_pub, report, nonce) = self.identity.init_report()?; Ok(Body::RuntimeCapabilityTEERakReportResponse { rak_pub, @@ -122,6 +125,9 @@ impl Handler { } async fn set_quote(&self, quote: Quote) -> Result { + // Make sure to abort the process on panics during attestation process. + let _guard = AbortOnPanic; + // Ensure a quote policy is configured. self.set_quote_policy().await?; diff --git a/runtime/src/common/mod.rs b/runtime/src/common/mod.rs index cf43ecde5e4..d05b3072d48 100644 --- a/runtime/src/common/mod.rs +++ b/runtime/src/common/mod.rs @@ -6,9 +6,12 @@ pub mod crypto; pub mod key_format; pub mod logger; pub mod namespace; +pub mod panic; pub mod process; pub mod quantity; pub mod sgx; +#[cfg(feature = "tdx")] +pub mod tdx; pub mod time; pub mod version; pub mod versioned; diff --git a/runtime/src/common/panic.rs b/runtime/src/common/panic.rs new file mode 100644 index 00000000000..cdf8ebcc37a --- /dev/null +++ b/runtime/src/common/panic.rs @@ -0,0 +1,16 @@ +//! Panic-related functions. + +/// A guard that will abort the process if dropped while panicking. +/// +/// This is to ensure that the runtime will terminate in case there is +/// a panic encountered during dispatch and the runtime is built with +/// a non-abort panic handler. +pub struct AbortOnPanic; + +impl Drop for AbortOnPanic { + fn drop(&mut self) { + if std::thread::panicking() { + crate::common::process::abort(); + } + } +} diff --git a/runtime/src/common/sgx/egetkey.rs b/runtime/src/common/sgx/egetkey.rs index def1a5d6ff3..d0b43c02f16 100644 --- a/runtime/src/common/sgx/egetkey.rs +++ b/runtime/src/common/sgx/egetkey.rs @@ -8,54 +8,57 @@ use sgx_isa::{Keyname, Keyrequest}; #[cfg(target_env = "sgx")] use tiny_keccak::{Hasher, Sha3}; -#[cfg(not(target_env = "sgx"))] -const MOCK_MRENCLAVE_KEY: &[u8] = b"Ekiden Test MRENCLAVE KEY"; -#[cfg(not(target_env = "sgx"))] -const MOCK_MRSIGNER_KEY: &[u8] = b"Ekiden Test MRSIGNER KEY"; -#[cfg(not(target_env = "sgx"))] -const MOCK_KDF_CUSTOM: &[u8] = b"Ekiden Extract Test SGX Seal Key"; - const SEAL_KDF_CUSTOM: &[u8] = b"Ekiden Expand SGX Seal Key"; -#[cfg(target_env = "sgx")] -fn egetkey_impl(key_policy: Keypolicy, context: &[u8]) -> [u8; 16] { - let mut req = Keyrequest::default(); - - req.keyname = Keyname::Seal as u16; - req.keypolicy = key_policy; - - let mut sha3 = Sha3::v256(); - sha3.update(context); - let mut k = [0; 32]; - sha3.finalize(&mut k); - req.keyid = k; - - // Fucking sgx_isa::Attributes doesn't have a -> [u64;2]. - req.attributemask[0] = 1 | 2 | 4; // SGX_FLAGS_INITTED | SGX_FLAGS_DEBUG | SGX_FLAGS_MODE64BIT - req.attributemask[1] = 3; // SGX_XFRM_LEGACY - - match req.egetkey() { - Err(e) => panic!("EGETKEY failed: {:?}", e), - Ok(k) => k, +cfg_if::cfg_if! { + if #[cfg(target_env = "sgx")] { + fn egetkey_impl(key_policy: Keypolicy, context: &[u8]) -> [u8; 16] { + let mut req = Keyrequest::default(); + + req.keyname = Keyname::Seal as u16; + req.keypolicy = key_policy; + + let mut sha3 = Sha3::v256(); + sha3.update(context); + let mut k = [0; 32]; + sha3.finalize(&mut k); + req.keyid = k; + + // Fucking sgx_isa::Attributes doesn't have a -> [u64;2]. + req.attributemask[0] = 1 | 2 | 4; // SGX_FLAGS_INITTED | SGX_FLAGS_DEBUG | SGX_FLAGS_MODE64BIT + req.attributemask[1] = 3; // SGX_XFRM_LEGACY + + match req.egetkey() { + Err(e) => panic!("EGETKEY failed: {:?}", e), + Ok(k) => k, + } + } + } else if #[cfg(feature = "tdx")] { + fn egetkey_impl(_key_policy: Keypolicy, _context: &[u8]) -> [u8; 16] { + unimplemented!("EGETKEY not implemented for TDX"); + } + } else { + const MOCK_MRENCLAVE_KEY: &[u8] = b"Ekiden Test MRENCLAVE KEY"; + const MOCK_MRSIGNER_KEY: &[u8] = b"Ekiden Test MRSIGNER KEY"; + const MOCK_KDF_CUSTOM: &[u8] = b"Ekiden Extract Test SGX Seal Key"; + + fn egetkey_impl(key_policy: Keypolicy, context: &[u8]) -> [u8; 16] { + let mut k = [0u8; 16]; + + // Deterministically generate a test master key from the context. + let mut kdf = match key_policy { + Keypolicy::MRENCLAVE => KMac::new_kmac256(MOCK_MRENCLAVE_KEY, MOCK_KDF_CUSTOM), + Keypolicy::MRSIGNER => KMac::new_kmac256(MOCK_MRSIGNER_KEY, MOCK_KDF_CUSTOM), + _ => panic!("Invalid key_policy"), + }; + kdf.update(context); + kdf.finalize(&mut k); + + k + } } } -#[cfg(not(target_env = "sgx"))] -fn egetkey_impl(key_policy: Keypolicy, context: &[u8]) -> [u8; 16] { - let mut k = [0u8; 16]; - - // Deterministically generate a test master key from the context. - let mut kdf = match key_policy { - Keypolicy::MRENCLAVE => KMac::new_kmac256(MOCK_MRENCLAVE_KEY, MOCK_KDF_CUSTOM), - Keypolicy::MRSIGNER => KMac::new_kmac256(MOCK_MRSIGNER_KEY, MOCK_KDF_CUSTOM), - _ => panic!("Invalid key_policy"), - }; - kdf.update(context); - kdf.finalize(&mut k); - - k -} - /// egetkey returns a 256 bit key suitable for sealing secrets to the /// enclave in cold storage, derived from the results of the `EGETKEY` /// instruction. The `context` field is a domain separation tag. diff --git a/runtime/src/common/sgx/mod.rs b/runtime/src/common/sgx/mod.rs index 95220afe79e..3b5a0d95670 100644 --- a/runtime/src/common/sgx/mod.rs +++ b/runtime/src/common/sgx/mod.rs @@ -34,6 +34,10 @@ impl EnclaveIdentity { mr_enclave: MrEnclave(report.mrenclave), mr_signer: MrSigner(report.mrsigner), }) + } else if #[cfg(feature = "tdx")] { + // TDX builds, generate TD report. + let report = crate::common::tdx::report::get_report(&[0; 64]).expect("failed to get report"); + Some(report.as_enclave_identity()) } else if #[cfg(feature = "debug-mock-sgx")] { // Non-SGX builds, mock SGX enabled, generate mock report. The mock MRENCLAVE is // expected to be passed in by the mock SGX runner. diff --git a/runtime/src/common/sgx/pcs/mod.rs b/runtime/src/common/sgx/pcs/mod.rs index fc195aaaed7..ed03000464d 100644 --- a/runtime/src/common/sgx/pcs/mod.rs +++ b/runtime/src/common/sgx/pcs/mod.rs @@ -63,6 +63,7 @@ pub enum Error { pub use policy::{QuotePolicy, TdxModulePolicy, TdxQuotePolicy}; pub use quote::{Quote, QuoteBundle}; +pub use report::{td_enclave_identity, TdAttributes, TdReport}; pub use tcb::TCBBundle; #[cfg(test)] diff --git a/runtime/src/common/sgx/pcs/report.rs b/runtime/src/common/sgx/pcs/report.rs index 7371b2febd7..104f188a4e6 100644 --- a/runtime/src/common/sgx/pcs/report.rs +++ b/runtime/src/common/sgx/pcs/report.rs @@ -138,29 +138,46 @@ impl TdReport { /// Converts this report into an enclave identity. pub fn as_enclave_identity(&self) -> EnclaveIdentity { - // TODO: Change the EnclaveIdentity structure to allow specifying all the different things. - - // Compute MRENCLAVE as TupleHash[TD_ENCLAVE_IDENTITY_CONTEXT](MRTD, RTMR0, RTMR1, RTMR2, RTMR3). - // - // MRTD -- Measurement of virtual firmware. - // RTMR0 -- Measurement of virtual firmware data and configuration. - // RTMR1 -- Measurement of OS loader, option ROM, boot parameters. - // RTMR2 -- Measurement of OS kernel, initrd, boot parameters. - // RTMR3 -- Reserved. - // - let mut mr_enclave = MrEnclave::default(); - let mut h = TupleHash::v256(TD_ENCLAVE_IDENTITY_CONTEXT); - h.update(&self.mr_td); - h.update(&self.rtmr0); - h.update(&self.rtmr1); - h.update(&self.rtmr2); - h.update(&self.rtmr3); - h.finalize(&mut mr_enclave.0); - - EnclaveIdentity { - mr_signer: Default::default(), // All-zero MRSIGNER (invalid in SGX). - mr_enclave, - } + td_enclave_identity( + &self.mr_td, + &self.rtmr0, + &self.rtmr1, + &self.rtmr2, + &self.rtmr3, + ) + } +} + +/// Compute enclave identity from the given measurements. +pub fn td_enclave_identity( + mr_td: &[u8; 48], + rtmr0: &[u8; 48], + rtmr1: &[u8; 48], + rtmr2: &[u8; 48], + rtmr3: &[u8; 48], +) -> EnclaveIdentity { + // TODO: Change the EnclaveIdentity structure to allow specifying all the different things. + + // Compute MRENCLAVE as TupleHash[TD_ENCLAVE_IDENTITY_CONTEXT](MRTD, RTMR0, RTMR1, RTMR2, RTMR3). + // + // MRTD -- Measurement of virtual firmware. + // RTMR0 -- Measurement of virtual firmware data and configuration. + // RTMR1 -- Measurement of OS loader, option ROM, boot parameters. + // RTMR2 -- Measurement of OS kernel, initrd, boot parameters. + // RTMR3 -- Reserved. + // + let mut mr_enclave = MrEnclave::default(); + let mut h = TupleHash::v256(TD_ENCLAVE_IDENTITY_CONTEXT); + h.update(mr_td); + h.update(rtmr0); + h.update(rtmr1); + h.update(rtmr2); + h.update(rtmr3); + h.finalize(&mut mr_enclave.0); + + EnclaveIdentity { + mr_signer: Default::default(), // All-zero MRSIGNER (invalid in SGX). + mr_enclave, } } diff --git a/runtime/src/common/tdx/init.rs b/runtime/src/common/tdx/init.rs new file mode 100644 index 00000000000..7e7a4457d30 --- /dev/null +++ b/runtime/src/common/tdx/init.rs @@ -0,0 +1,55 @@ +//! TDX-specific initialization. +use nix::{ + mount::{mount, MsFlags}, + sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal}, +}; +use slog::info; + +use crate::common::logger::get_logger; + +/// Perform TDX-specific early initialization. +pub fn init() { + let logger = get_logger("runtime/tdx"); + + // Mount required filesystems. + info!(logger, "Mounting required filesystems"); + let _ = mount( + None::<&str>, + "/proc", + Some("proc"), + MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC, + None::<&str>, + ); + let _ = mount( + None::<&str>, + "/sys", + Some("sysfs"), + MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC, + None::<&str>, + ); + let _ = mount( + None::<&str>, + "/dev", + Some("devtmpfs"), + MsFlags::MS_NOSUID, + None::<&str>, + ); + let _ = mount( + None::<&str>, + "/sys/kernel/config", + Some("configfs"), + MsFlags::MS_NOSUID | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC, + None::<&str>, + ); + + // Ignore SIGCHLD and let the kernel reap zombies. + info!(logger, "Setting up signal handlers"); + unsafe { + let _ = sigaction( + Signal::SIGCHLD, + &SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()), + ); + } + + info!(logger, "Early initialization completed"); +} diff --git a/runtime/src/common/tdx/mod.rs b/runtime/src/common/tdx/mod.rs new file mode 100644 index 00000000000..cac39bc0191 --- /dev/null +++ b/runtime/src/common/tdx/mod.rs @@ -0,0 +1,4 @@ +//! Support utilities for TDX. + +pub mod init; +pub mod report; diff --git a/runtime/src/common/tdx/report.rs b/runtime/src/common/tdx/report.rs new file mode 100644 index 00000000000..5103cea16d3 --- /dev/null +++ b/runtime/src/common/tdx/report.rs @@ -0,0 +1,197 @@ +//! Generating quotes from reports. +use std::{ + convert::TryInto, + fs::File, + os::fd::AsRawFd, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, Result}; + +use crate::common::sgx::{ + pcs::{td_enclave_identity, TdAttributes}, + EnclaveIdentity, +}; + +/// Linux ConfigFS TSM report subsystem path. +const CONFIGFS_TSM_REPORT_PATH: &str = "/sys/kernel/config/tsm/report"; + +/// A ConfigFS TSM transaction for generating a quote. +struct ReportTransaction { + entry: PathBuf, + expected_generation: u64, +} + +impl ReportTransaction { + fn create() -> Result { + // Start a new tsm report transaction by creating an entry. + let entry = Path::new(CONFIGFS_TSM_REPORT_PATH).join("entry"); + std::fs::create_dir_all(entry.clone())?; + + // Read expected generation. + let expected_generation = Self::read_generation(&entry)?; + + Ok(Self { + entry, + expected_generation, + }) + } + + fn read_generation(entry: &Path) -> Result { + let data = std::fs::read_to_string(entry.join("generation"))?; + Ok(data.trim_end_matches('\n').parse()?) + } + + fn write_option(&mut self, name: &str, data: &[u8]) -> Result<()> { + std::fs::write(self.entry.join(name), data)?; + + // Increment expected generation. + self.expected_generation += 1; + + Ok(()) + } + + fn read_option(&self, name: &str) -> Result> { + let data = std::fs::read(self.entry.join(name))?; + + // Check generation. + let generation = Self::read_generation(&self.entry)?; + if generation != self.expected_generation { + return Err(anyhow!( + "unexpected generation (expected: {} got: {})", + self.expected_generation, + generation + )); + } + + Ok(data) + } +} + +impl Drop for ReportTransaction { + fn drop(&mut self) { + // Ensure everything gets cleaned up at the end. + let _ = std::fs::remove_dir_all(&self.entry); + } +} + +/// Length of the REPORTDATA used in TDG.MR.REPORT TDCALL. +const TDX_REPORTDATA_LEN: usize = 64; +/// Length of TDREPORT used in TDG.MR.REPORT TDCALL. +const TDX_REPORT_LEN: usize = 1024; +/// Path to the TDX guest device. +const TDX_GUEST_DEVICE: &str = "/dev/tdx_guest"; + +/// Request struct for TDX_CMD_GET_REPORT0 IOCTL. +#[repr(C)] +struct tdx_report_req { + /// User buffer with REPORTDATA to be included into TDREPORT. + reportdata: [u8; TDX_REPORTDATA_LEN], + /// User buffer to store TDREPORT output from TDCALL[TDG.MR.REPORT]. + tdreport: [u8; TDX_REPORT_LEN], +} + +/// Raw TDX TD report as returned by TDG.MR.REPORT. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RawTdReport { + /// TD attributes. + pub td_attributes: TdAttributes, + /// XFAM (eXtended Features Available Mask). + pub xfam: [u8; 8], + /// Measurement of the initial contents of the TD. + pub mr_td: [u8; 48], + /// Software-defined ID for non-owner-defined configuration of the TD, e.g., runtime or OS + /// configuration. + pub mr_config_id: [u8; 48], + /// Software-defined ID for the TD’s owner. + pub mr_owner: [u8; 48], + /// Software-defined ID for owner-defined configuration of the TD, e.g., specific to the + /// workload rather than the runtime or OS. + pub mr_owner_config: [u8; 48], + /// Runtime extendable measurement register 0. + pub rtmr0: [u8; 48], + /// Runtime extendable measurement register 1. + pub rtmr1: [u8; 48], + /// Runtime extendable measurement register 2. + pub rtmr2: [u8; 48], + /// Runtime extendable measurement register 3. + pub rtmr3: [u8; 48], +} + +impl RawTdReport { + /// Parse given TDREPORT_STRUCT. + pub fn parse(data: &[u8]) -> Result { + if data.len() != TDX_REPORT_LEN { + return Err(anyhow!("malformed TD report")); + } + + // Skip first 512 bytes of TDREPORT_STRUCT that we do not currently need. These contain + // REPORTMACSTRUCT, opaque TEE_TCB_INFO_STRUCT and some reserved bytes. + let data = &data[512..]; + + // Only parse the TDINFO_STRUCT. + Ok(Self { + td_attributes: TdAttributes::parse(&data[0..8])?, + xfam: data[8..16].try_into().unwrap(), + mr_td: data[16..64].try_into().unwrap(), + mr_config_id: data[64..112].try_into().unwrap(), + mr_owner: data[112..160].try_into().unwrap(), + mr_owner_config: data[160..208].try_into().unwrap(), + rtmr0: data[208..256].try_into().unwrap(), + rtmr1: data[256..304].try_into().unwrap(), + rtmr2: data[304..352].try_into().unwrap(), + rtmr3: data[352..400].try_into().unwrap(), + }) + } + + /// Converts this report into an enclave identity. + pub fn as_enclave_identity(&self) -> EnclaveIdentity { + td_enclave_identity( + &self.mr_td, + &self.rtmr0, + &self.rtmr1, + &self.rtmr2, + &self.rtmr3, + ) + } +} + +/// Generates a TD report with the given report data. +pub fn get_report(report_data: &[u8]) -> Result { + if report_data.len() != TDX_REPORTDATA_LEN { + return Err(anyhow!("invalid report data length")); + } + + let mut request = tdx_report_req { + reportdata: [0; TDX_REPORTDATA_LEN], + tdreport: [0; TDX_REPORT_LEN], + }; + request.reportdata.copy_from_slice(report_data); + + nix::ioctl_readwrite!(get_report0_ioctl, b'T', 0x01, tdx_report_req); + + let device = File::options() + .read(true) + .write(true) + .open(TDX_GUEST_DEVICE)?; + + unsafe { + get_report0_ioctl( + device.as_raw_fd(), + std::ptr::addr_of!(request) as *mut tdx_report_req, + )?; + } + + // Read and parse output. + let report = RawTdReport::parse(&request.tdreport)?; + + Ok(report) +} + +/// First generates a TD report with the given report data and then uses it to generate a quote. +pub fn get_quote(report_data: &[u8]) -> Result> { + let mut tx = ReportTransaction::create()?; + tx.write_option("inblob", report_data)?; + let quote = tx.read_option("outblob")?; + Ok(quote) +} diff --git a/runtime/src/common/time.rs b/runtime/src/common/time.rs index 6eba25d75e9..38db800c299 100644 --- a/runtime/src/common/time.rs +++ b/runtime/src/common/time.rs @@ -7,9 +7,9 @@ use std::{ use lazy_static::lazy_static; use slog::error; -use crate::common::logger::get_logger; +use crate::common::{logger::get_logger, process}; -const INITIAL_MINIMUM_TIME: i64 = 1659312000; // Mon, 01 Aug 2022 00:00:00 UTC +const INITIAL_MINIMUM_TIME: i64 = 1704067200; // Mon, 01 Jan 2024 00:00:00 UTC struct TimeSource { inner: Mutex, @@ -36,7 +36,7 @@ pub fn insecure_posix_time() -> i64 { get_logger("runtime/time"), "clock appeared to have ran backwards" ); - panic!("time: clock appeared to have ran backwards") + process::abort(); } inner.timestamp = now; diff --git a/runtime/src/dispatcher.rs b/runtime/src/dispatcher.rs index e77c5b8ea0d..2164946008a 100644 --- a/runtime/src/dispatcher.rs +++ b/runtime/src/dispatcher.rs @@ -15,7 +15,7 @@ use crate::{ common::{ crypto::{hash::Hash, signature::Signer}, logger::get_logger, - process, + panic::AbortOnPanic, sgx::QuotePolicy, }, consensus::{ @@ -99,21 +99,6 @@ pub struct PostInitState { pub app: Option>, } -/// A guard that will abort the process if dropped while panicking. -/// -/// This is to ensure that the runtime will terminate in case there is -/// a panic encountered during dispatch and the runtime is built with -/// a non-abort panic handler. -struct AbortOnPanic; - -impl Drop for AbortOnPanic { - fn drop(&mut self) { - if thread::panicking() { - process::abort(); - } - } -} - impl From for Error { fn from(e: tokio::task::JoinError) -> Self { Error::new( diff --git a/runtime/src/identity.rs b/runtime/src/identity.rs index 36d9bc853cc..5a28e4ccb9f 100644 --- a/runtime/src/identity.rs +++ b/runtime/src/identity.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::Result; use base64::prelude::*; use rand::{rngs::OsRng, Rng}; -use sgx_isa::{Report, Targetinfo}; +use sgx_isa::Targetinfo; use thiserror::Error; use tiny_keccak::{Hasher, TupleHash}; @@ -23,6 +23,7 @@ use crate::{ time::insecure_posix_time, }, consensus::registry::EndorsedCapabilityTEE, + TeeType, BUILD_INFO, }; /// Context used for computing the RAK digest. @@ -31,10 +32,8 @@ const RAK_HASH_CONTEXT: &[u8] = b"oasis-core/node: TEE RAK binding"; const QUOTE_NONCE_CONTEXT: &[u8] = b"oasis-core/node: TEE quote nonce"; /// A dummy RAK seed for use in non-SGX tests where integrity is not needed. -#[cfg(not(any(target_env = "sgx", feature = "debug-mock-sgx")))] const INSECURE_RAK_SEED: &str = "ekiden test key manager RAK seed"; /// A dummy REK seed for use in non-SGX tests where confidentiality is not needed. -#[cfg(not(any(target_env = "sgx", feature = "debug-mock-sgx")))] const INSECURE_REK_SEED: &str = "ekiden test key manager REK seed"; /// Identity-related error. @@ -49,6 +48,8 @@ enum IdentityError { /// Quote-related errors. #[derive(Error, Debug)] enum QuoteError { + #[error("target info not set")] + TargetInfoNotSet, #[error("malformed target_info")] MalformedTargetInfo, #[error("MRENCLAVE mismatch")] @@ -101,15 +102,24 @@ impl Default for Identity { impl Identity { /// Create an uninitialized runtime identity. pub fn new() -> Self { - #[cfg(any(target_env = "sgx", feature = "debug-mock-sgx"))] - let rak = signature::PrivateKey::generate(); - #[cfg(any(target_env = "sgx", feature = "debug-mock-sgx"))] - let rek = x25519::PrivateKey::generate(); - - #[cfg(not(any(target_env = "sgx", feature = "debug-mock-sgx")))] - let rak = signature::PrivateKey::from_test_seed(INSECURE_RAK_SEED.to_string()); - #[cfg(not(any(target_env = "sgx", feature = "debug-mock-sgx")))] - let rek = x25519::PrivateKey::from_test_seed(INSECURE_REK_SEED.to_string()); + let (rak, rek) = match BUILD_INFO.tee_type { + TeeType::None => { + // Use insecure mock keys for insecure non-TEE builds. + assert!(!BUILD_INFO.is_secure); + + ( + signature::PrivateKey::from_test_seed(INSECURE_RAK_SEED.to_string()), + x25519::PrivateKey::from_test_seed(INSECURE_REK_SEED.to_string()), + ) + } + _ => { + // Generate ephemeral RAK and REK. + ( + signature::PrivateKey::generate(), + x25519::PrivateKey::generate(), + ) + } + }; Self { inner: RwLock::new(Inner { @@ -156,50 +166,82 @@ impl Identity { /// Initialize the SGX target info. pub(crate) fn init_target_info(&self, target_info: Vec) -> Result<()> { - let mut inner = self.inner.write().unwrap(); - - // Set the Quoting Enclave target_info first, as unlike key generation - // it can fail. - let target_info = match Targetinfo::try_copy_from(&target_info) { - Some(target_info) => target_info, - None => return Err(QuoteError::MalformedTargetInfo.into()), - }; - inner.target_info = Some(target_info); + match BUILD_INFO.tee_type { + TeeType::Sgx => { + let mut inner = self.inner.write().unwrap(); + + // Set the Quoting Enclave target_info first, as unlike key generation + // it can fail. + let target_info = match Targetinfo::try_copy_from(&target_info) { + Some(target_info) => target_info, + None => return Err(QuoteError::MalformedTargetInfo.into()), + }; + inner.target_info = Some(target_info); + + Ok(()) + } + TeeType::Tdx => { + // Target info configuration is not needed on TDX and MUST be empty. + if !target_info.is_empty() { + return Err(QuoteError::MalformedTargetInfo.into()); + } - Ok(()) + Ok(()) + } + TeeType::None => Ok(()), + } } /// Initialize the attestation report. - pub(crate) fn init_report(&self) -> (signature::PublicKey, x25519::PublicKey, Report, String) { + pub(crate) fn init_report( + &self, + ) -> Result<(signature::PublicKey, x25519::PublicKey, Vec, String)> { let rak_pub = self.public_rak(); let rek_pub = self.public_rek(); - let target_info = self - .get_sgx_target_info() - .expect("target_info must be configured"); // Generate a new anti-replay nonce. let nonce = Self::generate_nonce(); - // The derived nonce is only used in case IAS-based attestation is used - // as it is included in the outer AVR envelope. But given that the body - // also includes the nonce in our specific case, this is not relevant. - let quote_nonce = BASE64_STANDARD.encode(&nonce[..24]); - // Generate report body. let report_body = Self::report_body_for_rak(&rak_pub); let mut report_data = [0; 64]; report_data[0..32].copy_from_slice(report_body.as_ref()); report_data[32..64].copy_from_slice(nonce.as_ref()); - let report = sgx::report_for(&target_info, &report_data); + let result = match BUILD_INFO.tee_type { + TeeType::Sgx => { + let target_info = self + .get_sgx_target_info() + .ok_or(QuoteError::TargetInfoNotSet)?; + + // The derived nonce is only used in case IAS-based attestation is used + // as it is included in the outer AVR envelope. But given that the body + // also includes the nonce in our specific case, this is not relevant. + let quote_nonce = BASE64_STANDARD.encode(&nonce[..24]); + + let report = sgx::report_for(&target_info, &report_data); + let report: &[u8] = report.as_ref(); + let report = report.to_vec(); - // This used to reset the quote, but that is now done in the external - // accessor combined with a freshness check. + // This used to reset the quote, but that is now done in the external + // accessor combined with a freshness check. + + (rak_pub, rek_pub, report, quote_nonce) + } + #[cfg(feature = "tdx")] + TeeType::Tdx => { + // In TDX we can immediately generate a quote. Do it and return it as a "report". + let quote = crate::common::tdx::report::get_quote(&report_data)?; + + (rak_pub, rek_pub, quote, String::new()) + } + _ => panic!("init_report called outside TEE environment"), + }; // Cache the nonce, the report was generated. let mut inner = self.inner.write().unwrap(); inner.nonce = Some(nonce); - (rak_pub, rek_pub, report, quote_nonce) + Ok(result) } /// Configure the remote attestation quote for RAK. @@ -212,8 +254,7 @@ impl Identity { let mut inner = self.inner.write().unwrap(); - // If there is no anti-replay nonce set, we aren't in the process - // of attesting. + // If there is no anti-replay nonce set, we aren't in the process of attesting. let expected_nonce = match &inner.nonce { Some(nonce) => *nonce, None => return Err(QuoteError::NonceMismatch.into()), diff --git a/runtime/src/init.rs b/runtime/src/init.rs index 0ea58beb0a9..d43a7a9310a 100644 --- a/runtime/src/init.rs +++ b/runtime/src/init.rs @@ -1,6 +1,7 @@ //! Runtime initialization. use std::sync::Arc; +use anyhow::Result; use slog::{error, info}; use crate::{ @@ -10,6 +11,7 @@ use crate::{ future::new_tokio_runtime, identity::Identity, protocol::{Protocol, Stream}, + TeeType, BUILD_INFO, }; /// Starts the runtime. @@ -19,6 +21,12 @@ pub fn start_runtime(initializer: Box, config: Config) { let logger = get_logger("runtime"); info!(logger, "Runtime is starting"); + // Perform TDX-specific early initialization. + #[cfg(feature = "tdx")] + if BUILD_INFO.tee_type == TeeType::Tdx { + crate::common::tdx::init::init(); + } + // Initialize runtime identity with runtime attestation key and runtime encryption key. let identity = Arc::new(Identity::new()); @@ -29,24 +37,15 @@ pub fn start_runtime(initializer: Box, config: Config) { // Initialize the dispatcher. let dispatcher = Dispatcher::new(tokio_handle.clone(), initializer, identity.clone()); + // Connect to the runtime host. info!(logger, "Establishing connection with the worker host"); - #[cfg(not(target_env = "sgx"))] - let stream = match Stream::connect(std::env::var("OASIS_WORKER_HOST").unwrap_or_default()) { - Err(error) => { - error!(logger, "Failed to connect with the worker host"; "err" => %error); - return; - } + let stream = match connect() { Ok(stream) => stream, - }; - - #[cfg(target_env = "sgx")] - let stream = match Stream::connect("worker-host") { - Err(error) => { - error!(logger, "Failed to connect with the worker host"; "err" => %error); + Err(err) => { + error!(logger, "Failed to connect with the worker host"; "err" => %err); return; } - Ok(stream) => stream, }; // Initialize the protocol handler loop. @@ -64,3 +63,42 @@ pub fn start_runtime(initializer: Box, config: Config) { info!(logger, "Protocol handler terminated, shutting down"); } + +/// Establish a connection with the host. +fn connect() -> Result { + match BUILD_INFO.tee_type { + #[cfg(not(target_env = "sgx"))] + TeeType::Sgx | TeeType::None => { + let stream = std::os::unix::net::UnixStream::connect( + std::env::var("OASIS_WORKER_HOST").unwrap_or_default(), + )?; + Ok(Stream::Unix(stream)) + } + + #[cfg(target_env = "sgx")] + TeeType::Sgx => { + let stream = std::net::TcpStream::connect("worker-host")?; + Ok(Stream::Tcp(stream)) + } + + #[cfg(feature = "tdx")] + TeeType::Tdx => { + /// VSOCK port used for the Runtime Host Protocol. + const VSOCK_PORT_RHP: u32 = 1; + + // Accept first connection. + let listener = vsock::VsockListener::bind(&vsock::VsockAddr::new( + libc::VMADDR_CID_ANY, + VSOCK_PORT_RHP, + ))?; + let stream = listener + .incoming() + .next() + .ok_or(anyhow::anyhow!("failed to accept connection"))??; + Ok(Stream::Vsock(stream)) + } + + #[allow(unreachable_patterns)] + _ => Err(anyhow::anyhow!("unsupported TEE type")), + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 794a65d3a75..d1ececc65b8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -40,60 +40,118 @@ pub mod storage; pub mod transaction; pub mod types; -use crate::common::version::{Version, PROTOCOL_VERSION}; - -#[cfg(target_env = "sgx")] -use self::common::sgx::{EnclaveIdentity, MrSigner}; +use common::{ + sgx::{EnclaveIdentity, MrSigner}, + version::{Version, PROTOCOL_VERSION}, +}; +// Validate features. #[cfg(all(target_env = "sgx", feature = "debug-mock-sgx"))] compile_error!("the debug-mock-sgx feature can only be enabled on non-sgx targets"); +#[cfg(all(target_env = "sgx", feature = "tdx"))] +compile_error!("the tdx feature can only be enabled on non-sgx targets"); + +#[cfg(all(feature = "tdx", feature = "debug-mock-sgx"))] +compile_error!("the tdx feature can't be enabled together with debug-mock-sgx"); + lazy_static! { pub static ref BUILD_INFO: BuildInfo = { - // Non-SGX builds are insecure by definition. - #[cfg(not(target_env = "sgx"))] - let is_secure = false; - - // SGX build security depends on how it was built. - #[cfg(target_env = "sgx")] - let is_secure = { - // Optimistically start out as "it could be secure", and any single - // insecure build time option will propagate failure. - let maybe_secure = true; - - // AVR signature verification MUST be enabled. - let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_SKIP_AVR_VERIFY").is_none(); - - // Disallow debug enclaves MUST be enabled. - let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_ALLOW_DEBUG_ENCLAVES").is_none(); - - // IAS `GROUP_OUT_OF_DATE` and `CONFIGRUATION_NEEDED` responses - // MUST count as IAS failure. - // - // Rationale: This is how IAS signifies that the host environment - // is insecure (eg: SMT is enabled when it should not be). - let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_LAX_AVR_VERIFY").is_none(); - - // The enclave MUST NOT be a debug one. - let maybe_secure = maybe_secure && !Report::for_self().attributes.flags.contains(AttributesFlags::DEBUG); - - // The enclave MUST NOT be signed by a test key, - let enclave_identity = EnclaveIdentity::current().unwrap(); - let fortanix_mrsigner = MrSigner::from("9affcfae47b848ec2caf1c49b4b283531e1cc425f93582b36806e52a43d78d1a"); - let maybe_secure = maybe_secure && (enclave_identity.mr_signer != fortanix_mrsigner); - - maybe_secure + // Determine TEE type. + let tee_type = if cfg!(any(target_env = "sgx", feature = "debug-mock-sgx")) { + TeeType::Sgx + } else if cfg!(feature = "tdx") { + TeeType::Tdx + } else { + TeeType::None + }; + + // Determine build security. + #[allow(clippy::let_and_return)] + let is_secure = match tee_type { + TeeType::Sgx => { + // SGX build security depends on how it was built. + // + // Optimistically start out as "it could be secure", and any single insecure build time + // option will propagate failure. + let maybe_secure = true; + + // Quote signature verification MUST be enabled. + let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_SKIP_AVR_VERIFY").is_none(); + + // Disallow debug enclaves MUST be enabled. + let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_ALLOW_DEBUG_ENCLAVES").is_none(); + + // Attestation `OutOfDate` and `ConfigurationNeeded` responses MUST count as attestation + // failure. + // + // Rationale: This is how remote attestation signifies that the host environment is + // insecure (eg: SMT is enabled when it should not be). + let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_LAX_AVR_VERIFY").is_none(); + + // The enclave MUST NOT be a debug one. + #[cfg(target_env = "sgx")] + let maybe_secure = maybe_secure && !Report::for_self().attributes.flags.contains(AttributesFlags::DEBUG); + + // The enclave MUST NOT be signed by a test key, + let enclave_identity = EnclaveIdentity::current().unwrap(); + let fortanix_mrsigner = MrSigner::from("9affcfae47b848ec2caf1c49b4b283531e1cc425f93582b36806e52a43d78d1a"); + let maybe_secure = maybe_secure && (enclave_identity.mr_signer != fortanix_mrsigner); + + maybe_secure + } + TeeType::Tdx => { + // TDX build security depends on how it was built. + // + // Optimistically start out as "it could be secure", and any single insecure build time + // option will propagate failure. + let maybe_secure = true; + + // Quote signature verification MUST be enabled. + let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_SKIP_AVR_VERIFY").is_none(); + + // Disallow debug enclaves MUST be enabled. + let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_ALLOW_DEBUG_ENCLAVES").is_none(); + + // Attestation `OutOfDate` and `ConfigurationNeeded` responses MUST count as attestation + // failure. + // + // Rationale: This is how remote attestation signifies that the host environment is + // insecure (eg: SMT is enabled when it should not be). + let maybe_secure = maybe_secure && option_env!("OASIS_UNSAFE_LAX_AVR_VERIFY").is_none(); + + // TODO: Debug TD attributes. + + maybe_secure + } + TeeType::None => { + // Non-TEE builds are insecure by definition. + false + } }; BuildInfo { + tee_type, protocol_version: PROTOCOL_VERSION, is_secure, } }; } +/// TEE type this build is for. +#[derive(Debug, Default, PartialEq, Eq)] +pub enum TeeType { + #[default] + None, + Sgx, + Tdx, +} + /// Runtime build information. +#[derive(Debug)] pub struct BuildInfo { + /// TEE type this build is for. + pub tee_type: TeeType, /// Supported runtime protocol version. pub protocol_version: Version, /// True iff the build can provide integrity and confidentiality. diff --git a/runtime/src/protocol.rs b/runtime/src/protocol.rs index f167d34770f..1ff19babe18 100644 --- a/runtime/src/protocol.rs +++ b/runtime/src/protocol.rs @@ -23,13 +23,54 @@ use crate::{ identity::Identity, storage::KeyValue, types::{Body, Error, Message, MessageType, RuntimeInfoRequest, RuntimeInfoResponse}, - BUILD_INFO, + TeeType, BUILD_INFO, }; -#[cfg(not(target_env = "sgx"))] -pub type Stream = ::std::os::unix::net::UnixStream; -#[cfg(target_env = "sgx")] -pub type Stream = ::std::net::TcpStream; +/// Stream used to communicate with the host. +pub enum Stream { + #[cfg(not(target_env = "sgx"))] + Unix(std::os::unix::net::UnixStream), + Tcp(std::net::TcpStream), + #[cfg(feature = "tdx")] + Vsock(vsock::VsockStream), +} + +impl Read for &Stream { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + #[allow(clippy::borrow_deref_ref)] + match self { + #[cfg(not(target_env = "sgx"))] + Stream::Unix(stream) => (&*stream).read(buf), + Stream::Tcp(stream) => (&*stream).read(buf), + #[cfg(feature = "tdx")] + Stream::Vsock(stream) => (&*stream).read(buf), + } + } +} + +impl Write for &Stream { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + #[allow(clippy::borrow_deref_ref)] + match self { + #[cfg(not(target_env = "sgx"))] + Stream::Unix(stream) => (&*stream).write(buf), + Stream::Tcp(stream) => (&*stream).write(buf), + #[cfg(feature = "tdx")] + Stream::Vsock(stream) => (&*stream).write(buf), + } + } + + fn flush(&mut self) -> std::io::Result<()> { + #[allow(clippy::borrow_deref_ref)] + match self { + #[cfg(not(target_env = "sgx"))] + Stream::Unix(stream) => (&*stream).flush(), + Stream::Tcp(stream) => (&*stream).flush(), + #[cfg(feature = "tdx")] + Stream::Vsock(stream) => (&*stream).flush(), + } + } +} /// Maximum message size. const MAX_MESSAGE_SIZE: usize = 16 * 1024 * 1024; // 16MiB @@ -374,7 +415,6 @@ impl Protocol { } // Attestation-related requests. - #[cfg(any(target_env = "sgx", feature = "debug-mock-sgx"))] Body::RuntimeCapabilityTEERakInitRequest { .. } | Body::RuntimeCapabilityTEERakReportRequest {} | Body::RuntimeCapabilityTEERakAvrRequest { .. } @@ -485,10 +525,14 @@ impl Protocol { .as_ref() .ok_or(ProtocolError::HostInfoNotConfigured)?; - #[cfg(any(target_env = "sgx", feature = "debug-mock-sgx"))] - self.identity - .quote() - .ok_or(ProtocolError::AttestationRequired)?; + match BUILD_INFO.tee_type { + TeeType::Sgx | TeeType::Tdx => { + self.identity + .quote() + .ok_or(ProtocolError::AttestationRequired)?; + } + TeeType::None => {} + } Ok(()) } diff --git a/tests/runtimes/simple-rofl-tdx/Cargo.lock b/tests/runtimes/simple-rofl-tdx/Cargo.lock new file mode 100644 index 00000000000..7fa6436676c --- /dev/null +++ b/tests/runtimes/simple-rofl-tdx/Cargo.lock @@ -0,0 +1,3085 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-serde" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba368df5de76a5bea49aaf0cf1b39ccfbbef176924d1ba5db3e4135216cbe3c7" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.77", + "which", +] + +[[package]] +name = "bit-vec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "contracts" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d1429e3bd78171c65aa010eabcdf8f863ba3254728dbfb0ad4b1545beac15c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "deoxysii" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fab9d9a7e9ff7a4762c5c378deb4158d6aaeaeab86952ecf64221159dcd20809" +dependencies = [ + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.6", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2 0.10.8", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "paste", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "honggfuzz" +version = "0.5.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c76b6234c13c9ea73946d1379d33186151148e0da231506b964b44f3d023505" +dependencies = [ + "arbitrary", + "lazy_static", + "memmap2", + "rustc_version", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "intrusive-collections" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" +dependencies = [ + "memoffset", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "mbedtls" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8730cf71e8d79ba70b3b7986af7af7629c0c4ee58b59e4a2e30d855cc31552e8" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cc", + "cfg-if", + "mbedtls-platform-support", + "mbedtls-sys-auto", + "rs-libc", + "serde", + "serde_derive", + "yasna 0.2.2", +] + +[[package]] +name = "mbedtls-platform-support" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be354d52c70402fbfb37bad9ae2aa99ab52af79f37423cb9d6c41c8fb863abc7" +dependencies = [ + "cc", + "cfg-if", + "chrono", + "mbedtls-sys-auto", +] + +[[package]] +name = "mbedtls-sys-auto" +version = "2.28.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bb8ccdfd2a163117d977677665a2008fbd9ab38c884b0b8a57828219868dc0" +dependencies = [ + "bindgen", + "cc", + "cfg-if", + "cmake", + "lazy_static", + "libc", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "oasis-cbor" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937dd928dca340f29f67f096760ccec7853e60ebc156175aadfc8eb203eed37d" +dependencies = [ + "impl-trait-for-tuples", + "oasis-cbor-derive", + "oasis-cbor-value", + "serde", + "thiserror", +] + +[[package]] +name = "oasis-cbor-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b593c6ebad6e6429a8d1dac3509555da30311f0e6fdf93b96475bce895abef6d" +dependencies = [ + "darling", + "oasis-cbor-value", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "oasis-cbor-value" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe0d7f5a7c55eba7e8e845046c6c81332f4fa4997f0ed497b9f44db1d7f2050" + +[[package]] +name = "oasis-core-keymanager" +version = "0.0.0" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.22.1", + "futures", + "group", + "lazy_static", + "lru", + "oasis-cbor", + "oasis-core-runtime", + "p256", + "p384", + "rand", + "rustc-hex", + "secret-sharing", + "sgx-isa", + "sp800-185", + "thiserror", + "tiny-keccak 2.0.2", + "tokio", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "oasis-core-runtime" +version = "0.0.0" +dependencies = [ + "anyhow", + "arbitrary", + "async-trait", + "base64 0.22.1", + "base64-serde", + "bech32", + "bincode", + "bitflags 2.6.0", + "byteorder", + "cfg-if", + "chrono", + "crossbeam", + "curve25519-dalek", + "deoxysii", + "ed25519-dalek", + "futures", + "hmac", + "honggfuzz", + "impl-trait-for-tuples", + "intrusive-collections", + "lazy_static", + "libc", + "log", + "lru", + "mbedtls", + "nix", + "num-bigint 0.4.6", + "num-derive", + "num-traits", + "oasis-cbor", + "oid-registry", + "percent-encoding", + "rand", + "rsa", + "rustc-hex", + "serde", + "serde_json", + "sgx-isa", + "sha2 0.10.8", + "slog", + "slog-json", + "slog-scope", + "slog-stdlog", + "snow", + "sp800-185", + "tendermint", + "tendermint-light-client", + "tendermint-proto", + "tendermint-rpc", + "thiserror", + "tiny-keccak 2.0.2", + "tokio", + "tokio-retry", + "vsock", + "x25519-dalek", + "x509-parser", + "yasna 0.5.2", + "zeroize", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "peg" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn 2.0.77", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rs-libc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683e8c8e8aac6ffa4b2287bac3c69575d5346accac4f218ae1e084303bb174ca" +dependencies = [ + "cc", + "zeroize", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secret-sharing" +version = "0.1.0" +dependencies = [ + "anyhow", + "group", + "honggfuzz", + "p384", + "rand", + "rand_core", + "sha3", + "subtle", + "thiserror", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sgx-isa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0746ac51caf664e67fdb6643b27f0e29d4b9e1cea0b374644ab28bfa2b83fc9" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "simple-rofl-tdx" +version = "0.0.0" +dependencies = [ + "anyhow", + "async-trait", + "oasis-cbor", + "oasis-core-keymanager", + "oasis-core-runtime", + "rand", + "tokio", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + +[[package]] +name = "slog-json" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1e53f61af1e3c8b852eef0a9dee29008f55d6dd63794f3f12cef786cf0f219" +dependencies = [ + "serde", + "serde_json", + "slog", + "time", +] + +[[package]] +name = "slog-scope" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f95a4b4c3274cd2869549da82b57ccc930859bdbf5bcea0424bc5f140b3c786" +dependencies = [ + "arc-swap", + "lazy_static", + "slog", +] + +[[package]] +name = "slog-stdlog" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6706b2ace5bbae7291d3f8d2473e2bfab073ccd7d03670946197aec98471fa3e" +dependencies = [ + "log", + "slog", + "slog-scope", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snow" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek", + "rand_core", + "rustc_version", + "sha2 0.10.8", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "sp800-185" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b18e3b1ddbf090b195425aca6edf8efb8e9b1fd42708131adf0f882db24fc9" +dependencies = [ + "byteorder", + "tiny-keccak 1.5.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tendermint" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3afea7809ffaaf1e5d9c3c9997cb3a834df7e94fbfab2fad2bc4577f1cde41" +dependencies = [ + "bytes", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures", + "num-traits", + "once_cell", + "prost", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature", + "subtle", + "subtle-encoding", + "tendermint-proto", + "time", + "zeroize", +] + +[[package]] +name = "tendermint-config" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8add7b85b0282e5901521f78fe441956ac1e2752452f4e1f2c0ce7e1f10d485" +dependencies = [ + "flex-error", + "serde", + "serde_json", + "tendermint", + "toml", + "url", +] + +[[package]] +name = "tendermint-light-client" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8595e8c4b202c41588930be1fdf13d7c9c39c621704e3ee6592e6f8064f50926" +dependencies = [ + "contracts", + "crossbeam-channel", + "derive_more", + "flex-error", + "futures", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "static_assertions", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-rpc", + "time", + "tracing", +] + +[[package]] +name = "tendermint-light-client-verifier" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8c85693e8f7d65c19898292ae7ec90b565a73c5b4df15564987e0f0e895a451" +dependencies = [ + "derive_more", + "flex-error", + "serde", + "tendermint", + "time", +] + +[[package]] +name = "tendermint-proto" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3abf34ecf33125621519e9952688e7a59a98232d51538037ba21fbe526a802" +dependencies = [ + "bytes", + "flex-error", + "prost", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-rpc" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9693f42544bf3b41be3cbbfa418650c86e137fb8f5a57981659a84b677721ecf" +dependencies = [ + "async-trait", + "bytes", + "flex-error", + "getrandom", + "peg", + "pin-project", + "rand", + "semver", + "serde", + "serde_bytes", + "serde_json", + "subtle", + "subtle-encoding", + "tendermint", + "tendermint-config", + "tendermint-proto", + "thiserror", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.22", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.20", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsock" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8b4d00e672f147fc86a09738fadb1445bd1c0a40542378dfb82909deeee688" +dependencies = [ + "libc", + "nix", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "yasna" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af3189e6b0484c9fd54208f8eeb8818cadee00ec81438b67a64c8e6f2f3694" +dependencies = [ + "bit-vec", + "num-bigint 0.2.6", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "num-bigint 0.4.6", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/tests/runtimes/simple-rofl-tdx/Cargo.toml b/tests/runtimes/simple-rofl-tdx/Cargo.toml new file mode 100644 index 00000000000..cacd08f94ca --- /dev/null +++ b/tests/runtimes/simple-rofl-tdx/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "simple-rofl-tdx" +version = "0.0.0" +authors = ["Oasis Protocol Foundation "] +edition = "2018" + +[[bin]] +name = "simple-rofl-tdx" + +[dependencies] +cbor = { version = "0.5.1", package = "oasis-cbor" } +oasis-core-runtime = { path = "../../../runtime", features = ["tdx"] } +oasis-core-keymanager = { path = "../../../keymanager" } + +anyhow = "1.0" +async-trait = "0.1.82" +rand = "0.8.5" +tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "sync"] } diff --git a/tests/runtimes/simple-rofl-tdx/src/main.rs b/tests/runtimes/simple-rofl-tdx/src/main.rs new file mode 100644 index 00000000000..9ddc84d54df --- /dev/null +++ b/tests/runtimes/simple-rofl-tdx/src/main.rs @@ -0,0 +1,102 @@ +//! A simple test runtime ROFL component. +use std::sync::Arc; + +use anyhow::Result; +use async_trait::async_trait; + +use oasis_core_runtime::{ + common::version::Version, + config::Config, + consensus::{roothash, verifier::TrustRoot}, + host, rofl, +}; + +/// A simple TDX ROFL application. +pub struct App { + notify: Arc, +} + +impl App { + fn new() -> Self { + Self { + notify: Arc::new(tokio::sync::Notify::new()), + } + } + + async fn process(_host: &Arc) -> Result<()> { + Ok(()) + } +} + +#[async_trait] +impl rofl::App for App { + fn on_init(&mut self, host: Arc) -> Result<()> { + let notify = self.notify.clone(); + + tokio::spawn(async move { + // Register for block notifications. + let _ = host + .register_notify(host::RegisterNotifyOpts { + runtime_block: true, + runtime_event: vec![], + }) + .await; + + println!("Hello ROFL TDX!"); + + // Avoid a queue if we are slow to process things. Just make sure to publish stuff on a + // best effort basis. + loop { + notify.notified().await; + let _ = Self::process(&host).await; + } + }); + + Ok(()) + } + + async fn on_runtime_block(&self, _blk: &roothash::AnnotatedBlock) -> Result<()> { + // Notify the worker to trigger a request. + self.notify.notify_one(); + + Ok(()) + } + + async fn on_runtime_event( + &self, + _blk: &roothash::AnnotatedBlock, + tags: &[Vec], + ) -> Result<()> { + // NOTE: This is not verified. + println!("Received runtime event: {:?}", tags); + + Ok(()) + } +} + +pub fn main() { + // Determine test trust root based on build settings. + #[allow(clippy::option_env_unwrap)] + let trust_root = option_env!("OASIS_TESTS_CONSENSUS_TRUST_HEIGHT").map(|height| { + let hash = option_env!("OASIS_TESTS_CONSENSUS_TRUST_HASH").unwrap(); + let runtime_id = option_env!("OASIS_TESTS_CONSENSUS_TRUST_RUNTIME_ID").unwrap(); + let chain_context = option_env!("OASIS_TESTS_CONSENSUS_TRUST_CHAIN_CONTEXT").unwrap(); + + TrustRoot { + height: height.parse::().unwrap(), + hash: hash.to_string(), + runtime_id: runtime_id.into(), + chain_context: chain_context.to_string(), + } + }); + + // Start the runtime. + oasis_core_runtime::start_runtime( + rofl::new(Box::new(App::new())), + Config { + version: Version::new(0, 0, 0), + trust_root, + ..Default::default() + }, + ); +}