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
12 changes: 9 additions & 3 deletions SE050Sim/Dockerfile.wolfcrypt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ RUN ln -sf /app/simw-top/build/sss/ex/src/libex_common.a \

# ---- Pass-B: rebuild wolfSSL with --with-se050 ----
# Links against the patched SDK we just built. Install overwrites Pass-A.
# WOLFSSL_EXTRA_CFLAGS lets CI build variants (e.g.
# -DWOLFSSL_SE050_ONLY_KEY_ID) without editing this file. It must reach both
# the library build and the test.c/main.c compiles below, since the macro
# gates test coverage in wolfcrypt/test/test.c as well.
ARG WOLFSSL_EXTRA_CFLAGS=""
WORKDIR /app/wolfssl
RUN make clean 2>&1 && \
./configure \
Expand All @@ -125,7 +130,8 @@ RUN make clean 2>&1 && \
--disable-examples \
--enable-crypttests \
CFLAGS="-DWOLFSSL_SE050_INIT -DWOLFSSL_SE050_NO_TRNG -DSIZEOF_LONG_LONG=8 \
-DECC_USER_CURVES -DHAVE_ECC224 -DHAVE_ECC256 -DHAVE_ECC384" \
-DECC_USER_CURVES -DHAVE_ECC224 -DHAVE_ECC256 -DHAVE_ECC384 \
${WOLFSSL_EXTRA_CFLAGS}" \
LDFLAGS="-L/app/simw-top/build" \
2>&1 && \
make -j$(nproc) 2>&1 && \
Expand All @@ -149,10 +155,10 @@ ENV SDK_INCS="-I/app/wolfssl \
-I/app/simw-top/build"

RUN gcc -c -o /tmp/test.o /app/wolfssl/wolfcrypt/test/test.c \
-DNO_MAIN_DRIVER -DHAVE_CONFIG_H $SDK_INCS 2>&1
-DNO_MAIN_DRIVER -DHAVE_CONFIG_H ${WOLFSSL_EXTRA_CFLAGS} $SDK_INCS 2>&1

RUN gcc -c -o /tmp/main.o /app/wolfcrypt_test_main.c \
-DHAVE_CONFIG_H $SDK_INCS 2>&1
-DHAVE_CONFIG_H ${WOLFSSL_EXTRA_CFLAGS} $SDK_INCS 2>&1

RUN gcc -o /app/wolfcrypt_se050_test /tmp/main.o /tmp/test.o \
-L/usr/local/lib \
Expand Down
30 changes: 26 additions & 4 deletions SE050Sim/sdk-test/test_se050.c
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ static void test_ecdh(const char *name, uint32_t obj_a, uint32_t obj_b,
uint8_t shared[64] = {0};
size_t shared_len = sizeof(shared);
size_t shared_bits = 0;
uint8_t dummy[64] = {0};

/* Generate two key pairs */
cleanup_object(obj_a);
Expand All @@ -357,7 +358,7 @@ static void test_ecdh(const char *name, uint32_t obj_a, uint32_t obj_b,
/* Compute ECDH(A_priv, B_pub) */
sss_key_object_init(&derived_key, &g_ks);
status = sss_key_object_allocate_handle(&derived_key, obj_ss,
kSSS_KeyPart_Default, kSSS_CipherType_Binary, key_bytes,
kSSS_KeyPart_Default, kSSS_CipherType_HMAC, key_bytes,
kKeyObject_Mode_Transient);
ASSERT_OK(status, "derived allocate");

Expand All @@ -366,12 +367,20 @@ static void test_ecdh(const char *name, uint32_t obj_a, uint32_t obj_b,
ASSERT_OK(status, "derive_key_context_init");

sss_key_store_erase_key(&g_ks, &derived_key);
/* Applet 7.2+ stores the shared secret into a pre-existing HMACKey
* object whose size must equal the secret exactly; create it before
* the derive */
status = sss_key_store_set_key(&g_ks, &derived_key, dummy,
key_bytes, key_bytes * 8, NULL, 0);
ASSERT_OK(status, "derived pre-create");
status = sss_derive_key_dh(&derive_ctx, &key_b, &derived_key);
ASSERT_OK(status, "derive_key_dh");

sss_derive_key_context_free(&derive_ctx);

/* Read shared secret */
/* Read shared secret. sss_key_store_get_key has no HMAC read case, so
* read the object back as AES type (both are a plain ReadObject). */
derived_key.cipherType = kSSS_CipherType_AES;
status = sss_key_store_get_key(&g_ks, &derived_key,
shared, &shared_len, &shared_bits);
ASSERT_OK(status, "get shared secret");
Expand Down Expand Up @@ -1390,6 +1399,7 @@ static void test_x25519_ecdh(void)
size_t shared_a_len = sizeof(shared_a), shared_b_len = sizeof(shared_b);
size_t shared_bits = 0;
uint8_t zeros[32] = {0};
uint8_t dummy[32] = {0};

cleanup_object(id_a);
cleanup_object(id_b);
Expand Down Expand Up @@ -1417,35 +1427,47 @@ static void test_x25519_ecdh(void)
/* ECDH(A_priv, B_pub) */
sss_key_object_init(&derived_a, &g_ks);
status = sss_key_object_allocate_handle(&derived_a, id_ss_a,
kSSS_KeyPart_Default, kSSS_CipherType_Binary, 32,
kSSS_KeyPart_Default, kSSS_CipherType_HMAC, 32,
kKeyObject_Mode_Transient);
ASSERT_OK(status, "derived_a allocate");

/* Applet 7.2+: derive target must be a pre-existing HMACKey object
* sized exactly to the shared secret */
status = sss_key_store_set_key(&g_ks, &derived_a, dummy,
sizeof(dummy), sizeof(dummy) * 8, NULL, 0);
ASSERT_OK(status, "derived_a pre-create");

status = sss_derive_key_context_init(&derive_ctx, &g_ctx.session,
&key_a, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret);
ASSERT_OK(status, "derive_a context_init");
status = sss_derive_key_dh(&derive_ctx, &key_b, &derived_a);
ASSERT_OK(status, "derive_a dh");
sss_derive_key_context_free(&derive_ctx);

derived_a.cipherType = kSSS_CipherType_AES;
status = sss_key_store_get_key(&g_ks, &derived_a,
shared_a, &shared_a_len, &shared_bits);
ASSERT_OK(status, "get shared_a");

/* ECDH(B_priv, A_pub) */
sss_key_object_init(&derived_b, &g_ks);
status = sss_key_object_allocate_handle(&derived_b, id_ss_b,
kSSS_KeyPart_Default, kSSS_CipherType_Binary, 32,
kSSS_KeyPart_Default, kSSS_CipherType_HMAC, 32,
kKeyObject_Mode_Transient);
ASSERT_OK(status, "derived_b allocate");

status = sss_key_store_set_key(&g_ks, &derived_b, dummy,
sizeof(dummy), sizeof(dummy) * 8, NULL, 0);
ASSERT_OK(status, "derived_b pre-create");

status = sss_derive_key_context_init(&derive_ctx, &g_ctx.session,
&key_b, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret);
ASSERT_OK(status, "derive_b context_init");
status = sss_derive_key_dh(&derive_ctx, &key_a, &derived_b);
ASSERT_OK(status, "derive_b dh");
sss_derive_key_context_free(&derive_ctx);

derived_b.cipherType = kSSS_CipherType_AES;
status = sss_key_store_get_key(&g_ks, &derived_b,
shared_b, &shared_b_len, &shared_bits);
ASSERT_OK(status, "get shared_b");
Expand Down
4 changes: 3 additions & 1 deletion SE050Sim/se050-sim/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
P1_EC => handlers::ec::handle_write_ec_key(apdu, store),
P1_RSA => handlers::rsa::handle_write_rsa_key(apdu, store),
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
Expand Down Expand Up @@ -128,7 +129,8 @@ pub fn dispatch(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
(P1_SIGNATURE, P2_VERIFY) => handlers::ec::handle_verify(apdu, store),

// ECDH shared secret (P2_DH=0x0F or P2_DH_REVERSE=0x59)
(P1_EC, P2_DH) | (P1_EC, 0x59) => handlers::ec::handle_ecdh(apdu, store),
(P1_EC, P2_DH) | (P1_EC, 0x59) => handlers::ec::handle_ecdh(
apdu, store, handlers::ec::strict_ecdh_from_env()),

// AES cipher oneshot
(P1_CIPHER, P2_ENCRYPT_ONESHOT) => {
Expand Down
77 changes: 77 additions & 0 deletions SE050Sim/se050-sim/src/handlers/aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,33 @@ pub fn handle_write_aes_key(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduR
}
}

/// Handle WRITE HMAC key command (WriteSymmKey with P1=HMAC).
/// Tag1=obj_id(4B), Tag3=key_data. HMAC keys have no fixed length, so any
/// non-empty Tag3 value is accepted as-is.
pub fn handle_write_hmac_key(apdu: &ParsedApdu, store: &mut ObjectStore) -> ApduResponse {
let tlvs = match apdu.parse_tlvs() {
Ok(t) => t,
Err(_) => return ApduResponse::error(SW_WRONG_DATA),
};

let obj_id = match tlv::find_tlv(&tlvs, TAG_1) {
Some(t) if t.value.len() == 4 => {
let mut id = [0u8; 4];
id.copy_from_slice(&t.value);
id
}
_ => return ApduResponse::error(SW_WRONG_DATA),
};

match tlv::find_tlv(&tlvs, TAG_3) {
Some(t) if !t.value.is_empty() => {
store.insert(obj_id, SecureObject::HMACKey { key: t.value.clone() });
ApduResponse::success()
}
_ => ApduResponse::error(SW_WRONG_DATA),
}
}

/// Handle AES Encrypt Oneshot.
/// INS=Crypto, P1=Cipher, P2=EncryptOneshot
/// Tag1=key_id(4B), Tag2=cipher_mode(1B), Tag3=plaintext, Tag4=IV(opt)
Expand Down Expand Up @@ -385,3 +412,53 @@ where

Some(result)
}

#[cfg(test)]
mod hmac_write_tests {
use super::*;
use crate::dispatch::dispatch;

#[test]
fn test_write_hmac_key_via_dispatch() {
// WriteSymmKey with P1=HMAC and the transient INS bit set
// (kSE05x_INS_WRITE | kSE05x_INS_TRANSIENT = 0x21), as sent by
// sss_key_store_set_key for a kSSS_CipherType_HMAC object.
let key = vec![0xA5u8; 32];
let mut data = vec![TAG_1, 0x04, 0x00, 0x00, 0x00, 0x66];
data.push(TAG_3);
data.push(key.len() as u8);
data.extend_from_slice(&key);

let apdu = ParsedApdu {
cla: 0x80,
ins: 0x21,
p1: P1_HMAC,
p2: P2_DEFAULT,
data,
le: None,
};
let mut store = ObjectStore::new();
let resp = dispatch(&apdu, &mut store);
assert_eq!(resp.sw, 0x9000);
match store.get(&[0x00, 0x00, 0x00, 0x66]) {
Some(SecureObject::HMACKey { key: stored }) => assert_eq!(stored, &key),
_ => panic!("HMACKey object not stored"),
}
}

#[test]
fn test_write_hmac_key_empty_value_rejected() {
let data = vec![TAG_1, 0x04, 0x00, 0x00, 0x00, 0x66, TAG_3, 0x00];
let apdu = ParsedApdu {
cla: 0x80,
ins: 0x01,
p1: P1_HMAC,
p2: P2_DEFAULT,
data,
le: None,
};
let mut store = ObjectStore::new();
let resp = dispatch(&apdu, &mut store);
assert_eq!(resp.sw, SW_WRONG_DATA);
}
}
Loading
Loading