mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-22 20:00:18 +00:00
COCOS-492: Cache VCEK on aTLS verification (#524)
* initial commit * made changes based on errors * remove unnecessary log --------- Co-authored-by: Jovan Djukic <jovan.djukic@ultraviolet.com>
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ultravioletrs/cocos/pkg/atls"
|
||||
"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"
|
||||
"github.com/ultravioletrs/cocos/pkg/clients"
|
||||
@@ -155,6 +156,13 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
err = quoteprovider.FetchCertificates(uint(cfg.Vmpl))
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to fetch certificates: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
svc := newService(ctx, logger, eventSvc, provider, cfg.Vmpl)
|
||||
|
||||
if err := os.MkdirAll(storageDir, 0o755); err != nil {
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
package quoteprovider
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
@@ -22,11 +26,13 @@ import (
|
||||
"github.com/google/go-sev-guest/verify/trust"
|
||||
"github.com/google/logger"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
const (
|
||||
cocosDirectory = ".cocos"
|
||||
caBundleName = "ask_ark.pem"
|
||||
cocosDirectory = "/cocos"
|
||||
arkAskBundleName = "ask_ark.pem"
|
||||
vcekName = "vcek.pem"
|
||||
Nonce = 64
|
||||
sevSnpProductMilan = "Milan"
|
||||
sevSnpProductGenoa = "Genoa"
|
||||
@@ -57,7 +63,7 @@ func fillInAttestationLocal(attestation *sevsnp.Attestation, cfg *check.Config)
|
||||
return err
|
||||
}
|
||||
|
||||
bundlePath := path.Join(homePath, cocosDirectory, product, caBundleName)
|
||||
bundlePath := path.Join(homePath, cocosDirectory, product, arkAskBundleName)
|
||||
if _, err := os.Stat(bundlePath); err == nil {
|
||||
amdRootCerts := trust.AMDRootCerts{}
|
||||
if err := amdRootCerts.FromKDSCert(bundlePath); err != nil {
|
||||
@@ -166,12 +172,39 @@ func FetchAttestation(reportDataSlice []byte, vmpl uint) ([]byte, error) {
|
||||
}
|
||||
copy(reportData[:], reportDataSlice)
|
||||
|
||||
rawQuote, err := qp.GetRawQuoteAtLevel(reportData, vmpl)
|
||||
quoteProto, err := client.GetQuoteProtoAtLevel(qp, reportData, vmpl)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("failed to get raw quote")
|
||||
return []byte{}, fmt.Errorf("failed to get quote proto")
|
||||
}
|
||||
|
||||
return rawQuote, nil
|
||||
homePath, _ := os.UserHomeDir()
|
||||
vcekPath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), vcekName)
|
||||
arkAskBundlePath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), arkAskBundleName)
|
||||
|
||||
vcekBytes, err := os.ReadFile(vcekPath)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("could not read VCEK file: %v", err)
|
||||
}
|
||||
|
||||
arkAskBundleBytes, err := os.ReadFile(arkAskBundlePath)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("could not read ask/ark bundle file: %v", err)
|
||||
}
|
||||
|
||||
vcekPem, _ := pem.Decode(vcekBytes)
|
||||
arkPem, rest := pem.Decode(arkAskBundleBytes)
|
||||
askPem, _ := pem.Decode(rest)
|
||||
|
||||
quoteProto.CertificateChain.VcekCert = vcekPem.Bytes
|
||||
quoteProto.CertificateChain.AskCert = askPem.Bytes
|
||||
quoteProto.CertificateChain.ArkCert = arkPem.Bytes
|
||||
|
||||
result, err := protojson.Marshal(quoteProto)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("failed to marshal quote proto: %v", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetProductName(product string) sevsnp.SevProduct_SevProductName {
|
||||
@@ -184,3 +217,83 @@ func GetProductName(product string) sevsnp.SevProduct_SevProductName {
|
||||
return sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
func derToPem(der []byte) []byte {
|
||||
// Try to parse to make sure it's a certificate
|
||||
if _, err := x509.ParseCertificate(der); err != nil {
|
||||
// cert_chain endpoint already returns PEM; just pass through
|
||||
return der
|
||||
}
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
|
||||
}
|
||||
|
||||
func FetchCertificates(vmpl uint) error {
|
||||
var reportData [Nonce]byte
|
||||
|
||||
qp, err := GetLeveledQuoteProvider()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get quote provider")
|
||||
}
|
||||
|
||||
if len(reportData) > Nonce {
|
||||
return fmt.Errorf("attestation report size mismatch")
|
||||
}
|
||||
|
||||
_, err = rand.Read(reportData[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read random data: %v", err)
|
||||
}
|
||||
|
||||
quoteProto, err := client.GetQuoteProtoAtLevel(qp, reportData, vmpl) // for coverage
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get quote proto")
|
||||
}
|
||||
|
||||
options := &verify.Options{
|
||||
CheckRevocations: true,
|
||||
DisableCertFetching: false,
|
||||
Getter: trust.DefaultHTTPSGetter(),
|
||||
Now: time.Now(),
|
||||
TrustedRoots: nil,
|
||||
Product: quoteProto.Product,
|
||||
}
|
||||
|
||||
result, err := verify.GetAttestationFromReport(quoteProto.Report, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get fetch certificates: %v", err)
|
||||
}
|
||||
|
||||
homePath, _ := os.UserHomeDir()
|
||||
|
||||
vcekPath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), vcekName)
|
||||
arkAskBundlePath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), arkAskBundleName)
|
||||
|
||||
vcekPem := derToPem(result.CertificateChain.VcekCert)
|
||||
askPem := derToPem(result.CertificateChain.AskCert)
|
||||
arkPem := derToPem(result.CertificateChain.ArkCert)
|
||||
|
||||
arkAskBundlePem := append(askPem, arkPem...)
|
||||
|
||||
vcekDir := filepath.Dir(vcekPath)
|
||||
err = os.MkdirAll(vcekDir, 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create VCEK directory: %v", err)
|
||||
}
|
||||
askArkBundleDir := filepath.Dir(arkAskBundlePath)
|
||||
err = os.MkdirAll(askArkBundleDir, 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create ask/ark bundle directory: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(vcekPath, vcekPem, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write VCEK file: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(arkAskBundlePath, arkAskBundlePem, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write ark/ask bundle file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestFillInAttestationLocal(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
bundleContent := []byte("mock ASK ARK bundle")
|
||||
bundlePath := path.Join(cocosDir, caBundleName)
|
||||
bundlePath := path.Join(cocosDir, arkAskBundleName)
|
||||
err = os.WriteFile(bundlePath, bundleContent, 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user