commit 5dffbf846dac3499f7bccb36fcb62e0eb1bf40b5
parent 63fe26cb1845878094469c437302d57ab48d8527
Author: Antoine A <>
Date: Tue, 17 Feb 2026 14:36:16 +0100
dd80: improve subject derivation
Diffstat:
1 file changed, 61 insertions(+), 19 deletions(-)
diff --git a/design-documents/080-short-wire-subject.rst b/design-documents/080-short-wire-subject.rst
@@ -140,17 +140,59 @@ configurable **hold delay** or such a registration is received.
Subject derivation
------------------
-When the encoding space is not limited or large enough like in SEPA transfers, no special derivation logic is used and we use the existing one.
+When the encoding space is not limited or large enough like in SEPA transfers,
+no special derivation logic is used and we use the existing one.
-The manner in which subjects are derived is not specified but the API must be idempotent and the derivation method must use the entire coding space.
+The manner in which subjects are derived is voluntarily not specified, clients
+should not expect anything about the derivation logic and treat it as a black box.
-An efficient way is to make the derivation idempotent and use hashing, for example you can make a SHA-512 hash of the key and metadata: SHA-512("RESERVE" + "ECDSA" + raw key bytes) or SHA-512("KYC" + "SLH-DSA" + raw key bytes). Then truncate to the number of bytes we can fill in the subject and encoded using a supported alphabet.
+As the API must idempotent and derivation must use the entire coding space I expect
+all implementation to converge on the same deterministic algorithm.
-Client should not have any expectation about how subject derivation works and changing the method should be possible in a non breaking way.
+All non trivial subject derivation would start by making a SHA-512 hash of the key and metadata:
+SHA-512("RESERVE" + "ECDSA" + raw key bytes) or SHA-512("KYC" + "SLH-DSA" + raw key bytes).
-- SIMPLE: BASE32 encode and trunc
+For binary subject format like BTC we simply truncate the hash. For text subject format it's way
+harder as we need to choose the alphabet to use for encoding.
+We currently use Crockford's Base32 because it is case-insensitive and typo complacent.
+However it make a poor use of the available entropy.
+
+We will have some format that only accept numbers 0-9 (10 chars), some will only accept simple
+ASCII A-Za-z0-9 (62 chars) and most will also accept some special characters A-Za-z0-9-: (64 characters).
+
+When we have enough space we should simply use our current Crockford’s Base32 and truncate the subject.
+It's working well and is efficient. With 16 characters we can encode 80bits and that is enough entropy
+for our use case.
+
+If we have access to less characters or have very limited alpahbet we will have to use a more complex
+approach an Arbitrary-Precision Base Conversion algorithm. We treat the hash as a single unsigned integer in base 256
+that we convert into a base A (length of the alphabet). Then using successive euclidean division we can ensure
+mathematical fariness across any alphabet size.
+
+.. code-block:: kotlin
+
+ import java.math.BigInteger
+ import java.security.MessageDigest
+
+ fun deterministicEncode(data: ByteArray, alphabet: String, n: Int): String {
+ val hash = MessageDigest.getInstance("SHA-512").digest(data)
+
+ var value = BigInteger(1, hash)
+ val base = BigInteger.valueOf(alphabet.length.toLong())
+ val result = StringBuilder()
+
+ repeat(n) {
+ val (quotient, remainder) = value.divideAndRemainder(base)
+ result.append(alphabet[remainder.toInt()])
+ value = quotient
+ }
+ return result.reverse().toString()
+ }
+
+- SIMPLE: no special encoding use the current very verbose encoding
+- CH-YUH: BASE32 encode and trunc N chars
+- NZ-SBI: BASE32 encode and trunc 36 char split in 3 chunk of 12 char
- BTC: trunc and encode in segwit address
-- NZ-SBI: BASE32 encode and trunc 36 char
.. ts:def:: TransferSubject
@@ -202,14 +244,14 @@ Sometimes some clients will have more restriction than others. We will sometimes
// Subject format supported by the client
// SIMPLE: SEPA like format >= 140 chars
- // CH-YUH: N chars ?
- // BTC: bitcoin address
+ // CH-YUH: N chars ?
// NZ-SBI: New Zealand special three fields
+ // BTC: bitcoin address
type SubjectFormat =
| "SIMPLE"
| "CH-YUH"
- | "BTC"
- | "NZ-SBI";
+ | "NZ-SBI"
+ | "BTC";
The backend will expose the supported formats in its ``/config`` endpoint.
@@ -234,7 +276,7 @@ As the available entropy space goes down, the difficulty to register a new key i
// How many iterations to run, must be > 0
iterations: Integer;
- // Number of lead bits that must be zero for the challenge to be accepted,
+ // Number of lead bits that must be zero for the challenge to be accepted,
// must be > 0
difficulty: Integer;
@@ -277,17 +319,17 @@ API
.. http:post:: /registration
Register a public key for wire transfer use.
-
- This endpoint generate an appropriate subject to link a transfer to the
+
+ This endpoint generate an appropriate subject to link a transfer to the
registered public key.
- A mapping public key can also be used for repeated wire transfers.
+ A mapping public key can also be used for repeated wire transfers.
Reusing a mapping public key replace previous mapping.
- As this endpoint is unauthenticated a PoW challenge may need to be
- solved. In this case the solution is included using the following HTTP
+ As this endpoint is unauthenticated a PoW challenge may need to be
+ solved. In this case the solution is included using the following HTTP
headers: ``Taler-Challenge-PoW-Salt`` and ``Taler-Challenge-PoW-Nonce``.
- A challenge solution might be reused, clients should store and reuse
+ A challenge solution might be reused, clients should store and reuse
previous solution.
A successfully completed challenge may be followed by a new challenge, and several challenges may need to be successfully completed. The client must handle this correctly and try again with the new challenge.
@@ -295,7 +337,7 @@ API
**Request:**
.. ts:def:: SubjectRequest
-
+
interface SubjectRequest {
// Public key algorithm;
alg: "ECDSA";
@@ -332,7 +374,7 @@ API
* ``TALER_EC_BANK_UNSUPPORTED_FORMAT``: format is not supported.
* ``TALER_EC_BANK_DERIVATION_REUSE``: derived short subject is already used, you should retry using another key.
* ``TALER_EC_BANK_BAD_SIGNATURE``: signature is invalid.
-
+
**Details:**