lsd0009

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

commit 1abafe19ee9fbfd194fb58b77a4ed154bf3ec8cd
parent 61094f3cb7110248757d1aa271ff9170439bb218
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date:   Mon, 27 Jan 2025 13:09:12 +0100

-make coin derivation from master_secret explicit

Diffstat:
Mdraft-guetschow-taler-protocol.md | 52+++++++++++++++++++++++++++++++---------------------
Mdraft-guetschow-taler-protocol.xml | 244++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
2 files changed, 166 insertions(+), 130 deletions(-)

diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md @@ -346,14 +346,16 @@ out = (data == exp) # The Taler Crypto Protocol // todo: explain persist, check +// todo: add KYC check ## Withdrawal -The wallet creates `n > 0` coins and requests `n` signatures from the Exchange, -giving values to the coins according to `n` denominations. +The wallet creates `n > 0` coins and requests `n` signatures from the exchange, +attributing value to the coins according to `n` chosen 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. +The symbol `*` in front of a certain part means that it is repeated `n` times for the `n` coins, +where `*?` denotes the index number `0 <= *? <= n`. ~~~ Wallet Exchange @@ -365,15 +367,15 @@ 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) + | persist (reserve.pub, value) +master_secret = random(256) | +persist master_secret | +*(coin, blind_secret) = GenerateCoin(master_secret, *?) | +*blind_coin = 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) | + |(*SHA-512(denom.pub), *blind_coin, sig) | | | | check *denom.pub known and not withdrawal-expired | check EdDSA-Verify(reserve.pub, msg, sig) @@ -400,26 +402,34 @@ msg = bigEndian(32, 40) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_ | SHA-512(commit) ~~~ -(for RSA, without age-restriction) +The wallet derives coins and blinding secrets using `GenerateCoin` from a master secret and an integer index. +This is strictly speaking an implementation detail since the master secret is never revealed to any other party, +and might be chosen to be implemented differently. -// todo: add KYC check +// todo: discuss with Florian: use reserve.priv as master_secret? -### Implementation Details +~~~ +GenerateCoin(secret, idx) -> (coin, bks) -in wallet-core, `coin.priv` and `blind_secret` are derived from a random `secretSeed` as follows: +Inputs: + secret secret to derive coin from + idx coin index +Output: + coin private-public keypair of the coin + bks random blinding key secret of length 32 bytes ~~~ -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:] +`coin` and `blind_secret` are calculated as follows: + +~~~ +tmp = HKDF(bigEndian(32, *?), master_secret, "taler-withdrawal-coin-derivation", 64) +(coin.priv, bks) = (tmp[:32], tmp[32:]) +coin.pub = EdDSA-GetPub(coin.priv) ~~~ +(for RSA, without age-restriction) + # Security Considerations 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="25"/> + <date year="2025" month="January" day="27"/> <workgroup>independent</workgroup> @@ -68,9 +68,14 @@ Use at your own risk!</t> <t><list style="symbols"> <t><spanx style="verb">"abc"</spanx> denotes the literal string <spanx style="verb">abc</spanx> encoded as ASCII <xref target="RFC20"></xref></t> <t><spanx style="verb">a | b</spanx> denotes the concatenation of a with b</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">padZero(12, a)</spanx> denotes the byte string a, zero-padded to the length of 12 bytes</t> + <t><spanx style="verb">bits(x)</spanx> (<spanx style="verb">bytes(x)</spanx>) denotes the minimal number of bits (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">bigEndianAmount(amount)</spanx> is formed from a fixed-point representation of <spanx style="verb">amount</spanx> +as <spanx style="verb">bigEndian(64, amount.value) | bigEndian(32, amount.fraction) | padZero(12, amount.currency)</spanx>, +where <spanx style="verb">amount.value</spanx> is the non-negative integer part of the base currency, +<spanx style="verb">amount.fraction</spanx> is given in unites of one hundred millionth (1e-8) of the base currency, +and <spanx style="verb">amount.currency</spanx> are the 3-11 ASCII characters used as currency code by the exchange.</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> @@ -371,15 +376,17 @@ out = (data == exp) </section> <section anchor="the-taler-crypto-protocol"><name>The Taler Crypto Protocol</name> -<t>// todo: explain persist, check</t> +<t>// todo: explain persist, check +// todo: add KYC check</t> <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. +<t>The wallet creates <spanx style="verb">n &gt; 0</spanx> coins and requests <spanx style="verb">n</spanx> signatures from the exchange, +attributing value to the coins according to <spanx style="verb">n</spanx> chosen 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> +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, +where <spanx style="verb">*?</spanx> denotes the index number <spanx style="verb">0 &lt;= *? &lt;= n</spanx>.</t> <figure><artwork><![CDATA[ Wallet Exchange @@ -391,17 +398,17 @@ 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) + | persist (reserve.pub, value) +master_secret = random(256) | +persist master_secret | +*(coin, blind_secret) = GenerateCoin(master_secret, *?) | +*blind_coin = 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) | + |(*SHA-512(denom.pub), *blind_coin, sig) | | | - | check *denom.pub valid + | check *denom.pub known and not withdrawal-expired | check EdDSA-Verify(reserve.pub, msg, sig) | check reserve.balance >= sum(*denom.valueAndFee) | reserve.balance -= sum(*denom.valueAndFee) @@ -426,25 +433,34 @@ msg = bigEndian(32, 40) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_ | SHA-512(commit) ]]></artwork></figure> -<t>(for RSA, without age-restriction)</t> - -<section anchor="implementation-details"><name>Implementation Details</name> +<t>The wallet derives coins and blinding secrets using <spanx style="verb">GenerateCoin</spanx> from a master secret and an integer index. +This is strictly speaking an implementation detail since the master secret is never revealed to any other party, +and might be chosen to be implemented differently.</t> -<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> +<t>// todo: discuss with Florian: use reserve.priv as master_secret?</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:] +GenerateCoin(secret, idx) -> (coin, bks) + +Inputs: + secret secret to derive coin from + idx coin index +Output: + coin private-public keypair of the coin + bks random blinding key secret of length 32 bytes ]]></artwork></figure> -</section> +<t><spanx style="verb">coin</spanx> and <spanx style="verb">blind_secret</spanx> are calculated as follows:</t> + +<figure><artwork><![CDATA[ +tmp = HKDF(bigEndian(32, *?), master_secret, "taler-withdrawal-coin-derivation", 64) +(coin.priv, bks) = (tmp[:32], tmp[32:]) +coin.pub = EdDSA-GetPub(coin.priv) +]]></artwork></figure> + +<t>(for RSA, without age-restriction)</t> + </section> </section> <section anchor="security-considerations"><name>Security Considerations</name> @@ -553,7 +569,7 @@ for coinIdx in 0..n-1: -<?line 425?> +<?line 442?> <section anchor="change-log"><name>Change log</name> @@ -571,87 +587,97 @@ Education and Research (BMBF) within the project Concrete Contracts.</t> </back> <!-- ##markdown-source: -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 -->