Repository navigation
Conversation
…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>
There was a problem hiding this comment.
🟡 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, .. }) |
There was a problem hiding this comment.
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.
| 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, |
There was a problem hiding this comment.
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.
| if closing || !store.exists(&session.auth_id) { | ||
| self.sessions.remove(&sid); |
There was a problem hiding this comment.
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).
| 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); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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>
…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>
…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>
|
Thanks for the review. All five points are addressed in 6c2bd02, with a test for each:
Verified locally: |


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 thecontrol 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.INS_AUTH_OBJECT) with a persistent failed-attempt counter and a hard block at the maximum;ReadObjectAttributesreports the counter and the maximum (AN12543 table 3).are not inherited across IDs (AN12543 3.7.1.2); read-only default policy for Authentication Objects (table 17);
ALLOW_SIGN/ALLOW_VERIFYchecked for MAC operations.0x81 nnform, which the NXP host uses for 128-255 byte values.SE050_SIM_TRACE=1logs 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