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:
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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
+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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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
-->