Skip to content

abi: parse v5 attestation reports #174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ const (
policyMemAES256XTSBit = 22
policyRAPLDisBit = 23
policyCipherTextHidingDRAMBit = 24
policyPageSwapDisableBit = 25

maxPlatformInfoBit = 5
maxPlatformInfoBit = 7

signatureOffset = 0x2A0
ecdsaRSsize = 72 // From the ECDSA-P384-SHA384 format in SEV SNP API specification.
Expand Down Expand Up @@ -136,14 +137,17 @@ const (
// ReportVersion2 is set by the SNP API specification
// https://web.archive.org/web/20231222054111if_/http://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf
ReportVersion2 = 2
// MinSupportedReportVersion is the lowest attestation report version that this library supports.
MinSupportedReportVersion = 2

// ReportVersion3 is set by the SNP API specification
// https://www.amd.com/system/files/TechDocs/56860.pdf
ReportVersion3 = 3
// ReportVersion4 is set by the SNP API specification
ReportVersion4 = 4
// ReportVersion5 is set by the SNP API specification
ReportVersion5 = 5
// MinSupportedReportVersion is the lowest attestation report version that this library supports.
MinSupportedReportVersion = ReportVersion2
// MaxSupportedReportVersion is the highest attestation report version that this library supports.
MaxSupportedReportVersion = 4
MaxSupportedReportVersion = ReportVersion5
)

// CertTableHeaderEntry defines an entry of the beginning of an extended attestation report which
Expand Down Expand Up @@ -204,6 +208,8 @@ type SnpPlatformInfo struct {
// AliasCheckComplete indicates that alias detection has completed since the last system reset and there are no aliasing addresses.
// Mitigation for https://badram.eu/, see https://www.amd.com/en/resources/product-security/bulletin/amd-sb-3015.html#mitigation.
AliasCheckComplete bool
// Indicates that SEV-TIO is enabled.
TIOEnabled bool
}

// SnpPolicy represents the bitmask guest policy that governs the VM's behavior from launch.
Expand All @@ -229,6 +235,8 @@ type SnpPolicy struct {
RAPLDis bool
// CipherTextHidingDRAM is true if ciphertext hiding for the DRAM must be enabled.
CipherTextHidingDRAM bool
// PageSwapDisable is true if Guest access to SNP_PAGE_MOVE, SNP_SWAP_OUT and SNP_SWAP_IN commands is disabled.
PageSwapDisable bool
}

// ParseSnpPolicy interprets the SEV SNP API's guest policy bitmask into an SnpPolicy struct type.
Expand All @@ -237,7 +245,7 @@ func ParseSnpPolicy(guestPolicy uint64) (SnpPolicy, error) {
if guestPolicy&uint64(1<<policyReserved1bit) == 0 {
return result, fmt.Errorf("policy[%d] is reserved, must be 1, got 0", policyReserved1bit)
}
if err := mbz64(guestPolicy, "policy", 63, 25); err != nil {
if err := mbz64(guestPolicy, "policy", 63, 26); err != nil {
return result, err
}
result.ABIMinor = uint8(guestPolicy & 0xff)
Expand All @@ -250,6 +258,7 @@ func ParseSnpPolicy(guestPolicy uint64) (SnpPolicy, error) {
result.MemAES256XTS = (guestPolicy & (1 << policyMemAES256XTSBit)) != 0
result.RAPLDis = (guestPolicy & (1 << policyRAPLDisBit)) != 0
result.CipherTextHidingDRAM = (guestPolicy & (1 << policyCipherTextHidingDRAMBit)) != 0
result.PageSwapDisable = (guestPolicy & (1 << policyPageSwapDisableBit)) != 0
return result, nil
}

Expand Down Expand Up @@ -280,6 +289,9 @@ func SnpPolicyToBytes(policy SnpPolicy) uint64 {
if policy.CipherTextHidingDRAM {
result |= uint64(1 << policyCipherTextHidingDRAMBit)
}
if policy.PageSwapDisable {
result |= uint64(1 << policyPageSwapDisableBit)
}
return result
}

Expand All @@ -293,6 +305,10 @@ func ParseSnpPlatformInfo(platformInfo uint64) (SnpPlatformInfo, error) {
RAPLDisabled: (platformInfo & (1 << 3)) != 0,
CiphertextHidingDRAMEnabled: (platformInfo & (1 << 4)) != 0,
AliasCheckComplete: (platformInfo & (1 << 5)) != 0,
TIOEnabled: (platformInfo & (1 << 7)) != 0,
}
if platformInfo&(1<<6) != 0 {
return result, fmt.Errorf("reserved platform info bit 6 set: 0x%x", platformInfo)
}
reserved := platformInfo & ^uint64((1<<(maxPlatformInfoBit+1))-1)
if reserved != 0 {
Expand Down Expand Up @@ -558,7 +574,9 @@ func ReportToProto(data []uint8) (*pb.Report, error) {
return nil, err
}
r.LaunchTcb = binary.LittleEndian.Uint64(data[0x1F0:0x1F8])
if err := mbz(data, 0x1F8, signatureOffset); err != nil {
r.LaunchMitVector = binary.LittleEndian.Uint64(data[0x1F8:0x200])
r.CurrentMitVector = binary.LittleEndian.Uint64(data[0x200:0x208])
if err := mbz(data, 0x208, signatureOffset); err != nil {
return nil, err
}
if r.SignatureAlgo == SignEcdsaP384Sha384 {
Expand Down Expand Up @@ -716,6 +734,8 @@ func ReportToAbiBytes(r *pb.Report) ([]byte, error) {
data[0x1ED] = byte(r.CommittedMinor)
data[0x1EE] = byte(r.CommittedMajor)
binary.LittleEndian.PutUint64(data[0x1F0:0x1F8], r.LaunchTcb)
binary.LittleEndian.PutUint64(data[0x1F8:0x200], r.LaunchMitVector)
binary.LittleEndian.PutUint64(data[0x200:0x208], r.CurrentMitVector)

copy(data[signatureOffset:ReportSize], r.Signature[:])
return data, nil
Expand Down
25 changes: 17 additions & 8 deletions abi/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ func TestReportMbz(t *testing.T) {
},
{
name: "pre-signature reserved",
changeIndex: 0x1f9,
wantErr: "mbz range [0x1f8:0x2a0] not all zero: 00cc",
changeIndex: 0x209,
wantErr: "mbz range [0x208:0x2a0] not all zero: 00cc",
},
{
name: "post-ecdsa signature reserved",
Expand All @@ -156,10 +156,10 @@ func TestReportMbz(t *testing.T) {
wantErr: "policy[17] is reserved, must be 1, got 0",
},
{
name: "Guest policy bit 25",
name: "Guest policy bit 26",
changeIndex: policyOffset + 3, // Bits 24-31
changeValue: 0x80, // Set bit 25
wantErr: "malformed guest policy: mbz range policy[0x19:0x3f] not all zero",
changeValue: 0x40, // Set bit 26
wantErr: "malformed guest policy: mbz range policy[0x1a:0x3f] not all zero",
},
}
reportProto := &spb.Report{}
Expand All @@ -177,7 +177,10 @@ func TestReportMbz(t *testing.T) {
changeValue = tc.changeValue
}
raw[tc.changeIndex] = changeValue
if _, err := ReportToProto(raw); !strings.Contains(err.Error(), tc.wantErr) {
_, err = ReportToProto(raw)
if err == nil {
t.Errorf("%s: ReportToProto(%v) = _, nil. Want error %q", tc.name, reportProto, tc.wantErr)
} else if !strings.Contains(err.Error(), tc.wantErr) {
t.Errorf("%s: ReportToProto(%v) = _, %v. Want error %v", tc.name, reportProto, err, tc.wantErr)
}
}
Expand Down Expand Up @@ -214,6 +217,7 @@ func TestSnpPolicySection(t *testing.T) {
MemAES256XTS: (entropy[tc*3+2] & 32) != 0,
RAPLDis: (entropy[tc*3+2] & 64) != 0,
CipherTextHidingDRAM: (entropy[tc*3+2] & 128) != 0,
PageSwapDisable: (entropy[tc*3+3] & 1) != 0,
}

got, err := ParseSnpPolicy(SnpPolicyToBytes(policy))
Expand Down Expand Up @@ -256,19 +260,24 @@ func TestSnpPlatformInfo(t *testing.T) {
},
},
{
input: 63,
input: 191,
want: SnpPlatformInfo{
TSMEEnabled: true,
SMTEnabled: true,
ECCEnabled: true,
RAPLDisabled: true,
CiphertextHidingDRAMEnabled: true,
AliasCheckComplete: true,
TIOEnabled: true,
},
},
{
input: 256,
wantErr: "unrecognized platform info bit(s): 0x100",
},
{
input: 64,
wantErr: "unrecognized platform info bit(s): 0x40",
wantErr: "reserved platform info bit 6 set: 0x40",
},
}
for _, tc := range tests {
Expand Down
4 changes: 2 additions & 2 deletions proto/check/check.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions proto/fakekds/fakekds.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions proto/sevsnp.proto
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ message Report {
bytes signature = 28; // Should be 512 bytes long

uint32 cpuid1eax_fms = 29; // The cpuid(1).eax & 0x0fff0fff representation of family/model/stepping
uint64 launch_mit_vector = 30; // The verified mitigation vector value at the time the guest was launched
uint64 current_mit_vector = 31; // Value of the current verified mitigation vector
}

message CertificateChain {
Expand Down
Loading