Skip to content

Commit

Permalink
Merge pull request #101 from deeglaze/deprecate
Browse files Browse the repository at this point in the history
Add Deprecated: notes and add proto helpers.
  • Loading branch information
deeglaze authored Jan 18, 2024
2 parents 869dc68 + d369ba4 commit f912a0a
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 155 deletions.
56 changes: 56 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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{
Expand All @@ -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 {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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)
}
Expand Down
12 changes: 11 additions & 1 deletion client/client_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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{}
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion client/client_macos.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
165 changes: 88 additions & 77 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}),
Expand Down Expand Up @@ -69,6 +70,7 @@ func initDevice() {
panic(err)
}
device = sevTestDevice
qp = &test.QuoteProvider{Device: sevTestDevice}
return
}

Expand All @@ -77,6 +79,7 @@ func initDevice() {
panic(err)
}
device = client
qp = &test.QuoteProvider{Device: device.(*test.Device)}
}

func cleanReport(report *spb.Report) {
Expand Down Expand Up @@ -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)
}
}
}
}
})
}
}

Expand Down
1 change: 0 additions & 1 deletion client/client_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading

0 comments on commit f912a0a

Please sign in to comment.