lsd0009

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

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


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