lsd0009

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

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


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