Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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