lsd0009

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

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


      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], without trailing '\0' character.
     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 {#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 {#cbs}
    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 total size
    525 (including the header) and 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-specific 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) + 8) | 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 The Taler payment protocol is a token-based _e-cash_ system
    576 which ensures anonymity for payers (much like physical cash),
    577 while guaranteing income transparency on the payees' side (much like most digital payment systems).
    578 Contrary to what the name might suggest,
    579 Taler neither is a separate currency (as cryptocurrencies do)
    580 nor is it tied to a specific currency.
    581 Instead, the payment system operator offering the Taler payment protocol
    582 can freely choose the assets backing the payment system.
    583 
    584 The basic system consists of three types of entities:
    585 
    586 1. The Taler _exchange_ is run by the payment system operator.
    587 It is the central, trusted entity which hands out e-cash and holds the corresponding value.
    588 2. A Taler _wallet_ manages e-cash in self-custody for end users.
    589 3. A Taler _merchant_ can redeem e-cash at the exchange
    590 after the wallet authorized a deposit permission during a payment.
    591 
    592 E-cash in Taler is represented as digital tokens called _coins_.
    593 They are public-private keypairs where ownership of the coin
    594 is equivalent to the knowledge of the private key `coin.priv`.
    595 Every coin has an initial value corresponding to a denomination (`denom`) offered by the exchange.
    596 The validity of coins is signaled by the presence of
    597 a valid denomination signature `coin.sig` on the (hash of the) public key `coin.pub`.
    598 To ensure payer anonymity, the exchange generates `coin.sig` without learning the actual (hash of) `coin.pub`
    599 using a _blind_ signature scheme.
    600 
    601 Wallets obtain coins from the exchange during _withdrawal_ (cf. {{withdraw}})
    602 and use them during _payment_ at merchants, who in turn _deposit_ them at the exchange (cf. {{payment}}).
    603 Residual value on partly spent coins can be _refreshed_ by the wallet subsequently in order to obtain unlinkable change (cf. {{refresh}}).
    604 Taler also supports receiving e-cash in a wallet without acting as a merchant using _wallet-to-wallet payments_ (W2W, cf. {{w2w}}),
    605 which are always handled via the exchange.
    606 
    607 Honest operation of the exchange can be optionally supervised by an independant third-party Taler _auditor_.
    608 This supervision is not part of the basic Taler protocol and thus not part of this document.
    609 
    610 ~~~
    611                  - exchange -
    612                 /            \
    613    Withdrawal  /              \  Deposit
    614      Refresh  /  W2W           \
    615              /                  \
    616           wallet ----------- merchant
    617                    Payment
    618 ~~~
    619 
    620 // todo: capitalize wallet, exchange, merchant everywhere?
    621 
    622 In the default configuration, Taler uses RSA-FDH (cf. {{rsa-fdh}}) for (blind) denomination signatures
    623 and Ed25519 (cf. {{ed25519}}) signatures everywhere else.
    624 Clause-Schnorr Signatures (cf. {{cbs}}) provide an alternative blind signature scheme operating on Elliptic Curves.
    625 As their usage is still experimental, they are not described as part of this document.
    626 
    627 Taler has optional support for age-restricted coins, enabling privacy-preserving age restriction.
    628 As an optional feature, it is not part of the basic Taler protocol and thus left out of the description in this document.
    629 
    630 ## Obtaining E-Cash
    631 
    632 ### Withdrawal {#withdraw}
    633 
    634 The wallet generates `n > 0` coins `⟨coinᵢ⟩` and requests `n` signatures `⟨blind_sigᵢ⟩` from the exchange,
    635 attributing value to the coins according to `n` chosen denominations `⟨denomᵢ⟩`.
    636 The total value and withdrawal fee (defined by the exchange per denomination)
    637 must be smaller or equal to the amount stored in the single reserve used for withdrawal.
    638 
    639 // todo: document TALER_MAX_COINS = 64 per operation (due to CS-encoding)
    640 
    641 // todo: extend with extra roundtrip for CBS
    642 
    643 ~~~
    644             wallet                                  exchange
    645 Knows ⟨denomᵢ⟩                          Knows ⟨denomᵢ.priv⟩
    646                |                                        |
    647 +-----------------------------+                         |
    648 | (W1) reserve key generation |                         |
    649 +-----------------------------+                         |
    650                |                                        |
    651                |----------- (bank transfer) ----------->|
    652                | (subject: reserve.pub, amount: value)  |
    653                |                                        |
    654                |                      +------------------------------+
    655                |                      | Persist (reserve.pub, value) |
    656                |                      +------------------------------+
    657                |                                        |
    658 +-----------------------------------+                   |
    659 | (W2) coin generation and blinding |                   |
    660 +-----------------------------------+                   |
    661                |                                        |
    662                |-------------- /withdraw -------------->|
    663                |    (reserve.pub, planchets, sig)       |
    664                |                                        |
    665                |                      +--------------------------------+
    666                |                      | (E1) coin issuance and signing |
    667                |                      +--------------------------------+
    668                |                                        |
    669                |<---------- (⟨blind_sigᵢ⟩) -------------|
    670                |                                        |
    671 +----------------------+                                |
    672 | (W3) coin unblinding |                                |
    673 +----------------------+                                |
    674                |                                        |
    675 ~~~
    676 
    677 where (for RSA, without age-restriction)
    678 
    679 ~~~ pseudocode
    680 (W1) reserve key generation (wallet)
    681 
    682 reserve = Ed25519-Keygen()
    683 Persist (reserve, value)
    684 ~~~
    685 
    686 The wallet derives coins and blinding secrets using a HKDF from a single seed per withdrawal operation,
    687 together with an integer index.
    688 This is strictly speaking an implementation detail since the seed is never revealed to any other party,
    689 and might be chosen to be implemented differently.
    690 
    691 ~~~ pseudocode
    692 (W2) coin generation and blinding (wallet)
    693 
    694 batch_seed = random(256)
    695 Persist batch_seed
    696 for i in 0..n:
    697   coin_seedᵢ = HKDF(uint32(i), batch_seed, "taler-withdrawal-coin-derivation", 64)
    698   blind_secretᵢ = coin_seedᵢ[32:]
    699   coinᵢ.priv = coin_seedᵢ[:32]
    700   coinᵢ.pub = Ed25519-GetPub(coinᵢ.priv)
    701   h_denomᵢ = Hash-Denom(denomᵢ)
    702   planchetᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub)
    703   h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
    704 planchets = (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩)
    705 msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
    706     ( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
    707     | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
    708 sig = Ed25519-Sign(reserve.priv, msg)
    709 
    710 // todo: exchange.git uses different derivation than wallet-core.git (above):
    711 ⟨coin_seedᵢ⟩ = HKDF(uint32(n), batch_seed, "taler-withdraw-secrets", 32*n)
    712 for i in 0..n:
    713   blind_secretᵢ = HKDF("bks", coin_seedᵢ, "", 32)
    714   coinᵢ.priv = HKDF("coin", coin_seedᵢ, "", 32)
    715 ~~~
    716 
    717 ~~~ pseudocode
    718 (E1) coin issuance and signing (exchange)
    719 
    720 (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩) = planchets
    721 for i in 0..n:
    722   denomᵢ = Lookup by h_denomᵢ
    723   Check denomᵢ known and not withdraw-expired
    724   h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ)
    725 msg = Gen-Msg(WALLET_RESERVE_WITHDRAW,
    726     ( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩
    727     | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
    728 Check Ed25519-Verify(reserve.pub, msg, sig)
    729 Check reserve KYC status ok or not needed
    730 total = Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
    731 Check-Subtract(reserve.balance, total)
    732 for i in 0..n:
    733   blind_sigᵢ = RSA-FDH-Sign(planchetᵢ, denomᵢ.priv)
    734 Persist withdrawal // todo: what exactly? should be checked first for replay?
    735 ~~~
    736 
    737 ~~~ pseudocode
    738 (W3) coin unblinding (wallet)
    739 
    740 for i in 0..n:
    741   coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub)
    742   Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
    743   coinᵢ.h_denom = h_denomᵢ
    744   coinᵢ.blind_secret = blind_secretᵢ  // todo: why save blind_secret, if batch_seed already persisted?
    745 Persist ⟨coinᵢ⟩
    746 ~~~
    747 
    748 ### Recoup {#withdraw-recoup}
    749 
    750 // todo
    751 
    752 ## Payment with E-Cash
    753 
    754 ### Payment and Deposit {#payment}
    755 
    756 The wallet obtains `contract` information for an `order` from the merchant
    757 after claiming it with a `nonce`.
    758 Payment of the order is prepared by signing (partial) deposit authorizations `⟨depositᵢ⟩` with coins `⟨coinᵢ⟩` of certain denominations `⟨denomᵢ⟩`,
    759 where the sum of all contributions (`contributionᵢ <= denomᵢ.value`) must match the `contract.price` plus potential deposit fees.
    760 The payment is complete as soon as the merchant successfully redeems the deposit authorizations at the exchange.
    761 
    762 Deposit could also be used directly by a wallet with its own payto and a minimal contract.
    763 
    764 // todo: should we integrate payment templates here?
    765 
    766 ~~~
    767       wallet                        merchant                       exchange
    768 Knows ⟨coinᵢ⟩                  Knows merchant.priv         Knows exchange.priv
    769         |                      Knows exchange, payto       Knows ⟨denomᵢ⟩
    770         |                              |                              |
    771         |                 +-----------------------+                   |
    772         |                 | (M1) order generation |                   |
    773         |                 +-----------------------+                   |
    774         |                              |                              |
    775         |<--- (QR-Code / NFC / URI) ---|                              |
    776         |      (order.{id,token?})     |                              |
    777         |                              |                              |
    778 +-----------------------+              |                              |
    779 | (W1) nonce generation |              |                              |
    780 +-----------------------+              |                              |
    781         |                              |                              |
    782         |-- /orders/{order.id}/claim ->|                              |
    783         |  (nonce.pub, order.token?)   |                              |
    784         |                              |                              |
    785         |                 +--------------------------+                |
    786         |                 | (M2) contract generation |                |
    787         |                 +--------------------------+                |
    788         |                              |                              |
    789         |<-- (contract, merchant.pub, -|                              |
    790         |            sig)              |                              |
    791         |                              |                              |
    792 +--------------------------+           |                              |
    793 | (W2) payment preparation |           |                              |
    794 +--------------------------+           |                              |
    795         |                              |                              |
    796         |--- /orders/{order.id}/pay -->|                              |
    797         |         (⟨depositᵢ⟩)         |                              |
    798         |                              |                              |
    799         |                 +--------------------------+                |
    800         |                 | (M3) deposit preparation |                |
    801         |                 +--------------------------+                |
    802         |                              |                              |
    803         |                              |-------- /batch-deposit ----->|
    804         |                              | (info, h_contract, ⟨depositᵢ⟩|
    805         |                              |        merchant.pub, sig)    |
    806         |                              |                              |
    807         |                              |                  +--------------------+
    808         |                              |                  | (E1) deposit check |
    809         |                              |                  +--------------------+
    810         |                              |                              |
    811         |                              |<------ (time_deposit, -------|
    812         |                              |      exchange.pub, sig)      |
    813         |                              |                              |
    814         |                 +---------------------------+               |
    815         |                 | (M4) deposit verification |               |
    816         |                 +---------------------------+               |
    817         |                              |                              |
    818         |<----------- (sig) -----------|                              |
    819         |                              |                              |
    820 +---------------------------+          |                              |
    821 | (W3) payment verification |          |                              |
    822 +---------------------------+          |                              |
    823         |                              |                              |
    824 ~~~
    825 
    826 where (without age restriction, policy and wallet data hash)
    827 
    828 ~~~ pseudocode
    829 (M1) order generation (merchant)
    830 
    831 wire_salt = random(128)
    832 determine price, and ASCII strings id, info, token?
    833 Persist order = (id, price, info, token?, wire_salt)
    834 ~~~
    835 
    836 ~~~ pseudocode
    837 (W1) nonce generation (wallet)
    838 
    839 nonce = Ed25519-Keygen()
    840 Persist nonce.priv
    841 ~~~
    842 
    843 Note that the private key of `nonce` is currently not used anywhere in the protocol.
    844 However, it could be used in the future to prove ownership of an order transaction,
    845 enabling use-cases such as "unclaiming" or transferring an order to another person,
    846 or proving the payment without resorting to the individual coins.
    847 
    848 ~~~ pseudocode
    849 (M2) contract generation (merchant)
    850 
    851 Check order.token? == token?
    852 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
    853 timestamp = now()
    854 determine refund_deadline, wire_deadline from timestamp
    855 contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline)
    856 contract.nonce = nonce.pub
    857 Persist contract
    858 h_contract = SHA-512(canonicalJSON(contract))
    859 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
    860 sig = Ed25519-Sign(merchant.priv, msg)
    861 ~~~
    862 
    863 ~~~ pseudocode
    864 (W2) payment preparation (wallet)
    865 
    866 h_contract = SHA-512(canonicalJSON(contract))
    867 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
    868 Check Ed25519-Verify(merchant.pub, msg, sig)
    869 Check contract.nonce == nonce
    870 // TODO: double-check extra hash check?
    871 // todo: maybe get rid of CoinSelection altogether by claiming we already know coinᵢ and contributionᵢ
    872 ⟨selectionᵢ⟩ = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
    873 for i in 0..n:
    874   (coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ
    875   msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT,
    876       ( h_contract | uint256(0x0)
    877       | uint512(0x0) | contract.h_wire | coinᵢ.h_denom
    878       | timestamp | contract.refund_deadline
    879       | contributionᵢ + denomᵢ.fee_deposit
    880       | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
    881   sigᵢ = Ed25519-Sign(coinᵢ.priv, msgᵢ)
    882   depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ)
    883 Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩)
    884 ~~~
    885 
    886 // TODO: explain CoinSelection
    887 
    888 ~~~ pseudocode
    889 (M3) deposit preparation (merchant)
    890 
    891 Check Sum ⟨depositᵢ.contribution⟩ == contract.price
    892 info.time = contract.{timestamp, wire_deadline, refund_deadline}
    893 info.wire = (payto, wire_salt)
    894 h_contract = SHA-512(canonicalJSON(contract))
    895 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract)
    896 sig = Ed25519-Sign(merchant.priv, msg)
    897 ~~~
    898 
    899 TODO: what about wire_fees, those should be checked for as well, or do we just assume merchant will pay those?
    900 see src/backend/taler-merchant-httpd_post-orders-ORDER_ID-pay.c:2760
    901 
    902 ~~~ pseudocode
    903 (E1) deposit check (exchange)
    904 
    905 h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64)
    906 for i in 0..n:
    907   coinᵢ = depositᵢ.coin
    908   denomᵢ = Lookup by coinᵢ.h_denom
    909   Check denomᵢ known and not deposit-expired
    910   totalᵢ = depositᵢ.contribution + denomᵢ.fee_deposit
    911   msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT,
    912       ( h_contract | uint256(0x0)
    913       | uint512(0x0) | h_wire | coinᵢ.h_denom
    914       | info.time.timestamp | info.time.refund_deadline
    915       | totalᵢ
    916       | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
    917   Check Ed25519-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
    918   Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
    919   Check-Subtract(coinᵢ.value, total)
    920 Persist deposit-record
    921 schedule bank transfer to payto
    922 time_deposit = now()
    923 msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
    924     ( h_contract | h_wire | uint512(0x0)
    925     | time_deposit | info.time.wire_deadline
    926     | info.time.refund_deadline
    927     | Sum ⟨depositᵢ.contribution⟩
    928     | SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
    929 sig = Ed25519-Sign(exchange.priv, msg)
    930 ~~~
    931 
    932 ~~~ pseudocode
    933 (M2) deposit verification (merchant)
    934 
    935 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
    936 msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT,
    937     ( h_contract | h_wire | uint512(0x0)
    938     | time_deposit | contract.wire_deadline
    939     | contract.refund_deadline
    940     | Sum ⟨depositᵢ.contribution⟩
    941     | SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub ))
    942 Check Ed25519-Verify(exchange.pub, msg, sig)
    943 msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
    944 sig = Ed25519-Sign(merchant.priv, msg)
    945 ~~~~
    946 
    947 ~~~ pseudocode
    948 (W3) payment verification (wallet)
    949 
    950 msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract)
    951 Check Ed25519-Verify(merchant.pub, msg, sig)
    952 ~~~
    953 
    954 ### Refund {#refund}
    955 
    956 A wallet can request a refund for an order from the merchant after it has been completed successfully
    957 (cf. {{payment}}) and before the merchant has been paid out by the exchange (i.e., before `contract.wire_deadline`).
    958 The merchant needs to approve the refund via its business logic,
    959 and is free to decide the total amount of the refund
    960 as well as which coins' deposit operations are (potentially partly) invalidated.
    961 After the exchange has accepted the refund request,
    962 the coins obtain their (partial) value back.
    963 The wallet should proceed to refresh (cf. {{refresh}}) the coins before spending them again
    964 to obtain unlinkability.
    965 
    966 In case the wallet itself has used deposit to its own payto,
    967 it can act as the merchant in the protocol below.
    968 
    969 ~~~
    970       wallet                        merchant                       exchange
    971 Knows order.id                 Knows merchant.priv         Knows deposit_record
    972 Knows contract                         |                         for coinᵢ.pub
    973         |                              |                              |
    974 +---------------------+                |                              |
    975 | (W1) refund request |                |                              |
    976 +---------------------+                |                              |
    977         |                              |                              |
    978         |- /orders/{order.id}/refund ->|                              |
    979         |          (h_contract)        |                              |
    980         |                              |                              |
    981         |                 +------------------------+                  |
    982         |                 | (M1) refund processing |                  |
    983         |                 +------------------------+                  |
    984         |                              |                              |
    985         |                              |- /coins/{coinᵢ.pub}/refund ->|
    986         |                              |   (valueᵢ, h_contract, id,   |
    987         |                              |      merchant.pub, sigᵢ)     |
    988         |                              |                              |
    989         |                              |                  +-------------------+
    990         |                              |                  | (E1) refund check |
    991         |                              |                  +-------------------+
    992         |                              |                              |
    993         |                              |<--- (exchange.pub, sigᵢ) ----|
    994         |                              |                              |
    995         |                 +--------------------------+                |
    996         |                 | (M2) refund confirmation |                |
    997         |                 +--------------------------+                |
    998         |                              |                              |
    999         |<-----(value, ⟨refundᵢ⟩,------|                              |
   1000         |        merchant.pub)         |                              | // todo: why merchant.pub if no sig transmitted?
   1001         |                              |                              |
   1002 +-----------------------+              |                              |
   1003 | (W2) refund reception |              |                              |
   1004 +-----------------------+              |                              |
   1005         |                              |                              |
   1006 ~~~
   1007 
   1008 where (for RSA, without age-restriction)
   1009 
   1010 {::comment}
   1011 
   1012 ⟨ᵧₖᵢ⟩
   1013 {:/}
   1014 
   1015 ~~~ pseudocode
   1016 (W1) refund request (wallet)
   1017 
   1018 h_contract = SHA-512(canonicalJSON(contract))
   1019 ~~~
   1020 
   1021 {::comment}
   1022 
   1023 ⟨ᵧₖᵢ⟩
   1024 {:/}
   1025 
   1026 ~~~ pseudocode
   1027 (M1) refund processing (merchant)
   1028 
   1029 Check h_contract known and refund possible
   1030 time = now()
   1031 ⟨coinᵢ⟩ = Lookup by h_contract
   1032 id = uint32(random(32))
   1033 for i in 0..n:
   1034   denomᵢ = Lookup by coinᵢ.h_denom
   1035   valueᵢ = refund amount // todo: split wisely
   1036   msgᵢ = Gen-Msg(MERCHANT_REFUND,
   1037        ( h_contract | coinᵢ.pub | id | valueᵢ | denomᵢ.fee_refund ))
   1038   sigᵢ = Ed25519-Sign(merchant.priv, msgᵢ)
   1039 ~~~
   1040 
   1041 {::comment}
   1042 
   1043 ⟨ᵧₖᵢ⟩
   1044 {:/}
   1045 
   1046 ~~~ pseudocode
   1047 (E1) refund check and confirmation (exchange)
   1048 
   1049 deposit_record = Lookup by h_contract // todo: needs to be persisted before with order.id and used coins!
   1050 Check refund possible (prior to wire transfer deadline)
   1051 for i in 0..n:
   1052   Check coinᵢ.pub part of deposit_record
   1053   denomᵢ = Lookup by coinᵢ.pub
   1054   msgᵢ = Gen-Msg(MERCHANT_REFUND,
   1055       ( h_contract | coinᵢ.pub | id | valueᵢ | denomᵢ.fee_refund ))
   1056   Check Ed25519-Verify(merchant.pub, msgᵢ, sigᵢ)
   1057   Check valueᵢ >= denomᵢ.fee_refund
   1058   remove/update scheduled wire transfer
   1059   mark coin part as unspent
   1060   msgᵢ = Gen-Msg(MERCHANT_REFUND_OK, SHA-512(order.id))
   1061   sigᵢ = Ed25519-Sign(exchange.priv, msgᵢ)
   1062 ~~~
   1063 
   1064 {::comment}
   1065 
   1066 ⟨ᵧₖᵢ⟩
   1067 {:/}
   1068 
   1069 ~~~ pseudocode
   1070 (M2) refund confirmation (merchant)
   1071 
   1072 for i in 0..n:
   1073   msgᵢ = Gen-Msg(MERCHANT_REFUND_OK, SHA-512(order.id))
   1074   Check Ed25519-Verify(exchange.pub, msgᵢ, sigᵢ)
   1075   update business logic
   1076   refundᵢ = (valueᵢ, sigᵢ, id, coinᵢ.pub, time)
   1077 value = sum ⟨valueᵢ⟩
   1078 ~~~
   1079 
   1080 {::comment}
   1081 
   1082 ⟨ᵧₖᵢ⟩
   1083 {:/}
   1084 
   1085 ~~~ pseudocode
   1086 (W2) refund reception (wallet)
   1087 
   1088 for i in 0..n:
   1089   (valueᵢ, sigᵢ, id, coinᵢ.pub, time) = refundᵢ
   1090 update persistent transaction information
   1091 refresh ⟨coinᵢ⟩
   1092 ~~~
   1093 
   1094 ## Obtaining unlinkable change
   1095 
   1096 ### Refresh {#refresh}
   1097 
   1098 The wallet obtains `n` new coins `⟨coinᵢ⟩` of denominations `⟨denomᵢ⟩`
   1099 in exchange for one old `coin` of denomination `denom` from the exchange.
   1100 There are three reasons why a wallet needs to do this:
   1101 
   1102 1. Obtaining unlinkable change after using only a part of the coin's value during a payment (cf. {{payment}}),
   1103 i.e. where `contribution <= denom.value`
   1104 2. Obtaining unlinkable change after a successful refund (cf. {{refund}})
   1105 3. Renewing a coin before it deposit-expires
   1106 
   1107 The sum of the refresh fee of `denom` and the new denominations' values and withdrawal fees (defined by the exchange)
   1108 must be smaller or equal to the residual value of the old `coin`.
   1109 
   1110 The private key of each new coin candidate `⟨coinₖᵢ.priv⟩` is transitively derived from the old coin's private key `coin.priv`
   1111 via a 512-bit secret `⟨sharedₖᵢ⟩` according to `Refresh-Derive`.
   1112 The secret is regeneratable with the knowledge of `coin.priv` via the link protocol (cf. {{link}}).
   1113 The derivation ensures that ownership of coins (knowledge of the private key) is correctly transferred,
   1114 and thereby that value transfer among untrusted parties can only happen via payment and deposit, not via refresh.
   1115 
   1116 ~~~
   1117 Refresh-Derive(shared, i, denom) =
   1118   planchet_seed = HKDF(uint32(i), shared, "taler-coin-derivation", 64)
   1119   blind_secret = HKDF("bks", planchet_seed, "", 32)
   1120   coin.priv = HKDF("coin", planchet_seed, "", 32)
   1121   coin.pub = Ed25519-GetPub(coin.priv)
   1122   planchet = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denom.pub)
   1123   h_planchet = Hash-Planchet(planchet, denom)
   1124   return (coin, blind_secret, planchet, h_planchet)
   1125 ~~~
   1126 
   1127 Taler uses a cut-and-choose protocol with the fixed parameter `κ=3` to enforce correct derivation
   1128 of `⟨sharedₖᵢ⟩` from a single seed per batch of planchets `⟨batch_seedₖ⟩`
   1129 (in (κ-1)/κ of the cases, making income concealment for tax evasion purposes unpractical).
   1130 
   1131 Refreshing consists of two parts:
   1132 
   1133 1. Melting of the old coin and commiting to κ batches of blinded planchet candidates
   1134 2. Revelation of κ-1 secrets `⟨revealed_seedₖ⟩` to prove the proper construction of the (revealed) batches of blinded planchet candidates.
   1135 
   1136 ~~~
   1137             wallet                                  exchange
   1138 Knows ⟨denomᵢ⟩                          Knows ⟨denomᵢ.priv⟩
   1139 Knows coin                                              |
   1140                |                                        |
   1141 +-------------------+                                   |
   1142 | (W1) coin melting |                                   |
   1143 +-------------------+                                   |
   1144                |                                        |
   1145                |---------------- /melt ---------------->|
   1146                |     (coin.{pub,sig,h_denom}, value,    |
   1147                |      refresh_seed, planchets, sig)     |
   1148                |                                        |
   1149                |                      +---------------------------------------+
   1150                |                      | (E1) gamma selection and coin signing |
   1151                |                      +---------------------------------------+
   1152                |                                        |
   1153                |<------ (ɣ, exchange.pub, sig) ---------|
   1154                |                                        |
   1155 +------------------------+                              |
   1156 | (W2) secret revelation |                              |
   1157 +------------------------+                              |
   1158                |                                        |
   1159                |------------ /reveal-melt ------------->|
   1160                |     (commitment, ⟨revealed_seedₖ⟩)     |
   1161                |                                        |
   1162                |                      +----------------------------+
   1163                |                      | (E2) commitment validation |
   1164                |                      +----------------------------+
   1165                |                                        |
   1166                |<---------- (⟨blind_sigᵢ⟩) -------------|
   1167                |                                        |
   1168 +----------------------+                                |
   1169 | (W3) coin unblinding |                                |
   1170 +----------------------+                                |
   1171                |                                        |
   1172 ~~~
   1173 
   1174 where (for RSA, without age-restriction)
   1175 
   1176 {::comment}
   1177 // see TALER_EXCHANGE_get_melt_data
   1178 ⟨batch_seedₖ⟩ // see TALER_refresh_expand_seed_to_kappa_batch_seeds
   1179 ⟨transferₖᵢ.priv⟩ // see TALER_refresh_expand_batch_seed_to_transfer_data
   1180 h_planchetₖᵢ // see TALER_coin_ev_hash
   1181 h_planchetsₖ // see TALER_wallet_blinded_planchet_details_hash
   1182 commitment // see TALER_refresh_get_commitment
   1183 
   1184 ⟨ᵧₖᵢ⟩
   1185 {:/}
   1186 
   1187 ~~~ pseudocode
   1188 (W1) coin melting (wallet)
   1189 
   1190 refresh_seed = random(256)
   1191 ⟨batch_seedₖ⟩ = HKDF("refresh-batch-seeds", refresh_seed, coin.priv, k*64)
   1192 for k in 0..κ:
   1193   ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
   1194   for i in 0..n:
   1195     transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv)
   1196     sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub)
   1197     (coinₖᵢ, blind_secretₖᵢ, planchetₖᵢ, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
   1198   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1199 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1200 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1201                     | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1202 for i in 0..n:
   1203   h_denomᵢ = Hash-Denom(denomᵢ)
   1204 planchets = (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩))
   1205 msg = Gen-Msg(WALLET_COIN_MELT,
   1206     ( commitment | coin.h_denom | uint256(0x0)
   1207     | value | denom.fee_refresh ))
   1208 sig = Ed25519-Sign(coin.priv, msg)
   1209 Persist (coin.denom.pub, ...) // todo: double-check
   1210 ~~~
   1211 
   1212 {::comment}
   1213 
   1214 see TEH_handler_melt
   1215 
   1216 ⟨ᵧₖᵢ⟩
   1217 {:/}
   1218 
   1219 ~~~ pseudocode
   1220 (E1) gamma selection and coin signing (exchange)
   1221 
   1222 denom = Lookup by coin.h_denom
   1223 Check denom known and not deposit-expired
   1224 Check RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub)
   1225 Check coin.pub known and dirty
   1226 (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) = planchets
   1227 for i in 0..n:
   1228   denomᵢ = Lookup by h_denomᵢ
   1229   Check denomᵢ known and not withdraw-expired
   1230 value' = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1231 Check value' == value
   1232 Check-Subtract(coin.value, value)
   1233 for k in 0..κ:
   1234   for i in 0..n:
   1235     h_planchetₖᵢ = Hash-Planchet(planchetₖᵢ, denomᵢ)
   1236   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1237 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1238                     | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1239 msg = Gen-Msg(WALLET_COIN_MELT,
   1240     ( commitment | coin.h_denom | uint256(0x0)
   1241     | value | denom.fee_refresh ))
   1242 Check Ed25519-Verify(coin.pub, msg, sig)
   1243 refresh_record = Lookup by commitment
   1244 (ɣ, _, _, done, _) = refresh_record
   1245 if refresh_record not found:
   1246   ɣ = 0..κ at random
   1247   for i in 0..n:
   1248     blind_sigᵢ = RSA-FDH-Sign(planchetᵧᵢ, denomᵧᵢ.priv)
   1249   link_info = (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig)
   1250   Persist refresh_record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, false, link_info)
   1251 msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
   1252     ( commitment | uint32(ɣ) ))
   1253 sig = Ed25519-Sign(exchange.priv, msg)
   1254 ~~~
   1255 
   1256 {::comment}
   1257 
   1258 // see src/lib/exchange_api_post-melt.c: handle_melt_finished
   1259 // see src/lib/exchange_api_post-reveal-melt.c: perform_protocol
   1260 
   1261 ⟨ᵧₖᵢ⟩
   1262 {:/}
   1263 
   1264 ~~~ pseudocode
   1265 (W2) secret revelation (wallet)
   1266 
   1267 Check exchange.pub known
   1268 msg = Gen-Msg(EXCHANGE_CONFIRM_MELT,
   1269     ( commitment | uint32(ɣ) ))
   1270 Check Ed25519-Verify(exchange.pub, msg, sig)
   1271 Persist refresh-challenge // what exactly?
   1272 for k in 0..κ and k != ɣ:
   1273   revealed_seedₖ = batch_seedₖ
   1274 ~~~
   1275 
   1276 {::comment}
   1277 
   1278 // see TEH_handler_reveal_melt
   1279 
   1280 ⟨ᵧₖᵢ⟩
   1281 {:/}
   1282 
   1283 ~~~ pseudocode
   1284 (E2) commitment validation (exchange)
   1285 
   1286 refresh_record = Lookup by commitment
   1287 (ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, done, _) = refresh_record
   1288 Check not done // todo: sure?
   1289 for k in 0..κ and k != ɣ:
   1290   ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32)
   1291   for i in 0..n:
   1292     transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv)
   1293     sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub)
   1294     (_, _, _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
   1295   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1296 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1297 commitment' = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1298                      | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1299 Check commitment == commitment'
   1300 Persist refresh_record = (_, _, _, true, _)
   1301 ~~~
   1302 
   1303 {::comment}
   1304 
   1305 // see src/lib/exchange_api_post-reveal-melt.c: reveal_melt_ok
   1306 
   1307 ⟨ᵧₖᵢ⟩
   1308 {:/}
   1309 
   1310 ~~~ pseudocode
   1311 (W3) coin unblinding (wallet)
   1312 
   1313 for i in 0..n:
   1314   coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub)
   1315   Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub)
   1316   coinᵧᵢ.h_denom = h_denomᵢ
   1317   Persist ⟨coinᵧᵢ⟩
   1318 ~~~
   1319 
   1320 ### Link {#link}
   1321 
   1322 Coins ⟨coinᵧᵢ⟩ obtained via the refresh protocol (cf. {{refresh}}) can be regenerated
   1323 with the knowledge of the old coin's private key `coin.priv` using the link protocol,
   1324 integrated in the coin history endpoint.
   1325 
   1326 ~~~
   1327             wallet                                  exchange
   1328 Knows coin                              Knows refresh_record for coin.pub
   1329                |                                        |
   1330 +----------------------+                                |
   1331 | (W1) history request |                                |
   1332 +----------------------+                                |
   1333                |                                        |
   1334                |------ /coins/{coin.pub}/history ------>|
   1335                |                 (sig)                  |
   1336                |                                        |
   1337                |                      +----------------------------+
   1338                |                      | (E1) refresh secret lookup |
   1339                |                      +----------------------------+
   1340                |                                        |
   1341                |<------------- (melt_info) -------------|
   1342                |                                        |
   1343 +-----------------------+                               |
   1344 | (W2) coin acquisition |                               |
   1345 +-----------------------+                               |
   1346                |                                        |
   1347 ~~~
   1348 
   1349 where (for RSA, without age-restriction)
   1350 
   1351 
   1352 {::comment}
   1353 
   1354 ⟨ᵧₖᵢ⟩
   1355 {:/}
   1356 
   1357 ~~~ pseudocode
   1358 (W1) history request (wallet)
   1359 
   1360 msg = Gen-Msg(COIN_HISTORY_REQUEST, uint64(0x0))
   1361 sig = Ed25519-Sign(coin.priv, msg)
   1362 ~~~
   1363 
   1364 {::comment}
   1365 
   1366 ⟨ᵧₖᵢ⟩
   1367 {:/}
   1368 
   1369 ~~~ pseudocode
   1370 (E1) refresh secret lookup (exchange)
   1371 
   1372 refresh_record = Lookup by coin.pub
   1373 (ɣ, ⟨blind_sigᵢ⟩, _, done, link_info) = refresh_record
   1374 if done:
   1375   melt_info = (ɣ, link_info, ⟨blind_sigᵢ⟩)
   1376 else:
   1377   melt_info = (ɣ, link_info)
   1378 ~~~
   1379 
   1380 {::comment}
   1381 
   1382 ⟨ᵧₖᵢ⟩
   1383 {:/}
   1384 
   1385 ~~~ pseudocode
   1386 (W2) coin acquisition (wallet)
   1387 
   1388 (ɣ, link_info, ⟨blind_sigᵢ⟩?) = melt_info
   1389 (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig) = link_info
   1390 
   1391 for i in 0..n:
   1392   denomᵢ = Lookup by h_denomᵢ
   1393 for k in 0..κ:
   1394   for i in 0..n:
   1395     sharedₖᵢ = ECDH-Ed25519-Priv(coin.priv, transferₖᵢ.pub)
   1396     (coinₖᵢ, blind_secretₖᵢ _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ)
   1397   h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ )
   1398 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩
   1399 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value
   1400                     | SHA-512( ⟨h_planchetsₖ⟩ ) )
   1401 msg = Gen-Msg(WALLET_COIN_MELT,
   1402     ( commitment | coin.h_denom | uint256(0x0)
   1403     | value | denom.fee_refresh ))
   1404 Check Ed25519-Verify(coin.pub, msg, sig)
   1405 
   1406 if ⟨blind_sigᵢ⟩ returned:
   1407   for i in 0..n:
   1408     coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub)
   1409     Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub)
   1410     coinᵧᵢ.h_denom = h_denomᵢ
   1411   Persist ⟨coinᵧᵢ⟩
   1412 ~~~
   1413 
   1414 ### Recoup {#refresh-recoup}
   1415 
   1416 // todo
   1417 
   1418 ## Transfer of E-Cash {#w2w}
   1419 
   1420 // todo: introductory text
   1421 
   1422 Transactions in E-Cash between wallets.
   1423 Commonly referred to as peer-to-peer transactions.
   1424 In Taler, interaction with exchange, therefore called wallet-to-wallet transactions.
   1425 
   1426 ### Account Creation {#w2w-account}
   1427 
   1428 ### Push Payment {#w2w-push}
   1429 
   1430 // todo
   1431 
   1432 ### Pull Payment {#w2w-pull}
   1433 
   1434 // todo
   1435 
   1436 # Security Considerations
   1437 
   1438 \[ TBD \]
   1439 
   1440 # IANA Considerations
   1441 
   1442 None.
   1443 
   1444 --- back
   1445 
   1446 # Test Vectors
   1447 
   1448 This appendix provides two sets of test vectors for testing Taler Protocol implementations.
   1449 They are generated by going through the protocol operations in the following order:
   1450 
   1451 1. Withdraw two coins `coin₀` and `coin₁` from a single `reserve` (cf. {{withdraw}}).
   1452 2. Pay for one `order` with the full value of `coin₀` and a partial value of `coin₁` (cf. {{payment}}).
   1453 3. Obtain a partial refund on `coin₀` used to pay for the `order` (cf. {{refund}}).
   1454 4. Refresh the now-dirty `coin₁` to two new coins `coin₂` and `coin₃` (cf. {{refresh}}).
   1455 5. Regenerate `coin₂` and `coin₃` with the knowledge of `coin₁` (cf. {{link}}).
   1456 6. Create an `account` for w2w transfers (cf. {{w2w-account}}).
   1457 7. Send a payment to `account` with the full value of `coin₂`, obtaining `coin₄` (cf. {{w2w-push}}).
   1458 8. Request a payment to `account`, which is paid with the full value of `coin₄`, obtaining `coin₅` (cf. {{w2w-pull}}).
   1459 9. Recoup the value of `coin₅` obtained via withdrawal from `account` (cf. {{withdraw-recoup}}).
   1460 10. Recoup the value of `coin₃` obtained via refresh from `coin₁` (cf. {{refresh-recoup}}).
   1461 
   1462 // todo: p2p sending full coins only works without fees, should we set fees to zero?
   1463 
   1464 // todo: refund would be slightly more interesting with 2 coins being (partially) refunded,
   1465 should we change to full refund coin0 + partial refund coin1 (coin1 value after fee_deposit + fee_refund should then match denom2 + denom3)
   1466 
   1467 The test vectors in this document have been generated by the GNU Taler reference implementation written in C.
   1468 All binary data is provided in hexadecimal notation.
   1469 Big numbers for RSA are represented in big-endian byte order (most significant byte first).
   1470 
   1471 ## Test Case 1
   1472 
   1473 {::include ./test-vectors/test-case-1.md}
   1474 
   1475 ## Test Case 2
   1476 
   1477 # Change log
   1478 
   1479 # Acknowledgments
   1480 {:numbered="false"}
   1481 
   1482 \[ TBD \]
   1483 
   1484 This work was supported in part by the German Federal Ministry of
   1485 Education and Research (BMBF) within the project Concrete Contracts.