@@ -21,10 +21,9 @@ import (
21
21
"strconv"
22
22
"strings"
23
23
24
- tpmProto "github.com/google/go-tpm-tools/proto/tpm"
25
-
26
24
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
27
25
"github.com/edgelesssys/constellation/v2/internal/atls"
26
+ azuretdx "github.com/edgelesssys/constellation/v2/internal/attestation/azure/tdx"
28
27
"github.com/edgelesssys/constellation/v2/internal/attestation/choose"
29
28
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
30
29
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
@@ -38,6 +37,10 @@ import (
38
37
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
39
38
"github.com/edgelesssys/constellation/v2/internal/verify"
40
39
"github.com/edgelesssys/constellation/v2/verify/verifyproto"
40
+
41
+ "github.com/google/go-tdx-guest/abi"
42
+ "github.com/google/go-tdx-guest/proto/tdx"
43
+ tpmProto "github.com/google/go-tpm-tools/proto/tpm"
41
44
"github.com/spf13/afero"
42
45
"github.com/spf13/cobra"
43
46
"github.com/spf13/pflag"
@@ -106,24 +109,7 @@ func runVerify(cmd *cobra.Command, _ []string) error {
106
109
dialer : dialer .New (nil , nil , & net.Dialer {}),
107
110
log : log ,
108
111
}
109
- formatterFactory := func (output string , attestation variant.Variant , log debugLog ) (attestationDocFormatter , error ) {
110
- if output == "json" &&
111
- (! attestation .Equal (variant.AzureSEVSNP {}) &&
112
- ! attestation .Equal (variant.AWSSEVSNP {}) &&
113
- ! attestation .Equal (variant.GCPSEVSNP {})) {
114
- return nil , errors .New ("json output is only supported for SEV-SNP" )
115
- }
116
- switch output {
117
- case "json" :
118
- return & jsonAttestationDocFormatter {log }, nil
119
- case "raw" :
120
- return & rawAttestationDocFormatter {log }, nil
121
- case "" :
122
- return & defaultAttestationDocFormatter {log }, nil
123
- default :
124
- return nil , fmt .Errorf ("invalid output value for formatter: %s" , output )
125
- }
126
- }
112
+
127
113
v := & verifyCmd {
128
114
fileHandler : fileHandler ,
129
115
log : log ,
@@ -132,13 +118,12 @@ func runVerify(cmd *cobra.Command, _ []string) error {
132
118
return err
133
119
}
134
120
v .log .Debug ("Using flags" , "clusterID" , v .flags .clusterID , "endpoint" , v .flags .endpoint , "ownerID" , v .flags .ownerID )
121
+
135
122
fetcher := attestationconfigapi .NewFetcher ()
136
- return v .verify (cmd , verifyClient , formatterFactory , fetcher )
123
+ return v .verify (cmd , verifyClient , fetcher )
137
124
}
138
125
139
- type formatterFactory func (output string , attestation variant.Variant , log debugLog ) (attestationDocFormatter , error )
140
-
141
- func (c * verifyCmd ) verify (cmd * cobra.Command , verifyClient verifyClient , factory formatterFactory , configFetcher attestationconfigapi.Fetcher ) error {
126
+ func (c * verifyCmd ) verify (cmd * cobra.Command , verifyClient verifyClient , configFetcher attestationconfigapi.Fetcher ) error {
142
127
c .log .Debug (fmt .Sprintf ("Loading configuration file from %q" , c .flags .pathPrefixer .PrefixPrintablePath (constants .ConfigFilename )))
143
128
conf , err := config .New (c .fileHandler , constants .ConfigFilename , configFetcher , c .flags .force )
144
129
var configValidationErr * config.ValidationError
@@ -202,20 +187,21 @@ func (c *verifyCmd) verify(cmd *cobra.Command, verifyClient verifyClient, factor
202
187
return fmt .Errorf ("verifying: %w" , err )
203
188
}
204
189
205
- // certificates are only available for Azure SEV-SNP and AWS SEV-SNP
206
- formatter , err := factory (c .flags .output , conf .GetAttestationConfig ().GetVariant (), c .log )
207
- if err != nil {
208
- return fmt .Errorf ("creating formatter: %w" , err )
190
+ var attDocOutput string
191
+ switch c .flags .output {
192
+ case "json" :
193
+ attDocOutput , err = formatJSON (cmd .Context (), rawAttestationDoc , attConfig , c .log )
194
+ case "raw" :
195
+ attDocOutput = fmt .Sprintf ("Attestation Document:\n %s\n " , rawAttestationDoc )
196
+ case "" :
197
+ attDocOutput , err = formatDefault (cmd .Context (), rawAttestationDoc , attConfig , c .log )
198
+ default :
199
+ return fmt .Errorf ("invalid output value for formatter: %s" , c .flags .output )
209
200
}
210
- attDocOutput , err := formatter .format (
211
- cmd .Context (),
212
- rawAttestationDoc ,
213
- (! attConfig .GetVariant ().Equal (variant.AzureSEVSNP {}) && ! attConfig .GetVariant ().Equal (variant.AWSSEVSNP {})),
214
- attConfig ,
215
- )
216
201
if err != nil {
217
202
return fmt .Errorf ("printing attestation document: %w" , err )
218
203
}
204
+
219
205
cmd .Println (attDocOutput )
220
206
cmd .PrintErrln ("Verification OK" )
221
207
@@ -255,82 +241,92 @@ func (c *verifyCmd) validateEndpointFlag(cmd *cobra.Command, stateFile *state.St
255
241
return endpoint , nil
256
242
}
257
243
258
- // an attestationDocFormatter formats the attestation document.
259
- type attestationDocFormatter interface {
260
- // format returns the raw or formatted attestation doc depending on the rawOutput argument.
261
- format (ctx context.Context , docString string , PCRsOnly bool , attestationCfg config.AttestationCfg ) (string , error )
262
- }
263
-
264
- type jsonAttestationDocFormatter struct {
265
- log debugLog
266
- }
267
-
268
- // format returns the json formatted attestation doc.
269
- func (f * jsonAttestationDocFormatter ) format (ctx context.Context , docString string , _ bool ,
270
- attestationCfg config.AttestationCfg ,
244
+ // formatJSON returns the json formatted attestation doc.
245
+ func formatJSON (ctx context.Context , docString string , attestationCfg config.AttestationCfg , log debugLog ,
271
246
) (string , error ) {
272
- var doc attestationDoc
247
+ var doc vtpm. AttestationDocument
273
248
if err := json .Unmarshal ([]byte (docString ), & doc ); err != nil {
274
- return "" , fmt .Errorf ("unmarshal attestation document: %w" , err )
249
+ return "" , fmt .Errorf ("unmarshalling attestation document: %w" , err )
275
250
}
276
251
277
- instanceInfo , err := extractInstanceInfo (doc )
278
- if err != nil {
252
+ switch attestationCfg .GetVariant () {
253
+ case variant.AWSSEVSNP {}, variant.AzureSEVSNP {}, variant.GCPSEVSNP {}:
254
+ return snpFormatJSON (ctx , doc .InstanceInfo , attestationCfg , log )
255
+ case variant.AzureTDX {}:
256
+ return tdxFormatJSON (doc .InstanceInfo , attestationCfg )
257
+ default :
258
+ return "" , fmt .Errorf ("json output is not supported for variant %s" , attestationCfg .GetVariant ())
259
+ }
260
+ }
261
+
262
+ func snpFormatJSON (ctx context.Context , instanceInfoRaw []byte , attestationCfg config.AttestationCfg , log debugLog ,
263
+ ) (string , error ) {
264
+ var instanceInfo snp.InstanceInfo
265
+ if err := json .Unmarshal (instanceInfoRaw , & instanceInfo ); err != nil {
279
266
return "" , fmt .Errorf ("unmarshalling instance info: %w" , err )
280
267
}
281
- report , err := verify .NewReport (ctx , instanceInfo , attestationCfg , f . log )
268
+ report , err := verify .NewReport (ctx , instanceInfo , attestationCfg , log )
282
269
if err != nil {
283
270
return "" , fmt .Errorf ("parsing SNP report: %w" , err )
284
271
}
285
272
286
273
jsonBytes , err := json .Marshal (report )
287
-
288
274
return string (jsonBytes ), err
289
275
}
290
276
291
- type rawAttestationDocFormatter struct {
292
- log debugLog
293
- }
277
+ func tdxFormatJSON (instanceInfoRaw []byte , attestationCfg config.AttestationCfg ) (string , error ) {
278
+ var rawQuote []byte
294
279
295
- // format returns the raw attestation doc.
296
- func (f * rawAttestationDocFormatter ) format (_ context.Context , docString string , _ bool ,
297
- _ config.AttestationCfg ,
298
- ) (string , error ) {
299
- b := & strings.Builder {}
300
- b .WriteString ("Attestation Document:\n " )
301
- b .WriteString (fmt .Sprintf ("%s\n " , docString ))
302
- return b .String (), nil
303
- }
280
+ if attestationCfg .GetVariant ().Equal (variant.AzureTDX {}) {
281
+ var instanceInfo azuretdx.InstanceInfo
282
+ if err := json .Unmarshal (instanceInfoRaw , & instanceInfo ); err != nil {
283
+ return "" , fmt .Errorf ("unmarshalling instance info: %w" , err )
284
+ }
285
+ rawQuote = instanceInfo .AttestationReport
286
+ }
287
+
288
+ tdxQuote , err := abi .QuoteToProto (rawQuote )
289
+ if err != nil {
290
+ return "" , fmt .Errorf ("converting quote to proto: %w" , err )
291
+ }
292
+ quote , ok := tdxQuote .(* tdx.QuoteV4 )
293
+ if ! ok {
294
+ return "" , fmt .Errorf ("unexpected quote type: %T" , tdxQuote )
295
+ }
304
296
305
- type defaultAttestationDocFormatter struct {
306
- log debugLog
297
+ quoteJSON , err := json . Marshal ( quote )
298
+ return string ( quoteJSON ), err
307
299
}
308
300
309
301
// format returns the formatted attestation doc.
310
- func (f * defaultAttestationDocFormatter ) format (ctx context.Context , docString string , PCRsOnly bool ,
311
- attestationCfg config.AttestationCfg ,
302
+ func formatDefault (ctx context.Context , docString string , attestationCfg config.AttestationCfg , log debugLog ,
312
303
) (string , error ) {
313
304
b := & strings.Builder {}
314
305
b .WriteString ("Attestation Document:\n " )
315
306
316
- var doc attestationDoc
307
+ var doc vtpm. AttestationDocument
317
308
if err := json .Unmarshal ([]byte (docString ), & doc ); err != nil {
318
309
return "" , fmt .Errorf ("unmarshal attestation document: %w" , err )
319
310
}
320
311
321
- if err := f . parseQuotes (b , doc .Attestation .Quotes , attestationCfg .GetMeasurements ()); err != nil {
312
+ if err := parseQuotes (b , doc .Attestation .Quotes , attestationCfg .GetMeasurements ()); err != nil {
322
313
return "" , fmt .Errorf ("parse quote: %w" , err )
323
314
}
324
- if PCRsOnly {
315
+
316
+ // If we have a non SNP variant, print only the PCRs
317
+ if ! (attestationCfg .GetVariant ().Equal (variant.AzureSEVSNP {}) ||
318
+ attestationCfg .GetVariant ().Equal (variant.AWSSEVSNP {}) ||
319
+ attestationCfg .GetVariant ().Equal (variant.GCPSEVSNP {})) {
325
320
return b .String (), nil
326
321
}
327
322
328
- instanceInfo , err := extractInstanceInfo (doc )
329
- if err != nil {
323
+ // SNP reports contain extra information that we can print
324
+ var instanceInfo snp.InstanceInfo
325
+ if err := json .Unmarshal (doc .InstanceInfo , & instanceInfo ); err != nil {
330
326
return "" , fmt .Errorf ("unmarshalling instance info: %w" , err )
331
327
}
332
328
333
- report , err := verify .NewReport (ctx , instanceInfo , attestationCfg , f . log )
329
+ report , err := verify .NewReport (ctx , instanceInfo , attestationCfg , log )
334
330
if err != nil {
335
331
return "" , fmt .Errorf ("parsing SNP report: %w" , err )
336
332
}
@@ -339,7 +335,7 @@ func (f *defaultAttestationDocFormatter) format(ctx context.Context, docString s
339
335
}
340
336
341
337
// parseQuotes parses the base64-encoded quotes and writes their details to the output builder.
342
- func ( f * defaultAttestationDocFormatter ) parseQuotes (b * strings.Builder , quotes []* tpmProto.Quote , expectedPCRs measurements.M ) error {
338
+ func parseQuotes (b * strings.Builder , quotes []* tpmProto.Quote , expectedPCRs measurements.M ) error {
343
339
writeIndentfln (b , 1 , "Quote:" )
344
340
345
341
var pcrNumbers []uint32
@@ -366,18 +362,6 @@ func (f *defaultAttestationDocFormatter) parseQuotes(b *strings.Builder, quotes
366
362
return nil
367
363
}
368
364
369
- // attestationDoc is the attestation document returned by the verifier.
370
- type attestationDoc struct {
371
- Attestation struct {
372
- AkPub string `json:"ak_pub"`
373
- Quotes []* tpmProto.Quote `json:"quotes"`
374
- EventLog string `json:"event_log"`
375
- TeeAttestation interface {} `json:"TeeAttestation"`
376
- } `json:"Attestation"`
377
- InstanceInfo string `json:"InstanceInfo"`
378
- UserData string `json:"UserData"`
379
- }
380
-
381
365
type constellationVerifier struct {
382
366
dialer grpcInsecureDialer
383
367
log debugLog
@@ -432,19 +416,6 @@ func writeIndentfln(b *strings.Builder, indentLvl int, format string, args ...an
432
416
b .WriteString (fmt .Sprintf (format + "\n " , args ... ))
433
417
}
434
418
435
- func extractInstanceInfo (doc attestationDoc ) (snp.InstanceInfo , error ) {
436
- instanceInfoString , err := base64 .StdEncoding .DecodeString (doc .InstanceInfo )
437
- if err != nil {
438
- return snp.InstanceInfo {}, fmt .Errorf ("decode instance info: %w" , err )
439
- }
440
-
441
- var instanceInfo snp.InstanceInfo
442
- if err := json .Unmarshal (instanceInfoString , & instanceInfo ); err != nil {
443
- return snp.InstanceInfo {}, fmt .Errorf ("unmarshal instance info: %w" , err )
444
- }
445
- return instanceInfo , nil
446
- }
447
-
448
419
func addPortIfMissing (endpoint string , defaultPort int ) (string , error ) {
449
420
if endpoint == "" {
450
421
return "" , errors .New ("endpoint is empty" )
0 commit comments