lsd0009

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

draft-guetschow-taler-protocol.md (49268B)


      1 ---
      2 v: 3
      3 
      4 title: "The GNU Taler Protocol"
      5 docname: draft-guetschow-taler-protocol
      6 category: info
      7 
      8 ipr: trust200902
      9 workgroup: independent
     10 stream: independent
     11 keyword:
     12   - taler
     13   - cryptography
     14   - ecash
     15   - payments
     16 
     17 #venue:
     18 #  repo: https://git.gnunet.org/lsd0009.git/
     19 #  latest: https://lsd.gnunet.org/lsd0009/
     20 
     21 author:
     22  -
     23     name: Mikolai Gütschow
     24     org: TUD Dresden University of Technology
     25     abbrev: TU Dresden
     26     street: Helmholtzstr. 10
     27     city: Dresden
     28     code: D-01069
     29     country: Germany
     30     email: mikolai.guetschow@tu-dresden.de
     31 
     32 normative:
     33   RFC20:
     34   RFC2104:
     35   RFC5869:
     36   RFC6234:
     37   RFC7748:
     38   RFC8032:
     39   HKDF: DOI.10.1007/978-3-642-14623-7_34
     40   SHS: DOI.10.6028/NIST.FIPS.180-4
     41 
     42 informative:
     43 
     44 
     45 --- abstract
     46 
     47 \[ TBW \]
     48 
     49 --- middle
     50 
     51 # Introduction
     52 
     53 \[ TBW \]
     54 
     55 Beware that this document is still work-in-progress and may contain errors.
     56 Use at your own risk!
     57 
     58 # Notation
     59 
     60 - `"abc"` denotes the literal string `abc` encoded as ASCII [RFC20]
     61 - `a | b` denotes the concatenation of a with b
     62 - `len(a)` denotes the length in bytes of the byte string a
     63 - `padZero(y, a)` denotes the byte string a, zero-padded to the length of y bytes
     64 - `bits(x)`/`bytes(x)` denotes the minimal number of bits/bytes necessary to represent the multiple precision integer x
     65 - `uint(y, x)` denotes the `y` least significant bits of the integer `x`, zero-padded and encoded in network byte order (big endian)
     66 - `uintY(x)` where `Y` is a positive integer number is equivalent to `uint(Y, x)`
     67 - `random(y)` denotes a randomly generated sequence of y bits
     68 - `a * b (mod N)` / `a ** b (mod N)` denotes the multiplication / exponentiation of multiple precision integers a and b, modulo N
     69 - `for`, `if`, variable assignment `=`, and conditional operators are to be interpreted like their Python/Julia equivalents
     70 - `data.key` denotes the property `key` on the object `data`
     71 - `0..n` denotes the exclusive range of integer numbers from `0` to `n`, i.e., `0, 1, 2, ..., n-1`
     72 - `⟨dataᵢ⟩` within a context of `i = 0..n` denotes `n` objects `dataᵢ`, represented in memory as a continuous array
     73 - `⟨dataᵢ.key⟩` within a context of `i = 0..n` denotes an array of the `n` properties `key` of all `n` objects `dataᵢ`
     74 
     75 # Cryptographic Primitives
     76 
     77 // todo: maybe change this description to something more similar to protocol functions (Julia-inspired syntax)
     78 
     79 ## Cryptographic Hash Functions
     80 
     81 ### SHA-256 {#sha256}
     82 
     83 ~~~
     84 SHA-256(msg) -> hash
     85 
     86 Input:
     87     msg     input message of length L < 2^61 octets
     88 
     89 Output:
     90     hash    message digest of fixed length HashLen = 32 octets
     91 ~~~
     92 
     93 `hash` is the output of SHA-256 as per Sections 4.1, 5.1, 6.1, and 6.2 of [RFC6234].
     94 
     95 ### SHA-512 {#sha512}
     96 
     97 ~~~
     98 SHA-512(msg) -> hash
     99 
    100 Input:
    101     msg     input message of length L < 2^125 octets
    102 
    103 Output:
    104     hash    message digest of fixed length HashLen = 64 octets
    105 ~~~
    106 
    107 `hash` is the output of SHA-512 as per Sections 4.2, 5.2, 6.3, and 6.4 of [RFC6234].
    108 
    109 ### SHA-512-256 (truncated SHA-512) {#sha512-trunc}
    110 
    111 ~~~
    112 SHA-512-256(msg) -> hash
    113 
    114 Input:
    115     msg     input message of length L < 2^125 octets
    116 
    117 Output:
    118     hash    message digest of fixed length HashLen = 32 octets
    119 ~~~
    120 
    121 The output `hash` corresponds to the first 32 octets of the output of SHA-512 defined in {{sha512}}:
    122 
    123 ~~~
    124 temp = SHA-512(msg)
    125 hash = temp[0:31]
    126 ~~~
    127 
    128 Note that this operation differs from SHA-512/256 as defined in [SHS] in the initial hash value.
    129 
    130 
    131 ## Message Authentication Codes
    132 
    133 ### HMAC {#hmac}
    134 
    135 ~~~
    136 HMAC-Hash(key, text) -> out
    137 
    138 Option:
    139     Hash    cryptographic hash function with output length HashLen
    140 
    141 Input:
    142     key     secret key of length at least HashLen
    143     text    input data of arbitary length
    144 
    145 Output:
    146     out     output of length HashLen
    147 ~~~
    148 
    149 `out` is calculated as defined in [RFC2104].
    150 
    151 
    152 ## Key Derivation Functions
    153 
    154 ### HKDF {#hkdf}
    155 
    156 The Hashed Key Derivation Function (HKDF) used in Taler is an instantiation of [RFC5869]
    157 with two different hash functions for the Extract and Expand step as suggested in [HKDF]:
    158 `HKDF-Extract` uses `HMAC-SHA512`, while `HKDF-Expand` uses `HMAC-SHA256` (cf. {{hmac}}).
    159 
    160 ~~~
    161 HKDF(salt, IKM, info, L) -> OKM
    162 
    163 Inputs:
    164     salt    optional salt value (a non-secret random value);
    165               if not provided, it is set to a string of 64 zeros.
    166     IKM     input keying material
    167     info    optional context and application specific information
    168               (can be a zero-length string)
    169     L       length of output keying material in octets
    170               (<= 255*32 = 8160)
    171 
    172 Output:
    173     OKM      output keying material (of L octets)
    174 ~~~
    175 
    176 The output OKM is calculated as follows:
    177 
    178 ~~~
    179 PRK = HKDF-Extract(salt, IKM) with Hash = SHA-512 (HashLen = 64)
    180 OKM = HKDF-Expand(PRK, info, L) with Hash = SHA-256 (HashLen = 32)
    181 ~~~
    182 
    183 ### HKDF-Mod
    184 
    185 Based on the HKDF defined in {{hkdf}}, this function returns an OKM that is smaller than a given multiple precision integer N.
    186 
    187 ~~~
    188 HKDF-Mod(N, salt, IKM, info) -> OKM
    189 
    190 Inputs:
    191     N        multiple precision integer
    192     salt     optional salt value (a non-secret random value);
    193               if not provided, it is set to a string of 64 zeros.
    194     IKM      input keying material
    195     info     optional context and application specific information
    196               (can be a zero-length string)
    197 
    198 Output:
    199     OKM      output keying material (smaller than N)
    200 ~~~
    201 
    202 The final output `OKM` is determined deterministically based on a counter initialized at zero.
    203 
    204 ~~~
    205 counter = 0
    206 do until OKM < N:
    207     x = HKDF(salt, IKM, info | uint16(counter), bytes(N))
    208     OKM = uint(bits(N), x)
    209     counter += 1
    210 ~~~
    211 
    212 ## Non-Blind Signatures
    213 
    214 ### Ed25519
    215 
    216 Taler uses EdDSA instantiated with curve25519 as Ed25519,
    217 as defined in Section 5.1 of [RFC8032].
    218 In particular, Taler does _not_ make use of Ed25519ph or Ed25519ctx
    219 as defined in that document.
    220 
    221 #### Key generation
    222 
    223 ~~~
    224 Ed25519-GetPub(priv) -> pub
    225 
    226 Input:
    227     priv    private Ed25519 key
    228 
    229 Output:
    230     pub     public Ed25519 key
    231 ~~~
    232 
    233 `pub` is calculated as described in Section 5.1.5 of [RFC8032].
    234 
    235 ~~~
    236 Ed25519-Keygen() -> (priv, pub)
    237 
    238 Output:
    239     priv    private Ed25519 key
    240     pub     public Ed25519 key
    241 ~~~
    242 
    243 `priv` and `pub` are calculated as described in Section 5.1.5 of [RFC8032],
    244 which is equivalent to the following:
    245 
    246 ~~~
    247 priv = random(256)
    248 pub = Ed25519-GetPub(priv)
    249 ~~~
    250 
    251 #### Signing
    252 
    253 ~~~
    254 Ed25519-Sign(priv, msg) -> sig
    255 
    256 Inputs:
    257     priv    Ed25519 private key
    258     msg     message to be signed
    259 
    260 Output:
    261     sig     signature on the message by the given private key
    262 ~~~
    263 
    264 `sig` is calculated as described in Section 5.1.6 of [RFC8032].
    265 
    266 #### Verifying
    267 
    268 ~~~
    269 Ed25519-Verify(pub, msg, sig) -> out
    270 
    271 Inputs:
    272     pub     Ed25519 public key
    273     msg     signed message
    274     sig     signature on msg
    275 
    276 Output:
    277     out     true, if sig is a valid signature for msg
    278 ~~~
    279 
    280 `out` is the outcome of the last check of Section 5.1.7 of [RFC8032].
    281 
    282 ## Key Agreement
    283 
    284 ### X25519
    285 
    286 Taler uses Elliptic Curve Diffie-Hellman (ECDH) on curve25519 as defined in Section 6.1 of [RFC7748],
    287 but reuses Ed25519 keypairs for one side of the agreement instead of random bytes.
    288 Depending on whether the private or public part is from Ed25519, two different functions are used.
    289 
    290 {::comment}
    291 see https://libsodium.gitbook.io/doc/advanced/scalar_multiplication
    292 see https://libsodium.gitbook.io/doc/advanced/ed25519-curve25519
    293 {:/}
    294 
    295 ~~~
    296 ECDH-Ed25519-Priv(priv, pub) -> shared
    297 
    298 Input:
    299     priv    private Ed25519 key
    300     pub     public X25519 key
    301 
    302 Output:
    303     shared  shared secret based on the given keys
    304 ~~~
    305 
    306 `shared` is calculated as follows, using the function X25519 defined in Section 5 of [RFC7748]:
    307 
    308 ~~~
    309 priv' = SHA-512-256(priv)
    310 // todo: missing bit clamping from https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c#L71
    311 shared' = X25519(priv', pub)
    312 shared = SHA-512(shared')
    313 ~~~
    314 
    315 {::comment}
    316 see GNUNET_CRYPTO_eddsa_ecdh
    317 {:/}
    318 
    319 ~~~
    320 ECDH-Ed25519-Pub(priv, pub) -> shared
    321 
    322 Input:
    323     priv    private X25519 key
    324     pub     public Ed25519 key
    325 
    326 Output:
    327     shared  shared secret based on the given keys
    328 ~~~
    329 
    330 `shared` is calculated as follows, using the function X25519 defined in Section 5 of [RFC7748],
    331 and `Convert-Point-Ed25519-Curve25519(p)` which implements the birational map of Section 4.1 of [RFC7748]:
    332 
    333 ~~~
    334 pub' = Convert-Point-Ed25519-Curve25519(pub)
    335 shared' = X25519(priv, pub')
    336 shared = SHA-512(shared')
    337 ~~~
    338 
    339 {::comment}
    340 see GNUNET_CRYPTO_eddsa_ecdh
    341 {:/}
    342 
    343 ~~~
    344 ECDH-GetPub(priv) -> pub
    345 
    346 Input:
    347     priv    private X25519 key
    348 
    349 Output:
    350     pub     public X25519 key
    351 ~~~
    352 
    353 `pub` is calculated according to Section 6.1 of [RFC7748]:
    354 
    355 ~~~
    356 pub = X25519(priv, 9)
    357 ~~~
    358 
    359 {::comment}
    360 see GNUNET_CRYPTO_ecdhe_key_get_public
    361 {:/}
    362 
    363 ## Blind Signatures
    364 
    365 ### RSA-FDH {#rsa-fdh}
    366 
    367 #### Supporting Functions
    368 
    369 ~~~
    370 RSA-FDH(msg, pubkey) -> fdh
    371 
    372 Inputs:
    373     msg     message
    374     pubkey  RSA public key consisting of modulus N and public exponent e
    375 
    376 Output:
    377     fdh     full-domain hash of msg over pubkey.N
    378 ~~~
    379 
    380 `fdh` is calculated based on HKDF-Mod from {{hkdf-mod}} as follows:
    381 
    382 ~~~
    383 info = "RSA-FDA FTpsW!"
    384 salt = uint16(bytes(pubkey.N)) | uint16(bytes(pubkey.e))
    385      | pubkey.N | pubkey.e
    386 fdh = HKDF-Mod(pubkey.N, salt, msg, info)
    387 ~~~
    388 
    389 The resulting `fdh` can be used to test against a malicious RSA pubkey
    390 by verifying that the greatest common denominator (gcd) of `fdh` and `pubkey.N` is 1.
    391 
    392 ~~~
    393 RSA-FDH-Derive(bks, pubkey) -> out
    394 
    395 Inputs:
    396     bks     blinding key secret of length L = 32 octets
    397     pubkey  RSA public key consisting of modulus N and public exponent e
    398 
    399 Output:
    400     out     full-domain hash of bks over pubkey.N
    401 ~~~
    402 
    403 `out` is calculated based on HKDF-Mod from {{hkdf-mod}} as follows:
    404 
    405 ~~~
    406 info = "Blinding KDF"
    407 salt = "Blinding KDF extractor HMAC key"
    408 fdh = HKDF-Mod(pubkey.N, salt, bks, info)
    409 ~~~
    410 
    411 #### Blinding
    412 
    413 ~~~
    414 RSA-FDH-Blind(msg, bks, pubkey) -> out
    415 
    416 Inputs:
    417     msg     message
    418     bks     blinding key secret of length L = 32 octets
    419     pubkey  RSA public key consisting of modulus N and public exponent e
    420 
    421 Output:
    422     out     message blinded for pubkey
    423 ~~~
    424 
    425 `out` is calculated based on RSA-FDH from {{rsa-fdh}} as follows:
    426 
    427 ~~~
    428 data = RSA-FDH(msg, pubkey)
    429 r = RSA-FDH-Derive(bks, pubkey)
    430 r_e = r ** pubkey.e (mod pubkey.N)
    431 out = r_e * data (mod pubkey.N)
    432 ~~~
    433 
    434 #### Signing
    435 
    436 ~~~
    437 RSA-FDH-Sign(data, privkey) -> sig
    438 
    439 Inputs:
    440     data    data to be signed, an integer smaller than privkey.N
    441     privkey RSA private key consisting of modulus N and private exponent d
    442 
    443 Output:
    444     sig     signature on data by privkey
    445 ~~~
    446 
    447 `sig` is calculated as follows:
    448 
    449 ~~~
    450 sig = data ** privkey.d (mod privkey.N)
    451 ~~~
    452 
    453 #### Unblinding
    454 
    455 ~~~
    456 RSA-FDH-Unblind(sig, bks, pubkey) -> out
    457 
    458 Inputs:
    459     sig     blind signature
    460     bks     blinding key secret of length L = 32 octets
    461     pubkey  RSA public key consisting of modulus N and public exponent e
    462 
    463 Output:
    464     out     unblinded signature
    465 ~~~
    466 
    467 `out` is calculated as follows:
    468 
    469 ~~~
    470 r = RSA-FDH-Derive(bks, pubkey)
    471 r_inv = inverse of r (mod pubkey.N)
    472 out = sig * r_inv (mod pubkey.N)
    473 ~~~
    474 
    475 #### Verifying
    476 
    477 ~~~
    478 RSA-FDH-Verify(msg, sig, pubkey) -> out
    479 
    480 Inputs:
    481     msg     message
    482     sig     signature of pubkey over msg
    483     pubkey  RSA public key consisting of modulus N and public exponent e
    484 
    485 Output:
    486     out     true, if sig is a valid signature
    487 ~~~
    488 
    489 `out` is calculated based on RSA-FDH from {{rsa-fdh}} as follows:
    490 
    491 ~~~
    492 data = RSA-FDH(msg, pubkey)
    493 exp = sig ** pubkey.e (mod pubkey.N)
    494 out = (data == exp)
    495 ~~~
    496 
    497 ### Clause-Schnorr
    498 
    499 # Datatypes and Notation
    500 
    501 ## Amounts {#amounts}
    502 
    503 Amounts are represented in Taler as positive fixed-point values
    504 consisting of `value` as the non-negative integer part of the base currency,
    505 the `fraction` given in units of one hundred millionth (1e-8) of the base currency,
    506 and `currency` as the 3-11 ASCII characters identifying the currency.
    507 
    508 Whenever used in the protocol, the binary representation of an `amount` is
    509 `uint64(amount.value) | uint32(amount.fraction) | padZero(12, amount.currency)`.
    510 
    511 ## Timestamps
    512 
    513 Absolute timestamps are represented as `uint64(x)` where `x` corresponds to
    514 the microseconds since `1970-01-01 00:00 CEST` (the UNIX epoch).
    515 The special value `0xFFFFFFFFFFFFFFFF` represents "never".
    516 <!--
    517 // todo: check if needed and correct
    518 Relative timestamps are represented as `uint64(x)` where `x` is given in microseconds.
    519 The special value `0xFFFFFFFFFFFFFFFF` represents "forever".
    520 -->
    521 
    522 ## Signatures
    523 
    524 All messages to be signed in Taler start with a header containing their size and
    525 a fixed signing context (purpose) as registered by GANA in the
    526 [GNUnet Signature Purposes](https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html)
    527 registry. Taler-related purposes start at 1000.
    528 
    529 ~~~
    530 Gen-Msg(purpose, msg) -> out
    531 
    532 Inputs:
    533     purpose signature purpose as registered at GANA
    534     msg     message content (excl. header) to be signed
    535 
    536 Output:
    537     out     complete message (incl. header) to be signed
    538 ~~~
    539 
    540 `out` is formed as follows:
    541 
    542 ~~~
    543 out = uint32(len(msg)) | uint32(purpose) | msg
    544 ~~~
    545 
    546 ## Helper Functions
    547 
    548 There are a certain number of single-argument functions which are often needed,
    549 and therefore omit the parentheses of the typical function syntax:
    550 
    551 - `Knows data` specifies `data` that is known a priori at the start of the protocol operation
    552 - `Check cond` verifies that the boolean condition or variable `cond` is true,
    553   or aborts the protocol operation otherwise
    554 - `Persist data` persists the given `data` to the local database
    555 - `data = Lookup by key` retrieves previously persisted `data` by the given `key`
    556 - `Sum ⟨dataᵢ⟩` is valid for numerical objects `dataᵢ` including amounts (cf. {{amounts}}),
    557   and denotes the numerical sum of these objects
    558 
    559 Some more functions that are commonly used throughout {{protocol}}:
    560 
    561 ~~~
    562 Hash-Denom(denom) =
    563   SHA-512(uint32(0) | uint32(1) | denom.pub)
    564 
    565 Hash-Planchet(planchet, denom) =
    566   SHA-512( SHA-512( denom.pub ) | uint32(0x1) | planchet )
    567 
    568 Check-Subtract(value, subtrahend) =
    569   Check value >= subtrahend
    570   Persist value -= subtrahend
    571 ~~~
    572 
    573 # The Taler Crypto Protocol {#protocol}
    574 
    575 // todo: briefly introduce the three components wallet, exchange, merchant; maybe with ASCII diagram version
    576 
    577 // todo: capitalize wallet, exchange, merchant?
    578 
    579 ## Obtaining E-Cash
    580 
    581 ### Withdrawal {#withdraw}
    582 
    583 The wallet generates `n > 0` coins `⟨coinᵢ⟩` and requests `n` signatures `⟨blind_sigᵢ⟩` from the exchange,
    584 attributing value to the coins according to `n` chosen denominations `⟨denomᵢ⟩`.
    585 The total value and withdrawal fee (defined by the exchange per denomination)
    586 must be smaller or equal to the amount stored in the single reserve used for withdrawal.
    587 
    588 // todo: document TALER_MAX_COINS = 64 per operation (due to CS-encoding)
    589 
    590 // todo: extend with extra roundtrip for CBS
    591 
    592 ~~~
    593             wallet                                  exchange
    594 Knows ⟨denomᵢ⟩                          Knows ⟨denomᵢ.priv⟩
    595                |                                        |
    596 +-----------------------------+                         |
    597 | (W1) reserve key generation |                         |
    598 +-----------------------------+                         |
    599                |                                        |
    600                |----------- (bank transfer) ----------->|
    601                | (subject: reserve.pub, amount: value)  |
    602                |                                        |
    603                |                      +------------------------------+
    604                |                      | Persist (reserve.pub, value) |
    605                |                      +------------------------------+
    606                |                                        |
    607 +-----------------------------------+                   |
    608 | (W2) coin generation and blinding |                   |
    609 +-----------------------------------+                   |
    610                |                                        |
    611                |-------------- /withdraw -------------->|
    612                |    (reserve.pub, planchets, sig)       |
    613                |                                        |
    614                |                      +--------------------------------+
    615                |                      | (E1) coin issuance and signing |
    616                |                      +--------------------------------+
    617                |                                        |
    618                |<---------- (⟨blind_sigᵢ⟩) -------------|
    619                |                                        |
    620 +----------------------+                                |
    621 | (W3) coin unblinding |                                |
    622 +----------------------+                                |
    623                |                                        |
    624 ~~~
    625 
    626 where (for RSA, without age-restriction)
    627 
    628 ~~~
    629 (W1) reserve key generation (wallet)
    630 
    631 reserve = Ed25519-Keygen()
    632 Persist (reserve, value)
    633 ~~~
    634 
    635 The wallet derives coins and blinding secrets using a HKDF from a single seed per withdrawal operation,
    636 together with an integer index.
    637 This is strictly speaking an implementation detail since the seed is never revealed to any other party,
    638 and might be chosen to be implemented differently.
    639 
    640 // todo: blind_secret/coin.priv differently generated in TALER_EXCHANGE_post_withdraw_start/prepare_coins, double check with wallet-core (probably implementation detail here)
    641 
    642 ~~~
    643 (W2) coin generation and blinding (wallet)
    644 
    645 batch_seed = random(256)
    646 Persist batch_seed
    647 for i in 0..n:
    648   coin_seedᵢ = HKDF(uint32(i), batch_seed, "taler-withdrawal-coin-derivation", 64)
    649   blind_secretᵢ = coin_seedᵢ[32:]
    650   coinᵢ.priv = coin_seedᵢ[:32]
    651   coinᵢ.pub = Ed25519-GetPub(coinᵢ.priv)
    652   h_denomᵢ = Hash-Denom(denomᵢ)
    653   planchetᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub)
    654   h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
    655 planchets = (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩)
    656 msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
    657     ( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
    658     | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
    659 sig = Ed25519-Sign(reserve.priv, msg)
    660 ~~~
    661 
    662 ~~~
    663 (E1) coin issuance and signing (exchange)
    664 
    665 (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩) = planchets
    666 for i in 0..n:
    667   denomᵢ = Lookup by h_denomᵢ
    668   Check denomᵢ known and not withdraw-expired
    669   h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
    670 msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
    671     ( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
    672     | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
    673 Check Ed25519-Verify(reserve.pub, msg, sig)
    674 Check reserve KYC status ok or not needed
    675 total = Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
    676 Check-Subtract(reserve.balance, total)
    677 for i in 0..n:
    678   blind_sigᵢ = RSA-FDH-Sign(planchetᵢ, denomᵢ.priv)
    679 Persist withdrawal // todo: what exactly? should be checked first for replay?
    680 ~~~
    681 
    682 ~~~
    683 (W3) coin unblinding (wallet)
    684 
    685 for i in 0..n:
    686   coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub)
    687   Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
    688   coinᵢ.h_denom = h_denomᵢ
    689   coinᵢ.blind_secret = blind_secretᵢ  // todo: why save blind_secret, if batch_seed already persisted?
    690 Persist ⟨coinᵢ⟩
    691 ~~~
    692 
    693 ### Recoup {#withdraw-recoup}
    694 
    695 // todo
    696 
    697 ## Payment with E-Cash
    698 
    699 ### Payment and Deposit {#payment}
    700 
    701 The wallet obtains `contract` information for an `order` from the merchant
    702 after claiming it with a `nonce`.
    703 Payment of the order is prepared by signing (partial) deposit authorizations `⟨depositᵢ⟩` with coins `⟨coinᵢ⟩` of certain denominations `⟨denomᵢ⟩`,
    704 where the sum of all contributions (`contributionᵢ <= denomᵢ.value`) must match the `contract.price` plus potential deposit fees.
    705 The payment is complete as soon as the merchant successfully redeems the deposit authorizations at the exchange.
    706 
    707 Deposit could also be used directly by a wallet with its own payto and a minimal contract.
    708 
    709 ~~~
    710       wallet                        merchant                       exchange
    711 Knows ⟨coinᵢ⟩                  Knows merchant.priv         Knows exchange.priv
    712         |                      Knows exchange, payto       Knows ⟨denomᵢ⟩
    713         |                              |                              |
    714         |                 +-----------------------+                   |
    715         |                 | (M1) order generation |                   |
    716         |                 +-----------------------+                   |
    717         |                              |                              |
    718         |<--- (QR-Code / NFC / URI) ---|                              |
    719         |      (order.{id,token?})     |                              |
    720         |                              |                              |
    721 +-----------------------+              |                              |
    722 | (W1) nonce generation |              |                              |
    723 +-----------------------+              |                              |
    724         |                              |                              |
    725         |-- /orders/{order.id}/claim ->|                              |
    726         |  (nonce.pub, order.token?)   |                              |
    727         |                              |                              |
    728         |                 +--------------------------+                |
    729         |                 | (M2) contract generation |                |
    730         |                 +--------------------------+                |
    731         |                              |                              |
    732         |<-- (contract, merchant.pub, -|                              |
    733         |            sig)              |                              |
    734         |                              |                              |
    735 +--------------------------+           |                              |
    736 | (W2) payment preparation |           |                              |
    737 +--------------------------+           |                              |
    738         |                              |                              |
    739         |--- /orders/{order.id}/pay -->|                              |
    740         |         (⟨depositᵢ⟩)         |                              |
    741         |                              |                              |
    742         |                 +--------------------------+                |
    743         |                 | (M3) deposit preparation |                |
    744         |                 +--------------------------+                |
    745         |                              |                              |
    746         |                              |-------- /batch-deposit ----->|
    747         |                              | (info, h_contract, ⟨depositᵢ⟩|
    748         |                              |        merchant.pub, sig)    |
    749         |                              |                              |
    750         |                              |                  +--------------------+
    751         |                              |                  | (E1) deposit check |
    752         |                              |                  +--------------------+
    753         |                              |                              |
    754         |                              |<- (timestamp, exchange.pub, -|
    755         |                              |        sig)                  |
    756         |                              |                              |
    757         |                 +---------------------------+               |
    758         |                 | (M4) deposit verification |               |
    759         |                 +---------------------------+               |
    760         |                              |                              |
    761         |<----------- (sig) -----------|                              |
    762         |                              |                              |
    763 +---------------------------+          |                              |
    764 | (W3) payment verification |          |                              |
    765 +---------------------------+          |                              |
    766         |                              |                              |
    767 ~~~
    768 
    769 where (without age restriction, policy and wallet data hash)
    770 
    771 ~~~
    772 (M1) order generation (merchant)
    773 
    774 wire_salt = random(128)
    775 determine id, price, info, token?
    776 Persist order = (id, price, info, token?, wire_salt)
    777 ~~~
    778 
    779 ~~~
    780 (W1) nonce generation (wallet)
    781 
    782 nonce = Ed25519-Keygen()
    783 Persist nonce.priv
    784 ~~~
    785 
    786 Note that the private key of `nonce` is currently not used anywhere in the protocol.
    787 However, it could be used in the future to prove ownership of an order transaction,
    788 enabling use-cases such as "unclaiming" or transferring an order to another person,
    789 or proving the payment without resorting to the individual coins.
    790 
    791 ~~~
    792 (M2) contract generation (merchant)
    793 
    794 Check order.token? == token?
    795 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
    796 determine timestamp, refund_deadline, wire_deadline
    797 contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline)
    798 contract.nonce = nonce.pub
    799 Persist contract
    800 h_contract = SHA-512(canonicalJSON(contract))
    801 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
    802 sig = Ed25519-Sign(merchant.priv, msg)
    803 ~~~
    804 
    805 ~~~
    806 (W2) payment preparation (wallet)
    807 
    808 h_contract = SHA-512(canonicalJSON(contract))
    809 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
    810 Check Ed25519-Verify(merchant.pub, msg, sig)
    811 Check contract.nonce == nonce
    812 // TODO: double-check extra hash check?
    813 // todo: maybe get rid of CoinSelection altogether by claiming we already know coinᵢ and contributionᵢ
    814 ⟨selectionᵢ⟩ = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
    815 for i in 0..n:
    816   (coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ
    817   msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT,
    818       ( h_contract | uint256(0x0)
    819       | uint512(0x0) | contract.h_wire | coinᵢ.h_denom
    820       | contract.timestamp | contract.refund_deadline
    821       | contributionᵢ + denomᵢ.fee_deposit
    822       | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
    823   sigᵢ = Ed25519-Sign(coinᵢ.priv, msgᵢ)
    824   depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ)
    825 Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
    826 ~~~
    827 
    828 // TODO: explain CoinSelection
    829 
    830 ~~~
    831 (M3) deposit preparation (merchant)
    832 
    833 Check Sum ⟨depositᵢ.contribution⟩ == contract.price
    834 info.time = contract.{timestamp, wire_deadline, refund_deadline}
    835 info.wire = (payto, wire_salt)
    836 h_contract = SHA-512(canonicalJSON(contract))
    837 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
    838 sig = Ed25519-Sign(merchant.priv, msg)
    839 ~~~
    840 
    841 ~~~
    842 (E1) deposit check (exchange)
    843 
    844 h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64)
    845 for i in 0..n:
    846   coinᵢ = depositᵢ.coin
    847   denomᵢ = Lookup by coinᵢ.h_denom
    848   Check denomᵢ known and not deposit-expired
    849   totalᵢ = depositᵢ.contribution + denomᵢ.fee_deposit
    850   msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT,
    851       ( h_contract | uint256(0x0)
    852       | uint512(0x0) | h_wire | coinᵢ.h_denom
    853       | info.time.timestamp | info.time.refund_deadline
    854       | totalᵢ
    855       | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
    856   Check Ed25519-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
    857   Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
    858   Check-Subtract(coinᵢ.value, total)
    859 Persist deposit-record
    860 schedule bank transfer to payto
    861 timestamp = now()
    862 msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
    863     ( h_contract | h_wire | uint512(0x0)
    864     | timestamp | info.time.wire_deadline
    865     | info.time.refund_deadline
    866     | Sum ⟨depositᵢ.contribution⟩
    867     | SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
    868 sig = Ed25519-Sign(exchange.priv, msg)
    869 ~~~
    870 
    871 ~~~
    872 (M2) deposit verification (merchant)
    873 
    874 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
    875 msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
    876     ( h_contract | h_wire | uint512(0x0)
    877     | timestamp | contract.wire_deadline
    878     | contract.refund_deadline
    879     | Sum ⟨depositᵢ.contribution⟩
    880     | SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
    881 Check Ed25519-Verify(exchange.pub, msg, sig)
    882 msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
    883 sig = Ed25519-Sign(merchant.priv, msg)
    884 ~~~~
    885 
    886 ~~~
    887 (W3) payment verification (wallet)
    888 
    889 msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
    890 Check Ed25519-Verify(merchant.pub, msg, sig)
    891 ~~~
    892 
    893 ### Refund {#refund}
    894 
    895 A wallet can request a refund for an order from the merchant after it has been completed successfully
    896 (cf. {{payment}}) and before the merchant has been paid out by the exchange (i.e., before `contract.wire_deadline`).
    897 The merchant needs to approve the refund via its business logic,
    898 and is free to decide the total amount of the refund
    899 as well as which coins' deposit operations are (potentially partly) invalidated.
    900 After the exchange has accepted the refund request,
    901 the coins obtain their (partial) value back.
    902 The wallet should proceed to refresh (cf. {{refresh}}) the coins before spending them again
    903 to obtain unlinkability.
    904 
    905 In case the wallet itself has used deposit to its own payto,
    906 it can act as the merchant in the protocol below.
    907 
    908 // todo: if proves practical, similar strucuture could be used for pay/deposit
    909 (interaction) between wallet, merchant and exchange
    910 
    911 ~~~
    912       wallet                        merchant                       exchange
    913 Knows order.id                 Knows merchant.priv         Knows deposit_record
    914 Knows contract                         |                         for coinᵢ.pub
    915         |                              |                              |
    916 +---------------------+                |                              |
    917 | (W1) refund request |                |                              |
    918 +---------------------+                |                              |
    919         |                              |                              |
    920         |- /orders/{order.id}/refund ->|                              |
    921         |          (h_contract)        |                              |
    922         |                              |                              |
    923         |                 +------------------------+                  |
    924         |                 | (M1) refund processing |                  |
    925         |                 +------------------------+                  |
    926         |                              |                              |
    927         |                              |- /coins/{coinᵢ.pub}/refund ->|
    928         |                              |   (valueᵢ, h_contract, id,   |
    929         |                              |      merchant.pub, sigᵢ)     |
    930         |                              |                              |
    931         |                              |                  +-------------------+
    932         |                              |                  | (E1) refund check |
    933         |                              |                  +-------------------+
    934         |                              |                              |
    935         |                              |<--- (exchange.pub, sigᵢ) ----|
    936         |                              |                              |
    937         |                 +--------------------------+                |
    938         |                 | (M2) refund confirmation |                |
    939         |                 +--------------------------+                |
    940         |                              |                              |
    941         |<-----(value, ⟨refundᵢ⟩,------|                              |
    942         |        merchant.pub)         |                              | // todo: why merchant.pub if no sig transmitted?
    943         |                              |                              |
    944 +-----------------------+              |                              |
    945 | (W2) refund reception |              |                              |
    946 +-----------------------+              |                              |
    947         |                              |                              |
    948 ~~~
    949 
    950 where (for RSA, without age-restriction)
    951 
    952 {::comment}
    953 
    954 ⟨ᵧₖᵢ⟩
    955 {:/}
    956 
    957 ~~~
    958 (W1) refund request (wallet)
    959 
    960 h_contract = SHA-512(canonicalJSON(contract))
    961 ~~~
    962 
    963 {::comment}
    964 
    965 ⟨ᵧₖᵢ⟩
    966 {:/}
    967 
    968 ~~~
    969 (M1) refund processing (merchant)
    970 
    971 Check h_contract known and refund possible
    972 time = now()
    973 ⟨coinᵢ⟩ = Lookup by h_contract
    974 id = ?
    975 for i in 0..n:
    976   denomᵢ = Lookup by coinᵢ.h_denom
    977   valueᵢ = refund amount // todo: split wisely
    978   msgᵢ = Gen-Msg(MERCHANT_REFUND,
    979        ( h_contract | coinᵢ.pub | id | valueᵢ | denomᵢ.fee_refund ))
    980   sigᵢ = Ed25519-Sign(merchant.priv, msgᵢ)
    981 ~~~
    982 
    983 {::comment}
    984 
    985 ⟨ᵧₖᵢ⟩
    986 {:/}
    987 
    988 ~~~
    989 (E1) refund check and confirmation (exchange)
    990 
    991 deposit_record = Lookup by h_contract // todo: needs to be persisted before with order.id and used coins!
    992 Check coinᵢ.pub part of deposit_record
    993 denomᵢ = Lookup by coinᵢ.pub
    994 msgᵢ = Gen-Msg(MERCHANT_REFUND,
    995     ( h_contract | coinᵢ.pub | id | valueᵢ | denomᵢ.fee_refund ))
    996 Check Ed25519-Verify(merchant.pub, msgᵢ, sigᵢ)
    997 Check valueᵢ >= denomᵢ.fee_refund
    998 Check refund possible (prior to wire transfer deadline)
    999 remove/update scheduled wire transfer
   1000 mark coin part as unspent
   1001 msgᵢ = Gen-Msg(MERCHANT_REFUND_OK, SHA-512(order.id)) // todo: hashing string without terminating \0
   1002 sigᵢ = Ed25519-Sign(exchange.priv, msgᵢ)
   1003 ~~~
   1004 
   1005 {::comment}
   1006 
   1007 ⟨ᵧₖᵢ⟩
   1008 {:/}
   1009 
   1010 ~~~
   1011 (M2) refund confirmation (merchant)
   1012 
   1013 for i in 0..n:
   1014   msgᵢ = Gen-Msg(MERCHANT_REFUND_OK, SHA-512(order.id)) // todo: hashing string without terminating \0
   1015   Check Ed25519-Verify(exchange.pub, msgᵢ, sigᵢ)
   1016   update business logic
   1017   refundᵢ = (valueᵢ, sigᵢ, id, coinᵢ.pub, time)
   1018 value = sum ⟨valueᵢ⟩
   1019 ~~~
   1020 
   1021 {::comment}
   1022 
   1023 ⟨ᵧₖᵢ⟩
   1024 {:/}
   1025 
   1026 ~~~
   1027 (W2) refund reception (wallet)
   1028 
   1029 for i in 0..n:
   1030   (valueᵢ, sigᵢ, id, coinᵢ.pub, time) = refundᵢ
   1031 update persistent transaction information
   1032 refresh ⟨coinᵢ⟩
   1033 ~~~
   1034 
   1035 ## Obtaining unlinkable change
   1036 
   1037 ### Refresh {#refresh}
   1038 
   1039 The wallet obtains `n` new coins `⟨coinᵢ⟩` of denominations `⟨denomᵢ⟩`
   1040 in exchange for one old `coin` of denomination `denom` from the exchange.
   1041 There are three reasons why a wallet needs to do this:
   1042 
   1043 1. Obtaining unlinkable change after using only a part of the coin's value during a payment (cf. {{payment}}),
   1044 i.e. where `contribution <= denom.value`
   1045 2. Obtaining unlinkable change after a successful refund (cf. {{refund}})
   1046 3. Renewing a coin before it deposit-expires
   1047 
   1048 The sum of the refresh fee of `denom` and the new denominations' values and withdrawal fees (defined by the exchange)
   1049 must be smaller or equal to the residual value of the old `coin`.
   1050 
   1051 The private key of each new coin candidate `⟨coinₖᵢ.priv⟩` is transitively derived from the old coin's private key `coin.priv`
   1052 via a 512-bit secret `⟨sharedₖᵢ⟩` according to `Refresh-Derive`.
   1053 The secret is regeneratable with the knowledge of `coin.priv` via the link protocol (cf. {{link}}).
   1054 The derivation ensures that ownership of coins (knowledge of the private key) is correctly transferred,
   1055 and thereby that value transfer among untrusted parties can only happen via payment and deposit, not via refresh.
   1056 
   1057 ~~~
   1058 Refresh-Derive(shared, denom) =
   1059   planchet_seed = HKDF(uint32(i), shared, "taler-coin-derivation", 64)
   1060   blind_secret = HKDF("bks", planchet_seed, "", 32)
   1061   coin.priv = HKDF("coin", planchet_seed, "", 32)
   1062   coin.pub = Ed25519-GetPub(coin.priv)
   1063   planchet = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denomᵢ.pub)
   1064   h_planchet = Hash-Planchet(planchet, denomᵢ)
   1065   return (coin, blind_secret, planchet, h_planchet)
   1066 ~~~
   1067 
   1068 Taler uses a cut-and-choose protocol with the fixed parameter `κ=3` to enforce correct derivation
   1069 of `⟨sharedₖᵢ⟩` from a single seed per batch of planchets `⟨batch_seedₖ⟩`
   1070 (in (κ-1)/κ of the cases, making income concealment for tax evasion purposes unpractical).
   1071 
   1072 Refreshing consists of two parts:
   1073 
   1074 1. Melting of the old coin and commiting to κ batches of blinded planchet candidates
   1075 2. Revelation of κ-1 secrets `⟨revealed_seedₖ⟩` to prove the proper construction of the (revealed) batches of blinded planchet candidates.
   1076 
   1077 ~~~
   1078             wallet                                  exchange
   1079 Knows ⟨denomᵢ⟩                          Knows ⟨denomᵢ.priv⟩
   1080 Knows coin                                              |
   1081                |                                        |
   1082 +-------------------+                                   |
   1083 | (W1) coin melting |                                   |
   1084 +-------------------+                                   |
   1085                |                                        |
   1086                |---------------- /melt ---------------->|
   1087                |     (coin.{pub,sig,h_denom}, value,    |
   1088                |      refresh_seed, planchets, sig)     |
   1089                |                                        |
   1090                |                      +---------------------------------------+
   1091                |                      | (E1) gamma selection and coin signing |
   1092                |                      +---------------------------------------+
   1093                |                                        |
   1094                |<------ (ɣ, exchange.pub, sig) ---------|
   1095                |                                        |
   1096 +------------------------+                              |
   1097 | (W2) secret revelation |                              |
   1098 +------------------------+                              |
   1099                |                                        |
   1100                |------------ /reveal-melt ------------->|
   1101                |     (commitment, ⟨revealed_seedₖ⟩)     |
   1102                |                                        |
   1103                |                      +----------------------------+
   1104                |                      | (E2) commitment validation |
   1105                |                      +----------------------------+
   1106                |                                        |
   1107                |<---------- (⟨blind_sigᵢ⟩) -------------|
   1108                |                                        |
   1109 +----------------------+                                |
   1110 | (W3) coin unblinding |                                |
   1111 +----------------------+                                |
   1112                |                                        |
   1113 ~~~
   1114 
   1115 where (for RSA, without age-restriction)
   1116 
   1117 {::comment}
   1118 // see TALER_EXCHANGE_get_melt_data
   1119 ⟨batch_seedₖ⟩ // see TALER_refresh_expand_seed_to_kappa_batch_seeds
   1120 ⟨transferₖᵢ.priv⟩ // see TALER_refresh_expand_batch_seed_to_transfer_data
   1121 // todo: pretty sure ECDH-GetPub and Ed25519-GetPub are equivalent, if not, transferₖᵢ.pub needs to change
   1122 h_planchetₖᵢ // see TALER_coin_ev_hash
   1123 h_planchetsₖ // see TALER_wallet_blinded_planchet_details_hash
   1124 commitment // see TALER_refresh_get_commitment
   1125 
   1126 ⟨ᵧₖᵢ⟩
   1127 {:/}
   1128 
   1129 ~~~
   1130 (W1) coin melting (wallet)
   1131 
   1132 refresh_seed = random(256)
   1133 ⟨batch_seedₖ⟩ = HKDF("refresh-batch-seeds", refresh_seed, coin.priv, k*64)
   1134 for k in 0..κ:
   1135   ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
   1136   for i in 0..n:
   1137     transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv)
   1138     sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub)
   1139     (coinₖᵢ, blind_secretₖᵢ, planchetₖᵢ, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
   1140   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1141 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1142 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1143                     | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1144 for i in 0..n:
   1145   h_denomᵢ = Hash-Denom(denomᵢ)
   1146 planchets = (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩))
   1147 msg = Gen-Msg(WALLET_COIN_MELT,
   1148     ( commitment | coin.h_denom | uint256(0x0)
   1149     | value | denom.fee_refresh ))
   1150 sig = Ed25519-Sign(coin.priv, msg)
   1151 Persist (coin.denom.pub, ...) // todo: double-check
   1152 ~~~
   1153 
   1154 {::comment}
   1155 
   1156 see TEH_handler_melt
   1157 
   1158 ⟨ᵧₖᵢ⟩
   1159 {:/}
   1160 
   1161 ~~~
   1162 (E1) gamma selection and coin signing (exchange)
   1163 
   1164 denom = Lookup by coin.h_denom
   1165 Check denom known and not deposit-expired
   1166 Check RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub)
   1167 Check coin.pub known and dirty
   1168 (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) = planchets
   1169 for i in 0..n:
   1170   denomᵢ = Lookup by h_denomᵢ
   1171   Check denomᵢ known and not withdraw-expired
   1172 value' = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1173 Check value' == value
   1174 Check-Subtract(coin.value, value)
   1175 for k in 0..κ:
   1176   for i in 0..n:
   1177     h_planchetₖᵢ = Hash-Planchet(planchetₖᵢ, denomᵢ)
   1178   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1179 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1180                     | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1181 msg = Gen-Msg(WALLET_COIN_MELT,
   1182     ( commitment | coin.h_denom | uint256(0x0)
   1183     | value | denom.fee_refresh ))
   1184 Check Ed25519-Verify(coin.pub, msg, sig)
   1185 refresh_record = Lookup by commitment
   1186 (ɣ, _, _, done, _) = refresh_record
   1187 if refresh_record not found:
   1188   ɣ = 0..κ at random
   1189   for i in 0..n:
   1190     blind_sigᵢ = RSA-FDH-Sign(planchetᵧᵢ, denomᵧᵢ.priv)
   1191   link_info = (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig)
   1192   Persist refresh_record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, false, link_info)
   1193 msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
   1194     ( commitment | uint32(ɣ) ))
   1195 sig = Ed25519-Sign(exchange.priv, msg)
   1196 ~~~
   1197 
   1198 {::comment}
   1199 
   1200 // see src/lib/exchange_api_post-melt.c: handle_melt_finished
   1201 // see src/lib/exchange_api_post-reveal-melt.c: perform_protocol
   1202 
   1203 ⟨ᵧₖᵢ⟩
   1204 {:/}
   1205 
   1206 ~~~
   1207 (W2) secret revelation (wallet)
   1208 
   1209 Check exchange.pub known
   1210 msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
   1211     ( commitment | ɣ ))
   1212 Check Ed25519-Verify(exchange.pub, msg, sig)
   1213 Persist refresh-challenge // what exactly?
   1214 for k in 0..κ and k != ɣ:
   1215   revealed_seedₖ = batch_seedₖ
   1216 ~~~
   1217 
   1218 {::comment}
   1219 
   1220 // see TEH_handler_reveal_melt
   1221 
   1222 ⟨ᵧₖᵢ⟩
   1223 {:/}
   1224 
   1225 ~~~
   1226 (E2) commitment validation (exchange)
   1227 
   1228 refresh_record = Lookup by commitment
   1229 (ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, done, _) = refresh_record
   1230 Check not done // todo: sure?
   1231 for k in 0..κ and k != ɣ:
   1232   ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
   1233   for i in 0..n:
   1234     transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv)
   1235     sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub)
   1236     (_, _, _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
   1237   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1238 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1239 commitment' = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1240                      | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1241 Check commitment == commitment'
   1242 Persist refresh_record = (_, _, _, true, _)
   1243 ~~~
   1244 
   1245 {::comment}
   1246 
   1247 // see src/lib/exchange_api_post-reveal-melt.c: reveal_melt_ok
   1248 
   1249 ⟨ᵧₖᵢ⟩
   1250 {:/}
   1251 
   1252 ~~~
   1253 (W3) coin unblinding (wallet)
   1254 
   1255 for i in 0..n:
   1256   coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub)
   1257   Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub)
   1258   coinᵧᵢ.h_denom = h_denomᵢ
   1259   Persist ⟨coinᵧᵢ⟩
   1260 ~~~
   1261 
   1262 ### Link {#link}
   1263 
   1264 Coins ⟨coinᵧᵢ⟩ obtained via the refresh protocol (cf. {{refresh}}) can be regenerated
   1265 with the knowledge of the old coin's private key `coin.priv` using the link protocol,
   1266 integrated in the coin history endpoint.
   1267 
   1268 ~~~
   1269             wallet                                  exchange
   1270 Knows coin                              Knows refresh_record for coin.pub
   1271                |                                        |
   1272 +----------------------+                                |
   1273 | (W1) history request |                                |
   1274 +----------------------+                                |
   1275                |                                        |
   1276                |------ /coins/{coin.pub}/history ------>|
   1277                |                 (sig)                  |
   1278                |                                        |
   1279                |                      +----------------------------+
   1280                |                      | (E1) refresh secret lookup |
   1281                |                      +----------------------------+
   1282                |                                        |
   1283                |<------------- (melt_info) -------------|
   1284                |                                        |
   1285 +-----------------------+                               |
   1286 | (W2) coin acquisition |                               |
   1287 +-----------------------+                               |
   1288                |                                        |
   1289 ~~~
   1290 
   1291 where (for RSA, without age-restriction)
   1292 
   1293 
   1294 {::comment}
   1295 
   1296 ⟨ᵧₖᵢ⟩
   1297 {:/}
   1298 
   1299 ~~~
   1300 (W1) history request (wallet)
   1301 
   1302 msg = Gen-Msg(COIN_HISTORY_REQUEST, uint64(0x0))
   1303 sig = Ed25519-Sign(coin.priv, msg)
   1304 ~~~
   1305 
   1306 {::comment}
   1307 
   1308 ⟨ᵧₖᵢ⟩
   1309 {:/}
   1310 
   1311 ~~~
   1312 (E1) refresh secret lookup (exchange)
   1313 
   1314 refresh_record = Lookup by coin.pub
   1315 (ɣ, ⟨blind_sigᵢ⟩, _, done, link_info) = refresh_record
   1316 if done:
   1317   melt_info = (ɣ, link_info, ⟨blind_sigᵢ⟩)
   1318 else:
   1319   melt_info = (ɣ, link_info)
   1320 ~~~
   1321 
   1322 {::comment}
   1323 
   1324 ⟨ᵧₖᵢ⟩
   1325 {:/}
   1326 
   1327 ~~~
   1328 (W2) coin acquisition (wallet)
   1329 
   1330 (ɣ, link_info, ⟨blind_sigᵢ⟩?) = melt_info
   1331 (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig) = link_info
   1332 
   1333 for i in 0..n:
   1334   denomᵢ = Lookup by h_denomᵢ
   1335 for k in 0..κ:
   1336   for i in 0..n:
   1337     sharedₖᵢ = ECDH-Ed25519-Priv(coin.priv, transferₖᵢ.pub)
   1338     (coinₖᵢ, blind_secretₖᵢ _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
   1339   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1340 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1341 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1342                     | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1343 msg = Gen-Msg(WALLET_COIN_MELT,
   1344     ( commitment | coin.h_denom | uint256(0x0)
   1345     | value | denom.fee_refresh ))
   1346 Check Ed25519-Verify(coin.pub, msg, sig)
   1347 
   1348 if ⟨blind_sigᵢ⟩ returned:
   1349   for i in 0..n:
   1350     coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub)
   1351     Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub)
   1352     coinᵧᵢ.h_denom = h_denomᵢ
   1353   Persist ⟨coinᵧᵢ⟩
   1354 ~~~
   1355 
   1356 ### Recoup {#refresh-recoup}
   1357 
   1358 // todo
   1359 
   1360 ## Transfer of E-Cash {#w2w}
   1361 
   1362 // todo: introductory text
   1363 
   1364 Transactions in E-Cash between wallets.
   1365 Commonly referred to as peer-to-peer transactions.
   1366 In Taler, interaction with exchange, therefore called wallet-to-wallet transactions.
   1367 
   1368 ### Account Creation {#w2w-account}
   1369 
   1370 ### Push Payment {#w2w-push}
   1371 
   1372 // todo
   1373 
   1374 ### Pull Payment {#w2w-pull}
   1375 
   1376 // todo
   1377 
   1378 # Security Considerations
   1379 
   1380 \[ TBD \]
   1381 
   1382 # IANA Considerations
   1383 
   1384 None.
   1385 
   1386 --- back
   1387 
   1388 # Test Vectors
   1389 
   1390 This appendix provides two sets of test vectors for testing Taler Protocol implementations.
   1391 They are generated by going through the protocol operations in the following order:
   1392 
   1393 1. Withdraw two coins `coin₀` and `coin₁` from a single `reserve` (cf. {{withdraw}}).
   1394 2. Pay for one `order` with the full value of `coin₀` and a partial value of `coin₁` (cf. {{payment}}).
   1395 3. Obtain a partial refund for `coin₀` used to pay for the `order` (cf. {{refund}}).
   1396 4. Refresh the now-dirty `coin₁` to two new coins `coin₂` and `coin₃` (cf. {{refresh}}).
   1397 5. Regenerate `coin₂` and `coin₃` with the knowledge of `coin₁` (cf. {{link}}).
   1398 6. Create an `account` for w2w transfers (cf. {{w2w-account}}).
   1399 7. Send a payment to `account` with the full value of `coin₂`, obtaining `coin₅` (cf. {{w2w-push}}).
   1400 8. Request a payment to `account`, which is paid with the full value of `coin₅`, obtaining `coin₆` (cf. {{w2w-pull}}).
   1401 9. Recoup the value of `coin₆` obtained via withdrawal from `account` (cf. {{withdraw-recoup}}).
   1402 10. Recoup the value of `coin₃` obtained via refresh from `coin₁` (cf. {{refresh-recoup}}).
   1403 
   1404 The test vectors in this document have been generated by the GNU Taler reference implementation written in C.
   1405 All binary data is provided in hexadecimal notation.
   1406 
   1407 ## Test Case 1
   1408 
   1409 ~~~
   1410 denom₀.priv = XXX
   1411 denom₀.pub = XXX
   1412 denom₀.value = XXX
   1413 denom₀.fee_withdraw = XXX
   1414 denom₁.priv = XXX
   1415 denom₁.pub = XXX
   1416 denom₁.value = XXX
   1417 denom₁.fee_withdraw = XXX
   1418 ~~~
   1419 
   1420 
   1421 ### Withdrawal {#tc1-withdraw}
   1422 
   1423 ~~~
   1424 (W1) reserve key generation (wallet)
   1425 
   1426 reserve.priv = XXX
   1427 reserve.pub = XXX
   1428 reserve.balance = XXX
   1429 ~~~
   1430 
   1431 ~~~
   1432 (W2) coin generation and blinding (wallet)
   1433 
   1434 batch_seed = XXX
   1435 coin_seed₀ = XXX
   1436 coin_seed₁ = XXX
   1437 blind_secret₀ = XXX
   1438 blind_secret₁ = XXX
   1439 coin₀.priv = XXX
   1440 coin₁.priv = XXX
   1441 coin₀.pub = XXX
   1442 coin₁.pub = XXX
   1443 h_denom₀ = XXX
   1444 h_denom₁ = XXX
   1445 planchet₀ = XXX
   1446 planchet₁ = XXX
   1447 h_planchet₀ = XXX
   1448 h_planchet₁ = XXX
   1449 msg = XXX
   1450 sig = XXX
   1451 ~~~
   1452 
   1453 ~~~
   1454 (E1) coin issuance and signing (exchange)
   1455 
   1456 total = XXX
   1457 blind_sig₀ = XXX
   1458 blind_sig₁ = XXX
   1459 ~~~
   1460 
   1461 ~~~
   1462 (W3) coin unblinding (wallet)
   1463 
   1464 coin₀.sig = XXX
   1465 coin₁.sig = XXX
   1466 ~~~
   1467 
   1468 ### Payment and Deposit {#tc1-payment}
   1469 
   1470 ### Refund {#tc1-refund}
   1471 
   1472 ### Refresh {#tc1-refresh}
   1473 
   1474 ### Link {#tc1-link}
   1475 
   1476 ### Account Creation {#tc1-w2w-account}
   1477 
   1478 ### Push Payment {#tc1-w2w-push}
   1479 
   1480 ### Pull Payment {#tc1-w2w-pull}
   1481 
   1482 ### Recoup Withdrawal {#tc1-withdraw-recoup}
   1483 
   1484 ### Recoup Refresh {#tc1-refresh-recoup}
   1485 
   1486 ## Test Case 2
   1487 
   1488 # Change log
   1489 
   1490 # Acknowledgments
   1491 {:numbered="false"}
   1492 
   1493 \[ TBD \]
   1494 
   1495 This work was supported in part by the German Federal Ministry of
   1496 Education and Research (BMBF) within the project Concrete Contracts.