Files
cocos/pkg/attestation/quoteprovider/sev.go
T
Sammy Kerata Oina 8eb1fac9ad NOISSUE - Refactor and update dependencies in the project (#491)
* Refactor and update dependencies in the project

- Updated go.sum to replace `github.com/absmach/magistrala` with `github.com/absmach/supermq` across various modules.
- Removed VSock configuration from environment variables and QEMU arguments.
- Updated QEMU configuration and related tests to remove references to guest CID and VSock.
- Added new HTTP transport layer for API endpoints in the manager.
- Introduced Prometheus monitoring configuration with alert rules and Alertmanager setup.
- Updated service and VM interfaces to remove unused methods and references.
- Refactored tests to align with the new structure and dependencies.

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* Add MaxVMs configuration and enforce limit on VM creation

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* Add comprehensive tests for HTTP transport handlers and endpoints

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* Add test case for exceeding maximum number of VMs in TestRun

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* Improve error handling in TestHandlerWithCustomRouter to ensure response writing is checked

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

* Update dependencies to latest versions

- Upgrade cel.dev/expr from v0.23.0 to v0.24.0
- Upgrade github.com/absmach/supermq from v0.16.0 to v0.17.0
- Upgrade github.com/cenkalti/backoff from v4.3.0 to v5.0.2
- Upgrade github.com/cncf/xds/go to v0.0.0-20250501225837-2ac532fd4443
- Upgrade github.com/go-chi/chi/v5 from v5.2.1 to v5.2.2
- Upgrade github.com/go-jose/go-jose/v3 from v3.0.3 to v3.0.4
- Upgrade github.com/gofrs/uuid/v5 from v5.3.0 to v5.3.2
- Upgrade github.com/prometheus/client_golang from v1.22.0 to v1.23.0
- Upgrade github.com/prometheus/client_model from v0.6.1 to v0.6.2
- Upgrade github.com/prometheus/common from v0.62.0 to v0.65.0
- Upgrade github.com/prometheus/procfs from v0.15.1 to v0.16.1
- Upgrade go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp from v0.60.0 to v0.62.0
- Upgrade go.opentelemetry.io/otel/exporters/otlp/otlptrace from v1.36.0 to v1.37.0
- Upgrade golang.org/x/crypto from v0.39.0 to v0.40.0
- Upgrade golang.org/x/sys from v0.33.0 to v0.34.0
- Upgrade golang.org/x/text from v0.26.0 to v0.27.0
- Upgrade golang.org/x/time from v0.11.0 to v0.12.0
- Upgrade google.golang.org/grpc from v1.73.0 to v1.74.2

Signed-off-by: Sammy Oina <sammyoina@gmail.com>

---------

Signed-off-by: Sammy Oina <sammyoina@gmail.com>
2025-08-05 11:22:02 +02:00

187 lines
4.9 KiB
Go

// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
//go:build !embed
// +build !embed
package quoteprovider
import (
"fmt"
"io"
"os"
"path"
"time"
"github.com/absmach/supermq/pkg/errors"
"github.com/google/go-sev-guest/client"
"github.com/google/go-sev-guest/proto/check"
"github.com/google/go-sev-guest/proto/sevsnp"
"github.com/google/go-sev-guest/validate"
"github.com/google/go-sev-guest/verify"
"github.com/google/go-sev-guest/verify/trust"
"github.com/google/logger"
"github.com/ultravioletrs/cocos/pkg/attestation"
)
const (
cocosDirectory = ".cocos"
caBundleName = "ask_ark.pem"
Nonce = 64
sevSnpProductMilan = "Milan"
sevSnpProductGenoa = "Genoa"
)
var (
timeout = time.Minute * 2
maxTryDelay = time.Second * 30
)
var (
ErrProductLine = errors.New(fmt.Sprintf("product name must be %s or %s", sevSnpProductMilan, sevSnpProductGenoa))
ErrAttVerification = errors.New("attestation verification failed")
errAttValidation = errors.New("attestation validation failed")
)
func fillInAttestationLocal(attestation *sevsnp.Attestation, cfg *check.Config) error {
product := cfg.RootOfTrust.ProductLine
chain := attestation.GetCertificateChain()
if chain == nil {
chain = &sevsnp.CertificateChain{}
attestation.CertificateChain = chain
}
if len(chain.GetAskCert()) == 0 || len(chain.GetArkCert()) == 0 {
homePath, err := os.UserHomeDir()
if err != nil {
return err
}
bundlePath := path.Join(homePath, cocosDirectory, product, caBundleName)
if _, err := os.Stat(bundlePath); err == nil {
amdRootCerts := trust.AMDRootCerts{}
if err := amdRootCerts.FromKDSCert(bundlePath); err != nil {
return err
}
chain.ArkCert = amdRootCerts.ProductCerts.Ark.Raw
chain.AskCert = amdRootCerts.ProductCerts.Ask.Raw
}
}
return 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))
}
if cfg.Policy.Product == nil {
productName := GetProductName(cfg.RootOfTrust.ProductLine)
if productName == sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN {
return ErrProductLine
}
sopts.Product = &sevsnp.SevProduct{
Name: productName,
}
} else {
sopts.Product = cfg.Policy.Product
}
sopts.Getter = &trust.RetryHTTPSGetter{
Timeout: timeout,
MaxRetryDelay: maxTryDelay,
Getter: &trust.SimpleHTTPSGetter{},
}
if err := fillInAttestationLocal(attestationPB, cfg); err != nil {
return fmt.Errorf("failed to fill the attestation with local ARK and ASK certificates %v", err)
}
if err := verify.SnpAttestation(attestationPB, sopts); err != nil {
return errors.Wrap(ErrAttVerification, err)
}
return nil
}
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))
}
if err = validate.SnpAttestation(attestationPB, opts); err != nil {
return errors.Wrap(errAttValidation, err)
}
return nil
}
func GetLeveledQuoteProvider() (client.LeveledQuoteProvider, error) {
return client.GetLeveledQuoteProvider()
}
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.
attestationPB.CertificateChain = nil
if len(reportData) != 0 {
config.Policy.ReportData = reportData[:]
}
return VerifyAndValidate(attestationPB, config)
}
func VerifyAndValidate(attestationPB *sevsnp.Attestation, cfg *check.Config) error {
logger.Init("", false, false, io.Discard)
if err := verifyReport(attestationPB, cfg); err != nil {
return err
}
if err := validateReport(attestationPB, cfg); err != nil {
return err
}
return nil
}
func FetchAttestation(reportDataSlice []byte, vmpl uint) ([]byte, error) {
var reportData [Nonce]byte
qp, err := GetLeveledQuoteProvider()
if err != nil {
return []byte{}, fmt.Errorf("could not get quote provider")
}
if len(reportData) > Nonce {
return []byte{}, fmt.Errorf("attestation report size mismatch")
}
copy(reportData[:], reportDataSlice)
rawQuote, err := qp.GetRawQuoteAtLevel(reportData, vmpl)
if err != nil {
return []byte{}, fmt.Errorf("failed to get raw quote")
}
return rawQuote, nil
}
func GetProductName(product string) sevsnp.SevProduct_SevProductName {
switch product {
case sevSnpProductMilan:
return sevsnp.SevProduct_SEV_PRODUCT_MILAN
case sevSnpProductGenoa:
return sevsnp.SevProduct_SEV_PRODUCT_GENOA
default:
return sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN
}
}