diff --git a/client/client.go b/client/client.go index 93ac948..7ced490 100644 --- a/client/client.go +++ b/client/client.go @@ -42,14 +42,22 @@ type Device interface { // LeveledQuoteProvider encapsulates calls to collect an extended attestation report at a given // privilege level. type LeveledQuoteProvider interface { + // IsSupported returns whether the kernel supports this implementation. IsSupported() bool + // GetRawQuote returns a raw report with the given privilege level. GetRawQuoteAtLevel(reportData [64]byte, vmpl uint) ([]uint8, error) + // Product returns AMD SEV-related CPU information of the calling CPU. + Product() *pb.SevProduct } // QuoteProvider encapsulates calls to collect an extended attestation report. type QuoteProvider interface { + // IsSupported returns whether the kernel supports this implementation. IsSupported() bool + // GetRawQuote returns a raw report with the default privilege level. GetRawQuote(reportData [64]byte) ([]uint8, error) + // Product returns AMD SEV-related CPU information of the calling CPU. + Product() *pb.SevProduct } // UseDefaultSevGuest returns true iff -sev_guest_device_path=default. @@ -76,6 +84,8 @@ func message(d Device, command uintptr, req *labi.SnpUserGuestRequest) error { // GetRawReportAtVmpl requests for an attestation report at the given VMPL that incorporates the // given user data. +// +// Deprecated: Use LeveledQuoteProvider. func GetRawReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte, error) { var snpReportRsp labi.SnpReportRespABI userGuestReq := labi.SnpUserGuestRequest{ @@ -92,11 +102,15 @@ func GetRawReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte, error) } // GetRawReport requests for an attestation report at VMPL0 that incorporates the given user data. +// +// Deprecated: Use QuoteProvider. func GetRawReport(d Device, reportData [64]byte) ([]byte, error) { return GetRawReportAtVmpl(d, reportData, 0) } // GetReportAtVmpl gets an attestation report at the given VMPL into its protobuf representation. +// +// Deprecated: Use GetQuoteProtoAtLevel. func GetReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Report, error) { data, err := GetRawReportAtVmpl(d, reportData, vmpl) if err != nil { @@ -106,6 +120,8 @@ func GetReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Report, error } // GetReport gets an attestation report at VMPL0 into its protobuf representation. +// +// Deprecated: Use GetQuoteProto. func GetReport(d Device, reportData [64]byte) (*pb.Report, error) { return GetReportAtVmpl(d, reportData, 0) } @@ -152,6 +168,8 @@ func queryCertificateLength(d Device, vmpl int) (uint32, error) { // GetRawExtendedReportAtVmpl requests for an attestation report that incorporates the given user // data at the given VMPL, and additional key certificate information. +// +// Deprecated: Use LeveledQuoteProvider. func GetRawExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte, []byte, error) { length, err := queryCertificateLength(d, vmpl) if err != nil { @@ -167,11 +185,47 @@ func GetRawExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte // GetRawExtendedReport requests for an attestation report that incorporates the given user data, // and additional key certificate information. +// +// Deprecated: Use QuoteProvider. func GetRawExtendedReport(d Device, reportData [64]byte) ([]byte, []byte, error) { return GetRawExtendedReportAtVmpl(d, reportData, 0) } +// GetQuoteProto uses the given QuoteProvider to return the +// protobuf representation of an attestation report with cached +// certificate chain. +func GetQuoteProto(qp QuoteProvider, reportData [64]byte) (*pb.Attestation, error) { + reportcerts, err := qp.GetRawQuote(reportData) + if err != nil { + return nil, err + } + attestation, err := abi.ReportCertsToProto(reportcerts) + if err != nil { + return nil, err + } + attestation.Product = qp.Product() + return attestation, nil +} + +// GetQuoteProtoAtLevel uses the given LeveledQuoteProvider to return the +// protobuf representation of an attestation report at a given VMPL with cached +// certificate chain. +func GetQuoteProtoAtLevel(qp LeveledQuoteProvider, reportData [64]byte, vmpl uint) (*pb.Attestation, error) { + reportcerts, err := qp.GetRawQuoteAtLevel(reportData, vmpl) + if err != nil { + return nil, err + } + attestation, err := abi.ReportCertsToProto(reportcerts) + if err != nil { + return nil, err + } + attestation.Product = qp.Product() + return attestation, nil +} + // GetExtendedReportAtVmpl gets an extended attestation report at the given VMPL into a structured type. +// +// Deprecated: Use GetQuoteProtoAtLevel func GetExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Attestation, error) { reportBytes, certBytes, err := GetRawExtendedReportAtVmpl(d, reportData, vmpl) if err != nil { @@ -195,6 +249,8 @@ func GetExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Attes } // GetExtendedReport gets an extended attestation report at VMPL0 into a structured type. +// +// Deprecated: Use GetQuoteProto. func GetExtendedReport(d Device, reportData [64]byte) (*pb.Attestation, error) { return GetExtendedReportAtVmpl(d, reportData, 0) } diff --git a/client/client_linux.go b/client/client_linux.go index 99f2886..b7b6abb 100644 --- a/client/client_linux.go +++ b/client/client_linux.go @@ -168,6 +168,11 @@ func (p *LinuxIoctlQuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, err return append(report, certs...), nil } +// Product returns the current CPU's associated AMD SEV product information. +func (*LinuxIoctlQuoteProvider) Product() *spb.SevProduct { + return abi.SevProduct() +} + // LinuxConfigFsQuoteProvider implements the QuoteProvider interface to fetch // attestation quote via ConfigFS. type LinuxConfigFsQuoteProvider struct{} @@ -207,6 +212,11 @@ func (p *LinuxConfigFsQuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, return append(resp.OutBlob, resp.AuxBlob...), nil } +// Product returns the current CPU's associated AMD SEV product information. +func (*LinuxConfigFsQuoteProvider) Product() *spb.SevProduct { + return abi.SevProduct() +} + // GetQuoteProvider returns a supported SEV-SNP QuoteProvider. func GetQuoteProvider() (QuoteProvider, error) { preferred := &LinuxConfigFsQuoteProvider{} @@ -217,7 +227,7 @@ func GetQuoteProvider() (QuoteProvider, error) { } // GetLeveledQuoteProvider returns a supported SEV-SNP LeveledQuoteProvider. -func GetLeveledQuoteProvider() (QuoteProvider, error) { +func GetLeveledQuoteProvider() (LeveledQuoteProvider, error) { preferred := &LinuxConfigFsQuoteProvider{} if !preferred.IsSupported() { return &LinuxIoctlQuoteProvider{}, nil diff --git a/client/client_macos.go b/client/client_macos.go index b53c506..530f413 100644 --- a/client/client_macos.go +++ b/client/client_macos.go @@ -26,7 +26,6 @@ import ( const DefaultSevGuestDevicePath = "unknown" // MacOSDevice implements the Device interface with Linux ioctls. -// Deprecated: Use MacOSQuoteProvider. type MacOSDevice struct{} // Open is not supported on MacOS. diff --git a/client/client_test.go b/client/client_test.go index 3e45ba5..03c9418 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -34,6 +34,7 @@ import ( var devMu sync.Once var device Device +var qp QuoteProvider var tests []test.TestCase var guestPolicy = flag.Uint64("guest_policy", abi.SnpPolicyToBytes(abi.SnpPolicy{SMT: true}), @@ -69,6 +70,7 @@ func initDevice() { panic(err) } device = sevTestDevice + qp = &test.QuoteProvider{Device: sevTestDevice} return } @@ -77,6 +79,7 @@ func initDevice() { panic(err) } device = client + qp = &test.QuoteProvider{Device: device.(*test.Device)} } func cleanReport(report *spb.Report) { @@ -129,106 +132,114 @@ func fixRawReportWants(raw []byte) error { func TestOpenGetReportClose(t *testing.T) { devMu.Do(initDevice) for _, tc := range tests { - reportProto := &spb.Report{} - if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil { - t.Fatalf("test failure: %v", err) - } - fixReportWants(reportProto) + t.Run(tc.Name, func(t *testing.T) { + reportProto := &spb.Report{} + if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil { + t.Fatalf("test failure: %v", err) + } + fixReportWants(reportProto) - // Does the proto report match expectations? - got, err := GetReport(device, tc.Input) - if !test.Match(err, tc.WantErr) { - t.Fatalf("GetReport(device, %v) = %v, %v. Want err: %v", tc.Input, got, err, tc.WantErr) - } + // Does the proto report match expectations? + attestation, err := GetQuoteProto(qp, tc.Input) + if !test.Match(err, tc.WantErr) { + t.Fatalf("GetReport(device, %v) = %v, %v. Want err: %v", tc.Input, attestation, err, tc.WantErr) + } - if tc.WantErr == "" { - cleanReport(got) - want := reportProto - want.Signature = got.Signature // Zeros were placeholders. - if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { - t.Errorf("%s: GetReport(%v) expectation diff %s", tc.Name, tc.Input, diff) + if tc.WantErr == "" { + got := attestation.Report + cleanReport(got) + want := reportProto + want.Signature = got.Signature // Zeros were placeholders. + if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { + t.Errorf("GetReport(%v) expectation diff %s", tc.Input, diff) + } } - } + }) } } func TestOpenGetRawExtendedReportClose(t *testing.T) { devMu.Do(initDevice) for _, tc := range tests { - raw, certs, err := GetRawExtendedReport(device, tc.Input) - if !test.Match(err, tc.WantErr) { - t.Fatalf("%s: GetRawExtendedReport(device, %v) = %v, %v, %v. Want err: %v", tc.Name, tc.Input, raw, certs, err, tc.WantErr) - } - if tc.WantErr == "" { - if err := cleanRawReport(raw); err != nil { - t.Fatal(err) - } - got := abi.SignedComponent(raw) - if err := fixRawReportWants(tc.Output[:]); err != nil { - t.Fatal(err) + t.Run(tc.Name, func(t *testing.T) { + rawcerts, err := qp.GetRawQuote(tc.Input) + if !test.Match(err, tc.WantErr) || (tc.WantErr == "" && len(rawcerts) < abi.ReportSize) { + t.Fatalf("qp.GetRawQuote(%v) = %v, %v. Want err: %v", tc.Input, rawcerts, err, tc.WantErr) } - want := abi.SignedComponent(tc.Output[:]) - if !bytes.Equal(got, want) { - t.Errorf("%s: GetRawExtendedReport(%v) = {data: %v, certs: _} want %v", tc.Name, tc.Input, got, want) - } - der, err := abi.ReportToSignatureDER(raw) - if err != nil { - t.Errorf("ReportToSignatureDER(%v) errored unexpectedly: %v", raw, err) - } - if UseDefaultSevGuest() { - tcdev := device.(*test.Device) - infoRaw, _ := abi.ReportSignerInfo(raw) - info, _ := abi.ParseSignerInfo(infoRaw) - reportSigner := tcdev.Signer.Vcek - if info.SigningKey == abi.VlekReportSigner { - reportSigner = tcdev.Signer.Vlek + if tc.WantErr == "" { + raw := rawcerts[:abi.ReportSize] + if err := cleanRawReport(raw); err != nil { + t.Fatal(err) + } + got := abi.SignedComponent(raw) + if err := fixRawReportWants(tc.Output[:]); err != nil { + t.Fatal(err) } - if err := reportSigner.CheckSignature(x509.ECDSAWithSHA384, got, der); err != nil { - t.Errorf("signature with test keys did not verify: %v", err) + want := abi.SignedComponent(tc.Output[:]) + if !bytes.Equal(got, want) { + t.Errorf("qp.GetRawQuote(%v) = {data: %v, certs: _} want %v", tc.Input, got, want) + } + der, err := abi.ReportToSignatureDER(raw) + if err != nil { + t.Errorf("ReportToSignatureDER(%v) errored unexpectedly: %v", raw, err) + } + if UseDefaultSevGuest() { + tcdev := device.(*test.Device) + infoRaw, _ := abi.ReportSignerInfo(raw) + info, _ := abi.ParseSignerInfo(infoRaw) + reportSigner := tcdev.Signer.Vcek + if info.SigningKey == abi.VlekReportSigner { + reportSigner = tcdev.Signer.Vlek + } + if err := reportSigner.CheckSignature(x509.ECDSAWithSHA384, got, der); err != nil { + t.Errorf("signature with test keys did not verify: %v", err) + } } } - } + }) } } -func TestOpenGetExtendedReportClose(t *testing.T) { +func TestGetQuoteProto(t *testing.T) { devMu.Do(initDevice) for _, tc := range tests { - ereport, err := GetExtendedReport(device, tc.Input) - if !test.Match(err, tc.WantErr) { - t.Fatalf("%s: GetExtendedReport(device, %v) = %v, %v. Want err: %v", tc.Name, tc.Input, ereport, err, tc.WantErr) - } - if tc.WantErr == "" { - reportProto := &spb.Report{} - if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil { - t.Fatalf("test failure: %v", err) + t.Run(tc.Name, func(t *testing.T) { + ereport, err := GetQuoteProto(qp, tc.Input) + if !test.Match(err, tc.WantErr) { + t.Fatalf("GetQuoteProto(qp, %v) = %v, %v. Want err: %v", tc.Input, ereport, err, tc.WantErr) } - fixReportWants(reportProto) - - got := ereport.Report - cleanReport(got) - want := reportProto - want.Signature = got.Signature // Zeros were placeholders. - if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { - t.Errorf("%s: GetExtendedReport(%v) = {data: %v, certs: _} want %v. Diff: %s", tc.Name, tc.Input, got, want, diff) - } - - if UseDefaultSevGuest() { - tcdev := device.(*test.Device) - if !bytes.Equal(ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) { - t.Errorf("ARK certificate mismatch. Got %v, want %v", - ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) + if tc.WantErr == "" { + reportProto := &spb.Report{} + if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil { + t.Fatalf("test failure: %v", err) } - if !bytes.Equal(ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) { - t.Errorf("ASK certificate mismatch. Got %v, want %v", - ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) + fixReportWants(reportProto) + + got := ereport.Report + cleanReport(got) + want := reportProto + want.Signature = got.Signature // Zeros were placeholders. + if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" { + t.Errorf("GetQuoteProto(qp, %v) = {data: %v, certs: _} want %v. Diff: %s", tc.Input, got, want, diff) } - if !bytes.Equal(ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) { - t.Errorf("VCEK certificate mismatch. Got %v, want %v", - ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) + + if UseDefaultSevGuest() { + tcdev := device.(*test.Device) + if !bytes.Equal(ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) { + t.Errorf("ARK certificate mismatch. Got %v, want %v", + ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) + } + if !bytes.Equal(ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) { + t.Errorf("ASK certificate mismatch. Got %v, want %v", + ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) + } + if !bytes.Equal(ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) { + t.Errorf("VCEK certificate mismatch. Got %v, want %v", + ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) + } } } - } + }) } } diff --git a/client/client_windows.go b/client/client_windows.go index c92c22d..080fc5b 100644 --- a/client/client_windows.go +++ b/client/client_windows.go @@ -23,7 +23,6 @@ import ( ) // WindowsDevice implements the Device interface with Linux ioctls. -// Deprecated: Use WindowsQuoteProvider. type WindowsDevice struct{} // Open is not supported on Windows. diff --git a/testing/client/client.go b/testing/client/client.go index 3deb19d..674c4c2 100644 --- a/testing/client/client.go +++ b/testing/client/client.go @@ -120,9 +120,9 @@ func GetSevQuoteProvider(tcs []test.TestCase, opts *test.DeviceOptions, tb testi { Product: "Milan", ProductCerts: &trust.ProductCerts{ - Ask: sevQp.Signer.Ask, - Ark: sevQp.Signer.Ark, - Asvk: sevQp.Signer.Asvk, + Ask: sevQp.Device.Signer.Ask, + Ark: sevQp.Device.Signer.Ark, + Asvk: sevQp.Device.Signer.Asvk, }, }, }, @@ -133,14 +133,14 @@ func GetSevQuoteProvider(tcs []test.TestCase, opts *test.DeviceOptions, tb testi Product: "Milan", ProductCerts: &trust.ProductCerts{ // No ASK, oops. - Ask: sevQp.Signer.Ark, - Ark: sevQp.Signer.Ark, - Asvk: sevQp.Signer.Ark, + Ask: sevQp.Device.Signer.Ark, + Ark: sevQp.Device.Signer.Ark, + Asvk: sevQp.Device.Signer.Ark, }, }, }, } - fakekds, err := test.FakeKDSFromSigner(sevQp.Signer) + fakekds, err := test.FakeKDSFromSigner(sevQp.Device.Signer) if err != nil { tb.Fatalf("failed to create fake KDS from signer: %v", err) } diff --git a/testing/mocks.go b/testing/mocks.go index e9d465b..d91ed80 100644 --- a/testing/mocks.go +++ b/testing/mocks.go @@ -150,10 +150,12 @@ func (d *Device) Product() *spb.SevProduct { // QuoteProvider represents a SEV-SNP backed configfs-tsm with pre-programmed responses to attestations. type QuoteProvider struct { - ReportDataRsp map[string]any - Certs []byte - Signer *AmdSigner - SevProduct *spb.SevProduct + Device *Device +} + +// Product returns the mocked product info or the default. +func (p *QuoteProvider) Product() *spb.SevProduct { + return p.Device.Product() } // IsSupported returns true @@ -163,7 +165,7 @@ func (*QuoteProvider) IsSupported() bool { // GetRawQuote returns the raw report assigned for given reportData. func (p *QuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, error) { - mockRspI, ok := p.ReportDataRsp[hex.EncodeToString(reportData[:])] + mockRspI, ok := p.Device.ReportDataRsp[hex.EncodeToString(reportData[:])] if !ok { return nil, fmt.Errorf("test error: no response for %v", reportData) } @@ -175,14 +177,14 @@ func (p *QuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, error) { return nil, syscall.Errno(unix.EIO) } report := mockRsp.Resp.Data[:abi.ReportSize] - r, s, err := p.Signer.Sign(abi.SignedComponent(report)) + r, s, err := p.Device.Signer.Sign(abi.SignedComponent(report)) if err != nil { return nil, fmt.Errorf("test error: could not sign report: %v", err) } if err := abi.SetSignature(r, s, report); err != nil { return nil, fmt.Errorf("test error: could not set signature: %v", err) } - return append(report, p.Certs...), nil + return append(report, p.Device.Certs...), nil } // GetResponse controls how often (Occurrences) a certain response should be diff --git a/testing/test_cases.go b/testing/test_cases.go index 4c3ef25..730400c 100644 --- a/testing/test_cases.go +++ b/testing/test_cases.go @@ -234,7 +234,7 @@ func TestCases() []TestCase { Name: "fw oom", Input: userZeros11, FwErr: abi.ResourceLimit, - WantErr: (&abi.SevFirmwareErr{Status: abi.ResourceLimit}).Error(), + WantErr: "input/output error", // All firmware errors get translated to EIO in configfs }, } } @@ -264,21 +264,9 @@ func TcDevice(tcs []TestCase, opts *DeviceOptions) (*Device, error) { // TcQuoteProvider returns a mock quote provider populated from test cases' inputs and expected outputs. func TcQuoteProvider(tcs []TestCase, opts *DeviceOptions) (*QuoteProvider, error) { - certs, signer, err := makeTestCerts(opts) + d, err := TcDevice(tcs, opts) if err != nil { - return nil, fmt.Errorf("test failure creating certificates: %v", err) - } - responses := map[string]any{} - for _, tc := range tcs { - responses[hex.EncodeToString(tc.Input[:])] = &GetReportResponse{ - Resp: labi.SnpReportRespABI{Data: tc.Output}, - FwErr: tc.FwErr, - } + return nil, err } - return &QuoteProvider{ - ReportDataRsp: responses, - Certs: certs, - Signer: signer, - SevProduct: opts.Product, - }, nil + return &QuoteProvider{Device: d}, nil } diff --git a/tools/attest/attest.go b/tools/attest/attest.go index fc0ee3a..a800cae 100644 --- a/tools/attest/attest.go +++ b/tools/attest/attest.go @@ -21,9 +21,11 @@ import ( "fmt" "io" "os" + "strconv" "github.com/google/go-sev-guest/abi" "github.com/google/go-sev-guest/client" + pb "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/go-sev-guest/tools/lib/cmdline" "github.com/google/logger" "google.golang.org/protobuf/encoding/prototext" @@ -51,10 +53,11 @@ var ( reportDataFile = flag.String("infile", "", "Path to a file containing 64 bytes of REPORT_DATA to include "+ "in the output attestation. Stdin is \"-\". Default -inform=bin.") - vmpl = flag.Int("vmpl", 0, "The VMPL at which to collect an attestation report") + vmpl = flag.String("vmpl", "default", "The VMPL at which to collect an attestation report") out = flag.String("out", "", "Path to output file to write attestation report to. "+ "If unset, outputs to stdout.") verbose = flag.Bool("v", false, "Enable verbose logging.") + vmplInt uint ) func indata() ([]byte, error) { @@ -91,17 +94,16 @@ func nonBinOut() func(proto.Message) ([]byte, error) { } } -func outputExtendedReport(device client.Device, data [abi.ReportDataSize]byte, out io.Writer) error { +func outputExtendedReport(data [abi.ReportDataSize]byte, out io.Writer) error { if *outform == "bin" { - report, certs, err := client.GetRawExtendedReportAtVmpl(device, data, *vmpl) + bin, err := getRaw(data) if err != nil { return err } - out.Write(report) - out.Write(certs) + out.Write(bin) return nil } - attestation, err := client.GetExtendedReportAtVmpl(device, data, *vmpl) + attestation, err := getProto(data) if err != nil { return err } @@ -113,20 +115,64 @@ func outputExtendedReport(device client.Device, data [abi.ReportDataSize]byte, o return nil } -func outputReport(device client.Device, data [abi.ReportDataSize]byte, out io.Writer) error { +func getVmpl() (uint, error) { + if *vmpl == "default" { + return 0, fmt.Errorf("getVmpl should not be called on \"default\"") + } + vmplInt, err := strconv.ParseUint(*vmpl, 10, 32) + if err != nil { + return 0, fmt.Errorf("--vmpl must be a non-negative integer or \"default\"") + } + return uint(vmplInt), nil +} + +func getRaw(data [abi.ReportDataSize]byte) ([]byte, error) { + if *vmpl == "default" { + qp, err := client.GetQuoteProvider() + if err != nil { + return nil, err + } + return qp.GetRawQuote(data) + } + qp, err := client.GetLeveledQuoteProvider() + if err != nil { + return nil, err + } + return qp.GetRawQuoteAtLevel(data, vmplInt) +} + +func getProto(data [abi.ReportDataSize]byte) (*pb.Attestation, error) { + if *vmpl == "default" { + qp, err := client.GetQuoteProvider() + if err != nil { + return nil, err + } + return client.GetQuoteProto(qp, data) + } + qp, err := client.GetLeveledQuoteProvider() + if err != nil { + return nil, err + } + return client.GetQuoteProtoAtLevel(qp, data, vmplInt) +} + +func outputReport(data [abi.ReportDataSize]byte, out io.Writer) error { if *outform == "bin" { - bytes, err := client.GetRawReportAtVmpl(device, data, *vmpl) + bytes, err := getRaw(data) if err != nil { return err } + if len(bytes) > abi.ReportSize { + bytes = bytes[:abi.ReportSize] + } out.Write(bytes) return nil } - report, err := client.GetReportAtVmpl(device, data, *vmpl) + attestation, err := getProto(data) if err != nil { return err } - bytes, err := nonBinOut()(report) + bytes, err := nonBinOut()(attestation.Report) if err != nil { return err } @@ -161,8 +207,12 @@ func main() { *outform) } - if *vmpl < 0 || *vmpl > 3 { - logger.Fatalf("-vmpl is %d. Expect 0-3.", *vmpl) + if *vmpl != "default" { + vint, err := getVmpl() + if err != nil || vint > 3 { + logger.Fatalf("--vmpl=%s. Expect 0-3 or \"default\"", *vmpl) + } + vmplInt = vint } outwriter, filetoclose, err := outWriter() @@ -175,19 +225,14 @@ func main() { } }() - device, err := client.OpenDevice() - if err != nil { - logger.Fatal(err) - } - defer device.Close() var reportData64 [abi.ReportDataSize]byte copy(reportData64[:], reportData) if *extended { - if err := outputExtendedReport(device, reportData64, outwriter); err != nil { + if err := outputExtendedReport(reportData64, outwriter); err != nil { logger.Fatal(err) } } else { - if err := outputReport(device, reportData64, outwriter); err != nil { + if err := outputReport(reportData64, outwriter); err != nil { logger.Fatal(err) } } diff --git a/validate/validate_test.go b/validate/validate_test.go index c406cfc..26a2c18 100644 --- a/validate/validate_test.go +++ b/validate/validate_test.go @@ -162,7 +162,7 @@ func TestValidateSnpAttestation(t *testing.T) { if err != nil { t.Fatal(err) } - device0, err := test.TcDevice(test.TestCases(), &test.DeviceOptions{Now: now, Signer: sign0}) + qp0, err := test.TcQuoteProvider(test.TestCases(), &test.DeviceOptions{Now: now, Signer: sign0}) if err != nil { t.Fatal(err) } @@ -243,7 +243,7 @@ func TestValidateSnpAttestation(t *testing.T) { }(), }, } - device, err := test.TcDevice(tcs, opts) + qp, err := test.TcQuoteProvider(tcs, opts) if err != nil { t.Fatal(err) } @@ -254,10 +254,12 @@ func TestValidateSnpAttestation(t *testing.T) { }, ) attestationFn := func(nonce [64]byte) *spb.Attestation { - report, err := sg.GetReport(device, nonce) + + q, err := sg.GetQuoteProto(qp, nonce) if err != nil { t.Fatal(err) } + report := q.Report attestation, err := verify.GetAttestationFromReport(report, &verify.Options{Getter: getter}) if err != nil { t.Fatal(err) @@ -279,10 +281,11 @@ func TestValidateSnpAttestation(t *testing.T) { { name: "just reportData", attestation: func() *spb.Attestation { - report, err := sg.GetReport(device0, nonce0s1) + q, err := sg.GetQuoteProto(qp0, nonce0s1) if err != nil { t.Fatal(err) } + report := q.Report return &spb.Attestation{ Report: report, CertificateChain: &spb.CertificateChain{ diff --git a/verify/verify_test.go b/verify/verify_test.go index e3dd54f..89372fe 100644 --- a/verify/verify_test.go +++ b/verify/verify_test.go @@ -161,32 +161,29 @@ func TestVerifyVcekCert(t *testing.T) { func TestSnpReportSignature(t *testing.T) { tests := test.TestCases() now := time.Date(2022, time.May, 3, 9, 0, 0, 0, time.UTC) - d, err := test.TcDevice(tests, &test.DeviceOptions{Now: now}) + qp, err := test.TcQuoteProvider(tests, &test.DeviceOptions{Now: now}) if err != nil { t.Fatal(err) } - if err := d.Open("/dev/sev-guest"); err != nil { - t.Error(err) - } - defer d.Close() for _, tc := range tests { if testclient.SkipUnmockableTestCase(&tc) { continue } // Does the Raw report match expectations? - raw, err := sg.GetRawReport(d, tc.Input) - if !test.Match(err, tc.WantErr) { - t.Fatalf("GetRawReport(d, %v) = %v, %v. Want err: %v", tc.Input, raw, err, tc.WantErr) + rawcombo, err := qp.GetRawQuote(tc.Input) + if !test.Match(err, tc.WantErr) || (tc.WantErr == "" && len(rawcombo) < abi.ReportSize) { + t.Fatalf("GetRawQuote(qp, %v) = %v, %v. Want err: %q", tc.Input, rawcombo, err, tc.WantErr) } if tc.WantErr == "" { + raw := rawcombo[:abi.ReportSize] got := abi.SignedComponent(raw) want := abi.SignedComponent(tc.Output[:]) if !bytes.Equal(got, want) { t.Errorf("%s: GetRawReport(%v) = %v, want %v", tc.Name, tc.Input, got, want) } - key := d.Signer.Vcek + key := qp.Device.Signer.Vcek if tc.EK == test.KeyChoiceVlek { - key = d.Signer.Vlek + key = qp.Device.Signer.Vlek } if err := SnpReportSignature(raw, key); err != nil { t.Errorf("signature with test keys did not verify: %v", err) @@ -433,15 +430,14 @@ func TestCRLRootValidity(t *testing.T) { func TestOpenGetExtendedReportVerifyClose(t *testing.T) { trust.ClearProductCertCache() tests := test.TestCases() - d, goodRoots, badRoots, kds := testclient.GetSevGuest(tests, &test.DeviceOptions{Now: time.Now()}, t) - defer d.Close() - type reportGetter func(sg.Device, [64]byte) (*pb.Attestation, error) - reportOnly := func(d sg.Device, input [64]byte) (*pb.Attestation, error) { - report, err := sg.GetReport(d, input) + qp, goodRoots, badRoots, kds := testclient.GetSevQuoteProvider(tests, &test.DeviceOptions{Now: time.Now()}, t) + type reportGetter func(sg.QuoteProvider, [64]byte) (*pb.Attestation, error) + reportOnly := func(d sg.QuoteProvider, input [64]byte) (*pb.Attestation, error) { + attestation, err := sg.GetQuoteProto(qp, input) if err != nil { return nil, err } - return &pb.Attestation{Report: report}, nil + return &pb.Attestation{Report: attestation.Report}, nil } reportGetters := []struct { name string @@ -455,7 +451,7 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { }{ { name: "GetExtendedReport", - getter: sg.GetExtendedReport, + getter: sg.GetQuoteProto, badRootErr: "error verifying VCEK certificate", vlekBadRootErr: "error verifying VLEK certificate", }, @@ -469,8 +465,8 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { }, { name: "GetReportVlek", - getter: func(d sg.Device, input [64]byte) (*pb.Attestation, error) { - attestation, err := reportOnly(d, input) + getter: func(qp sg.QuoteProvider, input [64]byte) (*pb.Attestation, error) { + attestation, err := reportOnly(qp, input) if err != nil { return nil, err } @@ -481,8 +477,8 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { chain := attestation.CertificateChain info, _ := abi.ParseSignerInfo(attestation.GetReport().GetSignerInfo()) if sg.UseDefaultSevGuest() && info.SigningKey == abi.VlekReportSigner { - if td, ok := d.(*test.Device); ok { - chain.VlekCert = td.Signer.Vlek.Raw + if td, ok := qp.(*test.QuoteProvider); ok { + chain.VlekCert = td.Device.Signer.Vlek.Raw } } return attestation, nil @@ -521,7 +517,7 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { t.Skip() return } - ereport, err := getReport.getter(d, tc.Input) + ereport, err := getReport.getter(qp, tc.Input) if !test.Match(err, tc.WantErr) { t.Fatalf("(d, %v) = %v, %v. Want err: %v", tc.Input, ereport, err, tc.WantErr) }