commit d2f671f6749d750dc33a3ee73cf49e306d6f5726
parent 567af687099de4fee506a673ed4ea575149270d6
Author: Antoine A <>
Date: Wed, 28 Jan 2026 15:08:12 +0100
dd80: improve API and PoW security
Diffstat:
1 file changed, 26 insertions(+), 35 deletions(-)
diff --git a/design-documents/080-short-wire-subject.rst b/design-documents/080-short-wire-subject.rst
@@ -176,6 +176,8 @@ TODO: do we add a prefix ?
subject: string;
}
+.. ts:def:: BitcoinSubject
+
interface BitcoinSubject {
// Subject for Bitcoin networks
type: "BTC";
@@ -184,6 +186,8 @@ TODO: do we add a prefix ?
address: string;
}
+.. ts:def:: NzSbiSubject
+
interface NzSbiSubject {
// Subject for New Zealand Settlement Before Interchange system
type: "NZ-SBI";
@@ -207,7 +211,11 @@ Sometimes some clients will have more restriction than others. We will sometimes
// CH-YUH: N chars ?
// BTC: bitcoin address
// NZ-SBI: New Zealand special three fields
- type SubjectFormat = "SIMPLE" | "CH-YUH" | "BTC" | "NZ-SBI"
+ type SubjectFormat =
+ | "SIMPLE"
+ | "CH-YUH"
+ | "BTC"
+ | "NZ-SBI";
The backend will expose the supported formats in its ``/config`` endpoint.
@@ -220,20 +228,14 @@ Proof of Work
To prevent bad actors from exhausting the encoding space we SHOULD add some PoW to each request. It's stays optional are in some cases the encoding space is so large than no special encoding is used.
-The algorithm and the difficulty can change over time and the client needs to handle those changes.
-
As the available entropy space goes down, the difficulty to register a new key increase automatically as new derived keys will collide with existing ones. The PoW difficulty is another layer of protection that should be based on request rate.
-The PoW challenge should be based on what makes a transfer unique: the public key algorithm, the public key bytes, and the transfer type. We deliberately made the subject format not part of the challenge so that users could easily try different formats. This is not a problem because key formatting is fast, idempotent and stateless. Wallets should store the associated PoW solution for reuse.
-
.. ts:def:: PowChallenge
interface PowChallenge {
// Algorithm used to perform PoW, new ones will be added in the future
// PBKDF2-HMAC-SHA256: PBKDF2 using SHA-512
- // none: no PoW is required
- // TODO: what is a good value for no POW: null or none or identify
- alg: "PBKDF2-HMAC-SHA256" | "none";
+ alg: "PBKDF2-HMAC-SHA256";
// How many iterations to run, used by PBKDF2-HMAC-SHA256
// Set to zero for none
@@ -243,9 +245,12 @@ The PoW challenge should be based on what makes a transfer unique: the public ke
difficulty: Integer;
// Unique salt to use when solving the challenge
- salt: String;
+ salt: string;
}
+The backend provides a salt challenge encoded as a string and expects the nonce solution to be a string. This design will work with all clients on all platforms.
+The challenge can be linked to a request, a user, a session, etc. It can be reused for a certain number of requests or for a certain period of time. All of these options should be supported by the current API, allowing us to strengthen security in the future in a transparent manner.
+
Auditor
-------
@@ -270,22 +275,10 @@ API
.. ts:def:: WireConfig
interface WireConfig {
- // Current pow challenge being used, can change over time
- pow: PowChallenge;
-
// Supported formats for registration, there must at least one.
supported_formats: SubjectFormat[];
}
-.. http:get:: /challenge
-
- Ask for a PoW challenge to solve.
-
- **Response:**
-
- :http:statuscode:`200 Ok`:
- Response is a `PowChallenge`.
-
.. http:post:: /registration
@@ -294,10 +287,16 @@ API
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. Reusing a
- mapping public key replace previous mapping.
+ 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 must be solved. On success the challenge will be consumed.
+ 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
+ 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.
**Request:**
@@ -314,7 +313,7 @@ API
type: "reserve" | "kyc";
// Pow salt used
- pow: String;
+ pow: string;
// Subject format requested
format: SubjectFormat;
@@ -328,26 +327,18 @@ API
// Optional signature of the raw public key using the mapping key,
// required if map is not null
signature?: Base32; // TODO add type to common types
-
- // Challenge salt
- // Use empty string if challenge alg is ``none``
- challenge_salt: String;
-
- // Challenge nonce solution
- // Use empty string if challenge alg is ``none``
- challenge_nonce: String;
}
**Response:**
:http:statuscode:`200 Ok`:
Response is a `SubjectResult`.
+ :http:statuscode:`202 Accepted`:
+ PoW is required for this operation. This returns the `PowChallenge` response.
:http:statuscode:`400 Bad request`:
Input data was invalid.
:http:statuscode:`409 Conflict`:
* ``TALER_EC_BANK_UNSUPPORTED_FORMAT``: format is not supported.
- * ``TALER_EC_BANK_CHALLENGE_FAILURE``: PoW challenge solution is wrong or challenge is unknown or challenge have checks failed.
- * ``TALER_EC_BANK_CHALLENGE_UNKNOWN``: PoW challenge is unknown or already consumed.
* ``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.