COCOS-192 - Add support for attested TLS (#279)

* add draft tls extension

* add client support for ipv6

* remove vscode

* add evidence request server payload

* clean up the code

* add fetch and verify for quote provider

* add build parameters for buildroot

* change Makefile to always enable CGO

* fix ci

* add malloc check for NULL

* add copyright

* renamed files and fix cgo lint

* fix cache test

* fix server tests

* remove ineffective assignment

* fix no-TLS connection

* add check for SSL_set_fd failure

* add tests for verification of attestation

* fix CI

* fix failing tests

* fix backend tests

* remove commented code

* separate verify and validate function

* fix failing test

* Simplify function name

---------

Co-authored-by: ultraviolet <cocosai@ultraviolet.local.pragmatic-it.com>
This commit is contained in:
Danko Miladinovic
2024-11-04 19:10:34 +01:00
committed by GitHub
parent 6f747190b9
commit e372cfc219
28 changed files with 2056 additions and 591 deletions
+3 -56
View File
@@ -17,12 +17,10 @@ import (
"github.com/google/go-sev-guest/proto/check"
"github.com/google/go-sev-guest/proto/sevsnp"
"github.com/google/go-sev-guest/tools/lib/report"
"github.com/google/go-sev-guest/validate"
"github.com/google/go-sev-guest/verify"
"github.com/google/go-sev-guest/verify/trust"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/wrapperspb"
)
@@ -269,7 +267,7 @@ func (cli *CLI) NewValidateAttestationValidationCmd() *cobra.Command {
return
}
if err := verifyAndValidateAttestation(attestation); err != nil {
if err := quoteprovider.VerifyAndValidate(attestation, &cfg); err != nil {
printError(cmd, "Attestation validation and verification failed with error: %v ❌ ", err)
return
}
@@ -464,57 +462,6 @@ func (cli *CLI) NewValidateAttestationValidationCmd() *cobra.Command {
return cmd
}
func verifyAndValidateAttestation(attestation []byte) error {
sopts, err := verify.RootOfTrustToOptions(cfg.RootOfTrust)
if err != nil {
return err
}
if cfg.Policy.Product == nil {
productName := sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN
switch cfg.RootOfTrust.ProductLine {
case sevProductNameMilan:
productName = sevsnp.SevProduct_SEV_PRODUCT_MILAN
case sevProductNameGenoa:
productName = sevsnp.SevProduct_SEV_PRODUCT_GENOA
default:
}
if productName == sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN {
return fmt.Errorf("product name must be %s or %s", sevProductNameMilan, sevProductNameGenoa)
}
sopts.Product = &sevsnp.SevProduct{
Name: productName,
}
} else {
sopts.Product = cfg.Policy.Product
}
sopts.Getter = &trust.RetryHTTPSGetter{
Timeout: timeout,
MaxRetryDelay: maxRetryDelay,
Getter: &trust.SimpleHTTPSGetter{},
}
// Only take the attestation report and ignore everything else.
attestationPB, err := abi.ReportCertsToProto(attestation[:abi.ReportSize])
if err != nil {
return err
}
if err = verify.SnpAttestation(attestationPB, sopts); err != nil {
return err
}
opts, err := validate.PolicyToOptions(cfg.Policy)
if err != nil {
return err
}
if err = validate.SnpAttestation(attestationPB, opts); err != nil {
return err
}
return nil
}
// parseConfig decodes config passed as json for check.Config struct.
// example
/* {
@@ -681,7 +628,7 @@ func getBase(val string) int {
}
func validateInput() error {
if len(cfg.RootOfTrust.CabundlePaths) != 0 || len(cfg.RootOfTrust.Cabundles) != 0 && cfg.RootOfTrust.Product == "" {
if len(cfg.RootOfTrust.CabundlePaths) != 0 || len(cfg.RootOfTrust.Cabundles) != 0 && cfg.RootOfTrust.ProductLine == "" {
return fmt.Errorf("product name must be set if CA bundles are provided")
}
+6 -11
View File
@@ -4,7 +4,6 @@ package cli
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
@@ -12,6 +11,7 @@ import (
"github.com/google/go-sev-guest/proto/check"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"google.golang.org/protobuf/encoding/protojson"
)
type fieldType int
@@ -39,11 +39,6 @@ var (
errBackendField = errors.New("the specified field type does not exist in the backend information")
)
type AttestationConfiguration struct {
SNPPolicy *check.Policy `json:"snp_policy,omitempty"`
RootOfTrust *check.RootOfTrust `json:"root_of_trust,omitempty"`
}
func (cli *CLI) NewBackendCmd() *cobra.Command {
return &cobra.Command{
Use: "backend [command]",
@@ -114,27 +109,27 @@ func changeAttestationConfiguration(fileName, base64Data string, expectedLength
return errDataLength
}
ac := AttestationConfiguration{}
ac := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
backendInfo, err := os.ReadFile(fileName)
if err != nil {
return errors.Wrap(errReadingBackendInfoFile, err)
}
if err = json.Unmarshal(backendInfo, &ac); err != nil {
if err = protojson.Unmarshal(backendInfo, &ac); err != nil {
return errors.Wrap(errUnmarshalJSON, err)
}
switch field {
case measurementField:
ac.SNPPolicy.Measurement = data
ac.Policy.Measurement = data
case hostDataField:
ac.SNPPolicy.HostData = data
ac.Policy.HostData = data
default:
return errBackendField
}
fileJson, err := json.MarshalIndent(ac, "", " ")
fileJson, err := protojson.Marshal(&ac)
if err != nil {
return errors.Wrap(errMarshalJSON, err)
}
+7 -12
View File
@@ -4,13 +4,13 @@ package cli
import (
"encoding/base64"
"encoding/json"
"os"
"testing"
"github.com/google/go-sev-guest/proto/check"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
)
func TestChangeAttestationConfiguration(t *testing.T) {
@@ -18,14 +18,9 @@ func TestChangeAttestationConfiguration(t *testing.T) {
require.NoError(t, err)
defer os.Remove(tmpfile.Name())
initialConfig := AttestationConfiguration{
SNPPolicy: &check.Policy{
Measurement: make([]byte, measurementLength),
HostData: make([]byte, hostDataLength),
},
}
initialConfig := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
initialJSON, err := json.Marshal(initialConfig)
initialJSON, err := protojson.Marshal(&initialConfig)
require.NoError(t, err)
err = os.WriteFile(tmpfile.Name(), initialJSON, 0o644)
require.NoError(t, err)
@@ -91,15 +86,15 @@ func TestChangeAttestationConfiguration(t *testing.T) {
content, err := os.ReadFile(tmpfile.Name())
require.NoError(t, err)
var config AttestationConfiguration
err = json.Unmarshal(content, &config)
config := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
err = protojson.Unmarshal(content, &config)
require.NoError(t, err)
decodedData, _ := base64.StdEncoding.DecodeString(tt.base64Data)
if tt.field == measurementField {
assert.Equal(t, decodedData, config.SNPPolicy.Measurement)
assert.Equal(t, decodedData, config.Policy.Measurement)
} else if tt.field == hostDataField {
assert.Equal(t, decodedData, config.SNPPolicy.HostData)
assert.Equal(t, decodedData, config.Policy.HostData)
}
}
})
+5 -4
View File
@@ -9,6 +9,7 @@ import (
"github.com/google/go-sev-guest/abi"
"github.com/google/go-sev-guest/kds"
"github.com/google/go-sev-guest/proto/check"
"github.com/google/go-sev-guest/verify/trust"
"github.com/spf13/cobra"
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
@@ -26,14 +27,14 @@ func (cli *CLI) NewCABundleCmd(fileSavePath string) *cobra.Command {
Example: "ca-bundle <path_to_platform_info_json>",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
attestationConfiguration := grpc.AttestationConfiguration{}
attestationConfiguration := check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
err := grpc.ReadBackendInfo(args[0], &attestationConfiguration)
if err != nil {
printError(cmd, "Error while reading manifest: %v ❌ ", err)
return
}
product := attestationConfiguration.RootOfTrust.Product
product := attestationConfiguration.RootOfTrust.ProductLine
getter := trust.DefaultHTTPSGetter()
caURL := kds.ProductCertChainURL(abi.VcekReportSigner, product)
@@ -54,8 +55,8 @@ func (cli *CLI) NewCABundleCmd(fileSavePath string) *cobra.Command {
return
}
bundleFilePath := path.Join(fileSavePath, product, caBundleName)
if err = saveToFile(bundleFilePath, bundle); err != nil {
bundlePath := path.Join(fileSavePath, product, caBundleName)
if err = saveToFile(bundlePath, bundle); err != nil {
printError(cmd, "Error while saving ARK-ASK to file: %v ❌ ", err)
return
}
+1 -1
View File
@@ -17,7 +17,7 @@ func TestNewCABundleCmd(t *testing.T) {
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
manifestContent := []byte(`{"root_of_trust": {"product": "Milan"}}`)
manifestContent := []byte(`{"root_of_trust": {"product_line": "Milan"}}`)
manifestPath := path.Join(tempDir, "manifest.json")
err = os.WriteFile(manifestPath, manifestContent, 0o644)
assert.NoError(t, err)