TKeeper supports threshold encryption and decryption using an integrated ECIES construction (ElGamal KEM) with a symmetric payload cipher.
Key properties:
- Private key material remains distributed as shares; the full private key is not reconstructed.
- Encryption/decryption are quorum-based operations executed by the keeper cluster.
- Payload encryption uses a standard symmetric AEAD cipher.
Curve support: threshold ECIES is available only for keys on secp256k1.
Supported payload cipher
TKeeper currently supports a single payload cipher:
AES_GCM
The API exposes this as algorithm, but the only accepted value is AES_GCM.
Encrypt
- POST
/v1/keeper/ecies/encrypt - Permission:
tkeeper.key.<logicalId>.encrypt
Request
{
"keyId": "custody-key",
"tweak": "my-user-id",
"algorithm": "AES_GCM",
"plaintext64": "base64(plaintext-bytes)"
}
Fields:
| Field | Type | Required | Meaning |
|---|---|---|---|
keyId |
string | yes | Key logicalId. |
tweak |
string | no | Optional tweak input used to derive a child key from the base key for this request. |
algorithm |
enum | no | Payload cipher. Default: AES_GCM. |
plaintext64 |
string | yes | Base64-encoded plaintext bytes. |
Key Tweaking (optional)
tweak allows deterministic key derivation from one base key (keyId) for encryption/decryption flows.
This is useful when one master key should produce separate derived keys per user, account, or domain input.
Warning
tweakin TKeeper is not Taproot tweaking and not BIP key derivation.
It is an application-level key derivation input for threshold operations.
Response
{
"ciphertext64": "base64(ciphertext-bytes)",
"generation": 3
}
Fields:
| Field | Meaning |
|---|---|
ciphertext64 |
Base64-encoded ciphertext bytes produced by threshold ECIES. |
generation |
Key generation used for encryption. |
Warning Persist the returned
generationalongside the ciphertext. If the key is rotated/refreshed later, decryption will require the original generation.
Decrypt
- POST
/v1/keeper/ecies/decrypt - Permission:
tkeeper.key.<logicalId>.decrypt
Request
{
"keyId": "custody-key",
"tweak": "my-user-id",
"algorithm": "AES_GCM",
"ciphertext64": "base64(ciphertext-bytes)",
"generation": 3,
"approvals": {
"keeperId": 1,
"nonce": "ops-2026-02-12-020",
"timestamp": 1764420000123,
"proofs": [
{
"fingerprint": "base64(sha256(public-key-bytes))",
"signature64": "base64(signature-over-request-hash)"
}
]
}
}
Fields:
| Field | Type | Required | Meaning |
|---|---|---|---|
keyId |
string | yes | Key logicalId. |
tweak |
string | no | Optional tweak input. Must match the value used at encryption time. |
algorithm |
enum | no | Payload cipher. Default: AES_GCM. |
ciphertext64 |
string | yes | Base64-encoded ciphertext bytes. |
generation |
int | no | Key generation to decrypt with. If omitted, the current generation is used. |
approvals |
object | no | Required when four-eye control is active for the key. |
Four-eye approvals (when enabled)
If the key has policy.fourEye, /v1/keeper/ecies/decrypt requires approvals with at least m valid proofs.
For approval schema, keeperId semantics, canonical payload signing rules, and replay/availability trade-offs, see:
Response
{
"plaintext64": "base64(plaintext-bytes)"
}
Notes
- If encryption used
tweak, decryption must use the sametweak.