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.