# HMAC Keys

The Vertex can hold shares of HMAC keys (SHA-256, SHA-384 and SHA-512) and use them to sign messages without any single party ever holding the full key.
This is most commonly used to protect exchange API secrets: the [Exchange API Vault](/exchange-api-vault/intro) shards an HMAC API key across a Vertex cluster and signs trading requests with it via MPC.
For the cryptographic background, see the [Message Authentication Codes](/cryptography/hmac) page.

Unlike ECDSA and Ed25519 keys, HMAC keys have no MPC key generation: there is no `keygen` operation.
Instead, an existing HMAC key is split into secret shares outside the Vertex and each share is imported into its own party.

:::note[Endpoint Availability]
The HMAC endpoints are available as part of the **Exchange API Vault** product.
If your Vertex is not configured for it, these endpoints return a `404` response. Please contact the Sodot team for access.
:::

All examples below use `hmac-sha256`. The same endpoints exist for the other variants: replace the path prefix with `hmac-sha384` or `hmac-sha512`.

## Importing an HMAC Key

### Splitting the Key into Shares

Before importing, the full HMAC key must be split into secret shares, one per party.
The [HMAC Key Sharing SDK](/exchange-api-vault/hmac-key-sharing-sdk) does this in the end user's browser and encrypts each share for a specific Vertex node, so no part of your backend ever sees the full key.
The [Exchange API Vault tutorial](/exchange-api-vault/tutorial#secure-key-import-hmac-api-keys) shows this flow end to end.

Shares produced by the SDK are encrypted, so they are imported with `"encrypted": true`. Plaintext shares are imported with `"encrypted": false` (the default).

There are two ways to import the shares, depending on how your Vertex is deployed:

### Importing into a Cluster

If your Vertex servers are set up as a [cluster](/vertex/keygen/cluster_keygen), a single call to the [`cluster import-secret-share`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/POST/cluster/hmac-sha256/import-secret-share) endpoint on each Vertex imports that Vertex's share and creates the key in one step:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/cluster/hmac-sha256/import-secret-share' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "cluster_name": "<CLUSTER_NAME>",
  "key_share": "<KEY_SHARE_HEX>",
  "encrypted": true,
  "key_name": "<OPTIONAL_CUSTOM_NAME>"
}'
```

The parameters are:

* `cluster_name`: The name of the cluster to import the share into.
* `key_share`: The hex-encoded secret share for this Vertex.
* `encrypted` (optional, defaults to `false`): Whether the share is encrypted for this Vertex (set to `true` for shares produced by the HMAC Key Sharing SDK).
* `key_name` (optional): A human-readable name for the key.
* `policy_name` or `policy_id` (optional, provide at most one): A [Policy](/vertex/policies/overview) to attach to the created key.

In response, each Vertex returns the `key_id` it created for the imported share:

```json
{
  "key_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

:::note[Authorization]
This endpoint requires a User with the `KeysManager` or `KeysImporter` [Role](/vertex/users_and_access_control).
:::

### Importing without a Cluster

Without a cluster, the import is a two-step process on each party: first create an empty key share, then import the secret share into it.

#### Step 1: Create a Key Share

Each party calls the [`create`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/POST/hmac-sha256/create) endpoint to set up a new HMAC key share before the secret is imported:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/hmac-sha256/create?key_name=<OPTIONAL_CUSTOM_NAME>' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

In response we receive a `key_id` that references the key share and a `keygen_id` that identifies this party:

```json
{
  "key_id": "550e8400-e29b-41d4-a716-446655440000",
  "key_name": "my-exchange-api-key",
  "keygen_id": "JBSFMeRq5N66aQwBH2dBvWEGx5YZsn7UnZfswKQhuC9z"
}
```

Every party must send its `keygen_id` to all other parties through an authenticated communication channel before moving to the next step.

:::note[Authorization]
This endpoint requires a User with the `KeysManager` [Role](/vertex/users_and_access_control).
:::

#### Step 2: Import the Secret Share

Once each party has the `keygen_id`s of all other parties, it imports its secret share using the [`import-secret-share`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/POST/hmac-sha256/import/import-secret-share) endpoint, referencing the same `key_id` (or `key_name`) returned by `create`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/hmac-sha256/import/import-secret-share' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "key_id": "<KEY_ID>",
  "key_share": "<KEY_SHARE_HEX>",
  "encrypted": true,
  "others_keygen_ids": [
    "<KEYGEN_ID_OF_PARTY_2>",
    "<KEYGEN_ID_OF_PARTY_3>"
  ]
}'
```

The parameters are:

* `key_id` or `key_name` (provide exactly one): The key share created in the previous step.
* `key_share`: The hex-encoded secret share for this party.
* `encrypted` (optional, defaults to `false`): Whether the share is encrypted for this Vertex.
* `others_keygen_ids`: The `keygen_id`s of all other parties sharing this key.

Receiving a `200 OK` response indicates the share was imported successfully. A `failed_to_import_key_share` error indicates the share could not be decrypted or deserialized (see [Vertex Errors](/vertex/vertex_errors)).

:::note[Authorization]
This endpoint requires a User with the `KeysManager` or `KeysImporter` [Role](/vertex/users_and_access_control).
:::

:::tip[Validate the Import with a Signature]
HMAC keys have no public key, so the Vertex cannot validate the imported key material on import.
After importing, run a `sign` operation and verify the resulting HMAC signature against the relevant validating party (e.g. the exchange server).
:::

## Signing a Message

:::info
Before the Vertex participates in signing, it will check if there is a Policy attached to the key share being used.
If there is a Policy, the Vertex will [send](/vertex/policies/rule_server) the message data to the Rule Server for evaluation.
:::

Signing is an MPC operation between all three parties holding shares of the key.

### Creating a Room for MPC Signing

Each MPC signing operation requires a room, created using the [`create-room`](https://docs.sodot.dev/vertex-api-reference/#tag/common/POST/create-room) endpoint:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/create-room' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_size": 3
}'
```

### Signing

All parties then call the [`sign`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/POST/hmac-sha256/sign) endpoint with the same `room_uuid`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/hmac-sha256/sign' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID>",
  "message": "<HEX_ENCODED_MESSAGE>",
  "extra_data": "<HEX_ENCODED_EXTRA_DATA>"
}'
```

The parameters are:

* `room_uuid`: The Room UUID for this signing operation.
* `key_id` or `key_name` (provide exactly one): The key share to sign with.
* `message`: The message to sign, as an array of bytes encoded as a hex string.
* `extra_data` (optional): Hex-encoded extra data passed to the Rule Server to help it validate the request.

In response, each party receives the HMAC signature as a hex-encoded string:

```json
{
  "signature": "4dc32369515d8608b86e31f4555cf4f30a87509f47c81a2041579a89897fb05f"
}
```

:::note[Authorization]
This endpoint requires a User with the `CryptoUser` or `KeysManager` [Role](/vertex/users_and_access_control), as well as at least the `Use` [Key Permission](/vertex/users_and_access_control#key-ownership-and-permissions) for this key.
:::

## Listing Key Shares

To list all HMAC key shares associated with the authenticated User, call the [`list-key-shares`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/GET/hmac-sha256/list-key-shares) endpoint:

```bash
curl -L -X GET 'https://<YOUR_VERTEX>/hmac-sha256/list-key-shares' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

The response lists each key share together with its type, name and the Users and Groups that have permissions for it:

```json
{
  "key_shares": [
    {
      "key_id": "550e8400-e29b-41d4-a716-446655440000",
      "key_type": "HmacSha256",
      "name": "my-exchange-api-key",
      "key_users": [
        {
          "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "user_name": "trading-machine",
          "usage_permission": "admin"
        }
      ],
      "key_groups": []
    }
  ]
}
```

:::note[Authorization]
This endpoint requires a User with the `KeysManager` [Role](/vertex/users_and_access_control).
:::

## Deleting a Key Share

To delete a key share, call the [`delete-key-share`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/DELETE/hmac-sha256/delete-key-share) endpoint with either a `key_id` or a `key_name` query parameter (but not both):

```bash
curl -L -X DELETE 'https://<YOUR_VERTEX>/hmac-sha256/delete-key-share?key_id=<KEY_ID>' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

Receiving a `200 OK` response indicates that the key share was deleted.

:::warning[Irreversible Operation]
Deleting a key share is irreversible. Once the key share is deleted, it cannot be recovered.
:::

:::note[Authorization]
This endpoint requires a User with the `KeysManager` [Role](/vertex/users_and_access_control).
:::

## Exporting the Full Key (Exit MPC)

The parties holding shares of an HMAC key can cooperate to reconstruct the full secret key and deliver it to a single party.
This is an MPC operation: a threshold of the parties must participate, and only the designated recipient receives the key.

### Getting the Export ID of the Recipient

Each key share has an `export_id` that identifies it as an export recipient.
On the party that should receive the full key, call the [`export/exportId`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/GET/hmac-sha256/export/exportId) endpoint with either a `key_id` or a `key_name` query parameter (but not both):

```bash
curl -L -X GET 'https://<YOUR_VERTEX>/hmac-sha256/export/exportId?key_id=<KEY_ID>' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>'
```

In response we receive the `export_id` of the key share:

```json
{
  "export_id": "JBSFMeRq5N66aQwBH2dBvWEGx5YZsn7UnZfswKQhuC9z"
}
```

Send this `export_id` to the other parties before running the full export.

### Running the Full Export

Create a room (as shown in the [signing flow](#signing-a-message) above) and have all parties call the [`export/full`](https://docs.sodot.dev/vertex-api-reference/#tag/hmacsha256/POST/hmac-sha256/export/full) endpoint with the same `room_uuid` and the recipient's `export_id` as `export_to`:

```bash
curl -L -X POST 'https://<YOUR_VERTEX>/hmac-sha256/export/full' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: <USER_API_KEY>' \
--data-raw '{
  "room_uuid": "<ROOM_UUID>",
  "key_id": "<KEY_ID>",
  "export_to": "<EXPORT_ID_OF_RECIPIENT>"
}'
```

The parameters are:

* `room_uuid`: The Room UUID for this export operation.
* `key_id` or `key_name` (provide exactly one): The key to export.
* `export_to`: The `export_id` of the party that should receive the full key.

Only the party whose `export_id` matches `export_to` receives the full secret key (hex encoded) in its response; all other parties receive an empty `private_key`:

```json
{
  "private_key": "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"
}
```

:::warning[Exiting MPC]
After a full export, the recipient holds the complete HMAC key in one place, so the security guarantees of MPC no longer apply to that copy.
Handle the exported key accordingly, and consider deleting the shares if the key is being migrated out of the Vertex.
:::

:::note[Authorization]
Both export endpoints require a User with the `KeysManager` or `KeysExporter` [Role](/vertex/users_and_access_control).
:::
