NOISSUE - Manifest checksum (#306)

* update backend info, and generate manifest checksun

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

* update report

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

* add test cases

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

* fix lint

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

---------

Signed-off-by: Sammy Oina <sammyoina@gmail.com>
This commit is contained in:
Sammy Kerata Oina
2024-11-08 16:56:45 +03:00
committed by GitHub
parent a3577da5b2
commit 1e285e32b4
5 changed files with 287 additions and 82 deletions
BIN
View File
Binary file not shown.
+69 -2
View File
@@ -3,12 +3,24 @@
package cli
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"os"
"github.com/spf13/cobra"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/internal"
"golang.org/x/crypto/sha3"
)
var (
ismanifest bool
toBase64 bool
)
func (cli *CLI) NewFileHashCmd() *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "checksum",
Short: "Compute the sha3-256 hash of a file",
Example: "checksum <file>",
@@ -16,13 +28,68 @@ func (cli *CLI) NewFileHashCmd() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
path := args[0]
if ismanifest {
hash, err := manifestChecksum(path)
if err != nil {
printError(cmd, "Error computing hash: %v ❌ ", err)
return
}
cmd.Println("Hash of manifest file:", hashOut(hash))
return
}
hash, err := internal.ChecksumHex(path)
if err != nil {
printError(cmd, "Error computing hash: %v ❌ ", err)
return
}
cmd.Println("Hash of file:", hash)
cmd.Println("Hash of file:", hashOut(hash))
},
}
cmd.Flags().BoolVarP(&ismanifest, "manifest", "m", false, "Compute the hash of the manifest file")
cmd.Flags().BoolVarP(&toBase64, "base64", "b", false, "Output the hash in base64")
return cmd
}
func manifestChecksum(path string) (string, error) {
file, err := os.ReadFile(path)
if err != nil {
return "", err
}
var cmp agent.Computation
if err := json.Unmarshal(file, &cmp); err != nil {
return "", err
}
jsonBytes, err := json.Marshal(cmp)
if err != nil {
return "", err
}
sum := sha3.Sum256(jsonBytes)
return hex.EncodeToString(sum[:]), nil
}
func hashOut(hashHex string) string {
if toBase64 {
return hexToBase64(hashHex)
}
return hashHex
}
func hexToBase64(hexStr string) string {
decoded, err := hex.DecodeString(hexStr)
if err != nil {
return ""
}
return base64.StdEncoding.EncodeToString(decoded)
}
+183 -55
View File
@@ -4,11 +4,12 @@ package cli
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
"github.com/ultravioletrs/cocos/internal"
"github.com/stretchr/testify/assert"
)
func TestNewFileHashCmd(t *testing.T) {
@@ -29,74 +30,201 @@ func TestNewFileHashCmd(t *testing.T) {
}
func TestNewFileHashCmdRun(t *testing.T) {
cli := &CLI{}
cmd := cli.NewFileHashCmd()
content := []byte("test content")
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.Write(content); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
testCases := []struct {
name string
isManifest bool
toBase64 bool
expectedOut string
expectedErr string
}{
{
name: "Valid file",
isManifest: false,
toBase64: false,
expectedOut: "Hash of file:",
expectedErr: "",
},
{
name: "Valid manifest file",
isManifest: true,
toBase64: false,
expectedOut: "Hash of manifest file:",
expectedErr: "",
},
{
name: "Valid file with base64 output",
isManifest: false,
toBase64: true,
expectedOut: "Hash of file:",
expectedErr: "",
},
{
name: "Non-existent file",
isManifest: false,
toBase64: false,
expectedOut: "Error computing hash:",
expectedErr: "",
},
}
var output bytes.Buffer
cmd.SetOut(&output)
cmd.SetErr(&output)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cli := &CLI{}
cmd := cli.NewFileHashCmd()
cmd.SetArgs([]string{tmpfile.Name()})
err = cmd.Execute()
if err != nil {
t.Fatalf("Error executing command: %v", err)
}
var output bytes.Buffer
cmd.SetOut(&output)
cmd.SetErr(&output)
expectedHash, err := internal.ChecksumHex(tmpfile.Name())
if err != nil {
t.Fatalf("Error computing expected hash: %v", err)
}
err := cmd.Flags().Set("manifest", fmt.Sprint(tc.isManifest))
assert.Nil(t, err)
err = cmd.Flags().Set("base64", fmt.Sprint(tc.toBase64))
assert.Nil(t, err)
if !strings.Contains(output.String(), expectedHash) {
t.Errorf("Expected output to contain hash %s, got %s", expectedHash, output.String())
if tc.name == "Non-existent file" {
cmd.SetArgs([]string{"non_existent_file.txt"})
} else {
content := []byte("{}")
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.Write(content); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
cmd.SetArgs([]string{tmpfile.Name()})
}
err = cmd.Execute()
if err != nil {
t.Fatalf("Error executing command: %v", err)
}
out := output.String()
if !strings.Contains(out, tc.expectedOut) {
t.Errorf("Expected output to contain '%s', got '%s'", tc.expectedOut, out)
}
if tc.expectedErr != "" && !strings.Contains(out, tc.expectedErr) {
t.Errorf("Expected output to contain '%s', got '%s'", tc.expectedErr, out)
}
})
}
}
func TestNewFileHashCmdInvalidArgs(t *testing.T) {
cli := &CLI{}
cmd := cli.NewFileHashCmd()
err := cmd.Execute()
if err == nil {
t.Error("Expected error when executing without arguments, got nil")
func TestManifestChecksum(t *testing.T) {
testCases := []struct {
name string
jsonContent string
expectedSum string
}{
{
name: "Valid manifest file",
jsonContent: `{
"id": "1234",
"name": "Example Computation",
"description": "This is an example computation"
}`,
expectedSum: "868825367c32c4b6d621d5d95e2890f233d8554df2348ab743aac2663a936f08",
},
{
name: "Invalid JSON",
jsonContent: `{`,
expectedSum: "",
},
}
cmd.SetArgs([]string{"file1", "file2"})
err = cmd.Execute()
if err == nil {
t.Error("Expected error when executing with too many arguments, got nil")
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
f, err := os.CreateTemp("", "test")
assert.Nil(t, err)
t.Cleanup(func() {
os.Remove(f.Name())
})
_, err = f.WriteString(tc.jsonContent)
assert.NoError(t, err)
err = f.Close()
assert.Nil(t, err)
hash, err := manifestChecksum(f.Name())
if tc.expectedSum == "" && err == nil {
t.Errorf("Expected error, got nil")
}
if tc.expectedSum != "" && err != nil {
t.Errorf("Unexpected error: %v", err)
}
if hash != tc.expectedSum {
t.Errorf("Expected hash %s, got %s", tc.expectedSum, hash)
}
})
}
}
func TestNewFileHashCmdNonExistentFile(t *testing.T) {
cli := &CLI{}
cmd := cli.NewFileHashCmd()
var output bytes.Buffer
cmd.SetOut(&output)
cmd.SetErr(&output)
cmd.SetArgs([]string{"non_existent_file.txt"})
err := cmd.Execute()
if err != nil {
t.Fatalf("Error executing command: %v", err)
func TestHexToBase64(t *testing.T) {
testCases := []struct {
name string
hexInput string
expectedOut string
}{
{
name: "Valid hex input",
hexInput: "48656c6c6f",
expectedOut: "SGVsbG8=",
},
{
name: "Invalid hex input",
hexInput: "invalid-hex",
expectedOut: "",
},
}
if !strings.Contains(output.String(), "Error computing hash") {
t.Errorf("Expected output to contain 'Error computing hash', got %s", output.String())
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
out := hexToBase64(tc.hexInput)
if out != tc.expectedOut {
t.Errorf("Expected %s, got %s", tc.expectedOut, out)
}
})
}
}
func TestHashOut(t *testing.T) {
testCases := []struct {
name string
hashHex string
toBase64 bool
expectedOut string
}{
{
name: "Hex output",
hashHex: "48656c6c6f",
toBase64: false,
expectedOut: "48656c6c6f",
},
{
name: "Base64 output",
hashHex: "48656c6c6f",
toBase64: true,
expectedOut: "SGVsbG8=",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
toBase64 = tc.toBase64
out := hashOut(tc.hashHex)
if out != tc.expectedOut {
t.Errorf("Expected %s, got %s", tc.expectedOut, out)
}
})
}
}
+13
View File
@@ -8,10 +8,15 @@ package quoteprovider
import (
"github.com/google/go-sev-guest/client"
"github.com/google/go-sev-guest/proto/check"
pb "github.com/google/go-sev-guest/proto/sevsnp"
cocosai "github.com/ultravioletrs/cocos"
)
var (
AttConfigurationSEVSNP = check.Config{Policy: &check.Policy{}, RootOfTrust: &check.RootOfTrust{}}
)
var _ client.QuoteProvider = (*embeddedQuoteProvider)(nil)
type embeddedQuoteProvider struct {
@@ -36,3 +41,11 @@ func (e *embeddedQuoteProvider) IsSupported() bool {
func (e *embeddedQuoteProvider) Product() *pb.SevProduct {
panic("unimplemented")
}
func FetchAttestation(reportDataSlice []byte) ([]byte, error) {
return cocosai.EmbeddedAttestation, nil
}
func VerifyAttestationReportTLS(attestationBytes []byte, reportData []byte) error {
return nil
}
+22 -25
View File
@@ -1,28 +1,25 @@
{
"policy": {
"policy": 196608,
"family_id": "AAAAAAAAAAAAAAAAAAAAAA==",
"image_id": "AAAAAAAAAAAAAAAAAAAAAA==",
"vmpl": 0,
"minimum_tcb": 15066229603414573059,
"minimum_launch_tcb": 15066229603414573059,
"require_author_key": false,
"measurement": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"host_data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"report_id_ma": "//////////////////////////////////////////8=",
"chip_id": "GrFqtQ+lrkLsjBslu9pcC6XqkrtFWY1ArIQ+I4gugQIsvCG0qekSvEtE4P/SLSJ6mHNpOkY0MHnGpvz1OkV+kw==",
"minimum_build": 21,
"minimum_version": "1.55",
"permit_provisional_firmware": false,
"require_id_block": false,
"product": {
"name": 1
"rootOfTrust":{
"product":"Milan",
"checkCrl":true,
"productLine":"Milan"
},
"policy":{
"policy":"196608",
"permit_provisional_firmware":true,
"familyId":"AAAAAAAAAAAAAAAAAAAAAA==",
"imageId":"AAAAAAAAAAAAAAAAAAAAAA==",
"vmpl":0,
"minimumTcb":"15352208179752599555",
"minimumLaunchTcb":"15352208179752599555",
"measurement":"TsWRmg8efWUW9XHZIomxBKrv4iCYeMO3ZlUPr+OhU5/QAPjCr96w0Dq9gJ7EaaP/",
"hostData":"HE5X+yGlBfpKlg4z9TTdV6ATs7MUr4Y+EhN+reuG+zY=",
"reportIdMa":"//////////////////////////////////////////8=",
"chipId":"GrFqtQ+lrkLsjBslu9pcC6XqkrtFWY1ArIQ+I4gugQIsvCG0qekSvEtE4P/SLSJ6mHNpOkY0MHnGpvz1OkV+kw==",
"minimumBuild":7,
"minimumVersion":"1.55",
"product":{
"name":"SEV_PRODUCT_MILAN"
}
}
},
"root_of_trust": {
"product": "Milan",
"check_crl": true,
"disallow_network": false,
"product_line": "Milan"
}
}