commit f1fd4a6c58e4d2bc7696bf9be64870ba09f1aec7
parent 09acb192cd7319115e366149537a28625eac2df9
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date: Wed, 8 Apr 2026 15:17:33 +0200
crypto: add description for Ed25519
Diffstat:
2 files changed, 431 insertions(+), 271 deletions(-)
diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md
@@ -34,6 +34,7 @@ normative:
RFC2104:
RFC5869:
RFC6234:
+ RFC8032:
HKDF: DOI.10.1007/978-3-642-14623-7_34
SHS: DOI.10.6028/NIST.FIPS.180-4
@@ -72,6 +73,8 @@ Use at your own risk!
# Cryptographic Primitives
+// todo: maybe change this description to something more similar to protocol functions (Julia-inspired syntax)
+
## Cryptographic Hash Functions
### SHA-256 {#sha256}
@@ -209,6 +212,76 @@ do until OKM < N:
### Ed25519
+Taler uses EdDSA instantiated with curve25519 as Ed25519,
+as defined in Section 5.1 of [RFC8032].
+In particular, Taler does _not_ make use of Ed25519ph or Ed25519ctx
+as defined in that document.
+
+#### Key generation
+
+~~~
+Ed25519-GetPub(priv) -> pub
+
+Input:
+ priv private Ed25519 key
+
+Output:
+ pub public Ed25519 key
+~~~
+
+`pub` is calculated as described in Section 5.1.5 of [RFC8032].
+
+~~~
+Ed25519-Keygen() -> (priv, pub)
+
+Output:
+ priv private Ed25519 key
+ pub public Ed25519 key
+~~~
+
+`priv` and `pub` are calculated as described in Section 5.1.5 of [RFC8032],
+which is equivalent to the following:
+
+~~~
+priv = random(256)
+pub = Ed25519-GetPub(priv)
+~~~
+
+#### Signing
+
+~~~
+Ed25519-Sign(priv, msg) -> sig
+
+Inputs:
+ priv Ed25519 private key
+ msg message to be signed
+
+Output:
+ sig signature on the message by the given private key
+~~~
+
+`sig` is calculated as described in Section 5.1.6 of [RFC8032].
+
+#### Verifying
+
+~~~
+Ed25519-Verify(pub, msg, sig) -> out
+
+Inputs:
+ pub Ed25519 public key
+ msg signed message
+ sig signature on msg
+
+Output:
+ out true, if sig is a valid signature for msg
+~~~
+
+`out` is the outcome of the last check of Section 5.1.7 of [RFC8032].
+
+## Key Agreement
+
+
+
## Blind Signatures
### RSA-FDH {#rsa-fdh}
@@ -394,8 +467,8 @@ out = uint32(len(msg)) | uint32(purpose) | msg
## Helper Functions
-There are a certain number of functions which are often needed,
-and therefore omit the typical function syntax of using parentheses:
+There are a certain number of single-argument functions which are often needed,
+and therefore omit the parentheses of the typical function syntax:
- `Knows data` specifies `data` that is known a priori at the start of the protocol operation
- `Check cond` verifies that the boolean condition or variable `cond` is true,
@@ -405,7 +478,7 @@ and therefore omit the typical function syntax of using parentheses:
- `Sum ⟨dataᵢ⟩` is valid for numerical objects `dataᵢ` including amounts (cf. {{amounts}}),
and denotes the numerical sum of these objects
-Some more functions that are used throughout {#protocol}:
+Some more functions that are commonly used throughout {{protocol}}:
~~~
Hash-Denom(denom) =
@@ -419,7 +492,7 @@ Check-Subtract(value, subtrahend) =
Persist value -= subtrahend
~~~
-# The Taler Crypto Protocol {#procotol}
+# The Taler Crypto Protocol {#protocol}
// todo: briefly introduce the three components wallet, exchange, merchant; maybe with ASCII diagram version
@@ -475,7 +548,7 @@ where (for RSA, without age-restriction)
~~~
(W1) reserve key generation (wallet)
-reserve = EdDSA-Keygen()
+reserve = Ed25519-Keygen()
Persist (reserve, value)
~~~
@@ -495,7 +568,7 @@ for i in 0..n:
coin_seedᵢ = HKDF(uint32(i), batch_seed, "taler-withdrawal-coin-derivation", 64)
blind_secretᵢ = coin_seedᵢ[32:]
coinᵢ.priv = coin_seedᵢ[:32]
- coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv)
+ coinᵢ.pub = Ed25519-GetPub(coinᵢ.priv)
h_denomᵢ = Hash-Denom(denomᵢ)
planchetᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub)
h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
@@ -503,7 +576,7 @@ planchets = (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩)
msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
| SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
-sig = EdDSA-Sign(reserve.priv, msg)
+sig = Ed25519-Sign(reserve.priv, msg)
~~~
~~~
@@ -517,7 +590,7 @@ for i in 0..n:
msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
| SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
-Check EdDSA-Verify(reserve.pub, msg, sig)
+Check Ed25519-Verify(reserve.pub, msg, sig)
Check reserve KYC status ok or not needed
total = Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
Check-Subtract(reserve.balance, total)
@@ -602,7 +675,7 @@ Persist order = (id, price, info, token?, wire_salt)
~~~
(W1) nonce generation (wallet)
-nonce = EdDSA-Keygen()
+nonce = Ed25519-Keygen()
Persist nonce.priv
~~~
@@ -622,7 +695,7 @@ contract.nonce = nonce.pub
Persist contract
h_contract = SHA-512(canonicalJSON(contract))
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
-sig = EdDSA-Sign(merchant.priv, msg)
+sig = Ed25519-Sign(merchant.priv, msg)
~~~
~~~
@@ -630,7 +703,7 @@ sig = EdDSA-Sign(merchant.priv, msg)
h_contract = SHA-512(canonicalJSON(contract))
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
-Check EdDSA-Verify(merchant.pub, msg, sig)
+Check Ed25519-Verify(merchant.pub, msg, sig)
Check contract.nonce == nonce
// TODO: double-check extra hash check?
// todo: maybe get rid of CoinSelection altogether by claiming we already know coinᵢ and contributionᵢ
@@ -643,7 +716,7 @@ for i in 0..n:
| contract.timestamp | contract.refund_deadline
| contributionᵢ + denomᵢ.fee_deposit
| denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
- sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ)
+ sigᵢ = Ed25519-Sign(coinᵢ.priv, msgᵢ)
depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ)
Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
~~~
@@ -658,14 +731,14 @@ Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
Check Sum ⟨depositᵢ.contribution⟩ == contract.price
Check Deposit(⟨depositᵢ⟩)
msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
-sig = EdDSA-Sign(merchant.priv, msg)
+sig = Ed25519-Sign(merchant.priv, msg)
~~~
~~~
(W3) payment verification (wallet)
msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
-Check EdDSA-Verify(merchant.pub, msg, sig)
+Check Ed25519-Verify(merchant.pub, msg, sig)
~~~
## Deposit {#deposit}
@@ -710,7 +783,7 @@ info.time = contract.{timestamp, wire_deadline, refund_deadline}
info.wire = (payto, wire_salt)
h_contract = SHA-512(canonicalJSON(contract))
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
-sig = EdDSA-Sign(merchant.priv, msg)
+sig = Ed25519-Sign(merchant.priv, msg)
~~~
~~~
@@ -728,7 +801,7 @@ for i in 0..n:
| info.time.timestamp | info.time.refund_deadline
| totalᵢ
| denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
- Check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
+ Check Ed25519-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
Check-Subtract(coinᵢ.value, total)
Persist deposit-record
@@ -740,7 +813,7 @@ msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
| info.time.refund_deadline
| Sum ⟨depositᵢ.contribution⟩
| SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
-sig = EdDSA-Sign(exchange.priv, msg)
+sig = Ed25519-Sign(exchange.priv, msg)
~~~
~~~
@@ -753,7 +826,7 @@ msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
| contract.refund_deadline
| Sum ⟨depositᵢ.contribution⟩
| SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
-Check EdDSA-Verify(exchange.pub, msg, sig)
+Check Ed25519-Verify(exchange.pub, msg, sig)
~~~
## Refresh {#refresh}
@@ -780,14 +853,16 @@ Refresh-Derive(shared, denom) =
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)
+ coin.pub = Ed25519-GetPub(coin.priv)
planchet = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denomᵢ.pub)
h_planchet = Hash-Planchet(planchet, denomᵢ)
return (coin, blind_secret, planchet, h_planchet)
~~~
Taler uses a cut-and-choose protocol with the fixed parameter `κ=3` to enforce correct derivation
-(in (κ-1)/κ, i.e. 2/3, of the cases, making income concealment for tax evasion purposes unpractical).
+of `⟨sharedₖᵢ⟩` from a single seed per batch of planchets `⟨batch_seedₖ⟩`
+(in (κ-1)/κ of the cases, making income concealment for tax evasion purposes unpractical).
+
Refreshing consists of two parts:
1. Melting of the old coin and commiting to κ batches of blinded planchet candidates
@@ -839,7 +914,7 @@ where (for RSA, without age-restriction)
// see TALER_EXCHANGE_get_melt_data
⟨batch_seedₖ⟩ // see TALER_refresh_expand_seed_to_kappa_batch_seeds
⟨transferₖᵢ.priv⟩ // see TALER_refresh_expand_batch_seed_to_transfer_data
-// todo: pretty sure ECDH-GetPub and EdDSA-GetPub are equivalent, if not, transferₖᵢ.pub needs to change
+// todo: pretty sure ECDH-GetPub and Ed25519-GetPub are equivalent, if not, transferₖᵢ.pub needs to change
h_planchetₖᵢ // see TALER_coin_ev_hash
h_planchetsₖ // see TALER_wallet_blinded_planchet_details_hash
commitment // see TALER_refresh_get_commitment
@@ -855,7 +930,7 @@ refresh_seed = random(256)
for k in 0..κ:
⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
for i in 0..n:
- transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv)
+ transferₖᵢ.pub = Ed25519-GetPub(transferₖᵢ.priv)
sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub)
(coinₖᵢ, blind_secretₖᵢ, planchetₖᵢ, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
@@ -868,7 +943,7 @@ planchets = (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩))
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
-sig = EdDSA-Sign(coin.priv, msg)
+sig = Ed25519-Sign(coin.priv, msg)
Persist (coin.denom.pub, ...) // todo: double-check
~~~
@@ -902,7 +977,7 @@ commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
-Check EdDSA-Verify(coin.pub, msg, sig)
+Check Ed25519-Verify(coin.pub, msg, sig)
refresh_record = Lookup by commitment
(ɣ, _, _, done, _) = refresh_record
if refresh_record not found:
@@ -913,7 +988,7 @@ if refresh_record not found:
Persist refresh_record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, false, link_info)
msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
( commitment | uint32(ɣ) ))
-sig = EdDSA-Sign(exchange.priv, msg)
+sig = Ed25519-Sign(exchange.priv, msg)
~~~
{::comment}
@@ -930,7 +1005,7 @@ sig = EdDSA-Sign(exchange.priv, msg)
Check exchange.pub known
msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
( commitment | ɣ ))
-Check EdDSA-Verify(exchange.pub, msg, sig)
+Check Ed25519-Verify(exchange.pub, msg, sig)
Persist refresh-challenge // what exactly?
for k in 0..κ and k != ɣ:
revealed_seedₖ = batch_seedₖ
@@ -952,7 +1027,7 @@ Check not done // todo: sure?
for k in 0..κ and k != ɣ:
⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
for i in 0..n:
- transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv)
+ transferₖᵢ.pub = Ed25519-GetPub(transferₖᵢ.priv)
sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub)
(_, _, _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
@@ -1019,7 +1094,7 @@ where (for RSA, without age-restriction)
(W1) history request (wallet)
msg = Gen-Msg(COIN_HISTORY_REQUEST, uint64(0x0))
-sig = EdDSA-Sign(coin.priv, msg)
+sig = Ed25519-Sign(coin.priv, msg)
~~~
{::comment}
@@ -1062,7 +1137,7 @@ commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
-Check EdDSA-Verify(coin.pub, msg, sig)
+Check Ed25519-Verify(coin.pub, msg, sig)
if ⟨blind_sigᵢ⟩ returned:
for i in 0..n:
diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml
@@ -31,13 +31,13 @@
<keyword>ecash</keyword>
<keyword>payments</keyword>
<abstract>
- <?line 43?>
+ <?line 44?>
<t>[ TBW ]</t>
</abstract>
</front>
<middle>
- <?line 47?>
+ <?line 48?>
<section anchor="introduction">
<name>Introduction</name>
@@ -94,6 +94,7 @@ Use at your own risk!</t>
</section>
<section anchor="cryptographic-primitives">
<name>Cryptographic Primitives</name>
+ <t>// todo: maybe change this description to something more similar to protocol functions (Julia-inspired syntax)</t>
<section anchor="cryptographic-hash-functions">
<name>Cryptographic Hash Functions</name>
<section anchor="sha256">
@@ -220,8 +221,70 @@ do until OKM < N:
<name>Non-Blind Signatures</name>
<section anchor="ed25519">
<name>Ed25519</name>
+ <t>Taler uses EdDSA instantiated with curve25519 as Ed25519,
+as defined in Section 5.1 of <xref target="RFC8032"/>.
+In particular, Taler does <em>not</em> make use of Ed25519ph or Ed25519ctx
+as defined in that document.</t>
+ <section anchor="key-generation">
+ <name>Key generation</name>
+ <artwork><![CDATA[
+Ed25519-GetPub(priv) -> pub
+
+Input:
+ priv private Ed25519 key
+
+Output:
+ pub public Ed25519 key
+]]></artwork>
+ <t><tt>pub</tt> is calculated as described in Section 5.1.5 of <xref target="RFC8032"/>.</t>
+ <artwork><![CDATA[
+Ed25519-Keygen() -> (priv, pub)
+
+Output:
+ priv private Ed25519 key
+ pub public Ed25519 key
+]]></artwork>
+ <t><tt>priv</tt> and <tt>pub</tt> are calculated as described in Section 5.1.5 of <xref target="RFC8032"/>,
+which is equivalent to the following:</t>
+ <artwork><![CDATA[
+priv = random(256)
+pub = Ed25519-GetPub(priv)
+]]></artwork>
+ </section>
+ <section anchor="signing">
+ <name>Signing</name>
+ <artwork><![CDATA[
+Ed25519-Sign(priv, msg) -> sig
+
+Inputs:
+ priv Ed25519 private key
+ msg message to be signed
+
+Output:
+ sig signature on the message by the given private key
+]]></artwork>
+ <t><tt>sig</tt> is calculated as described in Section 5.1.6 of <xref target="RFC8032"/>.</t>
+ </section>
+ <section anchor="verifying">
+ <name>Verifying</name>
+ <artwork><![CDATA[
+Ed25519-Verify(pub, msg, sig) -> out
+
+Inputs:
+ pub Ed25519 public key
+ msg signed message
+ sig signature on msg
+
+Output:
+ out true, if sig is a valid signature for msg
+]]></artwork>
+ <t><tt>out</tt> is the outcome of the last check of Section 5.1.7 of <xref target="RFC8032"/>.</t>
+ </section>
</section>
</section>
+ <section anchor="key-agreement">
+ <name>Key Agreement</name>
+ </section>
<section anchor="blind-signatures">
<name>Blind Signatures</name>
<section anchor="rsa-fdh">
@@ -285,7 +348,7 @@ r_e = r ** pubkey.e (mod pubkey.N)
out = r_e * data (mod pubkey.N)
]]></artwork>
</section>
- <section anchor="signing">
+ <section anchor="signing-1">
<name>Signing</name>
<artwork><![CDATA[
RSA-FDH-Sign(data, privkey) -> sig
@@ -322,7 +385,7 @@ r_inv = inverse of r (mod pubkey.N)
out = sig * r_inv (mod pubkey.N)
]]></artwork>
</section>
- <section anchor="verifying">
+ <section anchor="verifying-1">
<name>Verifying</name>
<artwork><![CDATA[
RSA-FDH-Verify(msg, sig, pubkey) -> out
@@ -394,8 +457,8 @@ out = uint32(len(msg)) | uint32(purpose) | msg
</section>
<section anchor="helper-functions">
<name>Helper Functions</name>
- <t>There are a certain number of functions which are often needed,
-and therefore omit the typical function syntax of using parentheses:</t>
+ <t>There are a certain number of single-argument functions which are often needed,
+and therefore omit the parentheses of the typical function syntax:</t>
<ul spacing="normal">
<li>
<t><tt>Knows data</tt> specifies <tt>data</tt> that is known a priori at the start of the protocol operation</t>
@@ -415,7 +478,7 @@ or aborts the protocol operation otherwise</t>
and denotes the numerical sum of these objects</t>
</li>
</ul>
- <t>Some more functions that are used throughout {#protocol}:</t>
+ <t>Some more functions that are commonly used throughout <xref target="protocol"/>:</t>
<artwork><![CDATA[
Hash-Denom(denom) =
SHA-512(uint32(0) | uint32(1) | denom.pub)
@@ -429,7 +492,7 @@ Check-Subtract(value, subtrahend) =
]]></artwork>
</section>
</section>
- <section anchor="procotol">
+ <section anchor="protocol">
<name>The Taler Crypto Protocol</name>
<t>// todo: briefly introduce the three components wallet, exchange, merchant; maybe with ASCII diagram version</t>
<t>// todo: capitalize wallet, exchange, merchant?</t>
@@ -478,7 +541,7 @@ Knows ⟨denomᵢ⟩ Knows ⟨denomᵢ.priv⟩
<artwork><![CDATA[
(W1) reserve key generation (wallet)
-reserve = EdDSA-Keygen()
+reserve = Ed25519-Keygen()
Persist (reserve, value)
]]></artwork>
<t>The wallet derives coins and blinding secrets using a HKDF from a single seed per withdrawal operation,
@@ -495,7 +558,7 @@ for i in 0..n:
coin_seedᵢ = HKDF(uint32(i), batch_seed, "taler-withdrawal-coin-derivation", 64)
blind_secretᵢ = coin_seedᵢ[32:]
coinᵢ.priv = coin_seedᵢ[:32]
- coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv)
+ coinᵢ.pub = Ed25519-GetPub(coinᵢ.priv)
h_denomᵢ = Hash-Denom(denomᵢ)
planchetᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub)
h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
@@ -503,7 +566,7 @@ planchets = (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩)
msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
| SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
-sig = EdDSA-Sign(reserve.priv, msg)
+sig = Ed25519-Sign(reserve.priv, msg)
]]></artwork>
<artwork><![CDATA[
(E1) coin issuance and signing (exchange)
@@ -516,7 +579,7 @@ for i in 0..n:
msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
| SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
-Check EdDSA-Verify(reserve.pub, msg, sig)
+Check Ed25519-Verify(reserve.pub, msg, sig)
Check reserve KYC status ok or not needed
total = Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
Check-Subtract(reserve.balance, total)
@@ -596,7 +659,7 @@ Persist order = (id, price, info, token?, wire_salt)
<artwork><![CDATA[
(W1) nonce generation (wallet)
-nonce = EdDSA-Keygen()
+nonce = Ed25519-Keygen()
Persist nonce.priv
]]></artwork>
<t>Note that the private key of <tt>nonce</tt> is currently not used anywhere in the protocol.
@@ -614,14 +677,14 @@ contract.nonce = nonce.pub
Persist contract
h_contract = SHA-512(canonicalJSON(contract))
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
-sig = EdDSA-Sign(merchant.priv, msg)
+sig = Ed25519-Sign(merchant.priv, msg)
]]></artwork>
<artwork><![CDATA[
(W2) payment preparation (wallet)
h_contract = SHA-512(canonicalJSON(contract))
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
-Check EdDSA-Verify(merchant.pub, msg, sig)
+Check Ed25519-Verify(merchant.pub, msg, sig)
Check contract.nonce == nonce
// TODO: double-check extra hash check?
// todo: maybe get rid of CoinSelection altogether by claiming we already know coinᵢ and contributionᵢ
@@ -634,7 +697,7 @@ for i in 0..n:
| contract.timestamp | contract.refund_deadline
| contributionᵢ + denomᵢ.fee_deposit
| denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
- sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ)
+ sigᵢ = Ed25519-Sign(coinᵢ.priv, msgᵢ)
depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ)
Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
]]></artwork>
@@ -646,13 +709,13 @@ Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
Check Sum ⟨depositᵢ.contribution⟩ == contract.price
Check Deposit(⟨depositᵢ⟩)
msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
-sig = EdDSA-Sign(merchant.priv, msg)
+sig = Ed25519-Sign(merchant.priv, msg)
]]></artwork>
<artwork><![CDATA[
(W3) payment verification (wallet)
msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
-Check EdDSA-Verify(merchant.pub, msg, sig)
+Check Ed25519-Verify(merchant.pub, msg, sig)
]]></artwork>
</section>
<section anchor="deposit">
@@ -693,7 +756,7 @@ info.time = contract.{timestamp, wire_deadline, refund_deadline}
info.wire = (payto, wire_salt)
h_contract = SHA-512(canonicalJSON(contract))
msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
-sig = EdDSA-Sign(merchant.priv, msg)
+sig = Ed25519-Sign(merchant.priv, msg)
]]></artwork>
<artwork><![CDATA[
(E1) Deposit validation (exchange)
@@ -710,7 +773,7 @@ for i in 0..n:
| info.time.timestamp | info.time.refund_deadline
| totalᵢ
| denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
- Check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
+ Check Ed25519-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
Check-Subtract(coinᵢ.value, total)
Persist deposit-record
@@ -722,7 +785,7 @@ msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
| info.time.refund_deadline
| Sum ⟨depositᵢ.contribution⟩
| SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
-sig = EdDSA-Sign(exchange.priv, msg)
+sig = Ed25519-Sign(exchange.priv, msg)
]]></artwork>
<artwork><![CDATA[
(M2) Deposit verification (merchant)
@@ -734,7 +797,7 @@ msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
| contract.refund_deadline
| Sum ⟨depositᵢ.contribution⟩
| SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
-Check EdDSA-Verify(exchange.pub, msg, sig)
+Check Ed25519-Verify(exchange.pub, msg, sig)
]]></artwork>
</section>
<section anchor="refresh">
@@ -762,14 +825,15 @@ Refresh-Derive(shared, denom) =
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)
+ coin.pub = Ed25519-GetPub(coin.priv)
planchet = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denomᵢ.pub)
h_planchet = Hash-Planchet(planchet, denomᵢ)
return (coin, blind_secret, planchet, h_planchet)
]]></artwork>
<t>Taler uses a cut-and-choose protocol with the fixed parameter <tt>κ=3</tt> to enforce correct derivation
-(in (κ-1)/κ, i.e. 2/3, of the cases, making income concealment for tax evasion purposes unpractical).
-Refreshing consists of two parts:</t>
+of <tt>⟨sharedₖᵢ⟩</tt> from a single seed per batch of planchets <tt>⟨batch_seedₖ⟩</tt>
+(in (κ-1)/κ of the cases, making income concealment for tax evasion purposes unpractical).</t>
+ <t>Refreshing consists of two parts:</t>
<ol spacing="normal" type="1"><li>
<t>Melting of the old coin and commiting to κ batches of blinded planchet candidates</t>
</li>
@@ -824,7 +888,7 @@ refresh_seed = random(256)
for k in 0..κ:
⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
for i in 0..n:
- transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv)
+ transferₖᵢ.pub = Ed25519-GetPub(transferₖᵢ.priv)
sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub)
(coinₖᵢ, blind_secretₖᵢ, planchetₖᵢ, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
@@ -837,7 +901,7 @@ planchets = (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩))
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
-sig = EdDSA-Sign(coin.priv, msg)
+sig = Ed25519-Sign(coin.priv, msg)
Persist (coin.denom.pub, ...) // todo: double-check
]]></artwork>
<artwork><![CDATA[
@@ -863,7 +927,7 @@ commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
-Check EdDSA-Verify(coin.pub, msg, sig)
+Check Ed25519-Verify(coin.pub, msg, sig)
refresh_record = Lookup by commitment
(ɣ, _, _, done, _) = refresh_record
if refresh_record not found:
@@ -874,7 +938,7 @@ if refresh_record not found:
Persist refresh_record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, false, link_info)
msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
( commitment | uint32(ɣ) ))
-sig = EdDSA-Sign(exchange.priv, msg)
+sig = Ed25519-Sign(exchange.priv, msg)
]]></artwork>
<artwork><![CDATA[
(W2) secret revelation (wallet)
@@ -882,7 +946,7 @@ sig = EdDSA-Sign(exchange.priv, msg)
Check exchange.pub known
msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
( commitment | ɣ ))
-Check EdDSA-Verify(exchange.pub, msg, sig)
+Check Ed25519-Verify(exchange.pub, msg, sig)
Persist refresh-challenge // what exactly?
for k in 0..κ and k != ɣ:
revealed_seedₖ = batch_seedₖ
@@ -896,7 +960,7 @@ Check not done // todo: sure?
for k in 0..κ and k != ɣ:
⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
for i in 0..n:
- transferₖᵢ.pub = EdDSA-GetPub(transferₖᵢ.priv)
+ transferₖᵢ.pub = Ed25519-GetPub(transferₖᵢ.priv)
sharedₖᵢ = ECDH-EdDSA(transferₖᵢ.priv, coin.pub)
(_, _, _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
@@ -945,7 +1009,7 @@ Knows coin Knows refresh_record for coin
(W1) history request (wallet)
msg = Gen-Msg(COIN_HISTORY_REQUEST, uint64(0x0))
-sig = EdDSA-Sign(coin.priv, msg)
+sig = Ed25519-Sign(coin.priv, msg)
]]></artwork>
<artwork><![CDATA[
(E1) refresh secret lookup (exchange)
@@ -976,7 +1040,7 @@ commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
msg = Gen-Msg(WALLET_COIN_MELT,
( commitment | coin.h_denom | uint256(0x0)
| value | denom.fee_refresh ))
-Check EdDSA-Verify(coin.pub, msg, sig)
+Check Ed25519-Verify(coin.pub, msg, sig)
if ⟨blind_sigᵢ⟩ returned:
for i in 0..n:
@@ -1066,6 +1130,19 @@ if ⟨blind_sigᵢ⟩ returned:
<seriesInfo name="RFC" value="6234"/>
<seriesInfo name="DOI" value="10.17487/RFC6234"/>
</reference>
+ <reference anchor="RFC8032">
+ <front>
+ <title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title>
+ <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
+ <author fullname="I. Liusvaara" initials="I." surname="Liusvaara"/>
+ <date month="January" year="2017"/>
+ <abstract>
+ <t>This document describes elliptic curve signature scheme Edwards-curve Digital Signature Algorithm (EdDSA). The algorithm is instantiated with recommended parameters for the edwards25519 and edwards448 curves. An example implementation and test vectors are provided.</t>
+ </abstract>
+ </front>
+ <seriesInfo name="RFC" value="8032"/>
+ <seriesInfo name="DOI" value="10.17487/RFC8032"/>
+ </reference>
<reference anchor="HKDF">
<front>
<title>Cryptographic Extraction and Key Derivation: The HKDF Scheme</title>
@@ -1091,7 +1168,7 @@ if ⟨blind_sigᵢ⟩ returned:
<refcontent>National Institute of Standards and Technology (U.S.)</refcontent>
</reference>
</references>
- <?line 1099?>
+ <?line 1174?>
<section anchor="change-log">
<name>Change log</name>
@@ -1104,214 +1181,222 @@ Education and Research (BMBF) within the project Concrete Contracts.</t>
</section>
</back>
<!-- ##markdown-source:
-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
-kQAA
+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
+cE3IWxI/UIY+6zj/Bxn0s+G/lwAA
-->