mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
* inital tdx attestation support * fix fetching and verification errors * fix tests * fix lint problems * fix reading attestation policy * add tdx package * remove Makefile configuration for staticly built binaries
This commit is contained in:
committed by
GitHub
parent
79c66a89c3
commit
9c8ddfd2b1
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/azure"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/tdx"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
|
||||
@@ -59,14 +60,37 @@ func formTeeData(pubKey []byte, teeNonce []byte) []byte {
|
||||
func getPlatformProvider(platformType attestation.PlatformType, pubKey []byte) (attestation.Provider, error) {
|
||||
switch platformType {
|
||||
case attestation.SNPvTPM:
|
||||
return vtpm.New(pubKey, true, vmpl2, nil), nil
|
||||
return vtpm.NewProvider(pubKey, true, vmpl2), nil
|
||||
case attestation.Azure:
|
||||
return azure.New(nil), nil
|
||||
return azure.NewProvider(), nil
|
||||
case attestation.TDX:
|
||||
return tdx.NewProvider(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported platform type: %d", platformType)
|
||||
}
|
||||
}
|
||||
|
||||
func getPlatformVerifier(platformType attestation.PlatformType, pubKey []byte) (attestation.Verifier, error) {
|
||||
var verifier attestation.Verifier
|
||||
|
||||
switch platformType {
|
||||
case attestation.SNPvTPM:
|
||||
verifier = vtpm.NewVerifier(pubKey, nil)
|
||||
case attestation.Azure:
|
||||
verifier = azure.NewVerifier(nil)
|
||||
case attestation.TDX:
|
||||
verifier = tdx.NewVerifier()
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported platform type: %d", platformType)
|
||||
}
|
||||
|
||||
err := verifier.JSONToPolicy(attestation.AttestationPolicyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return verifier, nil
|
||||
}
|
||||
|
||||
//export callVerificationValidationCallback
|
||||
func callVerificationValidationCallback(platformType C.int, pubKey *C.uchar, pubKeyLen C.int, attestReport *C.uchar, attestReportSize C.int, teeNonceByte *C.uchar, vTPMNonceByte *C.uchar) C.int {
|
||||
pubKeyCert := C.GoBytes(unsafe.Pointer(pubKey), pubKeyLen)
|
||||
@@ -74,16 +98,16 @@ func callVerificationValidationCallback(platformType C.int, pubKey *C.uchar, pub
|
||||
vTPMNonce := C.GoBytes(unsafe.Pointer(vTPMNonceByte), vtpm.Nonce)
|
||||
pType := attestation.PlatformType(int(platformType))
|
||||
attestationReport := C.GoBytes(unsafe.Pointer(attestReport), attestReportSize)
|
||||
|
||||
|
||||
teeData := formTeeData(pubKeyCert, teeNonceData)
|
||||
|
||||
provider, err := getPlatformProvider(pType, pubKeyCert)
|
||||
verifier, err := getPlatformVerifier(pType, pubKeyCert)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "no attestation provider found for platform type %s", err.Error())
|
||||
return C.int(-1)
|
||||
}
|
||||
|
||||
err = provider.VerifyAttestation(attestationReport, teeData, vTPMNonce)
|
||||
err = verifier.VerifyAttestation(attestationReport, teeData, vTPMNonce)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "verification callback failed %s", err.Error())
|
||||
return C.int(-1)
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/google/go-sev-guest/client"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
tdxcliet "github.com/google/go-tdx-guest/client"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
type PlatformType int
|
||||
@@ -25,6 +22,7 @@ const (
|
||||
SNPvTPM
|
||||
AzureToken
|
||||
Azure
|
||||
TDX
|
||||
NoCC
|
||||
)
|
||||
|
||||
@@ -33,16 +31,7 @@ const (
|
||||
azureApiVersion = "2021-02-01"
|
||||
)
|
||||
|
||||
var (
|
||||
AttestationPolicy = Config{Config: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}, PcrConfig: &PcrConfig{}}
|
||||
ErrAttestationPolicyOpen = errors.New("failed to open Attestation Policy file")
|
||||
ErrAttestationPolicyDecode = errors.New("failed to decode Attestation Policy file")
|
||||
ErrAttestationPolicyMissing = errors.New("failed due to missing Attestation Policy file")
|
||||
ErrAttestationPolicyEncode = errors.New("failed to encode the Attestation Policy")
|
||||
ErrProtoMarshalFailed = errors.New("failed to marshal protojson")
|
||||
ErrJsonMarshalFailed = errors.New("failed to marshal json")
|
||||
ErrJsonUnarshalFailed = errors.New("failed to unmarshal json")
|
||||
)
|
||||
var AttestationPolicyPath string
|
||||
|
||||
type PcrValues struct {
|
||||
Sha256 map[string]string `json:"sha256"`
|
||||
@@ -68,65 +57,14 @@ type Provider interface {
|
||||
Attestation(teeNonce []byte, vTpmNonce []byte) ([]byte, error)
|
||||
TeeAttestation(teeNonce []byte) ([]byte, error)
|
||||
VTpmAttestation(vTpmNonce []byte) ([]byte, error)
|
||||
VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error
|
||||
VerifTeeAttestation(report []byte, teeNonce []byte) error
|
||||
VerifVTpmAttestation(report []byte, vTpmNonce []byte) error
|
||||
AzureAttestationToken(tokenNonce []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
func ReadAttestationPolicy(policyPath string, attestationConfiguration *Config) error {
|
||||
if policyPath != "" {
|
||||
policyData, err := os.ReadFile(policyPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(ErrAttestationPolicyOpen, err)
|
||||
}
|
||||
|
||||
return ReadAttestationPolicyFromByte(policyData, attestationConfiguration)
|
||||
}
|
||||
|
||||
return ErrAttestationPolicyMissing
|
||||
}
|
||||
|
||||
func ReadAttestationPolicyFromByte(policyData []byte, attestationConfiguration *Config) error {
|
||||
unmarshalOptions := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
|
||||
|
||||
if err := unmarshalOptions.Unmarshal(policyData, attestationConfiguration.Config); err != nil {
|
||||
return errors.Wrap(ErrAttestationPolicyDecode, err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(policyData, attestationConfiguration.PcrConfig); err != nil {
|
||||
return errors.Wrap(ErrAttestationPolicyDecode, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConvertAttestationPolicyToJSON(attestationConfiguration *Config) ([]byte, error) {
|
||||
pbJson, err := protojson.Marshal(attestationConfiguration.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrProtoMarshalFailed, err)
|
||||
}
|
||||
|
||||
var pbMap map[string]interface{}
|
||||
if err := json.Unmarshal(pbJson, &pbMap); err != nil {
|
||||
return nil, errors.Wrap(ErrJsonUnarshalFailed, err)
|
||||
}
|
||||
|
||||
pcrJson, err := json.Marshal(attestationConfiguration.PcrConfig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrJsonMarshalFailed, err)
|
||||
}
|
||||
|
||||
var pcrMap map[string]interface{}
|
||||
if err := json.Unmarshal(pcrJson, &pcrMap); err != nil {
|
||||
return nil, errors.Wrap(ErrJsonUnarshalFailed, err)
|
||||
}
|
||||
|
||||
for k, v := range pcrMap {
|
||||
pbMap[k] = v
|
||||
}
|
||||
|
||||
return json.MarshalIndent(pbMap, "", " ")
|
||||
type Verifier interface {
|
||||
VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error
|
||||
VerifTeeAttestation(report []byte, teeNonce []byte) error
|
||||
VerifVTpmAttestation(report []byte, vTpmNonce []byte) error
|
||||
JSONToPolicy(path string) error
|
||||
}
|
||||
|
||||
// CCPlatform returns the type of the confidential computing platform.
|
||||
@@ -135,6 +73,7 @@ func CCPlatform() PlatformType {
|
||||
{SevGuestvTPMExists, SNPvTPM},
|
||||
{SevGuesDeviceExists, SNP},
|
||||
{isAzureVM, Azure},
|
||||
{TDXGuestDeviceExists, TDX},
|
||||
}
|
||||
|
||||
for _, c := range checks {
|
||||
@@ -197,3 +136,13 @@ func isAzureVM() bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func TDXGuestDeviceExists() bool {
|
||||
d, err := tdxcliet.OpenDevice()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
d.Close()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -25,16 +25,20 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var MaaURL = "https://sharedeus2.eus2.attest.azure.net"
|
||||
var (
|
||||
MaaURL = "https://sharedeus2.eus2.attest.azure.net"
|
||||
ErrFetchAzureToken = errors.New("failed to fetch Azure token")
|
||||
)
|
||||
|
||||
var _ attestation.Provider = (*provider)(nil)
|
||||
var (
|
||||
_ attestation.Provider = (*provider)(nil)
|
||||
_ attestation.Verifier = (*verifier)(nil)
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
writer io.Writer
|
||||
}
|
||||
type provider struct{}
|
||||
|
||||
func New(writer io.Writer) attestation.Provider {
|
||||
return provider{writer: writer}
|
||||
func NewProvider() attestation.Provider {
|
||||
return provider{}
|
||||
}
|
||||
|
||||
func (a provider) Attestation(teeNonce []byte, vTpmNonce []byte) ([]byte, error) {
|
||||
@@ -84,20 +88,56 @@ func (a provider) VTpmAttestation(vTpmNonce []byte) ([]byte, error) {
|
||||
return proto.Marshal(quote)
|
||||
}
|
||||
|
||||
func (a provider) VerifTeeAttestation(report []byte, teeNonce []byte) error {
|
||||
func (a provider) AzureAttestationToken(tokenNonce []byte) ([]byte, error) {
|
||||
quote, err := FetchAzureAttestationToken(tokenNonce, MaaURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrFetchAzureToken, err)
|
||||
}
|
||||
|
||||
return quote, nil
|
||||
}
|
||||
|
||||
type verifier struct {
|
||||
writer io.Writer
|
||||
Policy *attestation.Config
|
||||
}
|
||||
|
||||
func NewVerifier(writer io.Writer) attestation.Verifier {
|
||||
policy := &attestation.Config{
|
||||
Config: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}},
|
||||
PcrConfig: &attestation.PcrConfig{},
|
||||
}
|
||||
|
||||
return verifier{
|
||||
writer: writer,
|
||||
Policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVerifierWithPolicy(writer io.Writer, policy *attestation.Config) attestation.Verifier {
|
||||
if policy == nil {
|
||||
return NewVerifier(writer)
|
||||
}
|
||||
return verifier{
|
||||
writer: writer,
|
||||
Policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
func (a verifier) VerifTeeAttestation(report []byte, teeNonce []byte) error {
|
||||
attestationReport, err := abi.ReportCertsToProto(report)
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("failed to convert TEE report to proto"), err)
|
||||
}
|
||||
|
||||
return quoteprovider.VerifyAttestationReportTLS(attestationReport, teeNonce)
|
||||
return quoteprovider.VerifyAttestationReportTLS(attestationReport, teeNonce, a.Policy)
|
||||
}
|
||||
|
||||
func (a provider) VerifVTpmAttestation(report []byte, vTpmNonce []byte) error {
|
||||
return vtpm.VerifyQuote(report, nil, vTpmNonce, a.writer)
|
||||
func (a verifier) VerifVTpmAttestation(report []byte, vTpmNonce []byte) error {
|
||||
return vtpm.VerifyQuote(report, nil, vTpmNonce, a.writer, a.Policy)
|
||||
}
|
||||
|
||||
func (a provider) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error {
|
||||
func (a verifier) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error {
|
||||
var tokenNonce [vtpm.Nonce]byte
|
||||
copy(tokenNonce[:], teeNonce)
|
||||
|
||||
@@ -108,20 +148,15 @@ func (a provider) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []
|
||||
}
|
||||
|
||||
snpReport := quote.GetSevSnpAttestation()
|
||||
if err = quoteprovider.VerifyAttestationReportTLS(snpReport, nil); err != nil {
|
||||
if err = quoteprovider.VerifyAttestationReportTLS(snpReport, nil, a.Policy); err != nil {
|
||||
return fmt.Errorf("failed to verify vTPM attestation report: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a provider) AzureAttestationToken(tokenNonce []byte) ([]byte, error) {
|
||||
quote, err := FetchAzureAttestationToken(tokenNonce, MaaURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(vtpm.ErrFetchAzureToken, err)
|
||||
}
|
||||
|
||||
return quote, nil
|
||||
func (a verifier) JSONToPolicy(path string) error {
|
||||
return vtpm.ReadPolicy(path, a.Policy)
|
||||
}
|
||||
|
||||
func GenerateAttestationPolicy(token, product string, policy uint64) (*attestation.Config, error) {
|
||||
|
||||
@@ -24,15 +24,3 @@ func (e *EmptyProvider) VTpmAttestation(vTpmNonce []byte) ([]byte, error) {
|
||||
func (e *EmptyProvider) AzureAttestationToken(nonce []byte) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (e *EmptyProvider) VerifTeeAttestation(report []byte, teeNonce []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmptyProvider) VerifVTpmAttestation(report []byte, vTpmNonce []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmptyProvider) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/google/go-sev-guest/verify/trust"
|
||||
"github.com/google/logger"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,8 +38,8 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
errProductLine = errors.New(fmt.Sprintf("product name must be %s or %s", sevProductNameMilan, sevProductNameGenoa))
|
||||
errAttVerification = errors.New("attestation verification failed")
|
||||
ErrProductLine = errors.New(fmt.Sprintf("product name must be %s or %s", sevProductNameMilan, sevProductNameGenoa))
|
||||
ErrAttVerification = errors.New("attestation verification failed")
|
||||
errAttValidation = errors.New("attestation validation failed")
|
||||
)
|
||||
|
||||
@@ -73,21 +72,16 @@ func fillInAttestationLocal(attestation *sevsnp.Attestation, cfg *check.Config)
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyConfig(attConf *check.Config) (*check.Config, error) {
|
||||
copy := proto.Clone(attConf).(*check.Config)
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
func verifyReport(attestationPB *sevsnp.Attestation, cfg *check.Config) error {
|
||||
sopts, err := verify.RootOfTrustToOptions(cfg.RootOfTrust)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get root of trust options: %v", errors.Wrap(errAttVerification, err))
|
||||
return fmt.Errorf("failed to get root of trust options: %v", errors.Wrap(ErrAttVerification, err))
|
||||
}
|
||||
|
||||
if cfg.Policy.Product == nil {
|
||||
productName := GetProductName(cfg.RootOfTrust.ProductLine)
|
||||
if productName == sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN {
|
||||
return errProductLine
|
||||
return ErrProductLine
|
||||
}
|
||||
|
||||
sopts.Product = &sevsnp.SevProduct{
|
||||
@@ -108,7 +102,7 @@ func verifyReport(attestationPB *sevsnp.Attestation, cfg *check.Config) error {
|
||||
}
|
||||
|
||||
if err := verify.SnpAttestation(attestationPB, sopts); err != nil {
|
||||
return errors.Wrap(errAttVerification, err)
|
||||
return errors.Wrap(ErrAttVerification, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -117,7 +111,7 @@ func verifyReport(attestationPB *sevsnp.Attestation, cfg *check.Config) error {
|
||||
func validateReport(attestationPB *sevsnp.Attestation, cfg *check.Config) error {
|
||||
opts, err := validate.PolicyToOptions(cfg.Policy)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get policy for validation: %v", errors.Wrap(errAttVerification, err))
|
||||
return fmt.Errorf("failed to get policy for validation: %v", errors.Wrap(ErrAttVerification, err))
|
||||
}
|
||||
|
||||
if err = validate.SnpAttestation(attestationPB, opts); err != nil {
|
||||
@@ -131,11 +125,8 @@ func GetLeveledQuoteProvider() (client.LeveledQuoteProvider, error) {
|
||||
return client.GetLeveledQuoteProvider()
|
||||
}
|
||||
|
||||
func VerifyAttestationReportTLS(attestationPB *sevsnp.Attestation, reportData []byte) error {
|
||||
config, err := copyConfig(attestation.AttestationPolicy.Config)
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("failed to create a copy of attestation policy"), err)
|
||||
}
|
||||
func VerifyAttestationReportTLS(attestationPB *sevsnp.Attestation, reportData []byte, policy *attestation.Config) error {
|
||||
config := policy.Config
|
||||
|
||||
// Certificate chain is populated based on the extra data that is appended to the SEV-SNP attestation report.
|
||||
// This data is not part of the attestation report and it will be ignored.
|
||||
|
||||
@@ -12,13 +12,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-sev-guest/proto/sevsnp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
config "github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
func TestFillInAttestationLocal(t *testing.T) {
|
||||
@@ -70,145 +67,3 @@ func TestFillInAttestationLocal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportSuccess(t *testing.T) {
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
goodProduct int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, validation and verification is performed succsessfully",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
goodProduct: 1,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := VerifyAttestationReportTLS(tt.attestationReport, tt.reportData)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportMalformedSignature(t *testing.T) {
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
|
||||
// Change random data so in the signature so the signature failes
|
||||
attestationPB.Report.Signature[0] = attestationPB.Report.Signature[0] ^ 0x01
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, distorted signature",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
err: errAttVerification,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := VerifyAttestationReportTLS(tt.attestationReport, tt.reportData)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportUnknownProduct(t *testing.T) {
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, unknown product",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
err: errProductLine,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
config.AttestationPolicy.Config.RootOfTrust.ProductLine = ""
|
||||
config.AttestationPolicy.Config.Policy.Product = nil
|
||||
err := VerifyAttestationReportTLS(tt.attestationReport, tt.reportData)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportMalformedPolicy(t *testing.T) {
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
|
||||
// Change random data in the measurement so the measurement does not match
|
||||
attestationPB.Report.Measurement[0] = attestationPB.Report.Measurement[0] ^ 0x01
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, malformed policy (measurement)",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
err: errAttVerification,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := VerifyAttestationReportTLS(tt.attestationReport, tt.reportData)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepVerifyAttReport(t *testing.T) (*sevsnp.Attestation, []byte) {
|
||||
file, err := os.ReadFile("../../../attestation.bin")
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(file) < abi.ReportSize {
|
||||
file = append(file, make([]byte, abi.ReportSize-len(file))...)
|
||||
}
|
||||
|
||||
rr, err := abi.ReportCertsToProto(file)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.AttestationPolicy = config.Config{Config: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}, PcrConfig: &config.PcrConfig{}}
|
||||
|
||||
attestationPolicyFile, err := os.ReadFile("../../../scripts/attestation_policy/attestation_policy.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
unmarshalOptions := protojson.UnmarshalOptions{DiscardUnknown: true}
|
||||
|
||||
err = unmarshalOptions.Unmarshal(attestationPolicyFile, config.AttestationPolicy.Config)
|
||||
require.NoError(t, err)
|
||||
|
||||
config.AttestationPolicy.Config.Policy.Product = &sevsnp.SevProduct{Name: sevsnp.SevProduct_SEV_PRODUCT_MILAN}
|
||||
config.AttestationPolicy.Config.Policy.FamilyId = rr.Report.FamilyId
|
||||
config.AttestationPolicy.Config.Policy.ImageId = rr.Report.ImageId
|
||||
config.AttestationPolicy.Config.Policy.Measurement = rr.Report.Measurement
|
||||
config.AttestationPolicy.Config.Policy.HostData = rr.Report.HostData
|
||||
config.AttestationPolicy.Config.Policy.ReportIdMa = rr.Report.ReportIdMa
|
||||
config.AttestationPolicy.Config.RootOfTrust.ProductLine = sevProductNameMilan
|
||||
|
||||
return rr, rr.Report.ReportData
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !embed
|
||||
// +build !embed
|
||||
|
||||
package tdx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/google/go-tdx-guest/abi"
|
||||
"github.com/google/go-tdx-guest/client"
|
||||
"github.com/google/go-tdx-guest/proto/checkconfig"
|
||||
valdatetdx "github.com/google/go-tdx-guest/validate"
|
||||
verifytdx "github.com/google/go-tdx-guest/verify"
|
||||
trusttdx "github.com/google/go-tdx-guest/verify/trust"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
var errOpenTDXDevice = errors.New("failed to open TDX device")
|
||||
|
||||
var (
|
||||
_ attestation.Provider = (*provider)(nil)
|
||||
_ attestation.Verifier = (*verifier)(nil)
|
||||
)
|
||||
|
||||
var (
|
||||
timeout = time.Minute * 2
|
||||
maxTryDelay = time.Second * 30
|
||||
)
|
||||
|
||||
type provider struct{}
|
||||
|
||||
func NewProvider() attestation.Provider {
|
||||
return provider{}
|
||||
}
|
||||
|
||||
func (v provider) Attestation(teeNonce []byte, vTpmNonce []byte) ([]byte, error) {
|
||||
return v.TeeAttestation(teeNonce)
|
||||
}
|
||||
|
||||
func (v provider) TeeAttestation(teeNonce []byte) ([]byte, error) {
|
||||
quoteprovider, err := client.GetQuoteProvider()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, errOpenTDXDevice)
|
||||
}
|
||||
|
||||
return quoteprovider.GetRawQuote([64]byte(teeNonce))
|
||||
}
|
||||
|
||||
func (v provider) VTpmAttestation(vTpmNonce []byte) ([]byte, error) {
|
||||
return nil, errors.New("vTPM attestation fetch is not supported")
|
||||
}
|
||||
|
||||
func (v provider) AzureAttestationToken(tokenNonce []byte) ([]byte, error) {
|
||||
return nil, errors.New("Azure attestation token is not supported")
|
||||
}
|
||||
|
||||
type verifier struct {
|
||||
Policy *checkconfig.Config
|
||||
}
|
||||
|
||||
func NewVerifier() attestation.Verifier {
|
||||
Policy := &checkconfig.Config{
|
||||
RootOfTrust: &checkconfig.RootOfTrust{},
|
||||
Policy: &checkconfig.Policy{HeaderPolicy: &checkconfig.HeaderPolicy{}, TdQuoteBodyPolicy: &checkconfig.TDQuoteBodyPolicy{}},
|
||||
}
|
||||
|
||||
return verifier{
|
||||
Policy: Policy,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVerifierWithPolicy(policy *checkconfig.Config) attestation.Verifier {
|
||||
if policy == nil {
|
||||
return NewVerifier()
|
||||
}
|
||||
return verifier{
|
||||
Policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
func (v verifier) VerifTeeAttestation(report []byte, teeNonce []byte) error {
|
||||
if v.Policy == nil {
|
||||
return fmt.Errorf("tdx policy is not provided")
|
||||
}
|
||||
|
||||
quote, err := abi.QuoteToProto(report)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sopts, err := verifytdx.RootOfTrustToOptions(v.Policy.RootOfTrust)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sopts.Getter = &trusttdx.RetryHTTPSGetter{
|
||||
Timeout: timeout,
|
||||
MaxRetryDelay: maxTryDelay,
|
||||
Getter: &trusttdx.SimpleHTTPSGetter{},
|
||||
}
|
||||
|
||||
if err := verifytdx.TdxQuote(quote, sopts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts, err := valdatetdx.PolicyToOptions(v.Policy.Policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := valdatetdx.TdxQuote(quote, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v verifier) VerifVTpmAttestation(report []byte, vTpmNonce []byte) error {
|
||||
return errors.New("VTPM attestation verification is not supported")
|
||||
}
|
||||
|
||||
func (v verifier) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error {
|
||||
return v.VerifTeeAttestation(report, teeNonce)
|
||||
}
|
||||
|
||||
func (v verifier) JSONToPolicy(path string) error {
|
||||
return ReadTDXAttestationPolicy(path, v.Policy)
|
||||
}
|
||||
|
||||
func ReadTDXAttestationPolicy(policyPath string, policy *checkconfig.Config) error {
|
||||
policyByte, err := os.ReadFile(policyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := protojson.Unmarshal(policyByte, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+124
-25
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-sev-guest/proto/sevsnp"
|
||||
"github.com/google/go-tpm-tools/client"
|
||||
"github.com/google/go-tpm-tools/proto/attest"
|
||||
@@ -26,11 +28,15 @@ import (
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var _ attestation.Provider = (*provider)(nil)
|
||||
var (
|
||||
_ attestation.Provider = (*provider)(nil)
|
||||
_ attestation.Verifier = (*verifier)(nil)
|
||||
)
|
||||
|
||||
const (
|
||||
eventLog = "/sys/kernel/security/tpm0/binary_bios_measurements"
|
||||
@@ -43,10 +49,15 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ExternalTPM io.ReadWriteCloser
|
||||
ErrNoHashAlgo = errors.New("hash algo is not supported")
|
||||
ErrFetchQuote = errors.New("failed to fetch vTPM quote")
|
||||
ErrFetchAzureToken = errors.New("failed to fetch Azure token")
|
||||
ExternalTPM io.ReadWriteCloser
|
||||
ErrNoHashAlgo = errors.New("hash algo is not supported")
|
||||
ErrFetchQuote = errors.New("failed to fetch vTPM quote")
|
||||
ErrAttestationPolicyOpen = errors.New("failed to open Attestation Policy file")
|
||||
ErrAttestationPolicyDecode = errors.New("failed to decode Attestation Policy file")
|
||||
ErrAttestationPolicyMissing = errors.New("failed due to missing Attestation Policy file")
|
||||
ErrProtoMarshalFailed = errors.New("failed to marshal protojson")
|
||||
ErrJsonMarshalFailed = errors.New("failed to marshal json")
|
||||
ErrJsonUnarshalFailed = errors.New("failed to unmarshal json")
|
||||
)
|
||||
|
||||
type tpm struct {
|
||||
@@ -97,15 +108,13 @@ type provider struct {
|
||||
pubKey []byte
|
||||
teeAttestaion bool
|
||||
vmpl uint
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
func New(pubKey []byte, teeAttestation bool, vmpl uint, writer io.Writer) attestation.Provider {
|
||||
func NewProvider(pubKey []byte, teeAttestation bool, vmpl uint) attestation.Provider {
|
||||
return &provider{
|
||||
pubKey: pubKey,
|
||||
teeAttestaion: teeAttestation,
|
||||
vmpl: vmpl,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,26 +135,61 @@ func (v provider) VTpmAttestation(vTpmNonce []byte) ([]byte, error) {
|
||||
return proto.Marshal(quote)
|
||||
}
|
||||
|
||||
func (v provider) VerifTeeAttestation(report []byte, teeNonce []byte) error {
|
||||
func (v provider) AzureAttestationToken(tokenNonce []byte) ([]byte, error) {
|
||||
return nil, errors.New("Azure attestation token is not supported")
|
||||
}
|
||||
|
||||
type verifier struct {
|
||||
pubKey []byte
|
||||
writer io.Writer
|
||||
Policy *attestation.Config
|
||||
}
|
||||
|
||||
func NewVerifier(pubKey []byte, writer io.Writer) attestation.Verifier {
|
||||
policy := &attestation.Config{
|
||||
Config: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}},
|
||||
PcrConfig: &attestation.PcrConfig{},
|
||||
}
|
||||
|
||||
return &verifier{
|
||||
pubKey: pubKey,
|
||||
writer: writer,
|
||||
Policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVerifierWithPolicy(pubKey []byte, writer io.Writer, policy *attestation.Config) attestation.Verifier {
|
||||
if policy == nil {
|
||||
return NewVerifier(pubKey, writer)
|
||||
}
|
||||
|
||||
return &verifier{
|
||||
pubKey: pubKey,
|
||||
writer: writer,
|
||||
Policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
func (v verifier) VerifTeeAttestation(report []byte, teeNonce []byte) error {
|
||||
attestReport, err := abi.ReportToProto(report)
|
||||
if err != nil {
|
||||
return errors.Wrap(fmt.Errorf("failed to convert TEE report to proto"), err)
|
||||
}
|
||||
|
||||
attestationReport := sevsnp.Attestation{Report: attestReport, CertificateChain: nil}
|
||||
return quoteprovider.VerifyAttestationReportTLS(&attestationReport, teeNonce)
|
||||
return quoteprovider.VerifyAttestationReportTLS(&attestationReport, teeNonce, v.Policy)
|
||||
}
|
||||
|
||||
func (v provider) VerifVTpmAttestation(report []byte, vTpmNonce []byte) error {
|
||||
return VerifyQuote(report, v.pubKey, vTpmNonce, v.writer)
|
||||
func (v verifier) VerifVTpmAttestation(report []byte, vTpmNonce []byte) error {
|
||||
return VerifyQuote(report, v.pubKey, vTpmNonce, v.writer, v.Policy)
|
||||
}
|
||||
|
||||
func (v provider) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error {
|
||||
return VTPMVerify(report, v.pubKey, teeNonce, vTpmNonce, v.writer)
|
||||
func (v verifier) VerifyAttestation(report []byte, teeNonce []byte, vTpmNonce []byte) error {
|
||||
return VTPMVerify(report, v.pubKey, teeNonce, vTpmNonce, v.writer, v.Policy)
|
||||
}
|
||||
|
||||
func (v provider) AzureAttestationToken(tokenNonce []byte) ([]byte, error) {
|
||||
return nil, errors.New("Azure attestation token is not supported")
|
||||
func (v verifier) JSONToPolicy(path string) error {
|
||||
return ReadPolicy(path, v.Policy)
|
||||
}
|
||||
|
||||
func Attest(teeNonce []byte, vTPMNonce []byte, teeAttestaion bool, vmpl uint) ([]byte, error) {
|
||||
@@ -164,8 +208,8 @@ func Attest(teeNonce []byte, vTPMNonce []byte, teeAttestaion bool, vmpl uint) ([
|
||||
return marshalQuote(attestation)
|
||||
}
|
||||
|
||||
func VTPMVerify(quote []byte, pubKeyTLS []byte, teeNonce []byte, vtpmNonce []byte, writer io.Writer) error {
|
||||
if err := VerifyQuote(quote, pubKeyTLS, vtpmNonce, writer); err != nil {
|
||||
func VTPMVerify(quote []byte, pubKeyTLS []byte, teeNonce []byte, vtpmNonce []byte, writer io.Writer, policy *attestation.Config) error {
|
||||
if err := VerifyQuote(quote, pubKeyTLS, vtpmNonce, writer, policy); err != nil {
|
||||
return fmt.Errorf("failed to verify vTPM quote: %v", err)
|
||||
}
|
||||
|
||||
@@ -176,14 +220,14 @@ func VTPMVerify(quote []byte, pubKeyTLS []byte, teeNonce []byte, vtpmNonce []byt
|
||||
return errors.Wrap(fmt.Errorf("failed to unmarshal quote"), err)
|
||||
}
|
||||
|
||||
if err := quoteprovider.VerifyAttestationReportTLS(attestation.GetSevSnpAttestation(), teeNonce); err != nil {
|
||||
if err := quoteprovider.VerifyAttestationReportTLS(attestation.GetSevSnpAttestation(), teeNonce, policy); err != nil {
|
||||
return fmt.Errorf("failed to verify TEE attestation report: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyQuote(quote []byte, pubKeyTLS []byte, vtpmNonce []byte, writer io.Writer) error {
|
||||
func VerifyQuote(quote []byte, pubKeyTLS []byte, vtpmNonce []byte, writer io.Writer, policy *attestation.Config) error {
|
||||
attestation := &attest.Attestation{}
|
||||
|
||||
err := proto.Unmarshal(quote, attestation)
|
||||
@@ -209,7 +253,7 @@ func VerifyQuote(quote []byte, pubKeyTLS []byte, vtpmNonce []byte, writer io.Wri
|
||||
|
||||
s256, s384 := calculatePCRTLSKey(pubKeyTLS)
|
||||
|
||||
if err := checkExpectedPCRValues(attestation, s256, s384); err != nil {
|
||||
if err := checkExpectedPCRValues(attestation, s256, s384, policy); err != nil {
|
||||
return fmt.Errorf("PCR values do not match expected PCR values: %w", err)
|
||||
}
|
||||
|
||||
@@ -286,7 +330,7 @@ func addTEEAttestation(attestation *attest.Attestation, nonce []byte, vmpl uint)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkExpectedPCRValues(attQuote *attest.Attestation, ePcr256, ePcr384 []byte) error {
|
||||
func checkExpectedPCRValues(attQuote *attest.Attestation, ePcr256, ePcr384 []byte, policy *attestation.Config) error {
|
||||
quotes := attQuote.GetQuotes()
|
||||
for i := range quotes {
|
||||
quote := quotes[i]
|
||||
@@ -294,21 +338,21 @@ func checkExpectedPCRValues(attQuote *attest.Attestation, ePcr256, ePcr384 []byt
|
||||
var pcr15 []byte
|
||||
switch quote.Pcrs.Hash {
|
||||
case ptpm.HashAlgo_SHA256:
|
||||
pcrMap = attestation.AttestationPolicy.PcrConfig.PCRValues.Sha256
|
||||
pcrMap = policy.PcrConfig.PCRValues.Sha256
|
||||
if ePcr256 == nil {
|
||||
pcr15 = make([]byte, 32)
|
||||
} else {
|
||||
pcr15 = ePcr256
|
||||
}
|
||||
case ptpm.HashAlgo_SHA384:
|
||||
pcrMap = attestation.AttestationPolicy.PcrConfig.PCRValues.Sha384
|
||||
pcrMap = policy.PcrConfig.PCRValues.Sha384
|
||||
if ePcr384 == nil {
|
||||
pcr15 = make([]byte, 48)
|
||||
} else {
|
||||
pcr15 = ePcr384
|
||||
}
|
||||
case ptpm.HashAlgo_SHA1:
|
||||
pcrMap = attestation.AttestationPolicy.PcrConfig.PCRValues.Sha1
|
||||
pcrMap = policy.PcrConfig.PCRValues.Sha1
|
||||
pcr15 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
default:
|
||||
return errors.Wrap(ErrNoHashAlgo, fmt.Errorf("algo: %s", ptpm.HashAlgo_name[int32(quote.Pcrs.Hash)]))
|
||||
@@ -383,3 +427,58 @@ func GetPCRSHA256Value(index int) ([]byte, error) {
|
||||
func GetPCRSHA384Value(index int) ([]byte, error) {
|
||||
return getPCRValue(index, tpm2.AlgSHA384)
|
||||
}
|
||||
|
||||
func ReadPolicy(policyPath string, attestationConfiguration *attestation.Config) error {
|
||||
if policyPath != "" {
|
||||
policyData, err := os.ReadFile(policyPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(ErrAttestationPolicyOpen, err)
|
||||
}
|
||||
|
||||
return ReadPolicyFromByte(policyData, attestationConfiguration)
|
||||
}
|
||||
|
||||
return ErrAttestationPolicyMissing
|
||||
}
|
||||
|
||||
func ReadPolicyFromByte(policyData []byte, attestationConfiguration *attestation.Config) error {
|
||||
unmarshalOptions := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
|
||||
|
||||
if err := unmarshalOptions.Unmarshal(policyData, attestationConfiguration.Config); err != nil {
|
||||
return errors.Wrap(ErrAttestationPolicyDecode, err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(policyData, attestationConfiguration.PcrConfig); err != nil {
|
||||
return errors.Wrap(ErrAttestationPolicyDecode, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConvertPolicyToJSON(attestationConfiguration *attestation.Config) ([]byte, error) {
|
||||
pbJson, err := protojson.Marshal(attestationConfiguration.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrProtoMarshalFailed, err)
|
||||
}
|
||||
|
||||
var pbMap map[string]interface{}
|
||||
if err := json.Unmarshal(pbJson, &pbMap); err != nil {
|
||||
return nil, errors.Wrap(ErrJsonUnarshalFailed, err)
|
||||
}
|
||||
|
||||
pcrJson, err := json.Marshal(attestationConfiguration.PcrConfig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ErrJsonMarshalFailed, err)
|
||||
}
|
||||
|
||||
var pcrMap map[string]interface{}
|
||||
if err := json.Unmarshal(pcrJson, &pcrMap); err != nil {
|
||||
return nil, errors.Wrap(ErrJsonUnarshalFailed, err)
|
||||
}
|
||||
|
||||
for k, v := range pcrMap {
|
||||
pbMap[k] = v
|
||||
}
|
||||
|
||||
return json.MarshalIndent(pbMap, "", " ")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package vtpm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/google/go-sev-guest/proto/sevsnp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
const sevProductNameMilan = "Milan"
|
||||
|
||||
var policy = attestation.Config{Config: &check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}, PcrConfig: &attestation.PcrConfig{}}
|
||||
|
||||
func TestVerifyAttestationReportMalformedSignature(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "policy")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
err = setAttestationPolicy(attestationPB, tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Change random data so in the signature so the signature failes
|
||||
attestationPB.Report.Signature[0] = attestationPB.Report.Signature[0] ^ 0x01
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, distorted signature",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
err: quoteprovider.ErrAttVerification,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := quoteprovider.VerifyAttestationReportTLS(tt.attestationReport, tt.reportData, &policy)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportUnknownProduct(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "policy")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
err = setAttestationPolicy(attestationPB, tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = changeProductAttestationPolicy()
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, unknown product",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
err: quoteprovider.ErrProductLine,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := quoteprovider.VerifyAttestationReportTLS(tt.attestationReport, tt.reportData, &policy)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportSuccess(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "policy")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
err = setAttestationPolicy(attestationPB, tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
goodProduct int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, validation and verification is performed succsessfully",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
goodProduct: 1,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := quoteprovider.VerifyAttestationReportTLS(tt.attestationReport, tt.reportData, &policy)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAttestationReportMalformedPolicy(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "policy")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
attestationPB, reportData := prepVerifyAttReport(t)
|
||||
err = setAttestationPolicy(attestationPB, tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Change random data in the measurement so the measurement does not match
|
||||
attestationPB.Report.Measurement[0] = attestationPB.Report.Measurement[0] ^ 0x01
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
attestationReport *sevsnp.Attestation
|
||||
reportData []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid attestation, malformed policy (measurement)",
|
||||
attestationReport: attestationPB,
|
||||
reportData: reportData,
|
||||
err: quoteprovider.ErrAttVerification,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := quoteprovider.VerifyAttestationReportTLS(tt.attestationReport, tt.reportData, &policy)
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepVerifyAttReport(t *testing.T) (*sevsnp.Attestation, []byte) {
|
||||
file, err := os.ReadFile("../../../attestation.bin")
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(file) < abi.ReportSize {
|
||||
file = append(file, make([]byte, abi.ReportSize-len(file))...)
|
||||
}
|
||||
|
||||
rr, err := abi.ReportCertsToProto(file)
|
||||
require.NoError(t, err)
|
||||
|
||||
return rr, rr.Report.ReportData
|
||||
}
|
||||
|
||||
func setAttestationPolicy(rr *sevsnp.Attestation, policyDirectory string) error {
|
||||
attestationPolicyFile, err := os.ReadFile("../../../scripts/attestation_policy/attestation_policy.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unmarshalOptions := protojson.UnmarshalOptions{DiscardUnknown: true}
|
||||
|
||||
err = unmarshalOptions.Unmarshal(attestationPolicyFile, policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policy.Config.Policy.Product = &sevsnp.SevProduct{Name: sevsnp.SevProduct_SEV_PRODUCT_MILAN}
|
||||
policy.Config.Policy.FamilyId = rr.Report.FamilyId
|
||||
policy.Config.Policy.ImageId = rr.Report.ImageId
|
||||
policy.Config.Policy.Measurement = rr.Report.Measurement
|
||||
policy.Config.Policy.HostData = rr.Report.HostData
|
||||
policy.Config.Policy.ReportIdMa = rr.Report.ReportIdMa
|
||||
policy.Config.RootOfTrust.ProductLine = sevProductNameMilan
|
||||
|
||||
policyByte, err := ConvertPolicyToJSON(&policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policyPath := filepath.Join(policyDirectory, "attestation_policy.json")
|
||||
|
||||
err = os.WriteFile(policyPath, policyByte, 0o644)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
attestation.AttestationPolicyPath = policyPath
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeProductAttestationPolicy() error {
|
||||
err := ReadPolicy(attestation.AttestationPolicyPath, &policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policy.Config.RootOfTrust.ProductLine = ""
|
||||
policy.Config.Policy.Product = nil
|
||||
|
||||
policyByte, err := ConvertPolicyToJSON(&policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(attestation.AttestationPolicyPath, policyByte, 0o644); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/ultravioletrs/cocos/agent"
|
||||
agentgrpc "github.com/ultravioletrs/cocos/agent/api/grpc"
|
||||
"github.com/ultravioletrs/cocos/agent/mocks"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
pkggrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/health"
|
||||
@@ -113,7 +112,7 @@ func TestAgentClientIntegration(t *testing.T) {
|
||||
},
|
||||
AttestedTLS: true,
|
||||
},
|
||||
err: attestation.ErrAttestationPolicyMissing,
|
||||
err: errors.New("failed to stat attestation policy file"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,18 @@ import (
|
||||
|
||||
func setupATLS(cfg AgentClientConfig) (credentials.TransportCredentials, security, error) {
|
||||
security := withaTLS
|
||||
err := attestation.ReadAttestationPolicy(cfg.AttestationPolicy, &attestation.AttestationPolicy)
|
||||
|
||||
info, err := os.Stat(cfg.AttestationPolicy)
|
||||
if err != nil {
|
||||
return nil, withoutTLS, errors.Wrap(fmt.Errorf("failed to read Attestation Policy"), err)
|
||||
return nil, withoutTLS, errors.Wrap(fmt.Errorf("failed to stat attestation policy file"), err)
|
||||
}
|
||||
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil, withoutTLS, fmt.Errorf("attestation policy file is not a regular file: %s", cfg.AttestationPolicy)
|
||||
}
|
||||
|
||||
attestation.AttestationPolicyPath = cfg.AttestationPolicy
|
||||
|
||||
var insecureSkipVerify bool = true
|
||||
var rootCAs *x509.CertPool = nil
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
)
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
@@ -108,7 +109,7 @@ func TestNewClient(t *testing.T) {
|
||||
AttestationPolicy: "no such file",
|
||||
},
|
||||
wantErr: true,
|
||||
err: fmt.Errorf("failed to read Attestation Policy"),
|
||||
err: fmt.Errorf("failed to stat attestation policy file"),
|
||||
},
|
||||
{
|
||||
name: "Fail with invalid ServerCAFile",
|
||||
@@ -226,25 +227,25 @@ func TestReadAttestationPolicy(t *testing.T) {
|
||||
name: "Invalid JSON",
|
||||
manifestPath: "invalid_manifest.json",
|
||||
fileContent: invalidJSON,
|
||||
err: attestation.ErrAttestationPolicyDecode,
|
||||
err: vtpm.ErrAttestationPolicyDecode,
|
||||
},
|
||||
{
|
||||
name: "Non-existent file",
|
||||
manifestPath: "nonexistent.json",
|
||||
fileContent: "",
|
||||
err: attestation.ErrAttestationPolicyOpen,
|
||||
err: vtpm.ErrAttestationPolicyOpen,
|
||||
},
|
||||
{
|
||||
name: "Empty manifest path",
|
||||
manifestPath: "",
|
||||
fileContent: "",
|
||||
err: attestation.ErrAttestationPolicyMissing,
|
||||
err: vtpm.ErrAttestationPolicyMissing,
|
||||
},
|
||||
{
|
||||
name: "Invalid JSON PCR",
|
||||
manifestPath: "invalid_manifest.json",
|
||||
fileContent: invalidJSONPCR,
|
||||
err: attestation.ErrAttestationPolicyDecode,
|
||||
err: vtpm.ErrAttestationPolicyDecode,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -257,7 +258,7 @@ func TestReadAttestationPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
config := attestation.Config{Config: &check.Config{}, PcrConfig: &attestation.PcrConfig{}}
|
||||
err := attestation.ReadAttestationPolicy(tt.manifestPath, &config)
|
||||
err := vtpm.ReadPolicy(tt.manifestPath, &config)
|
||||
|
||||
assert.True(t, errors.Contains(err, tt.err), fmt.Sprintf("expected error %v, got %v", tt.err, err))
|
||||
if tt.err == nil {
|
||||
|
||||
Reference in New Issue
Block a user