lsd0009

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

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


      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   HKDF: DOI.10.1007/978-3-642-14623-7_34
     38   SHS: DOI.10.6028/NIST.FIPS.180-4
     39 
     40 informative:
     41 
     42 
     43 --- abstract
     44 
     45 \[ TBW \]
     46 
     47 --- middle
     48 
     49 # Introduction
     50 
     51 \[ TBW \]
     52 
     53 Beware that this document is still work-in-progress and may contain errors.
     54 Use at your own risk!
     55 
     56 # Notation
     57 
     58 - `"abc"` denotes the literal string `abc` encoded as ASCII [RFC20]
     59 - `a | b` denotes the concatenation of a with b
     60 - `len(a)` denotes the length in bytes of the byte string a
     61 - `padZero(y, a)` denotes the byte string a, zero-padded to the length of y bytes
     62 - `bits(x)`/`bytes(x)` denotes the minimal number of bits/bytes necessary to represent the multiple precision integer x
     63 - `uint(y, x)` denotes the `y` least significant bits of the integer `x` encoded in network byte order (big endian)
     64 - `uint16(x)`/`uint32(x)`/`uint64(x)`/`uint256(x)`/`uint512(x)` is equivalent to `uint(16, x)`/`uint(32, x)`/`uint(64, x)`/`uint(256, x)`/`uint(512, x)`, respectively
     65 - `random(y)` denotes a randomly generated sequence of y bits
     66 - `a * b (mod N)` / `a ** b (mod N)` denotes the multiplication / exponentiation of multiple precision integers a and b, modulo N
     67 
     68 # Cryptographic Primitives
     69 
     70 ## Cryptographic Hash Functions
     71 
     72 ### SHA-256 {#sha256}
     73 
     74 ~~~
     75 SHA-256(msg) -> hash
     76 
     77 Input:
     78     msg     input message of length L < 2^61 octets
     79 
     80 Output:
     81     hash    message digest of fixed length HashLen = 32 octets
     82 ~~~
     83 
     84 `hash` is the output of SHA-256 as per Sections 4.1, 5.1, 6.1, and 6.2 of [RFC6234].
     85 
     86 ### SHA-512 {#sha512}
     87 
     88 ~~~
     89 SHA-512(msg) -> hash
     90 
     91 Input:
     92     msg     input message of length L < 2^125 octets
     93 
     94 Output:
     95     hash    message digest of fixed length HashLen = 64 octets
     96 ~~~
     97 
     98 `hash` is the output of SHA-512 as per Sections 4.2, 5.2, 6.3, and 6.4 of [RFC6234].
     99 
    100 ### SHA-512-256 (truncated SHA-512) {#sha512-trunc}
    101 
    102 ~~~
    103 SHA-512-256(msg) -> hash
    104 
    105 Input:
    106     msg     input message of length L < 2^125 octets
    107 
    108 Output:
    109     hash    message digest of fixed length HashLen = 32 octets
    110 ~~~
    111 
    112 The output `hash` corresponds to the first 32 octets of the output of SHA-512 defined in {{sha512}}:
    113 
    114 ~~~
    115 temp = SHA-512(msg)
    116 hash = temp[0:31]
    117 ~~~
    118 
    119 Note that this operation differs from SHA-512/256 as defined in [SHS] in the initial hash value.
    120 
    121 
    122 ## Message Authentication Codes
    123 
    124 ### HMAC {#hmac}
    125 
    126 ~~~
    127 HMAC-Hash(key, text) -> out
    128 
    129 Option:
    130     Hash    cryptographic hash function with output length HashLen
    131 
    132 Input:
    133     key     secret key of length at least HashLen
    134     text    input data of arbitary length
    135 
    136 Output:
    137     out     output of length HashLen
    138 ~~~
    139 
    140 `out` is calculated as defined in [RFC2104].
    141 
    142 
    143 ## Key Derivation Functions
    144 
    145 ### HKDF {#hkdf}
    146 
    147 The Hashed Key Derivation Function (HKDF) used in Taler is an instantiation of [RFC5869]
    148 with two different hash functions for the Extract and Expand step as suggested in [HKDF]:
    149 `HKDF-Extract` uses `HMAC-SHA512`, while `HKDF-Expand` uses `HMAC-SHA256` (cf. {{hmac}}).
    150 
    151 ~~~
    152 HKDF(salt, IKM, info, L) -> OKM
    153 
    154 Inputs:
    155     salt    optional salt value (a non-secret random value);
    156               if not provided, it is set to a string of 64 zeros.
    157     IKM     input keying material
    158     info    optional context and application specific information
    159               (can be a zero-length string)
    160     L       length of output keying material in octets
    161               (<= 255*32 = 8160)
    162 
    163 Output:
    164     OKM      output keying material (of L octets)
    165 ~~~
    166 
    167 The output OKM is calculated as follows:
    168 
    169 ~~~
    170 PRK = HKDF-Extract(salt, IKM) with Hash = SHA-512 (HashLen = 64)
    171 OKM = HKDF-Expand(PRK, info, L) with Hash = SHA-256 (HashLen = 32)
    172 ~~~
    173 
    174 ### HKDF-Mod
    175 
    176 Based on the HKDF defined in {{hkdf}}, this function returns an OKM that is smaller than a given multiple precision integer N.
    177 
    178 ~~~
    179 HKDF-Mod(N, salt, IKM, info) -> OKM
    180 
    181 Inputs:
    182     N        multiple precision integer
    183     salt     optional salt value (a non-secret random value);
    184               if not provided, it is set to a string of 64 zeros.
    185     IKM      input keying material
    186     info     optional context and application specific information
    187               (can be a zero-length string)
    188 
    189 Output:
    190     OKM      output keying material (smaller than N)
    191 ~~~
    192 
    193 The final output `OKM` is determined deterministically based on a counter initialized at zero.
    194 
    195 ~~~
    196 counter = 0
    197 do until OKM < N:
    198     x = HKDF(salt, IKM, info | uint16(counter), bytes(N))
    199     OKM = uint(bits(N), x)
    200     counter += 1
    201 ~~~
    202 
    203 ## Non-Blind Signatures
    204 
    205 ### Ed25519
    206 
    207 ## Blind Signatures
    208 
    209 ### RSA-FDH {#rsa-fdh}
    210 
    211 #### Supporting Functions
    212 
    213 ~~~
    214 RSA-FDH(msg, pubkey) -> fdh
    215 
    216 Inputs:
    217     msg     message
    218     pubkey  RSA public key consisting of modulus N and public exponent e
    219 
    220 Output:
    221     fdh     full-domain hash of msg over pubkey.N
    222 ~~~
    223 
    224 `fdh` is calculated based on HKDF-Mod from {{hkdf-mod}} as follows:
    225 
    226 ~~~
    227 info = "RSA-FDA FTpsW!"
    228 salt = uint16(bytes(pubkey.N)) | uint16(bytes(pubkey.e))
    229      | pubkey.N | pubkey.e
    230 fdh = HKDF-Mod(pubkey.N, salt, msg, info)
    231 ~~~
    232 
    233 The resulting `fdh` can be used to test against a malicious RSA pubkey
    234 by verifying that the greatest common denominator (gcd) of `fdh` and `pubkey.N` is 1.
    235 
    236 ~~~
    237 RSA-FDH-Derive(bks, pubkey) -> out
    238 
    239 Inputs:
    240     bks     blinding key secret of length L = 32 octets
    241     pubkey  RSA public key consisting of modulus N and public exponent e
    242 
    243 Output:
    244     out     full-domain hash of bks over pubkey.N
    245 ~~~
    246 
    247 `out` is calculated based on HKDF-Mod from {{hkdf-mod}} as follows:
    248 
    249 ~~~
    250 info = "Blinding KDF"
    251 salt = "Blinding KDF extractor HMAC key"
    252 fdh = HKDF-Mod(pubkey.N, salt, bks, info)
    253 ~~~
    254 
    255 #### Blinding
    256 
    257 ~~~
    258 RSA-FDH-Blind(msg, bks, pubkey) -> out
    259 
    260 Inputs:
    261     msg     message
    262     bks     blinding key secret of length L = 32 octets
    263     pubkey  RSA public key consisting of modulus N and public exponent e
    264 
    265 Output:
    266     out     message blinded for pubkey
    267 ~~~
    268 
    269 `out` is calculated based on RSA-FDH from {{rsa-fdh}} as follows:
    270 
    271 ~~~
    272 data = RSA-FDH(msg, pubkey)
    273 r = RSA-FDH-Derive(bks, pubkey)
    274 r_e = r ** pubkey.e (mod pubkey.N)
    275 out = r_e * data (mod pubkey.N)
    276 ~~~
    277 
    278 #### Signing
    279 
    280 ~~~
    281 RSA-FDH-Sign(data, privkey) -> sig
    282 
    283 Inputs:
    284     data    data to be signed, an integer smaller than privkey.N
    285     privkey RSA private key consisting of modulus N and private exponent d
    286 
    287 Output:
    288     sig     signature on data by privkey
    289 ~~~
    290 
    291 `sig` is calculated as follows:
    292 
    293 ~~~
    294 sig = data ** privkey.d (mod privkey.N)
    295 ~~~
    296 
    297 #### Unblinding
    298 
    299 ~~~
    300 RSA-FDH-Unblind(sig, bks, pubkey) -> out
    301 
    302 Inputs:
    303     sig     blind signature
    304     bks     blinding key secret of length L = 32 octets
    305     pubkey  RSA public key consisting of modulus N and public exponent e
    306 
    307 Output:
    308     out     unblinded signature
    309 ~~~
    310 
    311 `out` is calculated as follows:
    312 
    313 ~~~
    314 r = RSA-FDH-Derive(bks, pubkey)
    315 r_inv = inverse of r (mod pubkey.N)
    316 out = sig * r_inv (mod pubkey.N)
    317 ~~~
    318 
    319 #### Verifying
    320 
    321 ~~~
    322 RSA-FDH-Verify(msg, sig, pubkey) -> out
    323 
    324 Inputs:
    325     msg     message
    326     sig     signature of pubkey over msg
    327     pubkey  RSA public key consisting of modulus N and public exponent e
    328 
    329 Output:
    330     out     true, if sig is a valid signature
    331 ~~~
    332 
    333 `out` is calculated based on RSA-FDH from {{rsa-fdh}} as follows:
    334 
    335 ~~~
    336 data = RSA-FDH(msg, pubkey)
    337 exp = sig ** pubkey.e (mod pubkey.N)
    338 out = (data == exp)
    339 ~~~
    340 
    341 ### Clause-Schnorr
    342 
    343 # Datatypes
    344 
    345 ## Amount
    346 
    347 Amounts are represented in Taler as positive fixed-point values
    348 consisting of `value` as the non-negative integer part of the base currency,
    349 the `fraction` given in units of one hundred millionth (1e-8) of the base currency,
    350 and `currency` as the 3-11 ASCII characters identifying the currency.
    351 
    352 Whenever used in the protocol, the binary representation of an `amount` is
    353 `uint64(amount.value) | uint32(amount.fraction) | padZero(12, amount.currency)`.
    354 
    355 ## Timestamps
    356 
    357 Absolute timestamps are represented as `uint64(x)` where `x` corresponds to
    358 the microseconds since `1970-01-01 00:00 CEST` (the UNIX epoch).
    359 The special value `0xFFFFFFFFFFFFFFFF` represents "never".
    360 <!--
    361 // todo: check if needed and correct
    362 Relative timestamps are represented as `uint64(x)` where `x` is given in microseconds.
    363 The special value `0xFFFFFFFFFFFFFFFF` represents "forever".
    364 -->
    365 
    366 ## Signatures
    367 
    368 All messages to be signed in Taler start with a header containing their size and
    369 a fixed signing context (purpose) as registered by GANA in the
    370 [GNUnet Signature Purposes](https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html)
    371 registry. Taler-related purposes start at 1000.
    372 
    373 ~~~
    374 Sign-Msg(purpose, msg) -> out
    375 
    376 Inputs:
    377     purpose signature purpose as registered at GANA
    378     msg     message content (excl. header) to be signed
    379 
    380 Output:
    381     out     complete message (incl. header) to be signed
    382 ~~~
    383 
    384 `out` is formed as follows:
    385 
    386 ~~~
    387 out = uint32(len(msg)) | uint32(purpose) | msg
    388 ~~~
    389 
    390 // todo: explain persist, check, knows, sum, indexing (if left of equal sign, single entry; if not refers to whole list)
    391 
    392 # The Taler Crypto Protocol
    393 
    394 ## Withdrawal {#withdrawal}
    395 
    396 The wallet generates `n > 0` coins (`coinᵢ`) and requests `n` signatures (`blind_sigᵢ`) from the exchange,
    397 attributing value to the coins according to `n` chosen denominations (`denomᵢ`).
    398 The total value and withdrawal fee (defined by the exchange per denomination)
    399 must be smaller or equal to the amount stored in the single reserve used for withdrawal.
    400 
    401 // todo: extend with extra roundtrip for CBS
    402 
    403 ~~~
    404             wallet                                  exchange
    405 knows denomᵢ.pub                        knows denomᵢ.priv
    406                |                                        |
    407 +-----------------------------+                         |
    408 | (W1) reserve key generation |                         |
    409 +-----------------------------+                         |
    410                |                                        |
    411                |----------- (bank transfer) ----------->|
    412                | (subject: reserve.pub, amount: value)  |
    413                |                                        |
    414                |                      +------------------------------+
    415                |                      | persist (reserve.pub, value) |
    416                |                      +------------------------------+
    417                |                                        |
    418 +-----------------------------------+                   |
    419 | (W2) coin generation and blinding |                   |
    420 +-----------------------------------+                   |
    421                |                                        |
    422                |-------------- /withdraw -------------->|
    423                |    (reserve.pub, ~(coinᵢ.h_denom),     |
    424                |           ~blind_coin, sig0)           |
    425                |                                        |
    426                |                      +--------------------------------+
    427                |                      | (E1) coin issuance and signing |
    428                |                      +--------------------------------+
    429                |                                        |
    430                |<----------- (~blind_sig) --------------|
    431                |                                        |
    432 +----------------------+                                |
    433 | (W3) coin unblinding |                                |
    434 +----------------------+                                |
    435                |                                        |
    436 ~~~
    437 
    438 where (for RSA, without age-restriction)
    439 
    440 ~~~
    441 (W1) reserve key generation (wallet)
    442 
    443 reserve = EdDSA-Keygen()
    444 persist (reserve, value)
    445 ~~~
    446 
    447 The wallet derives coins and blinding secrets using a HKDF from a single master secret per withdrawal operation,
    448 together with an integer index.
    449 This is strictly speaking an implementation detail since the master secret is never revealed to any other party,
    450 and might be chosen to be implemented differently.
    451 
    452 ~~~
    453 (W2) coin generation and blinding (wallet)
    454 
    455 master_secret = random(256)
    456 persist master_secret
    457 coin_seedᵢ = HKDF(uint32(i), master_secret, "taler-withdrawal-coin-derivation", 64)
    458 blind_secretᵢ = coin_seedᵢ[32:]
    459 coinᵢ.priv = coin_seedᵢ[:32]
    460 coinᵢ.pub = EdDSA-GetPub(coinᵢ.priv)
    461 coinᵢ.h_denom = SHA-512(uint32(0) | uint32(1) | denomᵢ.pub)
    462 blind_coinᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub)
    463 msg = Sign-Msg(WALLET_RESERVE_WITHDRAW,
    464     ( sum(denomᵢ.value) | sum(denomᵢ.fee_withdraw)
    465     | SHA-512( SHA-512(~(denomᵢ.pub)) | uint32(0x1) | blind_coinᵢ )
    466     | uint256(0x0) | uint32(0x0) | uint32(0x0) ))
    467 sig = EdDSA-Sign(reserve.priv, msg)
    468 ~~~
    469 
    470 ~~~
    471 (E1) coin issuance and signing (exchange)
    472 
    473 denomᵢ = Denom-Lookup(coinᵢ.h_denom)
    474 check denomᵢ.pub known and not withdrawal-expired
    475 check EdDSA-Verify(reserve.pub, msg, sig)
    476 check reserve KYC status ok or not needed
    477 total = sum(~(denomᵢ.value)) + sum(~(denomᵢ.fee_withdraw))
    478 check reserve.balance >= total
    479 reserve.balance -= total
    480 blind_sigᵢ = RSA-FDH-Sign(blind_coinᵢ, denomᵢ.priv)
    481 persist withdrawal
    482 ~~~
    483 
    484 ~~~
    485 (W4) coin unblinding (wallet)
    486 
    487 coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub)
    488 check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
    489 persist (coinᵢ, blind_secretᵢ)
    490 ~~~
    491 
    492 ## Payment {#payment}
    493 
    494 The wallet obtains `contract` information for an `order` from the merchant
    495 after claiming it with a `nonce`.
    496 Payment of the order is prepared by signing (partial) deposit authorizations (`depositᵢ`) of coins (`coinᵢ`),
    497 where the sum of all contributions (`contributionᵢ <= denomᵢ.value`) must match the `contract.price` plus potential deposit fees.
    498 The payment is complete as soon as the merchant successfully redeems the deposit authorizations at the exchange (cf. {{deposit}}).
    499 
    500 ~~~
    501             wallet                                  merchant
    502 knows valid coinᵢ                       knows merchant.priv
    503                                         knows exchange, payto
    504                |                                        |
    505                |                      +-----------------------+
    506                |                      | (M1) order generation |
    507                |                      +-----------------------+
    508                |                                        |
    509                |<------- (QR-Code / NFC / URI) ---------|
    510                |          (order.{id,token?})           |
    511                |                                        |
    512 +-----------------------+                               |
    513 | (W1) nonce generation |                               |
    514 +-----------------------+                               |
    515                |                                        |
    516                |------- /orders/{order.id}/claim ------>|
    517                |       (nonce.pub, order.token?)        |
    518                |                                        |
    519                |                      +--------------------------+
    520                |                      | (M2) contract generation |
    521                |                      +--------------------------+
    522                |                                        |
    523                |<---- (contract, merchant.pub, contract_sig) ----|
    524                |                                        |
    525 +--------------------------+                            |
    526 | (W2) payment preparation |                            |
    527 +--------------------------+                            |
    528                |                                        |
    529                |------- /orders/{order.id}/pay -------->|
    530                |              (~deposit)                |
    531                |                                        |
    532                |                      +--------------------+
    533                |                      | (M3) deposit check |
    534                |                      +--------------------+
    535                |                                        |
    536                |<--------------- (sig) -----------------|
    537                |                                        |
    538 +---------------------------+                           |
    539 | (W3) payment verification |                           |
    540 +---------------------------+                           |
    541                |                                        |
    542 ~~~
    543 
    544 where (without age restriction, policy and wallet data hash)
    545 
    546 ~~~
    547 (M1) order generation (merchant)
    548 
    549 wire_salt = random(128)
    550 persist order = (id, price, info, token?, wire_salt)
    551 ~~~
    552 
    553 ~~~
    554 (W1) nonce generation (wallet)
    555 
    556 nonce = EdDSA-Keygen()
    557 persist nonce.priv
    558 ~~~
    559 
    560 Note that the private key of `nonce` is currently not used anywhere in the protocol.
    561 However, it could be used in the future to prove ownership of an order transaction,
    562 enabling use-cases such as "unclaiming" or transferring an order to another person,
    563 or proving the payment without resorting to the individual coins.
    564 
    565 ~~~
    566 (M2) contract generation (merchant)
    567 
    568 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
    569 determine timestamp, refund_deadline, wire_deadline
    570 contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline)
    571 check contract.order.token == token
    572 contract.nonce = nonce.pub
    573 persist contract
    574 h_contract = SHA-512(canonicalJSON(contract))
    575 msg = Sign-Msg(MERCHANT_CONTRACT, h_contract)
    576 contract_sig = EdDSA-Sign(merchant.priv, msg)
    577 ~~~
    578 
    579 ~~~
    580 (W2) payment preparation (wallet)
    581 
    582 h_contract = SHA-512(canonicalJSON(contract))
    583 msg = Sign-Msg(MERCHANT_CONTRACT, h_contract)
    584 check EdDSA-Verify(merchant.pub, msg, contract_sig)
    585 check contract.nonce = nonce
    586 TODO: double-check extra hash check?
    587 ~selection = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here
    588 (coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ
    589 h_contract = SHA-512(canonicalJSON(contract))
    590 msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT,
    591     ( h_contract | uint256(0x0)
    592     | uint512(0x0) | contract.h_wire | coinᵢ.h_denom
    593     | contract.timestamp | contract.refund_deadline
    594     | contributionᵢ + denomᵢ.fee_deposit
    595     | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
    596 sigᵢ = EdDSA-Sign(coinᵢ.priv, msgᵢ)
    597 depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ)
    598 persist (contract, ~sig, ~deposit)
    599 ~~~
    600 
    601 // TODO: explain CoinSelection
    602 // TODO: maybe better {sigᵢ} instead of ~sig?
    603 // TODO: maybe introduce symbol for pub/priv
    604 // TODO: maybe rename Sign-Msg as name is currently a bit confusing
    605 
    606 ~~~
    607 (M3) deposit check (merchant)
    608 
    609 check sum(depositᵢ.contribution) == contract.price
    610 check Deposit(~deposit)
    611 msg = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
    612 sig = EdDSA-Sign(merchant.priv, msg)
    613 ~~~
    614 
    615 ~~~
    616 (W3) payment verification (wallet)
    617 
    618 msg = Sign-Msg(MERCHANT_PAYMENT_OK, h_contract)
    619 check EdDSA-Verify(merchant.pub, msg, sig)
    620 ~~~
    621 
    622 ## Deposit {#deposit}
    623 
    624 // todo: add introductory text
    625 
    626 ~~~
    627        merchant/wallet                              exchange
    628 knows exchange.pub                      knows exchange.priv
    629 knows contract_sig                      knows denomᵢ.pub
    630 knows payto, wire_salt                                  |
    631 knows contract, ~deposit                                |
    632                |                                        |
    633 +--------------------------+                            |
    634 | (M1) deposit preparation |                            |
    635 +--------------------------+                            |
    636                |                                        |
    637                |----------- /batch-deposit ------------>|
    638                |    (info, h_contract, ~deposit         |
    639                |     merchant.pub, contract_sig)        |
    640                |                                        |
    641                |                      +-------------------------+
    642                |                      | (E1) deposit validation |
    643                |                      +-------------------------+
    644                |                                        |
    645                |<--- (timestamp, exchange.pub, sig) ----|
    646                |                                        |
    647 +---------------------------+                           |
    648 | (M2) deposit verification |                           |
    649 +---------------------------+                           |
    650                |                                        |
    651 ~~~
    652 
    653 where (without age restriction, policy and wallet data hash)
    654 
    655 ~~~
    656 (M1) Deposit preparation (merchant/wallet)
    657 
    658 h_contract = SHA-512(canonicalJSON(contract))
    659 info.time = contract.{timestamp, wire_deadline, refund_deadline}
    660 info.wire = (payto, wire_salt)
    661 ~~~
    662 
    663 ~~~
    664 (E1) Deposit validation (exchange)
    665 
    666 coinᵢ = depositᵢ.coin
    667 denomᵢ = Denom-Lookup(coinᵢ.h_denom)
    668 check denomᵢ.pub known and not deposit-expired
    669 h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64)
    670 msgᵢ = Sign-Msg(WALLET_COIN_DEPOSIT,
    671     ( h_contract | uint256(0x0)
    672     | uint512(0x0) | h_wire | coinᵢ.h_denom
    673     | info.time.timestamp | info.time.refund_deadline
    674     | depositᵢ.contribution + denomᵢ.fee_deposit
    675     | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) ))
    676 check EdDSA-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig)
    677 check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub)
    678 check not overspending
    679 persist deposit-record, mark-spent
    680 schedule bank transfer to payto
    681 timestamp = now()
    682 msg = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT,
    683     ( h_contract | h_wire | uint512(0x0)
    684     | timestamp | info.time.wire_deadline
    685     | info.time.refund_deadline
    686     | sum(depositᵢ.contribution) - sum(denomᵢ.fee_deposit)
    687     | SHA-512(depositᵢ.sig) | merchant.pub ))
    688 sig = EdDSA-Sign(exchange.priv, msg)
    689 ~~~
    690 
    691 ~~~
    692 (M2) Deposit verification (merchant/wallet)
    693 
    694 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64)
    695 msg = Sign-Msg(EXCHANGE_CONFIRM_DEPOSIT,
    696     ( h_contract | h_wire | uint512(0x0)
    697     | timestamp | contract.wire_deadline
    698     | contract.refund_deadline
    699     | sum(depositᵢ.contribution) - sum(denomᵢ.fee_deposit)
    700     | SHA-512(depositᵢ.sig) | merchant.pub ))
    701 check EdDSA-Verify(exchange.pub, msg, sig)
    702 ~~~
    703 
    704 ## Refresh {#refresh}
    705 
    706 // todo
    707 
    708 ## Refund {#refund}
    709 
    710 // todo
    711 
    712 ## Recoup {#recoup}
    713 
    714 // todo
    715 
    716 ## Wallet-to-Wallet Push Payment {#w2w-push}
    717 
    718 // todo
    719 
    720 ## Wallet-to-Wallet Pull Payment {#w2w-pull}
    721 
    722 // todo
    723 
    724 # Security Considerations
    725 
    726 \[ TBD \]
    727 
    728 # IANA Considerations
    729 
    730 None.
    731 
    732 --- back
    733 
    734 # Change log
    735 
    736 # Acknowledgments
    737 {:numbered="false"}
    738 
    739 \[ TBD \]
    740 
    741 This work was supported in part by the German Federal Ministry of
    742 Education and Research (BMBF) within the project Concrete Contracts.