..
This file is part of GNU TALER.
Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 2.1, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see
@author Christian Grothoff
@author Pius Loosli
=====================
The Donau RESTful API
=====================
The API specified here follows the :ref:`general conventions `
for all details not specified in the individual requests.
The `glossary `_
defines all specific terms used in this section.
.. contents:: Table of Contents
.. _donau-overview:
------------
API Overview
------------
This is intended to provide a quick overview of the whole REST API. For a more detailed view of the protocol, see the protocol specification.
The chapters group the families of requests frequently encountered when using the Donau API:
* :ref:`Status information`: get the public signing keys of the Donau, the donation unit key, the Donaus config or some entropy
* :ref:`Issue receipts`: For use by charities: Issue receipts for blinded unique donor ids.
* :ref:`Submit receipts`: Receive the receipts and, if valid, add all of it's donation units to the donor total. Returns a signature on the total yearly donation amount, hash of taxid+salt and year.
* :ref:`Charity administration and status information`:
* For use by administrators to add/modify a charity
* For use by charities to get their remaining donation volume
.. include:: tos.rst
.. _donau_status:
----------------------------------------
Donau public keys and status information
----------------------------------------
This API is used by donors and charities to obtain global information about
the Donau, such as online signing keys and available donation units. This is
typically the first call any Donau client makes, as it returns information
required to process all of the other interactions with the Donau. The
returned information is secured by signature(s) from the Donau, especially the
long-term offline signing key of the Donau, which clients should cache.
.. http:get:: /keys
Get a list of all donation units keys offered by the Donau,
as well as the Donau's current online signing key (used for donation statements).
**Request:**
:query last_issue_date: Optional argument specifying the maximum value of
any of the ``stamp_start`` members of the
donation unit keys of a ``/keys`` response that is
already known to the client. Allows the Donau to
only return keys that have changed since that
timestamp. The given value must be an unsigned
64-bit integer representing seconds after 1970. If
the timestamp does not exactly match the
``stamp_start`` of one of the donation unit keys, all
keys are returned.
**Response:**
:http:statuscode:`200 OK`:
The Donau responds with a `DonauKeysResponse` object. This request should
virtually always be successful. It only fails if the Donau is misconfigured or
has not yet been provisioned with key signatures via ``taler-donau-offline``.
**Details:**
.. ts:def:: DonauKeysResponse
interface DonauKeysResponse {
// libtool-style representation of the Donau protocol version, see
// https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
// The format is "current:revision:age".
version: string;
// Financial domain this Donau operates for.
domain: string;
// The Donau's base URL.
base_url: string;
// The Donau's currency.
currency: string;
// How many digits should the amounts be rendered
// with by default. Small capitals should
// be used to render fractions beyond the number
// given here (like on gas stations).
currency_fraction_digits: Integer;
// Donation Units offered by this Donau
donation_units: DonationUnitKeyGroup[];
// The Donau's signing keys.
signkeys: SignKey[];
}
.. ts:def:: DonationUnitKeyGroup
type DonationUnitKeyGroup =
| DonationUnitKeyGroupRsa
| DonationUnitKeyGroupCs;
.. ts:def:: DonationUnitKeyGroupRsa
interface DonationUnitKeyGroupRsa extends DonationUnitKeyGroupCommon {
cipher: "RSA";
donation_units: ({
rsa_pub: RsaPublicKey;
} & DonationUnitKeyCommon)[];
}
.. ts:def:: DonationUnitKeyGroupCs
interface DonationUnitKeyGroupCs extends DonationUnitKeyGroupCommon {
cipher: "CS";
donation_units: ({
cs_pub: Cs25519Point;
} & DonationUnitKeyCommon)[];
}
.. ts:def:: DonationUnitKeyGroupCommon
// Common attributes for all donation unit groups
interface DonationUnitKeyGroupCommon {
// How much was donated based on this donation receipt.
value: Amount;
}
.. ts:def:: DonationUnitKeyCommon
interface DonationUnitKeyCommon {
// For which year is this donation unit key valid.
year: Integer;
// Set to 'true' if the Donau somehow "lost" the private key. The donation unit was not
// revoked, but still cannot be used to withdraw receipts at this time (theoretically,
// the private key could be recovered in the future; receipts signed with the private key
// remain valid).
lost?: boolean;
}
.. ts:def:: DonationUnitKey
type DonationUnitKey =
| RsaDonationUnitKey
| CSDonationUnitKey;
.. ts:def:: RsaDonationUnitKey
interface RsaDonationUnitKey {
cipher: "RSA";
// RSA public key
rsa_public_key: RsaPublicKey;
}
.. ts:def:: CSDonationUnitKey
interface CSDonationUnitKey {
cipher: "CS";
// Public key of the donation unit.
cs_public_key: Cs25519Point;
}
A signing key in the ``signkeys`` list is a JSON object with the following fields:
.. ts:def:: SignKey
interface SignKey {
// The actual Donau's EdDSA signing public key.
key: EddsaPublicKey;
// Initial validity date for the signing key.
year: Integer;
}
.. note::
Both the individual donation units *and* the donation units list is signed,
allowing customers to prove that they received an inconsistent list.
.. http:get:: /seed
Return an entropy seed. The Donau will return a high-entropy
value that will differ for every call. The response is NOT in
JSON, but simply high-entropy binary data in the HTTP body.
This API should be used by wallets to guard themselves against
running on low-entropy (bad PRNG) hardware. Naturally, the entropy
returned MUST be mixed with locally generated entropy.
.. http:get:: /config
Return the protocol version, financial domain and currency supported by this
Donau backend.
**Response:**
:http:statuscode:`200 OK`:
The body is a `VersionResponse`.
.. ts:def:: DonauVersionResponse
interface DonauVersionResponse {
// libtool-style representation of the Donau protocol version, see
// https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
// The format is "current:revision:age".
version: string;
// Name of the protocol.
name: "taler-donau";
// Currency supported by this Donau.
currency: string;
// Financial domain by this Donau.
domain: string;
}
.. _donau_issue:
--------------
Issue receipts
--------------
Inspired by the Taler exchange :ref:`Withdrawal`.
This API is used by the charity to obtain valid, attested donation receipts from the Donau.
Use the :ref:`charity GET route` to see the remaining donation volume for the current year.
All incoming `BDID` are recorded under the corresponding charity_id by the Donau.
.. http:POST:: /batch-issue/$CHARITY_ID
Send in a `IssueReceiptsRequest` and ask the Donau to sign all it's contained `BDID`.
**Request:** `IssueReceiptsRequest`
**Response:**
:http:statuscode:`200 OK`:
The request was successful, and the response is a `BSDonationReceipts`.
:http:statuscode:`403 Forbidden`:
The charity signature is invalid. This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`. This suggests a bug in the donor as it should have used current donation unit keys from :ref:`/keys`.
:http:statuscode:`409 Conflict`:
The donation volume of the charity is not sufficient to issue donation receipts vor all sent in blinded udids. The response is a `IssueError` object.
:http:statuscode:`410 Gone`:
The requested donation unit key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly.
**Details:**
.. ts:def:: IssueReceiptsRequest
interface IssueReceiptsRequest {
charity_signature: EddsaSignature;
year: Integer;
bdids: BDID[];
}
.. ts:def:: BDID
interface BDID {
donau_pub_hash: HashCode;
taxpayer_blinded_id: BDIDEnvelope;
}
.. ts:def:: BDIDEnvelope
type BDIDEnvelope = RSABDIDEnvelope | CSBDIDEnvelope ;
.. ts:def:: RSABDIDEnvelope
interface RSABDIDEnvelope {
cipher: "RSA" | "RSA+age_restricted";
rsa_blinded_UDID: string; // Crockford Base32 encoded
}
.. ts:def:: CSBDIDEnvelope
// For donation unit signatures based on Blind Clause-Schnorr, the UDID
// consists of the public nonce and two Curve25519 scalars which are two
// blinded challenges in the Blinded Clause-Schnorr signature scheme.
// See https://taler.net/papers/cs-thesis.pdf for details.
interface CSBDIDEnvelope {
cipher: "CS" | "CS+age_restricted";
cs_nonce: string; // Crockford Base32 encoded
cs_blinded_c0: string; // Crockford Base32 encoded
cs_blinded_c1: string; // Crockford Base32 encoded
}
.. ts:def:: BDIDBlindingKeyP
// Secret for blinding/unblinding.
// An RSA blinding secret, which is basically
// a 256-bit nonce, converted to Crockford Base32.
type BDIDBlindingKeyP = string;
.. ts:def:: BSDonationReceipts
interface DonationReceipts {
blind_signed_receipt_signatures: DonationReceiptSignature[];
}
.. ts:def:: DonationReceiptSignature
.. ts:def:: BlindedDonationReceiptSignature
type BlindedDonationReceiptSignature =
| RSABlindedDonationReceiptSignature
| CSBlindedDonationReceiptSignature;
.. ts:def:: RSABlindedDonationReceiptSignature
interface RSABlindedDonationReceiptSignature {
cipher: "RSA";
// (blinded) RSA signature
blinded_rsa_signature: BlindedRsaSignature;
}
.. ts:def:: CSBlindedDonationReceiptSignature
interface CSBlindedDonationReceiptSignature {
type: "CS";
// Signer chosen bit value, 0 or 1, used
// in Clause Blind Schnorr to make the
// ROS problem harder.
b: Integer;
// Blinded scalar calculated from c_b.
s: Cs25519Scalar;
}
type DonationReceiptSignature = RSADonationReceiptSignature | CSDonationReceiptSignature ;
.. ts:def:: RSADonationReceiptSignature
interface RSADonationReceiptSignature {
cipher: "RSA";
rsa_blinded_donation_receipt_sig: string; // Crockford Base32 encoded
}
.. ts:def:: CSDonationReceiptSignature
interface CSDonationReceiptSignature {
cipher: "CS";
cs_nonce: string; // Crockford Base32 encoded
cs_blinded_c0: string; // Crockford Base32 encoded
cs_blinded_c1: string; // Crockford Base32 encoded
}
.. ts:def:: IssueError
interface IssueError{
max_per_year: Amount;
current_year: Amount;
}
.. ts:def:: DonationUnitUnknownError
interface DonationUnitUnknownError{
unknown_hash_pub_donation_unit: HashCode[];
donau_pub: EddsaPublicKey;
donau_sig: EddsaSignature;
}
.. ts:def:: DonationUnitExpiredMessage
interface DonationUnitExpiredMessage{
h_donation_unit_pub: HashCode;
donau_pub: EddsaPublicKey;
donau_sig: EddsaSignature;
}
.. _donau_submit:
---------------
Submit receipts
---------------
Inspired by the Taler exchange :ref:`Deposit`.
.. http:POST:: /submit
Send in donation receipts for the past fiscal year, receive signed total back.
**Request:** `SubmitDonationReceiptsRequest`
**Response:**
:http:statuscode:`200 OK`:
The request was successful, and the response is a `SubmitResponse`.
:http:statuscode:`403 Forbidden`:
One of the signatures is invalid. This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`.
:http:statuscode:`410 Gone`:
The requested donation unit key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly. FIXME: text does not match our use case well.
**Details:**
.. ts:def:: SubmitDonationReceiptsRequest
interface SubmitDonationReceiptsRequest{
// hashed taxpayer ID plus salt
taxnr_hashed: HashCode;
// All donation receipts must be for this year.
year: Integer;
// Receipts should be sorted by amount.
donation_receipts: DonationReceipt[];
}
.. ts:def:: DonationReceipt
interface DonationReceipt{
donation_unit_pub_hash: HashCode;
nonce: string;
donau_sig: DonationSignature
}
.. ts:def:: DonationSignature
type DonationSignature =
RsaDonationSignature | CSDonationSignature;
.. ts:def:: RsaDonationSignature
interface RsaDonationSignature {
cipher: "RSA";
// RSA signature
rsa_signature: RsaSignature;
}
.. ts:def:: CSDonationSignature
interface CSDonationSignature {
type: "CS";
// R value component of the signature.
cs_signature_r: Cs25519Point;
// s value component of the signature.
cs_signature_s: Cs25519Scalar:
}
.. ts:def:: SubmitResponse
interface SubmitResponse{
// *accepted* total
total: Amount;
// signature over taxid_hashed, total, year
signature: EddsaSignature;
}
.. _donau_charity:
---------------------------------------------
Charity administration and status information
---------------------------------------------
The administration requests require an authorized bearer token to be set in the HTTP "Authorization" Header. This token can be set by a proxy validating authentication/authorization (using e.g. LDAP).
.. http:GET:: /charities
return all charities
**Request:**
**Reponse:**
:http:statuscode:`200 OK`:
The request was successful, and the response is a `Charities`.
**Details:**
.. ts:def:: Charities
interface Charities{
charities: CharitySummary[];
}
.. ts:def:: CharitySummary
interface CharitySummary{
charity_id: Integer;
name: string;
max_per_year: Amount;
receipts_to_date: Amount;
}
.. _donau_charity_get:
.. http:get:: /charities/$CHARITY_ID
Request information about a charity.
**Request:**
**Response:**
:http:statuscode:`200 OK`:
The Donau responds with a `Charity` object
:http:statuscode:`404 Not found`:
The charity id does not belong to a charity known to the Donau.
.. ts:def:: Charity
interface Charity {
name: string;
pub_key: EddsaPublicKey;
max_per_year: Amount;
donation_history: CharityHistoryYear[];
}
.. ts:def:: CharityHistoryYear
interface CharityHistoryYear {
year: Integer;
final_amout: Amount;
}
.. http:POST:: /charities
Add a charity. Only allowed if the request comes with the administrator bearer token.
**Request:** `CharityRequest`
**Response:**
**Details:**
:http:statuscode:`201 Created`:
The request was successful, and the response is a `CharityResponse`.
:http:statuscode:`403 Forbidden`:
The request did not contain an accepted administrator bearer token in it's header.
.. ts:def:: CharityRequest
interface CharityRequest{
pub_key: EddsaPublicKey;
max_per_year: Amount;
name: string;
}
.. ts:def:: CharityResponse
interface CharityResponse{
id: Integer;
}
.. http:PATCH:: /charities/{id}
Modify a charity. Only allowed if the request comes with the administrator bearer token.
**Request:** `CharityRequest`
**Response:**
:http:statuscode:`200 OK`:
The request was successful.
:http:statuscode:`403 Forbidden`:
The request did not contain an accepted administrator bearer token in it's header.
.. http:DELETE:: /charities/{id}
Delete (or deactivate) a charity. Only allowed if the request comes with the administrator bearer token.
**Request:**
**Response:**
:http:statuscode:`200 OK`:
The request was successful.
:http:statuscode:`403 Forbidden`:
The request did not contain an accepted administrator bearer token in it's header.