Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions SE050Sim/se050-sim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ embedded-hal = "0.2"
crc16 = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
p192 = { version = "0.13", features = ["ecdsa", "arithmetic"] }
elliptic-curve = { version = "0.13", features = ["ecdh"] }
p224 = { version = "0.13", features = ["ecdsa", "ecdh"] }
p256 = { version = "0.13", features = ["ecdsa", "ecdh"] }
p384 = { version = "0.13", features = ["ecdsa", "ecdh"] }
p521 = { version = "0.13", features = ["ecdsa", "ecdh"] }
ecdsa = { version = "0.16", features = ["signing", "verifying", "der"] }
ed25519-dalek = { version = "2", features = ["rand_core"] }
x25519-dalek = { version = "2", features = ["static_secrets"] }
Expand All @@ -21,6 +24,8 @@ cbc = { version = "0.1", features = ["alloc"] }
rsa = { version = "0.9", features = ["sha2", "hazmat"] }
sha1 = "0.10"
sha2 = "0.10"
hmac = "0.12"
cmac = "0.7"
signature = "2.2"
rand = "0.8"
hex = "0.4"
Expand Down
4 changes: 4 additions & 0 deletions SE050Sim/se050-sim/src/apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,12 @@ pub const P2_DELETE_ALL: u8 = 0x2A;
pub const P2_ID: u8 = 0x36;
pub const P2_ENCRYPT_ONESHOT: u8 = 0x37;
pub const P2_DECRYPT_ONESHOT: u8 = 0x38;
pub const P2_PARAM: u8 = 0x40;
pub const P2_ENCRYPT_INIT: u8 = 0x42;
pub const P2_DECRYPT_INIT: u8 = 0x43;
pub const P2_MAC_VALIDATE: u8 = 0x44;
pub const P2_GENERATE_ONESHOT: u8 = 0x45;
pub const P2_VALIDATE_ONESHOT: u8 = 0x46;
pub const P2_CRYPTO_LIST: u8 = 0x47;
pub const P2_RAW: u8 = 0x4F;
pub const P2_RANDOM: u8 = 0x49;
Expand Down
104 changes: 104 additions & 0 deletions SE050Sim/se050-sim/src/applet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* applet.rs
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of SE050Sim.
*
* SE050Sim is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* SE050Sim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/

/// Applet personality selection.
///
/// The simulator can present itself as either of the two applet
/// generations that were bench-characterized on real silicon (August
/// 2026, see SE050Sim/HARDWARE_VALIDATION.md): an SE050C running applet
/// 3.1.1 or an SE051 running applet 7.2.0. Almost all behavior is
/// identical between the two; the differences the simulator models are:
///
/// * SELECT / GetVersion version bytes.
/// * GetFreeMemory response width (2 bytes on 3.x, 4 bytes on 7.2) and
/// the reported per-type values.
/// * GetRandom maximum request size (880 bytes on the SE050C, 1018 on
/// the SE051).
/// * ReadType secure-object type codes for EC keys (generic 0x01/0x03
/// on 3.x, curve-specific on 7.2).
/// * CreateECCurve on an already existing curve: applet 7.2 refuses
/// with SW 0x6985; applet 3.1.1 returns 0x9000 and silently resets
/// the curve to a parameter-less state (subsequent key generation on
/// it fails 0x6985 until the parameters are uploaded again).

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AppletVersion {
/// SE050C, applet 3.1.1 (ATR historical bytes "JCOP4").
V3_1_1,
/// SE051, applet 7.2.0 (ATR historical bytes "eSE051"). Default.
V7_2_0,
}

impl AppletVersion {
/// Read the personality from the SE050_SIM_APPLET environment
/// variable. Accepts "3", "3.1.1" (SE050C) and "7", "7.2", "7.2.0"
/// (SE051). Unset or unrecognized values select 7.2.0, matching the
/// version the simulator has always advertised.
pub fn from_env() -> Self {
match std::env::var("SE050_SIM_APPLET") {
Ok(v) if v.starts_with('3') => AppletVersion::V3_1_1,
_ => AppletVersion::V7_2_0,
}
}

/// 7-byte version blob returned by SELECT and GetVersion:
/// major, minor, patch, appletConfig (2B), secureBox (2B).
/// Captured from real parts: SE050C applet 3.1.1 returns
/// 03 01 01 6f ff 01 0b, SE051 applet 7.2.0 returns
/// 07 02 00 3f ff ff ff.
pub fn version_bytes(self) -> [u8; 7] {
match self {
AppletVersion::V3_1_1 => [0x03, 0x01, 0x01, 0x6F, 0xFF, 0x01, 0x0B],
AppletVersion::V7_2_0 => [0x07, 0x02, 0x00, 0x3F, 0xFF, 0xFF, 0xFF],
}
}

/// Largest GetRandom request the applet serves; one byte more
/// returns SW 0x6985 (bench-measured: 880 on SE050C 3.1.1, 1018 on
/// SE051 7.2.0).
pub fn get_random_max(self) -> usize {
match self {
AppletVersion::V3_1_1 => 880,
AppletVersion::V7_2_0 => 1018,
}
}

/// GetFreeMemory reply for a memory type, as measured on the bench
/// parts. Applet 3.x replies with a 2-byte value, 7.2 with 4 bytes
/// (the v04.07.01 middleware parses U16 vs U32 accordingly).
pub fn free_memory_bytes(self, memory_type: u8) -> Option<Vec<u8>> {
let (persistent, transient_reset, transient_deselect): (u32, u32, u32) =
match self {
AppletVersion::V3_1_1 => (31304, 575, 560),
AppletVersion::V7_2_0 => (21000, 605, 592),
};
let value = match memory_type {
0x01 => persistent,
0x02 => transient_reset,
0x03 => transient_deselect,
_ => return None,
};
Some(match self {
AppletVersion::V3_1_1 => (value as u16).to_be_bytes().to_vec(),
AppletVersion::V7_2_0 => value.to_be_bytes().to_vec(),
})
}
}
52 changes: 31 additions & 21 deletions SE050Sim/se050-sim/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@
/// based on CLA, INS (masked with 0x1F), P1, and P2.

use crate::apdu::*;
use crate::applet::AppletVersion;
use crate::handlers;
use crate::object_store::ObjectStore;

pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
// Applet personality (SE050_SIM_APPLET env var; defaults to the
// SE051 / applet 7.2.0 the simulator has always advertised).
let version = AppletVersion::from_env();
let v7 = version == AppletVersion::V7_2_0;

// SELECT command (CLA=0x00, INS=0xA4)
if apdu.cla == 0x00 && apdu.ins == 0xA4 {
return handlers::session::handle_select(apdu, store);
return handlers::session::handle_select(apdu, store, version);
}

// All other SE050 proprietary commands use CLA=0x80 or 0x84
Expand All @@ -47,10 +53,11 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
P1_AES => handlers::aes::handle_write_aes_key(apdu, store),
P1_HMAC => handlers::aes::handle_write_hmac_key(apdu, store),
P1_CRYPTO_OBJ => handlers::crypto_obj::handle_create(apdu, store),
P1_CURVE => {
// CreateECCurve / SetECCurveParam: our crypto libs have curves built-in
ApduResponse::success()
}
P1_CURVE => match apdu.p2 {
P2_CREATE => handlers::curve::handle_create(apdu, store, version),
P2_PARAM => handlers::curve::handle_set_param(apdu, store),
_ => ApduResponse::error(SW_WRONG_P1P2),
},
P1_BINARY | P1_USERID | P1_COUNTER => {
handlers::object_mgmt::handle_write(apdu, store)
}
Expand Down Expand Up @@ -89,7 +96,7 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
ApduResponse::error(SW_CONDITIONS_NOT_SATISFIED)
}
}
_ => ApduResponse::error(SW_FILE_NOT_FOUND),
_ => ApduResponse::error(SW_CONDITIONS_NOT_SATISFIED),
}
}
(P1_CRYPTO_OBJ, _) => handlers::crypto_obj::handle_list(apdu, store),
Expand All @@ -105,22 +112,12 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
&[crate::tlv::Tlv::new(crate::tlv::TAG_1, &[cid])]),
None => ApduResponse::error(SW_CONDITIONS_NOT_SATISFIED),
},
None => ApduResponse::error(SW_FILE_NOT_FOUND),
None => ApduResponse::error(SW_CONDITIONS_NOT_SATISFIED),
}
}
(P1_CURVE, _) => {
// ReadECCurveList: return 17-byte list marking all NIST curves as SET.
// Index = curve_id - 1, value 0x01 = SET, 0x00 = NOT_SET.
let mut curve_list = [0u8; 0x11]; // kSE05x_ECCurve_Total_Weierstrass_Curves
curve_list[0x00] = 0x01; // NIST_P192
curve_list[0x01] = 0x01; // NIST_P224
curve_list[0x02] = 0x01; // NIST_P256
curve_list[0x03] = 0x01; // NIST_P384
curve_list[0x04] = 0x01; // NIST_P521
ApduResponse::success_with_tlvs(
&[crate::tlv::Tlv::new(crate::tlv::TAG_1, &curve_list)])
}
_ => handlers::object_mgmt::handle_read(apdu, store),
(P1_CURVE, P2_LIST) => handlers::curve::handle_list(store),
(P1_CURVE, _) => ApduResponse::error(SW_WRONG_P1P2),
_ => handlers::object_mgmt::handle_read(apdu, store, v7),
},

INS_CRYPTO => match (cred_type, apdu.p2) {
Expand Down Expand Up @@ -163,6 +160,15 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
(P1_DEFAULT, P2_UPDATE) => handlers::digest::handle_digest_update(apdu, store),
(P1_DEFAULT, P2_FINAL) => handlers::digest::handle_digest_final(apdu, store),

// MAC (HMAC / AES-CMAC): one-shot and multi-step.
// MACInit uses P2 = Generate (0x03) / Validate (0x44).
(P1_MAC, P2_GENERATE_ONESHOT) => handlers::mac::handle_oneshot(apdu, store, false),
(P1_MAC, P2_VALIDATE_ONESHOT) => handlers::mac::handle_oneshot(apdu, store, true),
(P1_MAC, P2_GENERATE) => handlers::mac::handle_init(apdu, store, false),
(P1_MAC, P2_MAC_VALIDATE) => handlers::mac::handle_init(apdu, store, true),
(P1_MAC, P2_UPDATE) => handlers::mac::handle_update(apdu, store),
(P1_MAC, P2_FINAL) => handlers::mac::handle_final(apdu, store),

_ => ApduResponse::error(SW_WRONG_P1P2),
},

Expand All @@ -172,9 +178,13 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
(P1_CRYPTO_OBJ, P2_DELETE_OBJECT) => {
handlers::crypto_obj::handle_delete(apdu, store)
}
// EC curve deletion
(P1_CURVE, P2_DELETE_OBJECT) => {
handlers::curve::handle_delete(apdu, store)
}
// General management
(_, P2_VERSION) | (_, P2_MEMORY) | (_, P2_RANDOM) | (_, P2_DELETE_ALL) => {
handlers::management::handle(apdu, store)
handlers::management::handle(apdu, store, version)
}
(_, P2_EXIST) | (_, P2_DELETE_OBJECT) => {
handlers::object_mgmt::handle_mgmt(apdu, store)
Expand Down
Loading
Loading