Skip to content

Commit

Permalink
verify: print formatted SNP report
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Meyer <[email protected]>
  • Loading branch information
katexochen committed Jul 31, 2023
1 parent c8b1765 commit 372aa0f
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 24 deletions.
1 change: 1 addition & 0 deletions cli/internal/cmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ go_library(
"//internal/versions",
"//operators/constellation-node-operator/api/v1alpha1",
"//verify/verifyproto",
"@com_github_google_go_sev_guest//abi",
"@com_github_google_go_sev_guest//kds",
"@com_github_google_uuid//:uuid",
"@com_github_mattn_go_isatty//:go-isatty",
Expand Down
112 changes: 99 additions & 13 deletions cli/internal/cmd/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/grpc/dialer"
"github.com/edgelesssys/constellation/v2/verify/verifyproto"
"github.com/google/go-sev-guest/abi"
"github.com/google/go-sev-guest/kds"
"github.com/spf13/afero"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -283,22 +284,23 @@ func (f *attestationDocFormatterImpl) format(docString string, PCRsOnly bool, ra
if err := f.parseCerts(b, "Certificate chain", instanceInfo.CertChain); err != nil {
return "", fmt.Errorf("print certificate chain: %w", err)
}
if err := f.parseSNPReport(b, instanceInfo.AttestationReport); err != nil {
return "", fmt.Errorf("print SNP report: %w", err)
}

return b.String(), nil
}

// parseCerts parses the base64-encoded PEM certificates and writes their details to the output builder.
func (f *attestationDocFormatterImpl) parseCerts(b *strings.Builder, certTypeName string, encCertString string) error {
certBytes, err := base64.StdEncoding.DecodeString(encCertString)
if err != nil {
return fmt.Errorf("decode %s: %w", certTypeName, err)
}
formattedCert := strings.ReplaceAll(string(certBytes[:len(certBytes)-1]), "\n", "\n\t\t") + "\n"
// parseCerts parses the PEM certificates and writes their details to the output builder.
func (f *attestationDocFormatterImpl) parseCerts(b *strings.Builder, certTypeName string, cert []byte) error {
formattedCert := strings.ReplaceAll(string(cert[:len(cert)-1]), "\n", "\n\t\t") + "\n"
b.WriteString(fmt.Sprintf("\tRaw %s:\n\t\t%s", certTypeName, formattedCert))

f.log.Debugf("Decoding PEM certificate: %s", certTypeName)
i := 1
for block, rest := pem.Decode(certBytes); block != nil; block, rest = pem.Decode(rest) {
var rest []byte
var block *pem.Block
for block, rest = pem.Decode(cert); block != nil; block, rest = pem.Decode(rest) {
f.log.Debugf("Parsing PEM block: %d", i)
if block.Type != "CERTIFICATE" {
return fmt.Errorf("parse %s: expected PEM block type 'CERTIFICATE', got '%s'", certTypeName, block.Type)
Expand Down Expand Up @@ -337,12 +339,16 @@ func (f *attestationDocFormatterImpl) parseCerts(b *strings.Builder, certTypeNam
writeIndentfln(b, 2, "SVN 7 (reserved): %d", tcb.Spl7)
writeIndentfln(b, 2, "SEV-SNP firmware SVN: %d", tcb.SnpSpl)
writeIndentfln(b, 2, "Microcode SVN: %d", tcb.UcodeSpl)
writeIndentfln(b, 2, "Hardware ID: %#x", vcekExts.HWID)
writeIndentfln(b, 2, "Hardware ID: %x", vcekExts.HWID)
}

i++
}

if len(rest) != 0 {
return fmt.Errorf("parse %s: remaining PEM block is not a valid certificate: %s", certTypeName, rest)
}

return nil
}

Expand All @@ -362,6 +368,85 @@ func (f *attestationDocFormatterImpl) parseQuotes(b *strings.Builder, quotes []q
return nil
}

func (f *attestationDocFormatterImpl) parseSNPReport(b *strings.Builder, reportBytes []byte) error {
report, err := abi.ReportToProto(reportBytes)
if err != nil {
return fmt.Errorf("parsing report to proto: %w", err)
}

policy, err := abi.ParseSnpPolicy(report.Policy)
if err != nil {
return fmt.Errorf("parsing policy: %w", err)
}

platformInfo, err := abi.ParseSnpPlatformInfo(report.PlatformInfo)
if err != nil {
return fmt.Errorf("parsing platform info: %w", err)
}

signature, err := abi.ReportToSignatureDER(reportBytes)
if err != nil {
return fmt.Errorf("parsing signature: %w", err)
}

writeTCB := func(tcbVersion uint64) {
tcb := kds.DecomposeTCBVersion(kds.TCBVersion(tcbVersion))
writeIndentfln(b, 3, "Secure Processor bootloader SVN: %d", tcb.BlSpl)
writeIndentfln(b, 3, "Secure Processor operating system SVN: %d", tcb.TeeSpl)
writeIndentfln(b, 3, "SVN 4 (reserved): %d", tcb.Spl4)
writeIndentfln(b, 3, "SVN 5 (reserved): %d", tcb.Spl5)
writeIndentfln(b, 3, "SVN 6 (reserved): %d", tcb.Spl6)
writeIndentfln(b, 3, "SVN 7 (reserved): %d", tcb.Spl7)
writeIndentfln(b, 3, "SEV-SNP firmware SVN: %d", tcb.SnpSpl)
writeIndentfln(b, 3, "Microcode SVN: %d", tcb.UcodeSpl)
}

writeIndentfln(b, 1, "SNP Report:")
writeIndentfln(b, 2, "Version: %d", report.Version)
writeIndentfln(b, 2, "Guest SVN: %d", report.GuestSvn)
writeIndentfln(b, 2, "Policy:")
writeIndentfln(b, 3, "ABI Minor: %d", policy.ABIMinor)
writeIndentfln(b, 3, "ABI Major: %d", policy.ABIMajor)
writeIndentfln(b, 3, "Symmetric Multithreading enabled: %t", policy.SMT)
writeIndentfln(b, 3, "Migration agent enabled: %t", policy.MigrateMA)
writeIndentfln(b, 3, "Debugging enabled (host decryption of VM): %t", policy.Debug)
writeIndentfln(b, 3, "Single socket enabled: %t", policy.SingleSocket)
writeIndentfln(b, 2, "Family ID: %x", report.FamilyId)
writeIndentfln(b, 2, "Image ID: %x", report.ImageId)
writeIndentfln(b, 2, "VMPL: %d", report.Vmpl)
writeIndentfln(b, 2, "Signature Algorithm: %d", report.SignatureAlgo)
writeIndentfln(b, 2, "Current TCB:")
writeTCB(report.CurrentTcb)
writeIndentfln(b, 2, "Platform Info:")
writeIndentfln(b, 3, "Symmetric Multithreading enabled (SMT): %t", platformInfo.SMTEnabled)
writeIndentfln(b, 3, "Transparent secure memory encryption (TSME): %t", platformInfo.TSMEEnabled)
writeIndentfln(b, 2, "Author Key ID: %x", report.AuthorKeyEn)
writeIndentfln(b, 2, "Report Data: %x", report.ReportData)
writeIndentfln(b, 2, "Measurement: %x", report.Measurement)
writeIndentfln(b, 2, "Host Data: %x", report.HostData)
writeIndentfln(b, 2, "ID Key Digest: %x", report.IdKeyDigest)
writeIndentfln(b, 2, "Author Key Digest: %x", report.AuthorKeyDigest)
writeIndentfln(b, 2, "Report ID: %x", report.ReportId)
writeIndentfln(b, 2, "Report ID MA: %x", report.ReportIdMa)
writeIndentfln(b, 2, "Reported TCB:")
writeTCB(report.ReportedTcb)
writeIndentfln(b, 2, "Chip ID: %x", report.ChipId)
writeIndentfln(b, 2, "Committed TCB:")
writeTCB(report.CommittedTcb)
writeIndentfln(b, 2, "Current Build: %d", report.CurrentBuild)
writeIndentfln(b, 2, "Current Minor: %d", report.CurrentMinor)
writeIndentfln(b, 2, "Current Major: %d", report.CurrentMajor)
writeIndentfln(b, 2, "Committed Build: %d", report.CommittedBuild)
writeIndentfln(b, 2, "Committed Minor: %d", report.CommittedMinor)
writeIndentfln(b, 2, "Committed Major: %d", report.CommittedMajor)
writeIndentfln(b, 2, "Launch TCB:")
writeTCB(report.LaunchTcb)
writeIndentfln(b, 2, "Signature (DER):")
writeIndentfln(b, 3, "%x", signature)

return nil
}

// attestationDoc is the attestation document returned by the verifier.
type attestationDoc struct {
Attestation struct {
Expand All @@ -386,10 +471,11 @@ type quote struct {
// azureInstanceInfo is the b64-decoded InstanceInfo field of the attestation document.
// as of now (2023-04-03), it only contains interesting data on Azure.
type azureInstanceInfo struct {
Vcek string `json:"Vcek"`
CertChain string `json:"CertChain"`
AttestationReport string `json:"AttestationReport"`
RuntimeData string `json:"RuntimeData"`
Vcek []byte
CertChain []byte
AttestationReport []byte
RuntimeData []byte
MAAToken string
}

type constellationVerifier struct {
Expand Down
22 changes: 11 additions & 11 deletions cli/internal/cmd/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,20 +278,20 @@ F/SjRih31+SAtWb42jueAA==
validCertExpected := "\tRaw Some Cert:\n\t\t-----BEGIN CERTIFICATE-----\n\t\tMIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA\n\t\toRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD\n\t\tVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs\n\t\tYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl\n\t\tczESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIyMTEyMzIyMzM0N1oXDTI5MTEyMzIy\n\t\tMzM0N1owejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD\n\t\tVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk\n\t\tIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF\n\t\tK4EEACIDYgAEVGm4GomfpkiziqEYP61nfKaz5OjDLr8Y0POrv4iAnFVHAmBT81Ms\n\t\tgfSLKL5r3V3mNzl1Zh7jwSBft14uhGdwpARoK0YNQc4OvptqVIiv2RprV53DMzge\n\t\trtwiumIargiCo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC\n\t\tBAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAzARBgorBgEEAZx4AQMCBAMC\n\t\tAQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE\n\t\tAZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB\n\t\tCDARBgorBgEEAZx4AQMIBAMCAXMwTQYJKwYBBAGceAEEBEB80kCZ1oAyCjWC6w3m\n\t\txOz+i4t6dFjk/Bqhm7+Jscf8D62CXtlwcKc4aM9CdO4LuKlwpdTU80VNQc6ZEuMF\n\t\tVzbRMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B\n\t\tAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQCN1qBYOywoZWGnQvk6u0Oh\n\t\t5zkEKykXU6sK8hA6L65rQcqWUjEHDa9AZUpx3UuCmpPc24dx6DTHc58M7TxcyKry\n\t\t8s4CvruBKFbQ6B8MHnH6k07MzsmiBnsiIhAscZ0ipGm6h8e/VM/6ULrAcVSxZ+Mh\n\t\tD/IogZAuCQARsGQ4QYXBT8Qc5mLnTkx30m1rZVlp1VcN4ngOo/1tz1jj1mfpG2zv\n\t\twNcQa9LwAzRLnnmLpxXA2OMbl7AaTWQenpL9rzBON2sg4OBl6lVhaSU0uBbFyCmR\n\t\tRvBqKC0iDD6TvyIikkMq05v5YwIKFYw++ICndz+fKcLEULZbziAsZ52qjM8iPVHC\n\t\tpN0yhVOr2g22F9zxlGH3WxTl9ymUytuv3vJL/aJiQM+n/Ri90Sc05EK4oIJ3+BS8\n\t\tyu5cVy9o2cQcOcQ8rhQh+Kv1sR9xrs25EXZF8KEETfhoJnN6KY1RwG7HsOfAQ3dV\n\t\tLWInQRaC/8JPyVS2zbd0+NRBJOnq4/quv/P3C4SBP98/ZuGrqN59uifyqC3Kodkl\n\t\tWkG/2UdhiLlCmOtsU+BYDZrSiYK1R9FNnlQCOGrkuVxpDwa2TbbvEEzQP7RXxotA\n\t\tKlxejvrY4VuK8agNqvffVofbdIIperK65K4+0mYIb+A6fU8QQHlCbti4ERSZ6UYD\n\t\tF/SjRih31+SAtWb42jueAA==\n\t\t-----END CERTIFICATE-----\n\tSome Cert (1):\n\t\tSerial Number: 0\n\t\tSubject: CN=SEV-VCEK,OU=Engineering,O=Advanced Micro Devices,L=Santa Clara,ST=CA,C=US\n\t\tIssuer: CN=SEV-Milan,OU=Engineering,O=Advanced Micro Devices,L=Santa Clara,ST=CA,C=US\n\t\tNot Before: 2022-11-23 22:33:47 +0000 UTC\n\t\tNot After: 2029-11-23 22:33:47 +0000 UTC\n\t\tSignature Algorithm: SHA384-RSAPSS\n\t\tPublic Key Algorithm: ECDSA\n"

testCases := map[string]struct {
formatter *attestationDocFormatterImpl
encodedCert string
expected string
wantErr bool
formatter *attestationDocFormatterImpl
cert []byte
expected string
wantErr bool
}{
"one cert": {
formatter: formatter(),
encodedCert: base64.StdEncoding.EncodeToString([]byte(validCert)),
expected: validCertExpected,
formatter: formatter(),
cert: []byte(validCert),
expected: validCertExpected,
},
"invalid cert": {
formatter: formatter(),
encodedCert: "invalid",
wantErr: true,
formatter: formatter(),
cert: []byte("invalid"),
wantErr: true,
},
}

Expand All @@ -300,7 +300,7 @@ F/SjRih31+SAtWb42jueAA==
assert := assert.New(t)

b := &strings.Builder{}
err := tc.formatter.parseCerts(b, "Some Cert", tc.encodedCert)
err := tc.formatter.parseCerts(b, "Some Cert", tc.cert)
if tc.wantErr {
assert.Error(err)
} else {
Expand Down

0 comments on commit 372aa0f

Please sign in to comment.