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
61 changes: 61 additions & 0 deletions docs/src-ja/chapter05.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,67 @@ wolfHSMのキャッシュスロットの数は`WOLFHSM_NUM_RAMKEYS`で設定さ

`wh_Client_KeyErase`は、指定された鍵をキャッシュから削除し、NVMからも消去します。

## キャッシュされた鍵の公開鍵のみのエクスポート

`wh_Client_KeyExport`は、キャッシュされている鍵のバイト列をそのまま返します。公開鍵ペアがキャッシュされている場合、秘密鍵もクライアント側に送信されてしまうため、クライアント側で公開鍵のみを必要とする用途(署名検証、証明書作成など)では、HSM内に鍵を保持するセキュリティ上の利点が失われます。

wolfHSMはこの用途のために、キャッシュされた公開鍵ペアのキーIDを指定すると、サーバー側で公開鍵部分のみをDER形式で再生成し、クライアント側でwolfCryptの鍵オブジェクトにデシリアライズする専用のパスを提供します。秘密鍵はHSM内に残ります。

アルゴリズムごとのヘルパー関数がトランスポート層をラップしています:

```c
int wh_Client_RsaExportPublicKey(whClientContext* ctx, whKeyId keyId,
RsaKey* key, uint32_t label_len, uint8_t* label);
int wh_Client_EccExportPublicKey(whClientContext* ctx, whKeyId keyId,
ecc_key* key, uint16_t label_len, uint8_t* label);
int wh_Client_Ed25519ExportPublicKey(whClientContext* ctx, whKeyId keyId,
ed25519_key* key, uint16_t label_len, uint8_t* label);
int wh_Client_Curve25519ExportPublicKey(whClientContext* ctx, whKeyId keyId,
curve25519_key* key, uint16_t label_len, uint8_t* label);
int wh_Client_MlDsaExportPublicKey(whClientContext* ctx, whKeyId keyId,
MlDsaKey* key, uint16_t label_len, uint8_t* label);
```

例: HSM上でRSAの鍵ペアを生成し秘密鍵を`NONEXPORTABLE`でマークしたうえで、クライアント側で公開鍵を取得し、HSMがキャッシュされた秘密鍵で生成した署名を検証する。

```c
whKeyId keyId = WH_KEYID_ERASED;
RsaKey pub;

/* 1. HSM内で鍵ペアを生成。NONEXPORTABLEはフルエクスポートをブロックする。 */
wh_Client_RsaMakeCacheKey(&clientCtx, 2048, 0x10001, &keyId,
WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY |
WH_NVM_FLAGS_NONEXPORTABLE, 0, NULL);

/* 2. 公開鍵のみを取得する。NONEXPORTABLEはこのパスをブロックしない。 */
wc_InitRsaKey_ex(&pub, NULL, INVALID_DEVID);
wh_Client_RsaExportPublicKey(&clientCtx, keyId, &pub, 0, NULL);
/* pub.type == RSA_PUBLIC; クライアント側の検証に使用可能 */
```

### `WH_NVM_FLAGS_NONEXPORTABLE`との関係

`NONEXPORTABLE`は`wh_Client_KeyExport`および各アルゴリズムのフルエクスポート関数をブロックしますが、公開鍵のみのエクスポートパスは**ブロックしません**。公開鍵は機密性を持たない情報であり、もし公開鍵の取り出しも禁止してしまうと、キャッシュされた鍵を外部での検証や鍵配送に使えなくなるためです。公開鍵の取り出しも禁止したい場合は、そもそも鍵を生成・インポートしないか、用途が終わった段階で`wh_Client_KeyErase`で削除してください。

### DMAバリアント

`WOLFHSM_CFG_DMA`が有効な場合、サーバーが公開鍵のDERをクライアント提供のバッファに直接DMAで書き込む並列APIが利用できます。これにより通信バッファ経由のコピーを回避できます。セマンティクス(`NONEXPORTABLE`の取り扱い、アルゴリズムセレクタ、エラー処理)は非DMAパスと同一です。

```c
int wh_Client_KeyExportPublicDma(whClientContext* c, whKeyId keyId,
uint16_t algo, const void* keyAddr, uint16_t keySz,
uint8_t* label, uint16_t labelSz, uint16_t* outSz);

int wh_Client_MlDsaExportPublicKeyDma(whClientContext* ctx, whKeyId keyId,
MlDsaKey* key, uint16_t label_len, uint8_t* label);
```

`wh_Client_KeyExportPublicDma`は汎用トランスポートで、呼び出し側は生の公開鍵DERを受け取って自身でデシリアライズします。`wh_Client_MlDsaExportPublicKeyDma`は既存の`wh_Client_MlDsaExportKeyDma`に対応するML-DSA専用ヘルパーで、ML-DSAの大きな公開鍵DERを通信バッファにコピーせずに済ませたい場合に使用できます。

### ワイヤプロトコル

公開鍵のみのエクスポートは、専用のキーストアアクション`WH_KEY_EXPORT_PUBLIC`および`WH_KEY_EXPORT_PUBLIC_DMA`を使用します。キャッシュされた鍵は不透明なDERバイト列として保存されているため、サーバー側でどのように解釈すべきかをリクエストごとにアルゴリズムセレクタ(`wolfhsm/wh_common.h`の`WH_KEY_ALGO_*`)で指定します。独自トランスポートを実装する場合は、`WH_KEY_EXPORT`/`WH_KEY_EXPORT_DMA`と同様にこれらのアクションもルーティングしてください。

## 暗号操作

クライアントアプリケーションでwolfCryptを使用する場合、`devId`引数として`WOLFHSM_DEV_ID`を渡すことで、互換性のある暗号化操作をwolfHSMサーバーで実行できます。
Expand Down
90 changes: 90 additions & 0 deletions docs/src/chapter05.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,96 @@ wh_Client_KeyErase(clientCtx, keyId);
`wh_Client_KeyExport` will read the key contents out of the HSM back to the client.
`wh_Client_KeyErase` will remove the indicated key from cache and erase it from NVM.

## Exporting only the public half of a cached key

`wh_Client_KeyExport` returns the raw cached key bytes. For a cached public-key
keypair this includes the private material, which defeats the security benefit
of leaving the key inside the HSM when the caller only needs the public half
(for example, to verify a signature on the client side or to package the key
into a certificate).

wolfHSM provides a dedicated public-only export path that, given a key ID for
a cached public-key keypair, re-emits only the public portion on the server
and deserializes it into a wolfCrypt key object on the client. The private
key never leaves the HSM.

Per-algorithm helpers wrap the transport layer:

```c
int wh_Client_RsaExportPublicKey(whClientContext* ctx, whKeyId keyId,
RsaKey* key, uint32_t label_len, uint8_t* label);
int wh_Client_EccExportPublicKey(whClientContext* ctx, whKeyId keyId,
ecc_key* key, uint16_t label_len, uint8_t* label);
int wh_Client_Ed25519ExportPublicKey(whClientContext* ctx, whKeyId keyId,
ed25519_key* key, uint16_t label_len, uint8_t* label);
int wh_Client_Curve25519ExportPublicKey(whClientContext* ctx, whKeyId keyId,
curve25519_key* key, uint16_t label_len, uint8_t* label);
int wh_Client_MlDsaExportPublicKey(whClientContext* ctx, whKeyId keyId,
MlDsaKey* key, uint16_t label_len, uint8_t* label);
```

Example: generate an RSA keypair on the HSM with the private key marked
`NONEXPORTABLE`, obtain the public key on the client, and verify a signature
the HSM produced using the cached private key.

```c
whKeyId keyId = WH_KEYID_ERASED;
RsaKey pub;

/* 1. Generate keypair inside the HSM. NONEXPORTABLE blocks full export. */
wh_Client_RsaMakeCacheKey(&clientCtx, 2048, 0x10001, &keyId,
WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY |
WH_NVM_FLAGS_NONEXPORTABLE, 0, NULL);

/* 2. Fetch only the public half. NONEXPORTABLE does NOT block this. */
wc_InitRsaKey_ex(&pub, NULL, INVALID_DEVID);
wh_Client_RsaExportPublicKey(&clientCtx, keyId, &pub, 0, NULL);
/* pub.type == RSA_PUBLIC; usable for client-side verification */
```

### Interaction with `WH_NVM_FLAGS_NONEXPORTABLE`

`NONEXPORTABLE` blocks `wh_Client_KeyExport` and the per-algorithm full-export
helpers. It does **not** block the public-only export path, because public key
material is non-sensitive — preventing it from ever leaving the HSM would make
the cached key unusable for any external verification or key-transport use
case. If you want to block even public extraction, do not generate or import
the key in the first place, or remove it with `wh_Client_KeyErase` after it
has served its purpose.

### DMA variant

For builds with `WOLFHSM_CFG_DMA` enabled, parallel APIs let the server
write the public DER directly into a client-provided buffer using the
existing DMA transport, avoiding the comm-buffer copy. The semantics
(NONEXPORTABLE carve-out, algo selector, error handling) are identical to
the non-DMA path.

```c
int wh_Client_KeyExportPublicDma(whClientContext* c, whKeyId keyId,
uint16_t algo, const void* keyAddr, uint16_t keySz,
uint8_t* label, uint16_t labelSz, uint16_t* outSz);

int wh_Client_MlDsaExportPublicKeyDma(whClientContext* ctx, whKeyId keyId,
MlDsaKey* key, uint16_t label_len, uint8_t* label);
```

`wh_Client_KeyExportPublicDma` is the generic transport — callers receive
raw public DER and deserialize it themselves. `wh_Client_MlDsaExportPublicKeyDma`
mirrors the existing `wh_Client_MlDsaExportKeyDma` per-algorithm helper for
the case where ML-DSA's large public DER benefits most from skipping the
comm-buffer staging.

### Wire protocol

The public-only export uses dedicated keystore actions,
`WH_KEY_EXPORT_PUBLIC` and `WH_KEY_EXPORT_PUBLIC_DMA`, with a per-request
algorithm selector (`WH_KEY_ALGO_*` in `wolfhsm/wh_common.h`) because
cached keys are stored as opaque DER bytes and the server needs to know
how to decode them. Integrators implementing custom transports should
route these actions the same way they route `WH_KEY_EXPORT` and
`WH_KEY_EXPORT_DMA`.

## Key Revocation

Key revocation updates key metadata to prevent further cryptographic use without destroying storage. Revocation clears all `WH_NVM_FLAGS_USAGE_*` bits and sets `WH_NVM_FLAGS_NONMODIFIABLE`. The revoked state is persisted when the key is already committed to NVM.
Expand Down
176 changes: 176 additions & 0 deletions src/wh_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,94 @@ int wh_Client_KeyExport(whClientContext* c, whKeyId keyId, uint8_t* label,
return ret;
}

int wh_Client_KeyExportPublicRequest(whClientContext* c, whKeyId keyId,
uint16_t algo)
{
whMessageKeystore_ExportPublicRequest* req = NULL;

if (c == NULL || keyId == WH_KEYID_ERASED) {
return WH_ERROR_BADARGS;
}

req = (whMessageKeystore_ExportPublicRequest*)wh_CommClient_GetDataPtr(
c->comm);
if (req == NULL) {
return WH_ERROR_BADARGS;
}
req->id = keyId;
req->algo = algo;

return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY,
WH_KEY_EXPORT_PUBLIC, sizeof(*req),
(uint8_t*)req);
}

int wh_Client_KeyExportPublicResponse(whClientContext* c, uint8_t* label,
uint16_t labelSz, uint8_t* out,
uint16_t* outSz)
{
uint16_t group;
uint16_t action;
uint16_t size;
int ret;
whMessageKeystore_ExportPublicResponse* resp = NULL;
uint8_t* packOut;

if (c == NULL || outSz == NULL) {
return WH_ERROR_BADARGS;
}

resp = (whMessageKeystore_ExportPublicResponse*)wh_CommClient_GetDataPtr(
c->comm);
if (resp == NULL) {
return WH_ERROR_BADARGS;
}
packOut = (uint8_t*)(resp + 1);

ret = wh_Client_RecvResponse(c, &group, &action, &size, (uint8_t*)resp);
if (ret == WH_ERROR_OK) {
if (resp->rc != 0) {
ret = resp->rc;
}
else {
if (out == NULL) {
*outSz = resp->len;
}
else if (*outSz < resp->len) {
ret = WH_ERROR_ABORTED;
}
else {
memcpy(out, packOut, resp->len);
*outSz = resp->len;
}
if ((ret == WH_ERROR_OK) && (label != NULL)) {
if (labelSz > sizeof(resp->label)) {
memcpy(label, resp->label, WH_NVM_LABEL_LEN);
}
else {
memcpy(label, resp->label, labelSz);
}
}
}
}
return ret;
}

int wh_Client_KeyExportPublic(whClientContext* c, whKeyId keyId, uint16_t algo,
uint8_t* label, uint16_t labelSz, uint8_t* out,
uint16_t* outSz)
{
int ret;
ret = wh_Client_KeyExportPublicRequest(c, keyId, algo);
if (ret == 0) {
do {
ret = wh_Client_KeyExportPublicResponse(c, label, labelSz, out,
outSz);
} while (ret == WH_ERROR_NOTREADY);
}
return ret;
}

int wh_Client_KeyCommitRequest(whClientContext* c, whNvmId keyId)
{
whMessageKeystore_CommitRequest* req = NULL;
Expand Down Expand Up @@ -1525,6 +1613,94 @@ int wh_Client_KeyExportDma(whClientContext* c, uint16_t keyId,
}
return ret;
}

int wh_Client_KeyExportPublicDmaRequest(whClientContext* c, whKeyId keyId,
uint16_t algo, const void* keyAddr,
uint16_t keySz)
{
whMessageKeystore_ExportPublicDmaRequest* req = NULL;

if (c == NULL || keyId == WH_KEYID_ERASED) {
return WH_ERROR_BADARGS;
}

req =
(whMessageKeystore_ExportPublicDmaRequest*)wh_CommClient_GetDataPtr(
c->comm);
if (req == NULL) {
return WH_ERROR_BADARGS;
}
req->id = keyId;
req->algo = algo;
req->key.addr = (uint64_t)((uintptr_t)keyAddr);
req->key.sz = keySz;

return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY,
WH_KEY_EXPORT_PUBLIC_DMA, sizeof(*req),
(uint8_t*)req);
}

int wh_Client_KeyExportPublicDmaResponse(whClientContext* c, uint8_t* label,
uint16_t labelSz, uint16_t* outSz)
{
uint16_t resp_group;
uint16_t resp_action;
uint16_t resp_size;
int rc;
whMessageKeystore_ExportPublicDmaResponse* resp = NULL;

if (c == NULL || outSz == NULL) {
return WH_ERROR_BADARGS;
}

resp =
(whMessageKeystore_ExportPublicDmaResponse*)wh_CommClient_GetDataPtr(
c->comm);
if (resp == NULL) {
return WH_ERROR_BADARGS;
}

rc = wh_Client_RecvResponse(c, &resp_group, &resp_action, &resp_size,
(uint8_t*)resp);
if (rc == 0) {
if ((resp_group != WH_MESSAGE_GROUP_KEY) ||
(resp_action != WH_KEY_EXPORT_PUBLIC_DMA) ||
(resp_size != sizeof(*resp))) {
rc = WH_ERROR_ABORTED;
}
else {
if (resp->rc != 0) {
rc = resp->rc;
}
else {
*outSz = resp->len;
if (label != NULL) {
if (labelSz > WH_NVM_LABEL_LEN) {
labelSz = WH_NVM_LABEL_LEN;
}
memcpy(label, resp->label, labelSz);
}
}
}
}
return rc;
}

int wh_Client_KeyExportPublicDma(whClientContext* c, whKeyId keyId,
uint16_t algo, const void* keyAddr,
uint16_t keySz, uint8_t* label,
uint16_t labelSz, uint16_t* outSz)
{
int ret;
ret = wh_Client_KeyExportPublicDmaRequest(c, keyId, algo, keyAddr, keySz);
if (ret == 0) {
do {
ret = wh_Client_KeyExportPublicDmaResponse(c, label, labelSz,
outSz);
} while (ret == WH_ERROR_NOTREADY);
}
return ret;
}
#endif /* WOLFHSM_CFG_DMA */

#endif /* WOLFHSM_CFG_ENABLE_CLIENT */
Loading
Loading