2022-09-05 03:06:08 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2023-06-09 09:41:02 -04:00
package snp
2022-03-22 11:03:15 -04:00
2022-10-27 05:04:23 -04:00
import (
"context"
2023-11-06 08:22:44 -05:00
"crypto/sha512"
"crypto/x509"
2022-10-27 05:04:23 -04:00
"encoding/json"
2023-11-06 08:22:44 -05:00
"encoding/pem"
2023-06-09 09:41:02 -04:00
"fmt"
2022-10-27 05:04:23 -04:00
"io"
2022-03-22 11:03:15 -04:00
2023-03-08 08:13:57 -05:00
"github.com/edgelesssys/constellation/v2/internal/attestation"
2023-11-06 08:22:44 -05:00
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
2023-06-09 09:41:02 -04:00
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
2022-10-27 05:04:23 -04:00
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
2023-11-06 08:22:44 -05:00
"github.com/google/go-sev-guest/abi"
sevclient "github.com/google/go-sev-guest/client"
2022-10-27 05:04:23 -04:00
"github.com/google/go-tpm-tools/client"
tpmclient "github.com/google/go-tpm-tools/client"
)
2023-06-09 09:41:02 -04:00
// Issuer for AWS SNP attestation.
2022-03-22 11:03:15 -04:00
type Issuer struct {
2023-06-09 09:41:02 -04:00
variant . AWSSEVSNP
2022-10-27 05:04:23 -04:00
* vtpm . Issuer
}
2023-06-09 09:41:02 -04:00
// NewIssuer creates a SEV-SNP based issuer for AWS.
2023-03-08 08:13:57 -05:00
func NewIssuer ( log attestation . Logger ) * Issuer {
2022-10-27 05:04:23 -04:00
return & Issuer {
Issuer : vtpm . NewIssuer (
vtpm . OpenVTPM ,
getAttestationKey ,
2023-11-06 08:22:44 -05:00
getInstanceInfo ,
2023-02-28 10:34:18 -05:00
log ,
2022-10-27 05:04:23 -04:00
) ,
}
}
// getAttestationKey returns a new attestation key.
func getAttestationKey ( tpm io . ReadWriter ) ( * tpmclient . Key , error ) {
tpmAk , err := client . AttestationKeyRSA ( tpm )
if err != nil {
2023-11-06 08:22:44 -05:00
return nil , fmt . Errorf ( "creating RSA Endorsement key: %w" , err )
2022-10-27 05:04:23 -04:00
}
return tpmAk , nil
}
2023-11-06 08:22:44 -05:00
// getInstanceInfo generates an extended SNP report, i.e. the report and any loaded certificates.
// Report generation is triggered by sending ioctl syscalls to the SNP guest device, the AMD PSP generates the report.
2022-10-27 05:04:23 -04:00
// The returned bytes will be written into the attestation document.
2023-11-06 08:22:44 -05:00
func getInstanceInfo ( _ context . Context , tpm io . ReadWriteCloser , _ [ ] byte ) ( [ ] byte , error ) {
tpmAk , err := client . AttestationKeyRSA ( tpm )
if err != nil {
return nil , fmt . Errorf ( "creating RSA Endorsement key: %w" , err )
}
encoded , err := x509 . MarshalPKIXPublicKey ( tpmAk . PublicKey ( ) )
if err != nil {
return nil , fmt . Errorf ( "marshalling public key: %w" , err )
}
akDigest := sha512 . Sum512 ( encoded )
device , err := sevclient . OpenDevice ( )
if err != nil {
return nil , fmt . Errorf ( "opening sev device: %w" , err )
}
defer device . Close ( )
report , certs , err := sevclient . GetRawExtendedReportAtVmpl ( device , akDigest , 0 )
if err != nil {
return nil , fmt . Errorf ( "getting extended report: %w" , err )
}
vlek , err := pemEncodedVLEK ( certs )
if err != nil {
return nil , fmt . Errorf ( "parsing vlek: %w" , err )
}
raw , err := json . Marshal ( snp . InstanceInfo { AttestationReport : report , ReportSigner : vlek } )
if err != nil {
return nil , fmt . Errorf ( "marshalling instance info: %w" , err )
2022-10-27 05:04:23 -04:00
}
2023-11-06 08:22:44 -05:00
return raw , nil
2022-03-22 11:03:15 -04:00
}
2023-11-06 08:22:44 -05:00
// pemEncodedVLEK takes a marshalled SNP certificate table and returns the PEM-encoded VLEK certificate.
// AMD documentation on certificate tables can be found in section 4.1.8.1, revision 2.03 "SEV-ES Guest-Hypervisor Communication Block Standardization".
// https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf
func pemEncodedVLEK ( certs [ ] byte ) ( [ ] byte , error ) {
certTable := abi . CertTable { }
if err := certTable . Unmarshal ( certs ) ; err != nil {
return nil , fmt . Errorf ( "unmarshalling SNP certificate table: %w" , err )
}
vlekRaw , err := certTable . GetByGUIDString ( abi . VlekGUID )
if err != nil {
return nil , fmt . Errorf ( "getting VLEK certificate: %w" , err )
}
// An optional check for certificate well-formedness. vlekRaw == cert.Raw.
cert , err := x509 . ParseCertificate ( vlekRaw )
if err != nil {
return nil , fmt . Errorf ( "parsing certificate: %w" , err )
}
certPEM := pem . EncodeToMemory ( & pem . Block {
Type : "CERTIFICATE" ,
Bytes : cert . Raw ,
} )
return certPEM , nil
2022-03-22 11:03:15 -04:00
}