Skip to content

Commit

Permalink
Merge pull request #2893 from oasislabs/yawning/feature/sgx-sigstruct
Browse files Browse the repository at this point in the history
runtime: Do SGX enclave signing ourself
  • Loading branch information
Yawning authored May 14, 2020
2 parents 9152bad + 2a90ba4 commit edb8515
Show file tree
Hide file tree
Showing 26 changed files with 835 additions and 126 deletions.
6 changes: 6 additions & 0 deletions .changelog/1707.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go/runtime/host/sgx: Add support for SIGSTRUCTs

For now this will just generate one, signed with the same key that
`runtime-loader` used to use (the Fortanix dummy key), but this will
also support using file backed signatures, once we have an idea on how
we are going to handle the process for such things.
99 changes: 82 additions & 17 deletions go/common/sgx/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"io"

"github.com/pkg/errors"
"math/big"
)

const (
Expand All @@ -20,8 +20,36 @@ const (

// enclaveIdentitySize is the total size of EnclaveIdentity in bytes.
enclaveIdentitySize = MrSignerSize + MrEnclaveSize

// ModulusSize is the required RSA modulus size in bits.
ModulusSize = 3072

modulusBytes = ModulusSize / 8
)

// AttributesFlags is attributes flags inside enclave report attributes.
type AttributesFlags uint64

// Predefined enclave report attributes flags.
const (
AttributeInit AttributesFlags = 0b0000_0001
AttributeDebug AttributesFlags = 0b0000_0010
AttributeMode64Bit AttributesFlags = 0b0000_0100
AttributeProvisionKey AttributesFlags = 0b0001_0000
AttributeEInitTokenKey AttributesFlags = 0b0010_0000
)

// Attributes is a SGX enclave attributes value inside report.
type Attributes struct {
Flags AttributesFlags
Xfrm uint64
}

// GetFlagInit returns value of given flag attribute of the Report.
func (a AttributesFlags) Contains(flag AttributesFlags) bool {
return (uint64(a) & uint64(flag)) != 0
}

// Mrenclave is a SGX enclave identity register value (MRENCLAVE).
type MrEnclave [MrEnclaveSize]byte

Expand All @@ -34,7 +62,7 @@ func (m *MrEnclave) MarshalBinary() (data []byte, err error) {
// UnmarshalBinary decodes a binary marshaled Mrenclave.
func (m *MrEnclave) UnmarshalBinary(data []byte) error {
if len(data) != MrEnclaveSize {
return errors.New("sgx: malformed MRENCLAVE")
return fmt.Errorf("sgx: malformed MRENCLAVE")
}

copy(m[:], data)
Expand Down Expand Up @@ -70,7 +98,7 @@ readLoop:
case io.EOF:
break readLoop
default:
return errors.Wrap(err, "sgx: failed to read .sgxs")
return fmt.Errorf("sgx: failed to read .sgxs: %w", err)
}
}

Expand Down Expand Up @@ -102,7 +130,7 @@ func (m *MrSigner) MarshalBinary() (data []byte, err error) {
// UnmarshalBinary decodes a binary marshaled MrSigner.
func (m *MrSigner) UnmarshalBinary(data []byte) error {
if len(data) != MrSignerSize {
return errors.New("sgx: malformed MRSIGNER")
return fmt.Errorf("sgx: malformed MRSIGNER")
}

copy(m[:], data)
Expand All @@ -122,22 +150,59 @@ func (m *MrSigner) UnmarshalHex(text string) error {

// FromPublicKey derives a MrSigner from a RSA public key.
func (m *MrSigner) FromPublicKey(pk *rsa.PublicKey) error {
const modulusBits = 3072 // Hardware constraint.
if pk.Size() != modulusBits/8 {
return errors.New("sgx: invalid RSA public key for SGX signing")
}

// The MRSIGNER is the SHA256 digest of the little endian representation
// of the RSA public key modulus.
modulus := pk.N.Bytes()
for left, right := 0, len(modulus)-1; left < right; left, right = left+1, right-1 {
modulus[left], modulus[right] = modulus[right], modulus[left]
modulus, err := To3072le(pk.N, false)
if err != nil {
return err
}

sum := sha256.Sum256(modulus)
return m.UnmarshalBinary(sum[:])
}

// To3072le converts a big.Int to a 3072 bit little endian representation,
// padding if allowed AND required.
func To3072le(z *big.Int, mayPad bool) ([]byte, error) {
buf := z.Bytes()

sz := len(buf)
if sz != modulusBytes {
padLen := modulusBytes - sz
if !mayPad || padLen < 0 {
return nil, fmt.Errorf("sgx: big int is not %v bits: %v", ModulusSize, sz)
}

// Pad before reversing.
padded := make([]byte, padLen, modulusBytes)
buf = append(padded, buf...)
}

buf = reverseBuffer(buf)

return buf, nil
}

// From3072le converts a 3072 bit buffer to the corresponding big.Int, assuming
// that the buffer is in little endian representation.
func From3072le(b []byte) (*big.Int, error) {
if sz := len(b); sz != modulusBytes {
return nil, fmt.Errorf("sgx: buffer is not %v bits: %v", modulusBytes, sz)
}

buf := reverseBuffer(b)
var ret big.Int
return ret.SetBytes(buf), nil
}

func reverseBuffer(b []byte) []byte {
buf := append([]byte{}, b...)
for left, right := 0, len(buf)-1; left < right; left, right = left+1, right-1 {
buf[left], buf[right] = buf[right], buf[left]
}
return buf
}

// String returns the string representation of a MrSigner.
func (m MrSigner) String() string {
return hex.EncodeToString(m[:])
Expand All @@ -158,13 +223,13 @@ func (id EnclaveIdentity) MarshalText() (data []byte, err error) {
func (id *EnclaveIdentity) UnmarshalText(text []byte) error {
b, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return errors.Wrap(err, "sgx: malformed EnclaveIdentity")
return fmt.Errorf("sgx: malformed EnclaveIdentity: %w", err)
}
if err := id.MrEnclave.UnmarshalBinary(b[:MrEnclaveSize]); err != nil {
return errors.Wrap(err, "sgx: malformed MrEnclave in EnclaveIdentity")
return fmt.Errorf("sgx: malformed MrEnclave in EnclaveIdentity: %w", err)
}
if err := id.MrSigner.UnmarshalBinary(b[MrEnclaveSize:]); err != nil {
return errors.Wrap(err, "sgx: malformed MrSigner in EnclaveIdentity")
return fmt.Errorf("sgx: malformed MrSigner in EnclaveIdentity: %w", err)
}

return nil
Expand All @@ -174,7 +239,7 @@ func (id *EnclaveIdentity) UnmarshalText(text []byte) error {
func (id *EnclaveIdentity) UnmarshalHex(text string) error {
b, err := hex.DecodeString(text)
if err != nil || len(b) != enclaveIdentitySize {
return errors.Wrap(err, "sgx: malformed EnclaveIdentity")
return fmt.Errorf("sgx: malformed EnclaveIdentity: %w", err)
}

copy(id.MrEnclave[:], b[:MrEnclaveSize])
Expand Down
18 changes: 3 additions & 15 deletions go/common/sgx/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package sgx

import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -13,19 +10,10 @@ import (
func TestMrSignerDerivation(t *testing.T) {
require := require.New(t)

rawPem, err := ioutil.ReadFile("testdata/dummy.pub.pem")
require.NoError(err, "Load test public key")

blk, _ := pem.Decode(rawPem)
require.NotNil(blk, "Test public key PEM has a block")

nakedPubKey, err := x509.ParsePKIXPublicKey(blk.Bytes)
require.NoError(err, "Parse PKIX RSA public key")

rsaPubKey := nakedPubKey.(*rsa.PublicKey)

// This could just use FortanixDummyMrSigner, since it's done in
// the package init()...
var mrSigner MrSigner
err = mrSigner.FromPublicKey(rsaPubKey)
err := mrSigner.FromPublicKey(fortanixDummyKey.Public().(*rsa.PublicKey))
require.NoError(err, "Derive MRSIGNER")

require.Equal(mrSigner.String(), "9affcfae47b848ec2caf1c49b4b283531e1cc425f93582b36806e52a43d78d1a")
Expand Down
93 changes: 93 additions & 0 deletions go/common/sgx/fortanix_dummy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package sgx

import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"

cmdFlags "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common/flags"
)

var (
// FortanixDummyMrSigner is the MRSIGNER value corresponding to the
// dummy signing key that is used by the Fortanix Rust SGX SDK's
// enclave-runner.
FortanixDummyMrSigner MrSigner

fortanixDummyKey *rsa.PrivateKey
)

// UnsafeFortanixDummyKey returns the Fortanix dummy signing key.
//
// This MUST only ever be used for launching test enclaves.
func UnsafeFortanixDummyKey() *rsa.PrivateKey {
if !cmdFlags.DebugDontBlameOasis() {
return nil
}
return fortanixDummyKey
}

// This is the "dummy" enclave signing key extracted from the
// Fortanix Rust SGX SDK's enclave-runner, converted to
// PEM format from the DER representation via:
//
// openssl rsa -in dummy.priv.der -inform der -out /tmp/dummy.priv.pem
//
// Bug reports of any kind regarding the existence of this private
// key in the git repository (especially those sent to our bug bounty
// program) will be ignored and mercilessly mocked.
//
// Source: https://github.com/fortanix/rust-sgx/blob/master/enclave-runner/src/dummy.key
const fortanixDummyPrivateKeyPEM = `
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----`

func init() {
var err error
blk, _ := pem.Decode([]byte(fortanixDummyPrivateKeyPEM))
fortanixDummyKey, err = x509.ParsePKCS1PrivateKey(blk.Bytes)
if err != nil {
panic("failed to parse dummy key DER: " + err.Error())
}

if err = FortanixDummyMrSigner.FromPublicKey(fortanixDummyKey.Public().(*rsa.PublicKey)); err != nil {
panic("failed to derive dummy key MrSigner: " + err.Error())
}
}
10 changes: 1 addition & 9 deletions go/common/sgx/ias/avr.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,6 @@ var (
}

mrSignerBlacklist = make(map[sgx.MrSigner]bool)

// FortanixTestMrSigner is the MRSIGNER value corresponding to the Fortanix
// test signing key that is used by default if no other signing key is
// specified.
FortanixTestMrSigner sgx.MrSigner
)

// ISVEnclaveQuoteStatus is the status of an enclave quote.
Expand Down Expand Up @@ -421,7 +416,7 @@ func UnsetAllowDebugEnclaves() {
func BuildMrSignerBlacklist(allowTestKeys bool) {
if !allowTestKeys {
for _, v := range []string{
FortanixTestMrSigner.String(),
sgx.FortanixDummyMrSigner.String(),
} {
var signer sgx.MrSigner
if err := signer.UnmarshalHex(v); err != nil {
Expand All @@ -433,9 +428,6 @@ func BuildMrSignerBlacklist(allowTestKeys bool) {
}

func init() {
// Fortanix test key.
_ = FortanixTestMrSigner.UnmarshalHex("9affcfae47b848ec2caf1c49b4b283531e1cc425f93582b36806e52a43d78d1a")

for k, v := range isvQuoteFwdMap {
isvQuoteRevMap[v] = k
}
Expand Down
31 changes: 4 additions & 27 deletions go/common/sgx/ias/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,34 +85,11 @@ func (b *Body) MarshalBinary() ([]byte, error) {
return bBin, nil
}

// AttributesFlags is attributes flags inside enclave report attributes.
type AttributesFlags uint64

// Predefined enclave report attributes flags.
const (
AttributeInit AttributesFlags = 0b0000_0001
AttributeDebug AttributesFlags = 0b0000_0010
AttributeMode64Bit AttributesFlags = 0b0000_0100
AttributeProvisionKey AttributesFlags = 0b0001_0000
AttributeEInitTokenKey AttributesFlags = 0b0010_0000
)

// Attributes is a SGX enclave attributes value inside report.
type Attributes struct {
Flags AttributesFlags
Xfrm uint64
}

// GetFlagInit returns value of given flag attribute of the Report.
func (a AttributesFlags) Contains(flag AttributesFlags) bool {
return (uint64(a) & uint64(flag)) != 0
}

// Report is an enclave report body.
type Report struct { // nolint: maligned
CPUSVN [16]byte
MiscSelect uint32
Attributes Attributes
Attributes sgx.Attributes
MRENCLAVE sgx.MrEnclave
MRSIGNER sgx.MrSigner
ISVProdID uint16
Expand Down Expand Up @@ -153,7 +130,7 @@ func (r *Report) MarshalBinary() ([]byte, error) {
func (r *Report) UnmarshalBinary(data []byte) error {
copy(r.CPUSVN[:], data[0:])
r.MiscSelect = binary.LittleEndian.Uint32(data[16:])
r.Attributes.Flags = AttributesFlags(binary.LittleEndian.Uint64(data[48:]))
r.Attributes.Flags = sgx.AttributesFlags(binary.LittleEndian.Uint64(data[48:]))
r.Attributes.Xfrm = binary.LittleEndian.Uint64(data[56:])
_ = r.MRENCLAVE.UnmarshalBinary(data[64 : 64+sgx.MrEnclaveSize])
_ = r.MRSIGNER.UnmarshalBinary(data[128 : 128+sgx.MrSignerSize])
Expand All @@ -177,12 +154,12 @@ func (q *Quote) Verify() error {

if !unsafeAllowDebugEnclaves {
// Disallow debug enclaves, if we are in production mode.
if q.Report.Attributes.Flags.Contains(AttributeDebug) {
if q.Report.Attributes.Flags.Contains(sgx.AttributeDebug) {
return fmt.Errorf("ias/avr: disallowed debug enclave since we are in production mode")
}
} else {
// Disallow non-debug enclaves, if we are in debug mode.
if !q.Report.Attributes.Flags.Contains(AttributeDebug) {
if !q.Report.Attributes.Flags.Contains(sgx.AttributeDebug) {
return fmt.Errorf("ias/avr: disallowed production enclave since we are in debug mode")
}
}
Expand Down
Loading

0 comments on commit edb8515

Please sign in to comment.