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