-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Is your feature request related to a problem? Please describe
RFC 9783 defines a profile tag:psacertified.org,2023:psa#tfm, which is not supported by this package.
Thus, the following PSA Attestation Token is rejected by Veraison.
NOTE: the psa-boot-seed label was also updated from 2397 to 268 in the v21 draft.
{
/ eat_profile / 265 : "tag:psacertified.org,2023:psa#tfm",
/ psa-client-id / 2394 : 1,
/ psa-lifecycle / 2395 : 12288,
/ psa-implementation-id / 2396 : 'acme-implementation-id-000000001',
/ psa-boot-seed / 268 : h'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
/ psa-software-components / 2399 : [ {
/ measurement-type / 1 : "BL",
/ measurement-value / 2 : h'87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7',
/ version / 4 : "2.1.0",
/ signer-id / 5 : h'acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b'
}, {
/ measurement-type / 1 : "PRoT",
/ measurement-value / 2 : h'0263829989b6fd954f72baaf2fc64bc2e2f01d692d4de72986ea808f6e99813f',
/ version / 4 : "1.3.5",
/ signer-id / 5 : h'acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b'
}, {
/ measurement-type / 1 : "ARoT",
/ measurement-value / 2 : h'a3a5e715f0cc574a73c3f9bebb6bc24f32ffd5b67b387244c2c909da779a1478',
/ version / 4 : "0.1.4",
/ signer-id / 5 : h'acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b'
} ],
/ psa-nonce / 10 : h'414a7c174141b3d0e9a1d28af31520f0d42299feac4007ded89d68ae6cd92f19',
/ psa-instance-id / 256 : h'01ceebae7b8927a3227e5303cf5e0f1f7b34bb542ad7250ac03fbcde36ec2f1508',
/ psa-verification-service-indicator / 2400 : "https://psa-verifier.org"
}
If possible, describe a solution
Adding claims_psacertified2023.go ?
// Copyright 2021-2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package psatoken
import (
_ "crypto/sha256" // used hash algorithms need to be imported explicitly
"encoding/json"
"fmt"
"github.com/veraison/eat"
)
const ProfilePSAC2023Name = "tag:psacertified.org,2023:psa#tfm"
// ProfilePSAC2023 provides the IClaims implementation associated with tag:psacertified.org,2023:psa#tfm
type ProfilePSAC2023 struct{}
func (o ProfilePSAC2023) GetName() string {
return ProfilePSAC2023Name
}
func (o ProfilePSAC2023) GetClaims() IClaims {
return newPSAC2023()
}
// PSAC2023 are associated with profile "tag:psacertified.org,2023:psa#tfm"
// See https://datatracker.ietf.org/doc/html/rfc9783
type PSAC2023 struct {
Profile *eat.Profile `cbor:"265,keyasint" json:"eat-profile"`
ClientID *int32 `cbor:"2394,keyasint" json:"psa-client-id"`
SecurityLifeCycle *uint16 `cbor:"2395,keyasint" json:"psa-security-lifecycle"`
ImplID *[]byte `cbor:"2396,keyasint" json:"psa-implementation-id"`
BootSeed *[]byte `cbor:"268,keyasint,omitempty" json:"psa-boot-seed,omitempty"`
CertificationReference *string `cbor:"2398,keyasint,omitempty" json:"psa-certification-reference,omitempty"`
SwComponents ISwComponents `cbor:"2399,keyasint" json:"psa-software-components"`
Nonce *eat.Nonce `cbor:"10,keyasint" json:"psa-nonce"`
InstID *eat.UEID `cbor:"256,keyasint" json:"psa-instance-id"`
VSI *string `cbor:"2400,keyasint,omitempty" json:"psa-verification-service-indicator,omitempty"`
// CanonicalProfile contains the "correct" profile name associated with
// this IClaims implementation (e.g. "tag:psacertified.org,2023:psa#tfm" for
// PSAC2023). The reason this is a field rather than a global constant
// is so that derived profiles can embed this struct and rely on its
// existing validation methods.
CanonicalProfile string `cbor:"-" json:"-"`
}
func newPSAC2023() IClaims {
p := eat.Profile{}
if err := p.Set(ProfilePSAC2023Name); err != nil {
// should never get here as using known good constant as input
panic(err)
}
return &PSAC2023{
Profile: &p,
SwComponents: &SwComponents[*SwComponent]{},
CanonicalProfile: ProfilePSAC2023Name,
}
}
// Semantic validation
func (c PSAC2023) Validate() error { //nolint:gocritic
return ValidateClaims(&c)
}
func (c *PSAC2023) SetClientID(v int32) error {
// any int32 value is acceptable
c.ClientID = &v
return nil
}
func (c *PSAC2023) SetSecurityLifeCycle(v uint16) error {
if err := ValidateSecurityLifeCycle(v); err != nil {
return err
}
c.SecurityLifeCycle = &v
return nil
}
func (c *PSAC2023) SetImplID(v []byte) error {
if err := ValidateImplID(v); err != nil {
return err
}
c.ImplID = &v
return nil
}
func (c *PSAC2023) SetBootSeed(v []byte) error {
l := len(v)
if l < 8 || l > 32 {
return fmt.Errorf(
"%w: invalid length %d (MUST be between 8 and 32 bytes)",
ErrWrongSyntax, l,
)
}
c.BootSeed = &v
return nil
}
func (c *PSAC2023) SetCertificationReference(v string) error {
if !CertificationReferenceP1RE.MatchString(v) &&
!CertificationReferenceP2RE.MatchString(v) {
return fmt.Errorf(
"%w: MUST be in EAN-13 or EAN-13+5 format",
ErrWrongSyntax,
)
}
c.CertificationReference = &v
return nil
}
func (c *PSAC2023) SetSoftwareComponents(scs []ISwComponent) error {
if c.SwComponents == nil {
c.SwComponents = &SwComponents[*SwComponent]{}
}
return c.SwComponents.Replace(scs)
}
func (c *PSAC2023) SetNonce(v []byte) error {
if err := ValidatePSAHashType(v); err != nil {
return err
}
n := eat.Nonce{}
if err := n.Add(v); err != nil {
return err
}
c.Nonce = &n
return nil
}
func (c *PSAC2023) SetInstID(v []byte) error {
if err := ValidateInstID(v); err != nil {
return err
}
ueid := eat.UEID(v)
c.InstID = &ueid
return nil
}
func (c *PSAC2023) SetVSI(v string) error {
if err := ValidateVSI(v); err != nil {
return err
}
c.VSI = &v
return nil
}
// Codecs
// this type alias is used to prevent infinite recursion during marshaling.
type psaC2023 PSAC2023
func (c *PSAC2023) UnmarshalCBOR(buf []byte) error {
c.Profile = nil // clear profile to make sure we take it from buf
return dm.Unmarshal(buf, (*psaC2023)(c))
}
func (c *PSAC2023) UnmarshalJSON(buf []byte) error {
c.Profile = nil // clear profile to make sure we take it from buf
return json.Unmarshal(buf, (*psaC2023)(c))
}
// Getters return a validated value or an error
// After successful call to Validate(), getters of mandatory claims are assured
// to never fail. Getters of optional claim may still fail with
// ErrOptionalClaimMissing in case the claim is not present.
func (c PSAC2023) GetProfile() (string, error) { //nolint:gocritic
if c.Profile == nil {
return "", ErrMandatoryClaimMissing
}
profileString, err := c.Profile.Get()
if err != nil {
return "", err
}
if profileString != c.CanonicalProfile {
return "", fmt.Errorf("%w: expecting %q, got %q",
ErrWrongProfile, c.CanonicalProfile, profileString)
}
return profileString, nil
}
func (c PSAC2023) GetClientID() (int32, error) { //nolint:gocritic
if c.ClientID == nil {
return 0, ErrMandatoryClaimMissing
}
return *c.ClientID, nil
}
func (c PSAC2023) GetSecurityLifeCycle() (uint16, error) { //nolint:gocritic
if c.SecurityLifeCycle == nil {
return 0, ErrMandatoryClaimMissing
}
if err := ValidateSecurityLifeCycle(*c.SecurityLifeCycle); err != nil {
return 0, err
}
return *c.SecurityLifeCycle, nil
}
func (c PSAC2023) GetImplID() ([]byte, error) { //nolint:gocritic
if c.ImplID == nil {
return nil, ErrMandatoryClaimMissing
}
if err := ValidateImplID(*c.ImplID); err != nil {
return nil, err
}
return *c.ImplID, nil
}
func (c PSAC2023) GetBootSeed() ([]byte, error) { //nolint:gocritic
if c.BootSeed == nil {
return nil, ErrOptionalClaimMissing
}
l := len(*c.BootSeed)
if l < 8 || l > 32 {
return nil, fmt.Errorf(
"%w: invalid length %d (MUST be between 8 and 32 bytes)",
ErrWrongSyntax, l,
)
}
return *c.BootSeed, nil
}
func (c PSAC2023) GetCertificationReference() (string, error) { //nolint:gocritic
if c.CertificationReference == nil {
return "", ErrOptionalClaimMissing
}
if !CertificationReferenceP2RE.MatchString(*c.CertificationReference) {
return "", fmt.Errorf(
"%w: MUST be in EAN-13+5 format",
ErrWrongSyntax,
)
}
return *c.CertificationReference, nil
}
func (c PSAC2023) GetSoftwareComponents() ([]ISwComponent, error) { //nolint:gocritic
if c.SwComponents == nil || c.SwComponents.IsEmpty() {
return nil, fmt.Errorf("%w (MUST have at least one sw component)",
ErrMandatoryClaimMissing)
}
return c.SwComponents.Values()
}
func (c PSAC2023) GetNonce() ([]byte, error) { //nolint:gocritic
v := c.Nonce
if v == nil {
return nil, ErrMandatoryClaimMissing
}
l := v.Len()
if l != 1 {
return nil, fmt.Errorf("%w: got %d nonces, want 1", ErrWrongSyntax, l)
}
n := v.GetI(0)
if err := ValidateNonce(n); err != nil {
return nil, err
}
return n, nil
}
func (c PSAC2023) GetInstID() ([]byte, error) { //nolint:gocritic
v := c.InstID
if v == nil {
return nil, ErrMandatoryClaimMissing
}
if err := ValidateInstID(*v); err != nil {
return nil, err
}
return *v, nil
}
func (c PSAC2023) GetVSI() (string, error) { //nolint:gocritic
if c.VSI == nil {
return "", ErrOptionalClaimMissing
}
if err := ValidateVSI(*c.VSI); err != nil {
return "", err
}
return *c.VSI, nil
}
func init() {
if err := RegisterProfile(ProfilePSAC2023{}); err != nil {
panic(err)
}
}(Not tested yet, just replaced some words.)
Describe alternatives you have considered
Fix the label in claims_p2.go
https://github.com/veraison/psatoken/compare/main...kentakayama:psatoken:fix-boot-seed?expand=1
Additional context
Summary of changes across the drafts and RFC 9783:
| Version | Profile | BootSeed label |
|---|---|---|
| v07 | PSA_IOT_PROFILE_1 |
-75004 |
| v13 | http://arm.com/psa/2.0.0 |
2397 |
| v14 | tag:psacertified.org,2023:psa#tfm |
2397 |
| v21 | tag:psacertified.org,2023:psa#tfm |
268 |
| RFC 9783 | tag:psacertified.org,2023:psa#tfm |
268 |
thomas-fossati
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request