commit a47e9aeb7e598178c291325bbd0624e7bf284ab1
parent 67ff83ddd7b4cb077c9b783c1c64eb608cd49c0b
Author: Bohdan Potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Wed, 8 Oct 2025 20:25:16 +0200
Update the definition of objects + hash function
Diffstat:
| M | draft-donau.xml | | | 148 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
1 file changed, 99 insertions(+), 49 deletions(-)
diff --git a/draft-donau.xml b/draft-donau.xml
@@ -149,18 +149,36 @@
</t>
<figure>
<artwork type="abnf"><![CDATA[
- donau-URI = ("donau://" / "DONAU://" / "donau+http://" / "DONAU+HTTP://" )
- host "/" year "/" taxid "/" salt [ "?" ["total=" amount "&sig=" algo ":" signature ]
- host = ALPHA *( ALPHA / DIGIT / "-" / "." )
- year = DIGIT DIGIT DIGIT DIGIT
- taxid = *( ALPHA / DIGIT / "-" /".")
- salt = *(DIGIT)
- amount = currency ":" unit [ "." fraction ]
- currency = 1*ALPHA
- unit = 1*(DIGIT)
- fraction = 1*(DIGIT)
- algo = "ED25519"
- signature = *(ALPHA / DIGIT)
+ ; Scheme and high-level structure
+ scheme = "donau://" / "DONAU://" / "donau+http://" / "DONAU+HTTP://"
+ donau-URI = scheme base "/" year "/" taxid-enc "/" salt
+ [ "?" [ "total=" amount "&sig=" algo ":" signature ] ]
+
+ ; The base is the HTTP(S) origin plus optional path segments that
+ ; identify the Donau REST API deployment. Everything before the last
+ ; three path segments (year, taxid-enc, salt) is part of the base.
+ base = 1*( ALPHA / DIGIT / "-" / "." / ":" / "/" / "_" )
+
+ ; Path parameters
+ year = DIGIT DIGIT DIGIT DIGIT
+
+ ; The taxid-enc is the percent-encoding (RFC 3986) of the UTF-8
+ ; taxpayer identifier string. See semantics for decoding rules.
+ taxid-enc = 1*( unreserved / pct-encoded )
+ pct-encoded = "%" HEXDIG HEXDIG
+ unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+
+ ; Salt remains an opaque string for the URI and is interpreted by
+ ; the Donau system. (See semantics.)
+ salt = 1*( DIGIT )
+
+ ; Query parameters and signature encoding
+ amount = currency ":" unit [ "." fraction ]
+ currency = 1*ALPHA
+ unit = 1*( DIGIT )
+ fraction = 1*( DIGIT )
+ algo = "ED25519"
+ signature = *( ALPHA / DIGIT )
]]>
</artwork>
</figure>
@@ -168,31 +186,51 @@
<section anchor="semantics" title="Semantics">
<t>
- The host of a donau-URI referes to the name of the machine <xref
- target="RFC1123"/>, its IP v4 address <xref target="RFC791"/>, or
- its IP v6 address <xref target="RFC8200"/>.
-
+ The base of a Donau URI refers to the base URL of the Donau REST API.
+ It consists of the network location (host name or IP address) and MAY include
+ additional path segments (for example, <tt>https://admin.ch/taxes/donau/</tt>)
+ mapping to the Donau deployment behind reverse proxies. Validators MUST NOT
+ assume a specific domain naming scheme (such as <tt>donau.example</tt>) and
+ MUST accept bases with or without additional path components.
+ </t>
+ <t>
The year of a Donau URI referes to the year when the donations
have been done.
-
+ </t>
+ <t>
The taxid of a Donau URI referes to the taxid of the person that
did the donations and asks for their Donau donation statement to
be verified. The format of the taxid is specific for each tax authority.
-
+ The taxid is conveyed in the URI as a single path segment using percent-encoding
+ per <xref target="RFC3986"/>. Implementations MUST decode <tt>taxid-enc</tt>
+ by applying percent-decoding to obtain the exact UTF-8 bytes of the tax identifier
+ before any processing. In particular, the character '/' MUST be percent-encoded
+ as <tt>%2F</tt> if present in a tax identifier.
+ </t>
+ <t>
The salt of a Donau URI referes to the information used by the
Donau-wallet application to generate the Donau donation statement. This salt is
specific for each wallet, allowing a taxpayer to have many Donau
donation statements for the same year that should be treated as
cummulative by the validator application. The salt can also be
used to obfuscate the taxpayer ID in the donation protocol.
-
+ </t>
+ <t>
The total follows the syntax from <xref target="RFC8905"/> and
represents the total amount donated in this year by the given
taxpayer with the given salt. Thus, given multiple 'donau' URIs
with the same salt, the maximum amount should be used, while
the amounts from 'donau' URIs with different salts should be
added.
-
+ </t>
+ <t>
+ Parsing note: A Donau validator MUST parse a Donau URI by taking the last three
+ path segments as <tt>{year}/{taxid-enc}/{salt}</tt>, and treat all preceding
+ path segments (plus the authority) as the <tt>base</tt>. This ensures that bases
+ with additional path components are unambiguous even when tax identifiers contain
+ reserved characters that are percent-encoded.
+ </t>
+ <t>
Finally, algo specifies the specific signature algorithm
used; for now,
only ED25519 (see <xref target="RFC7748" />) is supported.
@@ -200,7 +238,8 @@
made over the information above (see <xref target="signature"/>)
and MUST be encoded using the Base 32 U Crockford encoding
scheme <xref target="base32-U-crockford"/>.
-
+ </t>
+ <t>
The default operation of applications that invoke a URI with the
Donau scheme MUST be to launch a Donau validator (if
available). If no Donau validator is available, an application
@@ -209,30 +248,38 @@
which application to launch. This allows users with multiple
validators to choose which validator to perform the operation
with.
-
+ </t>
+ <t>
An application SHOULD allow dereferencing a "donau://" URI even
if the action of that URI is not registered in the "Donau URI
Actions" sub-registry.
-
+ </t>
+ <t>
Donau-validators seeing a "donau://" URI MUST use HTTP over TLS when talking
to the respective network service.
Donau-validators seeing a "donau+http://" URI MUST use HTTP without TLS when talking
to the respective network service. Donau-validators SHOULD support
"donau+http://"-URIs only when run in developer or debug mode.
- Validators would contact the host to obtain the public key
- used for the signature. The host domain SHOULD also be shown to the
+ Validators would contact the base to obtain the public key
+ used for the signature. The base origin SHOULD also be shown to the
user to indicate which authority issued the proof of donation.
Alternatively, specific validation apps MAY only accept 'donau'
URLs from a specific set of hard-coded authorities and simply display
an error message when given 'donau' URLs from other authorities.
-
</t>
</section>
<section anchor="examples" title="Examples">
<figure>
<artwork><![CDATA[
+ ; Minimal example with a simple base and simple taxid
donau://example.com/2025/7560001010000/1234?total=EUR:15&sig=ED25519:H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28
+
+ ; Example with percent-encoded taxid containing '/'
+ donau://donau.test.taler.net/2025/123%2F456%2F789/1234?total=TESTKUDOS:1&sig=ED25519:B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG21W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30
+
+ ; Example with a base URL that includes path components
+ donau://admin.ch/taxes/donau/2025/7560001010000/1234?total=EUR:15&sig=ED25519:H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28
]]>
</artwork>
</figure>
@@ -265,10 +312,10 @@
keys">
<t>
The verification app MUST have a signing public key
- corresponding to the host for the given year. The key MAY be retrieved from a
- cache. If no key for this host and this year is available in the
+ corresponding to the base for the given year. The key MAY be retrieved from a
+ cache. If no key for this base and this year is available in the
cache, the verification app MUST download the key from the
- host. The Donau server MUST provide the key at the /keys
+ base. The Donau server MUST provide the key at the /keys
entrypoint. If the key is not available, no verification can take place.
The verification app SHOULD cache downloaded signing
@@ -279,7 +326,7 @@
<list style="symbols">
<t>Endpoint: /keys</t>
<t>Method: GET</t>
- <t>Syntax: /keys</t>
+ <t>Syntax: base/keys</t>
<t>Syntax response: defined hereunder</t>
<t>Contact: N/A</t>
<t>References: [this.I-D]</t>
@@ -366,13 +413,15 @@ Example, a response to a GET request to the /key/ endpoint.
<t>
If the Donau-URI does not contain the total or the signature, the
verification app MUST download them from the /donation-statement/
- endpoint of the host.<br/>
+ endpoint of the base.<br/>
- The verification app will hash together the taxid and the salt
- using SHA256 <xref target="RFC6234"/>. The app concatenates both
- taxid and salt and then runs the function SHA256 on the
- concatenated binary data this produces the hash-donor-id. The verification app will contact the
- host in the endpoint /donation-statement with the year and the hash-donor-id.
+ The verification app will compute the donor hash H using SHA256 <xref target="RFC6234"/>
+ over the exact UTF-8 bytes of the taxpayer ID string, followed by a single
+ NUL byte (0x00), followed by the salt string, followed by a final NUL byte (0x00):
+ <tt>H = SHA-256(taxid || 0x00 || salt || 0x00)</tt>. Here, <tt>taxid</tt> is the
+ UTF-8 string obtained by percent-decoding the <tt>taxid-enc</tt> path segment.
+ This produces the hash-donor-id. The verification app will contact the
+ base in the endpoint /donation-statement with the year and the hash-donor-id.
The hash-donor-id must be encoded using Base 32 U Crockford
encoding <xref target="base32-U-crockford"/>.
@@ -381,7 +430,7 @@ Example, a response to a GET request to the /key/ endpoint.
<list style="symbols">
<t>Endpoint: /donation-statement</t>
<t>Method: GET</t>
-<t>Syntax: /donation-statement/{year}/{hash-donor-id}</t>
+<t>Syntax: base/donation-statement/{year}/{hash-donor-id}</t>
<t>Contact: N/A</t>
<t>References: [this.I-D]</t>
</list>
@@ -450,7 +499,7 @@ Example of an element of USD 100.00 :
the claimed values.
If the information is not in the URI, the verification app MUST download the total and the signature from the
- host using the GET /donation-statement/ endpoint <xref
+ base using the GET /donation-statement/ endpoint <xref
target="get-donation-statement"/>. Otherwise, it SHOULD use the
value given in the URI.
@@ -468,9 +517,10 @@ Example of an element of USD 100.00 :
in the network order on an 32-bit unsigned integer.</li>
<li>The total number is written in three fields. First, the integer part of the value represented on 64-bit unsigned integer in big endian. Then comes the fractional part of the total value represented by a unsigned integer on 32 bit (also big endian) representing the number of 1/100000000th of the base unit. The third part is a 12-byte string (terminating with the 0x00 character) representing the currency of the total value.</li>
<li>The year on a 64 bit long unsigned integer in network order.</li>
- <li>FIXME-EB: this should include hashing over the salt, not just the taxpayer
- ID and thus needs to be elaborated on how exactly this is hashed.</li>
- <li>The SHA256 hash code for the donor ID.</li>
+ <li>The SHA256 donor hash H computed as
+ <tt>H = SHA-256(taxid || 0x00 || salt || 0x00)</tt>, where all strings are
+ UTF-8 encoded and <tt>0x00</tt> denotes a single NUL byte separator.</li>
+ <li>The SHA256 hash code for the donor ID (i.e., H).</li>
</ul>
</t>
@@ -623,7 +673,7 @@ Value Encoding Value Encoding Value Encoding Value Encoding
<section anchor="appendix-test-vectors" title="Appendix. Test Vector: Verify from URI">
<t>
This appendix shows how to verify a donation statement starting from a
- received URI. Public key is fetched from the Donau host.
+ received URI. Public key is fetched from the Donau base.
</t>
<figure>
@@ -632,7 +682,7 @@ Example URI:
donau://example.com/megacharity/1234/2025/7560001010000/1234?total=EUR:15&sig=ED25519:H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28
Extracted fields:
-- host: example.com/megacharity/1234
+- base: example.com/megacharity/1234
- year: 2025
- taxid: 7560001010000
- salt: 1234
@@ -659,10 +709,10 @@ Fetched public key for year 2025 from https://example.com/megacharity/1234/keys:
<tt>8a6d41af83b228e9ac6487c100bcf2cd77d3ad0a8f70f7d3233dff43ebbf2d396ee9cdffe150df0b0a9f69d58fec9634d651b0d6a7c19fcb3b177ad1507d2f09</tt>.
</t>
<t>
- Compute the donor hash H from the exact UTF-8 bytes of <tt>taxid</tt>
- followed by <tt>salt</tt> (no separator):
- <tt>SHA-256("7560001010000" || "1234")</tt> =
- <tt>1172853d7ff368186af26cfced4a70e34c5e93ea9cf92b6980a432be65241417</tt> (hex).
+ Compute the donor hash H from the exact UTF-8 bytes of <tt>taxid</tt>,
+ a NUL separator (0x00), then <tt>salt</tt>, then a final NUL (0x00):
+ <tt>SHA-256("7560001010000" || 0x00 || "1234" || 0x00)</tt> =
+ <tt>09fbc815d838c845f147dc90fbc3ae6455d050f238239dd3e9141dc461e4dc8c</tt> (hex).
When requesting the donation statement, encode H with Base32 (Crockford)
for the URL path.
</t>
@@ -673,12 +723,12 @@ Fetched public key for year 2025 from https://example.com/megacharity/1234/keys:
SIZE (4 bytes): 0x00000044 (total payload length = 68)
PURPOSE (4 bytes): 0x000005DC (decimal 1500)
AMOUNT (8+4+12): value=15 (0x000000000000000F), fraction=0 (0x00000000), currency="EUR" + 9x 0x00
-DONOR HASH (32 B): H = SHA-256(taxid || salt)
+DONOR HASH (32 B): H = SHA-256(taxid || 0x00 || salt || 0x00)
YEAR (4 bytes): 0x000007E9 (2025)
]]></artwork></figure>
Complete payload (hex):
<figure><artwork><![CDATA[
-00000044000005dc000000000000000f000000004555520000000000000000001172853d7ff368186af26cfced4a70e34c5e93ea9cf92b6980a432be65241417000007e9
+00000044000005dc000000000000000f0000000045555200000000000000000009fbc815d838c845f147dc90fbc3ae6455d050f238239dd3e9141dc461e4dc8c000007e9
]]></artwork></figure>
</t>
<t>