draft-guetschow-taler-protocol.xml (68555B)
1 <?xml version='1.0' encoding='utf-8'?> 2 <!DOCTYPE rfc [ 3 <!ENTITY nbsp " "> 4 <!ENTITY zwsp "​"> 5 <!ENTITY nbhy "‑"> 6 <!ENTITY wj "⁠"> 7 ]> 8 <?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?> 9 <!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.35 (Ruby 3.1.2) --> 10 <rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-guetschow-taler-protocol" category="info" submissionType="independent" tocInclude="true" sortRefs="true" symRefs="true" version="3"> 11 <!-- xml2rfc v2v3 conversion 3.32.0 --> 12 <front> 13 <title>The GNU Taler Protocol</title> 14 <seriesInfo name="Internet-Draft" value="draft-guetschow-taler-protocol"/> 15 <author initials="M." surname="Gütschow" fullname="Mikolai Gütschow"> 16 <organization abbrev="TU Dresden">TUD Dresden University of Technology</organization> 17 <address> 18 <postal> 19 <street>Helmholtzstr. 10</street> 20 <city>Dresden</city> 21 <code>D-01069</code> 22 <country>Germany</country> 23 </postal> 24 <email>mikolai.guetschow@tu-dresden.de</email> 25 </address> 26 </author> 27 <date year="2026" month="April" day="08"/> 28 <workgroup>independent</workgroup> 29 <keyword>taler</keyword> 30 <keyword>cryptography</keyword> 31 <keyword>ecash</keyword> 32 <keyword>payments</keyword> 33 <abstract> 34 <?line 45?> 35 36 <t>[ TBW ]</t> 37 </abstract> 38 </front> 39 <middle> 40 <?line 49?> 41 42 <section anchor="introduction"> 43 <name>Introduction</name> 44 <t>[ TBW ]</t> 45 <t>Beware that this document is still work-in-progress and may contain errors. 46 Use at your own risk!</t> 47 </section> 48 <section anchor="notation"> 49 <name>Notation</name> 50 <ul spacing="normal"> 51 <li> 52 <t><tt>"abc"</tt> denotes the literal string <tt>abc</tt> encoded as ASCII <xref target="RFC20"/></t> 53 </li> 54 <li> 55 <t><tt>a | b</tt> denotes the concatenation of a with b</t> 56 </li> 57 <li> 58 <t><tt>len(a)</tt> denotes the length in bytes of the byte string a</t> 59 </li> 60 <li> 61 <t><tt>padZero(y, a)</tt> denotes the byte string a, zero-padded to the length of y bytes</t> 62 </li> 63 <li> 64 <t><tt>bits(x)</tt>/<tt>bytes(x)</tt> denotes the minimal number of bits/bytes necessary to represent the multiple precision integer x</t> 65 </li> 66 <li> 67 <t><tt>uint(y, x)</tt> denotes the <tt>y</tt> least significant bits of the integer <tt>x</tt>, zero-padded and encoded in network byte order (big endian)</t> 68 </li> 69 <li> 70 <t><tt>uintY(x)</tt> where <tt>Y</tt> is a positive integer number is equivalent to <tt>uint(Y, x)</tt></t> 71 </li> 72 <li> 73 <t><tt>random(y)</tt> denotes a randomly generated sequence of y bits</t> 74 </li> 75 <li> 76 <t><tt>a * b (mod N)</tt> / <tt>a ** b (mod N)</tt> denotes the multiplication / exponentiation of multiple precision integers a and b, modulo N</t> 77 </li> 78 <li> 79 <t><tt>for</tt>, <tt>if</tt>, variable assignment <tt>=</tt>, and conditional operators are to be interpreted like their Python/Julia equivalents</t> 80 </li> 81 <li> 82 <t><tt>data.key</tt> denotes the property <tt>key</tt> on the object <tt>data</tt></t> 83 </li> 84 <li> 85 <t><tt>0..n</tt> denotes the exclusive range of integer numbers from <tt>0</tt> to <tt>n-1</tt></t> 86 </li> 87 <li> 88 <t><tt>⟨dataᵢ⟩</tt> within a context of <tt>i = 0..n</tt> denotes <tt>n</tt> objects <tt>dataᵢ</tt>, represented in memory as a continuous array</t> 89 </li> 90 <li> 91 <t><tt>⟨dataᵢ.key⟩</tt> within a context of <tt>i = 0..n</tt> denotes an array of the <tt>n</tt> properties <tt>key</tt> of all <tt>n</tt> objects <tt>dataᵢ</tt></t> 92 </li> 93 </ul> 94 </section> 95 <section anchor="cryptographic-primitives"> 96 <name>Cryptographic Primitives</name> 97 <t>// todo: maybe change this description to something more similar to protocol functions (Julia-inspired syntax)</t> 98 <section anchor="cryptographic-hash-functions"> 99 <name>Cryptographic Hash Functions</name> 100 <section anchor="sha256"> 101 <name>SHA-256</name> 102 <artwork><![CDATA[ 103 SHA-256(msg) -> hash 104 105 Input: 106 msg input message of length L < 2^61 octets 107 108 Output: 109 hash message digest of fixed length HashLen = 32 octets 110 ]]></artwork> 111 <t><tt>hash</tt> is the output of SHA-256 as per Sections 4.1, 5.1, 6.1, and 6.2 of <xref target="RFC6234"/>.</t> 112 </section> 113 <section anchor="sha512"> 114 <name>SHA-512</name> 115 <artwork><![CDATA[ 116 SHA-512(msg) -> hash 117 118 Input: 119 msg input message of length L < 2^125 octets 120 121 Output: 122 hash message digest of fixed length HashLen = 64 octets 123 ]]></artwork> 124 <t><tt>hash</tt> is the output of SHA-512 as per Sections 4.2, 5.2, 6.3, and 6.4 of <xref target="RFC6234"/>.</t> 125 </section> 126 <section anchor="sha512-trunc"> 127 <name>SHA-512-256 (truncated SHA-512)</name> 128 <artwork><![CDATA[ 129 SHA-512-256(msg) -> hash 130 131 Input: 132 msg input message of length L < 2^125 octets 133 134 Output: 135 hash message digest of fixed length HashLen = 32 octets 136 ]]></artwork> 137 <t>The output <tt>hash</tt> corresponds to the first 32 octets of the output of SHA-512 defined in <xref target="sha512"/>:</t> 138 <artwork><![CDATA[ 139 temp = SHA-512(msg) 140 hash = temp[0:31] 141 ]]></artwork> 142 <t>Note that this operation differs from SHA-512/256 as defined in <xref target="SHS"/> in the initial hash value.</t> 143 </section> 144 </section> 145 <section anchor="message-authentication-codes"> 146 <name>Message Authentication Codes</name> 147 <section anchor="hmac"> 148 <name>HMAC</name> 149 <artwork><![CDATA[ 150 HMAC-Hash(key, text) -> out 151 152 Option: 153 Hash cryptographic hash function with output length HashLen 154 155 Input: 156 key secret key of length at least HashLen 157 text input data of arbitary length 158 159 Output: 160 out output of length HashLen 161 ]]></artwork> 162 <t><tt>out</tt> is calculated as defined in <xref target="RFC2104"/>.</t> 163 </section> 164 </section> 165 <section anchor="key-derivation-functions"> 166 <name>Key Derivation Functions</name> 167 <section anchor="hkdf"> 168 <name>HKDF</name> 169 <t>The Hashed Key Derivation Function (HKDF) used in Taler is an instantiation of <xref target="RFC5869"/> 170 with two different hash functions for the Extract and Expand step as suggested in <xref target="HKDF"/>: 171 <tt>HKDF-Extract</tt> uses <tt>HMAC-SHA512</tt>, while <tt>HKDF-Expand</tt> uses <tt>HMAC-SHA256</tt> (cf. <xref target="hmac"/>).</t> 172 <artwork><![CDATA[ 173 HKDF(salt, IKM, info, L) -> OKM 174 175 Inputs: 176 salt optional salt value (a non-secret random value); 177 if not provided, it is set to a string of 64 zeros. 178 IKM input keying material 179 info optional context and application specific information 180 (can be a zero-length string) 181 L length of output keying material in octets 182 (<= 255*32 = 8160) 183 184 Output: 185 OKM output keying material (of L octets) 186 ]]></artwork> 187 <t>The output OKM is calculated as follows:</t> 188 <artwork><![CDATA[ 189 PRK = HKDF-Extract(salt, IKM) with Hash = SHA-512 (HashLen = 64) 190 OKM = HKDF-Expand(PRK, info, L) with Hash = SHA-256 (HashLen = 32) 191 ]]></artwork> 192 </section> 193 <section anchor="hkdf-mod"> 194 <name>HKDF-Mod</name> 195 <t>Based on the HKDF defined in <xref target="hkdf"/>, this function returns an OKM that is smaller than a given multiple precision integer N.</t> 196 <artwork><![CDATA[ 197 HKDF-Mod(N, salt, IKM, info) -> OKM 198 199 Inputs: 200 N multiple precision integer 201 salt optional salt value (a non-secret random value); 202 if not provided, it is set to a string of 64 zeros. 203 IKM input keying material 204 info optional context and application specific information 205 (can be a zero-length string) 206 207 Output: 208 OKM output keying material (smaller than N) 209 ]]></artwork> 210 <t>The final output <tt>OKM</tt> is determined deterministically based on a counter initialized at zero.</t> 211 <artwork><![CDATA[ 212 counter = 0 213 do until OKM < N: 214 x = HKDF(salt, IKM, info | uint16(counter), bytes(N)) 215 OKM = uint(bits(N), x) 216 counter += 1 217 ]]></artwork> 218 </section> 219 </section> 220 <section anchor="non-blind-signatures"> 221 <name>Non-Blind Signatures</name> 222 <section anchor="ed25519"> 223 <name>Ed25519</name> 224 <t>Taler uses EdDSA instantiated with curve25519 as Ed25519, 225 as defined in Section 5.1 of <xref target="RFC8032"/>. 226 In particular, Taler does <em>not</em> make use of Ed25519ph or Ed25519ctx 227 as defined in that document.</t> 228 <section anchor="key-generation"> 229 <name>Key generation</name> 230 <artwork><![CDATA[ 231 Ed25519-GetPub(priv) -> pub 232 233 Input: 234 priv private Ed25519 key 235 236 Output: 237 pub public Ed25519 key 238 ]]></artwork> 239 <t><tt>pub</tt> is calculated as described in Section 5.1.5 of <xref target="RFC8032"/>.</t> 240 <artwork><![CDATA[ 241 Ed25519-Keygen() -> (priv, pub) 242 243 Output: 244 priv private Ed25519 key 245 pub public Ed25519 key 246 ]]></artwork> 247 <t><tt>priv</tt> and <tt>pub</tt> are calculated as described in Section 5.1.5 of <xref target="RFC8032"/>, 248 which is equivalent to the following:</t> 249 <artwork><![CDATA[ 250 priv = random(256) 251 pub = Ed25519-GetPub(priv) 252 ]]></artwork> 253 </section> 254 <section anchor="signing"> 255 <name>Signing</name> 256 <artwork><![CDATA[ 257 Ed25519-Sign(priv, msg) -> sig 258 259 Inputs: 260 priv Ed25519 private key 261 msg message to be signed 262 263 Output: 264 sig signature on the message by the given private key 265 ]]></artwork> 266 <t><tt>sig</tt> is calculated as described in Section 5.1.6 of <xref target="RFC8032"/>.</t> 267 </section> 268 <section anchor="verifying"> 269 <name>Verifying</name> 270 <artwork><![CDATA[ 271 Ed25519-Verify(pub, msg, sig) -> out 272 273 Inputs: 274 pub Ed25519 public key 275 msg signed message 276 sig signature on msg 277 278 Output: 279 out true, if sig is a valid signature for msg 280 ]]></artwork> 281 <t><tt>out</tt> is the outcome of the last check of Section 5.1.7 of <xref target="RFC8032"/>.</t> 282 </section> 283 </section> 284 </section> 285 <section anchor="key-agreement"> 286 <name>Key Agreement</name> 287 <section anchor="x25519"> 288 <name>X25519</name> 289 <t>Taler uses Elliptic Curve Diffie-Hellman (ECDH) on curve25519 as defined in Section 6.1 of <xref target="RFC7748"/>, 290 but reuses Ed25519 keypairs for one side of the agreement instead of random bytes. 291 Depending on whether the private or public part is from Ed25519, two different functions are used.</t> 292 <artwork><![CDATA[ 293 ECDH-Ed25519-Priv(priv, pub) -> shared 294 295 Input: 296 priv private Ed25519 key 297 pub public X25519 key 298 299 Output: 300 shared shared secret based on the given keys 301 ]]></artwork> 302 <t><tt>shared</tt> is calculated as follows, using the function X25519 defined in Section 5 of <xref target="RFC7748"/>:</t> 303 <artwork><![CDATA[ 304 priv' = SHA-512-256(priv) 305 // todo: missing bit clamping from https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c#L71 306 shared' = X25519(priv', pub) 307 shared = SHA-512(shared') 308 ]]></artwork> 309 <artwork><![CDATA[ 310 ECDH-Ed25519-Pub(priv, pub) -> shared 311 312 Input: 313 priv private X25519 key 314 pub public Ed25519 key 315 316 Output: 317 shared shared secret based on the given keys 318 ]]></artwork> 319 <t><tt>shared</tt> is calculated as follows, using the function X25519 defined in Section 5 of <xref target="RFC7748"/>, 320 and <tt>Convert-Point-Ed25519-Curve25519(p)</tt> which implements the birational map of Section 4.1 of <xref target="RFC7748"/>:</t> 321 <artwork><![CDATA[ 322 pub' = Convert-Point-Ed25519-Curve25519(pub) 323 shared' = X25519(priv, pub') 324 shared = SHA-512(shared') 325 326 {::comment} 327 see GNUNET_CRYPTO_eddsa_ecdh 328 {:/} 329 ]]></artwork> 330 <artwork><![CDATA[ 331 ECDH-GetPub(priv) -> pub 332 333 Input: 334 priv private X25519 key 335 336 Output: 337 pub public X25519 key 338 ]]></artwork> 339 <t><tt>pub</tt> is calculated according to Section 6.1 of <xref target="RFC7748"/>:</t> 340 <artwork><![CDATA[ 341 pub = X25519(priv, 9) 342 ]]></artwork> 343 </section> 344 </section> 345 <section anchor="blind-signatures"> 346 <name>Blind Signatures</name> 347 <section anchor="rsa-fdh"> 348 <name>RSA-FDH</name> 349 <section anchor="supporting-functions"> 350 <name>Supporting Functions</name> 351 <artwork><![CDATA[ 352 RSA-FDH(msg, pubkey) -> fdh 353 354 Inputs: 355 msg message 356 pubkey RSA public key consisting of modulus N and public exponent e 357 358 Output: 359 fdh full-domain hash of msg over pubkey.N 360 ]]></artwork> 361 <t><tt>fdh</tt> is calculated based on HKDF-Mod from <xref target="hkdf-mod"/> as follows:</t> 362 <artwork><![CDATA[ 363 info = "RSA-FDA FTpsW!" 364 salt = uint16(bytes(pubkey.N)) | uint16(bytes(pubkey.e)) 365 | pubkey.N | pubkey.e 366 fdh = HKDF-Mod(pubkey.N, salt, msg, info) 367 ]]></artwork> 368 <t>The resulting <tt>fdh</tt> can be used to test against a malicious RSA pubkey 369 by verifying that the greatest common denominator (gcd) of <tt>fdh</tt> and <tt>pubkey.N</tt> is 1.</t> 370 <artwork><![CDATA[ 371 RSA-FDH-Derive(bks, pubkey) -> out 372 373 Inputs: 374 bks blinding key secret of length L = 32 octets 375 pubkey RSA public key consisting of modulus N and public exponent e 376 377 Output: 378 out full-domain hash of bks over pubkey.N 379 ]]></artwork> 380 <t><tt>out</tt> is calculated based on HKDF-Mod from <xref target="hkdf-mod"/> as follows:</t> 381 <artwork><![CDATA[ 382 info = "Blinding KDF" 383 salt = "Blinding KDF extractor HMAC key" 384 fdh = HKDF-Mod(pubkey.N, salt, bks, info) 385 ]]></artwork> 386 </section> 387 <section anchor="blinding"> 388 <name>Blinding</name> 389 <artwork><![CDATA[ 390 RSA-FDH-Blind(msg, bks, pubkey) -> out 391 392 Inputs: 393 msg message 394 bks blinding key secret of length L = 32 octets 395 pubkey RSA public key consisting of modulus N and public exponent e 396 397 Output: 398 out message blinded for pubkey 399 ]]></artwork> 400 <t><tt>out</tt> is calculated based on RSA-FDH from <xref target="rsa-fdh"/> as follows:</t> 401 <artwork><![CDATA[ 402 data = RSA-FDH(msg, pubkey) 403 r = RSA-FDH-Derive(bks, pubkey) 404 r_e = r ** pubkey.e (mod pubkey.N) 405 out = r_e * data (mod pubkey.N) 406 ]]></artwork> 407 </section> 408 <section anchor="signing-1"> 409 <name>Signing</name> 410 <artwork><![CDATA[ 411 RSA-FDH-Sign(data, privkey) -> sig 412 413 Inputs: 414 data data to be signed, an integer smaller than privkey.N 415 privkey RSA private key consisting of modulus N and private exponent d 416 417 Output: 418 sig signature on data by privkey 419 ]]></artwork> 420 <t><tt>sig</tt> is calculated as follows:</t> 421 <artwork><![CDATA[ 422 sig = data ** privkey.d (mod privkey.N) 423 ]]></artwork> 424 </section> 425 <section anchor="unblinding"> 426 <name>Unblinding</name> 427 <artwork><![CDATA[ 428 RSA-FDH-Unblind(sig, bks, pubkey) -> out 429 430 Inputs: 431 sig blind signature 432 bks blinding key secret of length L = 32 octets 433 pubkey RSA public key consisting of modulus N and public exponent e 434 435 Output: 436 out unblinded signature 437 ]]></artwork> 438 <t><tt>out</tt> is calculated as follows:</t> 439 <artwork><![CDATA[ 440 r = RSA-FDH-Derive(bks, pubkey) 441 r_inv = inverse of r (mod pubkey.N) 442 out = sig * r_inv (mod pubkey.N) 443 ]]></artwork> 444 </section> 445 <section anchor="verifying-1"> 446 <name>Verifying</name> 447 <artwork><![CDATA[ 448 RSA-FDH-Verify(msg, sig, pubkey) -> out 449 450 Inputs: 451 msg message 452 sig signature of pubkey over msg 453 pubkey RSA public key consisting of modulus N and public exponent e 454 455 Output: 456 out true, if sig is a valid signature 457 ]]></artwork> 458 <t><tt>out</tt> is calculated based on RSA-FDH from <xref target="rsa-fdh"/> as follows:</t> 459 <artwork><![CDATA[ 460 data = RSA-FDH(msg, pubkey) 461 exp = sig ** pubkey.e (mod pubkey.N) 462 out = (data == exp) 463 ]]></artwork> 464 </section> 465 </section> 466 <section anchor="clause-schnorr"> 467 <name>Clause-Schnorr</name> 468 </section> 469 </section> 470 </section> 471 <section anchor="datatypes-and-notation"> 472 <name>Datatypes and Notation</name> 473 <section anchor="amounts"> 474 <name>Amounts</name> 475 <t>Amounts are represented in Taler as positive fixed-point values 476 consisting of <tt>value</tt> as the non-negative integer part of the base currency, 477 the <tt>fraction</tt> given in units of one hundred millionth (1e-8) of the base currency, 478 and <tt>currency</tt> as the 3-11 ASCII characters identifying the currency.</t> 479 <t>Whenever used in the protocol, the binary representation of an <tt>amount</tt> is 480 <tt>uint64(amount.value) | uint32(amount.fraction) | padZero(12, amount.currency)</tt>.</t> 481 </section> 482 <section anchor="timestamps"> 483 <name>Timestamps</name> 484 <t>Absolute timestamps are represented as <tt>uint64(x)</tt> where <tt>x</tt> corresponds to 485 the microseconds since <tt>1970-01-01 00:00 CEST</tt> (the UNIX epoch). 486 The special value <tt>0xFFFFFFFFFFFFFFFF</tt> represents "never". 487 <!-- 488 // todo: check if needed and correct 489 Relative timestamps are represented as `uint64(x)` where `x` is given in microseconds. 490 The special value `0xFFFFFFFFFFFFFFFF` represents "forever". 491 --> 492 </t> 493 </section> 494 <section anchor="signatures"> 495 <name>Signatures</name> 496 <t>All messages to be signed in Taler start with a header containing their size and 497 a fixed signing context (purpose) as registered by GANA in the 498 <eref target="https://gana.gnunet.org/gnunet-signatures/gnunet_signatures.html">GNUnet Signature Purposes</eref> 499 registry. Taler-related purposes start at 1000.</t> 500 <artwork><![CDATA[ 501 Gen-Msg(purpose, msg) -> out 502 503 Inputs: 504 purpose signature purpose as registered at GANA 505 msg message content (excl. header) to be signed 506 507 Output: 508 out complete message (incl. header) to be signed 509 ]]></artwork> 510 <t><tt>out</tt> is formed as follows:</t> 511 <artwork><![CDATA[ 512 out = uint32(len(msg)) | uint32(purpose) | msg 513 ]]></artwork> 514 </section> 515 <section anchor="helper-functions"> 516 <name>Helper Functions</name> 517 <t>There are a certain number of single-argument functions which are often needed, 518 and therefore omit the parentheses of the typical function syntax:</t> 519 <ul spacing="normal"> 520 <li> 521 <t><tt>Knows data</tt> specifies <tt>data</tt> that is known a priori at the start of the protocol operation</t> 522 </li> 523 <li> 524 <t><tt>Check cond</tt> verifies that the boolean condition or variable <tt>cond</tt> is true, 525 or aborts the protocol operation otherwise</t> 526 </li> 527 <li> 528 <t><tt>Persist data</tt> persists the given <tt>data</tt> to the local database</t> 529 </li> 530 <li> 531 <t><tt>data = Lookup by key</tt> retrieves previously persisted <tt>data</tt> by the given <tt>key</tt></t> 532 </li> 533 <li> 534 <t><tt>Sum ⟨dataᵢ⟩</tt> is valid for numerical objects <tt>dataᵢ</tt> including amounts (cf. <xref target="amounts"/>), 535 and denotes the numerical sum of these objects</t> 536 </li> 537 </ul> 538 <t>Some more functions that are commonly used throughout <xref target="protocol"/>:</t> 539 <artwork><![CDATA[ 540 Hash-Denom(denom) = 541 SHA-512(uint32(0) | uint32(1) | denom.pub) 542 543 Hash-Planchet(planchet, denom) = 544 SHA-512( SHA-512( denom.pub ) | uint32(0x1) | planchet ) 545 546 Check-Subtract(value, subtrahend) = 547 Check value >= subtrahend 548 Persist value -= subtrahend 549 ]]></artwork> 550 </section> 551 </section> 552 <section anchor="protocol"> 553 <name>The Taler Crypto Protocol</name> 554 <t>// todo: briefly introduce the three components wallet, exchange, merchant; maybe with ASCII diagram version</t> 555 <t>// todo: capitalize wallet, exchange, merchant?</t> 556 <section anchor="withdrawal"> 557 <name>Withdrawal</name> 558 <t>The wallet generates <tt>n > 0</tt> coins <tt>⟨coinᵢ⟩</tt> and requests <tt>n</tt> signatures <tt>⟨blind_sigᵢ⟩</tt> from the exchange, 559 attributing value to the coins according to <tt>n</tt> chosen denominations <tt>⟨denomᵢ⟩</tt>. 560 The total value and withdrawal fee (defined by the exchange per denomination) 561 must be smaller or equal to the amount stored in the single reserve used for withdrawal.</t> 562 <t>// todo: document TALER_MAX_COINS = 64 per operation (due to CS-encoding)</t> 563 <t>// todo: extend with extra roundtrip for CBS</t> 564 <artwork><![CDATA[ 565 wallet exchange 566 Knows ⟨denomᵢ⟩ Knows ⟨denomᵢ.priv⟩ 567 | | 568 +-----------------------------+ | 569 | (W1) reserve key generation | | 570 +-----------------------------+ | 571 | | 572 |----------- (bank transfer) ----------->| 573 | (subject: reserve.pub, amount: value) | 574 | | 575 | +------------------------------+ 576 | | Persist (reserve.pub, value) | 577 | +------------------------------+ 578 | | 579 +-----------------------------------+ | 580 | (W2) coin generation and blinding | | 581 +-----------------------------------+ | 582 | | 583 |-------------- /withdraw -------------->| 584 | (reserve.pub, planchets, sig) | 585 | | 586 | +--------------------------------+ 587 | | (E1) coin issuance and signing | 588 | +--------------------------------+ 589 | | 590 |<---------- (⟨blind_sigᵢ⟩) -------------| 591 | | 592 +----------------------+ | 593 | (W3) coin unblinding | | 594 +----------------------+ | 595 | | 596 ]]></artwork> 597 <t>where (for RSA, without age-restriction)</t> 598 <artwork><![CDATA[ 599 (W1) reserve key generation (wallet) 600 601 reserve = Ed25519-Keygen() 602 Persist (reserve, value) 603 ]]></artwork> 604 <t>The wallet derives coins and blinding secrets using a HKDF from a single seed per withdrawal operation, 605 together with an integer index. 606 This is strictly speaking an implementation detail since the seed is never revealed to any other party, 607 and might be chosen to be implemented differently.</t> 608 <t>// todo: blind_secret/coin.priv differently generated in TALER_EXCHANGE_post_withdraw_start/prepare_coins, double check with wallet-core (probably implementation detail here)</t> 609 <artwork><![CDATA[ 610 (W2) coin generation and blinding (wallet) 611 612 batch_seed = random(256) 613 Persist batch_seed 614 for i in 0..n: 615 coin_seedᵢ = HKDF(uint32(i), batch_seed, "taler-withdrawal-coin-derivation", 64) 616 blind_secretᵢ = coin_seedᵢ[32:] 617 coinᵢ.priv = coin_seedᵢ[:32] 618 coinᵢ.pub = Ed25519-GetPub(coinᵢ.priv) 619 h_denomᵢ = Hash-Denom(denomᵢ) 620 planchetᵢ = RSA-FDH-Blind(SHA-512(coinᵢ.pub), blind_secretᵢ, denomᵢ.pub) 621 h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ) 622 planchets = (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩) 623 msg = Gen-Msg(WALLET_RESERVE_WITHDRAW, 624 ( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩ 625 | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) 626 sig = Ed25519-Sign(reserve.priv, msg) 627 ]]></artwork> 628 <artwork><![CDATA[ 629 (E1) coin issuance and signing (exchange) 630 631 (⟨h_denomᵢ⟩, ⟨planchetᵢ⟩) = planchets 632 for i in 0..n: 633 denomᵢ = Lookup by h_denomᵢ 634 Check denomᵢ known and not withdraw-expired 635 h_planchetᵢ = Hash-Planchet(planchetᵢ, denomᵢ) 636 msg = Gen-Msg(WALLET_RESERVE_WITHDRAW, 637 ( Sum ⟨denomᵢ.value⟩ | Sum ⟨denomᵢ.fee_withdraw⟩ 638 | SHA-512( ⟨h_planchetᵢ⟩ ) | uint256(0x0) | uint32(0x0) | uint32(0x0) )) 639 Check Ed25519-Verify(reserve.pub, msg, sig) 640 Check reserve KYC status ok or not needed 641 total = Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩ 642 Check-Subtract(reserve.balance, total) 643 for i in 0..n: 644 blind_sigᵢ = RSA-FDH-Sign(planchetᵢ, denomᵢ.priv) 645 Persist withdrawal // todo: what exactly? should be checked first for replay? 646 ]]></artwork> 647 <artwork><![CDATA[ 648 (W3) coin unblinding (wallet) 649 650 for i in 0..n: 651 coinᵢ.sig = RSA-FDH-Unblind(blind_sigᵢ, blind_secretᵢ, denomᵢ.pub) 652 Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) 653 coinᵢ.h_denom = h_denomᵢ 654 coinᵢ.blind_secret = blind_secretᵢ // todo: why save blind_secret, if batch_seed already persisted? 655 Persist ⟨coinᵢ⟩ 656 ]]></artwork> 657 </section> 658 <section anchor="payment"> 659 <name>Payment</name> 660 <t>The wallet obtains <tt>contract</tt> information for an <tt>order</tt> from the merchant 661 after claiming it with a <tt>nonce</tt>. 662 Payment of the order is prepared by signing (partial) deposit authorizations <tt>⟨depositᵢ⟩</tt> with coins <tt>⟨coinᵢ⟩</tt> of certain denominations <tt>⟨denomᵢ⟩</tt>, 663 where the sum of all contributions (<tt>contributionᵢ <= denomᵢ.value</tt>) must match the <tt>contract.price</tt> plus potential deposit fees. 664 The payment is complete as soon as the merchant successfully redeems the deposit authorizations at the exchange (cf. <xref target="deposit"/>).</t> 665 <artwork><![CDATA[ 666 wallet merchant 667 Knows ⟨coinᵢ⟩ Knows merchant.priv 668 Knows exchange, payto 669 | | 670 | +-----------------------+ 671 | | (M1) order generation | 672 | +-----------------------+ 673 | | 674 |<------- (QR-Code / NFC / URI) ---------| 675 | (order.{id,token?}) | 676 | | 677 +-----------------------+ | 678 | (W1) nonce generation | | 679 +-----------------------+ | 680 | | 681 |------- /orders/{order.id}/claim ------>| 682 | (nonce.pub, order.token?) | 683 | | 684 | +--------------------------+ 685 | | (M2) contract generation | 686 | +--------------------------+ 687 | | 688 |<---- (contract, merchant.pub, sig) ----| 689 | | 690 +--------------------------+ | 691 | (W2) payment preparation | | 692 +--------------------------+ | 693 | | 694 |------- /orders/{order.id}/pay -------->| 695 | (⟨depositᵢ⟩) | 696 | | 697 | +--------------------+ 698 | | (M3) deposit check | 699 | +--------------------+ 700 | | 701 |<--------------- (sig) -----------------| 702 | | 703 +---------------------------+ | 704 | (W3) payment verification | | 705 +---------------------------+ | 706 | | 707 ]]></artwork> 708 <t>where (without age restriction, policy and wallet data hash)</t> 709 <artwork><![CDATA[ 710 (M1) order generation (merchant) 711 712 wire_salt = random(128) 713 determine id, price, info, token? 714 Persist order = (id, price, info, token?, wire_salt) 715 ]]></artwork> 716 <artwork><![CDATA[ 717 (W1) nonce generation (wallet) 718 719 nonce = Ed25519-Keygen() 720 Persist nonce.priv 721 ]]></artwork> 722 <t>Note that the private key of <tt>nonce</tt> is currently not used anywhere in the protocol. 723 However, it could be used in the future to prove ownership of an order transaction, 724 enabling use-cases such as "unclaiming" or transferring an order to another person, 725 or proving the payment without resorting to the individual coins.</t> 726 <artwork><![CDATA[ 727 (M2) contract generation (merchant) 728 729 Check order.token? == token? 730 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64) 731 determine timestamp, refund_deadline, wire_deadline 732 contract = (order.{id,price,info,token?}, exchange, h_wire, timestamp, refund_deadline, wire_deadline) 733 contract.nonce = nonce.pub 734 Persist contract 735 h_contract = SHA-512(canonicalJSON(contract)) 736 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract) 737 sig = Ed25519-Sign(merchant.priv, msg) 738 ]]></artwork> 739 <artwork><![CDATA[ 740 (W2) payment preparation (wallet) 741 742 h_contract = SHA-512(canonicalJSON(contract)) 743 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract) 744 Check Ed25519-Verify(merchant.pub, msg, sig) 745 Check contract.nonce == nonce 746 // TODO: double-check extra hash check? 747 // todo: maybe get rid of CoinSelection altogether by claiming we already know coinᵢ and contributionᵢ 748 ⟨selectionᵢ⟩ = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here 749 for i in 0..n: 750 (coinᵢ, denomᵢ, contributionᵢ) = selectionᵢ 751 msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT, 752 ( h_contract | uint256(0x0) 753 | uint512(0x0) | contract.h_wire | coinᵢ.h_denom 754 | contract.timestamp | contract.refund_deadline 755 | contributionᵢ + denomᵢ.fee_deposit 756 | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) 757 sigᵢ = Ed25519-Sign(coinᵢ.priv, msgᵢ) 758 depositᵢ = (coinᵢ.{pub,sig,h_denom}, contributionᵢ, sigᵢ) 759 Persist (contract, ⟨sigᵢ⟩, ⟨depositᵢ⟩) 760 ]]></artwork> 761 <t>// TODO: explain CoinSelection</t> 762 <t>// TODO: maybe introduce symbol for pub/priv</t> 763 <artwork><![CDATA[ 764 (M3) deposit check (merchant) 765 766 Check Sum ⟨depositᵢ.contribution⟩ == contract.price 767 Check Deposit(⟨depositᵢ⟩) 768 msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract) 769 sig = Ed25519-Sign(merchant.priv, msg) 770 ]]></artwork> 771 <artwork><![CDATA[ 772 (W3) payment verification (wallet) 773 774 msg = Gen-Msg(MERCHANT_PAYMENT_OK, h_contract) 775 Check Ed25519-Verify(merchant.pub, msg, sig) 776 ]]></artwork> 777 </section> 778 <section anchor="deposit"> 779 <name>Deposit</name> 780 <t>// todo: add introductory text</t> 781 <t>Deposit could also be used directly by a wallet with its own payto and a minimal contract.</t> 782 <artwork><![CDATA[ 783 merchant exchange 784 Knows exchange.pub Knows exchange.priv 785 Knows merchant.priv Knows ⟨denomᵢ⟩ 786 Knows payto, wire_salt | 787 Knows contract, ⟨depositᵢ⟩ | 788 | | 789 +--------------------------+ | 790 | (M1) deposit preparation | | 791 +--------------------------+ | 792 | | 793 |----------- /batch-deposit ------------>| 794 | (info, h_contract, ⟨depositᵢ⟩ | 795 | merchant.pub, sig) | 796 | | 797 | +-------------------------+ 798 | | (E1) deposit validation | 799 | +-------------------------+ 800 | | 801 |<--- (timestamp, exchange.pub, sig) ----| 802 | | 803 +---------------------------+ | 804 | (M2) deposit verification | | 805 +---------------------------+ | 806 | | 807 ]]></artwork> 808 <t>where (without age restriction, policy and wallet data hash)</t> 809 <artwork><![CDATA[ 810 (M1) Deposit preparation (merchant) 811 812 info.time = contract.{timestamp, wire_deadline, refund_deadline} 813 info.wire = (payto, wire_salt) 814 h_contract = SHA-512(canonicalJSON(contract)) 815 msg = Gen-Msg(MERCHANT_CONTRACT, h_contract) 816 sig = Ed25519-Sign(merchant.priv, msg) 817 ]]></artwork> 818 <artwork><![CDATA[ 819 (E1) Deposit validation (exchange) 820 821 h_wire = HKDF(info.wire.wire_salt, info.wire.payto, "merchant-wire-signature", 64) 822 for i in 0..n: 823 coinᵢ = depositᵢ.coin 824 denomᵢ = Lookup by coinᵢ.h_denom 825 Check denomᵢ known and not deposit-expired 826 totalᵢ = depositᵢ.contribution + denomᵢ.fee_deposit 827 msgᵢ = Gen-Msg(WALLET_COIN_DEPOSIT, 828 ( h_contract | uint256(0x0) 829 | uint512(0x0) | h_wire | coinᵢ.h_denom 830 | info.time.timestamp | info.time.refund_deadline 831 | totalᵢ 832 | denomᵢ.fee_deposit | merchant.pub | uint512(0x0) )) 833 Check Ed25519-Verify(coinᵢ.pub, msgᵢ, depositᵢ.sig) 834 Check RSA-FDH-Verify(SHA-512(coinᵢ.pub), coinᵢ.sig, denomᵢ.pub) 835 Check-Subtract(coinᵢ.value, total) 836 Persist deposit-record 837 schedule bank transfer to payto 838 timestamp = now() 839 msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT, 840 ( h_contract | h_wire | uint512(0x0) 841 | timestamp | info.time.wire_deadline 842 | info.time.refund_deadline 843 | Sum ⟨depositᵢ.contribution⟩ 844 | SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub )) 845 sig = Ed25519-Sign(exchange.priv, msg) 846 ]]></artwork> 847 <artwork><![CDATA[ 848 (M2) Deposit verification (merchant) 849 850 h_wire = HKDF(wire_salt, payto, "merchant-wire-signature", 64) 851 msg = Gen-Msg(EXCHANGE_CONFIRM_DEPOSIT, 852 ( h_contract | h_wire | uint512(0x0) 853 | timestamp | contract.wire_deadline 854 | contract.refund_deadline 855 | Sum ⟨depositᵢ.contribution⟩ 856 | SHA-512( ⟨depositᵢ.sig⟩ ) | merchant.pub )) 857 Check Ed25519-Verify(exchange.pub, msg, sig) 858 ]]></artwork> 859 </section> 860 <section anchor="refresh"> 861 <name>Refresh</name> 862 <t>The wallet obtains <tt>n</tt> new coins <tt>⟨coinᵢ⟩</tt> of denominations <tt>⟨denomᵢ⟩</tt> 863 in exchange for one old <tt>coin</tt> of denomination <tt>denom</tt> from the exchange. 864 There are two reasons why a wallet needs to do this:</t> 865 <ol spacing="normal" type="1"><li> 866 <t>Obtaining unlinkable change after using only a part of the coin's value during a payment (cf. <xref target="payment"/>) or deposit (cf. <xref target="deposit"/>), 867 i.e. where <tt>contribution <= denom.value</tt></t> 868 </li> 869 <li> 870 <t>Renewing a coin before it deposit-expires.</t> 871 </li> 872 </ol> 873 <t>The sum of the refresh fee of <tt>denom</tt> and the new denominations' values and withdrawal fees (defined by the exchange) 874 must be smaller or equal to the residual value of the old <tt>coin</tt>.</t> 875 <t>The private key of each new coin candidate <tt>⟨coinₖᵢ.priv⟩</tt> is transitively derived from the old coin's private key <tt>coin.priv</tt> 876 via a 512-bit secret <tt>⟨sharedₖᵢ⟩</tt> according to <tt>Refresh-Derive</tt>. 877 The secret is regeneratable with the knowledge of <tt>coin.priv</tt> via the link protocol (cf. <xref target="link"/>). 878 The derivation ensures that ownership of coins (knowledge of the private key) is correctly transferred, 879 and thereby that value transfer among untrusted parties can only happen via payment and deposit, not via refresh.</t> 880 <artwork><![CDATA[ 881 Refresh-Derive(shared, denom) = 882 planchet_seed = HKDF(uint32(i), shared, "taler-coin-derivation", 64) 883 blind_secret = HKDF("bks", planchet_seed, "", 32) 884 coin.priv = HKDF("coin", planchet_seed, "", 32) 885 coin.pub = Ed25519-GetPub(coin.priv) 886 planchet = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denomᵢ.pub) 887 h_planchet = Hash-Planchet(planchet, denomᵢ) 888 return (coin, blind_secret, planchet, h_planchet) 889 ]]></artwork> 890 <t>Taler uses a cut-and-choose protocol with the fixed parameter <tt>κ=3</tt> to enforce correct derivation 891 of <tt>⟨sharedₖᵢ⟩</tt> from a single seed per batch of planchets <tt>⟨batch_seedₖ⟩</tt> 892 (in (κ-1)/κ of the cases, making income concealment for tax evasion purposes unpractical).</t> 893 <t>Refreshing consists of two parts:</t> 894 <ol spacing="normal" type="1"><li> 895 <t>Melting of the old coin and commiting to κ batches of blinded planchet candidates</t> 896 </li> 897 <li> 898 <t>Revelation of κ-1 secrets <tt>⟨revealed_seedₖ⟩</tt> to prove the proper construction of the (revealed) batches of blinded planchet candidates.</t> 899 </li> 900 </ol> 901 <artwork><![CDATA[ 902 wallet exchange 903 Knows ⟨denomᵢ⟩ Knows ⟨denomᵢ.priv⟩ 904 Knows coin | 905 | | 906 +-------------------+ | 907 | (W1) coin melting | | 908 +-------------------+ | 909 | | 910 |---------------- /melt ---------------->| 911 | (coin.{pub,sig,h_denom}, value, | 912 | refresh_seed, planchets, sig) | 913 | | 914 | +---------------------------------------+ 915 | | (E1) gamma selection and coin signing | 916 | +---------------------------------------+ 917 | | 918 |<------ (ɣ, exchange.pub, sig) ---------| 919 | | 920 +------------------------+ | 921 | (W2) secret revelation | | 922 +------------------------+ | 923 | | 924 |------------ /reveal-melt ------------->| 925 | (commitment, ⟨revealed_seedₖ⟩) | 926 | | 927 | +----------------------------+ 928 | | (E2) commitment validation | 929 | +----------------------------+ 930 | | 931 |<---------- (⟨blind_sigᵢ⟩) -------------| 932 | | 933 +----------------------+ | 934 | (W3) coin unblinding | | 935 +----------------------+ | 936 | | 937 ]]></artwork> 938 <t>where (for RSA, without age-restriction)</t> 939 <artwork><![CDATA[ 940 (W1) coin melting (wallet) 941 942 refresh_seed = random(256) 943 ⟨batch_seedₖ⟩ = HKDF("refresh-batch-seeds", refresh_seed, coin.priv, k*64) 944 for k in 0..κ: 945 ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) 946 for i in 0..n: 947 transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv) 948 sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub) 949 (coinₖᵢ, blind_secretₖᵢ, planchetₖᵢ, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ) 950 h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) 951 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩ 952 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value 953 | SHA-512( ⟨h_planchetsₖ⟩ ) ) 954 for i in 0..n: 955 h_denomᵢ = Hash-Denom(denomᵢ) 956 planchets = (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) 957 msg = Gen-Msg(WALLET_COIN_MELT, 958 ( commitment | coin.h_denom | uint256(0x0) 959 | value | denom.fee_refresh )) 960 sig = Ed25519-Sign(coin.priv, msg) 961 Persist (coin.denom.pub, ...) // todo: double-check 962 ]]></artwork> 963 <artwork><![CDATA[ 964 (E1) gamma selection and coin signing (exchange) 965 966 denom = Lookup by coin.h_denom 967 Check denom known and not deposit-expired 968 Check RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub) 969 Check coin.pub known and dirty 970 (⟨h_denomᵢ⟩, ⟨planchetₖᵢ⟩, ⟨transferₖᵢ.pub⟩)) = planchets 971 for i in 0..n: 972 denomᵢ = Lookup by h_denomᵢ 973 Check denomᵢ known and not withdraw-expired 974 value' = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩ 975 Check value' == value 976 Check-Subtract(coin.value, value) 977 for k in 0..κ: 978 for i in 0..n: 979 h_planchetₖᵢ = Hash-Planchet(planchetₖᵢ, denomᵢ) 980 h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) 981 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value 982 | SHA-512( ⟨h_planchetsₖ⟩ ) ) 983 msg = Gen-Msg(WALLET_COIN_MELT, 984 ( commitment | coin.h_denom | uint256(0x0) 985 | value | denom.fee_refresh )) 986 Check Ed25519-Verify(coin.pub, msg, sig) 987 refresh_record = Lookup by commitment 988 (ɣ, _, _, done, _) = refresh_record 989 if refresh_record not found: 990 ɣ = 0..κ at random 991 for i in 0..n: 992 blind_sigᵢ = RSA-FDH-Sign(planchetᵧᵢ, denomᵧᵢ.priv) 993 link_info = (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig) 994 Persist refresh_record = (commitment, ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, false, link_info) 995 msg = Gen-Msg(EXCHANGE_CONFIRM_MELT, 996 ( commitment | uint32(ɣ) )) 997 sig = Ed25519-Sign(exchange.priv, msg) 998 ]]></artwork> 999 <artwork><![CDATA[ 1000 (W2) secret revelation (wallet) 1001 1002 Check exchange.pub known 1003 msg = Gen-Msg(EXCHANGE_CONFIRM_MELT, 1004 ( commitment | ɣ )) 1005 Check Ed25519-Verify(exchange.pub, msg, sig) 1006 Persist refresh-challenge // what exactly? 1007 for k in 0..κ and k != ɣ: 1008 revealed_seedₖ = batch_seedₖ 1009 ]]></artwork> 1010 <artwork><![CDATA[ 1011 (E2) commitment validation (exchange) 1012 1013 refresh_record = Lookup by commitment 1014 (ɣ, ⟨blind_sigᵢ⟩, h_planchetsᵧ, done, _) = refresh_record 1015 Check not done // todo: sure? 1016 for k in 0..κ and k != ɣ: 1017 ⟨transferₖᵢ.priv⟩ = HKDF("refresh-transfer-private-keys", batch_seedₖ, "", n*32) 1018 for i in 0..n: 1019 transferₖᵢ.pub = ECDH-GetPub(transferₖᵢ.priv) 1020 sharedₖᵢ = ECDH-Ed25519-Pub(transferₖᵢ.priv, coin.pub) 1021 (_, _, _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ) 1022 h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) 1023 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩ 1024 commitment' = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value 1025 | SHA-512( ⟨h_planchetsₖ⟩ ) ) 1026 Check commitment == commitment' 1027 Persist refresh_record = (_, _, _, true, _) 1028 ]]></artwork> 1029 <artwork><![CDATA[ 1030 (W3) coin unblinding (wallet) 1031 1032 for i in 0..n: 1033 coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub) 1034 Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub) 1035 coinᵧᵢ.h_denom = h_denomᵢ 1036 Persist ⟨coinᵧᵢ⟩ 1037 ]]></artwork> 1038 <section anchor="link"> 1039 <name>Link</name> 1040 <t>// todo: add introductory text</t> 1041 <artwork><![CDATA[ 1042 wallet exchange 1043 Knows coin Knows refresh_record for coin 1044 | | 1045 +----------------------+ | 1046 | (W1) history request | | 1047 +----------------------+ | 1048 | | 1049 |------ /coins/{coin.pub}/history ------>| 1050 | (sig) | 1051 | | 1052 | +----------------------------+ 1053 | | (E1) refresh secret lookup | 1054 | +----------------------------+ 1055 | | 1056 |<------------- (melt_info) -------------| 1057 | | 1058 +-----------------------+ | 1059 | (W2) coin acquisition | | 1060 +-----------------------+ | 1061 | | 1062 ]]></artwork> 1063 <t>where (for RSA, without age-restriction)</t> 1064 <artwork><![CDATA[ 1065 (W1) history request (wallet) 1066 1067 msg = Gen-Msg(COIN_HISTORY_REQUEST, uint64(0x0)) 1068 sig = Ed25519-Sign(coin.priv, msg) 1069 ]]></artwork> 1070 <artwork><![CDATA[ 1071 (E1) refresh secret lookup (exchange) 1072 1073 refresh_record = Lookup by coin.pub 1074 (ɣ, ⟨blind_sigᵢ⟩, _, done, link_info) = refresh_record 1075 if done: 1076 melt_info = (ɣ, link_info, ⟨blind_sigᵢ⟩) 1077 else: 1078 melt_info = (ɣ, link_info) 1079 ]]></artwork> 1080 <artwork><![CDATA[ 1081 (W2) coin acquisition (wallet) 1082 1083 (ɣ, link_info, ⟨blind_sigᵢ⟩?) = melt_info 1084 (refresh_seed, ⟨transferₖᵢ.pub⟩, ⟨h_denomᵢ⟩, coin_sig) = link_info 1085 1086 for i in 0..n: 1087 denomᵢ = Lookup by h_denomᵢ 1088 for k in 0..κ: 1089 for i in 0..n: 1090 sharedₖᵢ = ECDH-Ed25519-Priv(coin.priv, transferₖᵢ.pub) 1091 (coinₖᵢ, blind_secretₖᵢ _, h_planchetₖᵢ) = Refresh-Derive(sharedₖᵢ, denomᵢ) 1092 h_planchetsₖ = SHA-512( ⟨h_planchetₖᵢ⟩ ) 1093 value = coin.denom.fee_refresh + Sum ⟨denomᵢ.value⟩ + Sum ⟨denomᵢ.fee_withdraw⟩ 1094 commitment = SHA-512( refresh_seed | uint256(0x0) | coin.pub | value 1095 | SHA-512( ⟨h_planchetsₖ⟩ ) ) 1096 msg = Gen-Msg(WALLET_COIN_MELT, 1097 ( commitment | coin.h_denom | uint256(0x0) 1098 | value | denom.fee_refresh )) 1099 Check Ed25519-Verify(coin.pub, msg, sig) 1100 1101 if ⟨blind_sigᵢ⟩ returned: 1102 for i in 0..n: 1103 coinᵧᵢ.sig = RSA-FDH-Unblind(blind_sigᵧᵢ, blind_secretᵧᵢ, denomᵢ.pub) 1104 Check RSA-FDH-Verify(SHA-512(coinᵧᵢ.pub), coinᵧᵢ.sig, denomᵢ.pub) 1105 coinᵧᵢ.h_denom = h_denomᵢ 1106 Persist ⟨coinᵧᵢ⟩ 1107 ]]></artwork> 1108 </section> 1109 </section> 1110 <section anchor="refund"> 1111 <name>Refund</name> 1112 <t>// todo</t> 1113 </section> 1114 <section anchor="recoup"> 1115 <name>Recoup</name> 1116 <t>// todo</t> 1117 </section> 1118 <section anchor="w2w-push"> 1119 <name>Wallet-to-Wallet Push Payment</name> 1120 <t>// todo</t> 1121 </section> 1122 <section anchor="w2w-pull"> 1123 <name>Wallet-to-Wallet Pull Payment</name> 1124 <t>// todo</t> 1125 </section> 1126 </section> 1127 <section anchor="security-considerations"> 1128 <name>Security Considerations</name> 1129 <t>[ TBD ]</t> 1130 </section> 1131 <section anchor="iana-considerations"> 1132 <name>IANA Considerations</name> 1133 <t>None.</t> 1134 </section> 1135 </middle> 1136 <back> 1137 <references anchor="sec-normative-references"> 1138 <name>Normative References</name> 1139 <reference anchor="RFC20"> 1140 <front> 1141 <title>ASCII format for network interchange</title> 1142 <author fullname="V.G. Cerf" initials="V.G." surname="Cerf"/> 1143 <date month="October" year="1969"/> 1144 </front> 1145 <seriesInfo name="STD" value="80"/> 1146 <seriesInfo name="RFC" value="20"/> 1147 <seriesInfo name="DOI" value="10.17487/RFC0020"/> 1148 </reference> 1149 <reference anchor="RFC2104"> 1150 <front> 1151 <title>HMAC: Keyed-Hashing for Message Authentication</title> 1152 <author fullname="H. Krawczyk" initials="H." surname="Krawczyk"/> 1153 <author fullname="M. Bellare" initials="M." surname="Bellare"/> 1154 <author fullname="R. Canetti" initials="R." surname="Canetti"/> 1155 <date month="February" year="1997"/> 1156 <abstract> 1157 <t>This document describes HMAC, a mechanism for message authentication using cryptographic hash functions. HMAC can be used with any iterative cryptographic hash function, e.g., MD5, SHA-1, in combination with a secret shared key. The cryptographic strength of HMAC depends on the properties of the underlying hash function. This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind</t> 1158 </abstract> 1159 </front> 1160 <seriesInfo name="RFC" value="2104"/> 1161 <seriesInfo name="DOI" value="10.17487/RFC2104"/> 1162 </reference> 1163 <reference anchor="RFC5869"> 1164 <front> 1165 <title>HMAC-based Extract-and-Expand Key Derivation Function (HKDF)</title> 1166 <author fullname="H. Krawczyk" initials="H." surname="Krawczyk"/> 1167 <author fullname="P. Eronen" initials="P." surname="Eronen"/> 1168 <date month="May" year="2010"/> 1169 <abstract> 1170 <t>This document specifies a simple Hashed Message Authentication Code (HMAC)-based key derivation function (HKDF), which can be used as a building block in various protocols and applications. The key derivation function (KDF) is intended to support a wide range of applications and requirements, and is conservative in its use of cryptographic hash functions. This document is not an Internet Standards Track specification; it is published for informational purposes.</t> 1171 </abstract> 1172 </front> 1173 <seriesInfo name="RFC" value="5869"/> 1174 <seriesInfo name="DOI" value="10.17487/RFC5869"/> 1175 </reference> 1176 <reference anchor="RFC6234"> 1177 <front> 1178 <title>US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)</title> 1179 <author fullname="D. Eastlake 3rd" initials="D." surname="Eastlake 3rd"/> 1180 <author fullname="T. Hansen" initials="T." surname="Hansen"/> 1181 <date month="May" year="2011"/> 1182 <abstract> 1183 <t>Federal Information Processing Standard, FIPS</t> 1184 </abstract> 1185 </front> 1186 <seriesInfo name="RFC" value="6234"/> 1187 <seriesInfo name="DOI" value="10.17487/RFC6234"/> 1188 </reference> 1189 <reference anchor="RFC7748"> 1190 <front> 1191 <title>Elliptic Curves for Security</title> 1192 <author fullname="A. Langley" initials="A." surname="Langley"/> 1193 <author fullname="M. Hamburg" initials="M." surname="Hamburg"/> 1194 <author fullname="S. Turner" initials="S." surname="Turner"/> 1195 <date month="January" year="2016"/> 1196 <abstract> 1197 <t>This memo specifies two elliptic curves over prime fields that offer a high level of practical security in cryptographic applications, including Transport Layer Security (TLS). These curves are intended to operate at the ~128-bit and ~224-bit security level, respectively, and are generated deterministically based on a list of required properties.</t> 1198 </abstract> 1199 </front> 1200 <seriesInfo name="RFC" value="7748"/> 1201 <seriesInfo name="DOI" value="10.17487/RFC7748"/> 1202 </reference> 1203 <reference anchor="RFC8032"> 1204 <front> 1205 <title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title> 1206 <author fullname="S. Josefsson" initials="S." surname="Josefsson"/> 1207 <author fullname="I. Liusvaara" initials="I." surname="Liusvaara"/> 1208 <date month="January" year="2017"/> 1209 <abstract> 1210 <t>This document describes elliptic curve signature scheme Edwards-curve Digital Signature Algorithm (EdDSA). The algorithm is instantiated with recommended parameters for the edwards25519 and edwards448 curves. An example implementation and test vectors are provided.</t> 1211 </abstract> 1212 </front> 1213 <seriesInfo name="RFC" value="8032"/> 1214 <seriesInfo name="DOI" value="10.17487/RFC8032"/> 1215 </reference> 1216 <reference anchor="HKDF"> 1217 <front> 1218 <title>Cryptographic Extraction and Key Derivation: The HKDF Scheme</title> 1219 <author fullname="Hugo Krawczyk" initials="H." surname="Krawczyk"> 1220 <organization/> 1221 </author> 1222 <date year="2010"/> 1223 </front> 1224 <seriesInfo name="Lecture Notes in Computer Science" value="pp. 631-648"/> 1225 <seriesInfo name="DOI" value="10.1007/978-3-642-14623-7_34"/> 1226 <seriesInfo name="ISBN" value="["9783642146220", "9783642146237"]"/> 1227 <refcontent>Springer Berlin Heidelberg</refcontent> 1228 </reference> 1229 <reference anchor="SHS"> 1230 <front> 1231 <title>Secure hash standard</title> 1232 <author> 1233 <organization/> 1234 </author> 1235 <date year="2015"/> 1236 </front> 1237 <seriesInfo name="DOI" value="10.6028/nist.fips.180-4"/> 1238 <refcontent>National Institute of Standards and Technology (U.S.)</refcontent> 1239 </reference> 1240 </references> 1241 <?line 1250?> 1242 1243 <section anchor="change-log"> 1244 <name>Change log</name> 1245 </section> 1246 <section numbered="false" anchor="acknowledgments"> 1247 <name>Acknowledgments</name> 1248 <t>[ TBD ]</t> 1249 <t>This work was supported in part by the German Federal Ministry of 1250 Education and Research (BMBF) within the project Concrete Contracts.</t> 1251 </section> 1252 </back> 1253 <!-- ##markdown-source: 1254 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 1406 FZ5tRyY+Bn1DHMcLfCqNm2IpVi42pomOrjYwn6A3VjOViVajYWJZpZVhMhwT 1407 z2TgXSaqlFUzueYc4Bp1ak1VDGAUfhGFU7qdBKKpjmaXBabW5hgOqW2PAiuV 1408 FVOMekhsnYk1hFfuelts6IE8fFfWFarca8l1prYTL1Y8m0KMHEIsDHiTip/0 1409 V8fAu2t7ocyIxIfKIbWrUhjM9vqztMw0Hc2Zxr0yLKmbABYsoEy2AYiWYMXU 1410 3588OzJuQqueO3t6eIwJ+9Pe/rOj0+O9/VOE1rRuSkZW4qm5bOQiq1+K2keE 1411 tjG3VvWL6sm1Op4VonEb5PTZwbMdtUnhsfHjAhM6ZkYPvq9fXznEq4Aiujlg 1412 Hzj9RMbqnClwpd4UGszKpMalNGkXzJTqzI6+CNVOCTjgTeR6PBX77lZnMYjr 1413 vDHMR5wJ4RGvhsvvpHjqZ+cHEV5+ippoPiumk1VlaqpdhwczxTY8DlXAchKw 1414 lpnF2p7eweHzZyePT9vKTLkW5WopVEfbL3yI3KEyqWZ1Suzf1vNkpqNpaSTO 1415 fliTvWovKwfzpagkS5UfZJo3vcT6WYvf6mug06UmVVoRK3t/pq0w2aKMu3Yg 1416 UePoVm+QmTF1qFb+bo48bTWPtSdaxgrISnpnuy3m3FSWZyMC8moSY6KrwmvW 1417 e2b9svovn40HeBkrH93rktFUCn/OnZxX9SZPrSHq2Csjrt8V1dSX6nnAXebd 1418 7kVa5Pnez08P4d9nP36o1lvk6pVq75ogXEuV6YSsQoB4c0un3az9XD8MDY0K 1419 vCsYi/kdR/dhf8OP89Q4HWGEByDw/q0Z3jPOPh/lROnIzGXCZpZvGjP3eRvS 1420 zOf7THJx1V+t7k5/7ehT/3N/9XbIcg3pvyV9q2lb1Vm5EaXXuvLvrepZkbUK 1421 N67qX3+wek7d84PCdXTdtWj++wzXKQjs0oaGp1diw7Oo+oqjh1L4FpJsKcwN 1422 6ZePs9rmdovpcM2yLY05Kue/qczXTcf9wrVcaVs7fPzE1+q4H6Mag8X/PHH/ 1423 QYPysK07ihn5Y8Iy328sOlYioLkI6R0PoOI9t66ZW59VFHRo4cMSJLsYoxq6 1424 mrV1rCC2fLheOLtobxvPz9veVJQsquSY96eXlnOoUa1qDioSaJiydOAWe9Uf 1425 OXpYGTQYBq1EDeXTRWGDXvMHBwaNPp9VNaDDgraNW3IAb67yoFYFotuqg0aq 1426 BsScSVP0BzcxzUInB5c+nMZ4TNoq06dsFe2elmjFKPvSrUuhqR0EKXzw+Php 1427 leg1khty2nhUBTvN9Kvmceo0b6Lu29XBSEOJUJU4ukaoQv3mMq+K+zqnU9Cs 1428 HDSZFVvJfmBC7BPQw6j+JnIsDdE/JjUaRa/qWMyHW8fyDAzlCO/t4k8L6l+S 1429 vkjk5eJCkuUFJA7+9pSuqtD3VqYx3jwAw8wNIPr0reH4Wcc6vIv3UGbSz/lo 1430 rhXeYT0ZHR8PU7q0e8dxtjri2UCfEZ8mQI9zn+t2CSQu0+FiaTpl6VeuYUAg 1431 b+fqYFo45dyyCZZVlYiuH3qH2wfGeZorIWk7UUd29PH6ilXR9TKqWMbZ7gB5 1432 AOs8HdWDDfiMcVS3W5iOPi2rcwq6tIspiyfnMJ+vUKoOKxM1K0S7ra6saDh5 1433 ly88erf6pB3AwFlzxp4uczK0V4DXdh+kH4wMw+F9YyH6H9Jw3t/+5X9aR9LU 1434 0WTQ1XQPB9CPS+PDkoFwQkVGe6q+KQrvOxeRD2jGGzDxmktVkYYT8q2BPCUf 1435 o6yccVQipG6dUUcZVfeITtrzrgAxHP9yAzRALySWIf8yigWGQDD4p9bAAJmT 1436 1YqP8CHVIZ1SFZP5PQmZ5HSIk3ZzKjsqLLFuZbradk+Li6kylSkxWyiVs+1E 1437 eF+fuTWG0R+nJFH0M4p4oMDnn1TCK+JIkkb+ZCITWpUWGD4YTezbJh8MXyp2 1438 1Xe6VXCq7m2snD/WZZm6DL5ex667qBr2dQrX9Sgbg/N8o12dAQaCR/gDAuyS 1439 6hp27oBPVvdYVNJu6tnNKellheoNVerLStQXliPbtchC/ZwBJ2frg5c9ymH1 1440 aZTyFmTQUNPCA9p6wSjFWyYM7xqe57s4MLIa466V6P/lT7t36Ni/xKLKQGou 1441 tFjbQfFoEsIFp1kod0KXJpmCezrZbGpEYQgySRCvCPcvf/K2Wt2//Mloetxc 1442 bON18lSumdDV0fiDhdKP+TYI3GP0r4S88OmnFsxdHdNkQjfSQJyGZYKKgdWd 1443 I3zfAc5xmZKIKJv0VMb6aiBbSamtkzH+wBgrGYCQVsBXSeh7sgyNjYLM2Whc 1444 0H0vfLsOLtGcB0JU6OM2NjbK7djyV+UIcJDrQI+Er1zdu7UmQB9QMnnjJ5d1 1445 WhMwfK2/m07DrDyyJqxiPAJ3rBhlnYk/ZM76gzU6LehZn190cQ1zpS2L6o9Y 1446 1TVsEakobgm0ypIoJdx0FPUzOIiqqXK9xObQH4/9csdS6Qngjxs/lnpN+Bog 1447 XlTsJNw//3FhwpP+PkrWcwX/m3I//Vs4pQ5dMfuHzFl/sHKBC3va04ouK2lv 1448 XuKWiBtaGzRxtHXQYCM+C9G5jrxQYY5e1c3uBXwcySDa/de57bJn/cGqHmXP 1449 a57btu9D73bRmawfBcab0FGcerhv4DQ4lKLST9sgSb9SRs16Rdo7h2DI75Vd 1450 cxxJx1LVmHbpeOUIOKoegGEzm+X4+8LFTGBgKKx76/mnACtRCKVTyt/baasf 1451 +2qLOmjQ1CRXlG9WhgTcqAo3HfGVFz36kdCyaQ5tqw3ZJ+wpR9I07PFB6ZwH 1452 sMS5ETtIpLKNg8j967/9amIG6/cd5j0r+2h+6T/Uzlc30V3HgKqbx5u3RN2N 1453 ds0ZMdFeW5x/obc7ztV2x1/+hPsdixmiPpFu5qlQ3sOfhNiwT3DDAByFJl9w 1454 HDq3uyKaSLxrs4vbAA3nRO2ITPexfzKjoWPbRMI8hFvmcqpBp35W5S07AOUn 1455 WMDVmCvQHewYt8Z/uwsOxRp+ES2Hsx18VL3DyTncENG5tfkTqdc4rGpxswVK 1456 hfvmjuWaRMJbdoQbz4ItOu2bK45tiYZ9ttVn6dc/wW4w2G5i5+kAbdqCA9C0 1457 Ufb08IlJ0FtYUsvXB0obtswUVvT+VYVWzfsVlkTSXoVVaGYoTi5qp9NpCevy 1458 r7Kicv4nNeg3NU4PH4HWSsIYFDMqmaXqaC3n3t561Wdqq/ueZj/Q2vJcsd+5 1459 ctPN2nGztttYhnX9qWLKcqYQ6zJXHNZfi0s+7VF+4p7bH0nerRsNb2P9Hwtw 1460 w2al3qlU177M24gGNT5nhhel/W5IMf6myuvTa46F29r1fTWNAd5Orgmo8Uwo 1461 BO7Rf2GKhSI9ZPRqXwecsNpoyLNneDMg0vzPf8SfB0WWwHPL7KU088Z6txv8 1462 arPFr7apx02Hnvq9ELfq0CwS3LaYF32+7oW3+rWanUNXJRBFLM2HQ7YXkAOo 1463 bXHmx3gTsoFz5SbwQjZRGwh//uOia0wW7m9X9L/yTtVPnXV1p54/iehSIYrM 1464 O8GOYAPBkcUZ/lzsCLTQyt5WeI+DTGSGVxL0dMJ9uePbmOMovd99dUjAqlYl 1465 pfneKAU+ve7mdI07wMwidLhRC6ip3KtRU46k2c/F73Zh1h3a1aimMfCCCds7 1466 Xkw623rzKGsY8YUZB9tyX0NDrMP7i/UHo5xsPm63G98FA8JViPvPFYX0tDL+ 1467 jxle3L5hE72WjdauYekm7Frfbtdl3LIAhhr8QyS991KwNRVpiXAvPV+uIN/n 1468 7p1f/7rO9Tu/zsW5NbN7nUt4fq1Xw2kYFl3F86tVMFi/jWfukpxf/1q5J+eW 1469 eILlCG9uUQHC6hMZN7Tlt3qrjtvVmAiJpEpEa3y75t+HJkohmhtFOeFDXbf9 1470 uSZK6w9UFpouS8y7b7Q+eNfV6+EGq64NwD+3doDgI0Db3O7GdhDoIk9Wxcpl 1471 itlWf847CEg+lxQducIffe9gzWtl9O2WfvDP0yiPPuNrZa65d1A1TCtSznWt 1472 sOhsHQWzjx6fnD47/rl3fPgPLw5PTttC/TwP2uq1klnzdnNFCqqZ29f2XllX 1473 LPZdTbhbRmqNYS82QttquBjdAhzU9Gsav+VICAOX97smThq5tqTZapi+xwUa 1474 cJybiJ5hQDNlgz+yKiW2VjZpqVONv5lucdr8CtbK7f/Hdbf/KyFmx/Ioz/Ny 1475 oeoNZbiAAT+OV33TfvVNeNbI8FMIvqkAHz6UzrV6G6Qgw/gWP9TevuS7povU 1476 40/i+RTIU95pebl96U2mVNS/olscz3WL40o3/LnxaRYVM/xl9jwK1UUnueP8 1477 /pU4/eFA/P41tnqMP59Wb3EECr3jOOiaDPzgHNvtc+V9nA7x216gC5Tph+NB 1478 B/MPcslwd4OyexvvKvPQ7eOXaXYOkQVeBkO/Mc53zFDhvipTfyizsZ+IBxJh 1479 icVTzLAVGVaYO4cQsJSXbB/LXPpZMBLuD09/eNAic19ebYM/wIFrQt6S+IFy 1480 9XnH+f9L/aigpJ4AAA== 1481 1482 --> 1483 1484 </rfc>