Skip to content

Commit d369ba4

Browse files
committed
Add Deprecated: notes and add proto helpers.
Add Product() as a required method for the quote provider interfaces. Closes #100. Signed-off-by: Dionna Glaze <dionnaglaze@google.com>
1 parent 776294f commit d369ba4

File tree

11 files changed

+264
-155
lines changed

11 files changed

+264
-155
lines changed

client/client.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,22 @@ type Device interface {
4242
// LeveledQuoteProvider encapsulates calls to collect an extended attestation report at a given
4343
// privilege level.
4444
type LeveledQuoteProvider interface {
45+
// IsSupported returns whether the kernel supports this implementation.
4546
IsSupported() bool
47+
// GetRawQuote returns a raw report with the given privilege level.
4648
GetRawQuoteAtLevel(reportData [64]byte, vmpl uint) ([]uint8, error)
49+
// Product returns AMD SEV-related CPU information of the calling CPU.
50+
Product() *pb.SevProduct
4751
}
4852

4953
// QuoteProvider encapsulates calls to collect an extended attestation report.
5054
type QuoteProvider interface {
55+
// IsSupported returns whether the kernel supports this implementation.
5156
IsSupported() bool
57+
// GetRawQuote returns a raw report with the default privilege level.
5258
GetRawQuote(reportData [64]byte) ([]uint8, error)
59+
// Product returns AMD SEV-related CPU information of the calling CPU.
60+
Product() *pb.SevProduct
5361
}
5462

5563
// UseDefaultSevGuest returns true iff -sev_guest_device_path=default.
@@ -76,6 +84,8 @@ func message(d Device, command uintptr, req *labi.SnpUserGuestRequest) error {
7684

7785
// GetRawReportAtVmpl requests for an attestation report at the given VMPL that incorporates the
7886
// given user data.
87+
//
88+
// Deprecated: Use LeveledQuoteProvider.
7989
func GetRawReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte, error) {
8090
var snpReportRsp labi.SnpReportRespABI
8191
userGuestReq := labi.SnpUserGuestRequest{
@@ -92,11 +102,15 @@ func GetRawReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte, error)
92102
}
93103

94104
// GetRawReport requests for an attestation report at VMPL0 that incorporates the given user data.
105+
//
106+
// Deprecated: Use QuoteProvider.
95107
func GetRawReport(d Device, reportData [64]byte) ([]byte, error) {
96108
return GetRawReportAtVmpl(d, reportData, 0)
97109
}
98110

99111
// GetReportAtVmpl gets an attestation report at the given VMPL into its protobuf representation.
112+
//
113+
// Deprecated: Use GetQuoteProtoAtLevel.
100114
func GetReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Report, error) {
101115
data, err := GetRawReportAtVmpl(d, reportData, vmpl)
102116
if err != nil {
@@ -106,6 +120,8 @@ func GetReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Report, error
106120
}
107121

108122
// GetReport gets an attestation report at VMPL0 into its protobuf representation.
123+
//
124+
// Deprecated: Use GetQuoteProto.
109125
func GetReport(d Device, reportData [64]byte) (*pb.Report, error) {
110126
return GetReportAtVmpl(d, reportData, 0)
111127
}
@@ -152,6 +168,8 @@ func queryCertificateLength(d Device, vmpl int) (uint32, error) {
152168

153169
// GetRawExtendedReportAtVmpl requests for an attestation report that incorporates the given user
154170
// data at the given VMPL, and additional key certificate information.
171+
//
172+
// Deprecated: Use LeveledQuoteProvider.
155173
func GetRawExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte, []byte, error) {
156174
length, err := queryCertificateLength(d, vmpl)
157175
if err != nil {
@@ -167,11 +185,47 @@ func GetRawExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) ([]byte
167185

168186
// GetRawExtendedReport requests for an attestation report that incorporates the given user data,
169187
// and additional key certificate information.
188+
//
189+
// Deprecated: Use QuoteProvider.
170190
func GetRawExtendedReport(d Device, reportData [64]byte) ([]byte, []byte, error) {
171191
return GetRawExtendedReportAtVmpl(d, reportData, 0)
172192
}
173193

194+
// GetQuoteProto uses the given QuoteProvider to return the
195+
// protobuf representation of an attestation report with cached
196+
// certificate chain.
197+
func GetQuoteProto(qp QuoteProvider, reportData [64]byte) (*pb.Attestation, error) {
198+
reportcerts, err := qp.GetRawQuote(reportData)
199+
if err != nil {
200+
return nil, err
201+
}
202+
attestation, err := abi.ReportCertsToProto(reportcerts)
203+
if err != nil {
204+
return nil, err
205+
}
206+
attestation.Product = qp.Product()
207+
return attestation, nil
208+
}
209+
210+
// GetQuoteProtoAtLevel uses the given LeveledQuoteProvider to return the
211+
// protobuf representation of an attestation report at a given VMPL with cached
212+
// certificate chain.
213+
func GetQuoteProtoAtLevel(qp LeveledQuoteProvider, reportData [64]byte, vmpl uint) (*pb.Attestation, error) {
214+
reportcerts, err := qp.GetRawQuoteAtLevel(reportData, vmpl)
215+
if err != nil {
216+
return nil, err
217+
}
218+
attestation, err := abi.ReportCertsToProto(reportcerts)
219+
if err != nil {
220+
return nil, err
221+
}
222+
attestation.Product = qp.Product()
223+
return attestation, nil
224+
}
225+
174226
// GetExtendedReportAtVmpl gets an extended attestation report at the given VMPL into a structured type.
227+
//
228+
// Deprecated: Use GetQuoteProtoAtLevel
175229
func GetExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Attestation, error) {
176230
reportBytes, certBytes, err := GetRawExtendedReportAtVmpl(d, reportData, vmpl)
177231
if err != nil {
@@ -195,6 +249,8 @@ func GetExtendedReportAtVmpl(d Device, reportData [64]byte, vmpl int) (*pb.Attes
195249
}
196250

197251
// GetExtendedReport gets an extended attestation report at VMPL0 into a structured type.
252+
//
253+
// Deprecated: Use GetQuoteProto.
198254
func GetExtendedReport(d Device, reportData [64]byte) (*pb.Attestation, error) {
199255
return GetExtendedReportAtVmpl(d, reportData, 0)
200256
}

client/client_linux.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ func (p *LinuxIoctlQuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8, err
168168
return append(report, certs...), nil
169169
}
170170

171+
// Product returns the current CPU's associated AMD SEV product information.
172+
func (*LinuxIoctlQuoteProvider) Product() *spb.SevProduct {
173+
return abi.SevProduct()
174+
}
175+
171176
// LinuxConfigFsQuoteProvider implements the QuoteProvider interface to fetch
172177
// attestation quote via ConfigFS.
173178
type LinuxConfigFsQuoteProvider struct{}
@@ -207,6 +212,11 @@ func (p *LinuxConfigFsQuoteProvider) GetRawQuote(reportData [64]byte) ([]uint8,
207212
return append(resp.OutBlob, resp.AuxBlob...), nil
208213
}
209214

215+
// Product returns the current CPU's associated AMD SEV product information.
216+
func (*LinuxConfigFsQuoteProvider) Product() *spb.SevProduct {
217+
return abi.SevProduct()
218+
}
219+
210220
// GetQuoteProvider returns a supported SEV-SNP QuoteProvider.
211221
func GetQuoteProvider() (QuoteProvider, error) {
212222
preferred := &LinuxConfigFsQuoteProvider{}
@@ -217,7 +227,7 @@ func GetQuoteProvider() (QuoteProvider, error) {
217227
}
218228

219229
// GetLeveledQuoteProvider returns a supported SEV-SNP LeveledQuoteProvider.
220-
func GetLeveledQuoteProvider() (QuoteProvider, error) {
230+
func GetLeveledQuoteProvider() (LeveledQuoteProvider, error) {
221231
preferred := &LinuxConfigFsQuoteProvider{}
222232
if !preferred.IsSupported() {
223233
return &LinuxIoctlQuoteProvider{}, nil

client/client_macos.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
const DefaultSevGuestDevicePath = "unknown"
2727

2828
// MacOSDevice implements the Device interface with Linux ioctls.
29-
// Deprecated: Use MacOSQuoteProvider.
3029
type MacOSDevice struct{}
3130

3231
// Open is not supported on MacOS.

client/client_test.go

Lines changed: 88 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434

3535
var devMu sync.Once
3636
var device Device
37+
var qp QuoteProvider
3738
var tests []test.TestCase
3839

3940
var guestPolicy = flag.Uint64("guest_policy", abi.SnpPolicyToBytes(abi.SnpPolicy{SMT: true}),
@@ -69,6 +70,7 @@ func initDevice() {
6970
panic(err)
7071
}
7172
device = sevTestDevice
73+
qp = &test.QuoteProvider{Device: sevTestDevice}
7274
return
7375
}
7476

@@ -77,6 +79,7 @@ func initDevice() {
7779
panic(err)
7880
}
7981
device = client
82+
qp = &test.QuoteProvider{Device: device.(*test.Device)}
8083
}
8184

8285
func cleanReport(report *spb.Report) {
@@ -129,106 +132,114 @@ func fixRawReportWants(raw []byte) error {
129132
func TestOpenGetReportClose(t *testing.T) {
130133
devMu.Do(initDevice)
131134
for _, tc := range tests {
132-
reportProto := &spb.Report{}
133-
if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil {
134-
t.Fatalf("test failure: %v", err)
135-
}
136-
fixReportWants(reportProto)
135+
t.Run(tc.Name, func(t *testing.T) {
136+
reportProto := &spb.Report{}
137+
if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil {
138+
t.Fatalf("test failure: %v", err)
139+
}
140+
fixReportWants(reportProto)
137141

138-
// Does the proto report match expectations?
139-
got, err := GetReport(device, tc.Input)
140-
if !test.Match(err, tc.WantErr) {
141-
t.Fatalf("GetReport(device, %v) = %v, %v. Want err: %v", tc.Input, got, err, tc.WantErr)
142-
}
142+
// Does the proto report match expectations?
143+
attestation, err := GetQuoteProto(qp, tc.Input)
144+
if !test.Match(err, tc.WantErr) {
145+
t.Fatalf("GetReport(device, %v) = %v, %v. Want err: %v", tc.Input, attestation, err, tc.WantErr)
146+
}
143147

144-
if tc.WantErr == "" {
145-
cleanReport(got)
146-
want := reportProto
147-
want.Signature = got.Signature // Zeros were placeholders.
148-
if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
149-
t.Errorf("%s: GetReport(%v) expectation diff %s", tc.Name, tc.Input, diff)
148+
if tc.WantErr == "" {
149+
got := attestation.Report
150+
cleanReport(got)
151+
want := reportProto
152+
want.Signature = got.Signature // Zeros were placeholders.
153+
if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
154+
t.Errorf("GetReport(%v) expectation diff %s", tc.Input, diff)
155+
}
150156
}
151-
}
157+
})
152158
}
153159
}
154160

155161
func TestOpenGetRawExtendedReportClose(t *testing.T) {
156162
devMu.Do(initDevice)
157163
for _, tc := range tests {
158-
raw, certs, err := GetRawExtendedReport(device, tc.Input)
159-
if !test.Match(err, tc.WantErr) {
160-
t.Fatalf("%s: GetRawExtendedReport(device, %v) = %v, %v, %v. Want err: %v", tc.Name, tc.Input, raw, certs, err, tc.WantErr)
161-
}
162-
if tc.WantErr == "" {
163-
if err := cleanRawReport(raw); err != nil {
164-
t.Fatal(err)
165-
}
166-
got := abi.SignedComponent(raw)
167-
if err := fixRawReportWants(tc.Output[:]); err != nil {
168-
t.Fatal(err)
164+
t.Run(tc.Name, func(t *testing.T) {
165+
rawcerts, err := qp.GetRawQuote(tc.Input)
166+
if !test.Match(err, tc.WantErr) || (tc.WantErr == "" && len(rawcerts) < abi.ReportSize) {
167+
t.Fatalf("qp.GetRawQuote(%v) = %v, %v. Want err: %v", tc.Input, rawcerts, err, tc.WantErr)
169168
}
170-
want := abi.SignedComponent(tc.Output[:])
171-
if !bytes.Equal(got, want) {
172-
t.Errorf("%s: GetRawExtendedReport(%v) = {data: %v, certs: _} want %v", tc.Name, tc.Input, got, want)
173-
}
174-
der, err := abi.ReportToSignatureDER(raw)
175-
if err != nil {
176-
t.Errorf("ReportToSignatureDER(%v) errored unexpectedly: %v", raw, err)
177-
}
178-
if UseDefaultSevGuest() {
179-
tcdev := device.(*test.Device)
180-
infoRaw, _ := abi.ReportSignerInfo(raw)
181-
info, _ := abi.ParseSignerInfo(infoRaw)
182-
reportSigner := tcdev.Signer.Vcek
183-
if info.SigningKey == abi.VlekReportSigner {
184-
reportSigner = tcdev.Signer.Vlek
169+
if tc.WantErr == "" {
170+
raw := rawcerts[:abi.ReportSize]
171+
if err := cleanRawReport(raw); err != nil {
172+
t.Fatal(err)
173+
}
174+
got := abi.SignedComponent(raw)
175+
if err := fixRawReportWants(tc.Output[:]); err != nil {
176+
t.Fatal(err)
185177
}
186-
if err := reportSigner.CheckSignature(x509.ECDSAWithSHA384, got, der); err != nil {
187-
t.Errorf("signature with test keys did not verify: %v", err)
178+
want := abi.SignedComponent(tc.Output[:])
179+
if !bytes.Equal(got, want) {
180+
t.Errorf("qp.GetRawQuote(%v) = {data: %v, certs: _} want %v", tc.Input, got, want)
181+
}
182+
der, err := abi.ReportToSignatureDER(raw)
183+
if err != nil {
184+
t.Errorf("ReportToSignatureDER(%v) errored unexpectedly: %v", raw, err)
185+
}
186+
if UseDefaultSevGuest() {
187+
tcdev := device.(*test.Device)
188+
infoRaw, _ := abi.ReportSignerInfo(raw)
189+
info, _ := abi.ParseSignerInfo(infoRaw)
190+
reportSigner := tcdev.Signer.Vcek
191+
if info.SigningKey == abi.VlekReportSigner {
192+
reportSigner = tcdev.Signer.Vlek
193+
}
194+
if err := reportSigner.CheckSignature(x509.ECDSAWithSHA384, got, der); err != nil {
195+
t.Errorf("signature with test keys did not verify: %v", err)
196+
}
188197
}
189198
}
190-
}
199+
})
191200
}
192201
}
193202

194-
func TestOpenGetExtendedReportClose(t *testing.T) {
203+
func TestGetQuoteProto(t *testing.T) {
195204
devMu.Do(initDevice)
196205
for _, tc := range tests {
197-
ereport, err := GetExtendedReport(device, tc.Input)
198-
if !test.Match(err, tc.WantErr) {
199-
t.Fatalf("%s: GetExtendedReport(device, %v) = %v, %v. Want err: %v", tc.Name, tc.Input, ereport, err, tc.WantErr)
200-
}
201-
if tc.WantErr == "" {
202-
reportProto := &spb.Report{}
203-
if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil {
204-
t.Fatalf("test failure: %v", err)
206+
t.Run(tc.Name, func(t *testing.T) {
207+
ereport, err := GetQuoteProto(qp, tc.Input)
208+
if !test.Match(err, tc.WantErr) {
209+
t.Fatalf("GetQuoteProto(qp, %v) = %v, %v. Want err: %v", tc.Input, ereport, err, tc.WantErr)
205210
}
206-
fixReportWants(reportProto)
207-
208-
got := ereport.Report
209-
cleanReport(got)
210-
want := reportProto
211-
want.Signature = got.Signature // Zeros were placeholders.
212-
if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
213-
t.Errorf("%s: GetExtendedReport(%v) = {data: %v, certs: _} want %v. Diff: %s", tc.Name, tc.Input, got, want, diff)
214-
}
215-
216-
if UseDefaultSevGuest() {
217-
tcdev := device.(*test.Device)
218-
if !bytes.Equal(ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) {
219-
t.Errorf("ARK certificate mismatch. Got %v, want %v",
220-
ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw)
211+
if tc.WantErr == "" {
212+
reportProto := &spb.Report{}
213+
if err := prototext.Unmarshal([]byte(tc.OutputProto), reportProto); err != nil {
214+
t.Fatalf("test failure: %v", err)
221215
}
222-
if !bytes.Equal(ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) {
223-
t.Errorf("ASK certificate mismatch. Got %v, want %v",
224-
ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw)
216+
fixReportWants(reportProto)
217+
218+
got := ereport.Report
219+
cleanReport(got)
220+
want := reportProto
221+
want.Signature = got.Signature // Zeros were placeholders.
222+
if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
223+
t.Errorf("GetQuoteProto(qp, %v) = {data: %v, certs: _} want %v. Diff: %s", tc.Input, got, want, diff)
225224
}
226-
if !bytes.Equal(ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) {
227-
t.Errorf("VCEK certificate mismatch. Got %v, want %v",
228-
ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw)
225+
226+
if UseDefaultSevGuest() {
227+
tcdev := device.(*test.Device)
228+
if !bytes.Equal(ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw) {
229+
t.Errorf("ARK certificate mismatch. Got %v, want %v",
230+
ereport.GetCertificateChain().GetArkCert(), tcdev.Signer.Ark.Raw)
231+
}
232+
if !bytes.Equal(ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw) {
233+
t.Errorf("ASK certificate mismatch. Got %v, want %v",
234+
ereport.GetCertificateChain().GetAskCert(), tcdev.Signer.Ask.Raw)
235+
}
236+
if !bytes.Equal(ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw) {
237+
t.Errorf("VCEK certificate mismatch. Got %v, want %v",
238+
ereport.GetCertificateChain().GetVcekCert(), tcdev.Signer.Vcek.Raw)
239+
}
229240
}
230241
}
231-
}
242+
})
232243
}
233244
}
234245

client/client_windows.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
)
2424

2525
// WindowsDevice implements the Device interface with Linux ioctls.
26-
// Deprecated: Use WindowsQuoteProvider.
2726
type WindowsDevice struct{}
2827

2928
// Open is not supported on Windows.

0 commit comments

Comments
 (0)