lsd0013

LSD0013: The donau:// scheme
Log | Files | Refs

draft-grothoff-donau-01.xml (38641B)


      1 <?xml version="1.0" encoding="UTF-8"?>
      2 <rfc xmlns:xi="http://www.w3.org/2001/XInclude"
      3      category="info" docName="draft-grothoff-donau-01"
      4      ipr="trust200902" submissionType="independent" version="3">
      5 <!-- SEE ALSO: https://docs.taler.net/core/taler-uri.html -->
      6 
      7 <!-- FIXME ipr is copied from lsd0001, need to be updated -->
      8   <front xmlns="">
      9     <title abbrev="The 'donau' URI scheme">
     10       The 'donau' URI scheme for validation of Donau donation statements.
     11     </title>
     12 
     13     <author fullname="Christian Grothoff" initials="C.G." surname="Grothoff">
     14       <organization>Bern University of Applied Sciences</organization>
     15       <address>
     16         <postal>
     17           <street>H&#246;heweg 80</street>
     18           <city>Biel/Bienne</city>
     19 	      <code>2501</code>
     20           <country>CH</country>
     21         </postal>
     22         <email>christian.grothoff@bfh.ch</email>
     23       </address>
     24     </author>
     25 
     26     <author fullname="Emmanuel Benoist" initials="E.B." surname="Benoist">
     27       <organization>Bern University of Applied Sciences</organization>
     28       <address>
     29         <postal>
     30           <street>H&#246;heweg 80</street>
     31           <city>Biel/Bienne</city>
     32 	      <code>2501</code>
     33           <country>CH</country>
     34         </postal>
     35        <email>emmanuel.benoist@bfh.ch</email>
     36       </address>
     37     </author>
     38 
     39 
     40     <author fullname="Bohdan Potuzhnyi" initials="B.P." surname="Potuzhnyi">
     41       <organization>Bern University of Applied Sciences</organization>
     42       <address>
     43         <postal>
     44           <street>H&#246;heweg 80</street>
     45           <city>Biel/Bienne</city>
     46 	      <code>2501</code>
     47           <country>CH</country>
     48         </postal>
     49         <email>bohdan.potuzhnyi@bfh.ch</email>
     50       </address>
     51     </author>
     52 
     53 
     54     <author fullname="Florian Dold" initials="F.D." surname="Dold">
     55       <organization>Taler Systems AG</organization>
     56       <address>
     57         <postal>
     58           <street>7, rue de Mondorf</street>
     59           <city>Erpeldange</city>
     60 	      <code>5421</code>
     61           <country>LU</country>
     62         </postal>
     63         <email>dold@taler.net</email>
     64       </address>
     65     </author>
     66 
     67 
     68     <date day="2" month="November" year="2025" />
     69 
     70     <!-- Meta-data Declarations -->
     71     <area>General</area>
     72     <workgroup>Independent Stream</workgroup>
     73     <keyword>taxation</keyword>
     74 
     75     <abstract>
     76 
     77       <t>
     78         This document defines the 'donau' Uniform Resource Identifier (URI) scheme
     79         for triggering interactions with a validator for Donau donation
     80 	statements.
     81       </t>
     82 
     83       <t>
     84         This URI scheme allows applications to trigger interactions
     85         with a Donau validator.  A Donau validator is typically run by
     86         a tax authority to validate tax records from citizens that
     87         made donations to a charity that supports the Donau protocol.
     88         The Donau validator will receive 'donau' URIs representing the
     89         sum of donations a taxpayer made to recognized charities over
     90         a year.  Donors would submit 'donau' URLs (or QR codes with
     91         'donau' URLs) to tax authorities to have their donations
     92         recognized by the tax authority as tax-deductible
     93         expenditures.  The application logic to verify the validity of
     94         the donation is triggered by 'donau' URIs. The validator
     95         application would then typically confirm to the tax official
     96         the validity of the signature encoded in the URI and show the
     97         total amount donated as well as the taxpayer identification
     98         number and the year of the donation.  Multiple URIs could be
     99         submitted per donor, and the application can correctly
    100         determine which submissions are cumulative and which ones are
    101         redundant.
    102       </t>
    103 
    104       <t>
    105 	This specification only covers the syntax of the 'donau' URI
    106 	scheme and excludes details on the protocol(s) that would
    107 	allow taxpayers to donate to recognized charities to obtain
    108 	these suitable signed donation statements.  While a
    109 	privacy-preserving protocol to obtain such statements exists
    110 	within the context of the GNU Taler protocol suite, other
    111 	protocols could be developed in the future and still yield
    112 	compatible 'donau' URIs as the URI scheme is reasonably generic.
    113       </t>
    114       <t>
    115 	The validation tool will be registered for all donau:// URIs.
    116         Since each taxation authority will typically use a different
    117         domain, it will not be feasible to encode all the
    118         domains of tax authorities servers inside the validation tool.
    119         Hence a new URI scheme is needed that will trigger the validation
    120         tool for any domain name.
    121       </t>
    122     </abstract>
    123 
    124   </front>
    125 
    126 <middle xmlns="">
    127 
    128 <section anchor="introduction">
    129   <name>Introduction</name>
    130 <t>
    131   This document defines the 'donau' Uniform Resource Identifier (URI)
    132   <xref target="RFC3986" /> scheme for triggering interactions with
    133   Donau validators.
    134 </t>
    135 
    136 <section>
    137   <name>Objective</name>
    138   <t>
    139     A 'donau' URI always instructs a Donau validator to perform the
    140     validation of a Donau donation statement.
    141     A 'donau' URI consists of the reference to the authority
    142     that signed the statement, an identifier for the specific taxpayer,
    143     the year of the donation, a salt and optional parameters.
    144     Optional parameters include the total amount donated by the
    145     taxpayer and the signature of the Donau donation statement.
    146   </t>
    147   <t>
    148     This specification is based on the Bachelor Thesis of
    149     Casaburi and Matya <xref target="BaThesis" /> presenting the
    150     solution and the first implementation of a Donau system.
    151   </t>
    152 </section>
    153 
    154 
    155 <section>
    156   <name>Requirements Language</name>
    157   <t>
    158     The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
    159     "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
    160     "OPTIONAL" in this document are to be interpreted as described in BCP 14
    161     <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when,
    162     they appear in all capitals, as shown here.
    163   </t>
    164 </section>
    165 
    166 </section>
    167 
    168 <section anchor="syntax">
    169   <name>Syntax of a 'donau' URI</name>
    170   <t>
    171   This document uses the Augmented Backus-Naur Form (ABNF) of <xref target="RFC5234"/>.
    172   </t>
    173   <figure>
    174   <artwork type="abnf"><![CDATA[
    175   ; Scheme and high-level structure
    176   scheme    = "donau" / "DONAU" / "donau+http" / "DONAU+HTTP"
    177   donau-URI = scheme "://" base "?" year-param "&"
    178               taxid-param "&" salt-param
    179               ("&" total-param "&" sig-param)?
    180 
    181   ; The base is the HTTP(S) origin plus optional path segments that
    182   ; identify the Donau REST API deployment. Everything before the last
    183   ; three path segments (year, taxid-enc, salt) is part of the base.
    184   base      = 1*( ALPHA / DIGIT / "-" / "." / ":" / "/" / "_" )
    185 
    186   year-param = [ "year" / "YEAR" ] "=" year
    187 
    188   tax-id-param = [ "id" / "ID" ] "=" tax-id-enc
    189 
    190   salt-param = [ "salt" / "SALT" ] "=" salt
    191 
    192   total-param = [ "total" / "TOTAL" ] "=" amount
    193 
    194   signature-param = [ "sig" / "SIG" ] "=" algo ":" signature
    195 
    196   ; Path parameters
    197   year      = DIGIT DIGIT DIGIT DIGIT
    198 
    199   ; The taxid-enc is the percent-encoding (RFC 3986) of the UTF-8
    200   ; taxpayer identifier string. See semantics for decoding rules.
    201   taxid-enc = 1*( unreserved / pct-encoded )
    202   pct-encoded = "%" HEXDIG HEXDIG
    203   unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
    204 
    205   ; Salt remains an opaque string for the URI and is interpreted by
    206   ; the Donau system. (See semantics.)
    207   salt      = 1*( ALPHA / DIGIT )
    208 
    209   ; Query parameters and signature encoding
    210   amount    = currency ":" unit [ "." fraction ]
    211   currency  = 1*ALPHA
    212   unit      = 1*( DIGIT )
    213   fraction  = 1*( DIGIT )
    214   algo      = "ED25519"
    215   signature = 1*( ALPHA / DIGIT )
    216 ]]>
    217   </artwork>
    218   </figure>
    219 </section>
    220 
    221 <section anchor="semantics">
    222   <name>Semantics</name>
    223   <t>
    224     Even if RFC3986 <xref target="RFC3986"/> explicitly writes that scheme names
    225     are case-insensitive, lower and upper case variations are presented here to
    226     emphasize this possibility. The applications encoding URI in a QR-code can
    227     benefit from the possibility to encode the URI using only uppercase letters
    228     which should result in a more compact encoding.
    229   </t>
    230   <t>
    231     The base of a Donau URI refers to the base URL of the Donau REST API.
    232     It consists of the network location (host name or IP address) and <bcp14>MAY</bcp14> include
    233     additional path segments (for example, <tt>https://admin.ch/taxes/donau/</tt>)
    234     mapping to the Donau deployment behind reverse proxies. Validators <bcp14>MUST NOT</bcp14>
    235     assume a specific domain naming convention or pattern
    236     (such as <tt>donau.example</tt>) and
    237     <bcp14>MUST</bcp14> accept bases with or without additional path components.
    238   </t>
    239     <t>
    240     The year of a Donau URI refers to the year when the donations
    241     have been done.
    242     </t>
    243     <t>
    244     The taxid of a Donau URI refers to the taxid of the person that
    245     did the donations and asks for their Donau donation statement to
    246     be verified. The format of the taxid is specific for each tax authority.
    247     The taxid is conveyed in the URI as a percent-encoded query parameter as
    248     per <xref target="RFC3986"/>. Implementations <bcp14>MUST</bcp14> decode <tt>taxid-enc</tt>
    249     by applying percent-decoding to obtain the exact UTF-8 bytes of the tax identifier
    250     before any processing. In particular, the character '/' <bcp14>MUST</bcp14> be percent-encoded
    251     as <tt>%2F</tt> if present in a tax identifier.
    252     </t>
    253     <t>
    254 
    255     The salt of a Donau URI refers to information used by a
    256     wallet application to generate the Donau donation statement.
    257     This salt is wallet-specific. As taxpayers <bcp14>MAY</bcp14>
    258     use multiple wallets, tax authorities <bcp14>SHOULD</bcp14>
    259     allowing a taxpayer to supply multiple Donau
    260     donation statements for the same year. If the salts
    261     are different, these should be treated as
    262     cumulative by the validator application.  The salt
    263     <bcp14>SHOULD</bcp14> also used to obfuscate the taxpayer ID
    264     in the donation protocol.
    265     </t>
    266     <t>
    267     The total follows the syntax from <xref target="RFC8905"/> and
    268     represents the total amount donated in this year by the given
    269     taxpayer with the given salt. Thus, given multiple 'donau' URIs
    270     with the same salt, the maximum total amount should be used, while
    271     the total amounts from 'donau' URIs with different salts should be
    272     added.
    273     </t>
    274     <t>
    275     Finally, <tt>algo</tt> specifies the specific signature algorithm
    276     used; for now,
    277     only ED25519 (see <xref target="RFC8032" />) is supported.
    278     The signature using the specified algorithm <bcp14>MUST</bcp14> be
    279     made over the information above (see <xref target="signature"/>)
    280     and <bcp14>MUST</bcp14> be encoded using the Base 32 U Crockford encoding
    281     scheme <xref target="base32-U-crockford"/>.
    282     </t>
    283     <t>
    284     The default operation of applications that invoke a URI with the
    285     Donau scheme <bcp14>MUST</bcp14> be to launch a Donau validator (if
    286     available). If no Donau validator is available, an application
    287     <bcp14>SHOULD</bcp14> show a QR code with the content of the URI.
    288     If multiple
    289     Donau validators are registered, the user <bcp14>SHOULD</bcp14> be able to choose
    290     which application to launch. This allows users with multiple
    291     validators to choose which validator to perform the operation
    292     with.
    293     </t>
    294     <!--<t>
    295     An application <bcp14>SHOULD</bcp14> allow dereferencing a "donau://" URI even
    296     if the action of that URI is not registered in the "Donau URI
    297     Actions" sub-registry.
    298     </t>-->
    299     <t>
    300       Validators <bcp14>MUST</bcp14> use HTTP over TLS when processing a "<tt>donau</tt>" URI.
    301       Validators <bcp14>MUST</bcp14> use HTTP without TLS when processing a "<tt>donau+http</tt>" URI.
    302       The base origin <bcp14>SHOULD</bcp14> be shown to the
    303       user to indicate which authority issued the proof of donation.
    304       Alternatively, specific validation apps <bcp14>MAY</bcp14> only accept 'donau'
    305       URLs from a specific set of hard-coded authorities and simply display
    306       an error message when given 'donau' URLs from other authorities.
    307     </t>
    308 </section>
    309 
    310 <section anchor="examples">
    311   <name>Examples</name>
    312   <figure>
    313     <artwork><![CDATA[
    314     ; Minimal example with a simple base and simple taxid
    315     donau://example.com/
    316       ?year=2025&id=7560001010000&
    317       salt=1234&
    318       total=EUR:15&
    319       sig=ED25519:
    320         H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1
    321         QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28
    322 
    323     ; Example with percent-encoded taxid containing '/'
    324     donau://donau.test.taler.net/
    325       ?year=2025&id=123%2F456%2F789&
    326       salt=1234&
    327       total=TESTKUDOS:1&
    328       sig=ED25519:
    329         B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG2
    330         1W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30
    331 
    332     ; Example with a base URL that includes path components
    333     donau://admin.ch/taxes/donau/
    334       ?year=2025&id=7560001010000&
    335       salt=1234&
    336       total=EUR:15&
    337       sig=ED25519:
    338         H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1
    339         QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28
    340 ]]>
    341     </artwork>
    342   </figure>
    343 </section>
    344 
    345 <section anchor="verification">
    346   <name>Verification of a Donau URI</name>
    347   <t>
    348     The verification requires the Donau verification application to test
    349     if a given taxpayer is entitled to a tax reduction.
    350     For doing this, the Donau verification application must
    351     verify that the Donau URI corresponds to a valid Donau donation
    352     statement. The Donau verification application <bcp14>MUST</bcp14> verify the
    353     validity of the donation statement. There are two possibilities:
    354     signature and total are available, or at least one is not available.
    355   </t>
    356   <t>
    357     If signature and total are available, then the verification
    358     application <bcp14>MUST</bcp14> verify
    359     that the signature is valid for the server key (see <xref
    360     target="get-server-keys"/> to get the key).
    361   </t>
    362   <t>
    363     If either or both are missing, the application <bcp14>MUST</bcp14> download them from
    364     the get donation statement endpoint <xref target="get-donation-statement"/>.
    365   </t>
    366 
    367 </section>
    368 <section anchor="get-server-keys">
    369   <name>Obtaining Donau Server Public Keys</name>
    370   <t>
    371     The verification app <bcp14>MUST</bcp14> obtain the signing public key
    372     corresponding to the base for the given year.
    373     The key <bcp14>MAY</bcp14> be retrieved from a
    374     cache. If no key for this base and this year is available in the
    375     cache, the verification app <bcp14>MUST</bcp14> download the key from the
    376     base. The Donau server <bcp14>MUST</bcp14> provide the key at the /keys
    377     endpoint. If the key is not available, no verification can take place
    378     and the validator <bcp14>MUST</bcp14> return an appropriate error.
    379 
    380     The verification app <bcp14>SHOULD</bcp14> cache downloaded signing
    381     public keys for the current year.
    382   </t>
    383   <ul>
    384     <li>Endpoint: /keys</li>
    385     <li>Method: GET</li>
    386     <li>Syntax: base/keys</li>
    387     <li>Syntax response: defined hereunder</li>
    388     <li>References: [this.I-D]</li>
    389   </ul>
    390 
    391   <t>
    392     Each of the "<tt>signkeys</tt>" is valid between "<tt>stamp_start</tt>" and
    393     "<tt>stamp_expire</tt>" and the public "<tt>key</tt>" returned is encoded using Base 32
    394     U Crockford encoding <xref target="base32-U-crockford"/>.
    395   </t>
    396 
    397   <sourcecode>
    398         <![CDATA[
    399 ; Core JSON tokens (simplified; assumes standard JSON dialect)
    400 JSONString      = DQUOTE *(%x20-21 / %x23-5B /
    401                  %x5D-10FFFF / "\" EscapedChar) DQUOTE
    402 UCrockford = *( ALPHA32 / "-" )
    403 ALPHA32 = DIGIT / LETTER
    404 DIGIT   = %x30-39                       ; 0-9
    405 LETTER  = %x41-48 / %x4A-4E / %x50-5A / %x61-68 / %x6A-6E / %x70-7A
    406 JSONUCrockfordString      = DQUOTE UCrockford DQUOTE
    407 EscapedChar    = %x22 / %x5C / %x2F / %x62 / %x66 /
    408                  %x6E / %x72 / %x74 / "u" 4HEXDIG
    409 JSONNumber     = [ "-" ] 1DIGIT *( DIGIT ) [ "." 1DIGIT *( DIGIT ) ]
    410                  [ ( "e" / "E" ) [ "+" / "-" ] 1DIGIT *( DIGIT ) ]
    411 JSONBool       = "true" / "false"
    412 JSONNull       = "null"
    413 JSONArray      = "[" [ JSONException *( "," JSONException ) ] "]"
    414 JSONObject     = "{" [ JSONMember *( "," JSONMember ) ] "}"
    415 JSONMember     = JSONString ":" JSONException
    416 JSONException  = JSONString / JSONNumber / JSONObject /
    417                  JSONArray / JSONBool / JSONNull
    418 
    419 ; Specific DonauKeysResponse structure
    420 DonauKeysResponse = "{"
    421                       JSONMemberSep
    422                     "}"
    423 JSONMemberSep  = version , domain , base_url , currency ,
    424                  currency_fraction_digits , donation_units , signkeys
    425 
    426 version       = DQUOTE "version" DQUOTE ":" JSONString
    427 domain        = DQUOTE "domain"  DQUOTE ":" JSONString
    428 base_url      = DQUOTE "base_url" DQUOTE ":" JSONString
    429 currency      = DQUOTE "currency" DQUOTE ":" JSONString
    430 currency_fraction_digits = DQUOTE "currency_fraction_digits"
    431                 DQUOTE ":" JSONNumber
    432 EDDSAPubKey   = JSONUCrockfordString
    433 SignKey       = DQUOTE "key" DQUOTE ":" EDDSAPubKey
    434 donation_units = DQUOTE "donation_units" DQUOTE ":" "["
    435                  DonationUnitKeyGroup *(
    436                    "," DonationUnitKeyGroup ) "]"
    437 signkeys       = DQUOTE "signkeys" DQUOTE ":" "[" SignKey
    438                  *( "," SignKey ) "]"
    439 
    440 DonationUnitKeyGroup = "{" cipher_field "," value_field ","
    441                        donation_units_array "}"
    442 cipher_field           = DQUOTE "cipher" DQUOTE ":" (
    443                         DQUOTE "RSA" DQUOTE / DQUOTE "CS" DQUOTE )
    444 value_field            = DQUOTE "value"  DQUOTE ":" JSONNumber
    445 donation_units_array   = DQUOTE "donation_units" DQUOTE ":" "["
    446                          DUNK_UNIT_KEY *(
    447                            "," DUNK_UNIT_KEY ) "]"
    448 
    449 ; donation unit entries differ depending on cipher
    450 DUNK_UNIT_KEY = "{" base_fields "," key_specific "}"
    451 base_fields   = DQUOTE "year"  DQUOTE ":" JSONNumber
    452                 [ "," DQUOTE "lost" DQUOTE ":" JSONBool ]
    453 key_specific  = ( DQUOTE "rsa_pub" DQUOTE ":" JSONObject ) /
    454                 ( DQUOTE "cs_pub"  DQUOTE ":" JSONString )
    455 
    456 SignKey = "{" DQUOTE "key" DQUOTE ":" JSONObject ","
    457           DQUOTE "year" DQUOTE ":" JSONNumber "}"
    458         ]]>
    459       </sourcecode>
    460 
    461   <t>Example, a response to a GET request to the <tt>/keys</tt> endpoint.</t>
    462   <sourcecode>
    463         <![CDATA[
    464  { "signkeys":
    465    [
    466      {
    467        "stamp_start": {"ts_s": 42 },
    468        "stamp_expire": {"ts_s": 43 },
    469        "key": "XXXXXXXXXXXXXXXX"
    470      }
    471    ]
    472  }
    473         ]]>
    474       </sourcecode>
    475 </section>
    476 
    477 
    478 <section anchor="get-donation-statement">
    479   <name>Donation Statement Retrieval</name>
    480   <t>
    481     If the Donau URI does not contain the total or the signature, the
    482     verification app <bcp14>MUST</bcp14> download them from the <tt>/donation-statement</tt>
    483     endpoint of the base.<br/>
    484 
    485     The verification app <bcp14>MUST</bcp14> compute the hash of the donor
    486     taxpayer ID <tt>hash-donor-id</tt> using SHA-512 <xref target="RFC6234"/>
    487     over the exact UTF-8 bytes of the taxpayer ID string, followed by a single
    488     NUL byte (0x00), followed by the salt string, followed by a final NUL byte (0x00):
    489     <tt>hash-donor-id = SHA-512(taxid || 0x00 || salt || 0x00)</tt>.
    490     Here, <tt>taxid</tt> is the UTF-8 string obtained by percent-decoding the <tt>taxid-enc</tt> path segment.
    491     The verification app <bcp14>MUST</bcp14> contact the
    492     base at the endpoint <tt>/donation-statement</tt> with the year and <tt>hash-donor-id</tt>.
    493 
    494     The hash of the donor taxpayer ID <tt>hash-donor-id</tt> <bcp14>MUST</bcp14> be encoded
    495     using Base 32 U Crockford
    496     encoding <xref target="base32-U-crockford"/>.
    497   </t>
    498 
    499   <ul>
    500     <li>Endpoint: /donation-statement</li>
    501     <li>Method: GET</li>
    502     <li>Syntax: base/donation-statement/{year}/{hash-donor-id}</li>
    503     <li>References: [this.I-D]</li>
    504   </ul>
    505 
    506   <t>
    507     Servers implementing the donation-statement endpoint
    508     <bcp14>MUST</bcp14> respect the
    509     following syntax; all three fields (<tt>total-field</tt>, <tt>sig-field</tt>, <tt>pub-field</tt>) <bcp14>MUST</bcp14> be included.
    510 
    511     The total amount is a string formed first of the currency (in capital
    512     letters) then ":" and then the value (can be an integer or a decimal
    513     number).
    514   </t>
    515 
    516   <sourcecode>
    517         <![CDATA[
    518 json-object     = "{" ws field-list ws "}"
    519 
    520 field-list      = field *(ws "," ws field)
    521 field           = total-field / sig-field / pub-field
    522 
    523 ; Allow any order of the three fields
    524 total-field     = DQUOTE "total" DQUOTE ws ":" ws
    525                   DQUOTE currency ":" value DQUOTE
    526 sig-field       = DQUOTE "donation_statement_sig" DQUOTE ws ":"
    527                   ws DQUOTE signature DQUOTE
    528 pub-field       = DQUOTE "donau_pub" DQUOTE ws ":" ws
    529                   DQUOTE signature DQUOTE  ; same format as signature
    530 
    531 currency        = 1*(%x41-5A)   ; A-Z uppercase letters
    532 value           = int / decimal
    533 int             = ["-"] 1*DIGIT
    534 decimal         = ["-"] 1*DIGIT "." 1*DIGIT
    535 
    536 signature       = 1*(ALPHA / DIGIT / "=")
    537 
    538 ; Common tokens
    539 ws              = *WSP
    540 DQUOTE          = %x22  ; "
    541 WSP             = %x20 / %x09
    542 DIGIT           = %x30-39
    543 ALPHA           = %x41-5A / %x61-7A
    544         ]]>
    545       </sourcecode>
    546 
    547   <t>Example of an element of USD 100.00 :</t>
    548   <sourcecode>
    549         <![CDATA[
    550 {
    551   "total": "USD:100",
    552   "donation_statement_sig": "SIGNATURE",
    553   "donau_pub": "EDDSA_PUBLIC_KEY"
    554 }
    555         ]]>
    556       </sourcecode>
    557 </section>
    558 
    559 
    560 <section anchor="signature-total-available">
    561   <name>Verification if Signature and Total are Available</name>
    562   <t>
    563     The verification app <bcp14>MUST</bcp14> verify that the signature corresponds to
    564     the claimed values.
    565 
    566     If the information is not in the URI, the verification app <bcp14>MUST</bcp14>
    567     download the total and the signature from the
    568     base using the GET <tt>/donation-statement</tt> endpoint <xref
    569     target="get-donation-statement"/>. Otherwise, it <bcp14>SHOULD</bcp14> use the
    570     value given in the URI.
    571 
    572     The verification of the signature is done using EdDSA as
    573     specified in <xref target="RFC8032" />.
    574 
    575     The verification <bcp14>MAY</bcp14> be done with the following instructions.
    576     The signed data is the concatenation of the following fields
    577     in network byte order (big-endian):
    578   </t>
    579 
    580   <ul>
    581     <li>size (4 bytes): 32-bit unsigned integer with the total length of the signed data in bytes.</li>
    582     <li>purpose (4 bytes): 32-bit unsigned integer with the constant value <tt>1500</tt>.</li>
    583     <li>total value (8 bytes): integer part as an unsigned 64-bit integer.</li>
    584     <li>total fraction (4 bytes): fractional part as an unsigned 32-bit integer in units of 1/100000000.</li>
    585     <li>currency (12 bytes): ASCII string, left-aligned with zero-padding on the right to 12 bytes.</li>
    586     <li>hash of donor taxpayer ID (64 bytes): <tt>hash-donor-id = SHA-512( UTF-8(taxid) || 0x00 || UTF-8(salt) || 0x00 )</tt>.</li>
    587     <li>year (4 bytes): 32-bit unsigned integer (the donation year).</li>
    588   </ul>
    589 </section>
    590 
    591 
    592 <section anchor="signature">
    593   <name>Creating Donation Statement Signatures</name>
    594   <t>
    595     The server signing the donation statement  <bcp14>MUST</bcp14> use the Sign(d,message) procedure implemented as defined in <xref target="RFC8032" />.
    596   </t>
    597   <t>
    598     The signature over the public key covers a 32-bit pseudo header
    599     conceptually prefixed to the donation data fields.
    600     The wire format is illustrated
    601     in <xref target="figure_donausigwithpseudo"/>.
    602   </t>
    603   <figure anchor="figure_donausigwithpseudo">
    604     <name>The Format for Creating Donation Statement Signatures</name>
    605     <artwork name="" type="" alt="">
    606 0     8     16    24    32    40    48    56
    607 +-----+-----+-----+-----+-----+-----+-----+-----+
    608 |         SIZE          |       PURPOSE (1500)  |
    609 +-----+-----+-----+-----+-----+-----+-----+-----+
    610 |                TOTAL value                    |
    611 +-----+-----+-----+-----+-----+-----+-----+-----+
    612 |       TOTAL  fraction |     CURRENCY          |
    613 +-----+-----+-----+-----+                       +
    614 |                                               |
    615 +-----+-----+-----+-----+-----+-----+-----+-----+
    616 |              SHA512 of the donor ID           |
    617 /                                               /
    618 /                                               /
    619 +-----+-----+-----+-----+-----+-----+-----+-----+
    620 |       YEAR            |
    621 +-----+-----+-----+-----+
    622     </artwork>
    623   </figure>
    624   <dl newline="false">
    625     <dt>SIZE:</dt>
    626     <dd>
    627       A 32-bit value containing the length of the signed data in bytes
    628       in network byte order.
    629     </dd>
    630     <dt>PURPOSE:</dt>
    631     <dd>
    632       A 32-bit signature purpose flag in network byte order. The value of this
    633       field <bcp14>MUST</bcp14> be 1500.  It defines the context in which
    634       the signature is created so that it cannot be reused in other parts
    635       of the protocol that might include possible future extensions.
    636       The value of this field corresponds to an entry in the
    637       GANA "GNUnet Signature Purposes" registry <xref target="GANA"/>.
    638     </dd>
    639     <dt>TOTAL value:</dt>
    640     <dd>
    641       The integer part of the total value.
    642       Unsigned integer containing the integer part of the value for the total.
    643       It is represented using a 64 bit value (in big endian).
    644     </dd>
    645     <dt>TOTAL fraction:</dt>
    646     <dd>
    647       The fractional part of the total value is represented by an
    648       unsigned integer on 32 bits (also in big endian notation).
    649       It represents the fractional part of the value  in 1/100000000th of the base unit.
    650     </dd>
    651     <dt>TOTAL currency</dt>
    652     <dd>
    653       A string representing the currency. ASCII, left-aligned and zero-padded to exactly 12 bytes.
    654     </dd>
    655     <dt>HASH:</dt>
    656     <dd>The SHA512 hash code for the donor ID.</dd>
    657     <dt>YEAR:</dt>
    658     <dd>The year on a 32 bit unsigned integer in big endian.</dd>
    659   </dl>
    660 </section>
    661 
    662 <section anchor="signature-verification">
    663   <name>Validating Donation Statement Signatures</name>
    664   <t>
    665     The signature verification step is taking the data presented in the
    666     figure <xref target="figure_donausigwithpseudo" /> as input.
    667     The signature <bcp14>MUST</bcp14> be verified using the EdDSA scheme described in
    668     <xref target="RFC8032" /> the procedure is
    669     <tt>Verify(zk,message,signature)</tt>.
    670     Public key and signature, which are
    671     encoded in Base 32 U Crockford in the URL,
    672     <bcp14>MUST</bcp14> first be decoded to obtain the
    673     respective binary value.
    674   </t>
    675   <t>
    676     The Sign(d,message) and Verify(zk,message,signature) procedures <bcp14>MUST</bcp14>
    677     be implemented as defined in <xref target="RFC8032" />.
    678   </t>
    679 </section>
    680 
    681 
    682 <section anchor="base32-U-crockford">
    683   <name>Base 32 Representation of Binary Data</name>
    684   <t>
    685     All binary data <bcp14>MUST</bcp14> be encoded to be transmitted. For encoding, one
    686     <bcp14>MUST</bcp14> use the Base32 U Crockford encoding. This is a variation of the
    687     base32 encoding <xref target ="RFC4648" />. This encoding is presented
    688     in detail in the appendix of the RFC for GNU Name System <xref target="RFC9498" />
    689   </t>
    690   <t>
    691     The encoding works similarly to the standard, but uses another Base 32
    692     Alphabet. The new alphabet is given in the Table <xref target="figure_base32_encoding" />, below.
    693   </t>
    694 
    695   <figure anchor="figure_base32_encoding">
    696     <name>The Base 32 Encoding Alphabet.</name>
    697     <artwork name="" type="" align="left" alt="table where value between 0 and 9 are encoded with the character corresponding to the number. Then between 10 and 31, we have all the letters expect I, L, O, U that could be misleading (OCR may not recognize them properly). Padding is the char ="><![CDATA[
    698 Value Encoding  Value Encoding  Value Encoding  Value Encoding
    699     0 0             9 9            18 J            27 V
    700     1 1            10 A            19 K            28 W
    701     2 2            11 B            20 M            29 X
    702     3 3            12 C            21 N            30 Y
    703     4 4            13 D            22 P            31 Z
    704     5 5            14 E            23 Q
    705     6 6            15 F            24 R           (pad) =
    706     7 7            16 G            25 S
    707     8 8            17 H            26 T
    708 
    709     ]]>
    710     </artwork>
    711   </figure>
    712 
    713   <t>
    714     To prevent optical character reading (OCR) problems, a system decoding
    715     binary data encoded with base 32 U Crockford <bcp14>MUST</bcp14>
    716     accept the codes presented in the Table <xref target="figure_base32_decoding"/>, below.
    717     System reading a <tt>U</tt> <bcp14>MUST</bcp14> evaluate it as a <tt>V</tt>.
    718   </t>
    719 
    720   <figure anchor="figure_base32_decoding">
    721     <name>The Base 32 Decoding Alphabet.</name>
    722         <artwork name="" type="" align="left" alt=""><![CDATA[
    723 Value Encoding  Value Encoding  Value Encoding  Value Encoding
    724     0 0             9 9            18 J|j          27 V|v|u|U
    725     1 1|i|I|l|L    10 A|a          19 K|k          28 W|w
    726     2 2            11 B|b          20 M|m          29 X|x
    727     3 3            12 C|c          21 N|n          30 Y|y
    728     4 4            13 D|d          22 P|p          31 Z|z
    729     5 5            14 E|e          23 Q|q
    730     6 6            15 F|f          24 R|r        (pad) =
    731     7 7            16 G|g          25 S|s
    732     8 8            17 H|h          26 T|t
    733     ]]></artwork>
    734   </figure>
    735 
    736 
    737 </section>
    738 
    739 <section anchor="security">
    740   <name>Security Considerations</name>
    741   <t>
    742     Donau validators <bcp14>SHOULD</bcp14> support
    743     "<tt>donau+http://</tt>"-URIs only when run in developer or debug mode
    744     as otherwise the integrity and authenticity of the public key
    745     cannot be assured.
    746   </t>
    747   <t>
    748     Running  "<tt>donau+http://</tt>"-URIs on a production server
    749     <bcp14>MUST NOT</bcp14> be permitted, since it would be a security issue.
    750     This scheme is intended only for testing purposes,
    751     for instance on <tt>localhost</tt>.
    752   </t>
    753   <t>
    754     Validator applications <bcp14>MUST</bcp14> include protections
    755     against repeated validations of the same donation statement
    756     with the same salt and year. Specifically, when summing up
    757     multiple donation statements for the same taxpayer and
    758     tax year, the <bcp14>MUST</bcp14> keep enough state to add
    759     up the maximum total amounts per salt if multiple donation
    760     statements are submitted with the same salt.
    761   </t>
    762 </section>
    763 
    764 
    765 <section anchor="iana">
    766   <name slugifiedName="name-iana-considerations">IANA Considerations</name>
    767   <t>
    768     IANA maintains the "Uniform Resource Identifier (URI) Schemes"
    769     registry that contains an entry for the 'donau' URI scheme.  IANA is
    770     requested to update that entry to reference this document when
    771     published as an RFC.
    772   </t>
    773   <dl indent="3" newline="false" spacing="normal">
    774     <dt>Scheme name:</dt>
    775     <dd> donau</dd>
    776     <dt>Status:</dt>
    777     <dd> provisional</dd>
    778     <dt>URI scheme syntax:</dt>
    779     <dd>See <xref target="syntax" format="default" sectionFormat="of" derivedContent="Section 3"/> of [This.I-D].</dd>
    780     <dt>URI scheme semantics:</dt>
    781     <dd>See <xref target="semantics" format="default" sectionFormat="of" derivedContent="Section 4"/> of [This.I-D].</dd>
    782     <dt>Applications/protocols that use this scheme name:</dt>
    783     <dd> GNU Taler</dd>
    784     <dt>Contact:</dt>
    785     <dd>
    786       <t><contact fullname="Christian Grothoff"/> &lt;grothoff@gnu.org&gt;</t>
    787     </dd>
    788     <dt>Change controller:</dt>
    789     <dd>
    790       <t><contact fullname="Christian Grothoff"/> &lt;grothoff@gnu.org&gt;</t>
    791     </dd>
    792     <dt>References:</dt>
    793     <dd> See <xref target="refs" format="default" sectionFormat="of" derivedContent="Section 13"/> of [This.I-D].</dd>
    794   </dl>
    795 </section>
    796 
    797 
    798 </middle>
    799 
    800 <back xmlns="">
    801 
    802 
    803 
    804 
    805   <references anchor="refs">
    806     <name>Normative References</name>
    807 
    808     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
    809 
    810     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.3986.xml"/>
    811 
    812     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
    813 
    814     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5234.xml"/>
    815 
    816     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6234.xml"/>
    817 
    818     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8032.xml"/>
    819 
    820     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9498.xml"/>
    821 
    822     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8905.xml"/>
    823 
    824 
    825   </references>
    826 
    827   <references>
    828     <name>Informational References</name>
    829 
    830     <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.4648.xml"/>
    831 
    832 
    833    <reference anchor="GANA" target="https://gana.gnunet.org/">
    834      <front>
    835          <title>GNUnet Assigned Numbers Authority (GANA)</title>
    836         <author><organization>GNUnet e.V.</organization>
    837         </author>
    838          <date month="April" year="2020" />
    839      </front>
    840    </reference>
    841 
    842    <reference anchor="BaThesis" target="https://www.taler.net/papers/donau-thesis.pdf">
    843      <front>
    844        <title>Tax-deductable Privacy-Preserving Donations</title>
    845        <author initials="J.C." surname="Casaburi" fullname="Johannes Casaburi">
    846          <organization>Bern University of Applied Science, Bachelor Thesis</organization>
    847        </author>
    848        <author initials="L.M." surname="Matya" fullname="Lukas Matya">
    849          <organization>Bern University of Applied Science, Bachelor Thesis</organization>
    850        </author>
    851      </front>
    852      </reference>
    853   </references>
    854 
    855 <section anchor="appendix-test-vectors">
    856   <name>Appendix. Test Vector: Verify from URI</name>
    857   <t>
    858     This appendix shows how to verify a donation statement starting from a
    859     received URI. Public key is fetched from the Donau base.
    860   </t>
    861 
    862   <figure>
    863    <artwork><![CDATA[
    864 Example URI:
    865 donau://donau.test.taler.net/?year=2025&id=123%2F456%2F789&
    866   salt=AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0&
    867   total=TESTKUDOS:1&
    868   sig=ED25519:
    869     B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG21
    870     W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30
    871 
    872 Extracted fields:
    873 - base: donau.test.taler.net
    874 - year: 2025
    875 - taxid: 123%2F456%2F789
    876 - taxid (decoded): 123/456/789
    877 - salt: AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0
    878 - total: TESTKUDOS:1
    879 - signature: ED25519:B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4
    880   GR24FP1C93C4ZPDG21W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30
    881 
    882 Fetched public key for year 2025 from https://donau.test.taler.net/keys:
    883 - pub: 2FRN2CAK9DMDWE157W6HY97RAVSP0ZCCC08X9N6JD2MK7413XXZG
    884 ]]></artwork>
    885   </figure>
    886 
    887   <t>Verification steps:</t>
    888   <ol>
    889     <li>Input (URI):<br/>
    890 <tt>donau://donau.test.taler.net/?year=2025&amp;id=123%2F456%2F789&amp;
    891 salt=AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0&amp;
    892 total=TESTKUDOS:1&amp;
    893 sig=ED25519:B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG21
    894 W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30</tt></li>
    895     <li>Parse fields:<br/>
    896           - base: <tt>donau.test.taler.net</tt><br/>
    897           - year: <tt>2025</tt> (4 digits)<br/>
    898           - taxid-enc: <tt>123%2F456%2F789</tt><br/>
    899           - taxid (UTF-8, percent-decoded, no trimming): <tt>123/456/789</tt><br/>
    900           - salt (ASCII): <tt>AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0</tt><br/>
    901           - total (amount string): <tt>TESTKUDOS:1</tt><br/>
    902           - sig (Crockford Base32, algorithm ED25519): <tt>B14WGS...XGY30</tt></li>
    903     <li>Public signing key (Crockford Base32) (from <tt>/keys</tt>):<br/>
    904 <tt>2FRN2CAK9DMDWE157W6HY97RAVSP0ZCCC08X9N6JD2MK7413XXZG</tt></li>
    905     <li>Hash of the donor taxpayer ID <tt>hash-donor-id</tt> = <tt>SHA-512( UTF-8(taxid) || 0x00 || UTF-8(salt) || 0x00 )</tt>:<br/>
    906           - length: 64 bytes<br/>
    907           - hex:<br/>
    908 <tt>4aaa1e16fc5be44842b863b1f17da39296ca7b3529a720e11aba9c8bd729f7a  1e2bb0b9a39c02d271da5dd15aea66ce95be78bcaf380de19a0bdbcd8a7938f1b</tt><br/>
    909           - Crockford Base32 (for HTTP endpoints):<br/>
    910 <tt>9AN1W5QWBFJ4GGNRCERZ2ZD3JABCMYSN56KJ1R8TQAE8QNS9YYGY5ERBK  8WW0B973PJXT5DEMSPEJPZ7HF5F706Y36GBVF6RMY9RY6R</tt></li>
    911     <li>Total amount encoding (<tt>TALER_AmountNBO</tt>):<br/>
    912           - currency (12 bytes): <tt>"TESTKUDOS"</tt> then <tt>0x00</tt> x 3<br/>
    913           - value (uint64 BE): <tt>1</tt> -> <tt>0x0000000000000001</tt><br/>
    914           - fraction (uint32 BE): <tt>0</tt> -> <tt>0x00000000</tt></li>
    915     <li>Signed message <tt>M</tt> layout (network byte order, total 100 bytes):<br/>
    916           - [0000..0003] 4 bytes <tt>size</tt>: <tt>0x00000064</tt><br/>
    917           - [0004..0007] 4 bytes <tt>purpose</tt>: <tt>0x000005DC</tt> (1500)<br/>
    918           - [0008..0015] 8 bytes <tt>amount.value</tt>: <tt>0x0000000000000001</tt><br/>
    919           - [0016..0019] 4 bytes <tt>amount.fraction</tt>: <tt>0x00000000</tt><br/>
    920           - [0020..0031] 12 bytes <tt>amount.currency</tt>: <tt>54 45 53 54 4b 55 44 4f 53 00 00 00</tt> ("TESTKUDOS\x00\x00\x00")<br/>
    921           - [0032..0095] 64 bytes <tt>i.hash</tt>: hash of donor taxpayer ID <tt>hash-donor-id</tt><br/>
    922           - [0096..0099] 4 bytes <tt>year</tt>: <tt>0x000007E9</tt> (2025)</li>
    923     <li>Message <tt>M</tt> (hex, 100 bytes):<br/>
    924 <tt>00000064 000005dc 0000000000000001 00000000 544553544b55444f53000000  4aaa1e16fc5be44842b863b1f17da39296ca7b3529a720e11aba9c8bd729f7a1e2bb0b  9a39c02d271da5dd15aea66ce95be78bcaf380de19a0bdbcd8a7938f1b 000007e9</tt></li>
    925     <li>Signature (ED25519, Crockford Base32 -> bytes):<br/>
    926           - length: 64 bytes<br/>
    927           - R (first 32 bytes, hex):<br/>
    928 <tt>5849c864837bece5a254ce0dc0c51434e2956c6393e2339b06b5054ac490c088</tt><br/>
    929           - S (last 32 bytes, hex):<br/>
    930 <tt>fb05891b09fb36020f0bcf132acfee46632411de4e4bf27fe972f891fd7b0f0c</tt></li>
    931     <li>Public key (Ed25519, Crockford Base32 -> bytes):<br/>
    932           - length: 32 bytes<br/>
    933           - hex:<br/>
    934 <tt>13f15131534b68de38253f0d1f24f856f3607d8c6011d4d4d268a9339023ef7f</tt></li>
    935     <li>Verification (expected): call <tt>crypto_sign_verify_detached(sig, M, 100, pubkey)</tt>. The result <bcp14>MUST</bcp14> indicate a valid signature for this vector.</li>
    936     <li>Negative check (optional): flip one bit of <tt>M</tt> or <tt>sig</tt> and repeat; verification <bcp14>MUST</bcp14> fail.</li>
    937   </ol>
    938 </section>
    939 
    940 <section numbered="false">
    941   <name>Acknowledgements</name>
    942   <t>
    943     The authors thank Ted Hardie for his comments on an earlier version of the draft.
    944     This work was funded in part by the European Commission through the
    945     Horizon Europe program under project number 101135475 (TALER). It also
    946     has received funding from the Swiss State Secretariat for Education,
    947     Research and Innovation (SERI).
    948   </t>
    949 </section>
    950 
    951 <!-- Change Log
    952 v00 2025-10-17  CG   Initial version
    953 v01 2025-10-21  EB   Including first reviews (motivations + change in URI structure)
    954   -->
    955 </back>
    956 </rfc>