- Timestamp:
- 04/23/09 18:42:34 (3 years ago)
- Location:
- trunk/sandbox/jhb/oc2
- Files:
-
- 2 modified
-
documentation.py (modified) (18 diffs)
-
protocols.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/sandbox/jhb/oc2/documentation.py
r275 r276 1 1 """ 2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 3 4 This is just a todo for jhb, and not the real spec 5 6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 7 8 OpenCoin Project N. Toedtmann 9 http://opencoin.org/ J. H. Baach 10 Category: Draft M. Ryden 11 12 OpenCoin Formats and Protocol 13 14 Status of this Memo 15 16 This draft is work in heavy progress. Do not consider it's content 17 stable in any sense as long as this note is present. Get in touch 18 with opencoin.org [1] and fetch a recent copy [2]. 19 20 21 Copyright Notice 22 23 Copyright (c) N. Toedtmann, J. H. Baach, M. Ryden (2008). 24 25 26 Abstract 27 28 This document describes the OpenCoin protocol which seeks to 29 implement David Chaum's concept of "untraceable payments" [3]. 30 31 32 ToDo 33 34 - licence of this document (GNU FDL, CC-BY-SA, Public Domain)? 35 - "Introduction", including scope of protocol 36 - JSON, 7-bit ASCII 37 - define token/certificate format, encryption padding 38 - define validity of certificates and tokens 39 - add note on randomness 40 - add PROTOCOL_ERROR 41 - add HANDSHAKE, CONTINUE, GOODBYE; warning that GOODBYE will disappear 42 - throw out reduntant "TRANSFER_TOKEN" explanatoins 43 - add authentication and authorization, at least for "target" 44 when minting 45 - add mandatory trusted channel (Bluetooth, TLS) 46 - reformat this into RFC-XML 47 - add warning on differences to scientific notation 48 49 50 Table of Contents 51 52 1. Introduction 53 1.1 Object of the OpenCoin protocol 54 1.2 Limited scope of the OpenCoin protocol 55 1.3 General Layout of the OpenCoin protocol 56 1.4 Encoding of messages, tokens and certificates 57 2. General guidelines 58 3. The OpenCoin protocol 59 3.1. Issuer setup 60 3.2. Wallet setup 61 3.3. Wallet creates blanks 62 3.3.5."TRANSFER_TOKEN": A generic wallet-issuer request 63 3.4. Wallet send minting request to issuer 64 3.5. Wallet gets token back 65 3.6. Wallet to wallet 66 3.7. Redeeming tokens 67 4. References 68 69 70 1. Introduction 71 72 1.1 Object of the OpenCoin protocol 73 74 The OpenCoin protocol aims to implement David Chaum's concept of "untraceable 75 payments" [3]. The general procedure is this: 76 77 * Minting 78 * A payer creates a yet unsigned, 'blank' token according to the rules 79 published by the issuer. It includes a serial number. 80 * He obfuscates this blank, yielding the 'blind'. He send the blind to the 81 issuer and request signing with a special minting key. 82 * If the issuer's requirements for minting (which may include a payment) 83 are met, he signs the payer's blind with the nominated minting key. 84 * The payer fetches the signed blind from the issuer and 'unblinds'. The 85 result is a token including a valid signature from the issuer. 86 87 * Spending 88 A payer sends the token to a payee. The payee verifies that the token is 89 valid according to the issuer's rules (format, data, signature, ...). In 90 the standard online case, he also checks it against the issuer's double 91 spending database (DSDB). He tells the payer if he accepts the token. 92 93 * Redemption 94 The payee sends the token to the issuer. The issuer verifies that the 95 token is valid and checks it against his DSDB. If he accepts the token, 96 he adds its serial number to the DSDB. He may offer the payee something 97 in exchange for the token (like a payment). 98 99 In the standard case of online payment, spending and redemption are actually 100 entwined to one simultanious operation. 101 102 Tokens include a reference to this protocol, a reference to the issuer, a 103 denomination, a random serial and the mint's signature over this data. The 104 minting key used to sign the token is deticated to mint exclusivly tokens 105 of this denomination. 106 107 This protocol is designed such that tokens are unforgable and untracable: 108 109 * Unforgeability/balance 110 Without knowledge of the issuer's private minting keys, no combination of 111 payers and payees can successfully redeem tokens of a total denomination 112 higher than the total denomination of tokens minted by the issuer for them. 113 In Particular, no one (except the issuer) can produce N+1 valid tokens 114 from N valid tokens ('one-more-forgery'). 115 116 * Untraceability 117 No combination of the issuer and a set of payees are able to correlate 118 blinds and tokens of a payer just by looking at them (but maybe by traffic 119 analysis). 120 121 122 1.2 Limited scope of the OpenCoin protocol 123 124 [ToDo] 125 126 127 1.3 General Layout of the OpenCoin protocol 128 129 The OpenCoin protocol typically involves three parties: the issuer, a sender/ 130 payer (Alice) and a receiver/payee (Bob). We call the OpenCoin user agents of 131 payer and payee 'wallets'. The issuer consists of four parts: 132 * The 'master key holder' (MHK) generates and keeps the master key pair 133 and signes and publishes the 'currency description document' (CDD) and 134 all the certificates. 135 * The mint generates and keeps the minting keys and signes blinds. 136 * The 'double spending database' (DSDB) keeps track of the serials of 137 tokens which got redeemed. 138 * The 'issuer service' (IS) is the public interface of the issuer on the 139 internet. 140 141 The participants send each other messages in request/response pairs. The 142 universal scheme is this: 143 144 * session initiation * 145 146 -- [ HANDSHAKE, DATA ] --> 147 <-- [ HANDSHAKE, null ] -- 148 149 -- [ REQUEST_1, DATA ] --> 150 <-- [ RESPONSE_1,DATA ] -- 151 152 -- [ REQUEST_2, DATA ] --> 153 <-- [ RESPONSE_2,DATA ] -- 154 155 -- [ CONTINUE, null ] --> 156 <-- [ CONTINUE, null ] -- 157 158 * pause * 159 160 -- [ REQUEST_3, DATA ] --> 161 <-- [ RESPONSE_3,DATA ] -- 162 163 -- [ REQUEST_4, DATA ] --> 164 <-- [ RESPONSE_4,DATA ] -- 165 166 -- [ GOODBYE, null ] --> 167 <-- [ GOODBYE, null ] -- 168 169 * session termination * 170 171 The standard case involves three sessions: 172 173 Payer Alice --[minting]---------------------------------> Issuer 174 Payer Alice --[spending]--> Payee Bob --[redemption]--> Issuer 175 176 the latter two usually happening at the same time ('online case'). 177 178 179 1.4 Encoding of messages, tokens and certificates 180 181 [ToDo] 182 183 184 185 2. General guidelines 186 187 [ToDo] 188 189 190 3. The OpenCoin protocol 191 192 3.1 Issuer setup 193 194 * issuer generates master key pair (ALG,pM,sM) 195 ############################################################################### 196 197 >>> from storage import Item 2 Setup an issuer 3 198 4 >>> from issuer import Issuer 199 5 >>> issuer = Issuer({}) 200 6 >>> issuer.createMasterKeys() 201 7 202 ############################################################################### 203 204 * issuer sets up "currency description document" = CDD (like a root certificate) 205 206 { 207 standard version = http://opencoin.org/OpenCoinProtocol/1.0 208 currency identifier = http://opencent.net/OpenCent 209 short currency identifier = OC 210 issuer service location = opencoin://issuer.opencent.net:8002 211 denominations = strlist(1, 2, 5, 10, 20, 50, 100, 200, 500, 1000) #list of strings seperated by commas 212 issuer cipher suite = HASH-ALG, SIGN-ALG, BLINDING-ALG 213 issuer public master key = base64(pM) 214 215 issuer = base64(hash(pM)) 216 base64(sig(sM,hash(content part))) 217 } 218 219 (question: is the "short currency identifier" needed?) 220 (question: "not use after") 221 (future: add additionial signatures, e.g. from wallet software vendors (set up in containers already)) 222 223 224 ############################################################################### 8 9 10 issuer sets up "currency description document" = CDD (like a root certificate) 225 11 226 12 >>> port = 9090 227 13 >>> denominations = [0,1,2,5,10,20] 228 >>> cdd = issuer.makeCDD('OpenCentA', 229 ... 'oca', 230 ... [str(d) for d in denominations], 231 ... 'http://localhost:%s/' % port, 232 ... '') 14 >>> cdd = issuer.makeCDD('OpenCentA','oca',[str(d) for d in denominations],'http://localhost:%s/' % port,'') 233 15 >>> issuer.getMasterPubKey().verifyContainerSignature(cdd) 234 16 True 235 17 236 ############################################################################### 237 238 239 * issuer publishes CDD at "currency identifier" URL 240 241 * mint (regularily) creates keypairs (pP,sP) for all denominations and id(p). 242 Master key holder generates keys certificate 243 244 { 245 key identifier = base64(hash(pP)) 246 currency identifier = http://opencent.net/OpenCent 247 denomination = denomination 248 not_before = TIME(...) 249 key_not_after = TIME(...) 250 token_not_after = TIME(...) 251 public key = base64(pP) 252 253 issuer = base64(hash(pM)) 254 base64(sig(sM, hash(content part))) 255 } 256 257 258 ############################################################################### 18 19 20 mint (regularily) creates keypairs (pP,sP) for all denominations and id(p). 21 Master key holder generates keys certificate 259 22 260 23 >>> from mint import Mint … … 266 29 True 267 30 268 ############################################################################### 269 270 271 Questions: 272 * CDD? 273 274 * issuer fires up issuer service (=IS) at <opencoin://issuer.opencent.net:8002> 275 276 277 3.2 Wallet setup 278 279 * fetch "currency description document" from issuer 280 281 ############################################################################### 31 32 33 Wallet fetches cdd from issuer 282 34 283 35 >>> #faked request … … 300 52 True 301 53 302 ############################################################################### 303 304 305 3.3 Wallet creates blanks 306 307 * Wallet: fetches current public minting keys for denomination 308 309 Wallet: 310 MINTING_KEY_FETCH_DENOMINATION(denomination) or MINTING_KEY_FETCH_KEYID(key_id) 311 IS: 312 MINTING_KEY_PASS(keycertificate) or MINTING_KEY_FAILURE(reason) 313 314 ############################################################################### 54 55 56 Wallet: fetches current public minting keys for denomination 315 57 316 58 >>> clientside = protocols.FetchMintKeys(transport,denominations=['1','5']) … … 320 62 True 321 63 322 ############################################################################### 323 324 * Wallet: creates blank according to CDD: 325 326 { 327 standard identifier = http://opencoin.org/OpenCoinProtocol/1.0 328 currency identifier = http://opencent.net/OpenCent 329 denomination = denomination 330 key identifier = key_id(signing key) 331 serial = base64(128bit random number) 332 } 333 334 #XXX: ist key id wirklich vom secret key, oder vom public key? 335 336 337 * Wallet: create random r, calculate 338 339 blind = blinding(r, pub_minting_key, hash(blank)) 340 341 Calculate a collision-free random transaction ID (128 bit) 342 343 Keep (r, blank, blind) in mind. 344 345 ############################################################################### 64 65 66 Wallet creates blank and blinds it 346 67 347 68 >>> mkc = mkcs[1] … … 354 75 >>> secret, blind = key.blindBlank(blank) 355 76 >>> tid = wallet.makeSerial() 356 357 77 >>> int(mkc.denomination) 358 78 5 359 79 360 ############################################################################### 361 362 363 3.3.5 "TRANSFER_TOKEN": A generic wallet-issuer request 364 365 The atom for this transaction is a list of tokens - if one of the tokens /blanks 366 fail, the whole transaction fails. 367 368 * Client sends 369 370 TRANSFER_TOKEN_REQUEST( 371 transaction_id, target, list_of_blinds+keyids, 372 list_of_tokens, list_of_options ) 373 374 [ WARNING: In future versions of this protocol, it wll change to 375 TRANSFER_TOKEN_REQUEST ( 376 transaction_id, list_of_options, 377 target, list_of_blinds+keyids, list_of_tokens ) 378 ] 379 380 to IS (issuer service), where 381 382 * transaction_id is a base64(random(128bit)) referencing this transaction 383 e.g. for later resume after an abort. 384 * list_of_option may contain variable=value pairs like "JITM=mandatory". 385 It must contain the option "type", which can have three values: 386 * mint : A minting request. target is a payment reference, 387 list_of_tokens must be empty. 388 * redeem : A token redemption. target is an account reference, 389 list_of_blinds+keyids must be empty. 390 * exchange: A request to mint new tokens for old ones. target must be 391 empty, value of blinds must equal value of tokens. 392 393 If at least one of the blinds or tokens is rejected, the issuer answers 394 395 TRANSFER_TOKEN_REJECT( 396 transaction_id, reason, 397 list( (blind1.key_id, reason1), ... ), 398 list( (token1.key_id, reason1), ... ) ) 399 400 where "reason" may be some general failure like "500 minting not available". 401 If the request is accepted with no delay, IS answers 402 403 TRANSFER_TOKEN_ACCEPT( 404 transaction_id, message, list_of_signed_blinds) 405 406 (with list_of_singed_blinds empty if no minting was required) 407 If minting was requested and acccepted but postponed, IS answers 408 409 TRANSFER_TOKEN_DELAY( transaction_id, message ) 410 411 In this case, the wallet can fetch the signed blinds later by 412 413 TRANSFER_TOKEN_RESUME( transaction_id ) 414 415 416 3.4 Wallet send minting request to issuer 417 418 * Send 419 420 TRANSFER_TOKEN_REQUEST( transaction_id, target, 421 list_of_blinds+keyids, (empty list) , list_of_options, ) 422 423 to issuer service 424 425 426 ############################################################################### 80 81 82 Lets try to get a coin minted 427 83 428 84 We first need to setup an authorizer, to (surpise) authorize the request. Nils says … … 436 92 >>> clientside = protocols.TransferRequest(transport,tid,'foo',[[mkc.keyId,blind]],[]) 437 93 438 ############################################################################### 439 440 * Issuer: if request will not be minted (e.g., "Bad Key ID" if the key_id 441 is not current): 442 443 TRANSFER_TOKEN_REJECT( transaction_id, reason, 444 list( (blind1.key_id, reason1), ... ), (empty list1) ) 445 446 ############################################################################### 447 >>> import time 448 >>> time.sleep(1) 94 Lets have the authorizer denying the request 95 449 96 >>> authorizer.deny = True 450 97 >>> testserver.run_once(port,mint=mint,authorizer=authorizer) … … 452 99 'TransferReject' 453 100 454 455 ############################################################################### 456 457 ElseIf minting is done just-in-time, IS answers 458 459 TRANSFER_TOKEN_ACCEPT( transaction_id, message, list_of_signed_blinds) 460 461 ############################################################################### 101 Now have a well working one 462 102 463 103 >>> authorizer.deny = False … … 466 106 >>> response.header 467 107 'TransferAccept' 108 109 And check it 110 468 111 >>> blindsign = response.signatures[0] 469 112 >>> blank.signature = key.unblind(secret,blindsign) … … 477 120 >>> mint.addToTransactions = issuer.addToTransactions 478 121 479 480 ############################################################################### 481 482 Else IS queues blind to the mint and tells wallet to wait 483 484 TRANSFER_TOKEN_DELAY( transaction_id, reason ) 485 486 487 ############################################################################### 122 The mint can also be a bit slow 488 123 489 124 >>> mint.delay = True … … 491 126 >>> clientside.run().header 492 127 'TransferDelay' 128 493 129 >>> mint.delay = False 494 130 495 ############################################################################### 496 497 498 Session is terminated. 499 500 501 In case of delayed minting, mint processes request (signs blind with key_id) 502 some time later and passes "signed blind"="blind token" back to IS 503 504 505 506 507 508 509 510 3.5 Wallet gets token back 511 512 * Wallet asks issuer service 513 514 TRANSFER_TOKEN_RESUME( transaction_id ) 515 516 * IS either rejects finally 517 518 TRANSFER_TOKEN_REJECT( transaction_id, reason, 519 list( (blind1.key_id, reason1), ... ), (empty list) ) 520 521 with reasons like "TID Unknown", "TID expired", "TID rejected", ..., 522 or tells to wait longer 523 524 TRANSFER_TOKEN_DELAY( transaction_id, reason ) 525 526 ############################################################################### 131 Or the issuer is slow 527 132 528 133 >>> issuer.delay = True … … 533 138 >>> issuer.delay = False 534 139 535 ############################################################################### 536 537 (question: what about key expiration while request is in mining queue) 538 (oierw thinks: as long as the key is valid for minting when the request is made, we are good) 539 540 or passes signed blinds to wallet Bob, must preserve order 541 542 TRANSFER_TOKEN_ACCEPT( transaction_id, message, list_of_singed_blinds ) 543 544 545 ############################################################################### 140 So we need to resume 546 141 547 142 >>> clientside = protocols.TransferResume(transport,tid) … … 551 146 'TransferAccept' 552 147 553 ############################################################################### 554 555 Session terminates 556 557 * wallet checks if blind fits request id and if blind was correctly signed. 558 If not, delete blind and inform user (optional: inform issuer about error) 559 (optional: if yes, inform issuer that he may delete the request) 560 561 * Wallet unblinds signed blind and yields token (or reblinds) 562 563 ############################################################################### 148 And we have a valid coin 564 149 565 150 >>> blindsign = response.signatures[0] … … 569 154 True 570 155 571 ############################################################################### 572 573 574 3.6 Wallet to wallet 575 576 Alice - sends a token 577 Bob - receives the token 578 579 [ Warning: 580 The messages "SPEND_TOKEN_*" may get exchanged by "TRANSFER_TOKEN_*" of 581 type "redeem" or "spend" in future versions of this protocol. ] 582 583 * Prerequisites 584 * Wallet Alice locates Wallet Bob and sets up (secure) connection 585 * Alice knows how much to send and tells her Wallet 586 * Wallet Alice calculates a splitting of sum into tokens (units) 587 and reates a list of tokens to send 588 * Wallet Alice and Wallet Bob are synchronized to UTC (within some small 589 margin of error) 590 591 592 * [ToDo] Handshake 593 594 595 * Wallet Alice announces sum of tokens she wishes to spend for a certain 596 prupose=target, wallet Bob decides if it is going to accept them: 597 598 A: SUM_ANNOUNCE( transaction_id, sum, target ) 599 B: SUM_ACCEPT( transaction_id ) 600 or SUM_REJECT( transaction_id, "Reason" ) 601 602 ############################################################################### 156 157 158 Now, wallet to wallet. We setup an alice and a bob side. Alice announces 159 a sum, and bob dedices if he wants to accept it 603 160 604 161 >>> bobport = 9091 … … 616 173 True 617 174 618 ############################################################################### 619 620 * Wallet Alice sends tokens to Wallet Bob (this time including their clear 621 serial and signature) 622 623 A: SPEND_TOKEN_REQUEST( transaction_id, list(token1, ...) ) 624 625 * The atom for a SPEND_TOKEN_REQUEST is the entire list of tokens 626 627 * Wallet Bob checks if the sum of their values matches the announced sum, if 628 they are valid and (if the former tests do not fail) tries itself to spent 629 the tokens at the issuer with a TRANSFER_TOKEN_REQUEST of type "redeem" or 630 "exchange", using a new, different transaction_id. If one of these fail, 631 wallet Bob rejects the request it with a reason/reasons, otherwise accepts 632 them: 633 634 B: SPEND_TOKEN_REJECT( transaction_id, list( (tokenN, "ReasonN") ) ) 635 or SPEND_TOKEN_REJECT( transaction_id, emptylist, "Reason") 636 or SPEND_TOKEN_ACCEPT( transaction_id ) 637 638 Possible reasons are "unknown", "invalid" ... [ToDo]. 639 640 In case of rejection, wallet Alice itself should immediatly exchange these 641 tokens at the issuer as an emergancy countermeasure against token theft. 642 643 In case of acceptance, wallet Alice must delete all instances of the spent 644 tokens. 645 646 ############################################################################### 175 176 177 Wallet Alice sends tokens to Wallet Bob (this time including their clear 178 serial and signature) 179 180 Lets have first a wrong transactionId 647 181 648 182 >>> bob = protocols.SpendListen(bobwallet) … … 653 187 .... 654 188 SpendReject: unknown transactionId 189 190 Or lets try to send a wrong amount 655 191 656 192 >>> alice = protocols.SpendRequest(bob.run, wallet, alicetid, []) … … 683 219 684 220 Lets try to double spend 221 685 222 >>> import messages 686 223 >>> bobblank = bobwallet._makeBlank(cdd,mkc) … … 695 232 696 233 697 ############################################################################### 698 699 3.7 Redeeming tokens 700 701 * Wallet sends tokens + target to IS 702 703 W: TRANSFER_TOKEN_REQUEST( 704 transaction_id, list_of_options, target, (empty list), list_of_tokens 705 ) 706 707 target may be an account and is of the form: 708 709 MINT_REQUEST=#base64(request_id) 710 ONLINE_BANKING_ACCOUNT=#string(account_identifier) 711 and so on... to be defined with relationship between IS and individual 712 713 714 * IS checks if tokens and target are valid 715 - if minting keys are still valid (XXX token has not expired) 716 - if serial is still valid (against DSDB) 717 - if signature is valid 718 719 If not, IS rejects with reason (key id unknown, token outdated, token spent, 720 signature invalid) per token or sweeping 721 722 IS: TRANSFER_TOKEN_REJECT( 723 transaction_id, reason, (empty list), list( (token1.key_id, reason1), ... ) ) 724 ) 725 726 If tokens and target are valid, IS enters the serials of the tokens into the 727 DSDB, servers the target and replies 728 729 IS: TRANSFER_TOKEN_ACCEPT( 730 transaction_id, message, (empty list) 731 ) 732 733 734 ############################################################################### 234 Last step - bob wants to redeem the coins 735 235 736 236 >>> bobtid = wallet.makeSerial() … … 740 240 'TransferAccept' 741 241 742 743 ###############################################################################744 745 746 747 4. References748 749 [1] The OpenCoin project <http://opencoin.org/>750 751 [2] The OpenCoin project, "OpenCoin protocol v1.0"752 <https://trac.opencoin.org/trac/opencoin/browser/trunk/standards/protocol.txt>753 754 [3] David Chaum, "Blind signatures for untraceable payments", Advances755 in Cryptology - Crypto '82, Springer-Verlag (1983), 199-203.756 757 [RFC4086] D. Eastlake, J. Schiller and S. Crocker, "Randomness Requirements758 for Security", RFC 4086, June 2005759 760 [RFC4627] D. Crockford, "The application/json Media Type for JavaScript761 Object Notation (JSON)", RFC 4627, July 2006762 242 """ 763 243 -
trunk/sandbox/jhb/oc2/protocols.py
r275 r276 244 244 245 245 246 class CoinsSpendSender(Protocol):247 248 def __init__(self,coins,target):249 self.coins = coins250 self.target = target251 252 def spendCoins(self,message):253 return ''254 255 def announceCoins(self,message):256 return ''257 258 259 260 class CoinsSpendRecipient(Protocol):261 262 def hearCoins(self,message):263 return ''264 265 def receiveCoins(self,message):266 return ''267 268 269
