lsd0009

LSD0009: The GNU Taler Protocol
Log | Files | Refs | README

commit ba33758e6f7ab5020785634ad4ba026c89443c9c
parent 78f571ddd833bb028b78ccac48ea0be773f61236
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date:   Fri, 24 Jan 2025 19:42:13 +0100

protocol: withdrawal

Diffstat:
Mdraft-guetschow-taler-protocol.md | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdraft-guetschow-taler-protocol.xml | 224++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
2 files changed, 235 insertions(+), 72 deletions(-)

diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md @@ -14,9 +14,9 @@ keyword: - ecash - payments -# venue: -# repo: https://git.gnunet.org/lsd0009.git/ -# latest: https://lsd.gnunet.org/lsd0009/ +#venue: +# repo: https://git.gnunet.org/lsd0009.git/ +# latest: https://lsd.gnunet.org/lsd0009/ author: - @@ -60,6 +60,7 @@ Use at your own risk! - `bits(x)` denotes the minimal number of bits necessary to represent the multiple precision integer x - `bytes(x)` denotes the minimal number of bytes necessary to represent the multiple precision integer x - `bigEndian(16, x)` denotes the 16 least significant bits of the integer x encoded in network byte order (big-endian) +- `random(256)` denotes a randomly generated sequence of 256 bits - `a * b (mod N)` denotes the multiplication, `a ** b (mod N)` the exponentiation of a and b, modulo N # Cryptographic Primitives @@ -234,7 +235,7 @@ by verifying that the greatest common denominator (gcd) of `fdh` and `pubkey.N` RSA-FDH-Derive(bks, pubkey) -> out Inputs: - bks blinding key secret of length L = 8 octets + bks blinding key secret of length L = 32 octets pubkey RSA public key consisting of modulus N and public exponent e Output: @@ -256,7 +257,7 @@ RSA-FDH-Blind(msg, bks, pubkey) -> out Inputs: msg message - bks blinding key secret of length L = 8 octets + bks blinding key secret of length L = 32 octets pubkey RSA public key consisting of modulus N and public exponent e Output: @@ -298,7 +299,7 @@ RSA-FDH-Unblind(sig, bks, pubkey) -> out Inputs: sig blind signature - bks blinding key secret of length L = 8 octets + bks blinding key secret of length L = 32 octets pubkey RSA public key consisting of modulus N and public exponent e Output: @@ -341,6 +342,76 @@ out = (data == exp) ## Withdrawal +The wallet creates `n > 0` coins and requests `n` signatures from the Exchange, +giving values to the coins according to `n` denominations. +The total value and withdrawal fee (defined by the Exchange per denomination) +must be smaller or equal to the amount stored in the single reserve used for withdrawal. +The symbol `*` in front of a certain part means that it is repeated `n` times for the `n` coins. + +~~~ + Wallet Exchange +knows *denom.pub knows *denom.priv + | | +reserve = EdDSA-Keygen() | +persist (reserve, value) | + | | + |----------- (bank transfer) ----------->| + | (subject: reserve.pub, amount: value) | + | | + | persist (reserve.pub, value) +*coin = EdDSA-Keygen() | +*blind_secret = random(256) | +persist *(coin, blind_secret) | +*b = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denom.pub) +sig = EdDSA-Sign(reserve.priv, msg) | + | | + |--- /reserves/{reserve.pub}/withdraw -->| + | (*SHA-512(denom.pub), *b, sig) | + | | + | check *denom.pub valid + | check EdDSA-Verify(reserve.pub, msg, sig) + | check reserve.balance >= sum(*denom.valueAndFee) + | reserve.balance -= sum(*denom.valueAndFee) + | *blind_sig = RSA-FDH-Sign(b, denom.priv) + | persist withdrawal + | | + |<------------ *blind_sig ---------------| + | | +*coin_sig = RSA-FDH-Unblind(blind_sig, blind_secret, denom.pub) +check *RSA-FDH-Verify(SHA-512(coin.pub), coin_sig, denom.pub) +persist *(coin, blind_secret, coin_sig) +~~~ + +where `msg` is formed as follows: + +~~~ +msg = bigEndian(32, 145) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */ + | bigEndianAmount(sum(*denom.value)) | bigEndianAmount(sum(*denom.fee_withdrawal)) + | SHA-512( *SHA-512(SHA-512(denom.pub) | SHA-512(bigEndian(32, 0x1) | b | bytes(b))) ) + | bigEndian(8, 0x00) | bigEndian(32, 0x00) +~~~ + +// todo: do we really want a uint8_t before a uint32_t? + +(for RSA, without age-restriction) + +### Implementation Details + +in wallet-core, `coin.priv` and `blind_secret` are derived from a random `secretSeed` as follows: + +~~~ +secretSeed = random(256) +IKM = secretSeed +info = "taler-withdrawal-coin-derivation" +for coinIdx in 0..n-1: + salt = bigEndian(32, coinIdx) + tmp = HKDF(salt, IKM, info, 64) + coin.priv = tmp[:32] + blind_secret = tmp[32:] + +~~~ + + # Security Considerations \[ TBD \] diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml @@ -29,7 +29,7 @@ </address> </author> - <date year="2025" month="January" day="22"/> + <date year="2025" month="January" day="24"/> <workgroup>independent</workgroup> @@ -71,6 +71,7 @@ Use at your own risk!</t> <t><spanx style="verb">bits(x)</spanx> denotes the minimal number of bits necessary to represent the multiple precision integer x</t> <t><spanx style="verb">bytes(x)</spanx> denotes the minimal number of bytes necessary to represent the multiple precision integer x</t> <t><spanx style="verb">bigEndian(16, x)</spanx> denotes the 16 least significant bits of the integer x encoded in network byte order (big-endian)</t> + <t><spanx style="verb">random(256)</spanx> denotes a randomly generated sequence of 256 bits</t> <t><spanx style="verb">a * b (mod N)</spanx> denotes the multiplication, <spanx style="verb">a ** b (mod N)</spanx> the exponentiation of a and b, modulo N</t> </list></t> @@ -256,7 +257,7 @@ by verifying that the greatest common denominator (gcd) of <spanx style="verb">f RSA-FDH-Derive(bks, pubkey) -> out Inputs: - bks blinding key secret of length L = 8 octets + bks blinding key secret of length L = 32 octets pubkey RSA public key consisting of modulus N and public exponent e Output: @@ -279,7 +280,7 @@ RSA-FDH-Blind(msg, bks, pubkey) -> out Inputs: msg message - bks blinding key secret of length L = 8 octets + bks blinding key secret of length L = 32 octets pubkey RSA public key consisting of modulus N and public exponent e Output: @@ -323,7 +324,7 @@ RSA-FDH-Unblind(sig, bks, pubkey) -> out Inputs: sig blind signature - bks blinding key secret of length L = 8 octets + bks blinding key secret of length L = 32 octets pubkey RSA public key consisting of modulus N and public exponent e Output: @@ -372,6 +373,76 @@ out = (data == exp) <section anchor="withdrawal"><name>Withdrawal</name> +<t>The wallet creates <spanx style="verb">n &gt; 0</spanx> coins and requests <spanx style="verb">n</spanx> signatures from the Exchange, +giving values to the coins according to <spanx style="verb">n</spanx> denominations. +The total value and withdrawal fee (defined by the Exchange per denomination) +must be smaller or equal to the amount stored in the single reserve used for withdrawal. +The symbol <spanx style="verb">*</spanx> in front of a certain part means that it is repeated <spanx style="verb">n</spanx> times for the <spanx style="verb">n</spanx> coins.</t> + +<figure><artwork><![CDATA[ + Wallet Exchange +knows *denom.pub knows *denom.priv + | | +reserve = EdDSA-Keygen() | +persist (reserve, value) | + | | + |----------- (bank transfer) ----------->| + | (subject: reserve.pub, amount: value) | + | | + | persist (reserve.pub, value) +*coin = EdDSA-Keygen() | +*blind_secret = random(256) | +persist *(coin, blind_secret) | +*b = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denom.pub) +sig = EdDSA-Sign(reserve.priv, msg) | + | | + |--- /reserves/{reserve.pub}/withdraw -->| + | (*SHA-512(denom.pub), *b, sig) | + | | + | check *denom.pub valid + | check EdDSA-Verify(reserve.pub, msg, sig) + | check reserve.balance >= sum(*denom.valueAndFee) + | reserve.balance -= sum(*denom.valueAndFee) + | *blind_sig = RSA-FDH-Sign(b, denom.priv) + | persist withdrawal + | | + |<------------ *blind_sig ---------------| + | | +*coin_sig = RSA-FDH-Unblind(blind_sig, blind_secret, denom.pub) +check *RSA-FDH-Verify(SHA-512(coin.pub), coin_sig, denom.pub) +persist *(coin, blind_secret, coin_sig) +]]></artwork></figure> + +<t>where <spanx style="verb">msg</spanx> is formed as follows:</t> + +<figure><artwork><![CDATA[ +msg = bigEndian(32, 145) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */ + | bigEndianAmount(sum(*denom.value)) | bigEndianAmount(sum(*denom.fee_withdrawal)) + | SHA-512( *SHA-512(SHA-512(denom.pub) | SHA-512(bigEndian(32, 0x1) | b | bytes(b))) ) + | bigEndian(8, 0x00) | bigEndian(32, 0x00) +]]></artwork></figure> + +<t>// todo: do we really want a uint8_t before a uint32_t?</t> + +<t>(for RSA, without age-restriction)</t> + +<section anchor="implementation-details"><name>Implementation Details</name> + +<t>in wallet-core, <spanx style="verb">coin.priv</spanx> and <spanx style="verb">blind_secret</spanx> are derived from a random <spanx style="verb">secretSeed</spanx> as follows:</t> + +<figure><artwork><![CDATA[ +secretSeed = random(256) +IKM = secretSeed +info = "taler-withdrawal-coin-derivation" +for coinIdx in 0..n-1: + salt = bigEndian(32, coinIdx) + tmp = HKDF(salt, IKM, info, 64) + coin.priv = tmp[:32] + blind_secret = tmp[32:] + +]]></artwork></figure> + +</section> </section> </section> <section anchor="security-considerations"><name>Security Considerations</name> @@ -480,7 +551,7 @@ out = (data == exp) -<?line 352?> +<?line 423?> <section anchor="change-log"><name>Change log</name> @@ -498,67 +569,88 @@ Education and Research (BMBF) within the project Concrete Contracts.</t> </back> <!-- ##markdown-source: -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 +pfx/AG6OOdPaKgAA -->