Skip to content

Commit

Permalink
Add life cycle state extraction.
Browse files Browse the repository at this point in the history
Add a way to extract the actual PSA/CCA life cycle state from the
LifeCycle claim field, representing the state as an enum. Use this
internally for validation.

Signed-off-by: setrofim <[email protected]>
  • Loading branch information
setrofim committed Feb 22, 2023
1 parent 23c9c32 commit 1dfd92c
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 48 deletions.
167 changes: 119 additions & 48 deletions claims_common.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2021-2022 Contributors to the Veraison project.
// Copyright 2021-2023 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

package psatoken
Expand Down Expand Up @@ -50,129 +50,200 @@ const (
SecurityLifecycleDecommissionedMax = 0x60ff
)

const (
CcaPlatformLifecycleUnknownMin = 0x0000
CcaPlatformLifecycleUnknownMax = 0x00ff
CcaPlatformLifecycleAssemblyAndTestMin = 0x1000
CcaPlatformLifecycleAssemblyAndTestMax = 0x10ff
CcaPlatformLifecycleRotProvisioningMin = 0x2000
CcaPlatformLifecycleRotProvisioningMax = 0x20ff
CcaPlatformLifecycleSecuredMin = 0x3000
CcaPlatformLifecycleSecuredMax = 0x30ff
CcaPlatformLifecycleNonCcaPlatformDebugMin = 0x4000
CcaPlatformLifecycleNonCcaPlatformDebugMax = 0x40ff
CcaPlatformLifecycleRecoverableCcaPlatformDebugMin = 0x5000
CcaPlatformLifecycleRecoverableCcaPlatformDebugMax = 0x50ff
CcaPlatformLifecycleDecommissionedMin = 0x6000
CcaPlatformLifecycleDecommissionedMax = 0x60ff
)
type PsaLifeCycleState uint16

const (
Invalid = "invalid"
PsaStateUnknown PsaLifeCycleState = iota
PsaStateAssemblyAndTest
PsaStatePsaRotProvisioning
PsaStateSecured
PsaStateNonPsaRotDebug
PsaStateRecoverablePsaRotDebug
PsaStateDecommissioned

PsaStateInvalid // must be last
)

var (
CertificationReferenceP1RE = regexp.MustCompile(`^[0-9]{13}$`)
CertificationReferenceP2RE = regexp.MustCompile(`^[0-9]{13}-[0-9]{5}$`)
)
func (o PsaLifeCycleState) IsValid() bool {
return o < PsaStateInvalid
}

func checkP1P2secLifeCycle(v uint16) string {
func (o PsaLifeCycleState) String() string {
switch o {
case PsaStateUnknown:
return "unknown"
case PsaStateAssemblyAndTest:
return "assembly-and-test"
case PsaStatePsaRotProvisioning:
return "psa-rot-provisioning"
case PsaStateSecured:
return "secured"
case PsaStateNonPsaRotDebug:
return "non-psa-rot-debug"
case PsaStateRecoverablePsaRotDebug:
return "recoverable-psa-rot-debug"
case PsaStateDecommissioned:
return "decommissioned"
default:
return "invalid"
}
}

func PsaLifeCycleToState(v uint16) PsaLifeCycleState {
if v >= SecurityLifecycleUnknownMin &&
v <= SecurityLifecycleUnknownMax {
return "unknown"
return PsaStateUnknown
}

if v >= SecurityLifecycleAssemblyAndTestMin &&
v <= SecurityLifecycleAssemblyAndTestMax {
return "assembly-and-test"
return PsaStateAssemblyAndTest
}

if v >= SecurityLifecyclePsaRotProvisioningMin &&
v <= SecurityLifecyclePsaRotProvisioningMax {
return "psa-rot-provisioning"
return PsaStatePsaRotProvisioning
}

if v >= SecurityLifecycleSecuredMin &&
v <= SecurityLifecycleSecuredMax {
return "secured"
return PsaStateSecured
}

if v >= SecurityLifecycleNonPsaRotDebugMin &&
v <= SecurityLifecycleNonPsaRotDebugMax {
return "non-psa-rot-debug"
return PsaStateNonPsaRotDebug
}

if v >= SecurityLifecycleRecoverablePsaRotDebugMin &&
v <= SecurityLifecycleRecoverablePsaRotDebugMax {
return "recoverable-psa-rot-debug"
return PsaStateRecoverablePsaRotDebug
}

if v >= SecurityLifecycleDecommissionedMin &&
v <= SecurityLifecycleDecommissionedMax {
return PsaStateDecommissioned
}

return PsaStateInvalid
}

const (
CcaPlatformLifecycleUnknownMin = 0x0000
CcaPlatformLifecycleUnknownMax = 0x00ff
CcaPlatformLifecycleAssemblyAndTestMin = 0x1000
CcaPlatformLifecycleAssemblyAndTestMax = 0x10ff
CcaPlatformLifecycleRotProvisioningMin = 0x2000
CcaPlatformLifecycleRotProvisioningMax = 0x20ff
CcaPlatformLifecycleSecuredMin = 0x3000
CcaPlatformLifecycleSecuredMax = 0x30ff
CcaPlatformLifecycleNonCcaPlatformDebugMin = 0x4000
CcaPlatformLifecycleNonCcaPlatformDebugMax = 0x40ff
CcaPlatformLifecycleRecoverableCcaPlatformDebugMin = 0x5000
CcaPlatformLifecycleRecoverableCcaPlatformDebugMax = 0x50ff
CcaPlatformLifecycleDecommissionedMin = 0x6000
CcaPlatformLifecycleDecommissionedMax = 0x60ff
)

type CcaLifeCycleState uint16

const (
CcaStateUnknown CcaLifeCycleState = iota
CcaStateAssemblyAndTest
CcaStateCcaRotProvisioning
CcaStateSecured
CcaStateNonCcaPlatformDebug
CcaStateRecoverableCcaPlatformDebug
CcaStateDecommissioned

CcaStateInvalid // must be last
)

func (o CcaLifeCycleState) IsValid() bool {
return o < CcaStateInvalid
}

func (o CcaLifeCycleState) String() string {
switch o {
case CcaStateUnknown:
return "unknown"
case CcaStateAssemblyAndTest:
return "assembly-and-test"
case CcaStateCcaRotProvisioning:
return "cca-platform-rot-provisioning"
case CcaStateSecured:
return "secured"
case CcaStateNonCcaPlatformDebug:
return "non-cca-platform-rot-debug"
case CcaStateRecoverableCcaPlatformDebug:
return "recoverable-cca-platform-rot-debug"
case CcaStateDecommissioned:
return "decommissioned"
default:
return "invalid"
}
return Invalid
}

func checkCcaLifeCycle(v uint16) string {
func CcaLifeCycleToState(v uint16) CcaLifeCycleState {
if v >= CcaPlatformLifecycleUnknownMin &&
v <= CcaPlatformLifecycleUnknownMax {
return "unknown"
return CcaStateUnknown
}

if v >= CcaPlatformLifecycleAssemblyAndTestMin &&
v <= CcaPlatformLifecycleAssemblyAndTestMax {
return "assembly-and-test"
return CcaStateAssemblyAndTest
}

if v >= CcaPlatformLifecycleRotProvisioningMin &&
v <= CcaPlatformLifecycleRotProvisioningMax {
return "cca-platform-rot-provisioning"
return CcaStateCcaRotProvisioning
}

if v >= CcaPlatformLifecycleSecuredMin &&
v <= CcaPlatformLifecycleSecuredMax {
return "secured"
return CcaStateSecured
}

if v >= CcaPlatformLifecycleNonCcaPlatformDebugMin &&
v <= CcaPlatformLifecycleNonCcaPlatformDebugMax {
return "non-cca-platform-rot-debug"
return CcaStateNonCcaPlatformDebug
}

if v >= CcaPlatformLifecycleRecoverableCcaPlatformDebugMin &&
v <= CcaPlatformLifecycleRecoverableCcaPlatformDebugMax {
return "recoverable-cca-platform-rot-debug"
return CcaStateRecoverableCcaPlatformDebug
}

if v >= CcaPlatformLifecycleDecommissionedMin &&
v <= CcaPlatformLifecycleDecommissionedMax {
return "decommissioned"
return CcaStateDecommissioned
}
return Invalid
return CcaStateInvalid
}

func securityLifeCycleToString(v uint16, profile string) string {
func isValidSecurityLifeCycle(v uint16, profile string) error {
var isValid bool

switch profile {
case PsaProfile1, PsaProfile2:
return checkP1P2secLifeCycle(v)
isValid = PsaLifeCycleToState(v).IsValid()
case CcaProfile:
return checkCcaLifeCycle(v)
isValid = CcaLifeCycleToState(v).IsValid()
}
return Invalid
}

func isValidSecurityLifeCycle(v uint16, profile string) error {
// Accept any security lifecycle in the state machine, including values that
// can't produce trustable PSA evidence.
if securityLifeCycleToString(v, profile) == "invalid" {
if !isValid {
return fmt.Errorf("%w: value %d is invalid", ErrWrongClaimSyntax, v)
}

return nil
}

var (
CertificationReferenceP1RE = regexp.MustCompile(`^[0-9]{13}$`)
CertificationReferenceP2RE = regexp.MustCompile(`^[0-9]{13}-[0-9]{5}$`)
)

func isValidImplID(v []byte) error {
l := len(v)

Expand Down
99 changes: 99 additions & 0 deletions claims_common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2023 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package psatoken

import (
"testing"

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

func Test_PsaLifeCycleState(t *testing.T) {
type TestVector struct {
Val uint16
Text string
}

validTestVectors := []TestVector{
{0x0000, "unknown"},
{0x00a7, "unknown"},
{0x00ff, "unknown"},
{0x1010, "assembly-and-test"},
{0x2001, "psa-rot-provisioning"},
{0x20ff, "psa-rot-provisioning"},
{0x3000, "secured"},
{0x3090, "secured"},
{0x30ff, "secured"},
{0x4020, "non-psa-rot-debug"},
{0x5000, "recoverable-psa-rot-debug"},
{0x50af, "recoverable-psa-rot-debug"},
{0x6001, "decommissioned"},
{0x60ff, "decommissioned"},
}

for _, tv := range validTestVectors {
state := PsaLifeCycleToState(tv.Val)

assert.True(t, state.IsValid())
assert.Equal(t, tv.Text, state.String())
}

invalidTestVectors := []TestVector{
{0x1500, "doesn't matter"},
{0x6100, "won't be used"},
{0x8a47, "who cares?"},
{0xffff, "pineapples"},
}

for _, tv := range invalidTestVectors {
state := PsaLifeCycleToState(tv.Val)

assert.False(t, state.IsValid())
assert.Equal(t, "invalid", state.String())
}
}

func Test_CcaLifeCycleState(t *testing.T) {
type TestVector struct {
Val uint16
Text string
}

validTestVectors := []TestVector{
{0x0000, "unknown"},
{0x00a7, "unknown"},
{0x00ff, "unknown"},
{0x1010, "assembly-and-test"},
{0x2001, "cca-platform-rot-provisioning"},
{0x20ff, "cca-platform-rot-provisioning"},
{0x3000, "secured"},
{0x3090, "secured"},
{0x30ff, "secured"},
{0x4020, "non-cca-platform-rot-debug"},
{0x5000, "recoverable-cca-platform-rot-debug"},
{0x50af, "recoverable-cca-platform-rot-debug"},
{0x6001, "decommissioned"},
{0x60ff, "decommissioned"},
}

for _, tv := range validTestVectors {
state := CcaLifeCycleToState(tv.Val)

assert.True(t, state.IsValid())
assert.Equal(t, tv.Text, state.String())
}

invalidTestVectors := []TestVector{
{0x1500, "doesn't matter"},
{0x6100, "won't be used"},
{0x8a47, "who cares?"},
{0xffff, "pineapples"},
}

for _, tv := range invalidTestVectors {
state := CcaLifeCycleToState(tv.Val)

assert.False(t, state.IsValid())
assert.Equal(t, "invalid", state.String())
}
}

0 comments on commit 1dfd92c

Please sign in to comment.