Skip to content

SE050Sim: ECKey applet sessions, Authentication Objects, per-owner policies - #24

Open
mcamposv wants to merge 2 commits into
wolfSSL:mainfrom
shosoin:eckey-sessions
Open

mcamposv wants to merge 2 commits into
wolfSSL:mainfrom
shosoin:eckey-sessions

Conversation

@mcamposv

@mcamposv mcamposv commented Nov 1, 2026

Copy link
Copy Markdown

Adds applet sessions with ECKey authentication, following AN12543 and the host side in Plug & Trust
(fsl_sss_se05x_eckey.c, se05x_tlv.c):

  • CreateSession / ProcessSessionCmd / CloseSession; ECKeySessionInternalAuthenticate (signature over the
    control reference template and the host's ephemeral key, master key from ECDH with the SE's ECKey session key pair
    0x7FFF0201), then the session's SCP03 channel, also when nested inside Platform SCP03.
  • Authentication Objects (INS_AUTH_OBJECT) with a persistent failed-attempt counter and a hard block at the maximum;
    ReadObjectAttributes reports the counter and the maximum (AN12543 table 3).
  • Policies evaluated for the session owner: the access rule of the session's Authentication Object ID applies, rules
    are not inherited across IDs (AN12543 3.7.1.2); read-only default policy for Authentication Objects (table 17);
    ALLOW_SIGN / ALLOW_VERIFY checked for MAC operations.
  • TLV lengths in the 0x81 nn form, which the NXP host uses for 128-255 byte values.
  • SE050_SIM_TRACE=1 logs each command after SCP03 unwrapping, with its status word (handy when debugging a host).

Unit test of the whole flow: creation over an authenticated channel, counting, blocking, policy scoping inside and
outside the session, close. All existing tests pass.

🤖 Generated with Claude Code

…licies

- CreateSession / ProcessSessionCmd / CloseSession (AN12543 3.6.2, 4.5.1) with
  ECKey authentication (ECKeySessionInternalAuthenticate, master key from
  ECDH with the SE's ECKey session key pair 0x7FFF0201) and the session's
  SCP03 channel, also nested inside Platform SCP03.
- Authentication Objects (INS_AUTH_OBJECT) with a persistent failed-attempt
  counter and a hard block at the maximum; ReadObjectAttributes reports it.
- Policies evaluated for the session owner (rules are not inherited across
  Authentication Object IDs), read-only default for Authentication Objects,
  ALLOW_SIGN / ALLOW_VERIFY checked for MAC operations.
- TLV lengths in the 0x81 nn form, as the NXP host encodes them.
- SE050_SIM_TRACE=1 logs every command after SCP03 unwrapping.

Unit test of the whole flow (counting, blocking, policy scoping, close).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical authorization gaps allow ordinary callers to recover session secrets and authenticate without possessing the authentication object's private key.

5 open issues
What changed in this PR

Adds ECKey-authenticated applet sessions to SE050Sim, integrating session-specific SCP03 channels and owner-scoped object policies with the existing transport and object store.

Changes:

  • Implements session creation, authentication, protected commands, and closure, with flow tests.
  • Persists Authentication Object retry counters and applies owner-specific policies to MAC operations.
  • Adds host-compatible TLV length parsing and optional command tracing.
File Description
SE050Sim/​se050-sim/​src/​tlv.rs Accepts 0x81 length encoding.
SE050Sim/​se050-sim/​src/​t1.rs Integrates applet sessions into transport dispatch.
SE050Sim/​se050-sim/​src/​scp03/​mod.rs Initializes channels from derived session keys.
SE050Sim/​se050-sim/​src/​policy.rs Adds owner-specific access-rule lookup.
SE050Sim/​se050-sim/​src/​object_store/​types.rs Defines persistent authentication retry state.
SE050Sim/​se050-sim/​src/​object_store/​mod.rs Stores authentication state, session ownership, and the agreement key.
SE050Sim/​se050-sim/​src/​lib.rs Exports the applet-session module.
SE050Sim/​se050-sim/​src/​handlers/​object_mgmt.rs Reports authentication counters in object attributes.
SE050Sim/​se050-sim/​src/​handlers/​mac.rs Checks signing and verification permissions.
SE050Sim/​se050-sim/​src/​handlers/​ec.rs Marks EC Authentication Objects and records retry limits.
SE050Sim/​se050-sim/​src/​applet_session.rs Implements ECKey session authentication, lifecycle, tracing, and tests.

Review effort: Balanced

let is_eckey = matches!(
store.get(&auth_id),
Some(SecureObject::ECPublicKey { curve: ECCurve::NistP256, .. })
| Some(SecureObject::ECKeyPair { curve: ECCurve::NistP256, .. })

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c2bd02. EC signing now requires POLICY_OBJ_ALLOW_SIGN and ECDH POLICY_OBJ_ALLOW_KA for the current session owner, as MAC generation already did. A key-pair Authentication Object without a policy is read only (applet default), so it can no longer sign the challenge for a caller without the private key. Objects without a policy keep the old behaviour. Covered by key_pair_authentication_object_does_not_sign_for_others.

Comment on lines +213 to +216
self.objects.insert(RESERVED_ID_ECKEY_SESSION, SecureObject::ECKeyPair {
curve: crate::object_store::types::ECCurve::NistP256,
private_key: sk.to_bytes().to_vec(),
public_key: pk,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c2bd02. Applet-reserved objects are now read only at dispatch: reads, attributes and existence checks work (the host reads the public key during session setup); any other command naming one of them in its TLVs (ECDH, ECDH into it, sign, write, delete) gets 0x6985, including inside a session and before the key is first provisioned. Only the session flow uses the private key. Covered by reserved_session_key_is_read_only.

Comment on lines +212 to +213
if closing || !store.exists(&session.auth_id) {
self.sessions.remove(&sid);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c2bd02. Authentication Objects now carry a random instance tag set at creation, and each session is bound to it. Before authentication or dispatch, a session whose object is gone or was recreated under the same ID is refused (0x6985) and dropped, no matter which session or connection deleted it. Covered by sessions_end_when_their_authentication_object_is_deleted (authenticated and half-open sessions, delete then recreate).

Comment thread SE050Sim/se050-sim/src/handlers/ec.rs Outdated
Comment on lines +153 to +156
let max = tlv::find_tlv(&tlvs, crate::tlv::TAG_MAX_ATTEMPTS)
.filter(|t| t.value.len() == 2)
.map(|t| u16::from_be_bytes([t.value[0], t.value[1]]) & 0x7FFF)
.unwrap_or(0);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c2bd02: a present TAG_MAX_ATTEMPTS must be two bytes and at most 0x7FFF, otherwise 0x6A80 before anything is stored; absent still means unlimited. ECKey Authentication Objects are also limited to NIST P-256, the only curve sessions accept. Covered by authentication_object_parameters_are_validated.

// SELECT terminates any secure channel, then proceeds plain.
if cla == 0x00 && ins == 0xA4 {
self.scp03.reset();
self.applet_sessions.reset();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c2bd02: handle_s_request clears applet sessions on both InterfaceSoftReset and resync. Covered by the transport-level test test_reset_and_resync_end_applet_sessions (CreateSession over T=1, reset, then the old session ID is refused; a control case without reset still works).

…sion lifetime

Fixes from review feedback on the ECKey applet sessions.

The SE's ECKey session key pair (0x7FFF0201) was an ordinary object: a
plain caller could run ECDH with it against a host's ephemeral key and
derive that session's keys, or sign, replace or delete it. Applet-reserved
objects are now read only at dispatch: reads, attributes and existence
checks work (hosts read the public key during session setup), any other
command naming one of them gets 0x6985. Only the applet's own session flow
uses the private key.

EC signing and ECDH ignored object policies, so a key-pair Authentication
Object could sign the authentication challenge for a caller without the
private key. They now require POLICY_OBJ_ALLOW_SIGN and
POLICY_OBJ_ALLOW_KA for the current session owner, as MAC generation
already did. Objects without a policy keep the applet default (everything
for ordinary objects, read only for Authentication Objects).

A session only noticed that its Authentication Object was gone after
running its next command, and a new object created under the same ID kept
old sessions alive. Authentication Objects now carry a random instance
tag; a session is tied to it and is refused (and dropped) before
authentication or dispatch once the object is deleted or replaced, by
whichever session.

A malformed TAG_MAX_ATTEMPTS (wrong length, or above 0x7FFF) silently
became "unlimited". It is now refused with 0x6A80 before anything is
stored; an absent tag still means unlimited. ECKey Authentication Objects
are also restricted to NIST P-256, the only curve sessions accept, so a
P-384 one is refused instead of being created unusable.

Applet sessions are now cleared by T=1 interface soft reset and resync,
not only by SELECT, as a chip reset ends them.

Tests: reserved key (ECDH, ECDH into it, sign, delete, replace; public
read still works; also inside a session), key-pair Authentication Object
refused for sign and ECDH, sessions ending on delete and recreate of
their object, Authentication Object parameter validation, and a T=1
transport test for both resets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mcamposv added a commit to shosoin/simulators that referenced this pull request Nov 2, 2026
…ttested reads in sessions

Fixes from review feedback specific to the UserID / AESKey sessions (the
ones shared with the ECKey sessions and the store writer are in the two
previous commits, also applied to wolfSSL#23 and wolfSSL#24).

The attempt limit was checked at INITIALIZE UPDATE only. With a maximum
of one, a host could start two AESKey handshakes, fail the first to block
the object, then complete the second, which also reset the counter. A
pending handshake now rechecks the block at EXTERNAL AUTHENTICATE and is
refused without touching the counter.

AES encryption and decryption ignored object policies, so a new AESKey
Authentication Object without a policy (read only by default) still
encrypted and decrypted in the default session. One-shot and CipherInit
now require POLICY_OBJ_ALLOW_ENC / POLICY_OBJ_ALLOW_DEC for the current
session owner; update and final use the key bound at init.

The SE's attestation key pair (0xF0000012) is now applet-reserved like
the ECKey session key: readable, but not usable for generic signing, nor
replaceable or deletable.

An attested ReadObject inside an authenticated session went to plain
dispatch, which masks INS_ATTEST, and came back without attestation
fields. It now goes through the attestation handler, with the session's
Authentication Object as the policy owner.

Updating an existing AESKey Authentication Object with a 24 or 32 byte key
succeeded and left it unusable for sessions. Authentication Objects stay
AES-128: the length is checked before anything is stored, on creation and
on update. TAG_MAX_ATTEMPTS is parsed by one helper for all three types:
a malformed or out-of-range value is refused with 0x6A80 instead of
meaning "unlimited" (UserID: at most 255).

Tests: two pending AESKey handshakes against a one-attempt object,
cipher policies (one-shot and init, both directions), AES-128 on update,
malformed maximum for AESKey and UserID, attested read in a UserID
session, and the reserved attestation key. The NXP Plug & Trust SDK test
passes 32/32 in all seven session modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mcamposv added a commit to shosoin/simulators that referenced this pull request Nov 2, 2026
…ttested reads in sessions

Fixes from review feedback specific to the UserID / AESKey sessions (the
ones shared with the ECKey sessions and the store writer are in the two
previous commits, also applied to wolfSSL#23 and wolfSSL#24).

The attempt limit was checked at INITIALIZE UPDATE only. With a maximum
of one, a host could start two AESKey handshakes, fail the first to block
the object, then complete the second, which also reset the counter. A
pending handshake now rechecks the block at EXTERNAL AUTHENTICATE and is
refused without touching the counter.

AES encryption and decryption ignored object policies, so a new AESKey
Authentication Object without a policy (read only by default) still
encrypted and decrypted in the default session. One-shot and CipherInit
now require POLICY_OBJ_ALLOW_ENC / POLICY_OBJ_ALLOW_DEC for the current
session owner; update and final use the key bound at init.

The SE's attestation key pair (0xF0000012) is now applet-reserved like
the ECKey session key: readable, but not usable for generic signing, nor
replaceable or deletable.

An attested ReadObject inside an authenticated session went to plain
dispatch, which masks INS_ATTEST, and came back without attestation
fields. It now goes through the attestation handler, with the session's
Authentication Object as the policy owner.

Updating an existing AESKey Authentication Object with a 24 or 32 byte key
succeeded and left it unusable for sessions. Authentication Objects stay
AES-128: the length is checked before anything is stored, on creation and
on update. TAG_MAX_ATTEMPTS is parsed by one helper for all three types:
a malformed or out-of-range value is refused with 0x6A80 instead of
meaning "unlimited" (UserID: at most 255).

Tests: two pending AESKey handshakes against a one-attempt object,
cipher policies (one-shot and init, both directions), AES-128 on update,
malformed maximum for AESKey and UserID, attested read in a UserID
session, and the reserved attestation key. The NXP Plug & Trust SDK test
passes 32/32 in all seven session modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mcamposv

mcamposv commented Nov 2, 2026

Copy link
Copy Markdown
Author

Thanks for the review. All five points are addressed in 6c2bd02, with a test for each:

  • the reserved ECKey session key is read-only at dispatch
  • EC sign and ECDH check ALLOW_SIGN / ALLOW_KA
  • sessions are bound to their Authentication Object instance
  • TAG_MAX_ATTEMPTS is validated and Authentication Objects are P-256 only
  • applet sessions end on T=1 soft reset and resync (transport-level test)

Verified locally: cargo test (unit, integration and SCP03 suites), the NXP Plug & Trust SDK test 32/32 in all seven session modes (None, UserID, AESKey, ECKey, and the three with Platform SCP03), and the wolfCrypt test, which gives the same result as on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants