lsd0009

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

commit a1509e657d610aeb94ba4ce943561ca02a9b8e4e
parent 32187fd69c489eece04907b549e76e92ff3f15fe
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date:   Tue,  7 Apr 2026 16:40:32 +0200

protocol: add explicit for-loops

Diffstat:
Mdraft-guetschow-taler-protocol.md | 193+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mdraft-guetschow-taler-protocol.xml | 536+++++++++++++++++++++++++++++++++++++++++--------------------------------------
2 files changed, 382 insertions(+), 347 deletions(-)

diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md @@ -459,14 +459,15 @@ and might be chosen to be implemented differently. master_secret = random(256) persist master_secret -coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) -blind_secretᵢ = coin_seedᵢ[32:] -coinᵢ.priv = coin_seedᵢ[:32] -coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv) -h_denomᵢ = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub) -blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) +for i in 0..n: + coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) + blind_secretᵢ = coin_seedᵢ[32:] + coinᵢ.priv = coin_seedᵢ[:32] + coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv) + h_denomᵢ = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub) + blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) + h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) planchets = (⟨h_denomᵢ⟩, ⟨blind_coinᵢ⟩) -h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) msg = Sign-Msg(WALLET_RESERVE_WITHDRAW, ( sum( ⟨denomᵢ.value⟩ ) | sum( ⟨denomᵢ.fee_withdraw⟩ ) | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) @@ -477,9 +478,10 @@ sig = EdDSA-Sign(reserve.priv, msg) (E1) coin issuance and signing (exchange) (⟨h_denomᵢ⟩, ⟨blind_coinᵢ⟩) = planchets -denomᵢ = Denom-Lookup(h_denomᵢ) -check denomᵢ.pub known and not withdrawal-expired -h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) +for i in 0..n: + denomᵢ = Denom-Lookup(h_denomᵢ) + check denomᵢ known and not withdrawal-expired + h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) msg = Sign-Msg(WALLET_RESERVE_WITHDRAW, ( sum( ⟨denomᵢ.value⟩ ) | sum( ⟨denomᵢ.fee_withdraw⟩ ) | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) @@ -488,17 +490,19 @@ check reserve KYC status ok or not needed total = sum( ⟨denomᵢ.value⟩ ) + sum( ⟨denomᵢ.fee_withdraw⟩ ) check reserve.balance >= total persist reserve.balance -= total -blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) +for i in 0..n: + blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) persist withdrawal // todo: what exactly? should be checked first for replay? ~~~ ~~~ (W3) coin unblinding (wallet) -coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) -check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) -coinᵢ.h_denom = h_denomᵢ -persist (coinᵢ, blind_secretᵢ) // todo: why blind_secret, if master_secret already persisted? +for i in 0..n: + coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) + check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) + coinᵢ.h_denom = h_denomᵢ + persist (coinᵢ, blind_secretᵢ) // todo: why blind_secret, if master_secret already persisted? ~~~ ## Payment {#payment} @@ -598,15 +602,16 @@ check EdDSA-Verify(merchant.pub, msg, sig) check contract.nonce == nonce // TODO: double-check extra hash check? ⟨selectionᵢ⟩ = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here -(coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ -msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, - ( h_contract | uint256(0x0) - | uint512(0x0) | contract.h_wire | coinᵢ.h_denom - | contract.timestamp | contract.refund_deadline - | contributionᵢ + denomᵢ.fee_deposit - | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) -sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ) -depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) +for i in 0..n: + (coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ + msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, + ( h_contract | uint256(0x0) + | uint512(0x0) | contract.h_wire | coinᵢ.h_denom + | contract.timestamp | contract.refund_deadline + | contributionᵢ + denomᵢ.fee_deposit + | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) + sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ) + depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩) ~~~ @@ -680,20 +685,23 @@ sig = EdDSA-Sign(merchant.priv, msg) ~~~ (E1) Deposit validation (exchange) -coinᵢ = depositᵢ.coin -denomᵢ = Denom-Lookup(coinᵢ.h_denom) -check denomᵢ.pub known and not deposit-expired // todo: check could be included in Denom-Lookup h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64) -msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, - ( h_contract | uint256(0x0) - | uint512(0x0) | h_wire | coinᵢ.h_denom - | info.time.timestamp | info.time.refund_deadline - | depositᵢ.contribution + denomᵢ.fee_deposit - | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) -check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig) -check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) -check not overspending -persist deposit-record, mark-spent +for i in 0..n: + coinᵢ = depositᵢ.coin + denomᵢ = Denom-Lookup(coinᵢ.h_denom) + check denomᵢ known and not deposit-expired // todo: check could be included in Denom-Lookup + totalᵢ = depositᵢ.contribution + denomᵢ.fee_deposit + msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, + ( h_contract | uint256(0x0) + | uint512(0x0) | h_wire | coinᵢ.h_denom + | info.time.timestamp | info.time.refund_deadline + | totalᵢ + | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) + check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig) + check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) + check coinᵢ.value >= totalᵢ + persist coinᵢ.value -= totalᵢ +persist deposit-record schedule bank transfer to payto timestamp = now() msg = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT, @@ -777,28 +785,31 @@ h_planchetₖᵢ // see TALER_coin_ev_hash h_planchetsₖ // see TALER_wallet_blinded_planchet_details_hash commitment // see TALER_refresh_get_commitment -⟨ₖᵢ⟩ +⟨ᵧₖᵢ⟩ {:/} ~~~ (W1) coin melting (wallet) -h_denomᵢ = SHA-512(uint32(0) | uint32(0x1) | denomᵢ.pub) refresh_seed = random(256) ⟨batch_seedₖ⟩ = HKDF("refresh-batch-seeds", refresh_seed, coin.priv, k*64) -⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) -transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) -sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) -planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) -blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) -coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) -coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) -blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) -h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) -h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) -value = coin.denom.fee_refresh + sum( denomᵢ.value ) + sum( denomᵢ.fee_withdraw ) +for k in 0..kappa: + ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) + for i in 0..n: + transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) + sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) + planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) + blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) + coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) + coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) + blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) + h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) + h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) +value = coin.denom.fee_refresh + sum( ⟨denomᵢ.value⟩ ) + sum( ⟨denomᵢ.fee_withdraw⟩ ) commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value | SHA-512( ⟨h_planchetsₖ⟩ ) ) +for i in 0..n: + h_denomᵢ = SHA-512(uint32(0) | uint32(0x1) | denomᵢ.pub) planchets = (⟨h_denomᵢ⟩, ⟨blind_coinₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) msg = Sign-Msg(WALLET_COIN_MELT, ( commitment | coin.h_denom | uint256(0x0) @@ -811,35 +822,40 @@ persist (coin.denom.pub, ...) // todo: double-check see TEH_handler_melt -⟨ₖᵢ⟩ +⟨ᵧₖᵢ⟩ {:/} ~~~ (E1) gamma selection and coin signing (exchange) denom = Denom-Lookup(coin.h_denom) -check denom.pub known and not refresh-expired +check denom known and not refresh-expired check RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub) -check coin dirty +check coin.pub known and dirty (⟨h_denomᵢ⟩, ⟨blind_coinₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) = planchets -denomᵢ = Denom-Lookup(h_denomᵢ) -check denomᵢ.pub known and not withdraw-expired -check value == coin.denom.fee_refresh + sum( denomᵢ.value ) + sum( denomᵢ.fee_withdraw ) -check not overspending coin with denom.value + denom.fee_refresh + sum(denomᵢ.fee_withdraw) -mark spend -h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) -h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) +for i in 0..n: + denomᵢ = Denom-Lookup(h_denomᵢ) + check denomᵢ known and not withdraw-expired +value' = coin.denom.fee_refresh + sum( ⟨denomᵢ.value⟩ ) + sum( ⟨denomᵢ.fee_withdraw⟩ ) +check value' == value +check coin.value >= value +persist coin.value -= value +for k in 0..kappa: + for i in 0..n: + h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) + h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value | SHA-512( ⟨h_planchetsₖ⟩ ) ) msg = Sign-Msg(WALLET_COIN_MELT, ( commitment | coin.h_denom | uint256(0x0) | value | denom.fee_refresh )) check EdDSA-Verify(coin.pub, msg, sig) -(ɣ, ⟨blind_sigᵢ⟩) = lookup refresh-record(commitment) -if refresh-record == NONE: - ɣ = 0..kappa at random - blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) // todo: notation for ɣ - persist refresh-record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsₖ) for k=ɣ +(ɣ, _, _) = lookup refresh-record(commitment) +if refresh-record not found: + ɣ = 0..kappa at random + for i in 0..n: + blind_sigᵢ = RSA-FDH-Sign(blind_coinᵧᵢ, denomᵧᵢ.priv) + persist refresh-record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ) msg = Sign-Msg(EXCHANGE_CONFIRM_MELT, ( commitment | uint32(ɣ) )) sig = EdDSA-Sign(exchange.priv, msg) @@ -850,45 +866,46 @@ sig = EdDSA-Sign(exchange.priv, msg) // see src/lib/exchange_api_post-melt.c: handle_melt_finished // see src/lib/exchange_api_post-reveal-melt.c: perform_protocol -⟨ₖᵢ⟩ +⟨ᵧₖᵢ⟩ {:/} ~~~ (W2) secret revelation (wallet) -check exchange.pub is expected +check exchange.pub known msg = Sign-Msg(EXCHANGE_CONFIRM_MELT, ( commitment | ɣ )) check EdDSA-Verify(exchange.pub, msg, sig) persist refresh-challenge // what exactly? -⟨revealed_seedₖ⟩ = ⟨batch_seedₖ⟩ without k = ɣ +for k in 0..kappa and k != ɣ: + revealed_seedₖ = batch_seedₖ ~~~ {::comment} // see TEH_handler_reveal_melt -⟨ₖᵢ⟩ +⟨ᵧₖᵢ⟩ {:/} ~~~ (E2) commitment validation (exchange) -// find_original_refresh -(ɣ, ⟨blind_sigᵢ⟩) = lookup refresh-record(commitment) +(ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ) = lookup refresh-record(commitment) // verify_commitment, for k != ɣ -⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) -transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) -sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) -planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) -blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) -coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) -coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) -blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) -h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) -h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) +for k in 0..kappa and k != ɣ: + ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) + for i in 0..n: + transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) + sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) + planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) + blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) + coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) + coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) + blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) + h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) + h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) value = coin.denom.fee_refresh + sum( denomᵢ.value ) + sum( denomᵢ.fee_withdraw ) -// todo: h_planchetsₖ for k=ɣ as saved before commitment' = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value | SHA-512( ⟨h_planchetsₖ⟩ ) ) check commitment == commitment' @@ -899,17 +916,17 @@ persist mark-revealed // see src/lib/exchange_api_post-reveal-melt.c: reveal_melt_ok -⟨ₖᵢ⟩ +⟨ᵧₖᵢ⟩ {:/} ~~~ (W3) coin unblinding (wallet) -set k=ɣ -coinₖᵢ.sig = RSA-FDH-Unblind(blind_sigₖᵢ, blind_secretₖᵢ, denomᵢ.pub) -check RSA-FDH-Verify(SHA-512(coinₖᵢ.pub), coinₖᵢ.sig, denomᵢ.pub) -coinₖᵢ.h_denom = h_denomᵢ -persist ⟨coinₖᵢ⟩ +for i in 0..n: + coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub) + check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub) + coinᵧᵢ.h_denom = h_denomᵢ + persist ⟨coinᵧᵢ⟩ ~~~ // todo: also add linking protocol? diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml @@ -440,14 +440,15 @@ and might be chosen to be implemented differently.</t> master_secret = random(256) persist master_secret -coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) -blind_secretᵢ = coin_seedᵢ[32:] -coinᵢ.priv = coin_seedᵢ[:32] -coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv) -h_denomᵢ = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub) -blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) +for i in 0..n: + coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64) + blind_secretᵢ = coin_seedᵢ[32:] + coinᵢ.priv = coin_seedᵢ[:32] + coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv) + h_denomᵢ = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub) + blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) + h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) planchets = (⟨h_denomᵢ⟩, ⟨blind_coinᵢ⟩) -h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) msg = Sign-Msg(WALLET_RESERVE_WITHDRAW, ( sum( ⟨denomᵢ.value⟩ ) | sum( ⟨denomᵢ.fee_withdraw⟩ ) | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) @@ -457,9 +458,10 @@ sig = EdDSA-Sign(reserve.priv, msg) (E1) coin issuance and signing (exchange) (⟨h_denomᵢ⟩, ⟨blind_coinᵢ⟩) = planchets -denomᵢ = Denom-Lookup(h_denomᵢ) -check denomᵢ.pub known and not withdrawal-expired -h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) +for i in 0..n: + denomᵢ = Denom-Lookup(h_denomᵢ) + check denomᵢ known and not withdrawal-expired + h_planchetᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinᵢ ) msg = Sign-Msg(WALLET_RESERVE_WITHDRAW, ( sum( ⟨denomᵢ.value⟩ ) | sum( ⟨denomᵢ.fee_withdraw⟩ ) | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) @@ -468,16 +470,18 @@ check reserve KYC status ok or not needed total = sum( ⟨denomᵢ.value⟩ ) + sum( ⟨denomᵢ.fee_withdraw⟩ ) check reserve.balance >= total persist reserve.balance -= total -blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) +for i in 0..n: + blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) persist withdrawal // todo: what exactly? should be checked first for replay? ]]></artwork> <artwork><![CDATA[ (W3) coin unblinding (wallet) -coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) -check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) -coinᵢ.h_denom = h_denomᵢ -persist (coinᵢ, blind_secretᵢ) // todo: why blind_secret, if master_secret already persisted? +for i in 0..n: + coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) + check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) + coinᵢ.h_denom = h_denomᵢ + persist (coinᵢ, blind_secretᵢ) // todo: why blind_secret, if master_secret already persisted? ]]></artwork> </section> <section anchor="payment"> @@ -570,15 +574,16 @@ check EdDSA-Verify(merchant.pub, msg, sig) check contract.nonce == nonce // TODO: double-check extra hash check? ⟨selectionᵢ⟩ = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here -(coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ -msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, - ( h_contract | uint256(0x0) - | uint512(0x0) | contract.h_wire | coinᵢ.h_denom - | contract.timestamp | contract.refund_deadline - | contributionᵢ + denomᵢ.fee_deposit - | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) -sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ) -depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) +for i in 0..n: + (coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ + msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, + ( h_contract | uint256(0x0) + | uint512(0x0) | contract.h_wire | coinᵢ.h_denom + | contract.timestamp | contract.refund_deadline + | contributionᵢ + denomᵢ.fee_deposit + | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) + sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ) + depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩) ]]></artwork> <t>// TODO: explain CoinSelection</t> @@ -642,20 +647,23 @@ sig = EdDSA-Sign(merchant.priv, msg) <artwork><![CDATA[ (E1) Deposit validation (exchange) -coinᵢ = depositᵢ.coin -denomᵢ = Denom-Lookup(coinᵢ.h_denom) -check denomᵢ.pub known and not deposit-expired // todo: check could be included in Denom-Lookup h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64) -msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, - ( h_contract | uint256(0x0) - | uint512(0x0) | h_wire | coinᵢ.h_denom - | info.time.timestamp | info.time.refund_deadline - | depositᵢ.contribution + denomᵢ.fee_deposit - | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) -check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig) -check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) -check not overspending -persist deposit-record, mark-spent +for i in 0..n: + coinᵢ = depositᵢ.coin + denomᵢ = Denom-Lookup(coinᵢ.h_denom) + check denomᵢ known and not deposit-expired // todo: check could be included in Denom-Lookup + totalᵢ = depositᵢ.contribution + denomᵢ.fee_deposit + msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT, + ( h_contract | uint256(0x0) + | uint512(0x0) | h_wire | coinᵢ.h_denom + | info.time.timestamp | info.time.refund_deadline + | totalᵢ + | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) + check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig) + check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) + check coinᵢ.value >= totalᵢ + persist coinᵢ.value -= totalᵢ +persist deposit-record schedule bank transfer to payto timestamp = now() msg = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT, @@ -726,22 +734,25 @@ knows coin | <artwork><![CDATA[ (W1) coin melting (wallet) -h_denomᵢ = SHA-512(uint32(0) | uint32(0x1) | denomᵢ.pub) refresh_seed = random(256) ⟨batch_seedₖ⟩ = HKDF("refresh-batch-seeds", refresh_seed, coin.priv, k*64) -⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) -transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) -sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) -planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) -blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) -coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) -coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) -blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) -h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) -h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) -value = coin.denom.fee_refresh + sum( denomᵢ.value ) + sum( denomᵢ.fee_withdraw ) +for k in 0..kappa: + ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) + for i in 0..n: + transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) + sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) + planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) + blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) + coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) + coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) + blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) + h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) + h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) +value = coin.denom.fee_refresh + sum( ⟨denomᵢ.value⟩ ) + sum( ⟨denomᵢ.fee_withdraw⟩ ) commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value | SHA-512( ⟨h_planchetsₖ⟩ ) ) +for i in 0..n: + h_denomᵢ = SHA-512(uint32(0) | uint32(0x1) | denomᵢ.pub) planchets = (⟨h_denomᵢ⟩, ⟨blind_coinₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) msg = Sign-Msg(WALLET_COIN_MELT, ( commitment | coin.h_denom | uint256(0x0) @@ -753,28 +764,33 @@ persist (coin.denom.pub, ...) // todo: double-check (E1) gamma selection and coin signing (exchange) denom = Denom-Lookup(coin.h_denom) -check denom.pub known and not refresh-expired +check denom known and not refresh-expired check RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub) -check coin dirty +check coin.pub known and dirty (⟨h_denomᵢ⟩, ⟨blind_coinₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) = planchets -denomᵢ = Denom-Lookup(h_denomᵢ) -check denomᵢ.pub known and not withdraw-expired -check value == coin.denom.fee_refresh + sum( denomᵢ.value ) + sum( denomᵢ.fee_withdraw ) -check not overspending coin with denom.value + denom.fee_refresh + sum(denomᵢ.fee_withdraw) -mark spend -h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) -h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) +for i in 0..n: + denomᵢ = Denom-Lookup(h_denomᵢ) + check denomᵢ known and not withdraw-expired +value' = coin.denom.fee_refresh + sum( ⟨denomᵢ.value⟩ ) + sum( ⟨denomᵢ.fee_withdraw⟩ ) +check value' == value +check coin.value >= value +persist coin.value -= value +for k in 0..kappa: + for i in 0..n: + h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) + h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value | SHA-512( ⟨h_planchetsₖ⟩ ) ) msg = Sign-Msg(WALLET_COIN_MELT, ( commitment | coin.h_denom | uint256(0x0) | value | denom.fee_refresh )) check EdDSA-Verify(coin.pub, msg, sig) -(ɣ, ⟨blind_sigᵢ⟩) = lookup refresh-record(commitment) -if refresh-record == NONE: - ɣ = 0..kappa at random - blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv) // todo: notation for ɣ - persist refresh-record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsₖ) for k=ɣ +(ɣ, _, _) = lookup refresh-record(commitment) +if refresh-record not found: + ɣ = 0..kappa at random + for i in 0..n: + blind_sigᵢ = RSA-FDH-Sign(blind_coinᵧᵢ, denomᵧᵢ.priv) + persist refresh-record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ) msg = Sign-Msg(EXCHANGE_CONFIRM_MELT, ( commitment | uint32(ɣ) )) sig = EdDSA-Sign(exchange.priv, msg) @@ -782,31 +798,32 @@ sig = EdDSA-Sign(exchange.priv, msg) <artwork><![CDATA[ (W2) secret revelation (wallet) -check exchange.pub is expected +check exchange.pub known msg = Sign-Msg(EXCHANGE_CONFIRM_MELT, ( commitment | ɣ )) check EdDSA-Verify(exchange.pub, msg, sig) persist refresh-challenge // what exactly? -⟨revealed_seedₖ⟩ = ⟨batch_seedₖ⟩ without k = ɣ +for k in 0..kappa and k != ɣ: + revealed_seedₖ = batch_seedₖ ]]></artwork> <artwork><![CDATA[ (E2) commitment validation (exchange) -// find_original_refresh -(ɣ, ⟨blind_sigᵢ⟩) = lookup refresh-record(commitment) +(ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ) = lookup refresh-record(commitment) // verify_commitment, for k != ɣ -⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) -transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) -sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) -planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) -blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) -coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) -coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) -blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) -h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) -h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) +for k in 0..kappa and k != ɣ: + ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) + for i in 0..n: + transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv) + sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub) + planchet_seedₖᵢ = HKDF(uint32(i), sharedₖᵢ, "taler-coin-derivation", 64) + blind_secretₖᵢ = HKDF("bks", planchet_seedₖᵢ, "", 32) + coinₖᵢ.priv = HKDF("coin", planchet_seedₖᵢ, "", 32) + coinₖᵢ.pub = EdDSA-GetPub(coinₖᵢ.priv) + blind_coinₖᵢ = RSA-FDH-Blind(SHA-512(coinₖᵢ.pub), blind_secretₖᵢ, denomᵢ.pub) + h_planchetₖᵢ = SHA-512( SHA-512( denomᵢ.pub ) | uint32(0x1) | blind_coinₖᵢ ) + h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) value = coin.denom.fee_refresh + sum( denomᵢ.value ) + sum( denomᵢ.fee_withdraw ) -// todo: h_planchetsₖ for k=ɣ as saved before commitment' = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value | SHA-512( ⟨h_planchetsₖ⟩ ) ) check commitment == commitment' @@ -815,11 +832,11 @@ persist mark-revealed <artwork><![CDATA[ (W3) coin unblinding (wallet) -set k=ɣ -coinₖᵢ.sig = RSA-FDH-Unblind(blind_sigₖᵢ, blind_secretₖᵢ, denomᵢ.pub) -check RSA-FDH-Verify(SHA-512(coinₖᵢ.pub), coinₖᵢ.sig, denomᵢ.pub) -coinₖᵢ.h_denom = h_denomᵢ -persist ⟨coinₖᵢ⟩ +for i in 0..n: + coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub) + check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub) + coinᵧᵢ.h_denom = h_denomᵢ + persist ⟨coinᵧᵢ⟩ ]]></artwork> <t>// todo: also add linking protocol?</t> </section> @@ -927,7 +944,7 @@ persist ⟨coinₖᵢ⟩ <refcontent>National Institute of Standards and Technology (U.S.)</refcontent> </reference> </references> - <?line 941?> + <?line 958?> <section anchor="change-log"> <name>Change log</name> @@ -940,181 +957,182 @@ Education and Research (BMBF) within the project Concrete Contracts.</t> </section> </back> <!-- ##markdown-source: -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 +OBprKtvIig+qhBQ+RW6C/QzBGaYJpa21O8C2Gm4lHdcXU/tgU7LST5LMxfDc +DfOQ8LNjVIHjXMvvDf7Z806/VmTlTdixYFqLzaluiUqyLSmbhUv14tbGLN9Y +1SJvSdGgSGps9MQnp7t18tMYVUJ8CtE3rhCKnFL46GWOyhZOGyAVQIcdePun +u84zPgM4u2aVzaMyjfnyiXT8HlWlEhWcmUZG1N0SiHNoM6pYmqaAz1Wh0MTF +MF5V6DBaMUIyHbjUrTQacYigYpk75SVUCmwxuIPQm3YHIxWCGWZ27jkNC7AY +zE900u5xyLcCmetSClxAwsf9rFgMwFwPQm9aDHLDGYsIAaxwyMrDxB+NKQiU +EajICvRouIyqtrcEMzOykwpB4zWRcBTUmNDG9k5MHim0O/jV3tPd4ycHXcgT +0q6iU5fyrCYkqYAZ7xIb6hAVZv2Ay7SaaCbY5UDsDDI0jaO+24dhqkmDgqbF +aJUryYVJ0FHOC6vsYgPr1oOdXJ4KMBbKso8zbDUatE8LR4KH3ANToVaIZYLk +1+rF1nW2IQ445CLjYHvH0/uUNuq07YIVKC66Nkd6s73VfitHVyFmGaS9vVUA +yfpaaZ7w9GXWt83WOOa4q2JWY5ednEzLyPw28ToPb7N+jrDs0igCi3Ul1ZuB +Da6TFydZn+t03FUesYhUfmG2YAaOrUvCsogVcFU5WKxVgqXPpwx2vs607c9T +RMicElw20JWA17vPnx+cdU8OTg9OfnnQfX149nT/ZPd1nayljdmrXQj/yQxh +yoD4zD2FLE5rBgFZwtLqCRKSBhVUT2q/d+uyVZj23LdaTS58CN7TgpAOOoDz +oqQhzCQp0HIHbqu8CPRnTQrC2Jru8xpkyNw+XjrPo+g8m9p5x0gTYRg0LGZZ +Qq1xj4yhT5CD+pDA3oXs/O3LgSCikAO5RFIIP9V6iYJUzvbZr/ewOpZmCYvO +sXKARBeFT0tUKDpL5/vNOvMtDNnouwGJ3g8dUQPR9rgM4CiAOcEyozjDIJEC +FHhr2h0yhWosw7VrV3iBmyb4pYtO+0eWQEATeMKlAvpYHqGdzogMOLjAnf1o +aFZVAJd7o0rfgkgJ9S2vPZrTW8OUCvKWFsiqzbIxbkU/8qFUT0AsV1R4rMMw +TdsSZjWTlLPCU1rtKvpkN4i5681Ut9z7UW+TeimO57Gre/KgXrEAGPWxSJ2w +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 -->