Skip to content

[Bug]: Silicon Labs crypto callback port: wrapped ECC keys cannot sign #11685

Description

@lucas-mmb

Contact Details

lucas.holzen@mmbnetworks.com

Version

Commit f6a75d4

Description

Summary

With WOLFSSL_SILABS_CRYPTOCB (PR #11267), a P-256 key created by wc_SilabsSe_EccGenerateWrappedKey() and bound with wc_SilabsSe_EccUseWrappedKey() cannot be used for ECDSA. wc_ecc_sign_hash() returns WC_HW_E (-248), because the Secure Engine rejects the sign command with SL_STATUS_INVALID_PARAMETER.

The Secure Engine only signs with an ECC key whose descriptor has SL_SE_KEY_FLAG_ASYMMETRIC_SIGNING_ONLY. The port's wrapped-key and built-in-key paths never set it. ECDH with the same key works.

Environment

  • wolfSSL f6a75d4aaf2a50eabf30b5718300d68578e26101 (merge of Add Silicon Labs EFR32xG25 Secure Element crypto callback port #11267)
  • EFR32FG25B121F1152IM56 (Secure Vault High), SE firmware 2.2.1
  • Simplicity SDK 2026.6.1, arm-none-eabi-gcc 14.2.rel1, FreeRTOS
  • user_settings.h: WOLFSSL_SILABS_CRYPTOCB (all engines), HAVE_ECC, ECC_USER_CURVES (P-256 only), WOLFSSL_SP_MATH, WOLFSSL_SMALL_STACK
  • wolfCrypt_Init() called at start-up, which registers the device at WOLFSSL_SILABS_DEVID

Reproduction steps

byte    wrapped[96];
word32  wrappedSz = sizeof(wrapped);
byte    pub[64];
word32  pubSz = sizeof(pub);
byte    hash[32] = { 0x11 /* ... any 32-byte digest */ };
byte    sig[80];
word32  sigSz = sizeof(sig);
ecc_key key;
WC_RNG  rng;

wc_InitRng(&rng);                                                    /* 0 */
wc_SilabsSe_EccGenerateWrappedKey(ECC_SECP256R1, wrapped, &wrappedSz,
                                  pub, &pubSz);                      /* 0 */
wc_ecc_init_ex(&key, NULL, WOLFSSL_SILABS_DEVID);                    /* 0 */
wc_SilabsSe_EccUseWrappedKey(&key, wrapped, wrappedSz,
                             ECC_SECP256R1);                         /* 0 */
wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigSz, &rng, &key);       /* WC_HW_E (-248) */

With the same bound key, wc_ecc_shared_secret() against a P-256 public point succeeds. The result equals the expected X coordinate when the peer is the base point.

Expected

wc_ecc_sign_hash() returns 0 with a valid signature, or wc_SilabsSe_EccGenerateWrappedKey() documents that its keys are for key agreement only.

Actual

wc_ecc_sign_hash() returns WC_HW_E. Calling sl_se_ecc_sign() directly with the bound descriptor (key.key) returns SL_STATUS_INVALID_PARAMETER (0x21), whatever the arguments:

Descriptor sl_se_ecc_sign()
Port's wrapped descriptor, hashed_message = true, SL_SE_HASH_NONE 0x21
Same, hashed_message = false, SL_SE_HASH_SHA256 0x21
Same with size = 0 instead of 32 0x21
Word-aligned static input and output buffers 0x21
Plaintext P-256 private key, flags HAS_PRIVATE_KEY 0x21
Plaintext P-256 private key, flags HAS_PRIVATE_KEY | SIGNING_ONLY 0 (OK)
Wrapped key generated with HAS_PRIVATE_KEY | SIGNING_ONLY, bound with the same flags 0 (OK)

sl_se_validate_key() accepts the port's descriptor (flags 0x01004000, type 0x80000020, wrapped size 60). The rejection comes from the Secure Engine's response, not from a check in the SE Manager.

Cause

On this part an ECC private key is either signing-only (SL_SE_KEY_FLAG_ASYMMETRIC_SIGNING_ONLY), usable for ECDSA, or a key-agreement key, usable for ECDH. For a wrapped key the flags are part of the wrapped blob, so they are fixed when the key is generated or imported. They must be described the same way every time the key is used: a descriptor with different flags is refused.

The port uses HAS_PRIVATE_KEY | NON_EXPORTABLE without SIGNING_ONLY everywhere it creates or binds a device-resident ECC key:

The plaintext paths do set it (silabs_ecc.c L257-L259, L331-L334). That is presumably why wolfcrypt_test and the ECDSA benchmark pass: they sign with plaintext keys, so the wrapped-key sign path is not exercised by them.

Suggested fix

Because the usage is fixed at creation, it has to be chosen when the key is generated and repeated when it is bound. For example:

/* usage: WC_SILABS_KEY_USAGE_SIGN or WC_SILABS_KEY_USAGE_AGREE */
int wc_SilabsSe_EccGenerateWrappedKeyEx(int curveId, int usage, byte* wrapped,
    word32* wrappedSz, byte* pubOut, word32* pubOutSz);
int wc_SilabsSe_EccUseWrappedKeyEx(ecc_key* key, const byte* wrapped,
    word32 wrappedSz, int curveId, int usage);

The existing calls could stay as they are (agreement keys) and say so in their documentation and the README's "Secure Vault key management" section. A test that signs with a wrapped key would cover the path.

Relevant log output

Activity

  1. self-assigned this
    on Oct 7, 2026
  2. dgarske commented on Oct 8, 2026

    @dgarske
    Member

    Hi @lucas-mmb , please review the fix in #11687 . Looking forward to your feedback
    Thanks, David Garske, wolfSSL

  3. lucas-mmb commented on Oct 8, 2026

    @lucas-mmb
    Author

    That looks great, thank you very much. Its resolved the issue.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions