Return a generated public key from cached keygen#458
Conversation
rizlik
left a comment
There was a problem hiding this comment.
My only doubt is that the ML-DSA MakeCacheKey response now carries the public key, which may not fit in the comm buffer, making the API fail on constrained devices. Those devices can use the new DMA make-and-export API, but it's probably worth a comment somewhere so that users know how to fix this when they upgrade.
Beside that, LGTM
Backport the LMS/XMSS keygen ergonomic to RSA: when a key is generated and cached in the HSM (non-ephemeral), the server now also serializes the generated public key into the keygen response body. The client gets both the keyId and a usable public key in one round-trip instead of following up with a separate wh_Client_RsaExportPublicKey call. The response reuses the existing keyId + len + body layout, so there is no wire-format change. Existing wh_Client_RsaMakeCacheKey callers ignore the extra body bytes and are unaffected. Add wh_Client_RsaMakeCacheKeyAndExportPublic, which caches the key and returns its public key into the caller's RsaKey object, pointing that object at the cached keyId for follow-on HSM operations. Add tests in both the main and refactored crypto test suites that cross-check the keygen-returned public key against wh_Client_RsaExportPublicKey and prove it is usable via an encrypt/HSM-decrypt round-trip.
Extend the cached ECC keygen path to serialize the generated public key into the keygen response body, so the client receives both the keyId and a usable public key in one round-trip instead of a follow-up wh_Client_EccExportPublicKey call. The response reuses the existing keyId + len + body layout, so there is no wire-format change and existing wh_Client_EccMakeCacheKey callers are unaffected. Add wh_Client_EccMakeCacheKeyAndExportPublic, which caches the key and returns its public key into the caller's ecc_key object, pointing that object at the cached keyId for follow-on HSM operations. Also drop the stale "TODO: RSA has the following" comment blocks in _HandleEccKeyGen that this change resolves. Add tests in both crypto test suites that cross-check the keygen-returned public key against wh_Client_EccExportPublicKey and verify an HSM-produced signature with it.
Extend the cached Curve25519 keygen path to serialize the generated public key into the keygen response body, so the client receives both the keyId and a usable public key in one round-trip instead of a follow-up wh_Client_Curve25519ExportPublicKey call. The response reuses the existing keyId + len + body layout, so there is no wire-format change. _Curve25519MakeKey now deserializes the response body whenever it is present rather than only for EPHEMERAL keygen, so the cached path can hand the public key back through the same key object. Existing MakeCacheKey (key == NULL) and MakeExportKey callers are unaffected. Add wh_Client_Curve25519MakeCacheKeyAndExportPublic plus tests in both crypto test suites that cross-check the keygen-returned public key against wh_Client_Curve25519ExportPublicKey and prove it via an X25519 shared-secret round-trip with the cached private key.
Extend the cached Ed25519 keygen path to serialize the generated public key into the keygen response body, so the client receives both the keyId and a usable public key in one round-trip instead of a follow-up wh_Client_Ed25519ExportPublicKey call. The response reuses the existing keyId + outSz + body layout, so there is no wire-format change. _Ed25519MakeKey now deserializes the response body whenever it is present rather than only for EPHEMERAL keygen, so the cached path can hand the public key back through the same key object. Existing MakeCacheKey (key == NULL) and MakeExportKey callers are unaffected. Add wh_Client_Ed25519MakeCacheKeyAndExportPublic plus tests in both crypto test suites that cross-check the keygen-returned public key against wh_Client_Ed25519ExportPublicKey and verify an HSM-produced signature with it.
Extend both the non-DMA and DMA cached ML-DSA keygen paths to return the generated public key to the client in one round-trip instead of a follow-up wh_Client_MlDsaExportPublicKey call. Non-DMA: the cache branch of _HandleMlDsaKeyGen serializes the public key into the response body (reusing the existing keyId + len + body layout). DMA: the cache branch of _HandleMlDsaKeyGenDma streams the public key back through the client's existing key DMA buffer and reports its size in keySize. Both are guarded by WOLFSSL_MLDSA_PUBLIC_KEY, matching the keystore public export path. No wire-format changes. _MlDsaMakeKey and _MlDsaMakeKeyDma now deserialize the response body/buffer whenever it is present rather than only for EPHEMERAL keygen, so the cached paths can hand the public key back through the same key object. Existing MakeCacheKey (key == NULL) and MakeExportKey callers are unaffected. Add wh_Client_MlDsaMakeCacheKeyAndExportPublic and wh_Client_MlDsaMakeCacheKeyDma, plus non-DMA and DMA tests in both crypto test suites that cross-check the keygen-returned public key against wh_Client_MlDsaExportPublicKey[Dma] and verify an HSM-produced signature with it.
Extend both the non-DMA and DMA cached ML-KEM keygen paths to return the generated public key to the client in one round-trip instead of a follow-up wh_Client_MlKemExportPublicKey call. Non-DMA: the cache branch of _HandleMlKemKeyGen encodes the public key into the response body (reusing the existing keyId + len + body layout). DMA: the cache branch of _HandleMlKemKeyGenDma streams the public key back through the client's existing key DMA buffer and reports its size in keySize. Both use wc_MlKemKey_PublicKeySize / wc_MlKemKey_EncodePublicKey, matching the keystore public export path. No wire-format changes. _MlKemMakeKey and _MlKemMakeKeyDma now deserialize the response body/buffer whenever it is present rather than only for EPHEMERAL keygen, so the cached paths can hand the public key back through the same key object. Existing MakeCacheKey (key == NULL) and MakeExportKey callers are unaffected. Add wh_Client_MlKemMakeCacheKeyAndExportPublic and wh_Client_MlKemMakeCacheKeyDma, plus non-DMA and DMA tests that cross-check the keygen-returned public key against wh_Client_MlKemExportPublicKey[Dma] and prove it via a KEM encapsulate/decapsulate round-trip with the cached private key.
5659b80 to
f70023a
Compare
|
@rizlik As discussed offline, I modified the logic to make the public key export "best effort". The server tries to store the public key in the response buffer. If the buffer is too small, no public key is stored there, but the response is sent anyway. On the client, the response is parsed. For existing |
|
LGTM. @bigbrett are you OK with the new best-effor behaviour? |
Summary
Cached key generation previously returned only a
keyId— to get the public key of a freshly generated cached key, the client had to make a secondExportPublicKeyround-trip. This PR adds the new LMS/XMSS ergonomic (keygen also hands back the public key in one round-trip, introduced in #352) to the remaining public-key algorithms: RSA, ECC, Curve25519, Ed25519, ML-DSA, and ML-KEM.The server serializes the public-only key into the existing keygen response body, so there are no new wire structures and the change is fully backward-compatible. Existing
MakeCacheKeycallers keep working unchanged.What changed
New client API (
wolfhsm/wh_client_crypto.h) — opt-in wrappers that cache the key and return its public part into the caller's key object, stamping the cachedkeyIdon it:wh_Client_RsaMakeCacheKeyAndExportPublic(...)wh_Client_EccMakeCacheKeyAndExportPublic(...)wh_Client_Curve25519MakeCacheKeyAndExportPublic(...)wh_Client_Ed25519MakeCacheKeyAndExportPublic(...)wh_Client_MlDsaMakeCacheKeyAndExportPublic(...)+wh_Client_MlDsaMakeCacheKeyDma(...)wh_Client_MlKemMakeCacheKeyAndExportPublic(...)+wh_Client_MlKemMakeCacheKeyDma(...)Server (
src/wh_server_crypto.c) — on the cache path, serializes the public-only key into the keygen response body using the same helpers as the keystore export path (wc_RsaKeyToPublicDer,wc_EccPublicKeyToDer(...,1),wc_Curve25519PublicKeyToDer(...,1),wc_Ed25519PublicKeyToDer(...,1),wc_MlDsaKey_PublicKeyToDer(...,1),wc_MlKemKey_*). DMA paths reuse thekeybuffer +keySize.Client (
src/wh_client_crypto.c) — the new wrappers parse the public key from the response body and set the returnedkeyId.Design notes
Wire-compatible / backward-compatible. Reuses the existing keygen response body (
keyId+len+ trailing DER); no new request/response structs, no translate or padding-test changes. Callers of the existingMakeCacheKeyfunctions ignore the extra bytes. Existing public API signatures are untouched.Fail-closed export on the server. The server always serializes the public key into the cache-path response body. A freshly generated key is expected to serialize, so a failure is treated as fatal: the server evicts the just-committed key and returns the error rather than handing back a
keyIdfor a key with no public part — no cache slots are orphaned.Strict contract on the client. The
...AndExportPublicwrappers returnWH_ERROR_ABORTEDif the server returns an empty body, while still propagating thekeyIdso the caller can evict the cached key.CryptoCb deliberately unchanged.
wc_MakeRsaKeyand friends stay ephemeral (the full key is returned to the client). Cached keygen keys have no auto-eviction (wolfCrypt does not routewc_*_freethrough the CryptoCb), so routing keygen through the cache would leak HSM cache slots on a make/use/free loop and change durability semantics. The new behavior is reachable only via the nativeMakeCacheKeyAndExportPublicfunctions.ML-DSA public-key serialization is guarded by
WOLFSSL_MLDSA_PUBLIC_KEY.Testing
Tests were added to both suites:
test/wh_test_crypto.c(main suite) — all six algorithms, including ML-KEM.test-refactor/client-server/wh_test_crypto_{rsa,ecc,curve25519,ed25519,mldsa}.c(the refactor suite has no ML-KEM file yet, so ML-KEM is main-suite only).Each test cross-checks the keygen-returned public key against
wh_Client_<Algo>ExportPublicKey(byte-equal) and proves it with a real operation — encrypt/HSM-decrypt, HSM-sign/client-verify, or ECDH round-trip. A negative test covers theEPHEMERAL+ NULL-arg contract.