Add ExportPublicKey API for cached asymmetric keys#346
Conversation
bigbrett
left a comment
There was a problem hiding this comment.
Overall, looks great. A few nits and suggetsions, with some architectural things as well.
One thing I'm not sure about is the bypass of the NONEXPORTABLE attribute. I totally see why you would do it this way, but I don't want to prevent a customer from making an object fully nonexportable (e.g. unreadable) for whatever reason. Therefore I wonder if it makes sense to have additional attributes here that only apply to keys? Happy to brainstorm this.
84c66a6 to
719669c
Compare
|
Addressed the review comments (except for two which are open for discussion). I also had to increase |
|
@Frauschi can you pls resolve conflicts |
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.
|
Now properly rebased to main with conflicts fixed. |
rizlik
left a comment
There was a problem hiding this comment.
I don’t like the server trusting the client to specify which algorithm to use when decoding the DER-encoded key.
It’s probably “safe,” since decoding should fail if the algorithm is wrong, but I don’t think we should rely on DER encoding to defend against this client-induced type confusion.
What are your thoughts?
|
@rizlik I see your point but what are the alternatives? Pretty sure this is the "blind deserialization" problem we face basically everywhere, not just in this PR. At some point, there is a DER blob stored in NVM and the server needs to know "how" to decode it. The client is the one that is requesting an operation to be done with said DER blob, so at some level it is always "assumed" that the DER blob is a properly formatted key of a given type, and then DER deserialization will fail if this is not the case. Do you have any other ideas? I suppose that a type could be supplied in the NVM metadata for keys that are provisioned offline, but that really doesn't buy you much more "safety" and is also outside the scope of this PR as it would apply to all algos |
|
@bigbrett isn't DER decoding currently
You are right, this is a currently existing problem. outside scope for this PR. |
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-agnosticwh_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:
WH_KEY_EXPORT_PUBLIC+ per-algorithm client wrappers.WH_KEY_EXPORT_PUBLIC_DMA+wh_Client_KeyExportPublicDmageneric transport + per-algorithm DMA wrapper for ML-DSA (matches the existing ML-DSA-only DMA-export precedent).Algorithms wired end-to-end
wh_Client_RsaExportPublicKeywh_Client_EccExportPublicKeywh_Client_Ed25519ExportPublicKeywh_Client_Curve25519ExportPublicKeywh_Client_MlDsaExportPublicKeywh_Client_MlDsaExportPublicKeyDmaDesign notes
WH_KEY_EXPORTinstead of duplicating it per algorithm. The selector is a newWH_KEY_ALGO_*enum inwolfhsm/wh_common.h.NONEXPORTABLEcarve-out.WH_NVM_FLAGS_NONEXPORTABLEblocks 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 dedicatedWH_KS_OP_EXPORT_PUBLICbranch in_KeystoreCheckPolicy(not a silent bypass) and is called out explicitly in the docs.cacheBuf/cacheMetausing the existingwh_Crypto_*DeserializeKeyDerhelpers (which already fall back to public-only decode), then re-emits public-only DER via the matchingwc_*PublicKeyToDer. No new server-side crypto helpers introduced.resp_packet(the DMA response struct itself only occupies the header), thenwhServerDma_CopyToClients it into the client-provided buffer. The response sent over the wire is justsizeof(resp).WH_ERROR_NOTFOUND.wc_*PublicKeyToDerreturning 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_ENUMso existing numeric values are preserved):WH_KEY_EXPORT_PUBLICWH_KEY_EXPORT_PUBLIC_DMA(underWOLFHSM_CFG_DMA)Each takes a
uint16_t algoselector alongside the standardkeyId. Integrators with custom transports route these the same way they routeWH_KEY_EXPORT/WH_KEY_EXPORT_DMA.Test plan
End-to-end per algorithm:
NONEXPORTABLEcached key → full export denied withWH_ERROR_ACCESS→wh_Client_RsaExportPublicKeysucceeds → client-sidewc_RsaPublicEncrypt/ HSM-sidewc_RsaPrivateDecryptround-trips plaintext. Includes wrong-algo and unknown-keyId (WH_ERROR_NOTFOUND) negative cases.type == ECC_PUBLICKEY.wh_Client_Ed25519Sign, client verifies withwc_ed25519_verify_msg; assertspubKeySet==1 && privKeySet==0.local_priv·hsm_pub == hsm_priv·local_pub.wh_Client_MlDsaExportPublicKey→ assertspubKeySet==1 && prvKeySet==0.DMA-specific coverage:
wh_Client_KeyExportPublicDma(generic transport).wh_Client_MlDsaExportPublicKeyDma+ flag assertions, plus a byte-identity check comparing DMA-path DER vs. non-DMA-path DER for the same cached key, plus aWH_ERROR_NOSPACEnegative test with an undersized client buffer.Docs updated in
docs/src/chapter05.mdanddocs/src-ja/chapter05.md.