lsd0013

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

commit 8c3e2849c2291d4abdb3024a66994c302fef2c21
parent a47e9aeb7e598178c291325bbd0624e7bf284ab1
Author: Bohdan Potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Thu,  9 Oct 2025 00:35:29 +0200

updating some rfc refs + sha256 -> sha256 + clarification for the signature format + update of the test vector(a bit cleaner + sha512)

Diffstat:
Mdraft-donau.xml | 144+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 71 insertions(+), 73 deletions(-)

diff --git a/draft-donau.xml b/draft-donau.xml @@ -233,7 +233,7 @@ <t> Finally, algo specifies the specific signature algorithm used; for now, - only ED25519 (see <xref target="RFC7748" />) is supported. + only ED25519 (see <xref target="RFC8032" />) is supported. The signature using the specified algorithm must be made over the information above (see <xref target="signature"/>) and MUST be encoded using the Base 32 U Crockford encoding @@ -415,10 +415,10 @@ Example, a response to a GET request to the /key/ endpoint. verification app MUST download them from the /donation-statement/ endpoint of the base.<br/> - The verification app will compute the donor hash H using SHA256 <xref target="RFC6234"/> + The verification app will compute the donor hash H using SHA-512 <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 + <tt>H = SHA-512(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. @@ -503,24 +503,19 @@ Example of an element of USD 100.00 : target="get-donation-statement"/>. Otherwise, it SHOULD use the value given in the URI. - The verification of the signature is done using EdDSA which was - presented in <xref target="RFC7748" />. + The verification of the signature is done using EdDSA as + specified in <xref target="RFC8032" />. - The verification MAY be done with the following instructions - - The data to be confirmed is composed of the following information: + The verification MAY be done with the following instructions. The signed data is the concatenation of the following fields in network byte order (big-endian): <ul> - <li>The size of the confirmation to verify written - in the network order on an 32-bit unsigned integer.</li> - <li>The value 1500 written - 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>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> + <li>size (4 bytes): 32-bit unsigned integer with the total length of the signed data in bytes.</li> + <li>purpose (4 bytes): 32-bit unsigned integer with the constant value <tt>1500</tt>.</li> + <li>total value (8 bytes): integer part as an unsigned 64-bit integer.</li> + <li>total fraction (4 bytes): fractional part as an unsigned 32-bit integer in units of 1/100000000.</li> + <li>currency (12 bytes): ASCII, left-aligned and zero-padded to 12 bytes.</li> + <li>donor hash H (64 bytes): <tt>H = SHA-512( UTF-8(taxid) || 0x00 || UTF-8(salt) || 0x00 )</tt>.</li> + <li>year (4 bytes): 32-bit unsigned integer (the donation year).</li> </ul> </t> @@ -557,7 +552,7 @@ Example of an element of USD 100.00 : +-----+-----+-----+-----+ + | | +-----+-----+-----+-----+-----+-----+-----+-----+ -| SHA256 of the donor ID | +| SHA512 of the donor ID | / / / / +-----+-----+-----+-----+-----+-----+-----+-----+ @@ -588,13 +583,12 @@ Example of an element of USD 100.00 : <dd> The fractional part of the total value is represented by an unsigned integer on 32 bits (also in big endian notation). It represents the fractional part of the value in 1/100000000th of the base unit. </dd> - <dt>TOTAL Currency</dt> + <dt>TOTAL currency</dt> <dd> - A string representing the currency. 12 bytes for the currency as an string. The last byte - must be a 0-terminator. + A string representing the currency. ASCII, left-aligned and zero-padded to exactly 12 bytes. </dd> <dt>HASH:</dt> - <dd>The SHA256 hash code for the donor ID.</dd> + <dd>The SHA512 hash code for the donor ID.</dd> <dt>YEAR:</dt> <dd>The year on a 32 bit unsigned integer in big endian.</dd> </dl> @@ -679,64 +673,68 @@ Value Encoding Value Encoding Value Encoding Value Encoding <figure> <artwork><![CDATA[ Example URI: -donau://example.com/megacharity/1234/2025/7560001010000/1234?total=EUR:15&sig=ED25519:H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28 +donau://donau.test.taler.net/2025/123%2F456%2F789/AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0?total=TESTKUDOS:1&sig=ED25519:B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG21W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30 Extracted fields: -- base: example.com/megacharity/1234 +- base: donau.test.taler.net - year: 2025 -- taxid: 7560001010000 -- salt: 1234 -- total: EUR:15 -- signature: ED25519:H9PM3BW3P8MEKB34GZ0G1F7JSNVX7B8AHXRFFMS37QZM7TXZ5MWPXTEDZZGN1QRB1AFPKNCFXJB39NJHP3BAFGCZSCXHEYPHA1YJY28 - -Fetched public key for year 2025 from https://example.com/megacharity/1234/keys: -- pub (Base32 Crockford): K641W1CZM7DRSV184M8CPM3Z8MZRBYYJMNYMJK70FTYJHBPX21J0 +- taxid: 123%2F456%2F789 +- taxid (decoded): 123/456/789 +- salt: AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0 +- total: TESTKUDOS:1 +- signature: ED25519:B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG21W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30 + +Fetched public key for year 2025 from https://donau.test.taler.net/keys: +- pub: 2FRN2CAK9DMDWE157W6HY97RAVSP0ZCCC08X9N6JD2MK7413XXZG ]]></artwork> </figure> <t>Verification steps:</t> <list style="numbers"> - <t> - Decode the public key from Base32 (Crockford) to 32 bytes. Accept - OCR-normalized characters as defined in <xref target="base32-U-crockford"/>. - Decoded public key (hex): - <tt>99881e059fa1db8cec282510cb507f453f85fbd2a57d494ce07ebd28aedd1064</tt>. - </t> - <t> - Normalize and decode the signature: remove the <tt>ED25519:</tt> prefix, then - decode the remainder using Base32 (Crockford) to 64 bytes. - Decoded signature (hex): - <tt>8a6d41af83b228e9ac6487c100bcf2cd77d3ad0a8f70f7d3233dff43ebbf2d396ee9cdffe150df0b0a9f69d58fec9634d651b0d6a7c19fcb3b177ad1507d2f09</tt>. - </t> - <t> - 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> - <t> - Construct the signed payload bytes in network byte order (big endian) - using the following field sequence: - <figure><artwork><![CDATA[ -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 || 0x00 || salt || 0x00) -YEAR (4 bytes): 0x000007E9 (2025) - ]]></artwork></figure> - Complete payload (hex): - <figure><artwork><![CDATA[ -00000044000005dc000000000000000f0000000045555200000000000000000009fbc815d838c845f147dc90fbc3ae6455d050f238239dd3e9141dc461e4dc8c000007e9 - ]]></artwork></figure> - </t> - <t> - Verify the Ed25519 detached signature over the payload using the decoded - public key. For example, with libsodium: - <tt>crypto_sign_verify_detached(signature, payload, payload_len, public_key)</tt>. - The verification returns success for this example. - </t> + <t>Input (URI):<br/> +<tt>donau://donau.test.taler.net/2025/123%2F456%2F789/AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0?total=TESTKUDOS:1&amp;sig=ED25519:B14WGS43FFPEB8JMSR6W1H8M6KH9AV33JFH376R6PM2MNH4GR24FP1C93C4ZPDG21W5WY4SASZQ4CRS427F4WJZJFZMQ5Y4HZNXGY30</tt></t> + <t>Parse fields:<br/> + - base: <tt>donau.test.taler.net</tt><br/> + - year: <tt>2025</tt> (4 digits)<br/> + - taxid-enc: <tt>123%2F456%2F789</tt><br/> + - taxid (UTF-8, percent-decoded, no trimming): <tt>123/456/789</tt><br/> + - salt (ASCII): <tt>AWNFDRFT0WX45W4Y32A9DJA03S1EF66GFQZ9EV5EF9JTHWZ37WR0</tt><br/> + - total (amount string): <tt>TESTKUDOS:1</tt><br/> + - sig (Crockford Base32, algorithm ED25519): <tt>B14WGS...XGY30</tt></t> + <t>Public signing key (Crockford Base32) (from <tt>/keys</tt>):<br/> +<tt>2FRN2CAK9DMDWE157W6HY97RAVSP0ZCCC08X9N6JD2MK7413XXZG</tt></t> + <t>Donor hash <tt>H</tt> = <tt>SHA-512( UTF-8(taxid) || 0x00 || UTF-8(salt) || 0x00 )</tt>:<br/> + - length: 64 bytes<br/> + - hex:<br/> +<tt>4aaa1e16fc5be44842b863b1f17da39296ca7b3529a720e11aba9c8bd729f7a1e2bb0b9a39c02d271da5dd15aea66ce95be78bcaf380de19a0bdbcd8a7938f1b</tt><br/> + - Crockford Base32 (for HTTP endpoints):<br/> +<tt>9AN1W5QWBFJ4GGNRCERZ2ZD3JABCMYSN56KJ1R8TQAE8QNS9YYGY5ERBK8WW0B973PJXT5DEMSPEJPZ7HF5F706Y36GBVF6RMY9RY6R</tt></t> + <t>Amount encoding (<tt>TALER_AmountNBO</tt>):<br/> + - currency (12 bytes): <tt>"TESTKUDOS"</tt> then <tt>0x00</tt> x 3<br/> + - value (uint64 BE): <tt>1</tt> -> <tt>0x0000000000000001</tt><br/> + - fraction (uint32 BE): <tt>0</tt> -> <tt>0x00000000</tt></t> + <t>Signed message <tt>M</tt> layout (network byte order, total 100 bytes):<br/> + - [0000..0003] 4 bytes <tt>size</tt>: <tt>0x00000064</tt><br/> + - [0004..0007] 4 bytes <tt>purpose</tt>: <tt>0x000005DC</tt> (1500)<br/> + - [0008..0015] 8 bytes <tt>amount.value</tt>: <tt>0x0000000000000001</tt><br/> + - [0016..0019] 4 bytes <tt>amount.fraction</tt>: <tt>0x00000000</tt><br/> + - [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/> + - [0032..0095] 64 bytes <tt>i.hash</tt>: donor hash <tt>H</tt><br/> + - [0096..0099] 4 bytes <tt>year</tt>: <tt>0x000007E9</tt> (2025)</t> + <t>Message <tt>M</tt> (hex, 100 bytes):<br/> +<tt>00000064 000005dc 0000000000000001 00000000 544553544b55444f53000000 4aaa1e16fc5be44842b863b1f17da39296ca7b3529a720e11aba9c8bd729f7a1e2bb0b9a39c02d271da5dd15aea66ce95be78bcaf380de19a0bdbcd8a7938f1b 000007e9</tt></t> + <t>Signature (ED25519, Crockford Base32 -> bytes):<br/> + - length: 64 bytes<br/> + - R (first 32 bytes, hex):<br/> +<tt>5849c864837bece5a254ce0dc0c51434e2956c6393e2339b06b5054ac490c088</tt><br/> + - S (last 32 bytes, hex):<br/> +<tt>fb05891b09fb36020f0bcf132acfee46632411de4e4bf27fe972f891fd7b0f0c</tt></t> + <t>Public key (Ed25519, Crockford Base32 -> bytes):<br/> + - length: 32 bytes<br/> + - hex:<br/> +<tt>13f15131534b68de38253f0d1f24f856f3607d8c6011d4d4d268a9339023ef7f</tt></t> + <t>Verification (expected): call <tt>crypto_sign_verify_detached(sig, M, 100, pubkey)</tt>. The result MUST indicate a valid signature for this vector.</t> + <t>Negative check (optional): flip one bit of <tt>M</tt> or <tt>sig</tt> and repeat; verification MUST fail.</t> </list> </section>