mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 09:28:13 +00:00
coins: validate derivation paths
Based on SLIP-44 ids and other checks. See docs/coins/README for info.
This commit is contained in:
parent
ab25381646
commit
31f987e988
@ -4,31 +4,47 @@ Each coin uses [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.med
|
|||||||
|
|
||||||
## List of used derivation paths
|
## List of used derivation paths
|
||||||
|
|
||||||
| coin | curve | getPublicKey | getAddress | sign | derivation | note |
|
| coin | curve | getPublicKey | getAddress | sign tx | derivation | note |
|
||||||
|----------------|----------------|----------------|------------------|------------------|-----------------|--------------|
|
|----------------|----------------|----------------|------------------|------------------|-----------------|--------------|
|
||||||
| Bitcoin | secp256k | 44'/0'/a' | 44'/0'/a'/y/i | 44'/0'/a'/y/i | BIP-32 | [6](#BitcoinDiagram) |
|
| Bitcoin | secp256k | 44'/c'/a' | 44'/c'/a'/y/i | 44'/c'/a'/y/i | BIP-32 | [1](#Bitcoin) |
|
||||||
| Ethereum | secp256k | 44'/60'/0' | 44'/60'/0'/0/i | 44'/60'/0'/0/i | BIP-32 | [1](#Ethereum)|
|
| Ethereum | secp256k | 44'/60'/0' | 44'/60'/0'/0/i | 44'/60'/0'/0/i | BIP-32 | [2](#Ethereum)|
|
||||||
| Ripple | secp256k | - | 44'/144'/a'/0/0 | 44'/144'/a'/0/0 | BIP-32 | [2](#Ripple) |
|
| Ripple | secp256k | - | 44'/144'/a'/0/0 | 44'/144'/a'/0/0 | BIP-32 | [3](#Ripple) |
|
||||||
| Cardano | ed25519 | 44'/1815'/a' | 44'/1815'/a'/0/i | 44'/1815'/a'/0/i | [Cardano's own](https://cardanolaunch.com/assets/Ed25519_BIP.pdf)<sup>[3](#Cardano)</sup> | |
|
| Cardano | ed25519 | 44'/1815'/a' | 44'/1815'/a'/{0,1}/i | 44'/1815'/a'/{0,1}/i | [Cardano's own](https://cardanolaunch.com/assets/Ed25519_BIP.pdf)<sup>[4](#Cardano)</sup> | |
|
||||||
| Stellar | ed25519 | - | 44'/148'/a' | 44'/148'/a' | SLIP-0010 | |
|
| Stellar | ed25519 | - | 44'/148'/a' | 44'/148'/a' | SLIP-0010 | |
|
||||||
| Lisk | ed25519 | 44'/134'/a' | 44'/134'/a' | 44'/134'/a' | SLIP-0010 | |
|
| Lisk | ed25519 | 44'/134'/a' | 44'/134'/a' | 44'/134'/a' | SLIP-0010 | |
|
||||||
| NEM | ed25519 | - | 44'/43'/a' | 44'/43'/a' | SLIP-0010 | [4](#NEM) |
|
| NEM | ed25519 | - | 44'/43'/a' | 44'/43'/a' | SLIP-0010 | [5](#NEM) |
|
||||||
| Monero | ed25519 | 44'/128'/a'<sup>[5](#Monero)</sup> | 44'/128'/a' | 44'/128'/a' | SLIP-0010 | |
|
| Monero | ed25519 | 44'/128'/a'<sup>[6](#Monero)</sup> | 44'/128'/a' | 44'/128'/a' | SLIP-0010 | |
|
||||||
|
| Tezos | ed25519<sup>[7](#Tezos)</sup> | 44'/1729'/a' | 44'/1729'/a' | 44'/1729'/a' | SLIP-0010 | |
|
||||||
|
|
||||||
Paths that do not conform to this table are allowed, but user needs to confirm a warning on Trezor. For getPublicKey we do not check if the path is followed by other non-hardened items (anyone can derive those anyway). This is beneficial for Ethereum and its MEW compatibility, which sends `44'/60'/0'/0` for getPublicKey.
|
Paths that do not conform to this table are allowed, but user needs to confirm a warning on Trezor. For getPublicKey we do not check if the path is followed by other non-hardened items (anyone can derive those anyway). This is beneficial for Ethereum and its MEW compatibility, which sends `44'/60'/0'/0` for getPublicKey.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
1. <a name="Ethereum"></a> We believe this should be `44'/60'/a'`, because Ethereum is account-based, rather than UTXO-based. Unfortunately, lot of Ethereum tools (MEW, Metamask) do not use such scheme and set `a = 0` and then iterate the address index `i`. Therefore for compatibility reasons we use the same scheme: `44'/60'/0'/0/i` and only the `i` is being iterated.
|
1. <a name="Bitcoin"></a> For Bitcoin and its derivatives it is a little bit more complicated. `p` is decided based on the following table:
|
||||||
|
|
||||||
2. <a name="Ripple"></a> Similar to Ethereum this should be `44'/144'/a'`. But for compatibility with other HW vendors we use `44'/144'/a'/0/0`.
|
| p | type | input script type |
|
||||||
|
|-----|--------------|--------------------|
|
||||||
|
| 44 | legacy | SPENDADDRESS |
|
||||||
|
| 48 | multisig | SPENDMULTISIG |
|
||||||
|
| 49 | p2sh segwit | SPENDP2SHWITNESS |
|
||||||
|
| 84 | native segwit | SPENDWITNESS |
|
||||||
|
|
||||||
3. <a name="Cardano"></a> Which allows non-hardened derivation on ed25519.
|
Other `p` are disallowed. `c` has to be equal to the coin's [slip44 id](https://github.com/satoshilabs/slips/blob/master/slip-0044.md) as for every coin. `y` needs to be 0 or 1.
|
||||||
|
|
||||||
4. <a name="NEM"></a> NEM's path should be `44'/60'/a'` as per SEP-0005, but we allow `44'/60'/a'/0'/0'` as well for compatibility reasons with NanoWallet.
|
2. <a name="Ethereum"></a> We believe this should be `44'/60'/a'`, because Ethereum is account-based, rather than UTXO-based. Unfortunately, lot of Ethereum tools (MEW, Metamask) do not use such scheme and set `a = 0` and then iterate the address index `i`. Therefore for compatibility reasons we use the same scheme: `44'/60'/0'/0/i` and only the `i` is being iterated.
|
||||||
|
|
||||||
5. <a name="Monero"></a> Actually it is GetWatchKey for Monero.
|
3. <a name="Ripple"></a> Similar to Ethereum this should be `44'/144'/a'`. But for compatibility with other HW vendors we use `44'/144'/a'/0/0`.
|
||||||
|
|
||||||
6. <a name="BitcoinDiagram"></a> It is a bit more complicated for Bitcoin-like coins. The following diagram shows how path should be validated for Bitcoin-like coins:
|
4. <a name="Cardano"></a> Which allows non-hardened derivation on ed25519.
|
||||||
|
|
||||||
![bitcoin-path-check](bitcoin-path-check.svg)
|
5. <a name="NEM"></a> NEM's path should be `44'/60'/a'` as per SEP-0005, but we allow `44'/60'/a'/0'/0'` as well for compatibility reasons with NanoWallet.
|
||||||
|
|
||||||
|
6. <a name="Monero"></a> Actually it is GetWatchKey for Monero.
|
||||||
|
|
||||||
|
7. <a name="Tezos"></a> Tezos supports multiple curves, but Trezor currently supports ed25519 only.
|
||||||
|
|
||||||
|
Sign message paths are validated in the same way as the sign tx paths are.
|
||||||
|
|
||||||
|
## Allowed values
|
||||||
|
|
||||||
|
For GetPublicKey `a` needs in the interval of \[0, 20]. For GetAddress and signing the longer five-items paths need to have `a` also in range of \[0, 20] and `i` in \[0, 1000000]. If only three-items paths are used (Stellar and Lisk for example), `a` is allowed to be in \[0, 1000000] (because there is no address index `i`).
|
||||||
|
@ -1,313 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg version="1.2" width="372.36mm" height="127mm" viewBox="0 0 37236 12700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
|
|
||||||
<defs class="ClipPathGroup">
|
|
||||||
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
|
|
||||||
<rect x="0" y="0" width="37236" height="12700"/>
|
|
||||||
</clipPath>
|
|
||||||
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
|
|
||||||
<rect x="37" y="12" width="37162" height="12675"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
<defs>
|
|
||||||
<font id="EmbeddedFont_1" horiz-adv-x="2048">
|
|
||||||
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
|
|
||||||
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
|
|
||||||
<glyph unicode="y" horiz-adv-x="1033" d="M 191,-425 C 142,-425 100,-421 67,-414 L 67,-279 C 92,-283 120,-285 151,-285 263,-285 352,-203 417,-38 L 434,5 5,1082 197,1082 425,484 C 428,475 432,464 437,451 442,438 457,394 482,320 507,246 521,205 523,196 L 593,393 830,1082 1020,1082 604,0 C 559,-115 518,-201 479,-258 440,-314 398,-356 351,-384 304,-411 250,-425 191,-425 Z"/>
|
|
||||||
<glyph unicode="x" horiz-adv-x="1006" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
|
|
||||||
<glyph unicode="w" horiz-adv-x="1509" d="M 1174,0 L 965,0 776,765 740,934 C 734,904 725,861 712,805 699,748 631,480 508,0 L 300,0 -3,1082 175,1082 358,347 C 363,331 377,265 401,149 L 418,223 644,1082 837,1082 1026,339 1072,149 1103,288 1308,1082 1484,1082 1174,0 Z"/>
|
|
||||||
<glyph unicode="u" horiz-adv-x="874" d="M 314,1082 L 314,396 C 314,325 321,269 335,230 349,191 371,162 402,145 433,128 478,119 537,119 624,119 692,149 742,208 792,267 817,350 817,455 L 817,1082 997,1082 997,231 C 997,105 999,28 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 826,132 825,185 L 822,185 C 781,110 733,58 679,27 624,-4 557,-20 476,-20 357,-20 271,10 216,69 161,128 133,225 133,361 L 133,1082 314,1082 Z"/>
|
|
||||||
<glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/>
|
|
||||||
<glyph unicode="s" horiz-adv-x="901" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
|
|
||||||
<glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
|
|
||||||
<glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 488,-20 376,43 319,168 L 314,168 C 317,163 318,106 318,-2 L 318,-425 138,-425 138,861 C 138,972 136,1046 132,1082 L 306,1082 C 307,1079 308,1070 309,1054 310,1037 312,1012 314,978 315,944 316,921 316,908 L 320,908 C 352,975 394,1024 447,1055 500,1086 569,1101 655,1101 788,1101 888,1056 954,967 1020,878 1053,737 1053,546 Z M 864,542 C 864,693 844,800 803,865 762,930 698,962 609,962 538,962 482,947 442,917 401,887 371,840 350,777 329,713 318,630 318,528 318,386 341,281 386,214 431,147 505,113 607,113 696,113 762,146 803,212 844,277 864,387 864,542 Z"/>
|
|
||||||
<glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/>
|
|
||||||
<glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/>
|
|
||||||
<glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
|
|
||||||
<glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
|
|
||||||
<glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 356,968 402,1020 457,1053 511,1086 580,1102 663,1102 780,1102 867,1073 923,1015 978,956 1006,858 1006,721 L 1006,0 825,0 825,686 C 825,762 818,819 804,856 790,893 767,920 735,937 703,954 659,963 602,963 517,963 450,934 399,875 348,816 322,737 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1057 321,1015 319,972 316,929 315,904 314,897 L 317,897 Z"/>
|
|
||||||
<glyph unicode="g" horiz-adv-x="927" d="M 548,-425 C 430,-425 336,-402 266,-356 196,-309 151,-243 131,-158 L 312,-132 C 324,-182 351,-220 392,-248 433,-274 486,-288 553,-288 732,-288 822,-183 822,27 L 822,201 820,201 C 786,132 739,80 680,45 621,10 551,-8 472,-8 339,-8 242,36 180,124 117,212 86,350 86,539 86,730 120,872 187,963 254,1054 355,1099 492,1099 569,1099 635,1082 692,1047 748,1012 791,962 822,897 L 824,897 C 824,917 825,952 828,1001 831,1050 833,1077 836,1082 L 1007,1082 C 1003,1046 1001,971 1001,858 L 1001,31 C 1001,-273 850,-425 548,-425 Z M 822,541 C 822,629 810,705 786,769 762,832 728,881 685,915 641,948 591,965 536,965 444,965 377,932 335,865 293,798 272,690 272,541 272,393 292,287 331,222 370,157 438,125 533,125 590,125 640,142 684,175 728,208 762,256 786,319 810,381 822,455 822,541 Z"/>
|
|
||||||
<glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1303 203,1374 246,1417 289,1460 356,1482 445,1482 495,1482 537,1478 572,1470 L 572,1333 C 542,1338 515,1341 492,1341 446,1341 413,1329 392,1306 371,1283 361,1240 361,1179 L 361,1082 572,1082 572,951 361,951 Z"/>
|
|
||||||
<glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
|
|
||||||
<glyph unicode="d" horiz-adv-x="927" d="M 821,174 C 788,105 744,55 689,25 634,-5 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,913 219,1102 484,1102 566,1102 634,1087 689,1057 744,1027 788,979 821,914 L 823,914 821,1035 821,1484 1001,1484 1001,223 C 1001,110 1003,36 1007,0 L 835,0 C 833,11 831,35 829,74 826,113 825,146 825,174 L 821,174 Z M 275,542 C 275,391 295,282 335,217 375,152 440,119 530,119 632,119 706,154 752,225 798,296 821,405 821,554 821,697 798,802 752,869 706,936 633,969 532,969 441,969 376,936 336,869 295,802 275,693 275,542 Z"/>
|
|
||||||
<glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/>
|
|
||||||
<glyph unicode="b" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 573,-20 505,-5 451,25 396,54 352,102 318,168 L 316,168 C 316,147 315,116 312,74 309,31 307,7 306,0 L 132,0 C 136,36 138,110 138,223 L 138,1484 318,1484 318,1061 C 318,1018 317,967 314,908 L 318,908 C 351,977 396,1027 451,1057 506,1087 574,1102 655,1102 792,1102 892,1056 957,964 1021,872 1053,733 1053,546 Z M 864,540 C 864,691 844,800 804,865 764,930 699,963 609,963 508,963 434,928 388,859 341,790 318,680 318,529 318,387 341,282 386,215 431,147 505,113 607,113 698,113 763,147 804,214 844,281 864,389 864,540 Z"/>
|
|
||||||
<glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
|
|
||||||
<glyph unicode="T" horiz-adv-x="1192" d="M 720,1253 L 720,0 530,0 530,1253 46,1253 46,1409 1204,1409 1204,1253 720,1253 Z"/>
|
|
||||||
<glyph unicode="P" horiz-adv-x="1112" d="M 1258,985 C 1258,852 1215,746 1128,667 1041,588 922,549 773,549 L 359,549 359,0 168,0 168,1409 761,1409 C 919,1409 1041,1372 1128,1298 1215,1224 1258,1120 1258,985 Z M 1066,983 C 1066,1165 957,1256 738,1256 L 359,1256 359,700 746,700 C 959,700 1066,794 1066,983 Z"/>
|
|
||||||
<glyph unicode="O" horiz-adv-x="1430" d="M 1495,711 C 1495,564 1467,435 1411,324 1354,213 1273,128 1168,69 1063,10 938,-20 795,-20 650,-20 526,9 421,68 316,127 235,212 180,323 125,434 97,563 97,711 97,936 159,1113 282,1240 405,1367 577,1430 797,1430 940,1430 1065,1402 1170,1345 1275,1288 1356,1205 1412,1096 1467,987 1495,859 1495,711 Z M 1300,711 C 1300,886 1256,1024 1169,1124 1081,1224 957,1274 797,1274 636,1274 511,1225 423,1126 335,1027 291,889 291,711 291,534 336,394 425,291 514,187 637,135 795,135 958,135 1083,185 1170,286 1257,386 1300,528 1300,711 Z"/>
|
|
||||||
<glyph unicode="K" horiz-adv-x="1191" d="M 1106,0 L 543,680 359,540 359,0 168,0 168,1409 359,1409 359,703 1038,1409 1263,1409 663,797 1343,0 1106,0 Z"/>
|
|
||||||
<glyph unicode="F" horiz-adv-x="1006" d="M 359,1253 L 359,729 1145,729 1145,571 359,571 359,0 168,0 168,1409 1169,1409 1169,1253 359,1253 Z"/>
|
|
||||||
<glyph unicode="=" horiz-adv-x="1033" d="M 100,856 L 100,1004 1095,1004 1095,856 100,856 Z M 100,344 L 100,492 1095,492 1095,344 100,344 Z"/>
|
|
||||||
<glyph unicode="<" horiz-adv-x="1033" d="M 101,571 L 101,776 1096,1194 1096,1040 238,674 1096,307 1096,154 101,571 Z"/>
|
|
||||||
<glyph unicode="9" horiz-adv-x="980" d="M 1042,733 C 1042,491 998,305 910,175 821,45 695,-20 532,-20 422,-20 334,3 268,50 201,96 154,171 125,274 L 297,301 C 333,184 412,125 535,125 638,125 718,173 775,269 832,365 861,502 864,680 837,620 792,572 727,536 662,499 591,481 514,481 387,481 286,524 210,611 134,698 96,813 96,956 96,1103 137,1219 220,1304 303,1388 418,1430 565,1430 722,1430 840,1372 921,1256 1002,1140 1042,966 1042,733 Z M 846,907 C 846,1020 820,1112 768,1181 716,1250 646,1284 559,1284 472,1284 404,1255 354,1196 304,1137 279,1057 279,956 279,853 304,772 354,713 404,653 472,623 557,623 609,623 657,635 702,659 747,682 782,716 808,759 833,802 846,852 846,907 Z"/>
|
|
||||||
<glyph unicode="8" horiz-adv-x="980" d="M 1050,393 C 1050,263 1009,162 926,89 843,16 725,-20 570,-20 419,-20 302,16 217,87 132,158 89,260 89,391 89,483 115,560 168,623 221,686 288,724 370,737 L 370,741 C 293,759 233,798 189,858 144,918 122,988 122,1069 122,1176 162,1263 243,1330 323,1397 431,1430 566,1430 705,1430 814,1397 895,1332 975,1267 1015,1178 1015,1067 1015,986 993,916 948,856 903,796 842,758 765,743 L 765,739 C 855,724 925,686 975,625 1025,563 1050,486 1050,393 Z M 828,1057 C 828,1216 741,1296 566,1296 481,1296 417,1276 373,1236 328,1196 306,1136 306,1057 306,976 329,915 375,873 420,830 485,809 568,809 653,809 717,829 762,868 806,907 828,970 828,1057 Z M 863,410 C 863,497 837,563 785,608 733,652 660,674 566,674 475,674 403,650 352,603 301,555 275,489 275,406 275,212 374,115 572,115 670,115 743,139 791,186 839,233 863,307 863,410 Z"/>
|
|
||||||
<glyph unicode="5" horiz-adv-x="980" d="M 1053,459 C 1053,310 1009,193 921,108 832,23 710,-20 553,-20 422,-20 316,9 235,66 154,123 103,206 82,315 L 264,336 C 302,197 400,127 557,127 654,127 729,156 784,215 839,273 866,353 866,455 866,544 839,615 784,670 729,725 654,752 561,752 512,752 467,744 425,729 383,714 341,688 299,651 L 123,651 170,1409 971,1409 971,1256 334,1256 307,809 C 385,869 482,899 598,899 737,899 847,858 930,777 1012,696 1053,590 1053,459 Z"/>
|
|
||||||
<glyph unicode="4" horiz-adv-x="1060" d="M 881,319 L 881,0 711,0 711,319 47,319 47,459 692,1409 881,1409 881,461 1079,461 1079,319 881,319 Z M 711,1206 C 710,1202 700,1184 683,1153 666,1122 653,1100 644,1087 L 283,555 229,481 213,461 711,461 711,1206 Z"/>
|
|
||||||
<glyph unicode="1" horiz-adv-x="927" d="M 156,0 L 156,153 515,153 515,1237 197,1010 197,1180 530,1409 696,1409 696,153 1039,153 1039,0 156,0 Z"/>
|
|
||||||
<glyph unicode="0" horiz-adv-x="980" d="M 1059,705 C 1059,470 1018,290 935,166 852,42 729,-20 567,-20 405,-20 283,42 202,165 121,288 80,468 80,705 80,947 120,1128 199,1249 278,1370 402,1430 573,1430 739,1430 862,1369 941,1247 1020,1125 1059,944 1059,705 Z M 876,705 C 876,908 853,1056 806,1147 759,1238 681,1284 573,1284 462,1284 383,1239 335,1149 286,1059 262,911 262,705 262,505 287,359 336,266 385,173 462,127 569,127 675,127 753,174 802,269 851,364 876,509 876,705 Z"/>
|
|
||||||
<glyph unicode="/" horiz-adv-x="583" d="M 0,-20 L 411,1484 569,1484 162,-20 0,-20 Z"/>
|
|
||||||
<glyph unicode="-" horiz-adv-x="531" d="M 91,464 L 91,624 591,624 591,464 91,464 Z"/>
|
|
||||||
<glyph unicode=")" horiz-adv-x="557" d="M 555,528 C 555,335 525,162 465,9 404,-144 311,-289 186,-424 L 12,-424 C 137,-284 229,-136 287,19 345,174 374,344 374,530 374,716 345,887 287,1042 228,1197 137,1345 12,1484 L 186,1484 C 312,1348 405,1203 465,1050 525,896 555,723 555,532 L 555,528 Z"/>
|
|
||||||
<glyph unicode="(" horiz-adv-x="583" d="M 127,532 C 127,725 157,898 218,1051 278,1204 371,1349 496,1484 L 670,1484 C 545,1345 454,1198 396,1042 337,886 308,715 308,530 308,345 337,175 395,20 452,-135 544,-283 670,-424 L 496,-424 C 370,-288 277,-143 217,11 157,164 127,337 127,528 L 127,532 Z"/>
|
|
||||||
<glyph unicode="'" horiz-adv-x="213" d="M 266,966 L 125,966 104,1409 288,1409 266,966 Z"/>
|
|
||||||
<glyph unicode="!" horiz-adv-x="239" d="M 359,397 L 211,397 187,1409 383,1409 359,397 Z M 185,0 L 185,201 379,201 379,0 185,0 Z"/>
|
|
||||||
<glyph unicode=" " horiz-adv-x="556"/>
|
|
||||||
</font>
|
|
||||||
</defs>
|
|
||||||
<defs class="TextShapeIndex">
|
|
||||||
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30"/>
|
|
||||||
</defs>
|
|
||||||
<defs class="EmbeddedBulletChars">
|
|
||||||
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
|
|
||||||
</g>
|
|
||||||
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
|
|
||||||
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
|
|
||||||
</g>
|
|
||||||
</defs>
|
|
||||||
<defs class="TextEmbeddedBitmaps"/>
|
|
||||||
<g>
|
|
||||||
<g id="id2" class="Master_Slide">
|
|
||||||
<g id="bg-id2" class="Background"/>
|
|
||||||
<g id="bo-id2" class="BackgroundObjects"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="SlideGroup">
|
|
||||||
<g>
|
|
||||||
<g id="container-id1">
|
|
||||||
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
|
|
||||||
<g class="Page">
|
|
||||||
<g class="com.sun.star.drawing.TextShape">
|
|
||||||
<g id="id3">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="1769" y="1700" width="5270" height="963"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="2019" y="2401"><tspan fill="rgb(0,0,0)" stroke="none">Path = p'/c'/a'/y/x</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id4">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="13970" y="2930" width="3178" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 15559,4228 L 13971,4228 13971,2931 17146,2931 17146,4228 15559,4228 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 15559,4228 L 13971,4228 13971,2931 17146,2931 17146,4228 15559,4228 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="14869" y="3751"><tspan fill="rgb(0,0,0)" stroke="none">c == 0</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id5">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="20915" y="5183" width="3052" height="2164"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 22440,5184 L 23965,6264 22440,7345 20916,6264 22440,5184 22440,5184 Z M 20916,5184 L 20916,5184 Z M 23965,7345 L 23965,7345 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 22440,5184 L 23965,6264 22440,7345 20916,6264 22440,5184 22440,5184 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 20916,5184 L 20916,5184 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 23965,7345 L 23965,7345 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="21233" y="6412"><tspan fill="rgb(0,0,0)" stroke="none">uses change</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id6">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="23963" y="6263" width="3122" height="839"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 23964,6264 L 25524,6264 25524,7100 27083,7100"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="25329" y="6903"><tspan fill="rgb(0,0,0)" stroke="none">T</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id7">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="18759" y="2927" width="2073" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 19795,4225 L 18760,4225 18760,2928 20830,2928 20830,4225 19795,4225 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 19795,4225 L 18760,4225 18760,2928 20830,2928 20830,4225 19795,4225 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="19099" y="3748"><tspan fill="rgb(0,0,0)" stroke="none">a < 10</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id8">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="31552" y="2840" width="3178" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 33141,4138 L 31553,4138 31553,2841 34728,2841 34728,4138 33141,4138 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 33141,4138 L 31553,4138 31553,2841 34728,2841 34728,4138 33141,4138 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="31769" y="3661"><tspan fill="rgb(0,0,0)" stroke="none">x < 1000000</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id9">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="27081" y="6450" width="3178" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 28670,7748 L 27082,7748 27082,6451 30257,6451 30257,7748 28670,7748 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 28670,7748 L 27082,7748 27082,6451 30257,6451 30257,7748 28670,7748 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="27482" y="7271"><tspan fill="rgb(0,0,0)" stroke="none">y == 0 or 1</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id10">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="19280" y="8831" width="2643" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 20601,10129 L 19281,10129 19281,8832 21921,8832 21921,10129 20601,10129 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 20601,10129 L 19281,10129 19281,8832 21921,8832 21921,10129 20601,10129 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="19911" y="9652"><tspan fill="rgb(0,0,0)" stroke="none">y == 0</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id11">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="20381" y="6263" width="543" height="2572"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 20916,6264 L 20415,6264 20415,8089 20601,8089 20601,8833"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="20382" y="7769"><tspan fill="rgb(0,0,0)" stroke="none"> </tspan><tspan fill="rgb(0,0,0)" stroke="none">F</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id12">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="6515" y="3782" width="4184" height="2925"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 8606,3783 L 10697,5244 8606,6705 6516,5244 8606,3783 8606,3783 Z M 6516,3783 L 6516,3783 Z M 10697,6705 L 10697,6705 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 8606,3783 L 10697,5244 8606,6705 6516,5244 8606,3783 8606,3783 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 6516,3783 L 6516,3783 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 10697,6705 L 10697,6705 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="7012" y="5392"><tspan fill="rgb(0,0,0)" stroke="none">if segwit-enabled</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id13">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="4381" y="6949" width="3178" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 5970,8247 L 4382,8247 4382,6950 7557,6950 7557,8247 5970,8247 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 5970,8247 L 4382,8247 4382,6950 7557,6950 7557,8247 5970,8247 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="5129" y="7770"><tspan fill="rgb(0,0,0)" stroke="none">p == 44</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id14">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="5969" y="5244" width="549" height="1709"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 6516,5245 L 5970,5245 5970,6951"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="6048" y="6319"><tspan fill="rgb(0,0,0)" stroke="none">F</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id15">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="9781" y="6949" width="3178" height="1300"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 11370,8247 L 9782,8247 9782,6950 12957,6950 12957,8247 11370,8247 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 11370,8247 L 9782,8247 9782,6950 12957,6950 12957,8247 11370,8247 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="9894" y="7770"><tspan fill="rgb(0,0,0)" stroke="none">p == 49 or 84</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id16">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="10695" y="5244" width="677" height="1709"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 10696,5245 L 11370,5245 11370,6951"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="10838" y="6319"><tspan fill="rgb(0,0,0)" stroke="none">T</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id17">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="23515" y="2882" width="4184" height="2925"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 25606,2883 L 27697,4344 25606,5805 23516,4344 25606,2883 25606,2883 Z M 23516,2883 L 23516,2883 Z M 27697,5805 L 27697,5805 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 25606,2883 L 27697,4344 25606,5805 23516,4344 25606,2883 25606,2883 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 23516,2883 L 23516,2883 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 27697,5805 L 27697,5805 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="24012" y="4492"><tspan fill="rgb(0,0,0)" stroke="none">if segwit-enabled</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id18">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="22439" y="4344" width="1079" height="842"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 23516,4345 L 22440,4345 22440,5184"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="22784" y="4985"><tspan fill="rgb(0,0,0)" stroke="none">F</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id19">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="27695" y="4344" width="977" height="2110"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 27696,4345 L 27696,5148 28670,5148 28670,6452"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="27989" y="5619"><tspan fill="rgb(0,0,0)" stroke="none">T</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id20">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="20829" y="2381" width="4779" height="1198"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 20830,3577 L 22173,3577 22173,2382 25606,2382 25606,2884"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id21">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="20600" y="8248" width="10307" height="2384"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 20601,10130 L 20601,10630 30905,10630 30905,8249"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id22">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="28669" y="3489" width="2887" height="4762"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 28670,7749 L 28670,8249 30905,8249 30905,3490 31554,3490"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.LineShape">
|
|
||||||
<g id="id23">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="33209" y="5825" width="301" height="1653"/>
|
|
||||||
<path fill="none" stroke="rgb(0,0,0)" d="M 33359,5826 L 33359,7047"/>
|
|
||||||
<path fill="rgb(0,0,0)" stroke="none" d="M 33359,7477 L 33509,7027 33209,7027 33359,7477 Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.TextShape">
|
|
||||||
<g id="id24">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="32570" y="7585" width="1599" height="963"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="32820" y="8286"><tspan fill="rgb(0,0,0)" stroke="none">OK!</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id25">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="5744" y="3529" width="2864" height="257"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 5745,3530 L 8606,3530 8606,3784"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id26">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="5969" y="3579" width="8005" height="5171"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 5970,8248 L 5970,8748 13444,8748 13444,3580 13972,3580"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id27">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="17146" y="3576" width="1616" height="6"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 17147,3580 L 17953,3580 17953,3577 18760,3577"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id28">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="11369" y="3579" width="2605" height="5171"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 11370,8248 L 11370,8748 13464,8748 13464,3580 13972,3580"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.ConnectorShape">
|
|
||||||
<g id="id29">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="33358" y="3489" width="1873" height="2339"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 34729,3490 L 35229,3490 35229,5826 33359,5826"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g class="com.sun.star.drawing.CustomShape">
|
|
||||||
<g id="id30">
|
|
||||||
<rect class="BoundingBox" stroke="none" fill="none" x="2188" y="2957" width="3559" height="1146"/>
|
|
||||||
<path fill="rgb(255,255,255)" stroke="none" d="M 3967,4101 L 2189,4101 2189,2958 5745,2958 5745,4101 3967,4101 Z"/>
|
|
||||||
<path fill="none" stroke="rgb(52,101,164)" d="M 3967,4101 L 2189,4101 2189,2958 5745,2958 5745,4101 3967,4101 Z"/>
|
|
||||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2424" y="3701"><tspan fill="rgb(0,0,0)" stroke="none">len(path) == 5</tspan></tspan></tspan></text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 34 KiB |
@ -1,4 +1,3 @@
|
|||||||
from trezor import wire
|
|
||||||
from trezor.crypto import base58, crc, hashlib
|
from trezor.crypto import base58, crc, hashlib
|
||||||
|
|
||||||
from . import cbor
|
from . import cbor
|
||||||
@ -6,14 +5,27 @@ from . import cbor
|
|||||||
from apps.common import HARDENED, seed
|
from apps.common import HARDENED, seed
|
||||||
|
|
||||||
|
|
||||||
def validate_derivation_path(path: list):
|
def validate_full_path(path: list) -> bool:
|
||||||
if len(path) < 2 or len(path) > 5:
|
"""
|
||||||
raise wire.ProcessError("Derivation path must be composed from 2-5 indices")
|
Validates derivation path to fit 44'/1815'/a'/{0,1}/i,
|
||||||
|
where `a` is an account number and i an address index.
|
||||||
if path[0] != HARDENED | 44 or path[1] != HARDENED | 1815:
|
The max value for `a` is 20, 1 000 000 for `i`.
|
||||||
raise wire.ProcessError("This is not cardano derivation path")
|
The derivation scheme v1 allowed a'/0/i only,
|
||||||
|
but in v2 it can be a'/1/i as well.
|
||||||
return path
|
"""
|
||||||
|
if len(path) != 5:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 1815 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 20 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[3] != 0 and path[3] != 1:
|
||||||
|
return False
|
||||||
|
if path[4] > 1000000:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _address_hash(data) -> bytes:
|
def _address_hash(data) -> bytes:
|
||||||
@ -33,8 +45,6 @@ def _get_address_root(node, payload):
|
|||||||
|
|
||||||
|
|
||||||
def derive_address_and_node(root_node, path: list):
|
def derive_address_and_node(root_node, path: list):
|
||||||
validate_derivation_path(path)
|
|
||||||
|
|
||||||
derived_node = root_node.clone()
|
derived_node = root_node.clone()
|
||||||
|
|
||||||
address_payload = None
|
address_payload = None
|
||||||
|
@ -2,13 +2,15 @@ from trezor import log, ui, wire
|
|||||||
from trezor.crypto import bip32
|
from trezor.crypto import bip32
|
||||||
from trezor.messages.CardanoAddress import CardanoAddress
|
from trezor.messages.CardanoAddress import CardanoAddress
|
||||||
|
|
||||||
from .address import derive_address_and_node
|
from .address import derive_address_and_node, validate_full_path
|
||||||
from .layout import confirm_with_pagination
|
from .layout import confirm_with_pagination
|
||||||
|
|
||||||
from apps.common import seed, storage
|
from apps.common import paths, seed, storage
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg):
|
async def get_address(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
mnemonic = storage.get_mnemonic()
|
mnemonic = storage.get_mnemonic()
|
||||||
passphrase = await seed._get_cached_passphrase(ctx)
|
passphrase = await seed._get_cached_passphrase(ctx)
|
||||||
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||||
|
@ -7,10 +7,14 @@ from trezor.messages.HDNodeType import HDNodeType
|
|||||||
|
|
||||||
from .address import derive_address_and_node
|
from .address import derive_address_and_node
|
||||||
|
|
||||||
from apps.common import layout, seed, storage
|
from apps.common import layout, paths, seed, storage
|
||||||
|
|
||||||
|
|
||||||
async def get_public_key(ctx, msg):
|
async def get_public_key(ctx, msg):
|
||||||
|
await paths.validate_path(
|
||||||
|
ctx, paths.validate_path_for_get_public_key, path=msg.address_n, slip44_id=1815
|
||||||
|
)
|
||||||
|
|
||||||
mnemonic = storage.get_mnemonic()
|
mnemonic = storage.get_mnemonic()
|
||||||
passphrase = await seed._get_cached_passphrase(ctx)
|
passphrase = await seed._get_cached_passphrase(ctx)
|
||||||
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||||
|
53
src/apps/cardano/sign_message.py
Normal file
53
src/apps/cardano/sign_message.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from trezor import log, ui, wire
|
||||||
|
from trezor.crypto import bip32
|
||||||
|
from trezor.crypto.curve import ed25519
|
||||||
|
from trezor.messages.CardanoMessageSignature import CardanoMessageSignature
|
||||||
|
|
||||||
|
from .address import derive_address_and_node, validate_full_path
|
||||||
|
from .layout import confirm_with_pagination
|
||||||
|
|
||||||
|
from apps.common import paths, seed, storage
|
||||||
|
|
||||||
|
|
||||||
|
async def sign_message(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
|
mnemonic = storage.get_mnemonic()
|
||||||
|
root_node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
|
try:
|
||||||
|
signature = _sign_message(root_node, msg.message, msg.address_n)
|
||||||
|
except ValueError as e:
|
||||||
|
if __debug__:
|
||||||
|
log.exception(__name__, e)
|
||||||
|
raise wire.ProcessError("Signing failed")
|
||||||
|
mnemonic = None
|
||||||
|
root_node = None
|
||||||
|
|
||||||
|
if not await confirm_with_pagination(
|
||||||
|
ctx, msg.message, "Signing message", ui.ICON_RECEIVE, ui.GREEN
|
||||||
|
):
|
||||||
|
raise wire.ActionCancelled("Signing cancelled")
|
||||||
|
|
||||||
|
if not await confirm_with_pagination(
|
||||||
|
ctx,
|
||||||
|
paths.break_address_n_to_lines(msg.address_n),
|
||||||
|
"With address",
|
||||||
|
ui.ICON_RECEIVE,
|
||||||
|
ui.GREEN,
|
||||||
|
):
|
||||||
|
raise wire.ActionCancelled("Signing cancelled")
|
||||||
|
|
||||||
|
return signature
|
||||||
|
|
||||||
|
|
||||||
|
def _sign_message(root_node, message: str, derivation_path: list):
|
||||||
|
address, node = derive_address_and_node(root_node, derivation_path)
|
||||||
|
|
||||||
|
signature = ed25519.sign_ext(node.private_key(), node.private_key_ext(), message)
|
||||||
|
|
||||||
|
sig = CardanoMessageSignature()
|
||||||
|
sig.public_key = seed.remove_ed25519_prefix(node.public_key())
|
||||||
|
sig.signature = signature
|
||||||
|
|
||||||
|
return sig
|
@ -6,12 +6,13 @@ from trezor.messages.CardanoTxRequest import CardanoTxRequest
|
|||||||
from trezor.messages.MessageType import CardanoTxAck
|
from trezor.messages.MessageType import CardanoTxAck
|
||||||
from trezor.ui.text import BR
|
from trezor.ui.text import BR
|
||||||
|
|
||||||
from .address import derive_address_and_node
|
from .address import derive_address_and_node, validate_full_path
|
||||||
from .layout import confirm_with_pagination, progress
|
from .layout import confirm_with_pagination, progress
|
||||||
|
|
||||||
from apps.cardano import cbor
|
from apps.cardano import cbor
|
||||||
from apps.common import seed, storage
|
from apps.common import seed, storage
|
||||||
from apps.common.layout import address_n_to_str, split_address
|
from apps.common.layout import address_n_to_str, split_address
|
||||||
|
from apps.common.paths import validate_path
|
||||||
from apps.homescreen.homescreen import display_homescreen
|
from apps.homescreen.homescreen import display_homescreen
|
||||||
|
|
||||||
|
|
||||||
@ -97,6 +98,9 @@ async def sign_tx(ctx, msg):
|
|||||||
# clear progress bar
|
# clear progress bar
|
||||||
display_homescreen()
|
display_homescreen()
|
||||||
|
|
||||||
|
for i in msg.inputs:
|
||||||
|
await validate_path(ctx, validate_full_path, path=i.address_n)
|
||||||
|
|
||||||
# sign the transaction bundle and prepare the result
|
# sign the transaction bundle and prepare the result
|
||||||
transaction = Transaction(
|
transaction = Transaction(
|
||||||
msg.inputs, msg.outputs, transactions, root_node, msg.network
|
msg.inputs, msg.outputs, transactions, root_node, msg.network
|
||||||
|
41
src/apps/cardano/verify_message.py
Normal file
41
src/apps/cardano/verify_message.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from ubinascii import hexlify
|
||||||
|
|
||||||
|
from trezor import log, ui, wire
|
||||||
|
from trezor.crypto.curve import ed25519
|
||||||
|
from trezor.messages.Failure import Failure
|
||||||
|
from trezor.messages.Success import Success
|
||||||
|
|
||||||
|
from .address import validate_full_path
|
||||||
|
from .layout import confirm_with_pagination
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
|
|
||||||
|
|
||||||
|
async def verify_message(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = _verify_message(msg.public_key, msg.signature, msg.message)
|
||||||
|
except ValueError as e:
|
||||||
|
if __debug__:
|
||||||
|
log.exception(__name__, e)
|
||||||
|
raise wire.ProcessError("Verifying failed")
|
||||||
|
|
||||||
|
if not res:
|
||||||
|
return Failure(message="Invalid signature")
|
||||||
|
|
||||||
|
if not await confirm_with_pagination(
|
||||||
|
ctx, msg.message, "Verifying message", ui.ICON_RECEIVE, ui.GREEN
|
||||||
|
):
|
||||||
|
raise wire.ActionCancelled("Verifying cancelled")
|
||||||
|
|
||||||
|
if not await confirm_with_pagination(
|
||||||
|
ctx, hexlify(msg.public_key), "With public key", ui.ICON_RECEIVE, ui.GREEN
|
||||||
|
):
|
||||||
|
raise wire.ActionCancelled("Verifying cancelled")
|
||||||
|
|
||||||
|
return Success(message="Message verified")
|
||||||
|
|
||||||
|
|
||||||
|
def _verify_message(public_key: bytes, signature: bytes, message: str):
|
||||||
|
return ed25519.verify(public_key, signature, message)
|
72
src/apps/common/paths.py
Normal file
72
src/apps/common/paths.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from micropython import const
|
||||||
|
|
||||||
|
from trezor import ui
|
||||||
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.ui.text import Text
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
|
from apps.common.confirm import require_confirm
|
||||||
|
|
||||||
|
|
||||||
|
async def validate_path(ctx, validate_func, **kwargs):
|
||||||
|
if not validate_func(**kwargs):
|
||||||
|
await show_path_warning(ctx, kwargs["path"])
|
||||||
|
|
||||||
|
|
||||||
|
async def show_path_warning(ctx, path: list):
|
||||||
|
text = Text("Confirm path", ui.ICON_WRONG, icon_color=ui.RED)
|
||||||
|
text.normal("The path")
|
||||||
|
text.mono(*break_address_n_to_lines(path))
|
||||||
|
text.normal("seems unusual.")
|
||||||
|
text.normal("Are you sure?")
|
||||||
|
return await require_confirm(
|
||||||
|
ctx, text, code=ButtonRequestType.UnknownDerivationPath
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_path_for_get_public_key(path: list, slip44_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if path has at least three hardened items and slip44 id matches.
|
||||||
|
The path is allowed to have more than three items, but all the following
|
||||||
|
items have to be non-hardened.
|
||||||
|
"""
|
||||||
|
length = len(path)
|
||||||
|
if length < 3 or length > 5:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != slip44_id | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 20 | HARDENED:
|
||||||
|
return False
|
||||||
|
if length > 3 and is_hardened(path[3]):
|
||||||
|
return False
|
||||||
|
if length > 4 and is_hardened(path[4]):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_hardened(i: int) -> bool:
|
||||||
|
if i & HARDENED:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def break_address_n_to_lines(address_n: list) -> list:
|
||||||
|
def path_item(i: int):
|
||||||
|
if i & HARDENED:
|
||||||
|
return str(i ^ HARDENED) + "'"
|
||||||
|
else:
|
||||||
|
return str(i)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
path_str = "m/" + "/".join([path_item(i) for i in address_n])
|
||||||
|
|
||||||
|
per_line = const(17)
|
||||||
|
while len(path_str) > per_line:
|
||||||
|
i = path_str[:per_line].rfind("/")
|
||||||
|
lines.append(path_str[:i])
|
||||||
|
path_str = path_str[i:]
|
||||||
|
lines.append(path_str)
|
||||||
|
|
||||||
|
return lines
|
55
src/apps/ethereum/address.py
Normal file
55
src/apps/ethereum/address.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
We believe Ethereum should use 44'/60'/a' for everything,because it is
|
||||||
|
account-based, rather than UTXO-based. Unfortunately, lot of Ethereum
|
||||||
|
tools (MEW, Metamask) do not use such scheme and set a = 0 and then
|
||||||
|
iterate the address index i. Therefore for compatibility reasons we use
|
||||||
|
the same scheme: 44'/60'/0'/0/i and only the i is being iterated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(path: list) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to equal 44'/60'/0'/0/i,
|
||||||
|
where `i` is an address index from 0 to 1 000 000.
|
||||||
|
"""
|
||||||
|
if len(path) != 5:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 60 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] != 0 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[3] != 0:
|
||||||
|
return False
|
||||||
|
if path[4] > 1000000:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def ethereum_address_hex(address, network=None):
|
||||||
|
from ubinascii import hexlify
|
||||||
|
from trezor.crypto.hashlib import sha3_256
|
||||||
|
|
||||||
|
rskip60 = network is not None and network.rskip60
|
||||||
|
|
||||||
|
hx = hexlify(address).decode()
|
||||||
|
|
||||||
|
prefix = str(network.chain_id) + "0x" if rskip60 else ""
|
||||||
|
hs = sha3_256(prefix + hx, keccak=True).digest()
|
||||||
|
h = ""
|
||||||
|
|
||||||
|
for i in range(20):
|
||||||
|
l = hx[i * 2]
|
||||||
|
if hs[i] & 0x80 and l >= "a" and l <= "f":
|
||||||
|
l = l.upper()
|
||||||
|
h += l
|
||||||
|
l = hx[i * 2 + 1]
|
||||||
|
if hs[i] & 0x08 and l >= "a" and l <= "f":
|
||||||
|
l = l.upper()
|
||||||
|
h += l
|
||||||
|
|
||||||
|
return "0x" + h
|
@ -1,3 +1,6 @@
|
|||||||
|
from .address import ethereum_address_hex, validate_full_path
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
from apps.ethereum import networks
|
from apps.ethereum import networks
|
||||||
|
|
||||||
@ -8,6 +11,8 @@ async def get_address(ctx, msg):
|
|||||||
from trezor.crypto.hashlib import sha3_256
|
from trezor.crypto.hashlib import sha3_256
|
||||||
from apps.common import seed
|
from apps.common import seed
|
||||||
|
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n)
|
node = await seed.derive_node(ctx, msg.address_n)
|
||||||
|
|
||||||
seckey = node.private_key()
|
seckey = node.private_key()
|
||||||
@ -19,7 +24,7 @@ async def get_address(ctx, msg):
|
|||||||
network = networks.by_slip44(msg.address_n[1] & 0x7FFFFFFF)
|
network = networks.by_slip44(msg.address_n[1] & 0x7FFFFFFF)
|
||||||
else:
|
else:
|
||||||
network = None
|
network = None
|
||||||
hex_addr = _ethereum_address_hex(address, network)
|
hex_addr = ethereum_address_hex(address, network)
|
||||||
desc = address_n_to_str(msg.address_n)
|
desc = address_n_to_str(msg.address_n)
|
||||||
while True:
|
while True:
|
||||||
if await show_address(ctx, hex_addr, desc=desc):
|
if await show_address(ctx, hex_addr, desc=desc):
|
||||||
@ -28,28 +33,3 @@ async def get_address(ctx, msg):
|
|||||||
break
|
break
|
||||||
|
|
||||||
return EthereumAddress(address=address)
|
return EthereumAddress(address=address)
|
||||||
|
|
||||||
|
|
||||||
def _ethereum_address_hex(address, network=None):
|
|
||||||
from ubinascii import hexlify
|
|
||||||
from trezor.crypto.hashlib import sha3_256
|
|
||||||
|
|
||||||
rskip60 = network is not None and network.rskip60
|
|
||||||
|
|
||||||
hx = hexlify(address).decode()
|
|
||||||
|
|
||||||
prefix = str(network.chain_id) + "0x" if rskip60 else ""
|
|
||||||
hs = sha3_256(prefix + hx, keccak=True).digest()
|
|
||||||
h = ""
|
|
||||||
|
|
||||||
for i in range(20):
|
|
||||||
l = hx[i * 2]
|
|
||||||
if hs[i] & 0x80 and l >= "a" and l <= "f":
|
|
||||||
l = l.upper()
|
|
||||||
h += l
|
|
||||||
l = hx[i * 2 + 1]
|
|
||||||
if hs[i] & 0x08 and l >= "a" and l <= "f":
|
|
||||||
l = l.upper()
|
|
||||||
h += l
|
|
||||||
|
|
||||||
return "0x" + h
|
|
||||||
|
@ -8,12 +8,12 @@ from trezor.utils import chunks, format_amount
|
|||||||
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
||||||
from apps.common.layout import split_address
|
from apps.common.layout import split_address
|
||||||
from apps.ethereum import networks, tokens
|
from apps.ethereum import networks, tokens
|
||||||
from apps.ethereum.get_address import _ethereum_address_hex
|
from apps.ethereum.address import ethereum_address_hex
|
||||||
|
|
||||||
|
|
||||||
async def require_confirm_tx(ctx, to, value, chain_id, token=None, tx_type=None):
|
async def require_confirm_tx(ctx, to, value, chain_id, token=None, tx_type=None):
|
||||||
if to:
|
if to:
|
||||||
to_str = _ethereum_address_hex(to, networks.by_chain_id(chain_id))
|
to_str = ethereum_address_hex(to, networks.by_chain_id(chain_id))
|
||||||
else:
|
else:
|
||||||
to_str = "new contract?"
|
to_str = "new contract?"
|
||||||
text = Text("Confirm sending", ui.ICON_SEND, icon_color=ui.GREEN, new_lines=False)
|
text = Text("Confirm sending", ui.ICON_SEND, icon_color=ui.GREEN, new_lines=False)
|
||||||
|
@ -4,7 +4,9 @@ from trezor.messages.EthereumMessageSignature import EthereumMessageSignature
|
|||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from apps.common import seed
|
from .address import validate_full_path
|
||||||
|
|
||||||
|
from apps.common import paths, seed
|
||||||
from apps.common.confirm import require_confirm
|
from apps.common.confirm import require_confirm
|
||||||
from apps.common.signverify import split_message
|
from apps.common.signverify import split_message
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ def message_digest(message):
|
|||||||
|
|
||||||
|
|
||||||
async def sign_message(ctx, msg):
|
async def sign_message(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
await require_confirm_sign_message(ctx, msg.message)
|
await require_confirm_sign_message(ctx, msg.message)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n)
|
node = await seed.derive_node(ctx, msg.address_n)
|
||||||
|
@ -7,7 +7,9 @@ from trezor.messages.EthereumTxRequest import EthereumTxRequest
|
|||||||
from trezor.messages.MessageType import EthereumTxAck
|
from trezor.messages.MessageType import EthereumTxAck
|
||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from apps.common import seed
|
from .address import validate_full_path
|
||||||
|
|
||||||
|
from apps.common import paths, seed
|
||||||
from apps.ethereum import tokens
|
from apps.ethereum import tokens
|
||||||
from apps.ethereum.layout import (
|
from apps.ethereum.layout import (
|
||||||
require_confirm_data,
|
require_confirm_data,
|
||||||
@ -22,6 +24,7 @@ MAX_CHAIN_ID = 2147483629
|
|||||||
async def sign_tx(ctx, msg):
|
async def sign_tx(ctx, msg):
|
||||||
msg = sanitize(msg)
|
msg = sanitize(msg)
|
||||||
check(msg)
|
check(msg)
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
data_total = msg.data_length
|
data_total = msg.data_length
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ from trezor.crypto.hashlib import sha3_256
|
|||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
|
|
||||||
|
from .address import validate_full_path
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
from apps.common.confirm import require_confirm
|
from apps.common.confirm import require_confirm
|
||||||
from apps.common.layout import split_address
|
from apps.common.layout import split_address
|
||||||
from apps.common.signverify import split_message
|
from apps.common.signverify import split_message
|
||||||
@ -12,6 +15,8 @@ from apps.ethereum.sign_message import message_digest
|
|||||||
|
|
||||||
|
|
||||||
async def verify_message(ctx, msg):
|
async def verify_message(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address)
|
||||||
|
|
||||||
digest = message_digest(msg.message)
|
digest = message_digest(msg.message)
|
||||||
sig = bytearray([msg.signature[64]]) + msg.signature[:64]
|
sig = bytearray([msg.signature[64]]) + msg.signature[:64]
|
||||||
pubkey = secp256k1.verify_recover(sig, digest)
|
pubkey = secp256k1.verify_recover(sig, digest)
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
from trezor.messages.LiskAddress import LiskAddress
|
from trezor.messages.LiskAddress import LiskAddress
|
||||||
|
|
||||||
from .helpers import LISK_CURVE, get_address_from_public_key
|
from .helpers import LISK_CURVE, get_address_from_public_key, validate_full_path
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg):
|
async def get_address(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
|
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
|
||||||
pubkey = node.public_key()
|
pubkey = node.public_key()
|
||||||
pubkey = pubkey[1:] # skip ed25519 pubkey marker
|
pubkey = pubkey[1:] # skip ed25519 pubkey marker
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
from trezor.messages.LiskPublicKey import LiskPublicKey
|
from trezor.messages.LiskPublicKey import LiskPublicKey
|
||||||
|
|
||||||
from .helpers import LISK_CURVE
|
from .helpers import LISK_CURVE, validate_full_path
|
||||||
|
|
||||||
from apps.common import layout, seed
|
from apps.common import layout, paths, seed
|
||||||
|
|
||||||
|
|
||||||
async def get_public_key(ctx, msg):
|
async def get_public_key(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
|
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
|
||||||
pubkey = node.public_key()
|
pubkey = node.public_key()
|
||||||
pubkey = pubkey[1:] # skip ed25519 pubkey marker
|
pubkey = pubkey[1:] # skip ed25519 pubkey marker
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
LISK_CURVE = "ed25519"
|
LISK_CURVE = "ed25519"
|
||||||
|
|
||||||
|
|
||||||
@ -31,3 +33,19 @@ def get_vote_tx_text(votes):
|
|||||||
|
|
||||||
def _text_with_plural(txt, value):
|
def _text_with_plural(txt, value):
|
||||||
return "%s %s %s" % (txt, value, ("votes" if value != 1 else "vote"))
|
return "%s %s %s" % (txt, value, ("votes" if value != 1 else "vote"))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(path: list) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to equal 44'/134'/a',
|
||||||
|
where `a` is an account index from 0 to 1 000 000.
|
||||||
|
"""
|
||||||
|
if len(path) != 3:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 134 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -4,9 +4,9 @@ from trezor.messages.LiskMessageSignature import LiskMessageSignature
|
|||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from .helpers import LISK_CURVE
|
from .helpers import LISK_CURVE, validate_full_path
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.confirm import require_confirm
|
from apps.common.confirm import require_confirm
|
||||||
from apps.common.signverify import split_message
|
from apps.common.signverify import split_message
|
||||||
from apps.wallet.sign_tx.signing import write_varint
|
from apps.wallet.sign_tx.signing import write_varint
|
||||||
@ -23,17 +23,15 @@ def message_digest(message):
|
|||||||
|
|
||||||
|
|
||||||
async def sign_message(ctx, msg):
|
async def sign_message(ctx, msg):
|
||||||
message = msg.message
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
address_n = msg.address_n
|
await require_confirm_sign_message(ctx, msg.message)
|
||||||
|
|
||||||
await require_confirm_sign_message(ctx, message)
|
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, address_n, LISK_CURVE)
|
|
||||||
seckey = node.private_key()
|
seckey = node.private_key()
|
||||||
pubkey = node.public_key()
|
pubkey = node.public_key()
|
||||||
pubkey = pubkey[1:] # skip ed25519 pubkey marker
|
pubkey = pubkey[1:] # skip ed25519 pubkey marker
|
||||||
|
|
||||||
signature = ed25519.sign(seckey, message_digest(message))
|
signature = ed25519.sign(seckey, message_digest(msg.message))
|
||||||
|
|
||||||
return LiskMessageSignature(public_key=pubkey, signature=signature)
|
return LiskMessageSignature(public_key=pubkey, signature=signature)
|
||||||
|
|
||||||
|
@ -8,12 +8,14 @@ from trezor.messages.LiskSignedTx import LiskSignedTx
|
|||||||
from trezor.utils import HashWriter
|
from trezor.utils import HashWriter
|
||||||
|
|
||||||
from . import layout
|
from . import layout
|
||||||
from .helpers import LISK_CURVE, get_address_from_public_key
|
from .helpers import LISK_CURVE, get_address_from_public_key, validate_full_path
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
|
|
||||||
|
|
||||||
async def sign_tx(ctx, msg):
|
async def sign_tx(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
pubkey, seckey = await _get_keys(ctx, msg)
|
pubkey, seckey = await _get_keys(ctx, msg)
|
||||||
transaction = _update_raw_tx(msg.transaction, pubkey)
|
transaction = _update_raw_tx(msg.transaction, pubkey)
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
from trezor.messages.MoneroAddress import MoneroAddress
|
from trezor.messages.MoneroAddress import MoneroAddress
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
from apps.monero import misc
|
from apps.monero import misc
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg):
|
async def get_address(ctx, msg):
|
||||||
|
await paths.validate_path(ctx, misc.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
|
creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
from trezor.messages.MoneroGetWatchKey import MoneroGetWatchKey
|
from trezor.messages.MoneroGetWatchKey import MoneroGetWatchKey
|
||||||
from trezor.messages.MoneroWatchKey import MoneroWatchKey
|
from trezor.messages.MoneroWatchKey import MoneroWatchKey
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
from apps.monero import misc
|
from apps.monero import misc
|
||||||
from apps.monero.layout import confirms
|
from apps.monero.layout import confirms
|
||||||
from apps.monero.xmr import crypto
|
from apps.monero.xmr import crypto
|
||||||
|
|
||||||
|
|
||||||
async def get_watch_only(ctx, msg: MoneroGetWatchKey):
|
async def get_watch_only(ctx, msg: MoneroGetWatchKey):
|
||||||
|
await paths.validate_path(ctx, misc.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
await confirms.require_confirm_watchkey(ctx)
|
await confirms.require_confirm_watchkey(ctx)
|
||||||
|
|
||||||
creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
|
creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
|
||||||
|
@ -7,6 +7,7 @@ from trezor.messages.MoneroKeyImageExportInitAck import MoneroKeyImageExportInit
|
|||||||
from trezor.messages.MoneroKeyImageSyncFinalAck import MoneroKeyImageSyncFinalAck
|
from trezor.messages.MoneroKeyImageSyncFinalAck import MoneroKeyImageSyncFinalAck
|
||||||
from trezor.messages.MoneroKeyImageSyncStepAck import MoneroKeyImageSyncStepAck
|
from trezor.messages.MoneroKeyImageSyncStepAck import MoneroKeyImageSyncStepAck
|
||||||
|
|
||||||
|
from apps.common import paths
|
||||||
from apps.monero import misc
|
from apps.monero import misc
|
||||||
from apps.monero.layout import confirms
|
from apps.monero.layout import confirms
|
||||||
from apps.monero.xmr import crypto, key_image, monero
|
from apps.monero.xmr import crypto, key_image, monero
|
||||||
@ -46,6 +47,8 @@ class KeyImageSync:
|
|||||||
|
|
||||||
|
|
||||||
async def _init_step(s, ctx, msg):
|
async def _init_step(s, ctx, msg):
|
||||||
|
await paths.validate_path(ctx, misc.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
s.creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
|
s.creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
|
||||||
|
|
||||||
await confirms.require_confirm_keyimage_sync(ctx)
|
await confirms.require_confirm_keyimage_sync(ctx)
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
|
|
||||||
async def get_creds(ctx, address_n=None, network_type=None):
|
async def get_creds(ctx, address_n=None, network_type=None):
|
||||||
from apps.common import seed
|
from apps.common import seed
|
||||||
from apps.monero.xmr import crypto, monero
|
from apps.monero.xmr import crypto, monero
|
||||||
@ -19,3 +22,19 @@ async def get_creds(ctx, address_n=None, network_type=None):
|
|||||||
|
|
||||||
creds = AccountCreds.new_wallet(view_sec, spend_sec, network_type)
|
creds = AccountCreds.new_wallet(view_sec, spend_sec, network_type)
|
||||||
return creds
|
return creds
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(path: list) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to equal 44'/128'/a',
|
||||||
|
where `a` is an account index from 0 to 1 000 000.
|
||||||
|
"""
|
||||||
|
if len(path) != 3:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 128 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -19,6 +19,9 @@ async def init_transaction(
|
|||||||
state: State, address_n: list, network_type: int, tsx_data: MoneroTransactionData
|
state: State, address_n: list, network_type: int, tsx_data: MoneroTransactionData
|
||||||
):
|
):
|
||||||
from apps.monero.signing import offloading_keys
|
from apps.monero.signing import offloading_keys
|
||||||
|
from apps.common import paths
|
||||||
|
|
||||||
|
await paths.validate_path(state.ctx, misc.validate_full_path, path=address_n)
|
||||||
|
|
||||||
state.creds = await misc.get_creds(state.ctx, address_n, network_type)
|
state.creds = await misc.get_creds(state.ctx, address_n, network_type)
|
||||||
state.fee = state.fee if state.fee > 0 else 0
|
state.fee = state.fee if state.fee > 0 else 0
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
from trezor.messages.NEMAddress import NEMAddress
|
from trezor.messages.NEMAddress import NEMAddress
|
||||||
|
|
||||||
from .helpers import NEM_CURVE, get_network_str
|
from .helpers import NEM_CURVE, check_path, get_network_str
|
||||||
from .validators import validate_network
|
from .validators import validate_network
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import seed
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
|
from apps.common.paths import validate_path
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg):
|
async def get_address(ctx, msg):
|
||||||
network = validate_network(msg.network)
|
network = validate_network(msg.network)
|
||||||
|
await validate_path(ctx, check_path, path=msg.address_n, network=msg.network)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE)
|
node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE)
|
||||||
address = node.nem_address(network)
|
address = node.nem_address(network)
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
NEM_NETWORK_MAINNET = const(0x68)
|
NEM_NETWORK_MAINNET = const(0x68)
|
||||||
NEM_NETWORK_TESTNET = const(0x98)
|
NEM_NETWORK_TESTNET = const(0x98)
|
||||||
NEM_NETWORK_MIJIN = const(0x60)
|
NEM_NETWORK_MIJIN = const(0x60)
|
||||||
@ -35,3 +37,28 @@ def get_network_str(network: int) -> str:
|
|||||||
return "Testnet"
|
return "Testnet"
|
||||||
elif network == NEM_NETWORK_MIJIN:
|
elif network == NEM_NETWORK_MIJIN:
|
||||||
return "Mijin"
|
return "Mijin"
|
||||||
|
|
||||||
|
|
||||||
|
def check_path(path: list, network=None) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to fit 44'/43'/a' or 44'/43'/a'/0'/0',
|
||||||
|
where `a` is an account number. We believe the path should be
|
||||||
|
44'/43'/a', but for compatibility reasons with NEM's NanoWallet
|
||||||
|
we allow 44'/43'/a'/0'/0' as well.
|
||||||
|
Testnet is also allowed: 44'/1'/a'{/0'/0'}
|
||||||
|
"""
|
||||||
|
length = len(path)
|
||||||
|
if length != 3 and length != 5:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if not (
|
||||||
|
path[1] == 43 | HARDENED
|
||||||
|
or (network == NEM_NETWORK_TESTNET and path[1] == 1 | HARDENED)
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
|
||||||
|
return False
|
||||||
|
if length == 5 and (path[3] != 0 | HARDENED or path[4] != 0 | HARDENED):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -3,14 +3,19 @@ from trezor.messages.NEMSignedTx import NEMSignedTx
|
|||||||
from trezor.messages.NEMSignTx import NEMSignTx
|
from trezor.messages.NEMSignTx import NEMSignTx
|
||||||
|
|
||||||
from . import mosaic, multisig, namespace, transfer
|
from . import mosaic, multisig, namespace, transfer
|
||||||
from .helpers import NEM_CURVE, NEM_HASH_ALG
|
from .helpers import NEM_CURVE, NEM_HASH_ALG, check_path
|
||||||
from .validators import validate
|
from .validators import validate
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import seed
|
||||||
|
from apps.common.paths import validate_path
|
||||||
|
|
||||||
|
|
||||||
async def sign_tx(ctx, msg: NEMSignTx):
|
async def sign_tx(ctx, msg: NEMSignTx):
|
||||||
validate(msg)
|
validate(msg)
|
||||||
|
await validate_path(
|
||||||
|
ctx, check_path, path=msg.transaction.address_n, network=msg.transaction.network
|
||||||
|
)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE)
|
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE)
|
||||||
|
|
||||||
if msg.multisig:
|
if msg.multisig:
|
||||||
|
@ -3,11 +3,13 @@ from trezor.messages.RippleGetAddress import RippleGetAddress
|
|||||||
|
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg: RippleGetAddress):
|
async def get_address(ctx, msg: RippleGetAddress):
|
||||||
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n)
|
node = await seed.derive_node(ctx, msg.address_n)
|
||||||
pubkey = node.public_key()
|
pubkey = node.public_key()
|
||||||
address = helpers.address_from_public_key(pubkey)
|
address = helpers.address_from_public_key(pubkey)
|
||||||
|
@ -4,6 +4,8 @@ from trezor.crypto.hashlib import ripemd160, sha256
|
|||||||
|
|
||||||
from . import base58_ripple
|
from . import base58_ripple
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
# HASH_TX_ID = const(0x54584E00) # 'TXN'
|
# HASH_TX_ID = const(0x54584E00) # 'TXN'
|
||||||
HASH_TX_SIGN = const(0x53545800) # 'STX'
|
HASH_TX_SIGN = const(0x53545800) # 'STX'
|
||||||
# HASH_TX_SIGN_TESTNET = const(0x73747800) # 'stx'
|
# HASH_TX_SIGN_TESTNET = const(0x73747800) # 'stx'
|
||||||
@ -45,3 +47,25 @@ def decode_address(address: str):
|
|||||||
"""Returns so called Account ID"""
|
"""Returns so called Account ID"""
|
||||||
adr = base58_ripple.decode_check(address)
|
adr = base58_ripple.decode_check(address)
|
||||||
return adr[1:]
|
return adr[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(path: list) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to equal 44'/144'/a'/0/0,
|
||||||
|
where `a` is an account index from 0 to 1 000 000.
|
||||||
|
Similar to Ethereum this should be 44'/144'/a', but for
|
||||||
|
compatibility with other HW vendors we use 44'/144'/a'/0/0.
|
||||||
|
"""
|
||||||
|
if len(path) != 5:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 144 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[3] != 0:
|
||||||
|
return False
|
||||||
|
if path[4] != 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -8,11 +8,13 @@ from trezor.wire import ProcessError
|
|||||||
from . import helpers, layout
|
from . import helpers, layout
|
||||||
from .serialize import serialize
|
from .serialize import serialize
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
|
|
||||||
|
|
||||||
async def sign_tx(ctx, msg: RippleSignTx):
|
async def sign_tx(ctx, msg: RippleSignTx):
|
||||||
validate(msg)
|
validate(msg)
|
||||||
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n)
|
node = await seed.derive_node(ctx, msg.address_n)
|
||||||
source_address = helpers.address_from_public_key(node.public_key())
|
source_address = helpers.address_from_public_key(node.public_key())
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
from trezor.messages.StellarAddress import StellarAddress
|
from trezor.messages.StellarAddress import StellarAddress
|
||||||
from trezor.messages.StellarGetAddress import StellarGetAddress
|
from trezor.messages.StellarGetAddress import StellarGetAddress
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
from apps.stellar import helpers
|
from apps.stellar import helpers
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg: StellarGetAddress):
|
async def get_address(ctx, msg: StellarGetAddress):
|
||||||
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n, helpers.STELLAR_CURVE)
|
node = await seed.derive_node(ctx, msg.address_n, helpers.STELLAR_CURVE)
|
||||||
pubkey = seed.remove_ed25519_prefix(node.public_key())
|
pubkey = seed.remove_ed25519_prefix(node.public_key())
|
||||||
address = helpers.address_from_public_key(pubkey)
|
address = helpers.address_from_public_key(pubkey)
|
||||||
|
@ -3,6 +3,8 @@ import ustruct
|
|||||||
from trezor.crypto import base32
|
from trezor.crypto import base32
|
||||||
from trezor.wire import ProcessError
|
from trezor.wire import ProcessError
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
STELLAR_CURVE = "ed25519"
|
STELLAR_CURVE = "ed25519"
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +28,22 @@ def address_from_public_key(pubkey: bytes):
|
|||||||
return base32.encode(address)
|
return base32.encode(address)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(path: list) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to equal 44'/148'/a',
|
||||||
|
where `a` is an account index from 0 to 1 000 000.
|
||||||
|
"""
|
||||||
|
if len(path) != 3:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 148 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _crc16_checksum_verify(data: bytes, checksum: bytes):
|
def _crc16_checksum_verify(data: bytes, checksum: bytes):
|
||||||
if _crc16_checksum(data) != checksum:
|
if _crc16_checksum(data) != checksum:
|
||||||
raise ProcessError("Invalid address checksum")
|
raise ProcessError("Invalid address checksum")
|
||||||
|
@ -7,7 +7,7 @@ from trezor.messages.StellarSignTx import StellarSignTx
|
|||||||
from trezor.messages.StellarTxOpRequest import StellarTxOpRequest
|
from trezor.messages.StellarTxOpRequest import StellarTxOpRequest
|
||||||
from trezor.wire import ProcessError
|
from trezor.wire import ProcessError
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.stellar import consts, helpers, layout, writers
|
from apps.stellar import consts, helpers, layout, writers
|
||||||
from apps.stellar.operations import process_operation
|
from apps.stellar.operations import process_operation
|
||||||
|
|
||||||
@ -16,6 +16,8 @@ async def sign_tx(ctx, msg: StellarSignTx):
|
|||||||
if msg.num_operations == 0:
|
if msg.num_operations == 0:
|
||||||
raise ProcessError("Stellar: At least one operation is required")
|
raise ProcessError("Stellar: At least one operation is required")
|
||||||
|
|
||||||
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n, consts.STELLAR_CURVE)
|
node = await seed.derive_node(ctx, msg.address_n, consts.STELLAR_CURVE)
|
||||||
pubkey = seed.remove_ed25519_prefix(node.public_key())
|
pubkey = seed.remove_ed25519_prefix(node.public_key())
|
||||||
|
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
from trezor.crypto import hashlib
|
from trezor.crypto import hashlib
|
||||||
from trezor.messages.TezosAddress import TezosAddress
|
from trezor.messages.TezosAddress import TezosAddress
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
from apps.tezos.helpers import (
|
from apps.tezos import helpers
|
||||||
TEZOS_CURVE,
|
|
||||||
TEZOS_ED25519_ADDRESS_PREFIX,
|
|
||||||
base58_encode_check,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_address(ctx, msg):
|
async def get_address(ctx, msg):
|
||||||
node = await seed.derive_node(ctx, msg.address_n, TEZOS_CURVE)
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
|
||||||
|
|
||||||
pk = seed.remove_ed25519_prefix(node.public_key())
|
pk = seed.remove_ed25519_prefix(node.public_key())
|
||||||
pkh = hashlib.blake2b(pk, outlen=20).digest()
|
pkh = hashlib.blake2b(pk, outlen=20).digest()
|
||||||
address = base58_encode_check(pkh, prefix=TEZOS_ED25519_ADDRESS_PREFIX)
|
address = helpers.base58_encode_check(
|
||||||
|
pkh, prefix=helpers.TEZOS_ED25519_ADDRESS_PREFIX
|
||||||
|
)
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
desc = address_n_to_str(msg.address_n)
|
desc = address_n_to_str(msg.address_n)
|
||||||
|
@ -4,16 +4,17 @@ from trezor.messages.TezosPublicKey import TezosPublicKey
|
|||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import chunks
|
from trezor.utils import chunks
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.confirm import require_confirm
|
from apps.common.confirm import require_confirm
|
||||||
from apps.tezos.helpers import TEZOS_CURVE, TEZOS_PUBLICKEY_PREFIX, base58_encode_check
|
from apps.tezos import helpers
|
||||||
|
|
||||||
|
|
||||||
async def get_public_key(ctx, msg):
|
async def get_public_key(ctx, msg):
|
||||||
node = await seed.derive_node(ctx, msg.address_n, TEZOS_CURVE)
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
|
||||||
|
|
||||||
pk = seed.remove_ed25519_prefix(node.public_key())
|
pk = seed.remove_ed25519_prefix(node.public_key())
|
||||||
pk_prefixed = base58_encode_check(pk, prefix=TEZOS_PUBLICKEY_PREFIX)
|
pk_prefixed = helpers.base58_encode_check(pk, prefix=helpers.TEZOS_PUBLICKEY_PREFIX)
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
await _show_tezos_pubkey(ctx, pk_prefixed)
|
await _show_tezos_pubkey(ctx, pk_prefixed)
|
||||||
|
@ -2,6 +2,8 @@ from micropython import const
|
|||||||
|
|
||||||
from trezor.crypto import base58
|
from trezor.crypto import base58
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
|
|
||||||
TEZOS_CURVE = "ed25519"
|
TEZOS_CURVE = "ed25519"
|
||||||
TEZOS_AMOUNT_DIVISIBILITY = const(6)
|
TEZOS_AMOUNT_DIVISIBILITY = const(6)
|
||||||
TEZOS_ED25519_ADDRESS_PREFIX = "tz1"
|
TEZOS_ED25519_ADDRESS_PREFIX = "tz1"
|
||||||
@ -35,3 +37,19 @@ def base58_decode_check(enc, prefix=None):
|
|||||||
if prefix is not None:
|
if prefix is not None:
|
||||||
decoded = decoded[len(TEZOS_PREFIX_BYTES[prefix]) :]
|
decoded = decoded[len(TEZOS_PREFIX_BYTES[prefix]) :]
|
||||||
return decoded
|
return decoded
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(path: list) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to equal 44'/1729'/a',
|
||||||
|
where `a` is an account index from 0 to 1 000 000.
|
||||||
|
"""
|
||||||
|
if len(path) != 3:
|
||||||
|
return False
|
||||||
|
if path[0] != 44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[1] != 1729 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -4,19 +4,14 @@ from trezor.crypto.curve import ed25519
|
|||||||
from trezor.messages import TezosContractType
|
from trezor.messages import TezosContractType
|
||||||
from trezor.messages.TezosSignedTx import TezosSignedTx
|
from trezor.messages.TezosSignedTx import TezosSignedTx
|
||||||
|
|
||||||
from apps.common import seed
|
from apps.common import paths, seed
|
||||||
from apps.common.writers import write_bytes, write_uint8
|
from apps.common.writers import write_bytes, write_uint8
|
||||||
from apps.tezos import layout
|
from apps.tezos import layout, helpers
|
||||||
from apps.tezos.helpers import (
|
|
||||||
TEZOS_CURVE,
|
|
||||||
TEZOS_ORIGINATED_ADDRESS_PREFIX,
|
|
||||||
TEZOS_SIGNATURE_PREFIX,
|
|
||||||
base58_encode_check,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def sign_tx(ctx, msg):
|
async def sign_tx(ctx, msg):
|
||||||
node = await seed.derive_node(ctx, msg.address_n, TEZOS_CURVE)
|
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
|
||||||
|
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
|
||||||
|
|
||||||
if msg.transaction is not None:
|
if msg.transaction is not None:
|
||||||
to = _get_address_from_contract(msg.transaction.destination)
|
to = _get_address_from_contract(msg.transaction.destination)
|
||||||
@ -71,9 +66,11 @@ async def sign_tx(ctx, msg):
|
|||||||
|
|
||||||
sig_op_contents = opbytes + signature
|
sig_op_contents = opbytes + signature
|
||||||
sig_op_contents_hash = hashlib.blake2b(sig_op_contents, outlen=32).digest()
|
sig_op_contents_hash = hashlib.blake2b(sig_op_contents, outlen=32).digest()
|
||||||
ophash = base58_encode_check(sig_op_contents_hash, prefix="o")
|
ophash = helpers.base58_encode_check(sig_op_contents_hash, prefix="o")
|
||||||
|
|
||||||
sig_prefixed = base58_encode_check(signature, prefix=TEZOS_SIGNATURE_PREFIX)
|
sig_prefixed = helpers.base58_encode_check(
|
||||||
|
signature, prefix=helpers.TEZOS_SIGNATURE_PREFIX
|
||||||
|
)
|
||||||
|
|
||||||
return TezosSignedTx(
|
return TezosSignedTx(
|
||||||
signature=sig_prefixed, sig_op_contents=sig_op_contents, operation_hash=ophash
|
signature=sig_prefixed, sig_op_contents=sig_op_contents, operation_hash=ophash
|
||||||
@ -85,7 +82,7 @@ def _get_address_by_tag(address_hash):
|
|||||||
tag = int(address_hash[0])
|
tag = int(address_hash[0])
|
||||||
|
|
||||||
if 0 <= tag < len(prefixes):
|
if 0 <= tag < len(prefixes):
|
||||||
return base58_encode_check(address_hash[1:], prefix=prefixes[tag])
|
return helpers.base58_encode_check(address_hash[1:], prefix=prefixes[tag])
|
||||||
raise wire.DataError("Invalid tag in address hash")
|
raise wire.DataError("Invalid tag in address hash")
|
||||||
|
|
||||||
|
|
||||||
@ -94,8 +91,8 @@ def _get_address_from_contract(address):
|
|||||||
return _get_address_by_tag(address.hash)
|
return _get_address_by_tag(address.hash)
|
||||||
|
|
||||||
elif address.tag == TezosContractType.Originated:
|
elif address.tag == TezosContractType.Originated:
|
||||||
return base58_encode_check(
|
return helpers.base58_encode_check(
|
||||||
address.hash[:-1], prefix=TEZOS_ORIGINATED_ADDRESS_PREFIX
|
address.hash[:-1], prefix=helpers.TEZOS_ORIGINATED_ADDRESS_PREFIX
|
||||||
)
|
)
|
||||||
|
|
||||||
raise wire.DataError("Invalid contract type")
|
raise wire.DataError("Invalid contract type")
|
||||||
|
@ -3,6 +3,7 @@ from trezor.messages.Address import Address
|
|||||||
|
|
||||||
from apps.common import coins, seed
|
from apps.common import coins, seed
|
||||||
from apps.common.layout import address_n_to_str, show_address, show_qr
|
from apps.common.layout import address_n_to_str, show_address, show_qr
|
||||||
|
from apps.common.paths import validate_path
|
||||||
from apps.wallet.sign_tx import addresses
|
from apps.wallet.sign_tx import addresses
|
||||||
|
|
||||||
|
|
||||||
@ -10,6 +11,14 @@ async def get_address(ctx, msg):
|
|||||||
coin_name = msg.coin_name or "Bitcoin"
|
coin_name = msg.coin_name or "Bitcoin"
|
||||||
coin = coins.by_name(coin_name)
|
coin = coins.by_name(coin_name)
|
||||||
|
|
||||||
|
await validate_path(
|
||||||
|
ctx,
|
||||||
|
addresses.validate_full_path,
|
||||||
|
path=msg.address_n,
|
||||||
|
coin=coin,
|
||||||
|
script_type=msg.script_type,
|
||||||
|
)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, msg.address_n, curve_name=coin.curve_name)
|
node = await seed.derive_node(ctx, msg.address_n, curve_name=coin.curve_name)
|
||||||
address = addresses.get_address(msg.script_type, coin, node, msg.multisig)
|
address = addresses.get_address(msg.script_type, coin, node, msg.multisig)
|
||||||
address_short = addresses.address_short(coin, address)
|
address_short = addresses.address_short(coin, address)
|
||||||
|
@ -3,7 +3,8 @@ from trezor.messages import InputScriptType
|
|||||||
from trezor.messages.HDNodeType import HDNodeType
|
from trezor.messages.HDNodeType import HDNodeType
|
||||||
from trezor.messages.PublicKey import PublicKey
|
from trezor.messages.PublicKey import PublicKey
|
||||||
|
|
||||||
from apps.common import coins, layout, seed
|
from apps.common import coins, layout, paths, seed
|
||||||
|
from apps.wallet.sign_tx.addresses import validate_path_for_bitcoin_public_key
|
||||||
|
|
||||||
|
|
||||||
async def get_public_key(ctx, msg):
|
async def get_public_key(ctx, msg):
|
||||||
@ -11,6 +12,10 @@ async def get_public_key(ctx, msg):
|
|||||||
coin = coins.by_name(coin_name)
|
coin = coins.by_name(coin_name)
|
||||||
script_type = msg.script_type or InputScriptType.SPENDADDRESS
|
script_type = msg.script_type or InputScriptType.SPENDADDRESS
|
||||||
|
|
||||||
|
await paths.validate_path(
|
||||||
|
ctx, validate_path_for_bitcoin_public_key, path=msg.address_n, coin=coin
|
||||||
|
)
|
||||||
|
|
||||||
curve_name = msg.ecdsa_curve_name
|
curve_name = msg.ecdsa_curve_name
|
||||||
if not curve_name:
|
if not curve_name:
|
||||||
curve_name = coin.curve_name
|
curve_name = coin.curve_name
|
||||||
|
@ -6,8 +6,9 @@ from trezor.ui.text import Text
|
|||||||
|
|
||||||
from apps.common import coins, seed
|
from apps.common import coins, seed
|
||||||
from apps.common.confirm import require_confirm
|
from apps.common.confirm import require_confirm
|
||||||
|
from apps.common.paths import validate_path
|
||||||
from apps.common.signverify import message_digest, split_message
|
from apps.common.signverify import message_digest, split_message
|
||||||
from apps.wallet.sign_tx.addresses import get_address
|
from apps.wallet.sign_tx.addresses import get_address, validate_full_path
|
||||||
|
|
||||||
|
|
||||||
async def sign_message(ctx, msg):
|
async def sign_message(ctx, msg):
|
||||||
@ -19,6 +20,14 @@ async def sign_message(ctx, msg):
|
|||||||
|
|
||||||
await require_confirm_sign_message(ctx, message)
|
await require_confirm_sign_message(ctx, message)
|
||||||
|
|
||||||
|
await validate_path(
|
||||||
|
ctx,
|
||||||
|
validate_full_path,
|
||||||
|
path=msg.address_n,
|
||||||
|
coin=coin,
|
||||||
|
script_type=msg.script_type,
|
||||||
|
)
|
||||||
|
|
||||||
node = await seed.derive_node(ctx, address_n, curve_name=coin.curve_name)
|
node = await seed.derive_node(ctx, address_n, curve_name=coin.curve_name)
|
||||||
seckey = node.private_key()
|
seckey = node.private_key()
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from trezor.messages.MessageType import TxAck
|
|||||||
from trezor.messages.RequestType import TXFINISHED
|
from trezor.messages.RequestType import TXFINISHED
|
||||||
from trezor.messages.TxRequest import TxRequest
|
from trezor.messages.TxRequest import TxRequest
|
||||||
|
|
||||||
from apps.common import coins, seed
|
from apps.common import coins, paths, seed
|
||||||
from apps.wallet.sign_tx.helpers import (
|
from apps.wallet.sign_tx.helpers import (
|
||||||
UiConfirmFeeOverThreshold,
|
UiConfirmFeeOverThreshold,
|
||||||
UiConfirmForeignAddress,
|
UiConfirmForeignAddress,
|
||||||
@ -50,7 +50,7 @@ async def sign_tx(ctx, msg):
|
|||||||
res = await layout.confirm_feeoverthreshold(ctx, req.fee, req.coin)
|
res = await layout.confirm_feeoverthreshold(ctx, req.fee, req.coin)
|
||||||
progress.report_init()
|
progress.report_init()
|
||||||
elif isinstance(req, UiConfirmForeignAddress):
|
elif isinstance(req, UiConfirmForeignAddress):
|
||||||
res = await layout.confirm_foreign_address(ctx, req.address_n, req.coin)
|
res = await paths.show_path_warning(ctx, req.address_n)
|
||||||
else:
|
else:
|
||||||
raise TypeError("Invalid signing instruction")
|
raise TypeError("Invalid signing instruction")
|
||||||
return req
|
return req
|
||||||
|
@ -5,7 +5,7 @@ from trezor.crypto.hashlib import sha256
|
|||||||
from trezor.messages import FailureType, InputScriptType
|
from trezor.messages import FailureType, InputScriptType
|
||||||
from trezor.utils import ensure
|
from trezor.utils import ensure
|
||||||
|
|
||||||
from apps.common import address_type
|
from apps.common import HARDENED, address_type, paths
|
||||||
from apps.common.coininfo import CoinInfo
|
from apps.common.coininfo import CoinInfo
|
||||||
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys, multisig_pubkey_index
|
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys, multisig_pubkey_index
|
||||||
from apps.wallet.sign_tx.scripts import (
|
from apps.wallet.sign_tx.scripts import (
|
||||||
@ -190,3 +190,90 @@ def address_short(coin: CoinInfo, address: str) -> str:
|
|||||||
return address[len(coin.cashaddr_prefix) + 1 :]
|
return address[len(coin.cashaddr_prefix) + 1 :]
|
||||||
else:
|
else:
|
||||||
return address
|
return address
|
||||||
|
|
||||||
|
|
||||||
|
def validate_full_path(
|
||||||
|
path: list, coin: CoinInfo, script_type: InputScriptType
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to fit Bitcoin-like coins. We mostly use
|
||||||
|
44', but for segwit-enabled coins we use either 49' (P2WPKH-nested-in-P2SH)
|
||||||
|
or 84' (native P2WPKH). Electrum uses m/45' for legacy addresses and
|
||||||
|
m/48' for segwit, so those two are allowed as well.
|
||||||
|
|
||||||
|
See docs/coins for what paths are allowed. Please note that this is not
|
||||||
|
a comprehensive check, some nuances are omitted for simplification.
|
||||||
|
"""
|
||||||
|
if len(path) != 5:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_purpose(path[0], coin):
|
||||||
|
return False
|
||||||
|
if not validate_purpose_against_script_type(path[0], script_type):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if path[1] != coin.slip44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 20 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[3] not in [0, 1]:
|
||||||
|
return False
|
||||||
|
if path[4] > 1000000:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def validate_purpose(purpose: int, coin: CoinInfo) -> bool:
|
||||||
|
if purpose not in (44 | HARDENED, 48 | HARDENED, 49 | HARDENED, 84 | HARDENED):
|
||||||
|
return False
|
||||||
|
if not coin.segwit and purpose not in (44 | HARDENED, 48 | HARDENED):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def validate_purpose_against_script_type(
|
||||||
|
purpose: int, script_type: InputScriptType
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Validates purpose against provided input's script type:
|
||||||
|
- 44 for spending address (script_type == SPENDADDRESS)
|
||||||
|
- 48 for multisig (script_type == SPENDMULTISIG)
|
||||||
|
- 49 for p2sh-segwit spend (script_type == SPENDP2SHWITNESS)
|
||||||
|
- 84 for native segwit spend (script_type == SPENDWITNESS)
|
||||||
|
"""
|
||||||
|
if purpose == 44 | HARDENED and script_type != InputScriptType.SPENDADDRESS:
|
||||||
|
return False
|
||||||
|
if purpose == 48 | HARDENED and script_type != InputScriptType.SPENDMULTISIG:
|
||||||
|
return False
|
||||||
|
if ( # p2wsh-nested-in-p2sh
|
||||||
|
purpose == 49 | HARDENED and script_type != InputScriptType.SPENDP2SHWITNESS
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
if ( # p2wsh
|
||||||
|
purpose == 84 | HARDENED and script_type != InputScriptType.SPENDWITNESS
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def validate_path_for_bitcoin_public_key(path: list, coin: CoinInfo) -> bool:
|
||||||
|
"""
|
||||||
|
Validates derivation path to fit Bitcoin-like coins for GetPublicKey.
|
||||||
|
Script type is omitted here because it is not usually sent.
|
||||||
|
"""
|
||||||
|
length = len(path)
|
||||||
|
if length < 3 or length > 5:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not validate_purpose(path[0], coin):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if path[1] != coin.slip44 | HARDENED:
|
||||||
|
return False
|
||||||
|
if path[2] < HARDENED or path[2] > 20 | HARDENED:
|
||||||
|
return False
|
||||||
|
if length > 3 and paths.is_hardened(path[3]):
|
||||||
|
return False
|
||||||
|
if length > 4 and paths.is_hardened(path[4]):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -39,9 +39,8 @@ class UiConfirmFeeOverThreshold:
|
|||||||
|
|
||||||
|
|
||||||
class UiConfirmForeignAddress:
|
class UiConfirmForeignAddress:
|
||||||
def __init__(self, address_n: list, coin: CoinInfo):
|
def __init__(self, address_n: list):
|
||||||
self.address_n = address_n
|
self.address_n = address_n
|
||||||
self.coin = coin
|
|
||||||
|
|
||||||
|
|
||||||
def confirm_output(output: TxOutputType, coin: CoinInfo):
|
def confirm_output(output: TxOutputType, coin: CoinInfo):
|
||||||
@ -56,8 +55,8 @@ def confirm_feeoverthreshold(fee: int, coin: CoinInfo):
|
|||||||
return (yield UiConfirmFeeOverThreshold(fee, coin))
|
return (yield UiConfirmFeeOverThreshold(fee, coin))
|
||||||
|
|
||||||
|
|
||||||
def confirm_foreign_address(address_n: list, coin: CoinInfo):
|
def confirm_foreign_address(address_n: list):
|
||||||
return (yield UiConfirmForeignAddress(address_n, coin))
|
return (yield UiConfirmForeignAddress(address_n))
|
||||||
|
|
||||||
|
|
||||||
def request_tx_meta(tx_req: TxRequest, tx_hash: bytes = None):
|
def request_tx_meta(tx_req: TxRequest, tx_hash: bytes = None):
|
||||||
|
@ -107,8 +107,8 @@ async def check_tx_fee(tx: SignTx, root: bip32.HDNode):
|
|||||||
hash143.add_prevouts(txi) # all inputs are included (non-segwit as well)
|
hash143.add_prevouts(txi) # all inputs are included (non-segwit as well)
|
||||||
hash143.add_sequence(txi)
|
hash143.add_sequence(txi)
|
||||||
|
|
||||||
if not address_n_matches_coin(txi.address_n, coin):
|
if not validate_full_path(txi.address_n, coin, txi.script_type):
|
||||||
await confirm_foreign_address(txi.address_n, coin)
|
await confirm_foreign_address(txi.address_n)
|
||||||
|
|
||||||
if txi.multisig:
|
if txi.multisig:
|
||||||
multifp.add(txi.multisig)
|
multifp.add(txi.multisig)
|
||||||
@ -828,16 +828,6 @@ def node_derive(root: bip32.HDNode, address_n: list) -> bip32.HDNode:
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
def address_n_matches_coin(address_n: list, coin: CoinInfo) -> bool:
|
|
||||||
if len(address_n) < 2:
|
|
||||||
return True # path is too short
|
|
||||||
if address_n[0] not in (44 | 0x80000000, 49 | 0x80000000, 84 | 0x80000000):
|
|
||||||
return True # path is not BIP44/49/84
|
|
||||||
return address_n[1] == (
|
|
||||||
coin.slip44 | 0x80000000
|
|
||||||
) # check whether coin_type matches slip44 value
|
|
||||||
|
|
||||||
|
|
||||||
def ecdsa_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
def ecdsa_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
||||||
sig = secp256k1.sign(node.private_key(), digest)
|
sig = secp256k1.sign(node.private_key(), digest)
|
||||||
sigder = der.encode_seq((sig[1:33], sig[33:65]))
|
sigder = der.encode_seq((sig[1:33], sig[33:65]))
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from common import *
|
from common import *
|
||||||
from apps.common import seed
|
from apps.common import seed
|
||||||
from trezor import wire
|
|
||||||
|
|
||||||
|
from apps.common import HARDENED
|
||||||
from apps.cardano.address import (
|
from apps.cardano.address import (
|
||||||
_get_address_root,
|
_get_address_root,
|
||||||
_address_hash,
|
_address_hash,
|
||||||
validate_derivation_path,
|
validate_full_path,
|
||||||
derive_address_and_node
|
derive_address_and_node
|
||||||
)
|
)
|
||||||
from trezor.crypto import bip32
|
from trezor.crypto import bip32
|
||||||
@ -131,27 +131,32 @@ class TestCardanoAddress(unittest.TestCase):
|
|||||||
self.assertEqual(result, b'\x1c\xca\xee\xc9\x80\xaf}\xb0\x9a\xa8\x96E\xd6\xa4\xd1\xb4\x13\x85\xb9\xc2q\x1d5/{\x12"\xca')
|
self.assertEqual(result, b'\x1c\xca\xee\xc9\x80\xaf}\xb0\x9a\xa8\x96E\xd6\xa4\xd1\xb4\x13\x85\xb9\xc2q\x1d5/{\x12"\xca')
|
||||||
|
|
||||||
|
|
||||||
def test_validate_derivation_path(self):
|
def test_paths(self):
|
||||||
incorrect_derivation_paths = [
|
incorrect_derivation_paths = [
|
||||||
[0x80000000 | 44],
|
[HARDENED | 44],
|
||||||
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815],
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 1815, HARDENED | 1815, HARDENED | 1815, HARDENED | 1815],
|
||||||
[0x80000000 | 43, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815],
|
[HARDENED | 43, HARDENED | 1815, HARDENED | 1815, HARDENED | 1815, HARDENED | 1815],
|
||||||
[0x80000000 | 44, 0x80000000 | 1816, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815],
|
[HARDENED | 44, HARDENED | 1816, HARDENED | 1815, HARDENED | 1815, HARDENED | 1815],
|
||||||
|
[HARDENED | 44, HARDENED | 1815, 0],
|
||||||
|
[HARDENED | 44, HARDENED | 1815, 0, 0],
|
||||||
|
[HARDENED | 44, HARDENED | 1815],
|
||||||
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 0],
|
||||||
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 1815, 1, 1],
|
||||||
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 1815, 0, 0], # a too large
|
||||||
]
|
]
|
||||||
|
|
||||||
correct_derivation_paths = [
|
correct_derivation_paths = [
|
||||||
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815, 0x80000000 | 1815],
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 0, 0, 1],
|
||||||
[0x80000000 | 44, 0x80000000 | 1815],
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 9, 0, 4],
|
||||||
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000],
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 0, 0, 9],
|
||||||
[0x80000000 | 44, 0x80000000 | 1815, 0],
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 0, 1, 1],
|
||||||
[0x80000000 | 44, 0x80000000 | 1815, 0, 0],
|
[HARDENED | 44, HARDENED | 1815, HARDENED | 0, 1, 9],
|
||||||
]
|
]
|
||||||
|
|
||||||
for derivation_path in incorrect_derivation_paths:
|
for path in incorrect_derivation_paths:
|
||||||
self.assertRaises(wire.ProcessError, validate_derivation_path, derivation_path)
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
for derivation_path in correct_derivation_paths:
|
for path in correct_derivation_paths:
|
||||||
self.assertEqual(derivation_path, validate_derivation_path(derivation_path))
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
def test_get_address_root_scheme(self):
|
def test_get_address_root_scheme(self):
|
||||||
mnemonic = "all all all all all all all all all all all all"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
|
46
tests/test_apps.common.paths.py
Normal file
46
tests/test_apps.common.paths.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from common import *
|
||||||
|
from apps.common import HARDENED
|
||||||
|
from apps.common.paths import validate_path_for_get_public_key, is_hardened
|
||||||
|
|
||||||
|
|
||||||
|
class TestPaths(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_is_hardened(self):
|
||||||
|
self.assertTrue(is_hardened(44 | HARDENED))
|
||||||
|
self.assertTrue(is_hardened(0 | HARDENED))
|
||||||
|
self.assertTrue(is_hardened(99999 | HARDENED))
|
||||||
|
|
||||||
|
self.assertFalse(is_hardened(44))
|
||||||
|
self.assertFalse(is_hardened(0))
|
||||||
|
self.assertFalse(is_hardened(99999))
|
||||||
|
|
||||||
|
def test_path_for_get_public_key(self):
|
||||||
|
# 44'/41'/0'
|
||||||
|
self.assertTrue(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED], 41))
|
||||||
|
# 44'/111'/0'
|
||||||
|
self.assertTrue(validate_path_for_get_public_key([44 | HARDENED, 111 | HARDENED, 0 | HARDENED], 111))
|
||||||
|
# 44'/0'/0'/0
|
||||||
|
self.assertTrue(validate_path_for_get_public_key([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0], 0))
|
||||||
|
# 44'/0'/0'/0/0
|
||||||
|
self.assertTrue(validate_path_for_get_public_key([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], 0))
|
||||||
|
|
||||||
|
# 44'/41'
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED], 41))
|
||||||
|
# 44'/41'/0
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0], 41))
|
||||||
|
# 44'/41'/0' slip44 mismatch
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED], 99))
|
||||||
|
# # 44'/41'/0'/0'
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED, 0 | HARDENED], 41))
|
||||||
|
# # 44'/41'/0'/0'/0
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0], 41))
|
||||||
|
# # 44'/41'/0'/0'/0'
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], 41))
|
||||||
|
# # 44'/41'/0'/0/0/0
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED, 0, 0, 0], 41))
|
||||||
|
# # 44'/41'/0'/0/0'
|
||||||
|
self.assertFalse(validate_path_for_get_public_key([44 | HARDENED, 41 | HARDENED, 0 | HARDENED, 0, 0 | HARDENED], 41))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -1,5 +1,6 @@
|
|||||||
from common import *
|
from common import *
|
||||||
from apps.ethereum.get_address import _ethereum_address_hex
|
from apps.common.paths import HARDENED
|
||||||
|
from apps.ethereum.address import ethereum_address_hex, validate_full_path
|
||||||
from apps.ethereum.networks import NetworkInfo
|
from apps.ethereum.networks import NetworkInfo
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ class TestEthereumGetAddress(unittest.TestCase):
|
|||||||
for s in eip55:
|
for s in eip55:
|
||||||
s = s[2:]
|
s = s[2:]
|
||||||
b = bytes([int(s[i:i + 2], 16) for i in range(0, len(s), 2)])
|
b = bytes([int(s[i:i + 2], 16) for i in range(0, len(s), 2)])
|
||||||
h = _ethereum_address_hex(b)
|
h = ethereum_address_hex(b)
|
||||||
self.assertEqual(h, '0x' + s)
|
self.assertEqual(h, '0x' + s)
|
||||||
|
|
||||||
def test_ethereum_address_hex_rskip60(self):
|
def test_ethereum_address_hex_rskip60(self):
|
||||||
@ -41,15 +42,39 @@ class TestEthereumGetAddress(unittest.TestCase):
|
|||||||
for s in rskip60_chain_30:
|
for s in rskip60_chain_30:
|
||||||
s = s[2:]
|
s = s[2:]
|
||||||
b = bytes([int(s[i:i + 2], 16) for i in range(0, len(s), 2)])
|
b = bytes([int(s[i:i + 2], 16) for i in range(0, len(s), 2)])
|
||||||
h = _ethereum_address_hex(b, n)
|
h = ethereum_address_hex(b, n)
|
||||||
self.assertEqual(h, '0x' + s)
|
self.assertEqual(h, '0x' + s)
|
||||||
n.chain_id = 31
|
n.chain_id = 31
|
||||||
for s in rskip60_chain_31:
|
for s in rskip60_chain_31:
|
||||||
s = s[2:]
|
s = s[2:]
|
||||||
b = bytes([int(s[i:i + 2], 16) for i in range(0, len(s), 2)])
|
b = bytes([int(s[i:i + 2], 16) for i in range(0, len(s), 2)])
|
||||||
h = _ethereum_address_hex(b, n)
|
h = ethereum_address_hex(b, n)
|
||||||
self.assertEqual(h, '0x' + s)
|
self.assertEqual(h, '0x' + s)
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/60'/0'/0/i is correct
|
||||||
|
incorrect_paths = [
|
||||||
|
[44 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0, 0],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 1 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 160 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
]
|
||||||
|
correct_paths = [
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 9],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 9999],
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in incorrect_paths:
|
||||||
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
|
for path in correct_paths:
|
||||||
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
36
tests/test_apps.lisk.address.py
Normal file
36
tests/test_apps.lisk.address.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from common import *
|
||||||
|
from apps.common.paths import HARDENED
|
||||||
|
from apps.lisk.helpers import validate_full_path
|
||||||
|
|
||||||
|
|
||||||
|
class TestLiskGetAddress(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/134'/a' is correct
|
||||||
|
incorrect_paths = [
|
||||||
|
[44 | HARDENED],
|
||||||
|
[44 | HARDENED, 134 | HARDENED],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 0],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 9999000 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[1 | HARDENED, 1 | HARDENED, 1 | HARDENED],
|
||||||
|
]
|
||||||
|
correct_paths = [
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 3 | HARDENED],
|
||||||
|
[44 | HARDENED, 134 | HARDENED, 9 | HARDENED],
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in incorrect_paths:
|
||||||
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
|
for path in correct_paths:
|
||||||
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
36
tests/test_apps.monero.address.py
Normal file
36
tests/test_apps.monero.address.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from common import *
|
||||||
|
from apps.common.paths import HARDENED
|
||||||
|
from apps.monero.misc import validate_full_path
|
||||||
|
|
||||||
|
|
||||||
|
class TestMoneroGetAddress(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/128'/a' is correct
|
||||||
|
incorrect_paths = [
|
||||||
|
[44 | HARDENED],
|
||||||
|
[44 | HARDENED, 128 | HARDENED],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 0],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 9999000 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[1 | HARDENED, 1 | HARDENED, 1 | HARDENED],
|
||||||
|
]
|
||||||
|
correct_paths = [
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 3 | HARDENED],
|
||||||
|
[44 | HARDENED, 128 | HARDENED, 9 | HARDENED],
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in incorrect_paths:
|
||||||
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
|
for path in correct_paths:
|
||||||
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -1,7 +1,8 @@
|
|||||||
from common import *
|
from common import *
|
||||||
from ubinascii import unhexlify
|
from ubinascii import unhexlify
|
||||||
from trezor.crypto import nem
|
from trezor.crypto import nem
|
||||||
from apps.nem.helpers import NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET
|
from apps.common import HARDENED
|
||||||
|
from apps.nem.helpers import check_path, NEM_NETWORK_MAINNET, NEM_NETWORK_TESTNET
|
||||||
|
|
||||||
|
|
||||||
class TestNemAddress(unittest.TestCase):
|
class TestNemAddress(unittest.TestCase):
|
||||||
@ -32,6 +33,47 @@ class TestNemAddress(unittest.TestCase):
|
|||||||
validity = nem.validate_address('NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP', NEM_NETWORK_TESTNET)
|
validity = nem.validate_address('NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP', NEM_NETWORK_TESTNET)
|
||||||
self.assertFalse(validity)
|
self.assertFalse(validity)
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/43'/0'/0'/0'
|
||||||
|
self.assertTrue(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 44'/43'/0'/0'/0'
|
||||||
|
self.assertTrue(check_path([44 | HARDENED, 43 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 44'/1'/0'/0'/0' testnet
|
||||||
|
self.assertTrue(check_path([44 | HARDENED, 1 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0 | HARDENED], network=0x98))
|
||||||
|
# 44'/43'/0'
|
||||||
|
self.assertTrue(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 44'/43'/2'
|
||||||
|
self.assertTrue(check_path([44 | HARDENED, 43 | HARDENED, 2 | HARDENED]))
|
||||||
|
# 44'/1'/0' testnet
|
||||||
|
self.assertTrue(check_path([44 | HARDENED, 1 | HARDENED, 0 | HARDENED], network=0x98))
|
||||||
|
|
||||||
|
# 44'/43'/0'/0'/1'
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1 | HARDENED]))
|
||||||
|
# 44'/43'/0'/1'/1'
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED, 1 | HARDENED, 1 | HARDENED]))
|
||||||
|
# 44'/43'/0'/1'/0'
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 44'/43'/99999'/0'/0'
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 99999000 | HARDENED, 0 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 44'/99'/0'/0'/0'
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 99 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 1'/43'/0'/0'/0'
|
||||||
|
self.assertFalse(check_path([1 | HARDENED, 43 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED]))
|
||||||
|
# 44'/43'/0'/0/0
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED, 0, 0]))
|
||||||
|
# 44'/43'/0'/0/5
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0 | HARDENED, 0, 5]))
|
||||||
|
# 44'/1'/3'/0'/1' testnet
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 1 | HARDENED, 3 | HARDENED, 0 | HARDENED, 1 | HARDENED], network=0x98))
|
||||||
|
|
||||||
|
# 44'/43'/0/0/1
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0, 0, 1]))
|
||||||
|
# 44'/43'/0/0/0
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0, 0, 0]))
|
||||||
|
# 44'/43'/0/0'/0'
|
||||||
|
self.assertFalse(check_path([44 | HARDENED, 43 | HARDENED, 0, 0 | HARDENED, 0 | HARDENED]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
47
tests/test_apps.ripple.address.py
Normal file
47
tests/test_apps.ripple.address.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from common import *
|
||||||
|
from apps.common.paths import HARDENED
|
||||||
|
from apps.ripple.helpers import address_from_public_key, validate_full_path
|
||||||
|
|
||||||
|
|
||||||
|
class TestRippleAddress(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_pubkey_to_address(self):
|
||||||
|
addr = address_from_public_key(unhexlify('ed9434799226374926eda3b54b1b461b4abf7237962eae18528fea67595397fa32'))
|
||||||
|
self.assertEqual(addr, 'rDTXLQ7ZKZVKz33zJbHjgVShjsBnqMBhmN')
|
||||||
|
|
||||||
|
addr = address_from_public_key(unhexlify('03e2b079e9b09ae8916da8f5ee40cbda9578dbe7c820553fe4d5f872eec7b1fdd4'))
|
||||||
|
self.assertEqual(addr, 'rhq549rEtUrJowuxQC2WsHNGLjAjBQdAe8')
|
||||||
|
|
||||||
|
addr = address_from_public_key(unhexlify('0282ee731039929e97db6aec242002e9aa62cd62b989136df231f4bb9b8b7c7eb2'))
|
||||||
|
self.assertEqual(addr, 'rKzE5DTyF9G6z7k7j27T2xEas2eMo85kmw')
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/144'/a'/0/0 is correct
|
||||||
|
incorrect_paths = [
|
||||||
|
[44 | HARDENED],
|
||||||
|
[44 | HARDENED, 144 | HARDENED],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 0],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 0 | HARDENED, 0, 5],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 9999 | HARDENED],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 9999000 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[1 | HARDENED, 1 | HARDENED, 1 | HARDENED],
|
||||||
|
]
|
||||||
|
correct_paths = [
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 3 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 144 | HARDENED, 9 | HARDENED, 0, 0],
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in incorrect_paths:
|
||||||
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
|
for path in correct_paths:
|
||||||
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -1,19 +0,0 @@
|
|||||||
from common import *
|
|
||||||
from apps.ripple.helpers import address_from_public_key
|
|
||||||
|
|
||||||
|
|
||||||
class TestStellarPubkeyToAddress(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_pubkey_to_address(self):
|
|
||||||
addr = address_from_public_key(unhexlify('ed9434799226374926eda3b54b1b461b4abf7237962eae18528fea67595397fa32'))
|
|
||||||
self.assertEqual(addr, 'rDTXLQ7ZKZVKz33zJbHjgVShjsBnqMBhmN')
|
|
||||||
|
|
||||||
addr = address_from_public_key(unhexlify('03e2b079e9b09ae8916da8f5ee40cbda9578dbe7c820553fe4d5f872eec7b1fdd4'))
|
|
||||||
self.assertEqual(addr, 'rhq549rEtUrJowuxQC2WsHNGLjAjBQdAe8')
|
|
||||||
|
|
||||||
addr = address_from_public_key(unhexlify('0282ee731039929e97db6aec242002e9aa62cd62b989136df231f4bb9b8b7c7eb2'))
|
|
||||||
self.assertEqual(addr, 'rKzE5DTyF9G6z7k7j27T2xEas2eMo85kmw')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
@ -1,9 +1,10 @@
|
|||||||
from common import *
|
from common import *
|
||||||
from apps.stellar.helpers import address_from_public_key, public_key_from_address
|
from apps.common.paths import HARDENED
|
||||||
|
from apps.stellar.helpers import address_from_public_key, public_key_from_address, validate_full_path
|
||||||
from trezor.wire import ProcessError
|
from trezor.wire import ProcessError
|
||||||
|
|
||||||
|
|
||||||
class TestStellarAddressToPubkey(unittest.TestCase):
|
class TestStellarAddress(unittest.TestCase):
|
||||||
|
|
||||||
def test_address_to_pubkey(self):
|
def test_address_to_pubkey(self):
|
||||||
self.assertEqual(public_key_from_address('GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V'),
|
self.assertEqual(public_key_from_address('GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V'),
|
||||||
@ -33,6 +34,32 @@ class TestStellarAddressToPubkey(unittest.TestCase):
|
|||||||
with self.assertRaises(ProcessError):
|
with self.assertRaises(ProcessError):
|
||||||
public_key_from_address('GCN2K2HG53AWX2SP5UHRPMJUUHLJF2XBTGSXROTPWRGAYJCDDP63J2AA') # invalid checksum
|
public_key_from_address('GCN2K2HG53AWX2SP5UHRPMJUUHLJF2XBTGSXROTPWRGAYJCDDP63J2AA') # invalid checksum
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/148'/a' is correct
|
||||||
|
incorrect_paths = [
|
||||||
|
[44 | HARDENED],
|
||||||
|
[44 | HARDENED, 148 | HARDENED],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 0],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 9999000 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[1 | HARDENED, 1 | HARDENED, 1 | HARDENED],
|
||||||
|
]
|
||||||
|
correct_paths = [
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 3 | HARDENED],
|
||||||
|
[44 | HARDENED, 148 | HARDENED, 9 | HARDENED],
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in incorrect_paths:
|
||||||
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
|
for path in correct_paths:
|
||||||
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
@ -5,6 +5,8 @@ from trezor.messages import TezosContractType
|
|||||||
from trezor.messages.TezosContractID import TezosContractID
|
from trezor.messages.TezosContractID import TezosContractID
|
||||||
|
|
||||||
from apps.tezos.sign_tx import _get_address_from_contract
|
from apps.tezos.sign_tx import _get_address_from_contract
|
||||||
|
from apps.tezos.helpers import validate_full_path
|
||||||
|
from apps.common.paths import HARDENED
|
||||||
|
|
||||||
|
|
||||||
class TestTezosAddress(unittest.TestCase):
|
class TestTezosAddress(unittest.TestCase):
|
||||||
@ -38,6 +40,32 @@ class TestTezosAddress(unittest.TestCase):
|
|||||||
for i, contract in enumerate(contracts):
|
for i, contract in enumerate(contracts):
|
||||||
self.assertEqual(_get_address_from_contract(contract), outputs[i])
|
self.assertEqual(_get_address_from_contract(contract), outputs[i])
|
||||||
|
|
||||||
|
def test_paths(self):
|
||||||
|
# 44'/1729'/a' is correct
|
||||||
|
incorrect_paths = [
|
||||||
|
[44 | HARDENED],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 0],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 9999000 | HARDENED],
|
||||||
|
[44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[1 | HARDENED, 1 | HARDENED, 1 | HARDENED],
|
||||||
|
]
|
||||||
|
correct_paths = [
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 3 | HARDENED],
|
||||||
|
[44 | HARDENED, 1729 | HARDENED, 9 | HARDENED],
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in incorrect_paths:
|
||||||
|
self.assertFalse(validate_full_path(path))
|
||||||
|
|
||||||
|
for path in correct_paths:
|
||||||
|
self.assertTrue(validate_full_path(path))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from common import *
|
from common import *
|
||||||
from trezor.crypto import bip32, bip39
|
from trezor.crypto import bip32, bip39
|
||||||
|
|
||||||
|
from apps.wallet.sign_tx.addresses import validate_full_path, validate_path_for_bitcoin_public_key
|
||||||
|
from apps.common.paths import HARDENED
|
||||||
from apps.common import coins
|
from apps.common import coins
|
||||||
from apps.wallet.sign_tx.signing import *
|
from apps.wallet.sign_tx.signing import *
|
||||||
|
|
||||||
@ -114,6 +116,133 @@ class TestAddress(unittest.TestCase):
|
|||||||
# def test_multisig_address_p2wsh(self):
|
# def test_multisig_address_p2wsh(self):
|
||||||
# todo couldn't find test data
|
# todo couldn't find test data
|
||||||
|
|
||||||
|
def test_paths_btc(self):
|
||||||
|
incorrect_derivation_paths = [
|
||||||
|
([49 | HARDENED], InputScriptType.SPENDP2SHWITNESS), # invalid length
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], InputScriptType.SPENDP2SHWITNESS), # too many HARDENED
|
||||||
|
([49 | HARDENED, 0 | HARDENED], InputScriptType.SPENDP2SHWITNESS), # invalid length
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS), # invalid length
|
||||||
|
([49 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS), # invalid slip44
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 1000 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS), # account too high
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 2, 0], InputScriptType.SPENDP2SHWITNESS), # invalid y
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDP2SHWITNESS), # address index too high
|
||||||
|
([84 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDWITNESS), # address index too high
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS), # invalid input type
|
||||||
|
([84 | HARDENED, 0 | HARDENED, 1 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS), # invalid input type
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDMULTISIG), # invalid input type
|
||||||
|
]
|
||||||
|
correct_derivation_paths = [
|
||||||
|
([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS), # btc is segwit coin, but non-segwit paths are allowed as well
|
||||||
|
([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 1], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDADDRESS),
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDP2SHWITNESS),
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 1123], InputScriptType.SPENDP2SHWITNESS),
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 44444], InputScriptType.SPENDP2SHWITNESS),
|
||||||
|
([49 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS),
|
||||||
|
([84 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS),
|
||||||
|
([84 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS),
|
||||||
|
([84 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDWITNESS),
|
||||||
|
([48 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDMULTISIG),
|
||||||
|
]
|
||||||
|
coin = coins.by_shortcut('BTC')
|
||||||
|
for path, input_type in incorrect_derivation_paths:
|
||||||
|
self.assertFalse(validate_full_path(path, coin, input_type))
|
||||||
|
|
||||||
|
for path, input_type in correct_derivation_paths:
|
||||||
|
self.assertTrue(validate_full_path(path, coin, input_type))
|
||||||
|
|
||||||
|
def test_paths_bch(self):
|
||||||
|
incorrect_derivation_paths = [
|
||||||
|
([44 | HARDENED], InputScriptType.SPENDADDRESS), # invalid length
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], InputScriptType.SPENDADDRESS), # too many HARDENED
|
||||||
|
([49 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS), # bch is not segwit coin so 49' is not allowed
|
||||||
|
([84 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 1], InputScriptType.SPENDWITNESS), # and neither is 84'
|
||||||
|
([44 | HARDENED, 145 | HARDENED], InputScriptType.SPENDADDRESS), # invalid length
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDADDRESS), # invalid length
|
||||||
|
([44 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDADDRESS), # invalid slip44
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 1000 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS), # account too high
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 1 | HARDENED, 2, 0], InputScriptType.SPENDADDRESS), # invalid y
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDADDRESS), # address index too high
|
||||||
|
([84 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 10000000], InputScriptType.SPENDWITNESS), # address index too high
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDWITNESS), # input type mismatch
|
||||||
|
]
|
||||||
|
correct_derivation_paths = [
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 1123], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 1, 44444], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 145 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),
|
||||||
|
([48 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDMULTISIG),
|
||||||
|
([48 | HARDENED, 145 | HARDENED, 5 | HARDENED, 0, 0], InputScriptType.SPENDMULTISIG),
|
||||||
|
([48 | HARDENED, 145 | HARDENED, 5 | HARDENED, 0, 10], InputScriptType.SPENDMULTISIG),
|
||||||
|
]
|
||||||
|
coin = coins.by_shortcut('BCH') # segwit is disabled
|
||||||
|
for path, input_type in incorrect_derivation_paths:
|
||||||
|
self.assertFalse(validate_full_path(path, coin, input_type))
|
||||||
|
|
||||||
|
for path, input_type in correct_derivation_paths:
|
||||||
|
self.assertTrue(validate_full_path(path, coin, input_type))
|
||||||
|
|
||||||
|
def test_paths_other(self):
|
||||||
|
incorrect_derivation_paths = [
|
||||||
|
([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDMULTISIG), # input type mismatch
|
||||||
|
]
|
||||||
|
correct_derivation_paths = [
|
||||||
|
([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 1, 0], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 1123], InputScriptType.SPENDADDRESS),
|
||||||
|
([44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 1, 44444], InputScriptType.SPENDADDRESS),
|
||||||
|
]
|
||||||
|
coin = coins.by_shortcut('DOGE') # segwit is disabled
|
||||||
|
for path, input_type in correct_derivation_paths:
|
||||||
|
self.assertTrue(validate_full_path(path, coin, input_type))
|
||||||
|
|
||||||
|
for path, input_type in incorrect_derivation_paths:
|
||||||
|
self.assertFalse(validate_full_path(path, coin, input_type))
|
||||||
|
|
||||||
|
def test_paths_public_key(self):
|
||||||
|
incorrect_derivation_paths = [
|
||||||
|
[49 | HARDENED], # invalid length
|
||||||
|
[49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], # too many HARDENED
|
||||||
|
[49 | HARDENED, 0 | HARDENED], # invalid length
|
||||||
|
[49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0, 0], # invalid length
|
||||||
|
[49 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], # invalid slip44
|
||||||
|
[49 | HARDENED, 0 | HARDENED, 1000 | HARDENED, 0, 0], # account too high
|
||||||
|
]
|
||||||
|
correct_derivation_paths = [
|
||||||
|
[44 | HARDENED, 0 | HARDENED, 0 | HARDENED], # btc is segwit coin, but non-segwit paths are allowed as well
|
||||||
|
[44 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 1, 0],
|
||||||
|
[49 | HARDENED, 0 | HARDENED, 5 | HARDENED],
|
||||||
|
[84 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
[84 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 0],
|
||||||
|
[84 | HARDENED, 0 | HARDENED, 5 | HARDENED, 0, 10],
|
||||||
|
]
|
||||||
|
coin = coins.by_shortcut('BTC')
|
||||||
|
for path in correct_derivation_paths:
|
||||||
|
self.assertTrue(validate_path_for_bitcoin_public_key(path, coin))
|
||||||
|
|
||||||
|
for path in incorrect_derivation_paths:
|
||||||
|
self.assertFalse(validate_path_for_bitcoin_public_key(path, coin))
|
||||||
|
|
||||||
|
incorrect_derivation_paths = [
|
||||||
|
[49 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 0], # no segwit
|
||||||
|
]
|
||||||
|
correct_derivation_paths = [
|
||||||
|
[44 | HARDENED, 3 | HARDENED, 0 | HARDENED],
|
||||||
|
[44 | HARDENED, 3 | HARDENED, 1 | HARDENED],
|
||||||
|
[44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0],
|
||||||
|
[44 | HARDENED, 3 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
|
]
|
||||||
|
coin = coins.by_shortcut('DOGE') # segwit is disabled
|
||||||
|
for path in correct_derivation_paths:
|
||||||
|
self.assertTrue(validate_path_for_bitcoin_public_key(path, coin))
|
||||||
|
|
||||||
|
for path in incorrect_derivation_paths:
|
||||||
|
self.assertFalse(validate_path_for_bitcoin_public_key(path, coin))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -61,6 +61,9 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
|
|||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
||||||
TxAck(tx=TransactionType(inputs=[inp1])),
|
TxAck(tx=TransactionType(inputs=[inp1])),
|
||||||
|
|
||||||
|
signing.UiConfirmForeignAddress(address_n=inp1.address_n),
|
||||||
|
True,
|
||||||
|
|
||||||
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
|
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
|
||||||
TxAck(tx=TransactionType(outputs=[out1])),
|
TxAck(tx=TransactionType(outputs=[out1])),
|
||||||
|
|
||||||
@ -156,6 +159,9 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
|
|||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
||||||
TxAck(tx=TransactionType(inputs=[inp1])),
|
TxAck(tx=TransactionType(inputs=[inp1])),
|
||||||
|
|
||||||
|
signing.UiConfirmForeignAddress(address_n=inp1.address_n),
|
||||||
|
True,
|
||||||
|
|
||||||
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
|
TxRequest(request_type=TXOUTPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None), serialized=None),
|
||||||
TxAck(tx=TransactionType(outputs=[out1])),
|
TxAck(tx=TransactionType(outputs=[out1])),
|
||||||
|
|
||||||
@ -212,7 +218,8 @@ class TestSignSegwitTxNativeP2WPKH(unittest.TestCase):
|
|||||||
def assertEqualEx(self, a, b):
|
def assertEqualEx(self, a, b):
|
||||||
# hack to avoid adding __eq__ to signing.Ui* classes
|
# hack to avoid adding __eq__ to signing.Ui* classes
|
||||||
if ((isinstance(a, signing.UiConfirmOutput) and isinstance(b, signing.UiConfirmOutput)) or
|
if ((isinstance(a, signing.UiConfirmOutput) and isinstance(b, signing.UiConfirmOutput)) or
|
||||||
(isinstance(a, signing.UiConfirmTotal) and isinstance(b, signing.UiConfirmTotal))):
|
(isinstance(a, signing.UiConfirmTotal) and isinstance(b, signing.UiConfirmTotal)) or
|
||||||
|
(isinstance(a, signing.UiConfirmForeignAddress) and isinstance(b, signing.UiConfirmForeignAddress))):
|
||||||
return self.assertEqual(a.__dict__, b.__dict__)
|
return self.assertEqual(a.__dict__, b.__dict__)
|
||||||
else:
|
else:
|
||||||
return self.assertEqual(a, b)
|
return self.assertEqual(a, b)
|
||||||
|
@ -57,8 +57,11 @@ class TestSignTxFeeThreshold(unittest.TestCase):
|
|||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
None,
|
None,
|
||||||
|
|
||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
||||||
TxAck(tx=TransactionType(inputs=[inp1])),
|
TxAck(tx=TransactionType(inputs=[inp1])),
|
||||||
|
signing.UiConfirmForeignAddress(address_n=inp1.address_n),
|
||||||
|
True,
|
||||||
TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
||||||
TxAck(tx=ptx1),
|
TxAck(tx=ptx1),
|
||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
||||||
@ -121,8 +124,11 @@ class TestSignTxFeeThreshold(unittest.TestCase):
|
|||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
None,
|
None,
|
||||||
|
|
||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
||||||
TxAck(tx=TransactionType(inputs=[inp1])),
|
TxAck(tx=TransactionType(inputs=[inp1])),
|
||||||
|
signing.UiConfirmForeignAddress(address_n=inp1.address_n),
|
||||||
|
True,
|
||||||
TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
||||||
TxAck(tx=ptx1),
|
TxAck(tx=ptx1),
|
||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
||||||
@ -151,6 +157,7 @@ class TestSignTxFeeThreshold(unittest.TestCase):
|
|||||||
# hack to avoid adding __eq__ to signing.Ui* classes
|
# hack to avoid adding __eq__ to signing.Ui* classes
|
||||||
if ((isinstance(a, signing.UiConfirmOutput) and isinstance(b, signing.UiConfirmOutput)) or
|
if ((isinstance(a, signing.UiConfirmOutput) and isinstance(b, signing.UiConfirmOutput)) or
|
||||||
(isinstance(a, signing.UiConfirmTotal) and isinstance(b, signing.UiConfirmTotal)) or
|
(isinstance(a, signing.UiConfirmTotal) and isinstance(b, signing.UiConfirmTotal)) or
|
||||||
|
(isinstance(a, signing.UiConfirmForeignAddress) and isinstance(b, signing.UiConfirmForeignAddress)) or
|
||||||
(isinstance(a, signing.UiConfirmFeeOverThreshold) and isinstance(b, signing.UiConfirmFeeOverThreshold))):
|
(isinstance(a, signing.UiConfirmFeeOverThreshold) and isinstance(b, signing.UiConfirmFeeOverThreshold))):
|
||||||
return self.assertEqual(a.__dict__, b.__dict__)
|
return self.assertEqual(a.__dict__, b.__dict__)
|
||||||
else:
|
else:
|
||||||
|
@ -58,8 +58,11 @@ class TestSignTx(unittest.TestCase):
|
|||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
None,
|
None,
|
||||||
|
|
||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=None)),
|
||||||
TxAck(tx=TransactionType(inputs=[inp1])),
|
TxAck(tx=TransactionType(inputs=[inp1])),
|
||||||
|
signing.UiConfirmForeignAddress(address_n=inp1.address_n),
|
||||||
|
True,
|
||||||
TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
TxRequest(request_type=TXMETA, details=TxRequestDetailsType(request_index=None, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
||||||
TxAck(tx=ptx1),
|
TxAck(tx=ptx1),
|
||||||
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
TxRequest(request_type=TXINPUT, details=TxRequestDetailsType(request_index=0, tx_hash=unhexlify('d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882')), serialized=None),
|
||||||
@ -107,6 +110,7 @@ class TestSignTx(unittest.TestCase):
|
|||||||
def assertEqualEx(self, a, b):
|
def assertEqualEx(self, a, b):
|
||||||
# hack to avoid adding __eq__ to signing.Ui* classes
|
# hack to avoid adding __eq__ to signing.Ui* classes
|
||||||
if ((isinstance(a, signing.UiConfirmOutput) and isinstance(b, signing.UiConfirmOutput)) or
|
if ((isinstance(a, signing.UiConfirmOutput) and isinstance(b, signing.UiConfirmOutput)) or
|
||||||
|
(isinstance(a, signing.UiConfirmForeignAddress) and isinstance(b, signing.UiConfirmForeignAddress)) or
|
||||||
(isinstance(a, signing.UiConfirmTotal) and isinstance(b, signing.UiConfirmTotal))):
|
(isinstance(a, signing.UiConfirmTotal) and isinstance(b, signing.UiConfirmTotal))):
|
||||||
return self.assertEqual(a.__dict__, b.__dict__)
|
return self.assertEqual(a.__dict__, b.__dict__)
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user