Skip to content

user_settings update to cohesively enable/disable features #11

@aidangarske

Description

@aidangarske

Right now, the PR's #define system is opt-in additive you enable what you want by defining macros on the build line. There's also an auto-enable dependency layer (CBOR encode/decode gets auto-pulled in by Sign/Encrypt/Mac operations), which is good, but it's only half-baked — Copilot correctly caught that the new multi-party APIs (WOLFCOSE_SIGN_SIGN, WOLFCOSE_ENCRYPT_ENCRYPT, etc.) don't participate in that auto-enable logic.

The bigger conceptual problem is there's no defined "default" build profile. The user has to know what they need before they know what they're building. That's backwards for an embedded library you want the smallest correct default and then opt in to complexity.


The Right Mental Model: Opt-Out, Not Opt-In

Follow wolfSSL's actual pattern here. wolfSSL ships with a sane default feature set and you trim it with NO_* defines. wolfCOSE should do the same:

Default build = minimum viable COSE: Sign1 + AES-GCM Encrypt0 + HMAC Mac0, one signer, one recipient/verifier. Everything beyond that is opt-in with a WOLFCOSE_ENABLE_* macro, or alternatively you strip features with WOLFCOSE_NO_*.

Since wolfCOSE is a new library with no installed base yet, I'd actually lean toward explicit opt-in for the heavy stuff (multi-party, PQC, ChaCha, AES-CCM, AES-MAC, RSA-PSS, Ed448) and opt-out for the basics (Sign1, Encrypt0, Mac0, ES256, AES-GCM, HMAC-256). That keeps the minimal flash footprint story crisp by default.


Proposed Macro Taxonomy

Here's how I'd organize them, grouped by what they actually control:

Feature gates (on by default, strip with NO_):

WOLFCOSE_NO_SIGN1          /* disable COSE_Sign1 entirely */
WOLFCOSE_NO_ENCRYPT0       /* disable COSE_Encrypt0 entirely */
WOLFCOSE_NO_MAC0           /* disable COSE_Mac0 entirely */
WOLFCOSE_NO_ES256          /* no EC P-256 signing */
WOLFCOSE_NO_AESGCM         /* no AES-GCM encryption */
WOLFCOSE_NO_HMAC256        /* no HMAC-SHA256 MAC */

Extension gates (off by default, enable with WOLFCOSE_ENABLE_):

WOLFCOSE_ENABLE_SIGN        /* COSE_Sign multi-signer */
WOLFCOSE_ENABLE_ENCRYPT     /* COSE_Encrypt multi-recipient */
WOLFCOSE_ENABLE_MAC         /* COSE_Mac multi-recipient */
WOLFCOSE_ENABLE_ES384
WOLFCOSE_ENABLE_ES512
WOLFCOSE_ENABLE_EDDSA       /* Ed25519 */
WOLFCOSE_ENABLE_ED448
WOLFCOSE_ENABLE_MLDSA       /* ML-DSA-44/65/87 */
WOLFCOSE_ENABLE_RSAPSS      /* PS256/384/512 */
WOLFCOSE_ENABLE_CHACHA20    /* ChaCha20-Poly1305 */
WOLFCOSE_ENABLE_AESCCM
WOLFCOSE_ENABLE_AESMAC
WOLFCOSE_ENABLE_AESWRAP     /* A128KW/A192KW/A256KW */
WOLFCOSE_ENABLE_ECDH_ES     /* ECDH-ES+HKDF key agreement */
WOLFCOSE_ENABLE_HMAC384
WOLFCOSE_ENABLE_HMAC512

Infrastructure toggles (auto-resolved, rarely set manually):

WOLFCOSE_CBOR_ENCODE        /* auto-enabled when any create op is active */
WOLFCOSE_CBOR_DECODE        /* auto-enabled when any verify/decrypt op is active */

The auto-enable block in the header needs to be extended to cover ALL message type ops, including the multi-party ones — that's the Copilot bug that needs fixing regardless.


user_settings.h

A wolfcose_user_settings.h in the repo root (or include/) that:

  1. Is not included by default — users opt into it with #define WOLFCOSE_USER_SETTINGS on the build line (same pattern as wolfSSL's WOLFSSL_USER_SETTINGS)
  2. Has every macro commented out with a description of what it does
  3. Is structured in logical groups matching the taxonomy above

Something like:

/* wolfcose_user_settings.h
 * Copy this file and customize for your target build.
 * Activate with: -DWOLFCOSE_USER_SETTINGS
 */

/* ── Strip default features ────────────────────────── */
/* #define WOLFCOSE_NO_SIGN1       */  /* Remove COSE_Sign1 support */
/* #define WOLFCOSE_NO_ENCRYPT0    */  /* Remove COSE_Encrypt0 support */
/* #define WOLFCOSE_NO_MAC0        */  /* Remove COSE_Mac0 support */
/* #define WOLFCOSE_NO_AESGCM      */  /* Remove AES-GCM encryption */
/* #define WOLFCOSE_NO_HMAC256     */  /* Remove HMAC-SHA256 MAC */

/* ── Enable optional features ──────────────────────── */
/* #define WOLFCOSE_ENABLE_SIGN    */  /* Multi-signer COSE_Sign (up to 4 signers) */
/* #define WOLFCOSE_ENABLE_ENCRYPT */  /* Multi-recipient COSE_Encrypt */
/* #define WOLFCOSE_ENABLE_MAC     */  /* Multi-recipient COSE_Mac */
/* #define WOLFCOSE_ENABLE_MLDSA   */  /* Post-quantum ML-DSA signatures */
/* #define WOLFCOSE_ENABLE_ECDH_ES */  /* ECDH-ES+HKDF key agreement */
/* #define WOLFCOSE_ENABLE_AESWRAP */  /* AES Key Wrap (A128KW/A192KW/A256KW) */
/* #define WOLFCOSE_ENABLE_CHACHA20 */ /* ChaCha20-Poly1305 encryption */
/* #define WOLFCOSE_ENABLE_AESCCM  */  /* AES-CCM encryption variants */
/* #define WOLFCOSE_ENABLE_RSAPSS  */  /* RSA-PSS signatures (PS256/384/512) */
/* #define WOLFCOSE_ENABLE_EDDSA   */  /* Ed25519 signatures */
/* #define WOLFCOSE_ENABLE_ED448   */  /* Ed448 signatures */

This also kills the "you need to pass 15 defines on the cmake/make command line" problem for integrators, which is a real barrier.


Build Process Recommendation

Keep the Makefile targets clean and map to these profiles:

  • make → default profile, Sign1+Encrypt0+Mac0, ES256+AES-GCM+HMAC256 only
  • make WOLFCOSE_USER_SETTINGS=1 → picks up wolfcose_user_settings.h
  • make full → enables everything (for CI/testing the comprehensive suite)
  • make test → runs against whatever the current build profile is, not always the full suite

That last point matters — your current CI runs 244 tests unconditionally, but a minimal build would need a subset of those tests to be gated too. The test framework should respect the same macros.


On Multi-Signer/Verifier Being Default

My recommendation: not default. The multi-party APIs add struct complexity (WOLFCOSE_SIGNATURE[], WOLFCOSE_RECIPIENT[]), more CBOR structure, and have ECDH-ES/key wrap dependencies that pull in significant wolfCrypt surface area. For a device that's just signing firmware updates with one key, it's pure overhead. Keep Sign1/Encrypt0/Mac0 as the default path and let WOLFCOSE_ENABLE_SIGN etc. unlock the multi-party path.

The one-to-one functions should stay the zero-overhead default and that's actually a strong differentiator from something like COSE-C which doesn't even have clean single-party fast paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions