Skip to content
Open
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
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ SCEN_IOTFLEET = examples/scenarios/iot_fleet_config
SCEN_SENSOR = examples/scenarios/sensor_attestation
SCEN_BROADCAST = examples/scenarios/group_broadcast_mac

.PHONY: all shared test zeroize-test coverage tool tool-test cmdline-test demo demos lean-verify mldsa-demo mldsa-verify comprehensive scenarios interop-tcose c99-check clean
.PHONY: all shared test zeroize-test ext-callback-test coverage tool tool-test cmdline-test demo demos lean-verify mldsa-demo mldsa-verify comprehensive scenarios interop-tcose c99-check clean

# --- Core library ---
all: $(LIB_A)
Expand All @@ -90,6 +90,12 @@ zeroize-test:
-o $(TEST_BIN) $(SRC) $(TEST_SRC) $(LDFLAGS)
./$(TEST_BIN)

# --- Delegated sign/verify seam test: exercises the ext-sign + ext-verify callbacks ---
ext-callback-test:
$(CC) $(CFLAGS) -DWOLFCOSE_ENABLE_EXT_SIGN -DWOLFCOSE_ENABLE_EXT_VERIFY \
-o $(TEST_BIN) $(SRC) $(TEST_SRC) $(LDFLAGS)
./$(TEST_BIN)

# --- Coverage ---
coverage: clean
$(CC) $(CFLAGS) --coverage -fprofile-arcs -ftest-coverage -c src/wolfcose_cbor.c -o src/wolfcose_cbor.o
Expand Down
28 changes: 28 additions & 0 deletions include/wolfcose/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@ extern "C" {
#endif
#endif /* WOLFCOSE_LEAN_MLDSA */

/* ----- Integration seams -----
*
* Opt-in in every build, including a full one. These are not algorithms, so
* there is no "wolfSSL provides the primitive" clause to auto-enable them and a
* default build is byte-identical to one built without them.
* ----- */

/* External/delegated signing: the caller supplies the signature over the
* to-be-signed bytes, so no private key material enters wolfCOSE — extension. */
#if defined(WOLFCOSE_ENABLE_EXT_SIGN)
#define WOLFCOSE_EXT_SIGN
#endif

/* Delegated verification: caller checks the signature; mirror of EXT_SIGN. */
#if defined(WOLFCOSE_ENABLE_EXT_VERIFY)
#define WOLFCOSE_EXT_VERIFY
#endif

/* ----- Signature algorithms ----- */

/* ES256 — core (on whenever wolfSSL has ECC) */
Expand Down Expand Up @@ -503,6 +521,16 @@ extern "C" {
#error "wolfCOSE: ML-DSA enabled but WOLFCOSE_MAX_SCRATCH_SZ too small"
#endif

#if defined(WOLFCOSE_EXT_SIGN) && !defined(WOLFCOSE_SIGN1_SIGN) && \
!defined(WOLFCOSE_SIGN_SIGN)
#error "WOLFCOSE_ENABLE_EXT_SIGN requires an enabled signing operation"
#endif

#if defined(WOLFCOSE_EXT_VERIFY) && !defined(WOLFCOSE_SIGN1_VERIFY) && \
!defined(WOLFCOSE_SIGN_VERIFY)
#error "WOLFCOSE_ENABLE_EXT_VERIFY requires an enabled verify operation"
#endif

#ifdef __cplusplus
}
#endif
Expand Down
96 changes: 95 additions & 1 deletion include/wolfcose/wolfcose.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,53 @@ typedef struct WOLFCOSE_HDR {
/** \brief Flag indicating payload is detached (RFC 9052 Section 2) */
#define WOLFCOSE_HDR_FLAG_DETACHED 0x01u

#if defined(WOLFCOSE_EXT_SIGN)
/**
* \brief Caller-supplied signature callback (RFC 9052 Section 4.4).
*
* Receives what the algorithm's signature primitive consumes: the digest of the
* Sig_structure for pre-hashed algorithms (ES256/384/512, PS256/384/512), or the
* Sig_structure itself for EdDSA/Ed448/ML-DSA.
*
* Returns the COSE signature bytes exactly as they appear in the message: for
* ECDSA that is fixed-width r||s (RFC 9053 Section 2.1), which is what
* psa_sign_hash() already produces. wolfCOSE performs no conversion, but does
* check the returned length.
*
* \param cbCtx Opaque caller context, passed through untouched.
* \param alg WOLFCOSE_ALG_* being signed with.
* \param tbs To-be-signed bytes (digest, or Sig_structure for EdDSA).
* \param tbsSz Length of tbs.
* \param sig Output buffer for the signature.
* \param sigSz Capacity of sig.
* \param sigLen Output: bytes written to sig.
* \return 0 on success, non-zero to fail the operation.
*/
typedef int (*WOLFCOSE_SIGN_CB)(void* cbCtx, int32_t alg,
const uint8_t* tbs, size_t tbsSz,
uint8_t* sig, size_t sigSz, size_t* sigLen);
#endif /* WOLFCOSE_EXT_SIGN */

#if defined(WOLFCOSE_EXT_VERIFY)
/**
* \brief Caller-supplied verification callback (RFC 9052 Section 4.4).
*
* Mirror of WOLFCOSE_SIGN_CB: wolfCOSE hands the caller the digest and r||s so
* no public-key operation runs in wolfCOSE, matching psa_verify_hash().
*
* \param cbCtx Opaque caller context, passed through untouched.
* \param alg WOLFCOSE_ALG_* being verified.
* \param tbs To-be-verified digest (or Sig_structure for EdDSA).
* \param tbsSz Length of tbs.
* \param sig COSE signature bytes.
* \param sigSz Length of sig.
* \return 0 when the signature is valid, non-zero otherwise.
*/
typedef int (*WOLFCOSE_VERIFY_CB)(void* cbCtx, int32_t alg,
const uint8_t* tbs, size_t tbsSz,
const uint8_t* sig, size_t sigSz);
#endif /* WOLFCOSE_EXT_VERIFY */

/**
* \brief COSE key structure. Pointers to caller-owned wolfCrypt key structs.
*
Expand Down Expand Up @@ -343,7 +390,19 @@ typedef struct WOLFCOSE_KEY {
const uint8_t* mldsaSeed; /**< RFC 9964 ML-DSA private seed (32B), caller-owned */
size_t mldsaSeedLen; /**< ML-DSA private seed length */
#endif
uint8_t hasPrivate; /**< 1 if private key material present */
uint8_t hasPrivate; /**< 1 if the key can produce signatures. With
* signCb set this means the external signer holds
* the private key, not wolfCOSE. */
#if defined(WOLFCOSE_EXT_SIGN)
/* Appended at the end: absent unless the seam is enabled, so the struct
* layout is unchanged in a default build. */
WOLFCOSE_SIGN_CB signCb; /**< NULL: sign locally with wolfCrypt */
void* signCtx; /**< Opaque, passed to signCb untouched */
#endif
#if defined(WOLFCOSE_EXT_VERIFY)
WOLFCOSE_VERIFY_CB verifyCb; /**< NULL: verify locally with wolfCrypt */
void* verifyCtx; /**< Opaque, passed to verifyCb untouched */
#endif
} WOLFCOSE_KEY;

/**
Expand Down Expand Up @@ -631,6 +690,41 @@ WOLFCOSE_API int wc_CoseKey_SetRsa(WOLFCOSE_KEY* key, RsaKey* rsaKey);
WOLFCOSE_API int wc_CoseKey_SetSymmetric(WOLFCOSE_KEY* key,
const uint8_t* data, size_t dataLen);

#if defined(WOLFCOSE_EXT_SIGN)
/**
* \brief Delegate signing to a caller-supplied callback.
*
* Does not touch kty/crv or the key union, so it composes with
* wc_CoseKey_SetEcc() and friends. Sets hasPrivate: for a delegated key that
* means the key can produce signatures, not that wolfCOSE holds the private
* material. With a callback set, wc_CoseSign1_Sign() accepts a NULL rng
* because the external signer owns its own randomness.
*
* \param key COSE key (must be initialized).
* \param cb Signature callback. Must not be NULL.
* \param cbCtx Opaque context passed to cb (may be NULL).
* \return WOLFCOSE_SUCCESS or negative error code.
*/
WOLFCOSE_API int wc_CoseKey_SetExtSigner(WOLFCOSE_KEY* key,
WOLFCOSE_SIGN_CB cb, void* cbCtx);
#endif

#if defined(WOLFCOSE_EXT_VERIFY)
/**
* \brief Delegate signature verification to a caller-supplied callback.
*
* Mirror of wc_CoseKey_SetExtSigner(): wolfCOSE parses and hashes, then hands
* the check to cb. The key union carries no ecc_key; kty and crv must match alg.
*
* \param key COSE key (must be initialized).
* \param cb Verification callback. Must not be NULL.
* \param cbCtx Opaque context passed to cb (may be NULL).
* \return WOLFCOSE_SUCCESS or negative error code.
*/
WOLFCOSE_API int wc_CoseKey_SetExtVerifier(WOLFCOSE_KEY* key,
WOLFCOSE_VERIFY_CB cb, void* cbCtx);
#endif

#if defined(WOLFCOSE_KEY_ENCODE)
/**
* \brief Encode a WOLFCOSE_KEY to CBOR COSE_Key map format.
Expand Down
Loading
Loading