diff --git a/cli/cmd/common.go b/cli/cmd/common.go index c27ca1301f..18be175eb5 100644 --- a/cli/cmd/common.go +++ b/cli/cmd/common.go @@ -16,6 +16,7 @@ import ( "github.com/edgelesssys/contrast/internal/atls" "github.com/edgelesssys/contrast/internal/attestation/certcache" "github.com/edgelesssys/contrast/internal/attestation/snp" + "github.com/edgelesssys/contrast/internal/attestation/tdx" "github.com/edgelesssys/contrast/internal/fsstore" "github.com/edgelesssys/contrast/internal/logger" "github.com/edgelesssys/contrast/internal/manifest" @@ -91,16 +92,25 @@ func validatorsFromManifest(m *manifest.Manifest, log *slog.Logger, hostData []b kdsCache := fsstore.New(kdsDir, log.WithGroup("kds-cache")) kdsGetter := certcache.NewCachedHTTPSGetter(kdsCache, certcache.NeverGCTicker, log.WithGroup("kds-getter")) + var validators []atls.Validator + opts, err := m.SNPValidateOpts(kdsGetter) if err != nil { return nil, fmt.Errorf("getting SNP validate options: %w", err) } - - var validators []atls.Validator for _, opt := range opts { validators = append(validators, snp.NewValidator(opt.VerifyOpts, opt.ValidateOpts, []manifest.HexString{manifest.NewHexString(hostData)}, logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "snp"}), )) } + + tdxOpts, err := m.TDXValidateOpts() + if err != nil { + return nil, fmt.Errorf("generating TDX validation options: %w", err) + } + for _, opt := range tdxOpts { + validators = append(validators, tdx.NewValidator(&tdx.StaticValidateOptsGenerator{Opts: opt}, logger.NewWithAttrs(logger.NewNamed(log, "validator"), map[string]string{"tee-type": "tdx"}))) + } + return validators, nil } diff --git a/coordinator/internal/authority/credentials.go b/coordinator/internal/authority/credentials.go index c547807e08..08b83312d1 100644 --- a/coordinator/internal/authority/credentials.go +++ b/coordinator/internal/authority/credentials.go @@ -15,6 +15,7 @@ import ( "github.com/edgelesssys/contrast/internal/atls" "github.com/edgelesssys/contrast/internal/attestation/certcache" "github.com/edgelesssys/contrast/internal/attestation/snp" + "github.com/edgelesssys/contrast/internal/attestation/tdx" "github.com/edgelesssys/contrast/internal/logger" "github.com/edgelesssys/contrast/internal/manifest" "github.com/edgelesssys/contrast/internal/memstore" @@ -76,6 +77,8 @@ func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.A State: state, } + var validators []atls.Validator + opts, err := state.Manifest.SNPValidateOpts(c.kdsGetter) if err != nil { return nil, nil, fmt.Errorf("generating SNP validation options: %w", err) @@ -86,13 +89,22 @@ func (c *Credentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.A allowedHostDataEntries = append(allowedHostDataEntries, entry) } - var validators []atls.Validator for _, opt := range opts { validator := snp.NewValidatorWithCallbacks(opt.VerifyOpts, opt.ValidateOpts, allowedHostDataEntries, logger.NewWithAttrs(logger.NewNamed(c.logger, "validator"), map[string]string{"tee-type": "snp"}), &authInfo) validators = append(validators, validator) } + + tdxOpts, err := state.Manifest.TDXValidateOpts() + if err != nil { + return nil, nil, fmt.Errorf("generating TDX validation options: %w", err) + } + for _, opt := range tdxOpts { + validators = append(validators, tdx.NewValidator(&tdx.StaticValidateOptsGenerator{Opts: opt}, + logger.NewWithAttrs(logger.NewNamed(c.logger, "validator"), map[string]string{"tee-type": "tdx"}))) + } + serverCfg, err := atls.CreateAttestationServerTLSConfig(c.issuer, validators, c.attestationFailuresCounter) if err != nil { return nil, nil, err diff --git a/internal/manifest/manifest.go b/internal/manifest/manifest.go index 357ff408e0..f3f58962e9 100644 --- a/internal/manifest/manifest.go +++ b/internal/manifest/manifest.go @@ -12,9 +12,10 @@ import ( "github.com/google/go-sev-guest/abi" "github.com/google/go-sev-guest/kds" - "github.com/google/go-sev-guest/validate" + snpvalidate "github.com/google/go-sev-guest/validate" "github.com/google/go-sev-guest/verify" "github.com/google/go-sev-guest/verify/trust" + tdxvalidate "github.com/google/go-tdx-guest/validate" ) // Manifest is the Coordinator manifest and contains the reference values of the deployment. @@ -191,16 +192,12 @@ func (m *Manifest) Validate() error { // by a Validator. type ValidatorOptions struct { VerifyOpts *verify.Options - ValidateOpts *validate.Options + ValidateOpts *snpvalidate.Options } // SNPValidateOpts returns validate options generators populated with the manifest's // SNP reference values and trusted measurement for the given runtime. func (m *Manifest) SNPValidateOpts(kdsGetter trust.HTTPSGetter) ([]ValidatorOptions, error) { - if len(m.ReferenceValues.SNP) == 0 { - return nil, errors.New("reference values cannot be empty") - } - if err := m.Validate(); err != nil { return nil, fmt.Errorf("validating manifest: %w", err) } @@ -224,7 +221,7 @@ func (m *Manifest) SNPValidateOpts(kdsGetter trust.HTTPSGetter) ([]ValidatorOpti verifyOpts.CheckRevocations = true verifyOpts.Getter = kdsGetter - validateOpts := validate.Options{ + validateOpts := snpvalidate.Options{ Measurement: trustedMeasurement, GuestPolicy: abi.SnpPolicy{ Debug: false, @@ -249,10 +246,6 @@ func (m *Manifest) SNPValidateOpts(kdsGetter trust.HTTPSGetter) ([]ValidatorOpti out = append(out, ValidatorOptions{VerifyOpts: verifyOpts, ValidateOpts: &validateOpts}) } - if len(out) == 0 { - return nil, errors.New("no SNP reference values found in manifest") - } - return out, nil } @@ -287,3 +280,72 @@ func trustedRoots(productName ProductName) (map[string][]*trust.AMDRootCerts, er return trustedRoots, nil } + +// The QE Vendor ID used by Intel. +var intelQeVendorID = []byte{0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07} + +// TDXValidateOpts returns validate options generators populated with the manifest's +// TDX reference values and trusted measurement for the given runtime. +func (m *Manifest) TDXValidateOpts() ([]*tdxvalidate.Options, error) { + if err := m.Validate(); err != nil { + return nil, fmt.Errorf("validating manifest: %w", err) + } + + var out []*tdxvalidate.Options + for _, refVal := range m.ReferenceValues.TDX { + mrTd, err := refVal.MrTd.Bytes() + if err != nil { + return nil, fmt.Errorf("failed to convert MrTd from manifest to byte slices: %w", err) + } + + minimumTeeTcbSvn, err := refVal.MinimumTeeTcbSvn.Bytes() + if err != nil { + return nil, fmt.Errorf("failed to convert MinimumTeeTcbSvn from manifest to byte slices: %w", err) + } + + mrSeam, err := refVal.MrSeam.Bytes() + if err != nil { + return nil, fmt.Errorf("failed to convert MrSeam from manifest to byte slices: %w", err) + } + + tdAttributes, err := refVal.TdAttributes.Bytes() + if err != nil { + return nil, fmt.Errorf("failed to convert TdAttributes from manifest to byte slices: %w", err) + } + + xfam, err := refVal.Xfam.Bytes() + if err != nil { + return nil, fmt.Errorf("failed to convert Xfam from manifest to byte slices: %w", err) + } + + var rtmrs [4][]byte + for i, rtmr := range refVal.Rtrms { + bytes, err := rtmr.Bytes() + if err != nil { + return nil, fmt.Errorf("failed to convert Rtmr[%d] from manifest to byte slices: %w", i, err) + } + rtmrs[i] = bytes + } + + out = append(out, &tdxvalidate.Options{ + HeaderOptions: tdxvalidate.HeaderOptions{ + MinimumQeSvn: *refVal.MinimumQeSvn, + MinimumPceSvn: *refVal.MinimumPceSvn, + QeVendorID: intelQeVendorID, + }, + TdQuoteBodyOptions: tdxvalidate.TdQuoteBodyOptions{ + MinimumTeeTcbSvn: minimumTeeTcbSvn, + MrSeam: mrSeam, + TdAttributes: tdAttributes, + Xfam: xfam, + // TODO(freax13): Re-enable validation of those fields once we figure out how to calculate the launch measurement. + // MrTd: mrTd, + // Rtmrs: rtmrs[:], + }, + }) + _ = mrTd + _ = rtmrs + } + + return out, nil +}