diff --git a/comid/flagsmap.go b/comid/flagsmap.go new file mode 100644 index 00000000..279cf1a4 --- /dev/null +++ b/comid/flagsmap.go @@ -0,0 +1,177 @@ +// Copyright 2023 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package comid + +var True = true +var False = false + +// Flag indicates whether a particular operational mode is active within the +// measured environment. +type Flag int + +const ( + FlagIsConfigured Flag = iota + FlagIsSecure + FlagIsRecovery + FlagIsDebug + FlagIsReplayProtected + FlagIsIntegrityProtected + FlagIsRuntimeMeasured + FlagIsImmutable + FlagIsTcb +) + +// FlagsMap describes a number of boolean operational modes. If a value is nil, +// then the operational mode is unknown. +type FlagsMap struct { + // IsConfigured indicates whether the measured environment is fully + // configured for normal operation. + IsConfigured *bool `cbor:"0,keyasint,omitempty" json:"is-configured,omitempty"` + // IsSecure indicates whether the measured environment's configurable + // security settings are fully enabled. + IsSecure *bool `cbor:"1,keyasint,omitempty" json:"is-secure,omitempty"` + // IsRecovery indicates whether the measured environment is in recovery + // mode. + IsRecovery *bool `cbor:"2,keyasint,omitempty" json:"is-recovery,omitempty"` + // IsDebug indicates whether the measured environment is in a debug + // enabled mode. + IsDebug *bool `cbor:"3,keyasint,omitempty" json:"is-debug,omitempty"` + // IsReplayProtected indicates whether the measured environment is + // protected from replay by a previous image that differs from the + // current image. + IsReplayProtected *bool `cbor:"4,keyasint,omitempty" json:"is-replay-protected,omitempty"` + // IsIntegrityProtected indicates whether the measured environment is + // protected from unauthorized update. + IsIntegrityProtected *bool `cbor:"5,keyasint,omitempty" json:"is-integrity-protected,omitempty"` + // IsRuntimeMeasured indicates whether the measured environment is + // measured after being loaded into memory. + IsRuntimeMeasured *bool `cbor:"6,keyasint,omitempty" json:"is-runtime-meas,omitempty"` + // IsImmutable indicates whether the measured environment is immutable. + IsImmutable *bool `cbor:"7,keyasint,omitempty" json:"is-immutable,omitempty"` + // IsTcb indicates whether the measured environment is a trusted + // computing base. + IsTcb *bool `cbor:"8,keyasint,omitempty" json:"is-tcb,omitempty"` +} + +func NewFlagsMap() *FlagsMap { + return &FlagsMap{} +} + +func (o *FlagsMap) AnySet() bool { + if o.IsConfigured != nil || o.IsSecure != nil || o.IsRecovery != nil || o.IsDebug != nil || + o.IsReplayProtected != nil || o.IsIntegrityProtected != nil || + o.IsRuntimeMeasured != nil || o.IsImmutable != nil || o.IsTcb != nil { + return true + } + + return false +} + +func (o *FlagsMap) SetTrue(flags ...Flag) { + for _, flag := range flags { + switch flag { + case FlagIsConfigured: + o.IsConfigured = &True + case FlagIsSecure: + o.IsSecure = &True + case FlagIsRecovery: + o.IsRecovery = &True + case FlagIsDebug: + o.IsDebug = &True + case FlagIsReplayProtected: + o.IsReplayProtected = &True + case FlagIsIntegrityProtected: + o.IsIntegrityProtected = &True + case FlagIsRuntimeMeasured: + o.IsRuntimeMeasured = &True + case FlagIsImmutable: + o.IsImmutable = &True + case FlagIsTcb: + o.IsTcb = &True + default: + } + } +} + +func (o *FlagsMap) SetFalse(flags ...Flag) { + for _, flag := range flags { + switch flag { + case FlagIsConfigured: + o.IsConfigured = &False + case FlagIsSecure: + o.IsSecure = &False + case FlagIsRecovery: + o.IsRecovery = &False + case FlagIsDebug: + o.IsDebug = &False + case FlagIsReplayProtected: + o.IsReplayProtected = &False + case FlagIsIntegrityProtected: + o.IsIntegrityProtected = &False + case FlagIsRuntimeMeasured: + o.IsRuntimeMeasured = &False + case FlagIsImmutable: + o.IsImmutable = &False + case FlagIsTcb: + o.IsTcb = &False + default: + } + } +} + +func (o *FlagsMap) Clear(flags ...Flag) { + for _, flag := range flags { + switch flag { + case FlagIsConfigured: + o.IsConfigured = nil + case FlagIsSecure: + o.IsSecure = nil + case FlagIsRecovery: + o.IsRecovery = nil + case FlagIsDebug: + o.IsDebug = nil + case FlagIsReplayProtected: + o.IsReplayProtected = nil + case FlagIsIntegrityProtected: + o.IsIntegrityProtected = nil + case FlagIsRuntimeMeasured: + o.IsRuntimeMeasured = nil + case FlagIsImmutable: + o.IsImmutable = nil + case FlagIsTcb: + o.IsTcb = nil + default: + } + } +} + +func (o *FlagsMap) Get(flag Flag) *bool { + switch flag { + case FlagIsConfigured: + return o.IsConfigured + case FlagIsSecure: + return o.IsSecure + case FlagIsRecovery: + return o.IsRecovery + case FlagIsDebug: + return o.IsDebug + case FlagIsReplayProtected: + return o.IsReplayProtected + case FlagIsIntegrityProtected: + return o.IsIntegrityProtected + case FlagIsRuntimeMeasured: + return o.IsRuntimeMeasured + case FlagIsImmutable: + return o.IsImmutable + case FlagIsTcb: + return o.IsTcb + default: + return nil + } +} + +// Valid returns an error if the FlagsMap is invalid. +func (o FlagsMap) Valid() error { + return nil +} diff --git a/comid/flagsmap_test.go b/comid/flagsmap_test.go new file mode 100644 index 00000000..e9bc477c --- /dev/null +++ b/comid/flagsmap_test.go @@ -0,0 +1,41 @@ +package comid + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_FlagsMap(t *testing.T) { + fm := NewFlagsMap() + assert.False(t, fm.AnySet()) + + for _, flag := range []Flag{ + FlagIsConfigured, + FlagIsSecure, + FlagIsRecovery, + FlagIsDebug, + FlagIsReplayProtected, + FlagIsIntegrityProtected, + FlagIsRuntimeMeasured, + FlagIsImmutable, + FlagIsTcb, + } { + fm.SetTrue(flag) + assert.True(t, fm.AnySet()) + assert.Equal(t, true, *fm.Get(flag)) + + fm.SetFalse(flag) + assert.True(t, fm.AnySet()) + assert.Equal(t, false, *fm.Get(flag)) + + fm.Clear(flag) + assert.False(t, fm.AnySet()) + assert.Equal(t, (*bool)(nil), fm.Get(flag)) + } + + fm.SetTrue(Flag(-1)) + fm.SetFalse(Flag(-1)) + assert.False(t, fm.AnySet()) + assert.Equal(t, (*bool)(nil), fm.Get(Flag(-1))) +} diff --git a/comid/measurement.go b/comid/measurement.go index 4a560b67..b1909d87 100644 --- a/comid/measurement.go +++ b/comid/measurement.go @@ -8,6 +8,8 @@ import ( "fmt" "net" + "github.com/veraison/corim/encoding" + "github.com/veraison/corim/extensions" "github.com/veraison/eat" "github.com/veraison/swid" ) @@ -16,8 +18,9 @@ const MaxUint64 = ^uint64(0) // Measurement stores a measurement-map with CBOR and JSON serializations. type Measurement struct { - Key *Mkey `cbor:"0,keyasint,omitempty" json:"key,omitempty"` - Val Mval `cbor:"1,keyasint" json:"value"` + Key *Mkey `cbor:"0,keyasint,omitempty" json:"key,omitempty"` + Val Mval `cbor:"1,keyasint" json:"value"` + AuthorizedBy *CryptoKey `cbor:"2,keyasint,omitempty" json:"authorized-by,omitempty"` } // Mkey stores a $measured-element-type-choice. @@ -212,7 +215,7 @@ type Mval struct { Ver *Version `cbor:"0,keyasint,omitempty" json:"version,omitempty"` SVN *SVN `cbor:"1,keyasint,omitempty" json:"svn,omitempty"` Digests *Digests `cbor:"2,keyasint,omitempty" json:"digests,omitempty"` - OpFlags *OpFlags `cbor:"3,keyasint,omitempty" json:"op-flags,omitempty"` + Flags *FlagsMap `cbor:"3,keyasint,omitempty" json:"flags,omitempty"` RawValue *RawValue `cbor:"4,keyasint,omitempty" json:"raw-value,omitempty"` RawValueMask *[]byte `cbor:"5,keyasint,omitempty" json:"raw-value-mask,omitempty"` MACAddr *MACaddr `cbor:"6,keyasint,omitempty" json:"mac-addr,omitempty"` @@ -220,13 +223,35 @@ type Mval struct { SerialNumber *string `cbor:"8,keyasint,omitempty" json:"serial-number,omitempty"` UEID *eat.UEID `cbor:"9,keyasint,omitempty" json:"ueid,omitempty"` UUID *UUID `cbor:"10,keyasint,omitempty" json:"uuid,omitempty"` + + Extensions +} + +// RegisterExtensions registers a struct as a collections of extensions +func (o *Mval) RegisterExtensions(exts extensions.IExtensionsValue) { + o.Extensions.Register(exts) +} + +// GetExtensions returns pervisouosly registered extension +func (o *Mval) GetExtensions() extensions.IExtensionsValue { + return o.Extensions.IExtensionsValue +} + +// UnmarshalCBOR deserializes from CBOR +func (o *Mval) UnmarshalCBOR(data []byte) error { + return encoding.PopulateStructFromCBOR(dm, data, o) +} + +// MarshalCBOR serializes to CBOR +func (o *Mval) MarshalCBOR() ([]byte, error) { + return encoding.SerializeStructToCBOR(em, o) } func (o Mval) Valid() error { if o.Ver == nil && o.SVN == nil && o.Digests == nil && - o.OpFlags == nil && + o.Flags == nil && o.RawValue == nil && o.RawValueMask == nil && o.MACAddr == nil && @@ -249,8 +274,8 @@ func (o Mval) Valid() error { } } - if o.OpFlags != nil { - if err := o.OpFlags.Valid(); err != nil { + if o.Flags != nil { + if err := o.Flags.Valid(); err != nil { return err } } @@ -259,7 +284,7 @@ func (o Mval) Valid() error { // TODO(tho) MAC addr & friends (see https://github.com/veraison/corim/issues/18) - return nil + return o.Extensions.ValidMval(&o) } // Version stores a version-map with JSON and CBOR serializations. @@ -450,16 +475,51 @@ func (o *Measurement) AddDigest(algID uint64, digest []byte) *Measurement { } o.Val.Digests = ds } + return o } -// SetOpFlags sets the supplied operational flags in the measurement-values-map -// of the target measurement -func (o *Measurement) SetOpFlags(flags ...OpFlags) *Measurement { +// SetFlagsTrue sets the supplied operational flags to true in the +// measurement-values-map of the target measurement +func (o *Measurement) SetFlagsTrue(flags ...Flag) *Measurement { + if o != nil { + if o.Val.Flags == nil { + o.Val.Flags = NewFlagsMap() + } + o.Val.Flags.SetTrue(flags...) + } + + return o +} + +// SetFlagsFalse sets the supplied operational flags to true in the +// measurement-values-map of the target measurement +func (o *Measurement) SetFlagsFalse(flags ...Flag) *Measurement { if o != nil { - o.Val.OpFlags = NewOpFlags() - o.Val.OpFlags.SetOpFlags(flags...) + if o.Val.Flags == nil { + o.Val.Flags = NewFlagsMap() + } + o.Val.Flags.SetFalse(flags...) } + + return o +} + +// ClearFlags clears the supplied operational flags in the +// measurement-values-map of the target measurement +func (o *Measurement) ClearFlags(flags ...Flag) *Measurement { + if o != nil { + if o.Val.Flags == nil { + return o + } + + o.Val.Flags.Clear(flags...) + + if !o.Val.Flags.AnySet() { + o.Val.Flags = nil + } + } + return o } diff --git a/comid/opflag.go b/comid/opflag.go deleted file mode 100644 index ce0c8d8b..00000000 --- a/comid/opflag.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2021 Contributors to the Veraison project. -// SPDX-License-Identifier: Apache-2.0 - -package comid - -import ( - "encoding/json" - "fmt" -) - -// OpFlags implements the flags-type, mapping to DiceTcbInfo.flags via the -// operational flags not-configured, not-secure, recovery and debug. -// If the flags field is omitted, all flags are assumed to be 0. -type OpFlags uint8 - -const ( - OpFlagNotConfigured OpFlags = 1 << iota - OpFlagNotSecure - OpFlagRecovery - OpFlagDebug -) - -func NewOpFlags() *OpFlags { - return new(OpFlags) -} - -func (o OpFlags) Strings() []string { - var a []string - - if o&OpFlagNotConfigured != 0 { - a = append(a, "notConfigured") - } - - if o&OpFlagNotSecure != 0 { - a = append(a, "notSecure") - } - - if o&OpFlagRecovery != 0 { - a = append(a, "recovery") - } - - if o&OpFlagDebug != 0 { - a = append(a, "debug") - } - - return a -} - -func (o OpFlags) Valid() error { - // While any combination in the lower half-byte is acceptable, the most - // significant nibble must be all zeroes. - if o&0xf0 != 0 { - return fmt.Errorf("op-flags has unknown bits asserted: %02x", o) - } - - return nil -} - -// SetFlags sets the target object as specified. As many flags as necessary can -// be specified in one call. -func (o *OpFlags) SetOpFlags(flags ...OpFlags) *OpFlags { - if o != nil { - for _, flag := range flags { - *o |= flag - } - } - return o -} - -func (o OpFlags) IsSet(flag OpFlags) bool { - return o&flag != 0 -} - -// UnmarshalJSON provides a custom deserializer for the OpFlags type that uses an -// array of identifiers rather than a bit set, e.g.: -// -// "op-flags": [ -// "notSecure", -// "debug" -// ] -func (o *OpFlags) UnmarshalJSON(data []byte) error { - var a []string - - if err := json.Unmarshal(data, &a); err != nil { - return err - } - - if len(a) == 0 { - *o = 0 - return nil - } - - for _, s := range a { - switch s { - case "notSecure": - *o |= OpFlagNotSecure - case "notConfigured": - *o |= OpFlagNotConfigured - case "recovery": - *o |= OpFlagRecovery - case "debug": - *o |= OpFlagDebug - default: - // ignore unknown opflags - continue - } - } - - return nil -} - -func (o OpFlags) MarshalJSON() ([]byte, error) { - return json.Marshal(o.Strings()) -} diff --git a/comid/opflag_test.go b/comid/opflag_test.go deleted file mode 100644 index 261dc963..00000000 --- a/comid/opflag_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2021 Contributors to the Veraison project. -// SPDX-License-Identifier: Apache-2.0 - -package comid - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestFlags_UnmarshalJSON_skip_unknown(t *testing.T) { - tv := []byte(`[ "notSecure", "mysteriousFlagWhichWillBeIgnored" ]`) - - flags := NewOpFlags().SetOpFlags(OpFlagNotSecure) - require.NotNil(t, flags) - expected := *flags - - var actual OpFlags - err := actual.UnmarshalJSON(tv) - - assert.Nil(t, err) - assert.Equal(t, expected, actual) - assert.True(t, actual.IsSet(OpFlagNotSecure)) -} - -func TestFlags_UnmarshalJSON_all_known(t *testing.T) { - tv := []byte(`[ "notSecure", "notConfigured", "recovery", "debug" ]`) - - flags := NewOpFlags(). - SetOpFlags(OpFlagNotSecure). - SetOpFlags(OpFlagNotConfigured). - SetOpFlags(OpFlagRecovery). - SetOpFlags(OpFlagDebug) - require.NotNil(t, flags) - expected := *flags - - var actual OpFlags - err := actual.UnmarshalJSON(tv) - - fmt.Printf("CBOR: %02x\n", actual) - - assert.Nil(t, err) - assert.Equal(t, expected, actual) - assert.True(t, actual.IsSet(OpFlagNotSecure)) - assert.True(t, actual.IsSet(OpFlagRecovery)) - assert.True(t, actual.IsSet(OpFlagDebug)) - assert.True(t, actual.IsSet(OpFlagNotConfigured)) -} - -func TestFlags_UnmarshalJSON_empty(t *testing.T) { - tv := []byte(`[ ]`) - - flags := NewOpFlags() - require.NotNil(t, flags) - expected := *flags - - var actual OpFlags - err := actual.UnmarshalJSON(tv) - - fmt.Printf("%02x\n", actual) - - assert.Nil(t, err) - assert.Equal(t, expected, actual) - assert.False(t, actual.IsSet(OpFlagNotSecure)) - assert.False(t, actual.IsSet(OpFlagRecovery)) - assert.False(t, actual.IsSet(OpFlagDebug)) - assert.False(t, actual.IsSet(OpFlagNotConfigured)) -} - -func TestFlags_Valid_ok(t *testing.T) { - // all valid flags combinations - for i := 1; i <= 15; i++ { - tv := OpFlags(i) - - assert.Nil(t, tv.Valid()) - } -} - -func TestFlags_Valid_bad_combos(t *testing.T) { - for i := 1; i <= 15; i++ { - for j := 1; j <= 15; j++ { - tv := OpFlags(i<<4 | j) - - expectedErr := fmt.Sprintf("op-flags has unknown bits asserted: %02x", tv) - - assert.EqualError(t, tv.Valid(), expectedErr) - } - } -}