+
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CONFIG_DIR ?= /etc/cocos
SERVICE_NAME ?= cocos-manager
SERVICE_DIR ?= /etc/systemd/system
SERVICE_FILE = init/systemd/$(SERVICE_NAME).service
IGVM_BUILD_SCRIPT := ./scripts/igvmmeasure/igvm.sh

define compile_service
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) \
Expand All @@ -27,8 +28,9 @@ endef

all: $(SERVICES)

$(SERVICES):
$(SERVICES):
$(call compile_service,$@)
@if [ "$@" = "cli" ]; then $(MAKE) build-igvm; fi

$(ATTESTATION_POLICY):
$(MAKE) -C ./scripts/attestation_policy
Expand Down Expand Up @@ -61,3 +63,7 @@ stop:
install_service:
sudo install -m 644 $(SERVICE_FILE) $(SERVICE_DIR)/$(SERVICE_NAME).service
sudo systemctl daemon-reload

build-igvm:
@echo "Running build script for igvmmeasure..."
@$(IGVM_BUILD_SCRIPT)
19 changes: 19 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,22 @@ When defining the manifest dataset and algorithm checksums are required. This ca
```bash
./build/cocos-cli checksum <path_to_dataset_or_algorithm>
```

#### Measure IGVM file
We assume that our current working directory is the root of the cocos repository, both on the host machine and in the VM.

`igvmmeasure` calculates the launch measurement for an IGVM file and can generate a signed version. It ensures integrity by precomputing the expected launch digest, which can be verified against the attestation report. The tool parses IGVM directives, outputs the measurement as a hex string, or creates a signed file for verification at guest launch.

##### Example
We measure an IGVM file using our measure command, run:

```bash
./build/cocos-cli igvmmeasure /path/to/igvm/file
```

The tool will parse the directives in the IGVM file, calculate the launch measurement, and output the computed digest. If successful, it prints the measurement to standard output.

Here is a sample output
```
91c4929bec2d0ecf11a708e09f0a57d7d82208bcba2451564444a4b01c22d047995ca27f9053f86de4e8063e9f810548
```
22 changes: 22 additions & 0 deletions cli/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,28 @@
return cmd
}

func (cli *CLI) NewMeasureCmd(igvmBinaryPath string) *cobra.Command {
igvmmeasureCmd := &cobra.Command{
Use: "igvmmeasure <INPUT>",
Short: "Measure an IGVM file",
Long: `igvmmeasure measures an IGVM file and outputs the calculated measurement.
It ensures integrity verification for the IGVM file.`,

Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("error: No input file provided")
}

Check warning on line 638 in cli/attestation.go

View check run for this annotation

Codecov / codecov/patch

cli/attestation.go#L637-L638

Added lines #L637 - L638 were not covered by tests

inputFile := args[0]

return cli.measurement.Run(inputFile)
},
}

return igvmmeasureCmd
}

func sevsnpverify(cmd *cobra.Command, args []string) error {
cmd.Println("Checking attestation")

Expand Down
59 changes: 56 additions & 3 deletions cli/attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ func TestNewAttestationCmd(t *testing.T) {
var buf bytes.Buffer
cmd.SetOut(&buf)

cmd.SetOutput(&buf)

reportData := bytes.Repeat([]byte{0x01}, quoteprovider.Nonce)
mockSDK.On("Attestation", mock.Anything, [quoteprovider.Nonce]byte(reportData), mock.Anything).Return(nil)

Expand Down Expand Up @@ -159,7 +157,7 @@ func TestNewGetAttestationCmd(t *testing.T) {
}
cmd := cli.NewGetAttestationCmd()
var buf bytes.Buffer
cmd.SetOutput(&buf)
cmd.SetOut(&buf)

mockSDK.On("Attestation", mock.Anything, [quoteprovider.Nonce]byte(bytes.Repeat([]byte{0x00}, quoteprovider.Nonce)), [vtpm.Nonce]byte(bytes.Repeat([]byte{0x00}, vtpm.Nonce)), mock.Anything, mock.Anything).Return(tc.mockError).Run(func(args mock.Arguments) {
_, err := args.Get(4).(*os.File).Write(tc.mockResponse)
Expand Down Expand Up @@ -285,6 +283,61 @@ func TestNewValidateAttestationValidationCmd(t *testing.T) {
})
}

type MockMeasurement struct {
mock.Mock
}

func (m *MockMeasurement) Run(igvmBinaryPath string) error {
args := m.Called(igvmBinaryPath)
return args.Error(0)
}

func (m *MockMeasurement) Stop() error {
args := m.Called()
return args.Error(0)
}

func TestNewMeasureCmd_RunSuccess(t *testing.T) {
cliInstance := &CLI{}
mockMeasurement := new(MockMeasurement)
cliInstance.measurement = mockMeasurement

mockMeasurement.On("Run", "testfile.igvm").Return(nil)

cmd := cliInstance.NewMeasureCmd("fake_binary_path")
buf := new(bytes.Buffer)
cmd.SetOut(buf)
cmd.SetErr(buf)
cmd.SetArgs([]string{"testfile.igvm"})

err := cmd.Execute()

assert.NoError(t, err)
mockMeasurement.AssertExpectations(t)
}

func TestNewMeasureCmd_RunError(t *testing.T) {
cliInstance := &CLI{}
mockMeasurement := new(MockMeasurement)
cliInstance.measurement = mockMeasurement
expectedError := errors.New("mocked measurement error")

mockMeasurement.On("Run", "testfile.igvm").Return(expectedError)

cmd := cliInstance.NewMeasureCmd("fake_binary_path")

buf := new(bytes.Buffer)
cmd.SetOut(buf)
cmd.SetErr(buf)
cmd.SetArgs([]string{"testfile.igvm"})

err := cmd.Execute()

assert.Error(t, err)
assert.Equal(t, expectedError.Error(), err.Error())
mockMeasurement.AssertExpectations(t)
}

func TestParseConfig(t *testing.T) {
cfgString = ""
err := parseConfig()
Expand Down
5 changes: 4 additions & 1 deletion cli/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

"github.com/spf13/cobra"
"github.com/ultravioletrs/cocos/manager"
"github.com/ultravioletrs/cocos/pkg/attestation/igvmmeasure"
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
"github.com/ultravioletrs/cocos/pkg/clients/grpc/agent"
managergrpc "github.com/ultravioletrs/cocos/pkg/clients/grpc/manager"
Expand All @@ -22,12 +23,14 @@
client grpc.Client
managerClient manager.ManagerServiceClient
connectErr error
measurement igvmmeasure.MeasurementProvider
}

func New(agentConfig grpc.AgentClientConfig, managerConfig grpc.ManagerClientConfig) *CLI {
func New(agentConfig grpc.AgentClientConfig, managerConfig grpc.ManagerClientConfig, measurement igvmmeasure.MeasurementProvider) *CLI {

Check warning on line 29 in cli/sdk.go

View check run for this annotation

Codecov / codecov/patch

cli/sdk.go#L29

Added line #L29 was not covered by tests
return &CLI{
agentConfig: agentConfig,
managerConfig: managerConfig,
measurement: measurement,

Check warning on line 33 in cli/sdk.go

View check run for this annotation

Codecov / codecov/patch

cli/sdk.go#L33

Added line #L33 was not covered by tests
}
}

Expand Down
14 changes: 12 additions & 2 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/ultravioletrs/cocos/cli"
"github.com/ultravioletrs/cocos/pkg/attestation/igvmmeasure"
"github.com/ultravioletrs/cocos/pkg/clients/grpc"
cmd "github.com/virtee/sev-snp-measure-go/sevsnpmeasure/cmd"
)
Expand All @@ -28,7 +29,8 @@ const (
)

type config struct {
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"info"`
LogLevel string `env:"AGENT_LOG_LEVEL" envDefault:"info"`
IgvmBinaryPath string `env:"IGVM_BINARY_PATH" envDefault:"./build/igvmmeasure"`
}

func main() {
Expand Down Expand Up @@ -106,7 +108,14 @@ func main() {
return
}

cliSVC := cli.New(agentGRPCConfig, managerGRPCConfig)
measurement, err := igvmmeasure.NewIgvmMeasurement(cfg.IgvmBinaryPath, os.Stderr, os.Stdout)
if err != nil {
message := color.New(color.FgRed).Sprintf("failed to initialize measurement: %s", err) // Use %s instead of %w
rootCmd.Println(message)
return
}

cliSVC := cli.New(agentGRPCConfig, managerGRPCConfig, measurement)

if err := cliSVC.InitializeAgentSDK(rootCmd); err == nil {
defer cliSVC.Close()
Expand Down Expand Up @@ -136,6 +145,7 @@ func main() {

// measure.
rootCmd.AddCommand(cmd.NewRootCmd())
rootCmd.AddCommand(cliSVC.NewMeasureCmd(cfg.IgvmBinaryPath))

// Flags
keysCmd.PersistentFlags().StringVarP(
Expand Down
79 changes: 79 additions & 0 deletions pkg/attestation/igvmmeasure/igvmmeasure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package igvmmeasure

import (
"fmt"
"io"
"os/exec"
"strings"
)

type MeasurementProvider interface {
Run(igvmBinaryPath string) error
Stop() error
}
type IgvmMeasurement struct {
binPath string
options []string
stderr io.Writer
stdout io.Writer
cmd *exec.Cmd
execCommand func(name string, arg ...string) *exec.Cmd
}

func NewIgvmMeasurement(binPath string, stderr, stdout io.Writer) (*IgvmMeasurement, error) {
if binPath == "" {
return nil, fmt.Errorf("pathToBinary cannot be empty")
}

return &IgvmMeasurement{
binPath: binPath,
stderr: stderr,
stdout: stdout,
execCommand: exec.Command,
}, nil
}

func (m *IgvmMeasurement) Run(pathToFile string) error {
binary := m.binPath
args := []string{}
args = append(args, m.options...)
args = append(args, pathToFile)
args = append(args, "measure")
args = append(args, "-b")

out, err := m.execCommand(binary, args...).CombinedOutput()
if err != nil {
fmt.Println("Error:", err)
}
outputString := string(out)

lines := strings.Split(strings.TrimSpace(outputString), "\n")

if len(lines) == 1 {
outputString = strings.ToLower(outputString)
fmt.Print(outputString)
} else {
return fmt.Errorf("error: %s", outputString)
}

return nil
}

func (m *IgvmMeasurement) Stop() error {
if m.cmd == nil || m.cmd.Process == nil {
return fmt.Errorf("no running process to stop")
}

Check warning on line 67 in pkg/attestation/igvmmeasure/igvmmeasure.go

View check run for this annotation

Codecov / codecov/patch

pkg/attestation/igvmmeasure/igvmmeasure.go#L64-L67

Added lines #L64 - L67 were not covered by tests

if err := m.cmd.Process.Kill(); err != nil {
return fmt.Errorf("failed to stop process: %v", err)
}

Check warning on line 71 in pkg/attestation/igvmmeasure/igvmmeasure.go

View check run for this annotation

Codecov / codecov/patch

pkg/attestation/igvmmeasure/igvmmeasure.go#L69-L71

Added lines #L69 - L71 were not covered by tests

return nil

Check warning on line 73 in pkg/attestation/igvmmeasure/igvmmeasure.go

View check run for this annotation

Codecov / codecov/patch

pkg/attestation/igvmmeasure/igvmmeasure.go#L73

Added line #L73 was not covered by tests
}

// SetExecCommand allows tests to inject a mock execCommand function.
func (m *IgvmMeasurement) SetExecCommand(cmdFunc func(name string, arg ...string) *exec.Cmd) {
m.execCommand = cmdFunc
}
77 changes: 77 additions & 0 deletions pkg/attestation/igvmmeasure/igvmmeasure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Ultraviolet
// SPDX-License-Identifier: Apache-2.0
package igvmmeasure

import (
"bytes"
"os/exec"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestIgvmMeasurement(t *testing.T) {
tests := []struct {
name string
setup func() *IgvmMeasurement
runArgs string
expectErr bool
expectedErr string
}{
{
name: "NewIgvmMeasurement - Empty pathToBinary",
setup: func() *IgvmMeasurement {
igvm, err := NewIgvmMeasurement("", nil, nil)
assert.Error(t, err)
assert.Nil(t, igvm)
return nil
},
expectErr: true,
expectedErr: "pathToBinary cannot be empty",
},
{
name: "Run - Successful Execution",
setup: func() *IgvmMeasurement {
igvm, _ := NewIgvmMeasurement("/valid/path", nil, nil)
igvm.SetExecCommand(func(name string, arg ...string) *exec.Cmd {
return exec.Command("sh", "-c", "echo 'measurement successful'")
})
return igvm
},
expectErr: false,
},
{
name: "Run - Failure Execution",
setup: func() *IgvmMeasurement {
igvm, _ := NewIgvmMeasurement("/invalid/path", nil, nil)
igvm.SetExecCommand(func(name string, arg ...string) *exec.Cmd {
return exec.Command("sh", "-c", "echo 'some error occurred\nextra line' && exit 1")
})
return igvm
},
expectErr: true,
expectedErr: "error: some error occurred\nextra line",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
igvm := tc.setup()

if igvm != nil {
buf := new(bytes.Buffer)
igvm.stdout = buf
igvm.stderr = buf

err := igvm.Run(tc.runArgs)
if tc.expectErr {
assert.Error(t, err)
assert.Equal(t, strings.TrimSpace(tc.expectedErr), strings.TrimSpace(err.Error()))
} else {
assert.NoError(t, err)
}
}
})
}
}
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载