Skip to content

Add ExportPublicKey API for cached asymmetric keys#346

Open
Frauschi wants to merge 1 commit intowolfSSL:mainfrom
Frauschi:export_pub_key
Open

Add ExportPublicKey API for cached asymmetric keys#346
Frauschi wants to merge 1 commit intowolfSSL:mainfrom
Frauschi:export_pub_key

Conversation

@Frauschi
Copy link
Copy Markdown
Contributor

Add ExportPublicKey API for cached asymmetric key objects

Summary

Adds a dedicated path for extracting only the public half of a cached public-key keypair, so callers that need a public key on the client side (signature verification, certificate building, key transport, etc.) no longer have to pull the private material out of the HSM.

Previously, the only way to get the public half of a cached keypair was to call wh_Client_<Algo>ExportKey(), which goes through the algorithm-agnostic wh_Client_KeyExport() and returns the raw cached DER — including any private material. For the common case "I cached a keypair for on-HSM signing and I just need the public key on the client," shipping the private key defeats the security benefit of caching.

This PR adds:

  • Non-DMA path: new keystore action WH_KEY_EXPORT_PUBLIC + per-algorithm client wrappers.
  • DMA path: parallel WH_KEY_EXPORT_PUBLIC_DMA + wh_Client_KeyExportPublicDma generic transport + per-algorithm DMA wrapper for ML-DSA (matches the existing ML-DSA-only DMA-export precedent).

Algorithms wired end-to-end

Algorithm Non-DMA client wrapper DMA
RSA wh_Client_RsaExportPublicKey via generic transport
ECC wh_Client_EccExportPublicKey via generic transport
Ed25519 wh_Client_Ed25519ExportPublicKey via generic transport
Curve25519 wh_Client_Curve25519ExportPublicKey via generic transport
ML-DSA (Dilithium) wh_Client_MlDsaExportPublicKey wh_Client_MlDsaExportPublicKeyDma

Design notes

  • Single keystore action with an algo selector. Cached keys are stored as opaque DER — NVM metadata does not record an algorithm type — so the server needs the algorithm to know how to decode. Using one wire action keeps the translate/lock/label plumbing shared with WH_KEY_EXPORT instead of duplicating it per algorithm. The selector is a new WH_KEY_ALGO_* enum in wolfhsm/wh_common.h.
  • NONEXPORTABLE carve-out. WH_NVM_FLAGS_NONEXPORTABLE blocks full-export but not public-only export, because public material is non-sensitive and blocking it would make cached keys unusable for any external verification or key-transport use case. This is a dedicated WH_KS_OP_EXPORT_PUBLIC branch in _KeystoreCheckPolicy (not a silent bypass) and is called out explicitly in the docs.
  • Reuse. The server handler deserializes directly from cacheBuf/cacheMeta using the existing wh_Crypto_*DeserializeKeyDer helpers (which already fall back to public-only decode), then re-emits public-only DER via the matching wc_*PublicKeyToDer. No new server-side crypto helpers introduced.
  • DMA staging with zero extra server stack. The DMA handler stages the public DER in the unused tail of resp_packet (the DMA response struct itself only occupies the header), then whServerDma_CopyToClients it into the client-provided buffer. The response sent over the wire is just sizeof(resp).
  • Error handling. Wrong algo selector → ASN parse error bubbles up. Unknown keyId → WH_ERROR_NOTFOUND. wc_*PublicKeyToDer returning 0 → WH_ERROR_ABORTED (explicitly, not a silent zero-length success). Too-small DMA client buffer → WH_ERROR_NOSPACE.

Wire protocol

New keystore actions (appended to enum WH_KEY_ENUM so existing numeric values are preserved):

  • WH_KEY_EXPORT_PUBLIC
  • WH_KEY_EXPORT_PUBLIC_DMA (under WOLFHSM_CFG_DMA)

Each takes a uint16_t algo selector alongside the standard keyId. Integrators with custom transports route these the same way they route WH_KEY_EXPORT / WH_KEY_EXPORT_DMA.

Test plan

End-to-end per algorithm:

  • RSANONEXPORTABLE cached key → full export denied with WH_ERROR_ACCESSwh_Client_RsaExportPublicKey succeeds → client-side wc_RsaPublicEncrypt / HSM-side wc_RsaPrivateDecrypt round-trips plaintext. Includes wrong-algo and unknown-keyId (WH_ERROR_NOTFOUND) negative cases.
  • ECC — HSM signs with cached private, client verifies locally with exported public; asserts type == ECC_PUBLICKEY.
  • Ed25519 — HSM signs via wh_Client_Ed25519Sign, client verifies with wc_ed25519_verify_msg; asserts pubKeySet==1 && privKeySet==0.
  • Curve25519 — X25519 shared-secret round-trip: local_priv·hsm_pub == hsm_priv·local_pub.
  • ML-DSA — MakeCacheKey (level 2) → wh_Client_MlDsaExportPublicKey → asserts pubKeySet==1 && prvKeySet==0.

DMA-specific coverage:

  • ECC DMA — HSM sign (non-DMA cryptoCb) + client verify, where the public half is pulled via wh_Client_KeyExportPublicDma (generic transport).
  • ML-DSA DMAwh_Client_MlDsaExportPublicKeyDma + flag assertions, plus a byte-identity check comparing DMA-path DER vs. non-DMA-path DER for the same cached key, plus a WH_ERROR_NOSPACE negative test with an undersized client buffer.

Docs updated in docs/src/chapter05.md and docs/src-ja/chapter05.md.

Introduces a new keystore action WH_KEY_EXPORT_PUBLIC that re-emits only
the public portion of a cached public-key object, so callers that need a
public key for a client-side operation (signature verification, key
transport, etc.) no longer have to pull private material out of the HSM.
A new WH_KS_OP_EXPORT_PUBLIC policy branch gates the path and
intentionally bypasses NONEXPORTABLE since public material is
non-sensitive.

Wired end-to-end for RSA, ECC, Ed25519, Curve25519, and ML-DSA, with
per-algorithm client wrappers (wh_Client_<Algo>ExportPublicKey) and
smoke tests that round-trip real operations (sign/verify, ECDH) against
the exported public keys, plus a negative test for unknown keyId.

Also adds a DMA variant (WH_KEY_EXPORT_PUBLIC_DMA) with a generic client
transport and an ML-DSA-specific wrapper, byte-identity cross-validation
against the non-DMA path, and a NOSPACE bounds-check test.

Documentation added to docs/src/chapter05.md and docs/src-ja/chapter05.md.
New message structs registered in the padding-check test.
@Frauschi Frauschi self-assigned this Apr 24, 2026
@Frauschi
Copy link
Copy Markdown
Contributor Author

Currently, this work is orthogonal to #336, so this PR is missing ML-KEM support and #336 is missing support for this new API. Depending on what goes into main first, the other one needs some updates.

Main goal for now is to get some feedback on the general API and design.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant