From 755077c6fe81050403709d4b339159313f639fb9 Mon Sep 17 00:00:00 2001 From: Thomas Tendyck Date: Tue, 30 Jan 2024 19:20:42 +0100 Subject: [PATCH] fix uniqueid and signerid commands if OE_LOG_LEVEL is set --- ego/cli/elf.go | 34 +++++++++++++++++++++++++---- ego/cli/signerid.go | 47 +++++++++++++++++----------------------- ego/cli/signerid_test.go | 43 +++++++++++++++++++++++++++--------- src/integration_test.sh | 15 ++++++++++++- 4 files changed, 97 insertions(+), 42 deletions(-) diff --git a/ego/cli/elf.go b/ego/cli/elf.go index 40aab9b7..49f4ad43 100644 --- a/ego/cli/elf.go +++ b/ego/cli/elf.go @@ -16,6 +16,8 @@ import ( "strings" ) +const oeinfoSectionName = ".oeinfo" + // ErrErrUnsupportedImportEClient is returned when an EGo binary uses the eclient package instead of the enclave package. var ErrUnsupportedImportEClient = errors.New("unsupported import: github.com/edgelesssys/ego/eclient") @@ -95,7 +97,7 @@ func getPayloadInformation(f io.ReaderAt) (uint64, int64, int64, error) { return 0, 0, 0, err } - oeInfo := elfFile.Section(".oeinfo") + oeInfo := elfFile.Section(oeinfoSectionName) if oeInfo == nil { return 0, 0, 0, ErrNoOEInfo } @@ -113,21 +115,45 @@ func getPayloadInformation(f io.ReaderAt) (uint64, int64, int64, error) { } func (c *Cli) getSymbolsFromELF(path string) ([]elf.Symbol, error) { - // Load ELF executable - file, err := c.fs.OpenFile(path, os.O_RDONLY, 0) + file, err := c.fs.Open(path) if err != nil { return nil, err } + defer file.Close() elfFile, err := elf.NewFile(file) if err != nil { return nil, err } - defer elfFile.Close() return elfFile.Symbols() } +func (c *Cli) readDataFromELF(path string, section string, offset int, size int) ([]byte, error) { + file, err := c.fs.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + elfFile, err := elf.NewFile(file) + if err != nil { + return nil, err + } + + sec := elfFile.Section(section) + if sec == nil { + return nil, errors.New("section not found") + } + + data := make([]byte, size) + if _, err := sec.ReadAt(data, int64(offset)); err != nil { + return nil, err + } + + return data, nil +} + // checkUnsupportedImports checks whether the to-be-signed or to-be-executed binary uses Go imports which are not supported. func (c *Cli) checkUnsupportedImports(path string) error { symbols, err := c.getSymbolsFromELF(path) diff --git a/ego/cli/signerid.go b/ego/cli/signerid.go index 3e6a7adc..cd5d0458 100644 --- a/ego/cli/signerid.go +++ b/ego/cli/signerid.go @@ -7,7 +7,9 @@ package cli import ( - "encoding/json" + "bytes" + "crypto/sha256" + "encoding/hex" "errors" "fmt" "os" @@ -18,12 +20,11 @@ import ( "ego/internal/launch" ) -type eradump struct { - SecurityVersion int - ProductID int - UniqueID string - SignerID string -} +const ( + offsetSigstruct = 144 + offsetModulus = 128 + offsetMRENCLAVE = 960 +) func (c *Cli) signeridByKey(path string) (string, error) { outBytes, err := c.runner.Output(exec.Command(c.getOesignPath(), "signerid", "-k", path)) @@ -46,37 +47,29 @@ func (c *Cli) signeridByKey(path string) (string, error) { return "", err } -func (c *Cli) readEradumpJSONtoStruct(path string) (*eradump, error) { - data, err := c.runner.Output(exec.Command(c.getOesignPath(), "eradump", "-e", path)) - if err != nil { - if err, ok := err.(*exec.ExitError); ok { - return nil, errors.New(string(err.Stderr)) - } - return nil, err - } - - var dump eradump - if err := json.Unmarshal(data, &dump); err != nil { - return nil, err - } - return &dump, nil -} - func (c *Cli) signeridByExecutable(path string) (string, error) { - dump, err := c.readEradumpJSONtoStruct(path) + const modulusSize = 384 + modulus, err := c.readDataFromELF(path, oeinfoSectionName, offsetSigstruct+offsetModulus, modulusSize) if err != nil { return "", err } - return dump.SignerID, nil + if bytes.Equal(modulus, make([]byte, modulusSize)) { + // if enclave is unsigned, return all zeros + return strings.Repeat("0", 64), nil + } + + // MRSIGNER is the sha256 of the modulus + sum := sha256.Sum256(modulus) + return hex.EncodeToString(sum[:]), nil } // Uniqueid returns the UniqueID of a signed executable. func (c *Cli) Uniqueid(path string) (string, error) { - dump, err := c.readEradumpJSONtoStruct(path) + data, err := c.readDataFromELF(path, oeinfoSectionName, offsetSigstruct+offsetMRENCLAVE, 32) if err != nil { return "", err } - return dump.UniqueID, nil + return hex.EncodeToString(data), nil } // Signerid returns the SignerID of a signed executable. diff --git a/ego/cli/signerid_test.go b/ego/cli/signerid_test.go index a14af008..544c3e74 100644 --- a/ego/cli/signerid_test.go +++ b/ego/cli/signerid_test.go @@ -14,28 +14,51 @@ import ( "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestUniqueid(t *testing.T) { assert := assert.New(t) + require := require.New(t) - cli := NewCli(signeridRunner{}, afero.NewMemMapFs()) + fs := afero.Afero{Fs: afero.NewMemMapFs()} + cli := NewCli(nil, fs) + const filename = "foo" - res, err := cli.Uniqueid("foo") - assert.NoError(err) - assert.Equal("uid foo", res) + _, err := cli.Uniqueid(filename) + assert.Error(err) + + require.NoError(fs.WriteFile(filename, elfUnsigned, 0)) + + res, err := cli.Uniqueid(filename) + require.NoError(err) + assert.Equal("0000000000000000000000000000000000000000000000000000000000000000", res) } -func TestSignerid(t *testing.T) { +func TestSigneridByExecutable(t *testing.T) { assert := assert.New(t) + require := require.New(t) - cli := NewCli(signeridRunner{}, afero.NewMemMapFs()) + fs := afero.Afero{Fs: afero.NewMemMapFs()} + cli := NewCli(nil, fs) + const filename = "foo" - res, err := cli.Signerid("foo") - assert.NoError(err) - assert.Equal("sid foo", res) + _, err := cli.Signerid(filename) + assert.Error(err) + + require.NoError(fs.WriteFile(filename, elfUnsigned, 0)) + + res, err := cli.Signerid(filename) + require.NoError(err) + assert.Equal("0000000000000000000000000000000000000000000000000000000000000000", res) +} + +func TestSigneridByKey(t *testing.T) { + assert := assert.New(t) + + cli := NewCli(signeridRunner{}, afero.NewMemMapFs()) - res, err = cli.Signerid("foo.pem") + res, err := cli.Signerid("foo.pem") assert.NoError(err) assert.Equal("id foo.pem", res) } diff --git a/src/integration_test.sh b/src/integration_test.sh index f25c73c1..244c4ad2 100755 --- a/src/integration_test.sh +++ b/src/integration_test.sh @@ -44,9 +44,22 @@ cp enclave.json /tmp/ego-integration-test/enclave.json export CGO_ENABLED=0 # test that ego-go ignores this run ego-go build -o /tmp/ego-integration-test/integration-test -# Sign & run intergration test +# Sign intergration test cd /tmp/ego-integration-test run ego sign + +# Test id commands +dump=$(ego-oesign dump -e integration-test) +run echo "$dump" | grep "^mrenclave=$(ego uniqueid integration-test)$" +run echo "$dump" | grep "^mrsigner=$(ego signerid integration-test)$" +run echo "$dump" | grep "^mrsigner=$(ego signerid public.pem)$" +export OE_LOG_LEVEL=INFO # regression: id commands were broken with OE_LOG_LEVEL set +run echo "$dump" | grep "^mrenclave=$(ego uniqueid integration-test)$" +run echo "$dump" | grep "^mrsigner=$(ego signerid integration-test)$" +run echo "$dump" | grep "^mrsigner=$(ego signerid public.pem)$" +unset OE_LOG_LEVEL + +# Run integration test run ego run integration-test # Test heap size check on sign