mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-12 06:11:26 -07:00
Compare commits
766 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c35633e01 | |||
| 674bd94f72 | |||
| ff24cfe314 | |||
| bc191d4f84 | |||
| 5c20676c76 | |||
| 11b7be1640 | |||
| 6ae8288142 | |||
| 7f1069cb0b | |||
| 23cd64ec35 | |||
| 3263e54144 | |||
| 6805b85f89 | |||
| 88c97cbbd0 | |||
| 17b1c8d338 | |||
| 0a5c85f8e3 | |||
| 9c20df5b1b | |||
| 7df7fec6f7 | |||
| e423a6d692 | |||
| f4c74a9f32 | |||
| dce176fa39 | |||
| d666ff3744 | |||
| 1678391db3 | |||
| d1174f66bc | |||
| 84a7e03178 | |||
| 1bffc8baac | |||
| c558b694eb | |||
| a06bde729b | |||
| 5107bdc06b | |||
| 5137d12d36 | |||
| c6ee025063 | |||
| 826cf1508b | |||
| 51edf01162 | |||
| deb8922181 | |||
| cdda8f0eb9 | |||
| cba8cb2201 | |||
| ae67f37fc1 | |||
| 8a78b0d241 | |||
| 874d6ccb60 | |||
| db3279e7da | |||
| 9251e06143 | |||
| a642ea0b28 | |||
| 00fc4c4e1b | |||
| 9ea11da00f | |||
| a8909f06ae | |||
| ee543a304a | |||
| 5c5b78cbbe | |||
| 54515af8cc | |||
| b894df2125 | |||
| 3f193d7446 | |||
| e127d21fed | |||
| 3060225f46 | |||
| c14b9d461a | |||
| 8060e3170e | |||
| 980947b355 | |||
| fd987b29ff | |||
| afcc2889ec | |||
| 5f001f6d51 | |||
| fc07211772 | |||
| c9e37a08be | |||
| dcbcf75269 | |||
| 59479b2ba6 | |||
| 007bd993a6 | |||
| 5a84a08225 | |||
| bb23648c71 | |||
| a4b8c1ea17 | |||
| 20e75cd463 | |||
| d74ddb2d91 | |||
| ec0831da70 | |||
| b12ba1508e | |||
| f3b0b82461 | |||
| fea38f6910 | |||
| 213b2fd0f8 | |||
| 42d595f6a1 | |||
| 656de10ba4 | |||
| f6f3a760bc | |||
| bafc791f1c | |||
| 1fc256dd90 | |||
| 41549b502b | |||
| f989f4df3e | |||
| 21d1c0e5a4 | |||
| d7b7804215 | |||
| 8472338bfa | |||
| 1de1d97429 | |||
| f21ead4f78 | |||
| e6bd79c882 | |||
| 9e417592e3 | |||
| db87c34613 | |||
| 159257597a | |||
| bdd6a86d73 | |||
| 1ab3ec5731 | |||
| b43c1f2b5b | |||
| b1e608bfba | |||
| 57c5455827 | |||
| cc185d98b4 | |||
| cd61251d39 | |||
| 43a199b57b | |||
| fa1875a8f1 | |||
| bed81c8829 | |||
| 7a592cbb31 | |||
| e931c82b9b | |||
| a7f0275093 | |||
| 9ab2fbea00 | |||
| 7fe86ed565 | |||
| 16584779fb | |||
| c5c2a4d09d | |||
| 912c326d8b | |||
| d496dbf3a0 | |||
| cbfb32c34c | |||
| 5cd57a3a7f | |||
| 563ce2bd81 | |||
| 9469b90b01 | |||
| d2e1ec13f0 | |||
| 6df07a2c0a | |||
| c0d407a2b4 | |||
| f378367fe7 | |||
| c291fffce1 | |||
| 900fe95ba0 | |||
| 5dd2e38aff | |||
| 5ad8ebba9f | |||
| e7719eed65 | |||
| 5f49b8997a | |||
| d6734c85b8 | |||
| 4ca23499fa | |||
| e201a2fabb | |||
| 5c4485f657 | |||
| bae2a6fc1e | |||
| 8777fa1c04 | |||
| d771b3286d | |||
| 0e6dd56ac1 | |||
| 9127e584c5 | |||
| eab0e730a0 | |||
| 7128bc4b34 | |||
| 7b672ac72a | |||
| 8f4ab3c07d | |||
| dc5cfacc0a | |||
| 3aea2e1fff | |||
| 59933b244d | |||
| 42aca72d9f | |||
| 7034a31745 | |||
| 9d0786ded7 | |||
| 4c51706941 | |||
| a8c4f46747 | |||
| 8e367d221b | |||
| a08250ac5b | |||
| 131c71ea76 | |||
| 9db04fe446 | |||
| abaa906f34 | |||
| fa093d9b09 | |||
| 4035a226da | |||
| df5ee2dd06 | |||
| 9b4cd0743c | |||
| 3f090ed523 | |||
| 79173ef119 | |||
| 44758f2a60 | |||
| 1f0b24b735 | |||
| 327853ab92 | |||
| a6878fcd91 | |||
| 61202b5abd | |||
| 56516ee8b2 | |||
| 1cc639bc93 | |||
| 2357293e05 | |||
| 7a8b1669b0 | |||
| bc160d2eb7 | |||
| b02b85d242 | |||
| 1d151c4c92 | |||
| 899fab64d9 | |||
| 00ab3beed9 | |||
| ca10fc2d47 | |||
| 1ed6a484ac | |||
| b0675358c3 | |||
| 456dd22dc4 | |||
| 02b8fefa30 | |||
| 95808fd2e7 | |||
| e63d71c23b | |||
| 23a49ce65b | |||
| ca6300c860 | |||
| 1ebb7f8e9d | |||
| 4685cf1085 | |||
| 80a0b161b0 | |||
| 27feb5998c | |||
| e88db2818b | |||
| 19535d8721 | |||
| 8ea8fbeccb | |||
| 4e300ff90a | |||
| 7bf25a4cf2 | |||
| 5f0f9f477e | |||
| dd7c65a89c | |||
| 8620281191 | |||
| c311a60aaf | |||
| 2993490c75 | |||
| 60b324d4ed | |||
| 89a2fdd106 | |||
| 8530829ca4 | |||
| 0e5d7801dd | |||
| d5db49a3b0 | |||
| 606f11ebe8 | |||
| 07a4f4d017 | |||
| d463adfe4f | |||
| 16bd19ed43 | |||
| eb811e8341 | |||
| 8a63b6dff7 | |||
| 829654e3a2 | |||
| cec08114fd | |||
| bf90b61908 | |||
| ae0a5b1a87 | |||
| 55ff9b857e | |||
| e7122a0950 | |||
| 539cec7496 | |||
| 000709b594 | |||
| c51f94b600 | |||
| b703d5ca58 | |||
| aff345f57f | |||
| a4664f82cc | |||
| 9fb090d1b8 | |||
| 4a2ad3b13a | |||
| 9d5afee24b | |||
| 6736205723 | |||
| f0a7f1812f | |||
| 9c9ae5987a | |||
| f588593ff1 | |||
| a626adac8e | |||
| 338793b68e | |||
| 59575d9b22 | |||
| b58a0b60d0 | |||
| 3a4c82abf8 | |||
| 946cf52df1 | |||
| a78f062499 | |||
| 3c47503780 | |||
| 6da82e038d | |||
| 38af752cd1 | |||
| 0428f508d8 | |||
| 3c24584290 | |||
| 535b8a9483 | |||
| 038086d1d9 | |||
| 9a16bc5fc5 | |||
| eee8e0faa2 | |||
| 3e7e779abf | |||
| 6a07effdd2 | |||
| 395e0fb225 | |||
| 9be9f0bb2c | |||
| 9e7cc8cf93 | |||
| 2743bf7e44 | |||
| d95c28f41b | |||
| e57224d6e4 | |||
| f67691c457 | |||
| d04c82e547 | |||
| 09fa1c29cd | |||
| ddcaa0de53 | |||
| 621b3c7829 | |||
| 68df35b378 | |||
| c1d853a24e | |||
| 30636f5114 | |||
| b46b3010ec | |||
| 31914e9cb9 | |||
| da6edb3764 | |||
| 9a9625f885 | |||
| 15b40a4f29 | |||
| 5406acc8fa | |||
| d5b0ec712b | |||
| 766d6aa946 | |||
| 895ebb95d5 | |||
| af636eedde | |||
| 57b7822e12 | |||
| 03736d8bcb | |||
| 3a5d80b291 | |||
| 967ccfc7cc | |||
| 712ae1c5ac | |||
| f93f2dfd5c | |||
| 9ac619f4a2 | |||
| 9cfd4a953e | |||
| 14e2cc17d6 | |||
| 5f41741e8c | |||
| 8a067bc1fc | |||
| cfdf2bfb77 | |||
| 2f8316840e | |||
| a660098106 | |||
| 90d1f9ab27 | |||
| 2097b09abb | |||
| f63f28302e | |||
| 98b539ac5c | |||
| 9d067ae562 | |||
| 5a542c7ad5 | |||
| 73f57c5933 | |||
| 2071d7ba84 | |||
| e8102a40d0 | |||
| 737677f1b6 | |||
| fc047a8043 | |||
| ae52e245ea | |||
| b8bfc715bf | |||
| b0aa08f85b | |||
| d6ee5e89fa | |||
| a263da0bbe | |||
| 9cb02aa51d | |||
| 799f4379d8 | |||
| bd51b21386 | |||
| 276284cbec | |||
| a8021f03a6 | |||
| c35d5d4c42 | |||
| 248a0bfa5f | |||
| 34791707fd | |||
| 37ae9eb609 | |||
| b6838731f5 | |||
| 991c1d4446 | |||
| d0bb1fb844 | |||
| ea43c030aa | |||
| d54f34a162 | |||
| a86cfa6e97 | |||
| 1b83f4a18b | |||
| 90ac400ec5 | |||
| 75c659fa41 | |||
| 8cfff254c9 | |||
| 8c2eb5df78 | |||
| a2f0ac0fd2 | |||
| 2987fc65a1 | |||
| c5bab58180 | |||
| 5ad88ea169 | |||
| 72d6b9b683 | |||
| 2f2c086382 | |||
| d9fafa7000 | |||
| 12e53f2336 | |||
| a2e729c303 | |||
| 7b89174a26 | |||
| 1d7f7a55d2 | |||
| 766485ccab | |||
| 6642aa94d5 | |||
| 1c9ec27d36 | |||
| 05e27fa92d | |||
| 7954a7a6f3 | |||
| 1d621295b1 | |||
| 02c2aff41b | |||
| 5476d43441 | |||
| b25e85a8bb | |||
| 190103883c | |||
| d460dd2bdc | |||
| 7ee7f4b064 | |||
| f2d3376c0b | |||
| 0fcd2f1d88 | |||
| 76f52dd6c9 | |||
| ae0be9c785 | |||
| 3f6f00d8e5 | |||
| c58eeca1b2 | |||
| 5032839abc | |||
| 2b1d85968d | |||
| 5896469f3b | |||
| 70c150fc83 | |||
| 2411febf83 | |||
| 5e7b031a1d | |||
| ef05e0858d | |||
| 880a18f124 | |||
| 5d94887e76 | |||
| 577049c69e | |||
| 46f46e645c | |||
| b13aa5db37 | |||
| 2990747cf8 | |||
| 62c30795e6 | |||
| bb94f4d129 | |||
| 7b53dbeb8a | |||
| 120ef168bf | |||
| 72dfb73c9d | |||
| 67dcd916e8 | |||
| 7f6f971284 | |||
| 67d5b97ff9 | |||
| efb2b05040 | |||
| 656e62d724 | |||
| ce8801c37f | |||
| c6c710465a | |||
| f3dc1f6e3b | |||
| 873b7f8588 | |||
| 7d3dfb1046 | |||
| f4782157d3 | |||
| 8de7d2f18f | |||
| aff8f06e3c | |||
| 0bf1b6c2f1 | |||
| 2017ebc1b5 | |||
| da56a75ad6 | |||
| 1e1228fb37 | |||
| 8d7c37e384 | |||
| cd65a15d81 | |||
| 9235e82451 | |||
| ecee0e2db2 | |||
| 0580eebd2a | |||
| 909a5016ee | |||
| c2c89e54a5 | |||
| 6389d9c11f | |||
| 2784e8ea51 | |||
| 85b71708dd | |||
| 649b5fa528 | |||
| d6a89d667d | |||
| b408ec6bac | |||
| 8545f316ff | |||
| 3bf7b416e7 | |||
| 0b83e3dae5 | |||
| d7d23e65ea | |||
| 2820bbc269 | |||
| 70c5153471 | |||
| 5961d4b316 | |||
| b59c80d6fd | |||
| 8c10f4cdde | |||
| 0cc72b536f | |||
| 8a7c2ea9d0 | |||
| cb1c10ce83 | |||
| 37c2e9bec3 | |||
| 4626cd03da | |||
| 3850be2e11 | |||
| 4c9aa30a1e | |||
| da977cf1e6 | |||
| ee97c5958f | |||
| 778bbee17c | |||
| 4efef08c94 | |||
| 87c835268a | |||
| 83ed0b37cd | |||
| dc49cf766f | |||
| 933754193a | |||
| 76eef47491 | |||
| 0e21f45076 | |||
| 9bfe3a94f4 | |||
| a2009220a2 | |||
| b410383aaf | |||
| b47736260a | |||
| 5154bb551a | |||
| 89084befb0 | |||
| f3caa4aee3 | |||
| 3ec253f385 | |||
| 759e342872 | |||
| 120b1101fb | |||
| 252fd0e928 | |||
| 1fa2af213d | |||
| de84db85cb | |||
| 33d85adf34 | |||
| 383d485e2a | |||
| 68d2b7bb89 | |||
| d667809e0a | |||
| 64ed4389ff | |||
| 4701b31b55 | |||
| e52cc73d50 | |||
| ac0ed13b35 | |||
| 269957b9fb | |||
| 464a675adc | |||
| 55f3e99f63 | |||
| 49fb0acfc9 | |||
| 09db245e4c | |||
| bc7972fbaf | |||
| 6f80d2dc36 | |||
| 46250168d6 | |||
| 509712f771 | |||
| 4b38dbe133 | |||
| ad0ffa4833 | |||
| b89fc9191c | |||
| 829e4cc67e | |||
| beb4699b46 | |||
| 031b0cc534 | |||
| ee504aa596 | |||
| baa5ea9258 | |||
| 9a490e4e0d | |||
| 252de70b0f | |||
| 4ae021cd4c | |||
| af962526df | |||
| 6024af172c | |||
| 8bd5a9bb9e | |||
| a9166f52f8 | |||
| 9bdd4c73a5 | |||
| 2362be11bf | |||
| 5b8a76c583 | |||
| 2a0e4f7a8c | |||
| ece82eecc3 | |||
| 67d02043fe | |||
| 29c80c238d | |||
| a4606e4da8 | |||
| d4df3f6383 | |||
| 58ff3dd1ed | |||
| cf8c9a6be4 | |||
| ea709451e8 | |||
| d47a403d29 | |||
| 1606f756b3 | |||
| 0b2f357bbe | |||
| 9a1c4dc56d | |||
| 7eabeda870 | |||
| 2e9298891e | |||
| f8b85339af | |||
| 12b8f91249 | |||
| 8e395cc6e9 | |||
| feba52002e | |||
| ca2b2c498e | |||
| 6e9e469abd | |||
| 0490ba46f4 | |||
| e0652ee2f4 | |||
| bc99bacb21 | |||
| 173527d631 | |||
| f8cb2bcad2 | |||
| b011487778 | |||
| c8cc130744 | |||
| fe0244606b | |||
| 036fa6482c | |||
| 04ca22b9ea | |||
| bf9ae77fbd | |||
| 4adfc120ba | |||
| d8bb93accc | |||
| e410908ce8 | |||
| d278c852cc | |||
| 364b64718f | |||
| 61335089c6 | |||
| 040b90ce76 | |||
| 843b2350eb | |||
| 526d338300 | |||
| a6aca5d6d1 | |||
| 05c67c3243 | |||
| 31b1aef44e | |||
| a8c09d77ff | |||
| 5665ae02bc | |||
| 92d3a681cd | |||
| cb66ed52ce | |||
| 2e1b2dc3ba | |||
| 65333181fc | |||
| 30ad923558 | |||
| a31ab31e96 | |||
| ac760a0bdd | |||
| fbf0180411 | |||
| 198ac00994 | |||
| 603764dbca | |||
| cb04333480 | |||
| 0e169fd1c2 | |||
| 68cf51c8f8 | |||
| 65afe6f70d | |||
| 291a064725 | |||
| bfbeb23f54 | |||
| 58e4a011c7 | |||
| 3de15987bd | |||
| c5c46c5073 | |||
| abe896a7be | |||
| f00df0afe9 | |||
| 35f21abc7b | |||
| b1977dfa4b | |||
| e23eba0914 | |||
| 4b245e2d83 | |||
| f169d8c396 | |||
| 39f8437f4d | |||
| 4aa8834d39 | |||
| f79efd43e4 | |||
| 058132e7e6 | |||
| 9b7c5f2b1b | |||
| 06e61c7315 | |||
| 1b23dbb228 | |||
| cc2ba146a6 | |||
| 3bbdc93882 | |||
| 0091193c04 | |||
| 1b5bcc1a99 | |||
| 5a661dc67b | |||
| fb48131f3c | |||
| 9300c99d65 | |||
| 9366bf44a6 | |||
| e8e3501443 | |||
| 9f96382558 | |||
| 82088e4a75 | |||
| b12bfe407d | |||
| 1e726bb3e0 | |||
| 1db5e1250f | |||
| e0ecae66fd | |||
| 0df7fe4247 | |||
| 84a8e17482 | |||
| cabaac5a68 | |||
| 2bb5c4cafc | |||
| bfff322eb9 | |||
| dd60802db4 | |||
| 6db42cfaec | |||
| 21f5b41150 | |||
| 1f007a46ee | |||
| 4fa4feb669 | |||
| 5fd3fc4c7c | |||
| ee2d3e00fd | |||
| 6f65ed6cc8 | |||
| 70525a12ca | |||
| 914950592c | |||
| 3c021f9c52 | |||
| 9ea88f1353 | |||
| 4d89249caf | |||
| ff0e976ff3 | |||
| 0df1645422 | |||
| bb6d73953c | |||
| c9c14bab8a | |||
| 7c6117bb8f | |||
| ae40946198 | |||
| 8063569cdd | |||
| dab72d5615 | |||
| 89493b70a9 | |||
| c12eb3ec93 | |||
| 7343ed0cac | |||
| 3102abf1aa | |||
| 03282c1234 | |||
| 490c8daedd | |||
| e78ee90ac2 | |||
| f809788f75 | |||
| 0888c69b57 | |||
| ab39644156 | |||
| 4c1a9d2b3f | |||
| f6308ab5b9 | |||
| 3baf8d92c3 | |||
| 34065865a0 | |||
| 3d90e580c5 | |||
| f635d3d8af | |||
| 29f1b79d70 | |||
| 955be66f1a | |||
| a0e6ae6f33 | |||
| 08d032859f | |||
| 4af77aeff6 | |||
| 6749639eb1 | |||
| 8a56bb3b5f | |||
| 37e79f9cca | |||
| a4c64002c5 | |||
| 63b6e8216c | |||
| 3e7b5670fb | |||
| e2e18324ed | |||
| 924039c01b | |||
| 2af2a035dc | |||
| f97ccca514 | |||
| 25e9255157 | |||
| 2e64866838 | |||
| b21b5ef222 | |||
| d9fab5e824 | |||
| 0424404140 | |||
| db89c2ccd0 | |||
| 0c97f6aa4e | |||
| af78ad2a87 | |||
| 9cc5cd9d40 | |||
| 59950bcad6 | |||
| 4587a55486 | |||
| 7640fb0483 | |||
| b8f9deb3d8 | |||
| 92aad90c6b | |||
| 506adfb105 | |||
| e819eebc63 | |||
| 841e73fcd5 | |||
| aa821991b8 | |||
| b71afdc3ee | |||
| bc139ba6c6 | |||
| 8af6da5de1 | |||
| 0b86038482 | |||
| 8d943f5902 | |||
| 32b27c690d | |||
| 3494a6dcd8 | |||
| 235fec23af | |||
| 97b066f112 | |||
| a99c0b3e4a | |||
| 2a6fb3a387 | |||
| 65204f13a8 | |||
| 8438d66e6b | |||
| 44c8da7bf2 | |||
| 41fdcfeecf | |||
| 70ba4b5321 | |||
| 71da3ef925 | |||
| 582bd760b7 | |||
| e86bb3a795 | |||
| e3a836f93c | |||
| 31b42a53fc | |||
| b71d3c739a | |||
| a162b51588 | |||
| 7c1119f217 | |||
| b0397581db | |||
| c7dc1220b3 | |||
| d16acdc89c | |||
| 1279ebe948 | |||
| aa5a95a4d1 | |||
| fa1cb28c8f | |||
| 3902273d68 | |||
| 811132ccbd | |||
| 391b3090c9 | |||
| d50380709d | |||
| 14a46c6d5e | |||
| b4e3da84c5 | |||
| 9d627e453a | |||
| d6e0e5d3f6 | |||
| 12b370ddc1 | |||
| 50f86dc14f | |||
| 9078ddaf5a | |||
| 8028033513 | |||
| 6b9202dfbf | |||
| 04c928fb9e | |||
| 354d00963c | |||
| 9e5e49a65d | |||
| 8a849bd1bd | |||
| 1b3fb11a31 | |||
| b30ceab864 | |||
| 3e1791aa5c | |||
| 2cb5cc774d | |||
| 5f7843a13d | |||
| 1b770fc3b2 | |||
| 79d3c3be66 | |||
| 55d42492ac | |||
| 9737c2ad0b | |||
| b9a813a69d | |||
| e0ac454ed0 | |||
| 1db95aa09d | |||
| c0bbe1e23d | |||
| 9e35361eb8 | |||
| cce42f4a6b | |||
| bd19081543 | |||
| dd9b0ae4e5 | |||
| 3b5d28f0ee | |||
| 0ca39c70a5 | |||
| 270348b112 | |||
| e67473d89a | |||
| 086478e8f2 | |||
| 5a8da5dcdb | |||
| f6f4734fee | |||
| f903951016 | |||
| e19460cbd7 | |||
| 6de2b7700f | |||
| 4e145cf69c | |||
| 341087a82b | |||
| 851fd1f8d1 | |||
| ce43b04688 | |||
| ff1ab74b3c | |||
| aed67ba665 | |||
| 70c1f9d0e1 | |||
| 761a079789 | |||
| 4116d66c59 | |||
| fc6edf65d7 | |||
| 548ab2e1b6 | |||
| 13a3c0e57c | |||
| 9834ceed42 | |||
| 4266a7c166 | |||
| 639cc9faa8 | |||
| e2cecafa66 | |||
| 4bcb68a973 | |||
| 7131772754 | |||
| c2e5602ee5 | |||
| 8714fd77a0 | |||
| c20839c461 | |||
| 51229a29f8 | |||
| 59675949da | |||
| 6564ce0fb0 | |||
| c36ac4bdfd | |||
| 764ce2a4b0 | |||
| d400a5a108 | |||
| 744eb7c6d8 | |||
| 1b79e2ca5f | |||
| 4cb0edc90b | |||
| 6201280468 | |||
| 9e36e28217 | |||
| 5edb2c5688 | |||
| ce5e7998ba | |||
| 4a4aca6829 | |||
| f26ed39e86 | |||
| 574d2baf09 | |||
| 1e622979f8 | |||
| 1f969fdc75 | |||
| e206d6ba35 | |||
| 7faa146004 | |||
| 75cb2c68cc | |||
| 942e91f94c | |||
| d8f06ed557 | |||
| a73ff00b02 | |||
| a58a08c0c3 | |||
| 5c533e477d | |||
| 21247721b4 | |||
| 4436d24440 | |||
| 7a16618ec6 | |||
| 2b2abc6b9f | |||
| 056840975f | |||
| 160b23f991 | |||
| a573d076e4 | |||
| 625cb03284 | |||
| 2f8d60ec47 | |||
| 37e33af342 | |||
| f7e0516254 | |||
| eafe57e923 | |||
| c76ab138eb | |||
| 16c176dc89 |
@@ -1 +1,2 @@
|
|||||||
*.odin linguist-language=Odin
|
*.odin linguist-language=Odin
|
||||||
|
* text=auto
|
||||||
@@ -3,6 +3,7 @@ on: [push, pull_request, workflow_dispatch]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_linux:
|
build_linux:
|
||||||
|
name: Ubuntu Build, Check, and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
@@ -46,6 +47,9 @@ jobs:
|
|||||||
- name: Odin check examples/all for Linux i386
|
- name: Odin check examples/all for Linux i386
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Linux arm64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||||
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for FreeBSD amd64
|
- name: Odin check examples/all for FreeBSD amd64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
@@ -53,6 +57,7 @@ jobs:
|
|||||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
build_macOS:
|
build_macOS:
|
||||||
|
name: MacOS Build, Check, and Test
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
@@ -87,9 +92,45 @@ jobs:
|
|||||||
cd tests/core
|
cd tests/core
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Vendor library tests
|
- name: Odin internals tests
|
||||||
run: |
|
run: |
|
||||||
cd tests/vendor
|
cd tests/internal
|
||||||
|
make
|
||||||
|
timeout-minutes: 10
|
||||||
|
build_macOS_arm:
|
||||||
|
name: MacOS ARM Build, Check, and Test
|
||||||
|
runs-on: macos-14 # This is an arm/m1 runner.
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Download LLVM, botan and setup PATH
|
||||||
|
run: |
|
||||||
|
brew install llvm@13 botan
|
||||||
|
echo "/opt/homebrew/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||||
|
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||||
|
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||||
|
- name: build odin
|
||||||
|
run: ./build_odin.sh release
|
||||||
|
- name: Odin version
|
||||||
|
run: ./odin version
|
||||||
|
timeout-minutes: 1
|
||||||
|
- name: Odin report
|
||||||
|
run: ./odin report
|
||||||
|
timeout-minutes: 1
|
||||||
|
- name: Odin check
|
||||||
|
run: ./odin check examples/demo -vet
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin run
|
||||||
|
run: ./odin run examples/demo
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin run -debug
|
||||||
|
run: ./odin run examples/demo -debug
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all
|
||||||
|
run: ./odin check examples/all -strict-style
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Core library tests
|
||||||
|
run: |
|
||||||
|
cd tests/core
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin internals tests
|
- name: Odin internals tests
|
||||||
@@ -97,13 +138,8 @@ jobs:
|
|||||||
cd tests/internal
|
cd tests/internal
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for Darwin arm64
|
|
||||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all for Linux arm64
|
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
|
||||||
timeout-minutes: 10
|
|
||||||
build_windows:
|
build_windows:
|
||||||
|
name: Windows Build, Check, and Test
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
@@ -168,7 +204,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\documentation
|
cd tests\documentation
|
||||||
call build.bat
|
rem call build.bat
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: core:math/big tests
|
- name: core:math/big tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_windows:
|
build_windows:
|
||||||
|
name: Windows Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
@@ -29,6 +30,7 @@ jobs:
|
|||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp LLVM-C.dll dist
|
cp LLVM-C.dll dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r bin dist
|
cp -r bin dist
|
||||||
@@ -39,6 +41,7 @@ jobs:
|
|||||||
name: windows_artifacts
|
name: windows_artifacts
|
||||||
path: dist
|
path: dist
|
||||||
build_ubuntu:
|
build_ubuntu:
|
||||||
|
name: Ubuntu Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -56,46 +59,92 @@ jobs:
|
|||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp libLLVM* dist
|
cp libLLVM* dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r examples dist
|
cp -r examples dist
|
||||||
|
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||||
|
zip -r dist.zip dist
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: ubuntu_artifacts
|
name: ubuntu_artifacts
|
||||||
path: dist
|
path: dist.zip
|
||||||
build_macos:
|
build_macos:
|
||||||
|
name: MacOS Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: macOS-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@13
|
brew install llvm@13 dylibbundler
|
||||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
- name: Odin run
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
run: ./odin run examples/demo
|
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||||
- name: Copy artifacts
|
- name: Bundle
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
mkdir dist
|
||||||
cp odin dist
|
cp odin dist
|
||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r examples dist
|
cp -r examples dist
|
||||||
|
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
|
||||||
|
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||||
|
zip -r dist.zip dist
|
||||||
|
- name: Odin run
|
||||||
|
run: ./dist/odin run examples/demo
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: macos_artifacts
|
name: macos_artifacts
|
||||||
path: dist
|
path: dist.zip
|
||||||
|
build_macos_arm:
|
||||||
|
name: MacOS ARM Build
|
||||||
|
if: github.repository == 'odin-lang/Odin'
|
||||||
|
runs-on: macos-14 # ARM machine
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Download LLVM and setup PATH
|
||||||
|
run: |
|
||||||
|
brew install llvm@13 dylibbundler
|
||||||
|
echo "/opt/homebrew/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||||
|
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||||
|
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||||
|
- name: build odin
|
||||||
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
|
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||||
|
- name: Bundle
|
||||||
|
run: |
|
||||||
|
mkdir dist
|
||||||
|
cp odin dist
|
||||||
|
cp LICENSE dist
|
||||||
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
|
cp -r core dist
|
||||||
|
cp -r vendor dist
|
||||||
|
cp -r examples dist
|
||||||
|
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
|
||||||
|
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||||
|
zip -r dist.zip dist
|
||||||
|
- name: Odin run
|
||||||
|
run: ./dist/odin run examples/demo
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: macos_arm_artifacts
|
||||||
|
path: dist.zip
|
||||||
upload_b2:
|
upload_b2:
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
needs: [build_windows, build_macos, build_ubuntu]
|
needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v2
|
||||||
@@ -126,6 +175,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: macos_artifacts
|
name: macos_artifacts
|
||||||
|
|
||||||
|
- name: Download macOS arm artifacts
|
||||||
|
uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: macos_arm_artifacts
|
||||||
|
|
||||||
- name: Create archives and upload
|
- name: Create archives and upload
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -140,8 +194,9 @@ jobs:
|
|||||||
echo Uploading artifcates to B2
|
echo Uploading artifcates to B2
|
||||||
chmod +x ./ci/upload_create_nightly.sh
|
chmod +x ./ci/upload_create_nightly.sh
|
||||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
||||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/dist.zip
|
||||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/dist.zip
|
||||||
|
./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/dist.zip
|
||||||
|
|
||||||
echo Deleting old artifacts in B2
|
echo Deleting old artifacts in B2
|
||||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
||||||
|
|||||||
+29
@@ -25,7 +25,33 @@ bld/
|
|||||||
tests/documentation/verify/
|
tests/documentation/verify/
|
||||||
tests/documentation/all.odin-doc
|
tests/documentation/all.odin-doc
|
||||||
tests/internal/test_map
|
tests/internal/test_map
|
||||||
|
tests/internal/test_pow
|
||||||
tests/internal/test_rtti
|
tests/internal/test_rtti
|
||||||
|
tests/core/test_core_compress
|
||||||
|
tests/core/test_core_container
|
||||||
|
tests/core/test_core_filepath
|
||||||
|
tests/core/test_core_fmt
|
||||||
|
tests/core/test_core_i18n
|
||||||
|
tests/core/test_core_image
|
||||||
|
tests/core/test_core_libc
|
||||||
|
tests/core/test_core_match
|
||||||
|
tests/core/test_core_math
|
||||||
|
tests/core/test_core_net
|
||||||
|
tests/core/test_core_os_exit
|
||||||
|
tests/core/test_core_reflect
|
||||||
|
tests/core/test_core_strings
|
||||||
|
tests/core/test_crypto
|
||||||
|
tests/core/test_hash
|
||||||
|
tests/core/test_hxa
|
||||||
|
tests/core/test_json
|
||||||
|
tests/core/test_linalg_glsl_math
|
||||||
|
tests/core/test_noise
|
||||||
|
tests/core/test_varint
|
||||||
|
tests/core/test_xml
|
||||||
|
tests/core/test_core_slice
|
||||||
|
tests/core/test_core_thread
|
||||||
|
tests/core/test_core_runtime
|
||||||
|
tests/vendor/vendor_botan
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
# Visual Studio Code options directory
|
# Visual Studio Code options directory
|
||||||
@@ -290,3 +316,6 @@ shared/
|
|||||||
examples/bug/
|
examples/bug/
|
||||||
build.sh
|
build.sh
|
||||||
!core/debug/
|
!core/debug/
|
||||||
|
|
||||||
|
# RAD debugger project file
|
||||||
|
*.raddbg
|
||||||
@@ -110,7 +110,7 @@ typeid_of :: proc($T: typeid) -> typeid ---
|
|||||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||||
|
|
||||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
quaternion :: proc(imag, jmag, kmag, real: Float) -> Quaternion_Type --- // fields must be named
|
||||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||||
jmag :: proc(value: Quaternion) -> Float ---
|
jmag :: proc(value: Quaternion) -> Float ---
|
||||||
@@ -5,6 +5,12 @@ package intrinsics
|
|||||||
// Package-Related
|
// Package-Related
|
||||||
is_package_imported :: proc(package_name: string) -> bool ---
|
is_package_imported :: proc(package_name: string) -> bool ---
|
||||||
|
|
||||||
|
// Matrix Related Procedures
|
||||||
|
transpose :: proc(m: $T/matrix[$R, $C]$E) -> matrix[C, R]E ---
|
||||||
|
outer_product :: proc(a: $A/[$X]$E, b: $B/[$Y]E) -> matrix[X, Y]E ---
|
||||||
|
hadamard_product :: proc(a, b: $T/matrix[$R, $C]$E) -> T ---
|
||||||
|
matrix_flatten :: proc(m: $T/matrix[$R, $C]$E) -> [R*C]E ---
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||||
|
|
||||||
@@ -162,7 +168,14 @@ type_is_matrix :: proc($T: typeid) -> bool ---
|
|||||||
type_has_nil :: proc($T: typeid) -> bool ---
|
type_has_nil :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||||
|
|
||||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||||
|
type_union_tag_type :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||||
|
type_union_tag_offset :: proc($T: typeid) -> uintptr where type_is_union(T) ---
|
||||||
|
type_union_base_tag_value :: proc($T: typeid) -> int where type_is_union(U) ---
|
||||||
|
type_union_variant_count :: proc($T: typeid) -> int where type_is_union(T) ---
|
||||||
|
type_variant_type_of :: proc($T: typeid, $index: int) -> typeid where type_is_union(T) ---
|
||||||
|
type_variant_index_of :: proc($U, $V: typeid) -> int where type_is_union(U) ---
|
||||||
|
|
||||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||||
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
||||||
@@ -18,9 +18,10 @@
|
|||||||
// This could change at a later date if the all these data structures are
|
// This could change at a later date if the all these data structures are
|
||||||
// implemented within the compiler rather than in this "preload" file
|
// implemented within the compiler rather than in this "preload" file
|
||||||
//
|
//
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// NOTE(bill): This must match the compiler's
|
// NOTE(bill): This must match the compiler's
|
||||||
Calling_Convention :: enum u8 {
|
Calling_Convention :: enum u8 {
|
||||||
@@ -180,6 +181,14 @@ Type_Info_Matrix :: struct {
|
|||||||
Type_Info_Soa_Pointer :: struct {
|
Type_Info_Soa_Pointer :: struct {
|
||||||
elem: ^Type_Info,
|
elem: ^Type_Info,
|
||||||
}
|
}
|
||||||
|
Type_Info_Bit_Field :: struct {
|
||||||
|
backing_type: ^Type_Info,
|
||||||
|
names: []string,
|
||||||
|
types: []^Type_Info,
|
||||||
|
bit_sizes: []uintptr,
|
||||||
|
bit_offsets: []uintptr,
|
||||||
|
tags: []string,
|
||||||
|
}
|
||||||
|
|
||||||
Type_Info_Flag :: enum u8 {
|
Type_Info_Flag :: enum u8 {
|
||||||
Comparable = 0,
|
Comparable = 0,
|
||||||
@@ -222,6 +231,7 @@ Type_Info :: struct {
|
|||||||
Type_Info_Relative_Multi_Pointer,
|
Type_Info_Relative_Multi_Pointer,
|
||||||
Type_Info_Matrix,
|
Type_Info_Matrix,
|
||||||
Type_Info_Soa_Pointer,
|
Type_Info_Soa_Pointer,
|
||||||
|
Type_Info_Bit_Field,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,6 +265,7 @@ Typeid_Kind :: enum u8 {
|
|||||||
Relative_Multi_Pointer,
|
Relative_Multi_Pointer,
|
||||||
Matrix,
|
Matrix,
|
||||||
Soa_Pointer,
|
Soa_Pointer,
|
||||||
|
Bit_Field,
|
||||||
}
|
}
|
||||||
#assert(len(Typeid_Kind) < 32)
|
#assert(len(Typeid_Kind) < 32)
|
||||||
|
|
||||||
@@ -269,7 +280,7 @@ Typeid_Kind :: enum u8 {
|
|||||||
|
|
||||||
// NOTE(bill): only the ones that are needed (not all types)
|
// NOTE(bill): only the ones that are needed (not all types)
|
||||||
// This will be set by the compiler
|
// This will be set by the compiler
|
||||||
type_table: []Type_Info
|
type_table: []^Type_Info
|
||||||
|
|
||||||
args__: []cstring
|
args__: []cstring
|
||||||
|
|
||||||
@@ -295,6 +306,14 @@ Source_Code_Location :: struct {
|
|||||||
procedure: string,
|
procedure: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used by the built-in directory `#load_directory(path: string) -> []Load_Directory_File`
|
||||||
|
*/
|
||||||
|
Load_Directory_File :: struct {
|
||||||
|
name: string,
|
||||||
|
data: []byte, // immutable data
|
||||||
|
}
|
||||||
|
|
||||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !
|
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !
|
||||||
|
|
||||||
// Allocation Stuff
|
// Allocation Stuff
|
||||||
@@ -306,6 +325,7 @@ Allocator_Mode :: enum byte {
|
|||||||
Query_Features,
|
Query_Features,
|
||||||
Query_Info,
|
Query_Info,
|
||||||
Alloc_Non_Zeroed,
|
Alloc_Non_Zeroed,
|
||||||
|
Resize_Non_Zeroed,
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||||
@@ -589,7 +609,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
|
|||||||
if n < 0 || n >= len(type_table) {
|
if n < 0 || n >= len(type_table) {
|
||||||
n = 0
|
n = 0
|
||||||
}
|
}
|
||||||
return &type_table[n]
|
return type_table[n]
|
||||||
}
|
}
|
||||||
|
|
||||||
when !ODIN_NO_RTTI {
|
when !ODIN_NO_RTTI {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
Maybe :: union($T: typeid) {T}
|
Maybe :: union($T: typeid) {T}
|
||||||
@@ -109,7 +109,7 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio
|
|||||||
|
|
||||||
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||||
//
|
//
|
||||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
// Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||||
assert(len(array) > 0, loc=loc)
|
assert(len(array) > 0, loc=loc)
|
||||||
@@ -122,7 +122,7 @@ pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bou
|
|||||||
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||||
// If the operation is not possible, it will return false.
|
// If the operation is not possible, it will return false.
|
||||||
@builtin
|
@builtin
|
||||||
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||||
if len(array) == 0 {
|
if len(array) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
|
|||||||
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||||
// If the operation is not possible, it will return false.
|
// If the operation is not possible, it will return false.
|
||||||
@builtin
|
@builtin
|
||||||
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||||
if len(array) == 0 {
|
if len(array) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -169,10 +169,16 @@ clear :: proc{clear_dynamic_array, clear_map}
|
|||||||
@builtin
|
@builtin
|
||||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
reserve :: proc{reserve_dynamic_array, reserve_map}
|
||||||
|
|
||||||
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
@builtin
|
||||||
|
non_zero_reserve :: proc{non_zero_reserve_dynamic_array}
|
||||||
|
|
||||||
|
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
@builtin
|
@builtin
|
||||||
resize :: proc{resize_dynamic_array}
|
resize :: proc{resize_dynamic_array}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize :: proc{non_zero_resize_dynamic_array}
|
||||||
|
|
||||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||||
@builtin
|
@builtin
|
||||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||||
@@ -234,6 +240,8 @@ delete :: proc{
|
|||||||
delete_dynamic_array,
|
delete_dynamic_array,
|
||||||
delete_slice,
|
delete_slice,
|
||||||
delete_map,
|
delete_map,
|
||||||
|
delete_soa_slice,
|
||||||
|
delete_soa_dynamic_array,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -304,6 +312,7 @@ make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, alloca
|
|||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
make_dynamic_array_error_loc(loc, len, cap)
|
make_dynamic_array_error_loc(loc, len, cap)
|
||||||
|
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
|
||||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
||||||
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
||||||
if data == nil && size_of(E) != 0 {
|
if data == nil && size_of(E) != 0 {
|
||||||
@@ -346,7 +355,7 @@ make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := con
|
|||||||
//
|
//
|
||||||
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||||
// type of its argument, not a pointer to it.
|
// type of its argument, not a pointer to it.
|
||||||
// Make uses the specified allocator, default is context.allocator, default is context.allocator
|
// Make uses the specified allocator, default is context.allocator.
|
||||||
@builtin
|
@builtin
|
||||||
make :: proc{
|
make :: proc{
|
||||||
make_slice,
|
make_slice,
|
||||||
@@ -404,10 +413,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
|
||||||
@builtin
|
|
||||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -418,7 +424,13 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
|||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+1 {
|
if cap(array) < len(array)+1 {
|
||||||
cap := 2 * cap(array) + max(8, 1)
|
cap := 2 * cap(array) + max(8, 1)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
if should_zero {
|
||||||
|
err = reserve(array, cap, loc)
|
||||||
|
} else {
|
||||||
|
err = non_zero_reserve(array, cap, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if cap(array)-len(array) > 0 {
|
if cap(array)-len(array) > 0 {
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
@@ -435,7 +447,16 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem(array, arg, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem(array, arg, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -452,7 +473,13 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
|||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+arg_len {
|
if cap(array) < len(array)+arg_len {
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
cap := 2 * cap(array) + max(8, arg_len)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
if should_zero {
|
||||||
|
err = reserve(array, cap, loc)
|
||||||
|
} else {
|
||||||
|
err = non_zero_reserve(array, cap, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
arg_len = min(cap(array)-len(array), arg_len)
|
||||||
if arg_len > 0 {
|
if arg_len > 0 {
|
||||||
@@ -468,11 +495,33 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems(array, true, loc, ..args)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems(array, false, loc, ..args)
|
||||||
|
}
|
||||||
|
|
||||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||||
|
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
args := transmute([]E)arg
|
||||||
|
if should_zero {
|
||||||
|
return append_elems(array, ..args, loc=loc)
|
||||||
|
} else {
|
||||||
|
return non_zero_append_elems(array, ..args, loc=loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
args := transmute([]E)arg
|
return _append_elem_string(array, arg, true, loc)
|
||||||
return append_elems(array, ..args, loc=loc)
|
}
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem_string(array, arg, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -492,6 +541,7 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
|||||||
|
|
||||||
// The append built-in procedure appends elements to the end of a dynamic array
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
||||||
|
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@@ -587,11 +637,14 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
|||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
if index+len(args) < len(array) {
|
new_size := index + len(args)
|
||||||
|
if len(args) == 0 {
|
||||||
|
ok = true
|
||||||
|
} else if new_size < len(array) {
|
||||||
copy(array[index:], args)
|
copy(array[index:], args)
|
||||||
ok = true
|
ok = true
|
||||||
} else {
|
} else {
|
||||||
resize(array, index+1+len(args), loc) or_return
|
resize(array, new_size, loc) or_return
|
||||||
copy(array[index:], args)
|
copy(array[index:], args)
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
@@ -633,8 +686,7 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
|||||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `reserve`.
|
// Note: Prefer the procedure group `reserve`.
|
||||||
@builtin
|
_reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -653,7 +705,12 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
new_size := capacity * size_of(E)
|
new_size := capacity * size_of(E)
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data: []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -663,11 +720,20 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array(array, capacity, true, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array(array, capacity, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `resize`
|
// Note: Prefer the procedure group `resize`
|
||||||
@builtin
|
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -687,7 +753,12 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
new_size := length * size_of(E)
|
new_size := length * size_of(E)
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data : []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -698,6 +769,16 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array(array, length, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array(array, length, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||||
|
|
||||||
@@ -743,42 +824,23 @@ map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
|||||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ.
|
||||||
@builtin
|
// - `prev_key_ptr` will return the previous pointer of a key if it exists, and `nil` otherwise.
|
||||||
incl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
// - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize
|
||||||
s^ |= {elem}
|
// - `found_previous` will be true if `prev_key_ptr != nil`
|
||||||
|
@(require_results)
|
||||||
|
map_insert_and_check_for_previous :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key_ptr: ^K, value_ptr: ^V, found_previous: bool) {
|
||||||
|
key, value := key, value
|
||||||
|
kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)
|
||||||
|
prev_key_ptr = (^K)(kp)
|
||||||
|
value_ptr = (^V)(vp)
|
||||||
|
found_previous = kp != nil
|
||||||
|
return
|
||||||
}
|
}
|
||||||
@builtin
|
|
||||||
incl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
|
||||||
for elem in elems {
|
|
||||||
s^ |= {elem}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
incl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
|
||||||
s^ |= other
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
|
||||||
s^ &~= {elem}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
|
||||||
for elem in elems {
|
|
||||||
s^ &~= {elem}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
|
||||||
s^ &~= other
|
|
||||||
}
|
|
||||||
|
|
||||||
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}
|
|
||||||
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}
|
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||||
when size_of(S) == 1 {
|
when size_of(S) == 1 {
|
||||||
return int(intrinsics.count_ones(transmute(u8)s))
|
return int(intrinsics.count_ones(transmute(u8)s))
|
||||||
} else when size_of(S) == 2 {
|
} else when size_of(S) == 2 {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -287,7 +287,7 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
|||||||
footer := raw_soa_footer(array)
|
footer := raw_soa_footer(array)
|
||||||
|
|
||||||
if size_of(E) > 0 && cap(array)-len(array) > 0 {
|
if size_of(E) > 0 && cap(array)-len(array) > 0 {
|
||||||
ti := type_info_of(typeid_of(T))
|
ti := type_info_of(T)
|
||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
field_count: uintptr
|
field_count: uintptr
|
||||||
+12
-12
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||||
total_size := uint(capacity + size_of(Memory_Block))
|
total_size := uint(capacity + max(alignment, size_of(Memory_Block)))
|
||||||
base_offset := uintptr(size_of(Memory_Block))
|
base_offset := uintptr(max(alignment, size_of(Memory_Block)))
|
||||||
|
|
||||||
min_alignment: int = max(16, align_of(Memory_Block))
|
min_alignment: int = max(16, align_of(Memory_Block), int(alignment))
|
||||||
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
||||||
block = (^Memory_Block)(raw_data(data))
|
block = (^Memory_Block)(raw_data(data))
|
||||||
end := uintptr(raw_data(data)[len(data):])
|
end := uintptr(raw_data(data)[len(data):])
|
||||||
@@ -102,20 +102,20 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
if size == 0 {
|
if size == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.capacity {
|
needed := align_forward_uint(size, alignment)
|
||||||
size = align_forward_uint(size, alignment)
|
if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
|
||||||
if arena.minimum_block_size == 0 {
|
if arena.minimum_block_size == 0 {
|
||||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
block_size := max(size, arena.minimum_block_size)
|
block_size := max(needed, arena.minimum_block_size)
|
||||||
|
|
||||||
if arena.backing_allocator.procedure == nil {
|
if arena.backing_allocator.procedure == nil {
|
||||||
arena.backing_allocator = default_allocator()
|
arena.backing_allocator = default_allocator()
|
||||||
}
|
}
|
||||||
|
|
||||||
new_block := memory_block_alloc(arena.backing_allocator, block_size, loc) or_return
|
new_block := memory_block_alloc(arena.backing_allocator, block_size, alignment, loc) or_return
|
||||||
new_block.prev = arena.curr_block
|
new_block.prev = arena.curr_block
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
@@ -134,7 +134,7 @@ arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc
|
|||||||
arena^ = {}
|
arena^ = {}
|
||||||
arena.backing_allocator = backing_allocator
|
arena.backing_allocator = backing_allocator
|
||||||
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
||||||
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, loc) or_return
|
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, 0, loc) or_return
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
return nil
|
return nil
|
||||||
@@ -195,7 +195,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
err = .Mode_Not_Implemented
|
err = .Mode_Not_Implemented
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
arena_free_all(arena, location)
|
arena_free_all(arena, location)
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
old_data := ([^]byte)(old_memory)
|
old_data := ([^]byte)(old_memory)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||||
|
default_allocator_proc :: nil_allocator_proc
|
||||||
|
default_allocator :: nil_allocator
|
||||||
|
} else when ODIN_DEFAULT_TO_PANIC_ALLOCATOR {
|
||||||
|
default_allocator_proc :: panic_allocator_proc
|
||||||
|
default_allocator :: panic_allocator
|
||||||
|
} else {
|
||||||
|
default_allocator :: heap_allocator
|
||||||
|
default_allocator_proc :: heap_allocator_proc
|
||||||
|
}
|
||||||
+6
-12
@@ -10,7 +10,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, .None
|
return nil, .None
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, .None
|
return nil, .None
|
||||||
}
|
}
|
||||||
@@ -31,14 +31,6 @@ nil_allocator :: proc() -> Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == .Freestanding {
|
|
||||||
default_allocator_proc :: nil_allocator_proc
|
|
||||||
default_allocator :: nil_allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
@@ -55,6 +47,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("panic allocator, .Resize called", loc=loc)
|
panic("panic allocator, .Resize called", loc=loc)
|
||||||
}
|
}
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
if size > 0 {
|
||||||
|
panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc)
|
||||||
|
}
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory != nil {
|
if old_memory != nil {
|
||||||
panic("panic allocator, .Free called", loc=loc)
|
panic("panic allocator, .Free called", loc=loc)
|
||||||
@@ -78,9 +74,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
panic_allocator :: proc() -> Allocator {
|
panic_allocator :: proc() -> Allocator {
|
||||||
return Allocator{
|
return Allocator{
|
||||||
procedure = nil_allocator_proc,
|
procedure = panic_allocator_proc,
|
||||||
data = nil,
|
data = nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ memcpy
|
|||||||
memove
|
memove
|
||||||
|
|
||||||
|
|
||||||
## Procedures required by the LLVM backend
|
## Procedures required by the LLVM backend if u128/i128 is used
|
||||||
umodti3
|
umodti3
|
||||||
udivti3
|
udivti3
|
||||||
modti3
|
modti3
|
||||||
@@ -59,11 +59,12 @@ truncdfhf2
|
|||||||
gnu_h2f_ieee
|
gnu_h2f_ieee
|
||||||
gnu_f2h_ieee
|
gnu_f2h_ieee
|
||||||
extendhfsf2
|
extendhfsf2
|
||||||
|
|
||||||
|
## Procedures required by the LLVM backend if f16 is used
|
||||||
__ashlti3 // wasm specific
|
__ashlti3 // wasm specific
|
||||||
__multi3 // wasm specific
|
__multi3 // wasm specific
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Required an entry point is defined (i.e. 'main')
|
## Required an entry point is defined (i.e. 'main')
|
||||||
|
|
||||||
args__
|
args__
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
||||||
@@ -44,7 +44,7 @@ _ :: intrinsics
|
|||||||
MAP_LOAD_FACTOR :: 75
|
MAP_LOAD_FACTOR :: 75
|
||||||
|
|
||||||
// Minimum log2 capacity.
|
// Minimum log2 capacity.
|
||||||
MAP_MIN_LOG2_CAPACITY :: 6 // 64 elements
|
MAP_MIN_LOG2_CAPACITY :: 3 // 8 elements
|
||||||
|
|
||||||
// Has to be less than 100% though.
|
// Has to be less than 100% though.
|
||||||
#assert(MAP_LOAD_FACTOR < 100)
|
#assert(MAP_LOAD_FACTOR < 100)
|
||||||
@@ -841,6 +841,33 @@ __dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__dynamic_map_get_key_and_value :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, key: rawptr) -> (key_ptr, value_ptr: rawptr) {
|
||||||
|
if m.len == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
pos := map_desired_position(m^, h)
|
||||||
|
distance := uintptr(0)
|
||||||
|
mask := (uintptr(1) << map_log2_cap(m^)) - 1
|
||||||
|
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
||||||
|
for {
|
||||||
|
element_hash := hs[pos]
|
||||||
|
if map_hash_is_empty(element_hash) {
|
||||||
|
return nil, nil
|
||||||
|
} else if distance > map_probe_distance(m^, element_hash, pos) {
|
||||||
|
return nil, nil
|
||||||
|
} else if element_hash == h {
|
||||||
|
other_key := rawptr(map_cell_index_dynamic(ks, info.ks, pos))
|
||||||
|
if info.key_equal(key, other_key) {
|
||||||
|
key_ptr = other_key
|
||||||
|
value_ptr = rawptr(map_cell_index_dynamic(vs, info.vs, pos))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos = (pos + 1) & mask
|
||||||
|
distance += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT: USED WITHIN THE COMPILER
|
// IMPORTANT: USED WITHIN THE COMPILER
|
||||||
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
|
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
|
||||||
if m.len >= map_resize_threshold(m^) {
|
if m.len >= map_resize_threshold(m^) {
|
||||||
@@ -874,6 +901,30 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
|
|||||||
m.len += 1
|
m.len += 1
|
||||||
return rawptr(result)
|
return rawptr(result)
|
||||||
}
|
}
|
||||||
|
__dynamic_map_set_extra_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
|
||||||
|
return __dynamic_map_set_extra(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
|
||||||
|
if prev_key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
|
||||||
|
intrinsics.mem_copy_non_overlapping(value_ptr, value, info.vs.size_of_type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := hash
|
||||||
|
err, has_grown := __dynamic_map_check_grow(m, info, loc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if has_grown {
|
||||||
|
hash = info.key_hasher(key, map_seed(m^))
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
||||||
|
m.len += 1
|
||||||
|
return nil, rawptr(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// IMPORTANT: USED WITHIN THE COMPILER
|
// IMPORTANT: USED WITHIN THE COMPILER
|
||||||
@(private)
|
@(private)
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build linux, darwin, freebsd, openbsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_BUILD_MODE == .Dynamic {
|
when ODIN_BUILD_MODE == .Dynamic {
|
||||||
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
||||||
@@ -26,8 +27,13 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
// to retrieve argc and argv from the stack
|
// to retrieve argc and argv from the stack
|
||||||
when ODIN_ARCH == .amd64 {
|
when ODIN_ARCH == .amd64 {
|
||||||
@require foreign import entry "entry_unix_no_crt_amd64.asm"
|
@require foreign import entry "entry_unix_no_crt_amd64.asm"
|
||||||
|
SYS_exit :: 60
|
||||||
} else when ODIN_ARCH == .i386 {
|
} else when ODIN_ARCH == .i386 {
|
||||||
@require foreign import entry "entry_unix_no_crt_i386.asm"
|
@require foreign import entry "entry_unix_no_crt_i386.asm"
|
||||||
|
SYS_exit :: 1
|
||||||
|
} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||||
|
@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
|
||||||
|
SYS_exit :: 1
|
||||||
}
|
}
|
||||||
@(link_name="_start_odin", linkage="strong", require)
|
@(link_name="_start_odin", linkage="strong", require)
|
||||||
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
||||||
@@ -36,11 +42,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
#force_no_inline _startup_runtime()
|
#force_no_inline _startup_runtime()
|
||||||
intrinsics.__entry_point()
|
intrinsics.__entry_point()
|
||||||
#force_no_inline _cleanup_runtime()
|
#force_no_inline _cleanup_runtime()
|
||||||
when ODIN_ARCH == .amd64 {
|
intrinsics.syscall(SYS_exit, 0)
|
||||||
intrinsics.syscall(/*SYS_exit = */60)
|
|
||||||
} else when ODIN_ARCH == .i386 {
|
|
||||||
intrinsics.syscall(/*SYS_exit = */1)
|
|
||||||
}
|
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.section __TEXT,__text
|
||||||
|
|
||||||
|
; NOTE(laytan): this should ideally be the -minimum-os-version flag but there is no nice way of preprocessing assembly in Odin.
|
||||||
|
; 10 seems to be the lowest it goes and I don't see it mess with any targeted os version so this seems fine.
|
||||||
|
.build_version macos, 10, 0
|
||||||
|
|
||||||
|
.extern __start_odin
|
||||||
|
|
||||||
|
.global _main
|
||||||
|
.align 2
|
||||||
|
_main:
|
||||||
|
mov x5, sp ; use x5 as the stack pointer
|
||||||
|
|
||||||
|
str x0, [x5] ; get argc into x0 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
|
||||||
|
str x1, [x5, #8] ; get argv into x1
|
||||||
|
|
||||||
|
and sp, x5, #~15 ; force 16-byte alignment of the stack
|
||||||
|
|
||||||
|
bl __start_odin ; call into Odin entry point
|
||||||
|
ret ; should never get here
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build wasm32, wasm64p32
|
//+build wasm32, wasm64p32
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||||
@(link_name="_start", linkage="strong", require, export)
|
@(link_name="_start", linkage="strong", require, export)
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
//+private
|
//+private
|
||||||
//+build windows
|
//+build windows
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_BUILD_MODE == .Dynamic {
|
when ODIN_BUILD_MODE == .Dynamic {
|
||||||
@(link_name="DllMain", linkage="strong", require)
|
@(link_name="DllMain", linkage="strong", require)
|
||||||
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
|
|
||||||
// Populate Windows DLL-specific global
|
// Populate Windows DLL-specific global
|
||||||
@@ -28,7 +29,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||||
mainCRTStartup :: proc "stdcall" () -> i32 {
|
mainCRTStartup :: proc "system" () -> i32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
#force_no_inline _startup_runtime()
|
#force_no_inline _startup_runtime()
|
||||||
intrinsics.__entry_point()
|
intrinsics.__entry_point()
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
bounds_trap :: proc "contextless" () -> ! {
|
bounds_trap :: proc "contextless" () -> ! {
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
windows_trap_array_bounds()
|
windows_trap_array_bounds()
|
||||||
@@ -8,6 +9,7 @@ bounds_trap :: proc "contextless" () -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
type_assertion_trap :: proc "contextless" () -> ! {
|
type_assertion_trap :: proc "contextless" () -> ! {
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
windows_trap_type_assertion()
|
windows_trap_type_assertion()
|
||||||
@@ -21,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
|||||||
if uint(index) < uint(count) {
|
if uint(index) < uint(count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Index ")
|
print_string(" Index ")
|
||||||
@@ -34,6 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
|||||||
handle_error(file, line, column, index, count)
|
handle_error(file, line, column, index, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid slice indices ")
|
print_string(" Invalid slice indices ")
|
||||||
@@ -46,6 +49,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
|
|||||||
bounds_trap()
|
bounds_trap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid slice indices ")
|
print_string(" Invalid slice indices ")
|
||||||
@@ -82,7 +86,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
|||||||
if 0 <= low && low <= high && high <= max {
|
if 0 <= low && low <= high && high <= max {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid dynamic array indices ")
|
print_string(" Invalid dynamic array indices ")
|
||||||
@@ -103,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
|
|||||||
uint(column_index) < uint(column_count) {
|
uint(column_index) < uint(column_count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Matrix indices [")
|
print_string(" Matrix indices [")
|
||||||
@@ -127,7 +131,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion\n")
|
print_string(" Invalid type assertion\n")
|
||||||
@@ -140,7 +144,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion\n")
|
print_string(" Invalid type assertion\n")
|
||||||
@@ -153,7 +157,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion from ")
|
print_string(" Invalid type assertion from ")
|
||||||
@@ -198,7 +202,7 @@ when ODIN_NO_RTTI {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||||
|
|
||||||
actual := variant_type(from, from_data)
|
actual := variant_type(from, from_data)
|
||||||
@@ -224,7 +228,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
|||||||
if 0 <= len {
|
if 0 <= len {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid slice length for make: ")
|
print_string(" Invalid slice length for make: ")
|
||||||
@@ -239,7 +243,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller
|
|||||||
if 0 <= len && len <= cap {
|
if 0 <= len && len <= cap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid dynamic array parameters for make: ")
|
print_string(" Invalid dynamic array parameters for make: ")
|
||||||
@@ -256,7 +260,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
|
|||||||
if 0 <= cap {
|
if 0 <= cap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid map capacity for make: ")
|
print_string(" Invalid map capacity for make: ")
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
heap_allocator :: proc() -> Allocator {
|
||||||
|
return Allocator{
|
||||||
|
procedure = heap_allocator_proc,
|
||||||
|
data = nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
|
size, alignment: int,
|
||||||
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
//
|
||||||
|
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||||
|
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||||
|
// padding. We also store the original pointer returned by heap_alloc right before
|
||||||
|
// the pointer we return to the user.
|
||||||
|
//
|
||||||
|
|
||||||
|
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||||
|
a := max(alignment, align_of(rawptr))
|
||||||
|
space := size + a - 1
|
||||||
|
|
||||||
|
allocated_mem: rawptr
|
||||||
|
if old_ptr != nil {
|
||||||
|
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
||||||
|
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||||
|
} else {
|
||||||
|
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||||
|
}
|
||||||
|
aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
|
||||||
|
|
||||||
|
ptr := uintptr(aligned_mem)
|
||||||
|
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
||||||
|
diff := int(aligned_ptr - ptr)
|
||||||
|
if (size + diff) > space || allocated_mem == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_mem = rawptr(aligned_ptr)
|
||||||
|
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
||||||
|
|
||||||
|
return byte_slice(aligned_mem, size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_free :: proc(p: rawptr) {
|
||||||
|
if p != nil {
|
||||||
|
heap_free(([^]rawptr)(p)[-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
new_memory = aligned_alloc(new_size, new_alignment, p, zero_memory) or_return
|
||||||
|
|
||||||
|
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||||
|
if zero_memory && new_size > old_size {
|
||||||
|
new_region := raw_data(new_memory[old_size:])
|
||||||
|
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
|
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||||
|
|
||||||
|
case .Free:
|
||||||
|
aligned_free(old_memory)
|
||||||
|
|
||||||
|
case .Free_All:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
|
if old_memory == nil {
|
||||||
|
return aligned_alloc(size, alignment, nil, mode == .Resize)
|
||||||
|
}
|
||||||
|
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
||||||
|
|
||||||
|
case .Query_Features:
|
||||||
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
|
if set != nil {
|
||||||
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
case .Query_Info:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
return _heap_alloc(size, zero_memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
return _heap_resize(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_free :: proc(ptr: rawptr) {
|
||||||
|
_heap_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//+build js, wasi, freestanding, essence
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc(ptr: rawptr) {
|
||||||
|
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//+build linux, darwin, freebsd, openbsd
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
when ODIN_OS == .Darwin {
|
||||||
|
foreign import libc "system:System.framework"
|
||||||
|
} else {
|
||||||
|
foreign import libc "system:c"
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
|
||||||
|
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
|
||||||
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||||
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if zero_memory {
|
||||||
|
return _unix_calloc(1, size)
|
||||||
|
} else {
|
||||||
|
return _unix_malloc(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||||
|
// POSIX platforms. Ensure your caller takes this into account.
|
||||||
|
return _unix_realloc(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc(ptr: rawptr) {
|
||||||
|
_unix_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign kernel32 {
|
||||||
|
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||||
|
|
||||||
|
// default_allocator
|
||||||
|
GetProcessHeap :: proc() -> rawptr ---
|
||||||
|
HeapAlloc :: proc(hHeap: rawptr, dwFlags: u32, dwBytes: uint) -> rawptr ---
|
||||||
|
HeapReAlloc :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr, dwBytes: uint) -> rawptr ---
|
||||||
|
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||||
|
}
|
||||||
|
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
if new_size == 0 {
|
||||||
|
_heap_free(ptr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ptr == nil {
|
||||||
|
return _heap_alloc(new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
|
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||||
|
}
|
||||||
|
_heap_free :: proc(ptr: rawptr) {
|
||||||
|
if ptr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
HeapFree(GetProcessHeap(), 0, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
||||||
@@ -11,7 +11,7 @@ RUNTIME_LINKAGE :: "strong" when (
|
|||||||
ODIN_BUILD_MODE == .Dynamic ||
|
ODIN_BUILD_MODE == .Dynamic ||
|
||||||
!ODIN_NO_CRT) &&
|
!ODIN_NO_CRT) &&
|
||||||
!IS_WASM) else "internal"
|
!IS_WASM) else "internal"
|
||||||
RUNTIME_REQUIRE :: !ODIN_TILDE
|
RUNTIME_REQUIRE :: false // !ODIN_TILDE
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
__float16 :: f16 when __ODIN_LLVM_F16_SUPPORTED else u16
|
__float16 :: f16 when __ODIN_LLVM_F16_SUPPORTED else u16
|
||||||
@@ -22,51 +22,7 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
|
|||||||
return ([^]byte)(data)[:max(len, 0)]
|
return ([^]byte)(data)[:max(len, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
bswap_16 :: proc "contextless" (x: u16) -> u16 {
|
is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
|
||||||
return x>>8 | x<<8
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_32 :: proc "contextless" (x: u32) -> u32 {
|
|
||||||
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_64 :: proc "contextless" (x: u64) -> u64 {
|
|
||||||
z := x
|
|
||||||
z = (z & 0x00000000ffffffff) << 32 | (z & 0xffffffff00000000) >> 32
|
|
||||||
z = (z & 0x0000ffff0000ffff) << 16 | (z & 0xffff0000ffff0000) >> 16
|
|
||||||
z = (z & 0x00ff00ff00ff00ff) << 8 | (z & 0xff00ff00ff00ff00) >> 8
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_128 :: proc "contextless" (x: u128) -> u128 {
|
|
||||||
z := transmute([4]u32)x
|
|
||||||
z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
|
|
||||||
z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
|
|
||||||
return transmute(u128)z
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f16 :: proc "contextless" (f: f16) -> f16 {
|
|
||||||
x := transmute(u16)f
|
|
||||||
z := bswap_16(x)
|
|
||||||
return transmute(f16)z
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f32 :: proc "contextless" (f: f32) -> f32 {
|
|
||||||
x := transmute(u32)f
|
|
||||||
z := bswap_32(x)
|
|
||||||
return transmute(f32)z
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f64 :: proc "contextless" (f: f64) -> f64 {
|
|
||||||
x := transmute(u64)f
|
|
||||||
z := bswap_64(x)
|
|
||||||
return transmute(f64)z
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
|
|
||||||
if x <= 0 {
|
if x <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -84,7 +40,7 @@ align_forward_int :: #force_inline proc(ptr, align: int) -> int {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
is_power_of_two_uintptr :: #force_inline proc(x: uintptr) -> bool {
|
is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool {
|
||||||
if x <= 0 {
|
if x <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -187,7 +143,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
if allocator.procedure == nil {
|
if allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -198,15 +154,27 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if ptr == nil {
|
} else if ptr == nil {
|
||||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
if should_zero {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
||||||
data = ([^]byte)(ptr)[:old_size]
|
data = ([^]byte)(ptr)[:old_size]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
if should_zero {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize_Non_Zeroed, new_size, alignment, ptr, old_size, loc)
|
||||||
|
}
|
||||||
if err == .Mode_Not_Implemented {
|
if err == .Mode_Not_Implemented {
|
||||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
if should_zero {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -216,6 +184,13 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc)
|
||||||
|
}
|
||||||
|
non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||||
switch {
|
switch {
|
||||||
case n == 0: return true
|
case n == 0: return true
|
||||||
@@ -589,36 +564,6 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
|||||||
return r, size
|
return r, size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
|
|
||||||
min_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
min_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
min_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
max_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
max_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
|
|
||||||
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
||||||
p, q := abs(real(x)), abs(imag(x))
|
p, q := abs(real(x)), abs(imag(x))
|
||||||
if p < q {
|
if p < q {
|
||||||
@@ -730,7 +675,7 @@ mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -742,7 +687,7 @@ mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||||
@@ -754,7 +699,7 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||||
@@ -768,7 +713,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -782,7 +727,7 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||||
@@ -796,7 +741,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
@@ -1089,3 +1034,25 @@ fixdfti :: proc(a: u64) -> i128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||||
|
for i in 0..<size {
|
||||||
|
j := offset+i
|
||||||
|
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
|
||||||
|
b := the_bit<<(j&7)
|
||||||
|
dst[j/8] &~= b
|
||||||
|
dst[j/8] |= b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||||
|
for j in 0..<size {
|
||||||
|
i := offset+j
|
||||||
|
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
|
||||||
|
b := the_bit<<(j&7)
|
||||||
|
dst[j/8] &~= b
|
||||||
|
dst[j/8] |= b
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
_OS_Errno :: distinct int
|
||||||
|
|
||||||
|
stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
return _stderr_write(data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//+build freebsd, openbsd
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="write")
|
||||||
|
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||||
|
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
ret := _unix_write(2, raw_data(data), len(data))
|
||||||
|
if ret < len(data) {
|
||||||
|
err := __error()
|
||||||
|
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//+build darwin
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
WRITE :: 0x20000004
|
||||||
|
STDERR :: 2
|
||||||
|
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
||||||
|
if ret < 0 {
|
||||||
|
return 0, _OS_Errno(-ret)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
//+build freestanding
|
//+build freestanding
|
||||||
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
// TODO(bill): reimplement `os.write`
|
// TODO(bill): reimplement `os.write`
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
return 0, -1
|
return 0, -1
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
//+build js
|
//+build js
|
||||||
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import "odin_env"
|
foreign import "odin_env"
|
||||||
|
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
foreign odin_env {
|
foreign odin_env {
|
||||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
when ODIN_ARCH == .amd64 {
|
||||||
|
SYS_write :: uintptr(1)
|
||||||
|
} else when ODIN_ARCH == .arm64 {
|
||||||
|
SYS_write :: uintptr(64)
|
||||||
|
} else when ODIN_ARCH == .i386 {
|
||||||
|
SYS_write :: uintptr(4)
|
||||||
|
} else when ODIN_ARCH == .arm32 {
|
||||||
|
SYS_write :: uintptr(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr :: 2
|
||||||
|
|
||||||
|
ret := int(intrinsics.syscall(SYS_write, uintptr(stderr), uintptr(raw_data(data)), uintptr(len(data))))
|
||||||
|
if ret < 0 && ret > -4096 {
|
||||||
|
return 0, _OS_Errno(-ret)
|
||||||
|
}
|
||||||
|
return ret, 0
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
//+build wasi
|
//+build wasi
|
||||||
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:sys/wasm/wasi"
|
import "core:sys/wasm/wasi"
|
||||||
|
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
data := (wasi.ciovec_t)(data)
|
data := (wasi.ciovec_t)(data)
|
||||||
n, err := wasi.fd_write(1, {data})
|
n, err := wasi.fd_write(1, {data})
|
||||||
return int(n), _OS_Errno(err)
|
return int(n), _OS_Errno(err)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
//+build windows
|
||||||
|
//+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign kernel32 {
|
||||||
|
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||||
|
|
||||||
|
// stderr_write
|
||||||
|
GetStdHandle :: proc(which: u32) -> rawptr ---
|
||||||
|
SetHandleInformation :: proc(hObject: rawptr, dwMask: u32, dwFlags: u32) -> b32 ---
|
||||||
|
WriteFile :: proc(hFile: rawptr, lpBuffer: rawptr, nNumberOfBytesToWrite: u32, lpNumberOfBytesWritten: ^u32, lpOverlapped: rawptr) -> b32 ---
|
||||||
|
GetLastError :: proc() -> u32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
STD_ERROR_HANDLE :: ~u32(0) -12 + 1
|
||||||
|
HANDLE_FLAG_INHERIT :: 0x00000001
|
||||||
|
MAX_RW :: 1<<30
|
||||||
|
|
||||||
|
h := GetStdHandle(STD_ERROR_HANDLE)
|
||||||
|
when size_of(uintptr) == 8 {
|
||||||
|
SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
single_write_length: u32
|
||||||
|
total_write: i64
|
||||||
|
length := i64(len(data))
|
||||||
|
|
||||||
|
for total_write < length {
|
||||||
|
remaining := length - total_write
|
||||||
|
to_write := u32(min(i32(remaining), MAX_RW))
|
||||||
|
|
||||||
|
e := WriteFile(h, &data[total_write], to_write, &single_write_length, nil)
|
||||||
|
if single_write_length <= 0 || !e {
|
||||||
|
err = _OS_Errno(GetLastError())
|
||||||
|
n = int(total_write)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total_write += i64(single_write_length)
|
||||||
|
}
|
||||||
|
n = int(total_write)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -123,13 +123,13 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_string :: proc "contextless" (str: string) -> (n: int) {
|
print_string :: proc "contextless" (str: string) -> (n: int) {
|
||||||
n, _ = os_write(transmute([]byte)str)
|
n, _ = stderr_write(transmute([]byte)str)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
||||||
for str in args {
|
for str in args {
|
||||||
m, err := os_write(transmute([]byte)str)
|
m, err := stderr_write(transmute([]byte)str)
|
||||||
n += m
|
n += m
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
break
|
break
|
||||||
@@ -139,7 +139,7 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
||||||
n, _ = os_write([]byte{b})
|
n, _ = stderr_write([]byte{b})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b, n := encode_rune(r)
|
b, n := encode_rune(r)
|
||||||
m, _ := os_write(b[:n])
|
m, _ := stderr_write(b[:n])
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
|||||||
}
|
}
|
||||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||||
|
|
||||||
os_write(a[i:])
|
stderr_write(a[i:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
|||||||
i -= 1; a[i] = '-'
|
i -= 1; a[i] = '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
os_write(a[i:])
|
stderr_write(a[i:])
|
||||||
}
|
}
|
||||||
|
|
||||||
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
||||||
@@ -459,6 +459,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
|||||||
}
|
}
|
||||||
print_byte(']')
|
print_byte(']')
|
||||||
|
|
||||||
|
case Type_Info_Bit_Field:
|
||||||
|
print_string("bit_field ")
|
||||||
|
print_type(info.backing_type)
|
||||||
|
print_string(" {")
|
||||||
|
for name, i in info.names {
|
||||||
|
if i > 0 { print_string(", ") }
|
||||||
|
print_string(name)
|
||||||
|
print_string(": ")
|
||||||
|
print_type(info.types[i])
|
||||||
|
print_string(" | ")
|
||||||
|
print_u64(u64(info.bit_sizes[i]))
|
||||||
|
}
|
||||||
|
print_byte('}')
|
||||||
|
|
||||||
|
|
||||||
case Type_Info_Simd_Vector:
|
case Type_Info_Simd_Vector:
|
||||||
print_string("#simd[")
|
print_string("#simd[")
|
||||||
@@ -4,7 +4,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
foreign import lib "system:NtDll.lib"
|
foreign import lib "system:NtDll.lib"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="stdcall")
|
@(default_calling_convention="system")
|
||||||
foreign lib {
|
foreign lib {
|
||||||
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
||||||
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
||||||
@@ -37,7 +37,18 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(link_name="bzero", linkage="strong", require)
|
||||||
|
bzero :: proc "c" (ptr: rawptr, len: int) -> rawptr {
|
||||||
|
if ptr != nil && len != 0 {
|
||||||
|
p := ([^]byte)(ptr)
|
||||||
|
for i := 0; i < len; i += 1 {
|
||||||
|
p[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
@(link_name="memmove", linkage="strong", require)
|
@(link_name="memmove", linkage="strong", require)
|
||||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||||
@@ -3,7 +3,7 @@ package runtime
|
|||||||
|
|
||||||
foreign import "system:Foundation.framework"
|
foreign import "system:Foundation.framework"
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
objc_id :: ^intrinsics.objc_object
|
objc_id :: ^intrinsics.objc_object
|
||||||
objc_Class :: ^intrinsics.objc_class
|
objc_Class :: ^intrinsics.objc_class
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
//+private
|
//+private
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
foreign kernel32 {
|
foreign kernel32 {
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//+private
|
//+private
|
||||||
|
//+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@require foreign import "system:int64.lib"
|
@require foreign import "system:int64.lib"
|
||||||
@@ -12,7 +13,7 @@ windows_trap_array_bounds :: proc "contextless" () -> ! {
|
|||||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
||||||
|
|
||||||
foreign kernel32 {
|
foreign kernel32 {
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||||
_ctz :: intrinsics.count_trailing_zeros
|
_ctz :: intrinsics.count_trailing_zeros
|
||||||
Binary file not shown.
Binary file not shown.
@@ -110,7 +110,8 @@ if %errorlevel% neq 0 goto end_of_build
|
|||||||
call build_vendor.bat
|
call build_vendor.bat
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
if %release_mode% EQU 0 odin run examples/demo
|
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
||||||
|
if %release_mode% EQU 0 odin run examples/demo -- Hellope World
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -27,11 +27,13 @@ error() {
|
|||||||
if [ -z "$LLVM_CONFIG" ]; then
|
if [ -z "$LLVM_CONFIG" ]; then
|
||||||
# darwin, linux, openbsd
|
# darwin, linux, openbsd
|
||||||
if [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
if [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||||
|
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||||
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||||
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||||
# freebsd
|
# freebsd
|
||||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config-17"
|
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||||
|
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||||
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||||
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||||
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||||
@@ -61,8 +63,7 @@ Darwin)
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
|
LDFLAGS="$LDFLAGS -liconv -ldl -framework System -lLLVM"
|
||||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
|
||||||
;;
|
;;
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
@@ -117,7 +118,7 @@ build_odin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_demo() {
|
run_demo() {
|
||||||
./odin run examples/demo/demo.odin -file
|
./odin run examples/demo/demo.odin -file -- Hellope World
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
|
|||||||
Regular → Executable
+14
-2
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
bucket=$1
|
bucket=$1
|
||||||
platform=$2
|
platform=$2
|
||||||
artifact=$3
|
artifact=$3
|
||||||
@@ -9,5 +11,15 @@ filename="odin-$platform-nightly+$now.zip"
|
|||||||
|
|
||||||
echo "Creating archive $filename from $artifact and uploading to $bucket"
|
echo "Creating archive $filename from $artifact and uploading to $bucket"
|
||||||
|
|
||||||
7z a -bd "output/$filename" -r "$artifact"
|
# If this is already zipped up (done before artifact upload to keep permissions in tact), just move it.
|
||||||
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
if [ "${artifact: -4}" == ".zip" ]
|
||||||
|
then
|
||||||
|
echo "Artifact already a zip"
|
||||||
|
mkdir -p "output"
|
||||||
|
mv "$artifact" "output/$filename"
|
||||||
|
else
|
||||||
|
echo "Artifact needs to be zipped"
|
||||||
|
7z a -bd "output/$filename" -r "$artifact"
|
||||||
|
fi
|
||||||
|
|
||||||
|
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "core:bytes"
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// Extra errors returns by scanning procedures
|
// Extra errors returns by scanning procedures
|
||||||
Scanner_Extra_Error :: enum i32 {
|
Scanner_Extra_Error :: enum i32 {
|
||||||
|
|||||||
@@ -895,7 +895,7 @@ split_multi_iterator :: proc(s: ^[]byte, substrs: [][]byte, skip_empty := false)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
|
// Scrubs invalid utf-8 characters and replaces them with the replacement string
|
||||||
// Adjacent invalid bytes are only replaced once
|
// Adjacent invalid bytes are only replaced once
|
||||||
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
||||||
str := s
|
str := s
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
package c
|
package c
|
||||||
|
|
||||||
import builtin "core:builtin"
|
import builtin "base:builtin"
|
||||||
|
|
||||||
char :: builtin.u8 // assuming -funsigned-char
|
char :: builtin.u8 // assuming -funsigned-char
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ foreign libc {
|
|||||||
crealf :: proc(z: complex_float) -> float ---
|
crealf :: proc(z: complex_float) -> float ---
|
||||||
}
|
}
|
||||||
|
|
||||||
import builtin "core:builtin"
|
import builtin "base:builtin"
|
||||||
|
|
||||||
complex_float :: distinct builtin.complex64
|
complex_float :: distinct builtin.complex64
|
||||||
complex_double :: distinct builtin.complex128
|
complex_double :: distinct builtin.complex128
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.12 Mathematics
|
// 7.12 Mathematics
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.16 Variable arguments
|
// 7.16 Variable arguments
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="none")
|
@(default_calling_convention="none")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.17 Atomics
|
// 7.17 Atomics
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
ATOMIC_BOOL_LOCK_FREE :: true
|
ATOMIC_BOOL_LOCK_FREE :: true
|
||||||
ATOMIC_CHAR_LOCK_FREE :: true
|
ATOMIC_CHAR_LOCK_FREE :: true
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:libucrt.lib",
|
"system:libucrt.lib",
|
||||||
@@ -218,3 +220,102 @@ foreign libc {
|
|||||||
ferror :: proc(stream: ^FILE) -> int ---
|
ferror :: proc(stream: ^FILE) -> int ---
|
||||||
perror :: proc(s: cstring) ---
|
perror :: proc(s: cstring) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
to_stream :: proc(file: ^FILE) -> io.Stream {
|
||||||
|
stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||||
|
unknown_or_eof :: proc(f: ^FILE) -> io.Error {
|
||||||
|
switch {
|
||||||
|
case ferror(f) != 0:
|
||||||
|
return .Unknown
|
||||||
|
case feof(f) != 0:
|
||||||
|
return .EOF
|
||||||
|
case:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file := (^FILE)(stream_data)
|
||||||
|
switch mode {
|
||||||
|
case .Close:
|
||||||
|
if fclose(file) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Flush:
|
||||||
|
if fflush(file) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Read:
|
||||||
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Read_At:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fseek(file, long(offset), SEEK_SET) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fseek(file, long(curr), SEEK_SET)
|
||||||
|
|
||||||
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Write:
|
||||||
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Write_At:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fseek(file, long(offset), SEEK_SET) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fseek(file, long(curr), SEEK_SET)
|
||||||
|
|
||||||
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Seek:
|
||||||
|
if fseek(file, long(offset), int(whence)) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Size:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
defer fseek(file, curr, SEEK_SET)
|
||||||
|
|
||||||
|
if fseek(file, 0, SEEK_END) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
n = i64(ftell(file))
|
||||||
|
if n == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Destroy:
|
||||||
|
return 0, .Empty
|
||||||
|
|
||||||
|
case .Query:
|
||||||
|
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data = file,
|
||||||
|
procedure = stream_proc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
// 7.24 String handling
|
// 7.24 String handling
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package libc
|
|||||||
|
|
||||||
import "core:c"
|
import "core:c"
|
||||||
|
|
||||||
|
#assert(!ODIN_NO_CRT, `"core:c/libc" cannot be imported when '-no-crt' is used`)
|
||||||
|
|
||||||
char :: c.char // assuming -funsigned-char
|
char :: c.char // assuming -funsigned-char
|
||||||
|
|
||||||
schar :: c.schar
|
schar :: c.schar
|
||||||
|
|||||||
+33
-43
@@ -12,7 +12,7 @@ package compress
|
|||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||||
@@ -20,10 +20,9 @@ import "core:runtime"
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
// When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
// we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -34,16 +33,14 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
when size_of(uintptr) == 8 {
|
when size_of(uintptr) == 8 {
|
||||||
/*
|
|
||||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||||
which is GZIP and PKZIP's max payload size.
|
// which is GZIP and PKZIP's max payload size.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||||
} else {
|
} else {
|
||||||
/*
|
|
||||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||||
*/
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -69,9 +66,8 @@ General_Error :: enum {
|
|||||||
Incompatible_Options,
|
Incompatible_Options,
|
||||||
Unimplemented,
|
Unimplemented,
|
||||||
|
|
||||||
/*
|
// Memory errors
|
||||||
Memory errors
|
|
||||||
*/
|
|
||||||
Allocation_Failed,
|
Allocation_Failed,
|
||||||
Resize_Failed,
|
Resize_Failed,
|
||||||
}
|
}
|
||||||
@@ -86,17 +82,16 @@ GZIP_Error :: enum {
|
|||||||
Payload_Length_Invalid,
|
Payload_Length_Invalid,
|
||||||
Payload_CRC_Invalid,
|
Payload_CRC_Invalid,
|
||||||
|
|
||||||
/*
|
// GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
// If you tell it you expect it to contain more, that's obviously an error.
|
||||||
If you tell it you expect it to contain more, that's obviously an error.
|
|
||||||
*/
|
Payload_Size_Exceeds_Max_Payload,
|
||||||
Payload_Size_Exceeds_Max_Payload,
|
|
||||||
/*
|
// For buffered instead of streamed output, the payload size can't exceed
|
||||||
For buffered instead of streamed output, the payload size can't exceed
|
// the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
//
|
||||||
|
// You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||||
|
|
||||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
|
||||||
*/
|
|
||||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,9 +132,8 @@ Context_Memory_Input :: struct #packed {
|
|||||||
code_buffer: u64,
|
code_buffer: u64,
|
||||||
num_bits: u64,
|
num_bits: u64,
|
||||||
|
|
||||||
/*
|
// If we know the data size, we can optimize the reads and writes.
|
||||||
If we know the data size, we can optimize the reads and writes.
|
|
||||||
*/
|
|
||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
}
|
}
|
||||||
@@ -159,18 +153,16 @@ Context_Stream_Input :: struct #packed {
|
|||||||
code_buffer: u64,
|
code_buffer: u64,
|
||||||
num_bits: u64,
|
num_bits: u64,
|
||||||
|
|
||||||
/*
|
// If we know the data size, we can optimize the reads and writes.
|
||||||
If we know the data size, we can optimize the reads and writes.
|
|
||||||
*/
|
|
||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
|
|
||||||
/*
|
// Flags:
|
||||||
Flags:
|
// `input_fully_in_memory`
|
||||||
`input_fully_in_memory`
|
// true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
// false = Try to refill `input_data` from the `input` stream.
|
||||||
false = Try to refill `input_data` from the `input` stream.
|
|
||||||
*/
|
|
||||||
input_fully_in_memory: b8,
|
input_fully_in_memory: b8,
|
||||||
|
|
||||||
padding: [1]u8,
|
padding: [1]u8,
|
||||||
@@ -214,7 +206,7 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
|||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||||
// the is literally no need for it
|
// there is literally no need for it
|
||||||
b := make([]u8, size, context.temp_allocator)
|
b := make([]u8, size, context.temp_allocator)
|
||||||
_ = io.read(z.input, b[:]) or_return
|
_ = io.read(z.input, b[:]) or_return
|
||||||
return b, nil
|
return b, nil
|
||||||
@@ -248,10 +240,8 @@ read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8,
|
|||||||
|
|
||||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
||||||
|
|
||||||
/*
|
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
// preferentially.
|
||||||
preferentially.
|
|
||||||
*/
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||||
if z.num_bits >= 8 {
|
if z.num_bits >= 8 {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
// package shoco is an implementation of the shoco short string compressor
|
// package shoco is an implementation of the shoco short string compressor
|
||||||
package shoco
|
package shoco
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:compress"
|
import "core:compress"
|
||||||
|
|
||||||
Shoco_Pack :: struct {
|
Shoco_Pack :: struct {
|
||||||
|
|||||||
@@ -0,0 +1,678 @@
|
|||||||
|
/*
|
||||||
|
package avl implements an AVL tree.
|
||||||
|
|
||||||
|
The implementation is non-intrusive, and non-recursive.
|
||||||
|
*/
|
||||||
|
package container_avl
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:slice"
|
||||||
|
|
||||||
|
_ :: intrinsics
|
||||||
|
_ :: runtime
|
||||||
|
|
||||||
|
// Originally based on the CC0 implementation by Eric Biggers
|
||||||
|
// See: https://github.com/ebiggers/avl_tree/
|
||||||
|
|
||||||
|
// Direction specifies the traversal direction for a tree iterator.
|
||||||
|
Direction :: enum i8 {
|
||||||
|
// Backward is the in-order backwards direction.
|
||||||
|
Backward = -1,
|
||||||
|
// Forward is the in-order forwards direction.
|
||||||
|
Forward = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordering specifies order when inserting/finding values into the tree.
|
||||||
|
Ordering :: slice.Ordering
|
||||||
|
|
||||||
|
// Tree is an AVL tree.
|
||||||
|
Tree :: struct($Value: typeid) {
|
||||||
|
// user_data is a parameter that will be passed to the on_remove
|
||||||
|
// callback.
|
||||||
|
user_data: rawptr,
|
||||||
|
// on_remove is an optional callback that can be called immediately
|
||||||
|
// after a node is removed from the tree.
|
||||||
|
on_remove: proc(value: Value, user_data: rawptr),
|
||||||
|
|
||||||
|
_root: ^Node(Value),
|
||||||
|
_node_allocator: runtime.Allocator,
|
||||||
|
_cmp_fn: proc(a, b: Value) -> Ordering,
|
||||||
|
_size: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node is an AVL tree node.
|
||||||
|
//
|
||||||
|
// WARNING: It is unsafe to mutate value if the node is part of a tree
|
||||||
|
// if doing so will alter the Node's sort position relative to other
|
||||||
|
// elements in the tree.
|
||||||
|
Node :: struct($Value: typeid) {
|
||||||
|
value: Value,
|
||||||
|
|
||||||
|
_parent: ^Node(Value),
|
||||||
|
_left: ^Node(Value),
|
||||||
|
_right: ^Node(Value),
|
||||||
|
_balance: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator is a tree iterator.
|
||||||
|
//
|
||||||
|
// WARNING: It is unsafe to modify the tree while iterating, except via
|
||||||
|
// the iterator_remove method.
|
||||||
|
Iterator :: struct($Value: typeid) {
|
||||||
|
_tree: ^Tree(Value),
|
||||||
|
_cur: ^Node(Value),
|
||||||
|
_next: ^Node(Value),
|
||||||
|
_direction: Direction,
|
||||||
|
_called_next: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a tree.
|
||||||
|
init :: proc {
|
||||||
|
init_ordered,
|
||||||
|
init_cmp,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init_cmp initializes a tree.
|
||||||
|
init_cmp :: proc(
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
cmp_fn: proc(a, b: Value) -> Ordering,
|
||||||
|
node_allocator := context.allocator,
|
||||||
|
) {
|
||||||
|
t._root = nil
|
||||||
|
t._node_allocator = node_allocator
|
||||||
|
t._cmp_fn = cmp_fn
|
||||||
|
t._size = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// init_ordered initializes a tree containing ordered items, with
|
||||||
|
// a comparison function that results in an ascending order sort.
|
||||||
|
init_ordered :: proc(
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
node_allocator := context.allocator,
|
||||||
|
) where intrinsics.type_is_ordered_numeric(Value) {
|
||||||
|
init_cmp(t, slice.cmp_proc(Value), node_allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroy de-initializes a tree.
|
||||||
|
destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
|
||||||
|
iter := iterator(t, Direction.Forward)
|
||||||
|
for _ in iterator_next(&iter) {
|
||||||
|
iterator_remove(&iter, call_on_remove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// len returns the number of elements in the tree.
|
||||||
|
len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
|
||||||
|
return t._size
|
||||||
|
}
|
||||||
|
|
||||||
|
// first returns the first node in the tree (in-order) or nil iff
|
||||||
|
// the tree is empty.
|
||||||
|
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||||
|
return tree_first_or_last_in_order(t, Direction.Backward)
|
||||||
|
}
|
||||||
|
|
||||||
|
// last returns the last element in the tree (in-order) or nil iff
|
||||||
|
// the tree is empty.
|
||||||
|
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||||
|
return tree_first_or_last_in_order(t, Direction.Forward)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find finds the value in the tree, and returns the corresponding
|
||||||
|
// node or nil iff the value is not present.
|
||||||
|
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
|
||||||
|
cur := t._root
|
||||||
|
descend_loop: for cur != nil {
|
||||||
|
switch t._cmp_fn(value, cur.value) {
|
||||||
|
case .Less:
|
||||||
|
cur = cur._left
|
||||||
|
case .Greater:
|
||||||
|
cur = cur._right
|
||||||
|
case .Equal:
|
||||||
|
break descend_loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur
|
||||||
|
}
|
||||||
|
|
||||||
|
// find_or_insert attempts to insert the value into the tree, and returns
|
||||||
|
// the node, a boolean indicating if the value was inserted, and the
|
||||||
|
// node allocator error if relevant. If the value is already
|
||||||
|
// present, the existing node is returned un-altered.
|
||||||
|
find_or_insert :: proc(
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
value: Value,
|
||||||
|
) -> (
|
||||||
|
n: ^Node(Value),
|
||||||
|
inserted: bool,
|
||||||
|
err: runtime.Allocator_Error,
|
||||||
|
) {
|
||||||
|
n_ptr := &t._root
|
||||||
|
for n_ptr^ != nil {
|
||||||
|
n = n_ptr^
|
||||||
|
switch t._cmp_fn(value, n.value) {
|
||||||
|
case .Less:
|
||||||
|
n_ptr = &n._left
|
||||||
|
case .Greater:
|
||||||
|
n_ptr = &n._right
|
||||||
|
case .Equal:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := n
|
||||||
|
n = new(Node(Value), t._node_allocator) or_return
|
||||||
|
n.value = value
|
||||||
|
n._parent = parent
|
||||||
|
n_ptr^ = n
|
||||||
|
tree_rebalance_after_insert(t, n)
|
||||||
|
|
||||||
|
t._size += 1
|
||||||
|
inserted = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove removes a node or value from the tree, and returns true iff the
|
||||||
|
// removal was successful. While the node's value will be left intact,
|
||||||
|
// the node itself will be freed via the tree's node allocator.
|
||||||
|
remove :: proc {
|
||||||
|
remove_value,
|
||||||
|
remove_node,
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove_value removes a value from the tree, and returns true iff the
|
||||||
|
// removal was successful. While the node's value will be left intact,
|
||||||
|
// the node itself will be freed via the tree's node allocator.
|
||||||
|
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
|
||||||
|
n := find(t, value)
|
||||||
|
if n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return remove_node(t, n, call_on_remove)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove_node removes a node from the tree, and returns true iff the
|
||||||
|
// removal was successful. While the node's value will be left intact,
|
||||||
|
// the node itself will be freed via the tree's node allocator.
|
||||||
|
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
|
||||||
|
if node._parent == node || (node._parent == nil && t._root != node) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
if call_on_remove && t.on_remove != nil {
|
||||||
|
t.on_remove(node.value, t.user_data)
|
||||||
|
}
|
||||||
|
free(node, t._node_allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
parent: ^Node(Value)
|
||||||
|
left_deleted: bool
|
||||||
|
|
||||||
|
t._size -= 1
|
||||||
|
if node._left != nil && node._right != nil {
|
||||||
|
parent, left_deleted = tree_swap_with_successor(t, node)
|
||||||
|
} else {
|
||||||
|
child := node._left
|
||||||
|
if child == nil {
|
||||||
|
child = node._right
|
||||||
|
}
|
||||||
|
parent = node._parent
|
||||||
|
if parent != nil {
|
||||||
|
if node == parent._left {
|
||||||
|
parent._left = child
|
||||||
|
left_deleted = true
|
||||||
|
} else {
|
||||||
|
parent._right = child
|
||||||
|
left_deleted = false
|
||||||
|
}
|
||||||
|
if child != nil {
|
||||||
|
child._parent = parent
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if child != nil {
|
||||||
|
child._parent = parent
|
||||||
|
}
|
||||||
|
t._root = child
|
||||||
|
node_reset(node)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if left_deleted {
|
||||||
|
parent = tree_handle_subtree_shrink(t, parent, +1, &left_deleted)
|
||||||
|
} else {
|
||||||
|
parent = tree_handle_subtree_shrink(t, parent, -1, &left_deleted)
|
||||||
|
}
|
||||||
|
if parent == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node_reset(node)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterator returns a tree iterator in the specified direction.
|
||||||
|
iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
|
||||||
|
it: Iterator(Value)
|
||||||
|
it._tree = transmute(^Tree(Value))t
|
||||||
|
it._direction = direction
|
||||||
|
|
||||||
|
iterator_first(&it)
|
||||||
|
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterator_from_pos returns a tree iterator in the specified direction,
|
||||||
|
// spanning the range [pos, last] (inclusive).
|
||||||
|
iterator_from_pos :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
pos: ^Node(Value),
|
||||||
|
direction: Direction,
|
||||||
|
) -> Iterator(Value) {
|
||||||
|
it: Iterator(Value)
|
||||||
|
it._tree = transmute(^Tree(Value))t
|
||||||
|
it._direction = direction
|
||||||
|
it._next = nil
|
||||||
|
it._called_next = false
|
||||||
|
|
||||||
|
if it._cur = pos; pos != nil {
|
||||||
|
it._next = node_next_or_prev_in_order(it._cur, it._direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterator_get returns the node currently pointed to by the iterator,
|
||||||
|
// or nil iff the node has been removed, the tree is empty, or the end
|
||||||
|
// of the tree has been reached.
|
||||||
|
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
|
||||||
|
return it._cur
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterator_remove removes the node currently pointed to by the iterator,
|
||||||
|
// and returns true iff the removal was successful. Semantics are the
|
||||||
|
// same as the Tree remove.
|
||||||
|
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
|
||||||
|
if it._cur == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := remove_node(it._tree, it._cur, call_on_remove)
|
||||||
|
if ok {
|
||||||
|
it._cur = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterator_next advances the iterator and returns the (node, true) or
|
||||||
|
// or (nil, false) iff the end of the tree has been reached.
|
||||||
|
//
|
||||||
|
// Note: The first call to iterator_next will return the first node instead
|
||||||
|
// of advancing the iterator.
|
||||||
|
iterator_next :: proc "contextless" (it: ^$I/Iterator($Value)) -> (^Node(Value), bool) {
|
||||||
|
// This check is needed so that the first element gets returned from
|
||||||
|
// a brand-new iterator, and so that the somewhat contrived case where
|
||||||
|
// iterator_remove is called before the first call to iterator_next
|
||||||
|
// returns the correct value.
|
||||||
|
if !it._called_next {
|
||||||
|
it._called_next = true
|
||||||
|
|
||||||
|
// There can be the contrived case where iterator_remove is
|
||||||
|
// called before ever calling iterator_next, which needs to be
|
||||||
|
// handled as an actual call to next.
|
||||||
|
//
|
||||||
|
// If this happens it._cur will be nil, so only return the
|
||||||
|
// first value, if it._cur is valid.
|
||||||
|
if it._cur != nil {
|
||||||
|
return it._cur, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if it._next == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
it._cur = it._next
|
||||||
|
it._next = node_next_or_prev_in_order(it._cur, it._direction)
|
||||||
|
|
||||||
|
return it._cur, true
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_first_or_last_in_order :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
direction: Direction,
|
||||||
|
) -> ^Node(Value) {
|
||||||
|
first, sign := t._root, i8(direction)
|
||||||
|
if first != nil {
|
||||||
|
for {
|
||||||
|
tmp := node_get_child(first, +sign)
|
||||||
|
if tmp == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
first = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_replace_child :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
parent, old_child, new_child: ^Node(Value),
|
||||||
|
) {
|
||||||
|
if parent != nil {
|
||||||
|
if old_child == parent._left {
|
||||||
|
parent._left = new_child
|
||||||
|
} else {
|
||||||
|
parent._right = new_child
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t._root = new_child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_rotate :: proc "contextless" (t: ^$T/Tree($Value), a: ^Node(Value), sign: i8) {
|
||||||
|
b := node_get_child(a, -sign)
|
||||||
|
e := node_get_child(b, +sign)
|
||||||
|
p := a._parent
|
||||||
|
|
||||||
|
node_set_child(a, -sign, e)
|
||||||
|
a._parent = b
|
||||||
|
|
||||||
|
node_set_child(b, +sign, a)
|
||||||
|
b._parent = p
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
e._parent = a
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_replace_child(t, p, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_double_rotate :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
b, a: ^Node(Value),
|
||||||
|
sign: i8,
|
||||||
|
) -> ^Node(Value) {
|
||||||
|
e := node_get_child(b, +sign)
|
||||||
|
f := node_get_child(e, -sign)
|
||||||
|
g := node_get_child(e, +sign)
|
||||||
|
p := a._parent
|
||||||
|
e_bal := e._balance
|
||||||
|
|
||||||
|
node_set_child(a, -sign, g)
|
||||||
|
a_bal := -e_bal
|
||||||
|
if sign * e_bal >= 0 {
|
||||||
|
a_bal = 0
|
||||||
|
}
|
||||||
|
node_set_parent_balance(a, e, a_bal)
|
||||||
|
|
||||||
|
node_set_child(b, +sign, f)
|
||||||
|
b_bal := -e_bal
|
||||||
|
if sign * e_bal <= 0 {
|
||||||
|
b_bal = 0
|
||||||
|
}
|
||||||
|
node_set_parent_balance(b, e, b_bal)
|
||||||
|
|
||||||
|
node_set_child(e, +sign, a)
|
||||||
|
node_set_child(e, -sign, b)
|
||||||
|
node_set_parent_balance(e, p, 0)
|
||||||
|
|
||||||
|
if g != nil {
|
||||||
|
g._parent = a
|
||||||
|
}
|
||||||
|
|
||||||
|
if f != nil {
|
||||||
|
f._parent = b
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_replace_child(t, p, a, e)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_handle_subtree_growth :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
node, parent: ^Node(Value),
|
||||||
|
sign: i8,
|
||||||
|
) -> bool {
|
||||||
|
old_balance_factor := parent._balance
|
||||||
|
if old_balance_factor == 0 {
|
||||||
|
node_adjust_balance_factor(parent, sign)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
new_balance_factor := old_balance_factor + sign
|
||||||
|
if new_balance_factor == 0 {
|
||||||
|
node_adjust_balance_factor(parent, sign)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if sign * node._balance > 0 {
|
||||||
|
tree_rotate(t, parent, -sign)
|
||||||
|
node_adjust_balance_factor(parent, -sign)
|
||||||
|
node_adjust_balance_factor(node, -sign)
|
||||||
|
} else {
|
||||||
|
tree_double_rotate(t, node, parent, -sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_rebalance_after_insert :: proc "contextless" (t: ^$T/Tree($Value), inserted: ^Node(Value)) {
|
||||||
|
node, parent := inserted, inserted._parent
|
||||||
|
switch {
|
||||||
|
case parent == nil:
|
||||||
|
return
|
||||||
|
case node == parent._left:
|
||||||
|
node_adjust_balance_factor(parent, -1)
|
||||||
|
case:
|
||||||
|
node_adjust_balance_factor(parent, +1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent._balance == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for done := false; !done; {
|
||||||
|
node = parent
|
||||||
|
if parent = node._parent; parent == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if node == parent._left {
|
||||||
|
done = tree_handle_subtree_growth(t, node, parent, -1)
|
||||||
|
} else {
|
||||||
|
done = tree_handle_subtree_growth(t, node, parent, +1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_swap_with_successor :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
x: ^Node(Value),
|
||||||
|
) -> (
|
||||||
|
^Node(Value),
|
||||||
|
bool,
|
||||||
|
) {
|
||||||
|
ret: ^Node(Value)
|
||||||
|
left_deleted: bool
|
||||||
|
|
||||||
|
y := x._right
|
||||||
|
if y._left == nil {
|
||||||
|
ret = y
|
||||||
|
} else {
|
||||||
|
q: ^Node(Value)
|
||||||
|
|
||||||
|
for {
|
||||||
|
q = y
|
||||||
|
if y = y._left; y._left == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if q._left = y._right; q._left != nil {
|
||||||
|
q._left._parent = q
|
||||||
|
}
|
||||||
|
y._right = x._right
|
||||||
|
x._right._parent = y
|
||||||
|
ret = q
|
||||||
|
left_deleted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
y._left = x._left
|
||||||
|
x._left._parent = y
|
||||||
|
|
||||||
|
y._parent = x._parent
|
||||||
|
y._balance = x._balance
|
||||||
|
|
||||||
|
tree_replace_child(t, x._parent, x, y)
|
||||||
|
|
||||||
|
return ret, left_deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
tree_handle_subtree_shrink :: proc "contextless" (
|
||||||
|
t: ^$T/Tree($Value),
|
||||||
|
parent: ^Node(Value),
|
||||||
|
sign: i8,
|
||||||
|
left_deleted: ^bool,
|
||||||
|
) -> ^Node(Value) {
|
||||||
|
old_balance_factor := parent._balance
|
||||||
|
if old_balance_factor == 0 {
|
||||||
|
node_adjust_balance_factor(parent, sign)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
node: ^Node(Value)
|
||||||
|
new_balance_factor := old_balance_factor + sign
|
||||||
|
if new_balance_factor == 0 {
|
||||||
|
node_adjust_balance_factor(parent, sign)
|
||||||
|
node = parent
|
||||||
|
} else {
|
||||||
|
node = node_get_child(parent, sign)
|
||||||
|
if sign * node._balance >= 0 {
|
||||||
|
tree_rotate(t, parent, -sign)
|
||||||
|
if node._balance == 0 {
|
||||||
|
node_adjust_balance_factor(node, -sign)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node_adjust_balance_factor(parent, -sign)
|
||||||
|
node_adjust_balance_factor(node, -sign)
|
||||||
|
} else {
|
||||||
|
node = tree_double_rotate(t, node, parent, -sign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := parent
|
||||||
|
if parent = node._parent; parent != nil {
|
||||||
|
left_deleted^ = node == parent._left
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
node_reset :: proc "contextless" (n: ^Node($Value)) {
|
||||||
|
// Mostly pointless as n will be deleted after this is called, but
|
||||||
|
// attempt to be able to catch cases of n not being in the tree.
|
||||||
|
n._parent = n
|
||||||
|
n._left = nil
|
||||||
|
n._right = nil
|
||||||
|
n._balance = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
node_set_parent_balance :: #force_inline proc "contextless" (
|
||||||
|
n, parent: ^Node($Value),
|
||||||
|
balance: i8,
|
||||||
|
) {
|
||||||
|
n._parent = parent
|
||||||
|
n._balance = balance
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
node_get_child :: #force_inline proc "contextless" (n: ^Node($Value), sign: i8) -> ^Node(Value) {
|
||||||
|
if sign < 0 {
|
||||||
|
return n._left
|
||||||
|
}
|
||||||
|
return n._right
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
node_next_or_prev_in_order :: proc "contextless" (
|
||||||
|
n: ^Node($Value),
|
||||||
|
direction: Direction,
|
||||||
|
) -> ^Node(Value) {
|
||||||
|
next, tmp: ^Node(Value)
|
||||||
|
sign := i8(direction)
|
||||||
|
|
||||||
|
if next = node_get_child(n, +sign); next != nil {
|
||||||
|
for {
|
||||||
|
tmp = node_get_child(next, -sign)
|
||||||
|
if tmp == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next = tmp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tmp, next = n, n._parent
|
||||||
|
for next != nil && tmp == node_get_child(next, +sign) {
|
||||||
|
tmp, next = next, next._parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
node_set_child :: #force_inline proc "contextless" (
|
||||||
|
n: ^Node($Value),
|
||||||
|
sign: i8,
|
||||||
|
child: ^Node(Value),
|
||||||
|
) {
|
||||||
|
if sign < 0 {
|
||||||
|
n._left = child
|
||||||
|
} else {
|
||||||
|
n._right = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
node_adjust_balance_factor :: #force_inline proc "contextless" (n: ^Node($Value), amount: i8) {
|
||||||
|
n._balance += amount
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
iterator_first :: proc "contextless" (it: ^Iterator($Value)) {
|
||||||
|
// This is private because behavior when the user manually calls
|
||||||
|
// iterator_first followed by iterator_next is unintuitive, since
|
||||||
|
// the first call to iterator_next MUST return the first node
|
||||||
|
// instead of advancing so that `for node in iterator_next(&next)`
|
||||||
|
// works as expected.
|
||||||
|
|
||||||
|
switch it._direction {
|
||||||
|
case .Forward:
|
||||||
|
it._cur = tree_first_or_last_in_order(it._tree, .Backward)
|
||||||
|
case .Backward:
|
||||||
|
it._cur = tree_first_or_last_in_order(it._tree, .Forward)
|
||||||
|
}
|
||||||
|
|
||||||
|
it._next = nil
|
||||||
|
it._called_next = false
|
||||||
|
|
||||||
|
if it._cur != nil {
|
||||||
|
it._next = node_next_or_prev_in_order(it._cur, it._direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package dynamic_bit_array
|
package dynamic_bit_array
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package container_intrusive_list
|
package container_intrusive_list
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// An intrusive doubly-linked list
|
// An intrusive doubly-linked list
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package container_lru
|
package container_lru
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package container_priority_queue
|
package container_priority_queue
|
||||||
|
|
||||||
import "core:builtin"
|
import "base:builtin"
|
||||||
|
|
||||||
Priority_Queue :: struct($T: typeid) {
|
Priority_Queue :: struct($T: typeid) {
|
||||||
queue: [dynamic]T,
|
queue: [dynamic]T,
|
||||||
@@ -140,3 +140,18 @@ remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek_safe :: proc(pq: $Q/Priority_Queue($T), loc := #caller_location) -> (res: T, ok: bool) {
|
||||||
|
if builtin.len(pq.queue) > 0 {
|
||||||
|
return pq.queue[0], true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peek :: proc(pq: $Q/Priority_Queue($T), loc := #caller_location) -> (res: T) {
|
||||||
|
assert(condition=builtin.len(pq.queue)>0, loc=loc)
|
||||||
|
|
||||||
|
if builtin.len(pq.queue) > 0 {
|
||||||
|
return pq.queue[0]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package container_queue
|
package container_queue
|
||||||
|
|
||||||
import "core:builtin"
|
import "base:builtin"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
// Dynamically resizable double-ended queue/ring-buffer
|
// Dynamically resizable double-ended queue/ring-buffer
|
||||||
@@ -22,7 +22,9 @@ init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := contex
|
|||||||
return reserve(q, capacity)
|
return reserve(q, capacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procedure to initialize a queue from a fixed backing slice
|
// Procedure to initialize a queue from a fixed backing slice.
|
||||||
|
// The contents of the `backing` will be overwritten as items are pushed onto the `Queue`.
|
||||||
|
// Any previous contents are not available.
|
||||||
init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||||
clear(q)
|
clear(q)
|
||||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||||
@@ -34,6 +36,21 @@ init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Procedure to initialize a queue from a fixed backing slice.
|
||||||
|
// Existing contents are preserved and available on the queue.
|
||||||
|
init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||||
|
clear(q)
|
||||||
|
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||||
|
data = raw_data(backing),
|
||||||
|
len = builtin.len(backing),
|
||||||
|
cap = builtin.len(backing),
|
||||||
|
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
|
||||||
|
}
|
||||||
|
q.len = len(backing)
|
||||||
|
q.offset = len(backing)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Procedure to destroy a queue
|
// Procedure to destroy a queue
|
||||||
destroy :: proc(q: ^$Q/Queue($T)) {
|
destroy :: proc(q: ^$Q/Queue($T)) {
|
||||||
delete(q.data)
|
delete(q.data)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package container_small_array
|
package container_small_array
|
||||||
|
|
||||||
import "core:builtin"
|
import "base:builtin"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// map type is being used to accelerate lookups.
|
// map type is being used to accelerate lookups.
|
||||||
package container_topological_sort
|
package container_topological_sort
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
@@ -80,11 +80,13 @@ sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for root in sorted do for k, _ in relations[root].dependents {
|
for root in sorted {
|
||||||
relation := &relations[k]
|
for k, _ in relations[root].dependents {
|
||||||
relation.dependencies -= 1
|
relation := &relations[k]
|
||||||
if relation.dependencies == 0 {
|
relation.dependencies -= 1
|
||||||
append(&sorted, k)
|
if relation.dependencies == 0 {
|
||||||
|
append(&sorted, k)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-86
@@ -1,95 +1,24 @@
|
|||||||
# crypto
|
# crypto
|
||||||
A crypto library for the Odin language
|
|
||||||
|
A cryptography library for the Odin language.
|
||||||
|
|
||||||
## Supported
|
## Supported
|
||||||
This library offers various algorithms implemented in Odin.
|
|
||||||
Please see the chart below for the options.
|
|
||||||
|
|
||||||
## Hashing algorithms
|
This package offers various algorithms implemented in Odin, along with
|
||||||
| Algorithm | |
|
useful helpers such as access to the system entropy source, and a
|
||||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
|
constant-time byte comparison.
|
||||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ |
|
|
||||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
|
||||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
|
||||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ |
|
|
||||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ |
|
|
||||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ |
|
|
||||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ |
|
|
||||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
|
||||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ |
|
|
||||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ |
|
|
||||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
|
|
||||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ |
|
|
||||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ |
|
|
||||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ |
|
|
||||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
|
||||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
|
||||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ |
|
|
||||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ |
|
|
||||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
|
||||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
|
||||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ |
|
|
||||||
|
|
||||||
#### High level API
|
## Implementation considerations
|
||||||
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*.
|
|
||||||
Included in these groups are six procedures.
|
|
||||||
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
|
|
||||||
* `hash_bytes` - Hash a given byte slice and return the computed hash
|
|
||||||
* `hash_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
|
|
||||||
* `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the hash
|
|
||||||
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
|
|
||||||
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
|
|
||||||
|
|
||||||
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
|
- The crypto packages are not thread-safe.
|
||||||
For instance, `HAVAL` offers different sizes as well as three different round amounts.
|
- Best-effort is make to mitigate timing side-channels on reasonable
|
||||||
Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`.
|
architectures. Architectures that are known to be unreasonable include
|
||||||
|
but are not limited to i386, i486, and WebAssembly.
|
||||||
|
- The packages attempt to santize sensitive data, however this is, and
|
||||||
|
will remain a "best-effort" implementation decision. As Thomas Pornin
|
||||||
|
puts it "In general, such memory cleansing is a fool's quest."
|
||||||
|
- All of these packages have not received independent third party review.
|
||||||
|
|
||||||
#### Low level API
|
## License
|
||||||
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
|
|
||||||
You may also directly call them, if you wish.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
```odin
|
|
||||||
package crypto_example
|
|
||||||
|
|
||||||
// Import the desired package
|
|
||||||
import "core:crypto/md4"
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
input := "foo"
|
|
||||||
|
|
||||||
// Compute the hash, using the high level API
|
|
||||||
computed_hash := md4.hash(input)
|
|
||||||
|
|
||||||
// Variant that takes a destination buffer, instead of returning the computed hash
|
|
||||||
hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
|
|
||||||
md4.hash(input, hash[:])
|
|
||||||
|
|
||||||
// Compute the hash, using the low level API
|
|
||||||
ctx: md4.Md4_Context
|
|
||||||
computed_hash_low: [16]byte
|
|
||||||
md4.init(&ctx)
|
|
||||||
md4.update(&ctx, transmute([]byte)input)
|
|
||||||
md4.final(&ctx, computed_hash_low[:])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
|
|
||||||
|
|
||||||
#### Thread safety
|
|
||||||
The crypto package is not thread-safe at the moment. This may change in the future.
|
|
||||||
|
|
||||||
### Disclaimer
|
|
||||||
The algorithms were ported out of curiosity and due to interest in the field.
|
|
||||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
|
||||||
Wherever we were able to find official test vectors, those were used to verify the implementation.
|
|
||||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
|
||||||
|
|
||||||
### ToDo
|
|
||||||
* Ciphers (Symmetric, Asymmetric)
|
|
||||||
* MACs (Message Authentication Code)
|
|
||||||
* CSPRNGs (Cryptographically Secure PseudoRandom Number Generator)
|
|
||||||
* KDFs (Key Derivation Function)
|
|
||||||
* KEAs (Key Exchange Algorithm)
|
|
||||||
|
|
||||||
### License
|
|
||||||
This library is made available under the BSD-3 license.
|
This library is made available under the BSD-3 license.
|
||||||
+182
-91
@@ -10,12 +10,13 @@ package _blake2
|
|||||||
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
|
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "../util"
|
import "core:encoding/endian"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
BLAKE2S_BLOCK_SIZE :: 64
|
BLAKE2S_BLOCK_SIZE :: 64
|
||||||
BLAKE2S_SIZE :: 32
|
BLAKE2S_SIZE :: 32
|
||||||
BLAKE2B_BLOCK_SIZE :: 128
|
BLAKE2B_BLOCK_SIZE :: 128
|
||||||
BLAKE2B_SIZE :: 64
|
BLAKE2B_SIZE :: 64
|
||||||
|
|
||||||
Blake2s_Context :: struct {
|
Blake2s_Context :: struct {
|
||||||
h: [8]u32,
|
h: [8]u32,
|
||||||
@@ -28,7 +29,8 @@ Blake2s_Context :: struct {
|
|||||||
is_keyed: bool,
|
is_keyed: bool,
|
||||||
size: byte,
|
size: byte,
|
||||||
is_last_node: bool,
|
is_last_node: bool,
|
||||||
cfg: Blake2_Config,
|
|
||||||
|
is_initialized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
Blake2b_Context :: struct {
|
Blake2b_Context :: struct {
|
||||||
@@ -42,15 +44,18 @@ Blake2b_Context :: struct {
|
|||||||
is_keyed: bool,
|
is_keyed: bool,
|
||||||
size: byte,
|
size: byte,
|
||||||
is_last_node: bool,
|
is_last_node: bool,
|
||||||
cfg: Blake2_Config,
|
|
||||||
|
is_initialized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
Blake2_Config :: struct {
|
Blake2_Config :: struct {
|
||||||
size: byte,
|
size: byte,
|
||||||
key: []byte,
|
key: []byte,
|
||||||
salt: []byte,
|
salt: []byte,
|
||||||
person: []byte,
|
person: []byte,
|
||||||
tree: union{Blake2_Tree},
|
tree: union {
|
||||||
|
Blake2_Tree,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Blake2_Tree :: struct {
|
Blake2_Tree :: struct {
|
||||||
@@ -63,11 +68,13 @@ Blake2_Tree :: struct {
|
|||||||
is_last_node: bool,
|
is_last_node: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
BLAKE2S_IV := [8]u32 {
|
BLAKE2S_IV := [8]u32 {
|
||||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
BLAKE2B_IV := [8]u64 {
|
BLAKE2B_IV := [8]u64 {
|
||||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
||||||
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||||
@@ -75,80 +82,94 @@ BLAKE2B_IV := [8]u64 {
|
|||||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc(ctx: ^$T) {
|
init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
block_size :: BLAKE2S_BLOCK_SIZE
|
max_size :: BLAKE2S_SIZE
|
||||||
} else when T == Blake2b_Context {
|
} else when T == Blake2b_Context {
|
||||||
block_size :: BLAKE2B_BLOCK_SIZE
|
max_size :: BLAKE2B_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
p := make([]byte, block_size)
|
if cfg.size > max_size {
|
||||||
defer delete(p)
|
panic("blake2: requested output size exceeeds algorithm max")
|
||||||
|
}
|
||||||
|
|
||||||
p[0] = ctx.cfg.size
|
// To save having to allocate a scratch buffer, use the internal
|
||||||
p[1] = byte(len(ctx.cfg.key))
|
// data buffer (`ctx.x`), as it is exactly the correct size.
|
||||||
|
p := ctx.x[:]
|
||||||
|
|
||||||
if ctx.cfg.salt != nil {
|
p[0] = cfg.size
|
||||||
|
p[1] = byte(len(cfg.key))
|
||||||
|
|
||||||
|
if cfg.salt != nil {
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
copy(p[16:], ctx.cfg.salt)
|
copy(p[16:], cfg.salt)
|
||||||
} else when T == Blake2b_Context {
|
} else when T == Blake2b_Context {
|
||||||
copy(p[32:], ctx.cfg.salt)
|
copy(p[32:], cfg.salt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.cfg.person != nil {
|
if cfg.person != nil {
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
copy(p[24:], ctx.cfg.person)
|
copy(p[24:], cfg.person)
|
||||||
} else when T == Blake2b_Context {
|
} else when T == Blake2b_Context {
|
||||||
copy(p[48:], ctx.cfg.person)
|
copy(p[48:], cfg.person)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.cfg.tree != nil {
|
if cfg.tree != nil {
|
||||||
p[2] = ctx.cfg.tree.(Blake2_Tree).fanout
|
p[2] = cfg.tree.(Blake2_Tree).fanout
|
||||||
p[3] = ctx.cfg.tree.(Blake2_Tree).max_depth
|
p[3] = cfg.tree.(Blake2_Tree).max_depth
|
||||||
util.PUT_U32_LE(p[4:], ctx.cfg.tree.(Blake2_Tree).leaf_size)
|
endian.unchecked_put_u32le(p[4:], cfg.tree.(Blake2_Tree).leaf_size)
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
p[8] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset)
|
p[8] = byte(cfg.tree.(Blake2_Tree).node_offset)
|
||||||
p[9] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 8)
|
p[9] = byte(cfg.tree.(Blake2_Tree).node_offset >> 8)
|
||||||
p[10] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 16)
|
p[10] = byte(cfg.tree.(Blake2_Tree).node_offset >> 16)
|
||||||
p[11] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 24)
|
p[11] = byte(cfg.tree.(Blake2_Tree).node_offset >> 24)
|
||||||
p[12] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 32)
|
p[12] = byte(cfg.tree.(Blake2_Tree).node_offset >> 32)
|
||||||
p[13] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 40)
|
p[13] = byte(cfg.tree.(Blake2_Tree).node_offset >> 40)
|
||||||
p[14] = ctx.cfg.tree.(Blake2_Tree).node_depth
|
p[14] = cfg.tree.(Blake2_Tree).node_depth
|
||||||
p[15] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size
|
p[15] = cfg.tree.(Blake2_Tree).inner_hash_size
|
||||||
} else when T == Blake2b_Context {
|
} else when T == Blake2b_Context {
|
||||||
util.PUT_U64_LE(p[8:], ctx.cfg.tree.(Blake2_Tree).node_offset)
|
endian.unchecked_put_u64le(p[8:], cfg.tree.(Blake2_Tree).node_offset)
|
||||||
p[16] = ctx.cfg.tree.(Blake2_Tree).node_depth
|
p[16] = cfg.tree.(Blake2_Tree).node_depth
|
||||||
p[17] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size
|
p[17] = cfg.tree.(Blake2_Tree).inner_hash_size
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p[2], p[3] = 1, 1
|
p[2], p[3] = 1, 1
|
||||||
}
|
}
|
||||||
ctx.size = ctx.cfg.size
|
ctx.size = cfg.size
|
||||||
for i := 0; i < 8; i += 1 {
|
for i := 0; i < 8; i += 1 {
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
ctx.h[i] = BLAKE2S_IV[i] ~ util.U32_LE(p[i * 4:])
|
ctx.h[i] = BLAKE2S_IV[i] ~ endian.unchecked_get_u32le(p[i * 4:])
|
||||||
}
|
}
|
||||||
when T == Blake2b_Context {
|
when T == Blake2b_Context {
|
||||||
ctx.h[i] = BLAKE2B_IV[i] ~ util.U64_LE(p[i * 8:])
|
ctx.h[i] = BLAKE2B_IV[i] ~ endian.unchecked_get_u64le(p[i * 8:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ctx.cfg.tree != nil && ctx.cfg.tree.(Blake2_Tree).is_last_node {
|
|
||||||
|
mem.zero(&ctx.x, size_of(ctx.x)) // Done with the scratch space, no barrier.
|
||||||
|
|
||||||
|
if cfg.tree != nil && cfg.tree.(Blake2_Tree).is_last_node {
|
||||||
ctx.is_last_node = true
|
ctx.is_last_node = true
|
||||||
}
|
}
|
||||||
if len(ctx.cfg.key) > 0 {
|
if len(cfg.key) > 0 {
|
||||||
copy(ctx.padded_key[:], ctx.cfg.key)
|
copy(ctx.padded_key[:], cfg.key)
|
||||||
update(ctx, ctx.padded_key[:])
|
update(ctx, ctx.padded_key[:])
|
||||||
ctx.is_keyed = true
|
ctx.is_keyed = true
|
||||||
}
|
}
|
||||||
copy(ctx.ih[:], ctx.h[:])
|
copy(ctx.ih[:], ctx.h[:])
|
||||||
copy(ctx.h[:], ctx.ih[:])
|
copy(ctx.h[:], ctx.ih[:])
|
||||||
if ctx.is_keyed {
|
if ctx.is_keyed {
|
||||||
update(ctx, ctx.padded_key[:])
|
update(ctx, ctx.padded_key[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.nx = 0
|
||||||
|
|
||||||
|
ctx.is_initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
update :: proc(ctx: ^$T, p: []byte) {
|
||||||
|
assert(ctx.is_initialized)
|
||||||
|
|
||||||
p := p
|
p := p
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
block_size :: BLAKE2S_BLOCK_SIZE
|
block_size :: BLAKE2S_BLOCK_SIZE
|
||||||
@@ -174,15 +195,43 @@ update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
|||||||
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
||||||
}
|
}
|
||||||
|
|
||||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
||||||
when T == Blake2s_Context {
|
assert(ctx.is_initialized)
|
||||||
blake2s_final(ctx, hash)
|
|
||||||
|
ctx := ctx
|
||||||
|
if finalize_clone {
|
||||||
|
tmp_ctx: T
|
||||||
|
clone(&tmp_ctx, ctx)
|
||||||
|
ctx = &tmp_ctx
|
||||||
}
|
}
|
||||||
when T == Blake2b_Context {
|
defer(reset(ctx))
|
||||||
|
|
||||||
|
when T == Blake2s_Context {
|
||||||
|
if len(hash) < int(ctx.size) {
|
||||||
|
panic("crypto/blake2s: invalid destination digest size")
|
||||||
|
}
|
||||||
|
blake2s_final(ctx, hash)
|
||||||
|
} else when T == Blake2b_Context {
|
||||||
|
if len(hash) < int(ctx.size) {
|
||||||
|
panic("crypto/blake2b: invalid destination digest size")
|
||||||
|
}
|
||||||
blake2b_final(ctx, hash)
|
blake2b_final(ctx, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone :: proc(ctx, other: ^$T) {
|
||||||
|
ctx^ = other^
|
||||||
|
}
|
||||||
|
|
||||||
|
reset :: proc(ctx: ^$T) {
|
||||||
|
if !ctx.is_initialized {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.zero_explicit(ctx, size_of(ctx^))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
||||||
if ctx.is_keyed {
|
if ctx.is_keyed {
|
||||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||||
@@ -203,16 +252,14 @@ blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
|||||||
|
|
||||||
blocks(ctx, ctx.x[:])
|
blocks(ctx, ctx.x[:])
|
||||||
|
|
||||||
j := 0
|
dst: [BLAKE2S_SIZE]byte
|
||||||
for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] {
|
for i := 0; i < BLAKE2S_SIZE / 4; i += 1 {
|
||||||
hash[j + 0] = byte(s >> 0)
|
endian.unchecked_put_u32le(dst[i * 4:], ctx.h[i])
|
||||||
hash[j + 1] = byte(s >> 8)
|
|
||||||
hash[j + 2] = byte(s >> 16)
|
|
||||||
hash[j + 3] = byte(s >> 24)
|
|
||||||
j += 4
|
|
||||||
}
|
}
|
||||||
|
copy(hash, dst[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
||||||
if ctx.is_keyed {
|
if ctx.is_keyed {
|
||||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||||
@@ -229,56 +276,52 @@ blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
|||||||
ctx.f[0] = 0xffffffffffffffff
|
ctx.f[0] = 0xffffffffffffffff
|
||||||
if ctx.is_last_node {
|
if ctx.is_last_node {
|
||||||
ctx.f[1] = 0xffffffffffffffff
|
ctx.f[1] = 0xffffffffffffffff
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks(ctx, ctx.x[:])
|
blocks(ctx, ctx.x[:])
|
||||||
|
|
||||||
j := 0
|
dst: [BLAKE2B_SIZE]byte
|
||||||
for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] {
|
for i := 0; i < BLAKE2B_SIZE / 8; i += 1 {
|
||||||
hash[j + 0] = byte(s >> 0)
|
endian.unchecked_put_u64le(dst[i * 8:], ctx.h[i])
|
||||||
hash[j + 1] = byte(s >> 8)
|
|
||||||
hash[j + 2] = byte(s >> 16)
|
|
||||||
hash[j + 3] = byte(s >> 24)
|
|
||||||
hash[j + 4] = byte(s >> 32)
|
|
||||||
hash[j + 5] = byte(s >> 40)
|
|
||||||
hash[j + 6] = byte(s >> 48)
|
|
||||||
hash[j + 7] = byte(s >> 56)
|
|
||||||
j += 8
|
|
||||||
}
|
}
|
||||||
|
copy(hash, dst[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
|
blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
blake2s_blocks(ctx, p)
|
blake2s_blocks(ctx, p)
|
||||||
}
|
} else when T == Blake2b_Context {
|
||||||
when T == Blake2b_Context {
|
|
||||||
blake2b_blocks(ctx, p)
|
blake2b_blocks(ctx, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []byte) {
|
blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []byte) {
|
||||||
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
h0, h1, h2, h3, h4, h5, h6, h7 :=
|
||||||
|
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||||
p := p
|
p := p
|
||||||
for len(p) >= BLAKE2S_BLOCK_SIZE {
|
for len(p) >= BLAKE2S_BLOCK_SIZE {
|
||||||
ctx.t[0] += BLAKE2S_BLOCK_SIZE
|
ctx.t[0] += BLAKE2S_BLOCK_SIZE
|
||||||
if ctx.t[0] < BLAKE2S_BLOCK_SIZE {
|
if ctx.t[0] < BLAKE2S_BLOCK_SIZE {
|
||||||
ctx.t[1] += 1
|
ctx.t[1] += 1
|
||||||
}
|
}
|
||||||
v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7
|
v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7
|
||||||
v8 := BLAKE2S_IV[0]
|
v8 := BLAKE2S_IV[0]
|
||||||
v9 := BLAKE2S_IV[1]
|
v9 := BLAKE2S_IV[1]
|
||||||
v10 := BLAKE2S_IV[2]
|
v10 := BLAKE2S_IV[2]
|
||||||
v11 := BLAKE2S_IV[3]
|
v11 := BLAKE2S_IV[3]
|
||||||
v12 := BLAKE2S_IV[4] ~ ctx.t[0]
|
v12 := BLAKE2S_IV[4] ~ ctx.t[0]
|
||||||
v13 := BLAKE2S_IV[5] ~ ctx.t[1]
|
v13 := BLAKE2S_IV[5] ~ ctx.t[1]
|
||||||
v14 := BLAKE2S_IV[6] ~ ctx.f[0]
|
v14 := BLAKE2S_IV[6] ~ ctx.f[0]
|
||||||
v15 := BLAKE2S_IV[7] ~ ctx.f[1]
|
v15 := BLAKE2S_IV[7] ~ ctx.f[1]
|
||||||
m: [16]u32
|
|
||||||
j := 0
|
m: [16]u32 = ---
|
||||||
for i := 0; i < 16; i += 1 {
|
for i := 0; i < 16; i += 1 {
|
||||||
m[i] = u32(p[j]) | u32(p[j + 1]) << 8 | u32(p[j + 2]) << 16 | u32(p[j + 3]) << 24
|
m[i] = endian.unchecked_get_u32le(p[i * 4:])
|
||||||
j += 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Round 1
|
||||||
v0 += m[0]
|
v0 += m[0]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -391,6 +434,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 2
|
||||||
v0 += m[14]
|
v0 += m[14]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -503,6 +548,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 3
|
||||||
v0 += m[11]
|
v0 += m[11]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -615,6 +662,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 4
|
||||||
v0 += m[7]
|
v0 += m[7]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -727,6 +776,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 5
|
||||||
v0 += m[9]
|
v0 += m[9]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -839,6 +890,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 6
|
||||||
v0 += m[2]
|
v0 += m[2]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -951,6 +1004,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 7
|
||||||
v0 += m[12]
|
v0 += m[12]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1063,6 +1118,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 8
|
||||||
v0 += m[13]
|
v0 += m[13]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1175,6 +1232,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 9
|
||||||
v0 += m[6]
|
v0 += m[6]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1287,6 +1346,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
|
// Round 10
|
||||||
v0 += m[10]
|
v0 += m[10]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1399,6 +1460,7 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (32 - 7) | v5 >> 7
|
v5 = v5 << (32 - 7) | v5 >> 7
|
||||||
|
|
||||||
h0 ~= v0 ~ v8
|
h0 ~= v0 ~ v8
|
||||||
h1 ~= v1 ~ v9
|
h1 ~= v1 ~ v9
|
||||||
h2 ~= v2 ~ v10
|
h2 ~= v2 ~ v10
|
||||||
@@ -1407,19 +1469,23 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
|||||||
h5 ~= v5 ~ v13
|
h5 ~= v5 ~ v13
|
||||||
h6 ~= v6 ~ v14
|
h6 ~= v6 ~ v14
|
||||||
h7 ~= v7 ~ v15
|
h7 ~= v7 ~ v15
|
||||||
|
|
||||||
p = p[BLAKE2S_BLOCK_SIZE:]
|
p = p[BLAKE2S_BLOCK_SIZE:]
|
||||||
}
|
}
|
||||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] =
|
||||||
|
h0, h1, h2, h3, h4, h5, h6, h7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []byte) {
|
blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []byte) {
|
||||||
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
h0, h1, h2, h3, h4, h5, h6, h7 :=
|
||||||
|
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||||
p := p
|
p := p
|
||||||
for len(p) >= BLAKE2B_BLOCK_SIZE {
|
for len(p) >= BLAKE2B_BLOCK_SIZE {
|
||||||
ctx.t[0] += BLAKE2B_BLOCK_SIZE
|
ctx.t[0] += BLAKE2B_BLOCK_SIZE
|
||||||
if ctx.t[0] < BLAKE2B_BLOCK_SIZE {
|
if ctx.t[0] < BLAKE2B_BLOCK_SIZE {
|
||||||
ctx.t[1]+=1
|
ctx.t[1] += 1
|
||||||
}
|
}
|
||||||
v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7
|
v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7
|
||||||
v8 := BLAKE2B_IV[0]
|
v8 := BLAKE2B_IV[0]
|
||||||
v9 := BLAKE2B_IV[1]
|
v9 := BLAKE2B_IV[1]
|
||||||
@@ -1429,13 +1495,13 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v13 := BLAKE2B_IV[5] ~ ctx.t[1]
|
v13 := BLAKE2B_IV[5] ~ ctx.t[1]
|
||||||
v14 := BLAKE2B_IV[6] ~ ctx.f[0]
|
v14 := BLAKE2B_IV[6] ~ ctx.f[0]
|
||||||
v15 := BLAKE2B_IV[7] ~ ctx.f[1]
|
v15 := BLAKE2B_IV[7] ~ ctx.f[1]
|
||||||
|
|
||||||
m: [16]u64 = ---
|
m: [16]u64 = ---
|
||||||
j := 0
|
for i := 0; i < 16; i += 1 {
|
||||||
for i := 0; i < 16; i+=1 {
|
m[i] = endian.unchecked_get_u64le(p[i * 8:])
|
||||||
m[i] = u64(p[j]) | u64(p[j + 1]) << 8 | u64(p[j + 2]) << 16 | u64(p[j + 3]) << 24 |
|
|
||||||
u64(p[j + 4]) << 32 | u64(p[j + 5]) << 40 | u64(p[j + 6]) << 48 | u64(p[j + 7]) << 56
|
|
||||||
j += 8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Round 1
|
||||||
v0 += m[0]
|
v0 += m[0]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1548,6 +1614,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 2
|
||||||
v0 += m[14]
|
v0 += m[14]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1660,6 +1728,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 3
|
||||||
v0 += m[11]
|
v0 += m[11]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1772,6 +1842,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 4
|
||||||
v0 += m[7]
|
v0 += m[7]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1884,6 +1956,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 5
|
||||||
v0 += m[9]
|
v0 += m[9]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -1996,6 +2070,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 6
|
||||||
v0 += m[2]
|
v0 += m[2]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2108,6 +2184,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 7
|
||||||
v0 += m[12]
|
v0 += m[12]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2220,6 +2298,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 8
|
||||||
v0 += m[13]
|
v0 += m[13]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2332,6 +2412,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 9
|
||||||
v0 += m[6]
|
v0 += m[6]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2444,6 +2526,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 10
|
||||||
v0 += m[10]
|
v0 += m[10]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2556,6 +2640,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 11
|
||||||
v0 += m[0]
|
v0 += m[0]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2668,6 +2754,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
|
// Round 12
|
||||||
v0 += m[14]
|
v0 += m[14]
|
||||||
v0 += v4
|
v0 += v4
|
||||||
v12 ~= v0
|
v12 ~= v0
|
||||||
@@ -2780,6 +2868,7 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
v10 += v15
|
v10 += v15
|
||||||
v5 ~= v10
|
v5 ~= v10
|
||||||
v5 = v5 << (64 - 63) | v5 >> 63
|
v5 = v5 << (64 - 63) | v5 >> 63
|
||||||
|
|
||||||
h0 ~= v0 ~ v8
|
h0 ~= v0 ~ v8
|
||||||
h1 ~= v1 ~ v9
|
h1 ~= v1 ~ v9
|
||||||
h2 ~= v2 ~ v10
|
h2 ~= v2 ~ v10
|
||||||
@@ -2788,7 +2877,9 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
|||||||
h5 ~= v5 ~ v13
|
h5 ~= v5 ~ v13
|
||||||
h6 ~= v6 ~ v14
|
h6 ~= v6 ~ v14
|
||||||
h7 ~= v7 ~ v15
|
h7 ~= v7 ~ v15
|
||||||
|
|
||||||
p = p[BLAKE2B_BLOCK_SIZE:]
|
p = p[BLAKE2B_BLOCK_SIZE:]
|
||||||
}
|
}
|
||||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] =
|
||||||
}
|
h0, h1, h2, h3, h4, h5, h6, h7
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package field_poly1305
|
package field_poly1305
|
||||||
|
|
||||||
import "core:crypto/util"
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
|
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
|
||||||
@@ -11,7 +11,7 @@ fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element)
|
|||||||
return transmute(^Tight_Field_Element)(arg1)
|
return transmute(^Tight_Field_Element)(arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte, sanitize: bool = true) {
|
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte) {
|
||||||
// fiat-crypto's deserialization routine effectively processes a
|
// fiat-crypto's deserialization routine effectively processes a
|
||||||
// single byte at a time, and wants 256-bits of input for a value
|
// single byte at a time, and wants 256-bits of input for a value
|
||||||
// that will be 128-bits or 129-bits.
|
// that will be 128-bits or 129-bits.
|
||||||
@@ -22,42 +22,29 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
|
|||||||
|
|
||||||
assert(len(arg1) == 16)
|
assert(len(arg1) == 16)
|
||||||
|
|
||||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
// While it may be unwise to do deserialization here on our
|
||||||
// While it may be unwise to do deserialization here on our
|
// own when fiat-crypto provides equivalent functionality,
|
||||||
// own when fiat-crypto provides equivalent functionality,
|
// doing it this way provides a little under 3x performance
|
||||||
// doing it this way provides a little under 3x performance
|
// improvement when optimization is enabled.
|
||||||
// improvement when optimization is enabled.
|
lo := endian.unchecked_get_u64le(arg1[0:])
|
||||||
src_p := transmute(^[2]u64)(&arg1[0])
|
hi := endian.unchecked_get_u64le(arg1[8:])
|
||||||
lo := src_p[0]
|
|
||||||
hi := src_p[1]
|
|
||||||
|
|
||||||
// This is inspired by poly1305-donna, though adjustments were
|
// This is inspired by poly1305-donna, though adjustments were
|
||||||
// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
|
// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
|
||||||
// and 43-bits wide.
|
// and 43-bits wide.
|
||||||
//
|
//
|
||||||
// Note: This could be transplated into fe_from_u64s, but that
|
// Note: This could be transplated into fe_from_u64s, but that
|
||||||
// code is called once per MAC, and is non-criticial path.
|
// code is called once per MAC, and is non-criticial path.
|
||||||
hibit := u64(arg2) << 41 // arg2 << 128
|
hibit := u64(arg2) << 41 // arg2 << 128
|
||||||
out1[0] = lo & 0xfffffffffff
|
out1[0] = lo & 0xfffffffffff
|
||||||
out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
|
out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
|
||||||
out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
|
out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
|
||||||
} else {
|
|
||||||
tmp: [32]byte
|
|
||||||
copy_slice(tmp[0:16], arg1[:])
|
|
||||||
tmp[16] = arg2
|
|
||||||
|
|
||||||
_fe_from_bytes(out1, &tmp)
|
|
||||||
if sanitize {
|
|
||||||
// This is used to deserialize `s` which is confidential.
|
|
||||||
mem.zero_explicit(&tmp, size_of(tmp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
||||||
tmp: [32]byte
|
tmp: [32]byte
|
||||||
util.PUT_U64_LE(tmp[0:8], lo)
|
endian.unchecked_put_u64le(tmp[0:], lo)
|
||||||
util.PUT_U64_LE(tmp[8:16], hi)
|
endian.unchecked_put_u64le(tmp[8:], hi)
|
||||||
|
|
||||||
_fe_from_bytes(out1, &tmp)
|
_fe_from_bytes(out1, &tmp)
|
||||||
|
|
||||||
|
|||||||
+168
-129
@@ -11,159 +11,198 @@ package _sha3
|
|||||||
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
|
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "../util"
|
import "core:math/bits"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
ROUNDS :: 24
|
ROUNDS :: 24
|
||||||
|
|
||||||
Sha3_Context :: struct {
|
RATE_224 :: 1152 / 8
|
||||||
st: struct #raw_union {
|
RATE_256 :: 1088 / 8
|
||||||
b: [200]u8,
|
RATE_384 :: 832 / 8
|
||||||
q: [25]u64,
|
RATE_512 :: 576 / 8
|
||||||
},
|
|
||||||
pt: int,
|
Context :: struct {
|
||||||
rsiz: int,
|
st: struct #raw_union {
|
||||||
mdlen: int,
|
b: [200]u8,
|
||||||
is_keccak: bool,
|
q: [25]u64,
|
||||||
|
},
|
||||||
|
pt: int,
|
||||||
|
rsiz: int,
|
||||||
|
mdlen: int,
|
||||||
|
is_keccak: bool,
|
||||||
|
|
||||||
|
is_initialized: bool,
|
||||||
|
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
keccakf :: proc "contextless" (st: ^[25]u64) {
|
keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||||
keccakf_rndc := [?]u64 {
|
keccakf_rndc := [?]u64 {
|
||||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||||
}
|
}
|
||||||
|
|
||||||
keccakf_rotc := [?]i32 {
|
keccakf_rotc := [?]int {
|
||||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||||
}
|
}
|
||||||
|
|
||||||
keccakf_piln := [?]i32 {
|
keccakf_piln := [?]i32 {
|
||||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
i, j, r: i32 = ---, ---, ---
|
i, j, r: i32 = ---, ---, ---
|
||||||
t: u64 = ---
|
t: u64 = ---
|
||||||
bc: [5]u64 = ---
|
bc: [5]u64 = ---
|
||||||
|
|
||||||
when ODIN_ENDIAN != .Little {
|
when ODIN_ENDIAN != .Little {
|
||||||
v: uintptr = ---
|
for i = 0; i < 25; i += 1 {
|
||||||
for i = 0; i < 25; i += 1 {
|
st[i] = bits.byte_swap(st[i])
|
||||||
v := uintptr(&st[i])
|
}
|
||||||
st[i] = u64((^u8)(v + 0)^ << 0) | u64((^u8)(v + 1)^ << 8) |
|
}
|
||||||
u64((^u8)(v + 2)^ << 16) | u64((^u8)(v + 3)^ << 24) |
|
|
||||||
u64((^u8)(v + 4)^ << 32) | u64((^u8)(v + 5)^ << 40) |
|
|
||||||
u64((^u8)(v + 6)^ << 48) | u64((^u8)(v + 7)^ << 56)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for r = 0; r < ROUNDS; r += 1 {
|
for r = 0; r < ROUNDS; r += 1 {
|
||||||
// theta
|
// theta
|
||||||
for i = 0; i < 5; i += 1 {
|
for i = 0; i < 5; i += 1 {
|
||||||
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
||||||
}
|
}
|
||||||
|
|
||||||
for i = 0; i < 5; i += 1 {
|
for i = 0; i < 5; i += 1 {
|
||||||
t = bc[(i + 4) % 5] ~ util.ROTL64(bc[(i + 1) % 5], 1)
|
t = bc[(i + 4) % 5] ~ bits.rotate_left64(bc[(i + 1) % 5], 1)
|
||||||
for j = 0; j < 25; j += 5 {
|
for j = 0; j < 25; j += 5 {
|
||||||
st[j + i] ~= t
|
st[j + i] ~= t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rho pi
|
// rho pi
|
||||||
t = st[1]
|
t = st[1]
|
||||||
for i = 0; i < 24; i += 1 {
|
for i = 0; i < 24; i += 1 {
|
||||||
j = keccakf_piln[i]
|
j = keccakf_piln[i]
|
||||||
bc[0] = st[j]
|
bc[0] = st[j]
|
||||||
st[j] = util.ROTL64(t, u64(keccakf_rotc[i]))
|
st[j] = bits.rotate_left64(t, keccakf_rotc[i])
|
||||||
t = bc[0]
|
t = bc[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// chi
|
// chi
|
||||||
for j = 0; j < 25; j += 5 {
|
for j = 0; j < 25; j += 5 {
|
||||||
for i = 0; i < 5; i += 1 {
|
for i = 0; i < 5; i += 1 {
|
||||||
bc[i] = st[j + i]
|
bc[i] = st[j + i]
|
||||||
}
|
}
|
||||||
for i = 0; i < 5; i += 1 {
|
for i = 0; i < 5; i += 1 {
|
||||||
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
st[0] ~= keccakf_rndc[r]
|
st[0] ~= keccakf_rndc[r]
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_ENDIAN != .Little {
|
when ODIN_ENDIAN != .Little {
|
||||||
for i = 0; i < 25; i += 1 {
|
for i = 0; i < 25; i += 1 {
|
||||||
v = uintptr(&st[i])
|
st[i] = bits.byte_swap(st[i])
|
||||||
t = st[i]
|
}
|
||||||
(^u8)(v + 0)^ = (t >> 0) & 0xff
|
}
|
||||||
(^u8)(v + 1)^ = (t >> 8) & 0xff
|
|
||||||
(^u8)(v + 2)^ = (t >> 16) & 0xff
|
|
||||||
(^u8)(v + 3)^ = (t >> 24) & 0xff
|
|
||||||
(^u8)(v + 4)^ = (t >> 32) & 0xff
|
|
||||||
(^u8)(v + 5)^ = (t >> 40) & 0xff
|
|
||||||
(^u8)(v + 6)^ = (t >> 48) & 0xff
|
|
||||||
(^u8)(v + 7)^ = (t >> 56) & 0xff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc "contextless" (c: ^Sha3_Context) {
|
init :: proc(ctx: ^Context) {
|
||||||
for i := 0; i < 25; i += 1 {
|
for i := 0; i < 25; i += 1 {
|
||||||
c.st.q[i] = 0
|
ctx.st.q[i] = 0
|
||||||
}
|
}
|
||||||
c.rsiz = 200 - 2 * c.mdlen
|
ctx.rsiz = 200 - 2 * ctx.mdlen
|
||||||
|
ctx.pt = 0
|
||||||
|
|
||||||
|
ctx.is_initialized = true
|
||||||
|
ctx.is_finalized = false
|
||||||
}
|
}
|
||||||
|
|
||||||
update :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
j := c.pt
|
assert(ctx.is_initialized)
|
||||||
for i := 0; i < len(data); i += 1 {
|
assert(!ctx.is_finalized)
|
||||||
c.st.b[j] ~= data[i]
|
|
||||||
j += 1
|
j := ctx.pt
|
||||||
if j >= c.rsiz {
|
for i := 0; i < len(data); i += 1 {
|
||||||
keccakf(&c.st.q)
|
ctx.st.b[j] ~= data[i]
|
||||||
j = 0
|
j += 1
|
||||||
}
|
if j >= ctx.rsiz {
|
||||||
}
|
keccakf(&ctx.st.q)
|
||||||
c.pt = j
|
j = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.pt = j
|
||||||
}
|
}
|
||||||
|
|
||||||
final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
if c.is_keccak {
|
assert(ctx.is_initialized)
|
||||||
c.st.b[c.pt] ~= 0x01
|
|
||||||
} else {
|
if len(hash) < ctx.mdlen {
|
||||||
c.st.b[c.pt] ~= 0x06
|
if ctx.is_keccak {
|
||||||
}
|
panic("crypto/keccac: invalid destination digest size")
|
||||||
|
}
|
||||||
c.st.b[c.rsiz - 1] ~= 0x80
|
panic("crypto/sha3: invalid destination digest size")
|
||||||
keccakf(&c.st.q)
|
}
|
||||||
for i := 0; i < c.mdlen; i += 1 {
|
|
||||||
hash[i] = c.st.b[i]
|
ctx := ctx
|
||||||
}
|
if finalize_clone {
|
||||||
|
tmp_ctx: Context
|
||||||
|
clone(&tmp_ctx, ctx)
|
||||||
|
ctx = &tmp_ctx
|
||||||
|
}
|
||||||
|
defer(reset(ctx))
|
||||||
|
|
||||||
|
if ctx.is_keccak {
|
||||||
|
ctx.st.b[ctx.pt] ~= 0x01
|
||||||
|
} else {
|
||||||
|
ctx.st.b[ctx.pt] ~= 0x06
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.st.b[ctx.rsiz - 1] ~= 0x80
|
||||||
|
keccakf(&ctx.st.q)
|
||||||
|
for i := 0; i < ctx.mdlen; i += 1 {
|
||||||
|
hash[i] = ctx.st.b[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shake_xof :: proc "contextless" (c: ^Sha3_Context) {
|
clone :: proc(ctx, other: ^Context) {
|
||||||
c.st.b[c.pt] ~= 0x1F
|
ctx^ = other^
|
||||||
c.st.b[c.rsiz - 1] ~= 0x80
|
|
||||||
keccakf(&c.st.q)
|
|
||||||
c.pt = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shake_out :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
reset :: proc(ctx: ^Context) {
|
||||||
j := c.pt
|
if !ctx.is_initialized {
|
||||||
for i := 0; i < len(hash); i += 1 {
|
return
|
||||||
if j >= c.rsiz {
|
}
|
||||||
keccakf(&c.st.q)
|
|
||||||
j = 0
|
mem.zero_explicit(ctx, size_of(ctx^))
|
||||||
}
|
}
|
||||||
hash[i] = c.st.b[j]
|
|
||||||
j += 1
|
shake_xof :: proc(ctx: ^Context) {
|
||||||
}
|
assert(ctx.is_initialized)
|
||||||
c.pt = j
|
assert(!ctx.is_finalized)
|
||||||
|
|
||||||
|
ctx.st.b[ctx.pt] ~= 0x1F
|
||||||
|
ctx.st.b[ctx.rsiz - 1] ~= 0x80
|
||||||
|
keccakf(&ctx.st.q)
|
||||||
|
ctx.pt = 0
|
||||||
|
|
||||||
|
ctx.is_finalized = true // No more absorb, unlimited squeeze.
|
||||||
|
}
|
||||||
|
|
||||||
|
shake_out :: proc(ctx: ^Context, hash: []byte) {
|
||||||
|
assert(ctx.is_initialized)
|
||||||
|
assert(ctx.is_finalized)
|
||||||
|
|
||||||
|
j := ctx.pt
|
||||||
|
for i := 0; i < len(hash); i += 1 {
|
||||||
|
if j >= ctx.rsiz {
|
||||||
|
keccakf(&ctx.st.q)
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
hash[i] = ctx.st.b[j]
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
ctx.pt = j
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,410 +0,0 @@
|
|||||||
package _tiger
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "../util"
|
|
||||||
|
|
||||||
T1 := [?]u64 {
|
|
||||||
0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a,
|
|
||||||
0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870,
|
|
||||||
0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c,
|
|
||||||
0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e,
|
|
||||||
0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92,
|
|
||||||
0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e,
|
|
||||||
0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04,
|
|
||||||
0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f,
|
|
||||||
0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3,
|
|
||||||
0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066,
|
|
||||||
0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05,
|
|
||||||
0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169,
|
|
||||||
0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b,
|
|
||||||
0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd,
|
|
||||||
0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e,
|
|
||||||
0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe,
|
|
||||||
0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853,
|
|
||||||
0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14,
|
|
||||||
0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2,
|
|
||||||
0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67,
|
|
||||||
0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062,
|
|
||||||
0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c,
|
|
||||||
0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f,
|
|
||||||
0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556,
|
|
||||||
0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881,
|
|
||||||
0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779,
|
|
||||||
0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4,
|
|
||||||
0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96,
|
|
||||||
0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808,
|
|
||||||
0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4,
|
|
||||||
0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017,
|
|
||||||
0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd,
|
|
||||||
0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2,
|
|
||||||
0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8,
|
|
||||||
0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49,
|
|
||||||
0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5,
|
|
||||||
0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada,
|
|
||||||
0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530,
|
|
||||||
0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143,
|
|
||||||
0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead,
|
|
||||||
0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a,
|
|
||||||
0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb,
|
|
||||||
0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40,
|
|
||||||
0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d,
|
|
||||||
0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d,
|
|
||||||
0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886,
|
|
||||||
0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65,
|
|
||||||
0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6,
|
|
||||||
0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450,
|
|
||||||
0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b,
|
|
||||||
0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de,
|
|
||||||
0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024,
|
|
||||||
0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7,
|
|
||||||
0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952,
|
|
||||||
0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77,
|
|
||||||
0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89,
|
|
||||||
0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af,
|
|
||||||
0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34,
|
|
||||||
0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c,
|
|
||||||
0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895,
|
|
||||||
0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74,
|
|
||||||
0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90,
|
|
||||||
0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab,
|
|
||||||
0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a,
|
|
||||||
}
|
|
||||||
|
|
||||||
T2 := [?]u64 {
|
|
||||||
0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5,
|
|
||||||
0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99,
|
|
||||||
0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3,
|
|
||||||
0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2,
|
|
||||||
0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2,
|
|
||||||
0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd,
|
|
||||||
0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa,
|
|
||||||
0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b,
|
|
||||||
0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85,
|
|
||||||
0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70,
|
|
||||||
0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d,
|
|
||||||
0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c,
|
|
||||||
0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822,
|
|
||||||
0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5,
|
|
||||||
0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e,
|
|
||||||
0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762,
|
|
||||||
0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96,
|
|
||||||
0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063,
|
|
||||||
0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae,
|
|
||||||
0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8,
|
|
||||||
0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5,
|
|
||||||
0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b,
|
|
||||||
0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a,
|
|
||||||
0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a,
|
|
||||||
0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4,
|
|
||||||
0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012,
|
|
||||||
0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621,
|
|
||||||
0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97,
|
|
||||||
0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab,
|
|
||||||
0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0,
|
|
||||||
0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c,
|
|
||||||
0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7,
|
|
||||||
0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c,
|
|
||||||
0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61,
|
|
||||||
0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0,
|
|
||||||
0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf,
|
|
||||||
0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb,
|
|
||||||
0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475,
|
|
||||||
0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee,
|
|
||||||
0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1,
|
|
||||||
0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40,
|
|
||||||
0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9,
|
|
||||||
0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb,
|
|
||||||
0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade,
|
|
||||||
0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e,
|
|
||||||
0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183,
|
|
||||||
0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d,
|
|
||||||
0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb,
|
|
||||||
0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23,
|
|
||||||
0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f,
|
|
||||||
0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9,
|
|
||||||
0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf,
|
|
||||||
0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a,
|
|
||||||
0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839,
|
|
||||||
0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552,
|
|
||||||
0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746,
|
|
||||||
0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f,
|
|
||||||
0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054,
|
|
||||||
0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847,
|
|
||||||
0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6,
|
|
||||||
0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c,
|
|
||||||
0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef,
|
|
||||||
0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86,
|
|
||||||
0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e,
|
|
||||||
}
|
|
||||||
|
|
||||||
T3 := [?]u64 {
|
|
||||||
0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63,
|
|
||||||
0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71,
|
|
||||||
0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579,
|
|
||||||
0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41,
|
|
||||||
0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47,
|
|
||||||
0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8,
|
|
||||||
0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902,
|
|
||||||
0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe,
|
|
||||||
0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f,
|
|
||||||
0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db,
|
|
||||||
0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851,
|
|
||||||
0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2,
|
|
||||||
0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74,
|
|
||||||
0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa,
|
|
||||||
0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840,
|
|
||||||
0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb,
|
|
||||||
0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30,
|
|
||||||
0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88,
|
|
||||||
0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252,
|
|
||||||
0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba,
|
|
||||||
0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6,
|
|
||||||
0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559,
|
|
||||||
0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5,
|
|
||||||
0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b,
|
|
||||||
0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6,
|
|
||||||
0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749,
|
|
||||||
0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede,
|
|
||||||
0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d,
|
|
||||||
0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e,
|
|
||||||
0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a,
|
|
||||||
0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80,
|
|
||||||
0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf,
|
|
||||||
0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b,
|
|
||||||
0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76,
|
|
||||||
0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143,
|
|
||||||
0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536,
|
|
||||||
0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d,
|
|
||||||
0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d,
|
|
||||||
0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1,
|
|
||||||
0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32,
|
|
||||||
0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28,
|
|
||||||
0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044,
|
|
||||||
0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c,
|
|
||||||
0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0,
|
|
||||||
0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8,
|
|
||||||
0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00,
|
|
||||||
0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4,
|
|
||||||
0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf,
|
|
||||||
0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6,
|
|
||||||
0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13,
|
|
||||||
0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa,
|
|
||||||
0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45,
|
|
||||||
0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a,
|
|
||||||
0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534,
|
|
||||||
0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c,
|
|
||||||
0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1,
|
|
||||||
0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2,
|
|
||||||
0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50,
|
|
||||||
0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e,
|
|
||||||
0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25,
|
|
||||||
0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68,
|
|
||||||
0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95,
|
|
||||||
0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9,
|
|
||||||
0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e,
|
|
||||||
}
|
|
||||||
|
|
||||||
T4 := [?]u64 {
|
|
||||||
0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7,
|
|
||||||
0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff,
|
|
||||||
0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0,
|
|
||||||
0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7,
|
|
||||||
0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab,
|
|
||||||
0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893,
|
|
||||||
0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d,
|
|
||||||
0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1,
|
|
||||||
0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c,
|
|
||||||
0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04,
|
|
||||||
0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4,
|
|
||||||
0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262,
|
|
||||||
0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5,
|
|
||||||
0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742,
|
|
||||||
0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e,
|
|
||||||
0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1,
|
|
||||||
0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b,
|
|
||||||
0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d,
|
|
||||||
0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf,
|
|
||||||
0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6,
|
|
||||||
0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208,
|
|
||||||
0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27,
|
|
||||||
0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd,
|
|
||||||
0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c,
|
|
||||||
0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216,
|
|
||||||
0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17,
|
|
||||||
0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64,
|
|
||||||
0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013,
|
|
||||||
0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476,
|
|
||||||
0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d,
|
|
||||||
0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638,
|
|
||||||
0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9,
|
|
||||||
0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3,
|
|
||||||
0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453,
|
|
||||||
0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd,
|
|
||||||
0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951,
|
|
||||||
0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c,
|
|
||||||
0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80,
|
|
||||||
0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b,
|
|
||||||
0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1,
|
|
||||||
0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac,
|
|
||||||
0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce,
|
|
||||||
0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d,
|
|
||||||
0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68,
|
|
||||||
0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782,
|
|
||||||
0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21,
|
|
||||||
0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de,
|
|
||||||
0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58,
|
|
||||||
0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc,
|
|
||||||
0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb,
|
|
||||||
0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457,
|
|
||||||
0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99,
|
|
||||||
0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a,
|
|
||||||
0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829,
|
|
||||||
0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0,
|
|
||||||
0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966,
|
|
||||||
0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503,
|
|
||||||
0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0,
|
|
||||||
0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9,
|
|
||||||
0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b,
|
|
||||||
0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4,
|
|
||||||
0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7,
|
|
||||||
0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044,
|
|
||||||
0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f,
|
|
||||||
}
|
|
||||||
|
|
||||||
Tiger_Context :: struct {
|
|
||||||
a: u64,
|
|
||||||
b: u64,
|
|
||||||
c: u64,
|
|
||||||
x: [64]byte,
|
|
||||||
nx: int,
|
|
||||||
length: u64,
|
|
||||||
ver: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
round :: #force_inline proc "contextless" (a, b, c, x, mul: u64) -> (u64, u64, u64) {
|
|
||||||
a, b, c := a, b, c
|
|
||||||
c ~= x
|
|
||||||
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
|
|
||||||
b += T4[(c >> 8) & 0xff] ~ T3[(c >> 24) & 0xff] ~ T2[(c >> 40) & 0xff] ~ T1[(c >> 56) & 0xff]
|
|
||||||
b *= mul
|
|
||||||
return a, b, c
|
|
||||||
}
|
|
||||||
|
|
||||||
pass :: #force_inline proc "contextless" (a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
|
|
||||||
x, y, z = round(a, b, c, d[0], mul)
|
|
||||||
y, z, x = round(y, z, x, d[1], mul)
|
|
||||||
z, x, y = round(z, x, y, d[2], mul)
|
|
||||||
x, y, z = round(x, y, z, d[3], mul)
|
|
||||||
y, z, x = round(y, z, x, d[4], mul)
|
|
||||||
z, x, y = round(z, x, y, d[5], mul)
|
|
||||||
x, y, z = round(x, y, z, d[6], mul)
|
|
||||||
y, z, x = round(y, z, x, d[7], mul)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key_schedule :: #force_inline proc "contextless" (x: []u64) {
|
|
||||||
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
|
|
||||||
x[1] ~= x[0]
|
|
||||||
x[2] += x[1]
|
|
||||||
x[3] -= x[2] ~ ((~x[1]) << 19)
|
|
||||||
x[4] ~= x[3]
|
|
||||||
x[5] += x[4]
|
|
||||||
x[6] -= x[5] ~ ((~x[4]) >> 23)
|
|
||||||
x[7] ~= x[6]
|
|
||||||
x[0] += x[7]
|
|
||||||
x[1] -= x[0] ~ ((~x[7]) << 19)
|
|
||||||
x[2] ~= x[1]
|
|
||||||
x[3] += x[2]
|
|
||||||
x[4] -= x[3] ~ ((~x[2]) >> 23)
|
|
||||||
x[5] ~= x[4]
|
|
||||||
x[6] += x[5]
|
|
||||||
x[7] -= x[6] ~ 0x0123456789abcdef
|
|
||||||
}
|
|
||||||
|
|
||||||
compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
|
|
||||||
a := ctx.a
|
|
||||||
b := ctx.b
|
|
||||||
c := ctx.c
|
|
||||||
x := util.cast_slice([]u64, data)
|
|
||||||
ctx.a, ctx.b, ctx.c = pass(ctx.a, ctx.b, ctx.c, x, 5)
|
|
||||||
key_schedule(x)
|
|
||||||
ctx.c, ctx.a, ctx.b = pass(ctx.c, ctx.a, ctx.b, x, 7)
|
|
||||||
key_schedule(x)
|
|
||||||
ctx.b, ctx.c, ctx.a = pass(ctx.b, ctx.c, ctx.a, x, 9)
|
|
||||||
ctx.a ~= a
|
|
||||||
ctx.b -= b
|
|
||||||
ctx.c += c
|
|
||||||
}
|
|
||||||
|
|
||||||
init :: proc "contextless" (ctx: ^Tiger_Context) {
|
|
||||||
ctx.a = 0x0123456789abcdef
|
|
||||||
ctx.b = 0xfedcba9876543210
|
|
||||||
ctx.c = 0xf096a5b4c3b2e187
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Tiger_Context, input: []byte) {
|
|
||||||
p := make([]byte, len(input))
|
|
||||||
copy(p, input)
|
|
||||||
|
|
||||||
length := len(p)
|
|
||||||
ctx.length += u64(length)
|
|
||||||
if ctx.nx > 0 {
|
|
||||||
n := len(p)
|
|
||||||
if n > 64 - ctx.nx {
|
|
||||||
n = 64 - ctx.nx
|
|
||||||
}
|
|
||||||
copy(ctx.x[ctx.nx:ctx.nx + n], p[:n])
|
|
||||||
ctx.nx += n
|
|
||||||
if ctx.nx == 64 {
|
|
||||||
compress(ctx, ctx.x[:64 - 1])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
p = p[n:]
|
|
||||||
}
|
|
||||||
for len(p) >= 64 {
|
|
||||||
compress(ctx, p[:64])
|
|
||||||
p = p[64:]
|
|
||||||
}
|
|
||||||
if len(p) > 0 {
|
|
||||||
ctx.nx = copy(ctx.x[:], p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
|
||||||
length := ctx.length
|
|
||||||
tmp: [64]byte
|
|
||||||
if ctx.ver == 1 {
|
|
||||||
tmp[0] = 0x01
|
|
||||||
} else {
|
|
||||||
tmp[0] = 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
size := length & 0x3f
|
|
||||||
if size < 56 {
|
|
||||||
update(ctx, tmp[:56 - size])
|
|
||||||
} else {
|
|
||||||
update(ctx, tmp[:64 + 56 - size])
|
|
||||||
}
|
|
||||||
|
|
||||||
length <<= 3
|
|
||||||
for i := uint(0); i < 8; i += 1 {
|
|
||||||
tmp[i] = byte(length >> (8 * i))
|
|
||||||
}
|
|
||||||
update(ctx, tmp[:8])
|
|
||||||
|
|
||||||
for i := uint(0); i < 8; i += 1 {
|
|
||||||
tmp[i] = byte(ctx.a >> (8 * i))
|
|
||||||
tmp[i + 8] = byte(ctx.b >> (8 * i))
|
|
||||||
tmp[i + 16] = byte(ctx.c >> (8 * i))
|
|
||||||
}
|
|
||||||
copy(hash[:], tmp[:len(hash)])
|
|
||||||
}
|
|
||||||
@@ -1,726 +0,0 @@
|
|||||||
package blake
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the BLAKE hashing algorithm, as defined in <https://web.archive.org/web/20190915215948/https://131002.net/blake>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE_224 :: 28
|
|
||||||
DIGEST_SIZE_256 :: 32
|
|
||||||
DIGEST_SIZE_384 :: 48
|
|
||||||
DIGEST_SIZE_512 :: 64
|
|
||||||
|
|
||||||
// hash_string_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_224 :: proc "contextless" (data: string) -> [DIGEST_SIZE_224]byte {
|
|
||||||
return hash_bytes_224(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: Blake256_Context
|
|
||||||
ctx.is224 = true
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Blake256_Context
|
|
||||||
ctx.is224 = true
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_224 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: Blake256_Context
|
|
||||||
ctx.is224 = true
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_224 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_224(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_224(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_224]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_224 :: proc {
|
|
||||||
hash_stream_224,
|
|
||||||
hash_file_224,
|
|
||||||
hash_bytes_224,
|
|
||||||
hash_string_224,
|
|
||||||
hash_bytes_to_buffer_224,
|
|
||||||
hash_string_to_buffer_224,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_256 :: proc "contextless" (data: string) -> [DIGEST_SIZE_256]byte {
|
|
||||||
return hash_bytes_256(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Blake256_Context
|
|
||||||
ctx.is224 = false
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Blake256_Context
|
|
||||||
ctx.is224 = false
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_256 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Blake256_Context
|
|
||||||
ctx.is224 = false
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_256 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_256(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_256(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_256]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_256 :: proc {
|
|
||||||
hash_stream_256,
|
|
||||||
hash_file_256,
|
|
||||||
hash_bytes_256,
|
|
||||||
hash_string_256,
|
|
||||||
hash_bytes_to_buffer_256,
|
|
||||||
hash_string_to_buffer_256,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_384 :: proc "contextless" (data: string) -> [DIGEST_SIZE_384]byte {
|
|
||||||
return hash_bytes_384(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: Blake512_Context
|
|
||||||
ctx.is384 = true
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Blake512_Context
|
|
||||||
ctx.is384 = true
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_384 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: Blake512_Context
|
|
||||||
ctx.is384 = true
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_384 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_384(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_384(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_384]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_384 :: proc {
|
|
||||||
hash_stream_384,
|
|
||||||
hash_file_384,
|
|
||||||
hash_bytes_384,
|
|
||||||
hash_string_384,
|
|
||||||
hash_bytes_to_buffer_384,
|
|
||||||
hash_string_to_buffer_384,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_512 :: proc "contextless" (data: string) -> [DIGEST_SIZE_512]byte {
|
|
||||||
return hash_bytes_512(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: Blake512_Context
|
|
||||||
ctx.is384 = false
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Blake512_Context
|
|
||||||
ctx.is384 = false
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_512 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: Blake512_Context
|
|
||||||
ctx.is384 = false
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_512 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_512(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_512(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_512]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_512 :: proc {
|
|
||||||
hash_stream_512,
|
|
||||||
hash_file_512,
|
|
||||||
hash_bytes_512,
|
|
||||||
hash_string_512,
|
|
||||||
hash_bytes_to_buffer_512,
|
|
||||||
hash_string_to_buffer_512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc "contextless" (ctx: ^$T) {
|
|
||||||
when T == Blake256_Context {
|
|
||||||
if ctx.is224 {
|
|
||||||
ctx.h[0] = 0xc1059ed8
|
|
||||||
ctx.h[1] = 0x367cd507
|
|
||||||
ctx.h[2] = 0x3070dd17
|
|
||||||
ctx.h[3] = 0xf70e5939
|
|
||||||
ctx.h[4] = 0xffc00b31
|
|
||||||
ctx.h[5] = 0x68581511
|
|
||||||
ctx.h[6] = 0x64f98fa7
|
|
||||||
ctx.h[7] = 0xbefa4fa4
|
|
||||||
} else {
|
|
||||||
ctx.h[0] = 0x6a09e667
|
|
||||||
ctx.h[1] = 0xbb67ae85
|
|
||||||
ctx.h[2] = 0x3c6ef372
|
|
||||||
ctx.h[3] = 0xa54ff53a
|
|
||||||
ctx.h[4] = 0x510e527f
|
|
||||||
ctx.h[5] = 0x9b05688c
|
|
||||||
ctx.h[6] = 0x1f83d9ab
|
|
||||||
ctx.h[7] = 0x5be0cd19
|
|
||||||
}
|
|
||||||
} else when T == Blake512_Context {
|
|
||||||
if ctx.is384 {
|
|
||||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
|
||||||
ctx.h[1] = 0x629a292a367cd507
|
|
||||||
ctx.h[2] = 0x9159015a3070dd17
|
|
||||||
ctx.h[3] = 0x152fecd8f70e5939
|
|
||||||
ctx.h[4] = 0x67332667ffc00b31
|
|
||||||
ctx.h[5] = 0x8eb44a8768581511
|
|
||||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
|
||||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
|
||||||
} else {
|
|
||||||
ctx.h[0] = 0x6a09e667f3bcc908
|
|
||||||
ctx.h[1] = 0xbb67ae8584caa73b
|
|
||||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
|
||||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
|
||||||
ctx.h[4] = 0x510e527fade682d1
|
|
||||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
|
||||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
|
||||||
ctx.h[7] = 0x5be0cd19137e2179
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc "contextless" (ctx: ^$T, data: []byte) {
|
|
||||||
data := data
|
|
||||||
when T == Blake256_Context {
|
|
||||||
if ctx.nx > 0 {
|
|
||||||
n := copy(ctx.x[ctx.nx:], data)
|
|
||||||
ctx.nx += n
|
|
||||||
if ctx.nx == BLOCKSIZE_256 {
|
|
||||||
block256(ctx, ctx.x[:])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
data = data[n:]
|
|
||||||
}
|
|
||||||
if len(data) >= BLOCKSIZE_256 {
|
|
||||||
n := len(data) &~ (BLOCKSIZE_256 - 1)
|
|
||||||
block256(ctx, data[:n])
|
|
||||||
data = data[n:]
|
|
||||||
}
|
|
||||||
if len(data) > 0 {
|
|
||||||
ctx.nx = copy(ctx.x[:], data)
|
|
||||||
}
|
|
||||||
} else when T == Blake512_Context {
|
|
||||||
if ctx.nx > 0 {
|
|
||||||
n := copy(ctx.x[ctx.nx:], data)
|
|
||||||
ctx.nx += n
|
|
||||||
if ctx.nx == BLOCKSIZE_512 {
|
|
||||||
block512(ctx, ctx.x[:])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
data = data[n:]
|
|
||||||
}
|
|
||||||
if len(data) >= BLOCKSIZE_512 {
|
|
||||||
n := len(data) &~ (BLOCKSIZE_512 - 1)
|
|
||||||
block512(ctx, data[:n])
|
|
||||||
data = data[n:]
|
|
||||||
}
|
|
||||||
if len(data) > 0 {
|
|
||||||
ctx.nx = copy(ctx.x[:], data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
|
||||||
when T == Blake256_Context {
|
|
||||||
tmp: [65]byte
|
|
||||||
} else when T == Blake512_Context {
|
|
||||||
tmp: [129]byte
|
|
||||||
}
|
|
||||||
nx := u64(ctx.nx)
|
|
||||||
tmp[0] = 0x80
|
|
||||||
length := (ctx.t + nx) << 3
|
|
||||||
|
|
||||||
when T == Blake256_Context {
|
|
||||||
if nx == 55 {
|
|
||||||
if ctx.is224 {
|
|
||||||
write_additional(ctx, {0x80})
|
|
||||||
} else {
|
|
||||||
write_additional(ctx, {0x81})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if nx < 55 {
|
|
||||||
if nx == 0 {
|
|
||||||
ctx.nullt = true
|
|
||||||
}
|
|
||||||
write_additional(ctx, tmp[0 : 55 - nx])
|
|
||||||
} else {
|
|
||||||
write_additional(ctx, tmp[0 : 64 - nx])
|
|
||||||
write_additional(ctx, tmp[1:56])
|
|
||||||
ctx.nullt = true
|
|
||||||
}
|
|
||||||
if ctx.is224 {
|
|
||||||
write_additional(ctx, {0x00})
|
|
||||||
} else {
|
|
||||||
write_additional(ctx, {0x01})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i : uint = 0; i < 8; i += 1 {
|
|
||||||
tmp[i] = byte(length >> (56 - 8 * i))
|
|
||||||
}
|
|
||||||
write_additional(ctx, tmp[0:8])
|
|
||||||
|
|
||||||
h := ctx.h[:]
|
|
||||||
if ctx.is224 {
|
|
||||||
h = h[0:7]
|
|
||||||
}
|
|
||||||
for s, i in h {
|
|
||||||
hash[i * 4] = byte(s >> 24)
|
|
||||||
hash[i * 4 + 1] = byte(s >> 16)
|
|
||||||
hash[i * 4 + 2] = byte(s >> 8)
|
|
||||||
hash[i * 4 + 3] = byte(s)
|
|
||||||
}
|
|
||||||
} else when T == Blake512_Context {
|
|
||||||
if nx == 111 {
|
|
||||||
if ctx.is384 {
|
|
||||||
write_additional(ctx, {0x80})
|
|
||||||
} else {
|
|
||||||
write_additional(ctx, {0x81})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if nx < 111 {
|
|
||||||
if nx == 0 {
|
|
||||||
ctx.nullt = true
|
|
||||||
}
|
|
||||||
write_additional(ctx, tmp[0 : 111 - nx])
|
|
||||||
} else {
|
|
||||||
write_additional(ctx, tmp[0 : 128 - nx])
|
|
||||||
write_additional(ctx, tmp[1:112])
|
|
||||||
ctx.nullt = true
|
|
||||||
}
|
|
||||||
if ctx.is384 {
|
|
||||||
write_additional(ctx, {0x00})
|
|
||||||
} else {
|
|
||||||
write_additional(ctx, {0x01})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i : uint = 0; i < 16; i += 1 {
|
|
||||||
tmp[i] = byte(length >> (120 - 8 * i))
|
|
||||||
}
|
|
||||||
write_additional(ctx, tmp[0:16])
|
|
||||||
|
|
||||||
h := ctx.h[:]
|
|
||||||
if ctx.is384 {
|
|
||||||
h = h[0:6]
|
|
||||||
}
|
|
||||||
for s, i in h {
|
|
||||||
hash[i * 8] = byte(s >> 56)
|
|
||||||
hash[i * 8 + 1] = byte(s >> 48)
|
|
||||||
hash[i * 8 + 2] = byte(s >> 40)
|
|
||||||
hash[i * 8 + 3] = byte(s >> 32)
|
|
||||||
hash[i * 8 + 4] = byte(s >> 24)
|
|
||||||
hash[i * 8 + 5] = byte(s >> 16)
|
|
||||||
hash[i * 8 + 6] = byte(s >> 8)
|
|
||||||
hash[i * 8 + 7] = byte(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SIZE_224 :: 28
|
|
||||||
SIZE_256 :: 32
|
|
||||||
SIZE_384 :: 48
|
|
||||||
SIZE_512 :: 64
|
|
||||||
BLOCKSIZE_256 :: 64
|
|
||||||
BLOCKSIZE_512 :: 128
|
|
||||||
|
|
||||||
Blake256_Context :: struct {
|
|
||||||
h: [8]u32,
|
|
||||||
s: [4]u32,
|
|
||||||
t: u64,
|
|
||||||
x: [64]byte,
|
|
||||||
nx: int,
|
|
||||||
is224: bool,
|
|
||||||
nullt: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
Blake512_Context :: struct {
|
|
||||||
h: [8]u64,
|
|
||||||
s: [4]u64,
|
|
||||||
t: u64,
|
|
||||||
x: [128]byte,
|
|
||||||
nx: int,
|
|
||||||
is384: bool,
|
|
||||||
nullt: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
SIGMA := [?]int {
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
|
||||||
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
|
||||||
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
|
||||||
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
|
||||||
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
|
||||||
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
|
||||||
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
|
||||||
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
|
||||||
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
U256 := [16]u32 {
|
|
||||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
|
||||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
|
||||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
|
||||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
|
||||||
}
|
|
||||||
|
|
||||||
U512 := [16]u64 {
|
|
||||||
0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89,
|
|
||||||
0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917,
|
|
||||||
0x9216d5d98979fb1b, 0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96,
|
|
||||||
0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16, 0x636920d871574e69,
|
|
||||||
}
|
|
||||||
|
|
||||||
G256 :: #force_inline proc "contextless" (a, b, c, d: u32, m: [16]u32, i, j: int) -> (u32, u32, u32, u32) {
|
|
||||||
a, b, c, d := a, b, c, d
|
|
||||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
|
||||||
a += b
|
|
||||||
d ~= a
|
|
||||||
d = d << (32 - 16) | d >> 16
|
|
||||||
c += d
|
|
||||||
b ~= c
|
|
||||||
b = b << (32 - 12) | b >> 12
|
|
||||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j)]]
|
|
||||||
a += b
|
|
||||||
d ~= a
|
|
||||||
d = d << (32 - 8) | d >> 8
|
|
||||||
c += d
|
|
||||||
b ~= c
|
|
||||||
b = b << (32 - 7) | b >> 7
|
|
||||||
return a, b, c, d
|
|
||||||
}
|
|
||||||
|
|
||||||
G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int) -> (u64, u64, u64, u64) {
|
|
||||||
a, b, c, d := a, b, c, d
|
|
||||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
|
||||||
a += b
|
|
||||||
d ~= a
|
|
||||||
d = d << (64 - 32) | d >> 32
|
|
||||||
c += d
|
|
||||||
b ~= c
|
|
||||||
b = b << (64 - 25) | b >> 25
|
|
||||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j)]]
|
|
||||||
a += b
|
|
||||||
d ~= a
|
|
||||||
d = d << (64 - 16) | d >> 16
|
|
||||||
c += d
|
|
||||||
b ~= c
|
|
||||||
b = b << (64 - 11) | b >> 11
|
|
||||||
return a, b, c, d
|
|
||||||
}
|
|
||||||
|
|
||||||
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) #no_bounds_check {
|
|
||||||
i, j: int = ---, ---
|
|
||||||
v, m: [16]u32 = ---, ---
|
|
||||||
p := p
|
|
||||||
for len(p) >= BLOCKSIZE_256 {
|
|
||||||
v[0] = ctx.h[0]
|
|
||||||
v[1] = ctx.h[1]
|
|
||||||
v[2] = ctx.h[2]
|
|
||||||
v[3] = ctx.h[3]
|
|
||||||
v[4] = ctx.h[4]
|
|
||||||
v[5] = ctx.h[5]
|
|
||||||
v[6] = ctx.h[6]
|
|
||||||
v[7] = ctx.h[7]
|
|
||||||
v[8] = ctx.s[0] ~ U256[0]
|
|
||||||
v[9] = ctx.s[1] ~ U256[1]
|
|
||||||
v[10] = ctx.s[2] ~ U256[2]
|
|
||||||
v[11] = ctx.s[3] ~ U256[3]
|
|
||||||
v[12] = U256[4]
|
|
||||||
v[13] = U256[5]
|
|
||||||
v[14] = U256[6]
|
|
||||||
v[15] = U256[7]
|
|
||||||
|
|
||||||
ctx.t += 512
|
|
||||||
if !ctx.nullt {
|
|
||||||
v[12] ~= u32(ctx.t)
|
|
||||||
v[13] ~= u32(ctx.t)
|
|
||||||
v[14] ~= u32(ctx.t >> 32)
|
|
||||||
v[15] ~= u32(ctx.t >> 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, j = 0, 0; i < 16; i, j = i+1, j+4 {
|
|
||||||
m[i] = u32(p[j]) << 24 | u32(p[j + 1]) << 16 | u32(p[j + 2]) << 8 | u32(p[j + 3])
|
|
||||||
}
|
|
||||||
|
|
||||||
for i = 0; i < 14; i += 1 {
|
|
||||||
v[0], v[4], v[8], v[12] = G256(v[0], v[4], v[8], v[12], m, i, 0)
|
|
||||||
v[1], v[5], v[9], v[13] = G256(v[1], v[5], v[9], v[13], m, i, 1)
|
|
||||||
v[2], v[6], v[10], v[14] = G256(v[2], v[6], v[10], v[14], m, i, 2)
|
|
||||||
v[3], v[7], v[11], v[15] = G256(v[3], v[7], v[11], v[15], m, i, 3)
|
|
||||||
v[0], v[5], v[10], v[15] = G256(v[0], v[5], v[10], v[15], m, i, 4)
|
|
||||||
v[1], v[6], v[11], v[12] = G256(v[1], v[6], v[11], v[12], m, i, 5)
|
|
||||||
v[2], v[7], v[8], v[13] = G256(v[2], v[7], v[8], v[13], m, i, 6)
|
|
||||||
v[3], v[4], v[9], v[14] = G256(v[3], v[4], v[9], v[14], m, i, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i = 0; i < 8; i += 1 {
|
|
||||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
|
||||||
}
|
|
||||||
p = p[BLOCKSIZE_256:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
|
|
||||||
i, j: int = ---, ---
|
|
||||||
v, m: [16]u64 = ---, ---
|
|
||||||
p := p
|
|
||||||
for len(p) >= BLOCKSIZE_512 {
|
|
||||||
v[0] = ctx.h[0]
|
|
||||||
v[1] = ctx.h[1]
|
|
||||||
v[2] = ctx.h[2]
|
|
||||||
v[3] = ctx.h[3]
|
|
||||||
v[4] = ctx.h[4]
|
|
||||||
v[5] = ctx.h[5]
|
|
||||||
v[6] = ctx.h[6]
|
|
||||||
v[7] = ctx.h[7]
|
|
||||||
v[8] = ctx.s[0] ~ U512[0]
|
|
||||||
v[9] = ctx.s[1] ~ U512[1]
|
|
||||||
v[10] = ctx.s[2] ~ U512[2]
|
|
||||||
v[11] = ctx.s[3] ~ U512[3]
|
|
||||||
v[12] = U512[4]
|
|
||||||
v[13] = U512[5]
|
|
||||||
v[14] = U512[6]
|
|
||||||
v[15] = U512[7]
|
|
||||||
|
|
||||||
ctx.t += 1024
|
|
||||||
if !ctx.nullt {
|
|
||||||
v[12] ~= ctx.t
|
|
||||||
v[13] ~= ctx.t
|
|
||||||
v[14] ~= 0
|
|
||||||
v[15] ~= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, j = 0, 0; i < 16; i, j = i + 1, j + 8 {
|
|
||||||
m[i] = u64(p[j]) << 56 | u64(p[j + 1]) << 48 | u64(p[j + 2]) << 40 | u64(p[j + 3]) << 32 |
|
|
||||||
u64(p[j + 4]) << 24 | u64(p[j + 5]) << 16 | u64(p[j + 6]) << 8 | u64(p[j + 7])
|
|
||||||
}
|
|
||||||
for i = 0; i < 16; i += 1 {
|
|
||||||
v[0], v[4], v[8], v[12] = G512(v[0], v[4], v[8], v[12], m, i, 0)
|
|
||||||
v[1], v[5], v[9], v[13] = G512(v[1], v[5], v[9], v[13], m, i, 1)
|
|
||||||
v[2], v[6], v[10], v[14] = G512(v[2], v[6], v[10], v[14], m, i, 2)
|
|
||||||
v[3], v[7], v[11], v[15] = G512(v[3], v[7], v[11], v[15], m, i, 3)
|
|
||||||
v[0], v[5], v[10], v[15] = G512(v[0], v[5], v[10], v[15], m, i, 4)
|
|
||||||
v[1], v[6], v[11], v[12] = G512(v[1], v[6], v[11], v[12], m, i, 5)
|
|
||||||
v[2], v[7], v[8], v[13] = G512(v[2], v[7], v[8], v[13], m, i, 6)
|
|
||||||
v[3], v[4], v[9], v[14] = G512(v[3], v[4], v[9], v[14], m, i, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i = 0; i < 8; i += 1 {
|
|
||||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
|
||||||
}
|
|
||||||
p = p[BLOCKSIZE_512:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write_additional :: proc "contextless" (ctx: ^$T, data: []byte) {
|
|
||||||
ctx.t -= u64(len(data)) << 3
|
|
||||||
update(ctx, data)
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
package blake2b implements the BLAKE2b hash algorithm.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- https://datatracker.ietf.org/doc/html/rfc7693
|
||||||
|
- https://www.blake2.net
|
||||||
|
*/
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -6,122 +13,47 @@ package blake2b
|
|||||||
|
|
||||||
List of contributors:
|
List of contributors:
|
||||||
zhibog, dotbmp: Initial implementation.
|
zhibog, dotbmp: Initial implementation.
|
||||||
|
|
||||||
Interface for the BLAKE2B hashing algorithm.
|
|
||||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../_blake2"
|
import "../_blake2"
|
||||||
|
|
||||||
/*
|
// DIGEST_SIZE is the BLAKE2b digest size in bytes.
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 64
|
DIGEST_SIZE :: 64
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
// BLOCK_SIZE is the BLAKE2b block size in bytes.
|
||||||
// computed hash
|
BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
// Context is a BLAKE2b instance.
|
||||||
|
Context :: _blake2.Blake2b_Context
|
||||||
|
|
||||||
|
// init initializes a Context with the default BLAKE2b config.
|
||||||
|
init :: proc(ctx: ^Context) {
|
||||||
|
cfg: _blake2.Blake2_Config
|
||||||
|
cfg.size = _blake2.BLAKE2B_SIZE
|
||||||
|
_blake2.init(ctx, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
// update adds more data to the Context.
|
||||||
// computed hash
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
_blake2.update(ctx, data)
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: _blake2.Blake2b_Context
|
|
||||||
cfg: _blake2.Blake2_Config
|
|
||||||
cfg.size = _blake2.BLAKE2B_SIZE
|
|
||||||
ctx.cfg = cfg
|
|
||||||
_blake2.init(&ctx)
|
|
||||||
_blake2.update(&ctx, data)
|
|
||||||
_blake2.final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
// computed hash to the second parameter.
|
// reset on the Context.
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
//
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
// which is useful for for calculating rolling digests.
|
||||||
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
|
_blake2.final(ctx, hash, finalize_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
// clone clones the Context other into ctx.
|
||||||
// computed hash into the second parameter.
|
clone :: proc(ctx, other: ^Context) {
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
_blake2.clone(ctx, other)
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: _blake2.Blake2b_Context
|
|
||||||
cfg: _blake2.Blake2_Config
|
|
||||||
cfg.size = _blake2.BLAKE2B_SIZE
|
|
||||||
ctx.cfg = cfg
|
|
||||||
_blake2.init(&ctx)
|
|
||||||
_blake2.update(&ctx, data)
|
|
||||||
_blake2.final(&ctx, hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// hash_stream will read the stream in chunks and compute a
|
// be used again.
|
||||||
// hash from its contents
|
reset :: proc(ctx: ^Context) {
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
_blake2.reset(ctx)
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: _blake2.Blake2b_Context
|
|
||||||
cfg: _blake2.Blake2_Config
|
|
||||||
cfg.size = _blake2.BLAKE2B_SIZE
|
|
||||||
ctx.cfg = cfg
|
|
||||||
_blake2.init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
_blake2.update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_blake2.final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
Blake2b_Context :: _blake2.Blake2b_Context
|
|
||||||
|
|
||||||
init :: proc(ctx: ^_blake2.Blake2b_Context) {
|
|
||||||
_blake2.init(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
|
|
||||||
_blake2.update(ctx, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
|
|
||||||
_blake2.final(ctx, hash)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
package blake2s implements the BLAKE2s hash algorithm.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- https://datatracker.ietf.org/doc/html/rfc7693
|
||||||
|
- https://www.blake2.net/
|
||||||
|
*/
|
||||||
package blake2s
|
package blake2s
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -6,122 +13,47 @@ package blake2s
|
|||||||
|
|
||||||
List of contributors:
|
List of contributors:
|
||||||
zhibog, dotbmp: Initial implementation.
|
zhibog, dotbmp: Initial implementation.
|
||||||
|
|
||||||
Interface for the BLAKE2S hashing algorithm.
|
|
||||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../_blake2"
|
import "../_blake2"
|
||||||
|
|
||||||
/*
|
// DIGEST_SIZE is the BLAKE2s digest size in bytes.
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 32
|
DIGEST_SIZE :: 32
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
// BLOCK_SIZE is the BLAKE2s block size in bytes.
|
||||||
// computed hash
|
BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
// Context is a BLAKE2s instance.
|
||||||
|
Context :: _blake2.Blake2s_Context
|
||||||
|
|
||||||
|
// init initializes a Context with the default BLAKE2s config.
|
||||||
|
init :: proc(ctx: ^Context) {
|
||||||
|
cfg: _blake2.Blake2_Config
|
||||||
|
cfg.size = _blake2.BLAKE2S_SIZE
|
||||||
|
_blake2.init(ctx, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
// update adds more data to the Context.
|
||||||
// computed hash
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
_blake2.update(ctx, data)
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: _blake2.Blake2s_Context
|
|
||||||
cfg: _blake2.Blake2_Config
|
|
||||||
cfg.size = _blake2.BLAKE2S_SIZE
|
|
||||||
ctx.cfg = cfg
|
|
||||||
_blake2.init(&ctx)
|
|
||||||
_blake2.update(&ctx, data)
|
|
||||||
_blake2.final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
// reset on the Context.
|
||||||
// computed hash to the second parameter.
|
//
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
// which is useful for for calculating rolling digests.
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
|
_blake2.final(ctx, hash, finalize_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
// clone clones the Context other into ctx.
|
||||||
// computed hash into the second parameter.
|
clone :: proc(ctx, other: ^Context) {
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
_blake2.clone(ctx, other)
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: _blake2.Blake2s_Context
|
|
||||||
cfg: _blake2.Blake2_Config
|
|
||||||
cfg.size = _blake2.BLAKE2S_SIZE
|
|
||||||
ctx.cfg = cfg
|
|
||||||
_blake2.init(&ctx)
|
|
||||||
_blake2.update(&ctx, data)
|
|
||||||
_blake2.final(&ctx, hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_stream will read the stream in chunks and compute a
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// hash from its contents
|
// be used again.
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
reset :: proc(ctx: ^Context) {
|
||||||
hash: [DIGEST_SIZE]byte
|
_blake2.reset(ctx)
|
||||||
ctx: _blake2.Blake2s_Context
|
|
||||||
cfg: _blake2.Blake2_Config
|
|
||||||
cfg.size = _blake2.BLAKE2S_SIZE
|
|
||||||
ctx.cfg = cfg
|
|
||||||
_blake2.init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
_blake2.update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_blake2.final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
Blake2s_Context :: _blake2.Blake2b_Context
|
|
||||||
|
|
||||||
init :: proc(ctx: ^_blake2.Blake2s_Context) {
|
|
||||||
_blake2.init(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
|
|
||||||
_blake2.update(ctx, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
|
|
||||||
_blake2.final(ctx, hash)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+135
-180
@@ -1,6 +1,6 @@
|
|||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
import "core:crypto/util"
|
import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -60,23 +60,23 @@ init :: proc (ctx: ^Context, key, nonce: []byte) {
|
|||||||
ctx._s[1] = _SIGMA_1
|
ctx._s[1] = _SIGMA_1
|
||||||
ctx._s[2] = _SIGMA_2
|
ctx._s[2] = _SIGMA_2
|
||||||
ctx._s[3] = _SIGMA_3
|
ctx._s[3] = _SIGMA_3
|
||||||
ctx._s[4] = util.U32_LE(k[0:4])
|
ctx._s[4] = endian.unchecked_get_u32le(k[0:4])
|
||||||
ctx._s[5] = util.U32_LE(k[4:8])
|
ctx._s[5] = endian.unchecked_get_u32le(k[4:8])
|
||||||
ctx._s[6] = util.U32_LE(k[8:12])
|
ctx._s[6] = endian.unchecked_get_u32le(k[8:12])
|
||||||
ctx._s[7] = util.U32_LE(k[12:16])
|
ctx._s[7] = endian.unchecked_get_u32le(k[12:16])
|
||||||
ctx._s[8] = util.U32_LE(k[16:20])
|
ctx._s[8] = endian.unchecked_get_u32le(k[16:20])
|
||||||
ctx._s[9] = util.U32_LE(k[20:24])
|
ctx._s[9] = endian.unchecked_get_u32le(k[20:24])
|
||||||
ctx._s[10] = util.U32_LE(k[24:28])
|
ctx._s[10] = endian.unchecked_get_u32le(k[24:28])
|
||||||
ctx._s[11] = util.U32_LE(k[28:32])
|
ctx._s[11] = endian.unchecked_get_u32le(k[28:32])
|
||||||
ctx._s[12] = 0
|
ctx._s[12] = 0
|
||||||
if !is_xchacha {
|
if !is_xchacha {
|
||||||
ctx._s[13] = util.U32_LE(n[0:4])
|
ctx._s[13] = endian.unchecked_get_u32le(n[0:4])
|
||||||
ctx._s[14] = util.U32_LE(n[4:8])
|
ctx._s[14] = endian.unchecked_get_u32le(n[4:8])
|
||||||
ctx._s[15] = util.U32_LE(n[8:12])
|
ctx._s[15] = endian.unchecked_get_u32le(n[8:12])
|
||||||
} else {
|
} else {
|
||||||
ctx._s[13] = 0
|
ctx._s[13] = 0
|
||||||
ctx._s[14] = util.U32_LE(n[0:4])
|
ctx._s[14] = endian.unchecked_get_u32le(n[0:4])
|
||||||
ctx._s[15] = util.U32_LE(n[4:8])
|
ctx._s[15] = endian.unchecked_get_u32le(n[4:8])
|
||||||
|
|
||||||
// The sub-key is stored in the keystream buffer. While
|
// The sub-key is stored in the keystream buffer. While
|
||||||
// this will be overwritten in most circumstances, explicitly
|
// this will be overwritten in most circumstances, explicitly
|
||||||
@@ -221,114 +221,114 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
|||||||
// quarterround(x, 0, 4, 8, 12)
|
// quarterround(x, 0, 4, 8, 12)
|
||||||
x0 += x4
|
x0 += x4
|
||||||
x12 ~= x0
|
x12 ~= x0
|
||||||
x12 = util.ROTL32(x12, 16)
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
x8 += x12
|
x8 += x12
|
||||||
x4 ~= x8
|
x4 ~= x8
|
||||||
x4 = util.ROTL32(x4, 12)
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
x0 += x4
|
x0 += x4
|
||||||
x12 ~= x0
|
x12 ~= x0
|
||||||
x12 = util.ROTL32(x12, 8)
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
x8 += x12
|
x8 += x12
|
||||||
x4 ~= x8
|
x4 ~= x8
|
||||||
x4 = util.ROTL32(x4, 7)
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
|
|
||||||
// quarterround(x, 1, 5, 9, 13)
|
// quarterround(x, 1, 5, 9, 13)
|
||||||
x1 += x5
|
x1 += x5
|
||||||
x13 ~= x1
|
x13 ~= x1
|
||||||
x13 = util.ROTL32(x13, 16)
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
x9 += x13
|
x9 += x13
|
||||||
x5 ~= x9
|
x5 ~= x9
|
||||||
x5 = util.ROTL32(x5, 12)
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
x1 += x5
|
x1 += x5
|
||||||
x13 ~= x1
|
x13 ~= x1
|
||||||
x13 = util.ROTL32(x13, 8)
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
x9 += x13
|
x9 += x13
|
||||||
x5 ~= x9
|
x5 ~= x9
|
||||||
x5 = util.ROTL32(x5, 7)
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
// quarterround(x, 2, 6, 10, 14)
|
// quarterround(x, 2, 6, 10, 14)
|
||||||
x2 += x6
|
x2 += x6
|
||||||
x14 ~= x2
|
x14 ~= x2
|
||||||
x14 = util.ROTL32(x14, 16)
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
x10 += x14
|
x10 += x14
|
||||||
x6 ~= x10
|
x6 ~= x10
|
||||||
x6 = util.ROTL32(x6, 12)
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
x2 += x6
|
x2 += x6
|
||||||
x14 ~= x2
|
x14 ~= x2
|
||||||
x14 = util.ROTL32(x14, 8)
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
x10 += x14
|
x10 += x14
|
||||||
x6 ~= x10
|
x6 ~= x10
|
||||||
x6 = util.ROTL32(x6, 7)
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
// quarterround(x, 3, 7, 11, 15)
|
// quarterround(x, 3, 7, 11, 15)
|
||||||
x3 += x7
|
x3 += x7
|
||||||
x15 ~= x3
|
x15 ~= x3
|
||||||
x15 = util.ROTL32(x15, 16)
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
x11 += x15
|
x11 += x15
|
||||||
x7 ~= x11
|
x7 ~= x11
|
||||||
x7 = util.ROTL32(x7, 12)
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
x3 += x7
|
x3 += x7
|
||||||
x15 ~= x3
|
x15 ~= x3
|
||||||
x15 = util.ROTL32(x15, 8)
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
x11 += x15
|
x11 += x15
|
||||||
x7 ~= x11
|
x7 ~= x11
|
||||||
x7 = util.ROTL32(x7, 7)
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
// quarterround(x, 0, 5, 10, 15)
|
// quarterround(x, 0, 5, 10, 15)
|
||||||
x0 += x5
|
x0 += x5
|
||||||
x15 ~= x0
|
x15 ~= x0
|
||||||
x15 = util.ROTL32(x15, 16)
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
x10 += x15
|
x10 += x15
|
||||||
x5 ~= x10
|
x5 ~= x10
|
||||||
x5 = util.ROTL32(x5, 12)
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
x0 += x5
|
x0 += x5
|
||||||
x15 ~= x0
|
x15 ~= x0
|
||||||
x15 = util.ROTL32(x15, 8)
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
x10 += x15
|
x10 += x15
|
||||||
x5 ~= x10
|
x5 ~= x10
|
||||||
x5 = util.ROTL32(x5, 7)
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
// quarterround(x, 1, 6, 11, 12)
|
// quarterround(x, 1, 6, 11, 12)
|
||||||
x1 += x6
|
x1 += x6
|
||||||
x12 ~= x1
|
x12 ~= x1
|
||||||
x12 = util.ROTL32(x12, 16)
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
x11 += x12
|
x11 += x12
|
||||||
x6 ~= x11
|
x6 ~= x11
|
||||||
x6 = util.ROTL32(x6, 12)
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
x1 += x6
|
x1 += x6
|
||||||
x12 ~= x1
|
x12 ~= x1
|
||||||
x12 = util.ROTL32(x12, 8)
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
x11 += x12
|
x11 += x12
|
||||||
x6 ~= x11
|
x6 ~= x11
|
||||||
x6 = util.ROTL32(x6, 7)
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
// quarterround(x, 2, 7, 8, 13)
|
// quarterround(x, 2, 7, 8, 13)
|
||||||
x2 += x7
|
x2 += x7
|
||||||
x13 ~= x2
|
x13 ~= x2
|
||||||
x13 = util.ROTL32(x13, 16)
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
x8 += x13
|
x8 += x13
|
||||||
x7 ~= x8
|
x7 ~= x8
|
||||||
x7 = util.ROTL32(x7, 12)
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
x2 += x7
|
x2 += x7
|
||||||
x13 ~= x2
|
x13 ~= x2
|
||||||
x13 = util.ROTL32(x13, 8)
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
x8 += x13
|
x8 += x13
|
||||||
x7 ~= x8
|
x7 ~= x8
|
||||||
x7 = util.ROTL32(x7, 7)
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
// quarterround(x, 3, 4, 9, 14)
|
// quarterround(x, 3, 4, 9, 14)
|
||||||
x3 += x4
|
x3 += x4
|
||||||
x14 ~= x3
|
x14 ~= x3
|
||||||
x14 = util.ROTL32(x14, 16)
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
x9 += x14
|
x9 += x14
|
||||||
x4 ~= x9
|
x4 ~= x9
|
||||||
x4 = util.ROTL32(x4, 12)
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
x3 += x4
|
x3 += x4
|
||||||
x14 ~= x3
|
x14 ~= x3
|
||||||
x14 = util.ROTL32(x14, 8)
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
x9 += x14
|
x9 += x14
|
||||||
x4 ~= x9
|
x4 ~= x9
|
||||||
x4 = util.ROTL32(x4, 7)
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
x0 += _SIGMA_0
|
x0 += _SIGMA_0
|
||||||
@@ -352,93 +352,48 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
|||||||
// this is "use vector operations", support for that is currently
|
// this is "use vector operations", support for that is currently
|
||||||
// a work in progress/to be designed.
|
// a work in progress/to be designed.
|
||||||
//
|
//
|
||||||
// Until dedicated assembly can be written leverage the fact that
|
// In the meantime:
|
||||||
// the callers of this routine ensure that src/dst are valid.
|
// - The caller(s) ensure that src/dst are valid.
|
||||||
|
// - The compiler knows if the target is picky about alignment.
|
||||||
|
|
||||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
#no_bounds_check {
|
||||||
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
|
|
||||||
// systems that also happen to not be strict about aligned
|
|
||||||
// memory access.
|
|
||||||
|
|
||||||
dst_p := transmute(^[16]u32)(&dst[0])
|
|
||||||
if src != nil {
|
if src != nil {
|
||||||
src_p := transmute(^[16]u32)(&src[0])
|
endian.unchecked_put_u32le(dst[0:4], endian.unchecked_get_u32le(src[0:4]) ~ x0)
|
||||||
dst_p[0] = src_p[0] ~ x0
|
endian.unchecked_put_u32le(dst[4:8], endian.unchecked_get_u32le(src[4:8]) ~ x1)
|
||||||
dst_p[1] = src_p[1] ~ x1
|
endian.unchecked_put_u32le(dst[8:12], endian.unchecked_get_u32le(src[8:12]) ~ x2)
|
||||||
dst_p[2] = src_p[2] ~ x2
|
endian.unchecked_put_u32le(dst[12:16], endian.unchecked_get_u32le(src[12:16]) ~ x3)
|
||||||
dst_p[3] = src_p[3] ~ x3
|
endian.unchecked_put_u32le(dst[16:20], endian.unchecked_get_u32le(src[16:20]) ~ x4)
|
||||||
dst_p[4] = src_p[4] ~ x4
|
endian.unchecked_put_u32le(dst[20:24], endian.unchecked_get_u32le(src[20:24]) ~ x5)
|
||||||
dst_p[5] = src_p[5] ~ x5
|
endian.unchecked_put_u32le(dst[24:28], endian.unchecked_get_u32le(src[24:28]) ~ x6)
|
||||||
dst_p[6] = src_p[6] ~ x6
|
endian.unchecked_put_u32le(dst[28:32], endian.unchecked_get_u32le(src[28:32]) ~ x7)
|
||||||
dst_p[7] = src_p[7] ~ x7
|
endian.unchecked_put_u32le(dst[32:36], endian.unchecked_get_u32le(src[32:36]) ~ x8)
|
||||||
dst_p[8] = src_p[8] ~ x8
|
endian.unchecked_put_u32le(dst[36:40], endian.unchecked_get_u32le(src[36:40]) ~ x9)
|
||||||
dst_p[9] = src_p[9] ~ x9
|
endian.unchecked_put_u32le(dst[40:44], endian.unchecked_get_u32le(src[40:44]) ~ x10)
|
||||||
dst_p[10] = src_p[10] ~ x10
|
endian.unchecked_put_u32le(dst[44:48], endian.unchecked_get_u32le(src[44:48]) ~ x11)
|
||||||
dst_p[11] = src_p[11] ~ x11
|
endian.unchecked_put_u32le(dst[48:52], endian.unchecked_get_u32le(src[48:52]) ~ x12)
|
||||||
dst_p[12] = src_p[12] ~ x12
|
endian.unchecked_put_u32le(dst[52:56], endian.unchecked_get_u32le(src[52:56]) ~ x13)
|
||||||
dst_p[13] = src_p[13] ~ x13
|
endian.unchecked_put_u32le(dst[56:60], endian.unchecked_get_u32le(src[56:60]) ~ x14)
|
||||||
dst_p[14] = src_p[14] ~ x14
|
endian.unchecked_put_u32le(dst[60:64], endian.unchecked_get_u32le(src[60:64]) ~ x15)
|
||||||
dst_p[15] = src_p[15] ~ x15
|
|
||||||
src = src[_BLOCK_SIZE:]
|
src = src[_BLOCK_SIZE:]
|
||||||
} else {
|
} else {
|
||||||
dst_p[0] = x0
|
endian.unchecked_put_u32le(dst[0:4], x0)
|
||||||
dst_p[1] = x1
|
endian.unchecked_put_u32le(dst[4:8], x1)
|
||||||
dst_p[2] = x2
|
endian.unchecked_put_u32le(dst[8:12], x2)
|
||||||
dst_p[3] = x3
|
endian.unchecked_put_u32le(dst[12:16], x3)
|
||||||
dst_p[4] = x4
|
endian.unchecked_put_u32le(dst[16:20], x4)
|
||||||
dst_p[5] = x5
|
endian.unchecked_put_u32le(dst[20:24], x5)
|
||||||
dst_p[6] = x6
|
endian.unchecked_put_u32le(dst[24:28], x6)
|
||||||
dst_p[7] = x7
|
endian.unchecked_put_u32le(dst[28:32], x7)
|
||||||
dst_p[8] = x8
|
endian.unchecked_put_u32le(dst[32:36], x8)
|
||||||
dst_p[9] = x9
|
endian.unchecked_put_u32le(dst[36:40], x9)
|
||||||
dst_p[10] = x10
|
endian.unchecked_put_u32le(dst[40:44], x10)
|
||||||
dst_p[11] = x11
|
endian.unchecked_put_u32le(dst[44:48], x11)
|
||||||
dst_p[12] = x12
|
endian.unchecked_put_u32le(dst[48:52], x12)
|
||||||
dst_p[13] = x13
|
endian.unchecked_put_u32le(dst[52:56], x13)
|
||||||
dst_p[14] = x14
|
endian.unchecked_put_u32le(dst[56:60], x14)
|
||||||
dst_p[15] = x15
|
endian.unchecked_put_u32le(dst[60:64], x15)
|
||||||
}
|
}
|
||||||
dst = dst[_BLOCK_SIZE:]
|
dst = dst[_BLOCK_SIZE:]
|
||||||
} else {
|
|
||||||
#no_bounds_check {
|
|
||||||
if src != nil {
|
|
||||||
util.PUT_U32_LE(dst[0:4], util.U32_LE(src[0:4]) ~ x0)
|
|
||||||
util.PUT_U32_LE(dst[4:8], util.U32_LE(src[4:8]) ~ x1)
|
|
||||||
util.PUT_U32_LE(dst[8:12], util.U32_LE(src[8:12]) ~ x2)
|
|
||||||
util.PUT_U32_LE(dst[12:16], util.U32_LE(src[12:16]) ~ x3)
|
|
||||||
util.PUT_U32_LE(dst[16:20], util.U32_LE(src[16:20]) ~ x4)
|
|
||||||
util.PUT_U32_LE(dst[20:24], util.U32_LE(src[20:24]) ~ x5)
|
|
||||||
util.PUT_U32_LE(dst[24:28], util.U32_LE(src[24:28]) ~ x6)
|
|
||||||
util.PUT_U32_LE(dst[28:32], util.U32_LE(src[28:32]) ~ x7)
|
|
||||||
util.PUT_U32_LE(dst[32:36], util.U32_LE(src[32:36]) ~ x8)
|
|
||||||
util.PUT_U32_LE(dst[36:40], util.U32_LE(src[36:40]) ~ x9)
|
|
||||||
util.PUT_U32_LE(dst[40:44], util.U32_LE(src[40:44]) ~ x10)
|
|
||||||
util.PUT_U32_LE(dst[44:48], util.U32_LE(src[44:48]) ~ x11)
|
|
||||||
util.PUT_U32_LE(dst[48:52], util.U32_LE(src[48:52]) ~ x12)
|
|
||||||
util.PUT_U32_LE(dst[52:56], util.U32_LE(src[52:56]) ~ x13)
|
|
||||||
util.PUT_U32_LE(dst[56:60], util.U32_LE(src[56:60]) ~ x14)
|
|
||||||
util.PUT_U32_LE(dst[60:64], util.U32_LE(src[60:64]) ~ x15)
|
|
||||||
src = src[_BLOCK_SIZE:]
|
|
||||||
} else {
|
|
||||||
util.PUT_U32_LE(dst[0:4], x0)
|
|
||||||
util.PUT_U32_LE(dst[4:8], x1)
|
|
||||||
util.PUT_U32_LE(dst[8:12], x2)
|
|
||||||
util.PUT_U32_LE(dst[12:16], x3)
|
|
||||||
util.PUT_U32_LE(dst[16:20], x4)
|
|
||||||
util.PUT_U32_LE(dst[20:24], x5)
|
|
||||||
util.PUT_U32_LE(dst[24:28], x6)
|
|
||||||
util.PUT_U32_LE(dst[28:32], x7)
|
|
||||||
util.PUT_U32_LE(dst[32:36], x8)
|
|
||||||
util.PUT_U32_LE(dst[36:40], x9)
|
|
||||||
util.PUT_U32_LE(dst[40:44], x10)
|
|
||||||
util.PUT_U32_LE(dst[44:48], x11)
|
|
||||||
util.PUT_U32_LE(dst[48:52], x12)
|
|
||||||
util.PUT_U32_LE(dst[52:56], x13)
|
|
||||||
util.PUT_U32_LE(dst[56:60], x14)
|
|
||||||
util.PUT_U32_LE(dst[60:64], x15)
|
|
||||||
}
|
|
||||||
dst = dst[_BLOCK_SIZE:]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the counter. Overflow checking is done upon
|
// Increment the counter. Overflow checking is done upon
|
||||||
@@ -451,141 +406,141 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_hchacha20 :: proc (dst, key, nonce: []byte) {
|
_hchacha20 :: proc "contextless" (dst, key, nonce: []byte) {
|
||||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
||||||
x4 := util.U32_LE(key[0:4])
|
x4 := endian.unchecked_get_u32le(key[0:4])
|
||||||
x5 := util.U32_LE(key[4:8])
|
x5 := endian.unchecked_get_u32le(key[4:8])
|
||||||
x6 := util.U32_LE(key[8:12])
|
x6 := endian.unchecked_get_u32le(key[8:12])
|
||||||
x7 := util.U32_LE(key[12:16])
|
x7 := endian.unchecked_get_u32le(key[12:16])
|
||||||
x8 := util.U32_LE(key[16:20])
|
x8 := endian.unchecked_get_u32le(key[16:20])
|
||||||
x9 := util.U32_LE(key[20:24])
|
x9 := endian.unchecked_get_u32le(key[20:24])
|
||||||
x10 := util.U32_LE(key[24:28])
|
x10 := endian.unchecked_get_u32le(key[24:28])
|
||||||
x11 := util.U32_LE(key[28:32])
|
x11 := endian.unchecked_get_u32le(key[28:32])
|
||||||
x12 := util.U32_LE(nonce[0:4])
|
x12 := endian.unchecked_get_u32le(nonce[0:4])
|
||||||
x13 := util.U32_LE(nonce[4:8])
|
x13 := endian.unchecked_get_u32le(nonce[4:8])
|
||||||
x14 := util.U32_LE(nonce[8:12])
|
x14 := endian.unchecked_get_u32le(nonce[8:12])
|
||||||
x15 := util.U32_LE(nonce[12:16])
|
x15 := endian.unchecked_get_u32le(nonce[12:16])
|
||||||
|
|
||||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
for i := _ROUNDS; i > 0; i = i - 2 {
|
||||||
// quarterround(x, 0, 4, 8, 12)
|
// quarterround(x, 0, 4, 8, 12)
|
||||||
x0 += x4
|
x0 += x4
|
||||||
x12 ~= x0
|
x12 ~= x0
|
||||||
x12 = util.ROTL32(x12, 16)
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
x8 += x12
|
x8 += x12
|
||||||
x4 ~= x8
|
x4 ~= x8
|
||||||
x4 = util.ROTL32(x4, 12)
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
x0 += x4
|
x0 += x4
|
||||||
x12 ~= x0
|
x12 ~= x0
|
||||||
x12 = util.ROTL32(x12, 8)
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
x8 += x12
|
x8 += x12
|
||||||
x4 ~= x8
|
x4 ~= x8
|
||||||
x4 = util.ROTL32(x4, 7)
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
|
|
||||||
// quarterround(x, 1, 5, 9, 13)
|
// quarterround(x, 1, 5, 9, 13)
|
||||||
x1 += x5
|
x1 += x5
|
||||||
x13 ~= x1
|
x13 ~= x1
|
||||||
x13 = util.ROTL32(x13, 16)
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
x9 += x13
|
x9 += x13
|
||||||
x5 ~= x9
|
x5 ~= x9
|
||||||
x5 = util.ROTL32(x5, 12)
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
x1 += x5
|
x1 += x5
|
||||||
x13 ~= x1
|
x13 ~= x1
|
||||||
x13 = util.ROTL32(x13, 8)
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
x9 += x13
|
x9 += x13
|
||||||
x5 ~= x9
|
x5 ~= x9
|
||||||
x5 = util.ROTL32(x5, 7)
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
// quarterround(x, 2, 6, 10, 14)
|
// quarterround(x, 2, 6, 10, 14)
|
||||||
x2 += x6
|
x2 += x6
|
||||||
x14 ~= x2
|
x14 ~= x2
|
||||||
x14 = util.ROTL32(x14, 16)
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
x10 += x14
|
x10 += x14
|
||||||
x6 ~= x10
|
x6 ~= x10
|
||||||
x6 = util.ROTL32(x6, 12)
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
x2 += x6
|
x2 += x6
|
||||||
x14 ~= x2
|
x14 ~= x2
|
||||||
x14 = util.ROTL32(x14, 8)
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
x10 += x14
|
x10 += x14
|
||||||
x6 ~= x10
|
x6 ~= x10
|
||||||
x6 = util.ROTL32(x6, 7)
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
// quarterround(x, 3, 7, 11, 15)
|
// quarterround(x, 3, 7, 11, 15)
|
||||||
x3 += x7
|
x3 += x7
|
||||||
x15 ~= x3
|
x15 ~= x3
|
||||||
x15 = util.ROTL32(x15, 16)
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
x11 += x15
|
x11 += x15
|
||||||
x7 ~= x11
|
x7 ~= x11
|
||||||
x7 = util.ROTL32(x7, 12)
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
x3 += x7
|
x3 += x7
|
||||||
x15 ~= x3
|
x15 ~= x3
|
||||||
x15 = util.ROTL32(x15, 8)
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
x11 += x15
|
x11 += x15
|
||||||
x7 ~= x11
|
x7 ~= x11
|
||||||
x7 = util.ROTL32(x7, 7)
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
// quarterround(x, 0, 5, 10, 15)
|
// quarterround(x, 0, 5, 10, 15)
|
||||||
x0 += x5
|
x0 += x5
|
||||||
x15 ~= x0
|
x15 ~= x0
|
||||||
x15 = util.ROTL32(x15, 16)
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
x10 += x15
|
x10 += x15
|
||||||
x5 ~= x10
|
x5 ~= x10
|
||||||
x5 = util.ROTL32(x5, 12)
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
x0 += x5
|
x0 += x5
|
||||||
x15 ~= x0
|
x15 ~= x0
|
||||||
x15 = util.ROTL32(x15, 8)
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
x10 += x15
|
x10 += x15
|
||||||
x5 ~= x10
|
x5 ~= x10
|
||||||
x5 = util.ROTL32(x5, 7)
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
// quarterround(x, 1, 6, 11, 12)
|
// quarterround(x, 1, 6, 11, 12)
|
||||||
x1 += x6
|
x1 += x6
|
||||||
x12 ~= x1
|
x12 ~= x1
|
||||||
x12 = util.ROTL32(x12, 16)
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
x11 += x12
|
x11 += x12
|
||||||
x6 ~= x11
|
x6 ~= x11
|
||||||
x6 = util.ROTL32(x6, 12)
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
x1 += x6
|
x1 += x6
|
||||||
x12 ~= x1
|
x12 ~= x1
|
||||||
x12 = util.ROTL32(x12, 8)
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
x11 += x12
|
x11 += x12
|
||||||
x6 ~= x11
|
x6 ~= x11
|
||||||
x6 = util.ROTL32(x6, 7)
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
// quarterround(x, 2, 7, 8, 13)
|
// quarterround(x, 2, 7, 8, 13)
|
||||||
x2 += x7
|
x2 += x7
|
||||||
x13 ~= x2
|
x13 ~= x2
|
||||||
x13 = util.ROTL32(x13, 16)
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
x8 += x13
|
x8 += x13
|
||||||
x7 ~= x8
|
x7 ~= x8
|
||||||
x7 = util.ROTL32(x7, 12)
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
x2 += x7
|
x2 += x7
|
||||||
x13 ~= x2
|
x13 ~= x2
|
||||||
x13 = util.ROTL32(x13, 8)
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
x8 += x13
|
x8 += x13
|
||||||
x7 ~= x8
|
x7 ~= x8
|
||||||
x7 = util.ROTL32(x7, 7)
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
// quarterround(x, 3, 4, 9, 14)
|
// quarterround(x, 3, 4, 9, 14)
|
||||||
x3 += x4
|
x3 += x4
|
||||||
x14 ~= x3
|
x14 ~= x3
|
||||||
x14 = util.ROTL32(x14, 16)
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
x9 += x14
|
x9 += x14
|
||||||
x4 ~= x9
|
x4 ~= x9
|
||||||
x4 = util.ROTL32(x4, 12)
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
x3 += x4
|
x3 += x4
|
||||||
x14 ~= x3
|
x14 ~= x3
|
||||||
x14 = util.ROTL32(x14, 8)
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
x9 += x14
|
x9 += x14
|
||||||
x4 ~= x9
|
x4 ~= x9
|
||||||
x4 = util.ROTL32(x4, 7)
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
util.PUT_U32_LE(dst[0:4], x0)
|
endian.unchecked_put_u32le(dst[0:4], x0)
|
||||||
util.PUT_U32_LE(dst[4:8], x1)
|
endian.unchecked_put_u32le(dst[4:8], x1)
|
||||||
util.PUT_U32_LE(dst[8:12], x2)
|
endian.unchecked_put_u32le(dst[8:12], x2)
|
||||||
util.PUT_U32_LE(dst[12:16], x3)
|
endian.unchecked_put_u32le(dst[12:16], x3)
|
||||||
util.PUT_U32_LE(dst[16:20], x12)
|
endian.unchecked_put_u32le(dst[16:20], x12)
|
||||||
util.PUT_U32_LE(dst[20:24], x13)
|
endian.unchecked_put_u32le(dst[20:24], x13)
|
||||||
util.PUT_U32_LE(dst[24:28], x14)
|
endian.unchecked_put_u32le(dst[24:28], x14)
|
||||||
util.PUT_U32_LE(dst[28:32], x15)
|
endian.unchecked_put_u32le(dst[28:32], x15)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package chacha20poly1305
|
|||||||
import "core:crypto"
|
import "core:crypto"
|
||||||
import "core:crypto/chacha20"
|
import "core:crypto/chacha20"
|
||||||
import "core:crypto/poly1305"
|
import "core:crypto/poly1305"
|
||||||
import "core:crypto/util"
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
KEY_SIZE :: chacha20.KEY_SIZE
|
KEY_SIZE :: chacha20.KEY_SIZE
|
||||||
@@ -87,8 +87,8 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
|||||||
// mac_data |= num_to_8_le_bytes(aad.length)
|
// mac_data |= num_to_8_le_bytes(aad.length)
|
||||||
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
||||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
endian.unchecked_put_u64le(l_buf[0:8], u64(aad_len))
|
||||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
endian.unchecked_put_u64le(l_buf[8:16], u64(ciphertext_len))
|
||||||
poly1305.update(&mac_ctx, l_buf)
|
poly1305.update(&mac_ctx, l_buf)
|
||||||
|
|
||||||
// tag = poly1305_mac(mac_data, otk)
|
// tag = poly1305_mac(mac_data, otk)
|
||||||
@@ -128,8 +128,8 @@ decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
|||||||
poly1305.update(&mac_ctx, ciphertext)
|
poly1305.update(&mac_ctx, ciphertext)
|
||||||
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
||||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
endian.unchecked_put_u64le(l_buf[0:8], u64(aad_len))
|
||||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
endian.unchecked_put_u64le(l_buf[8:16], u64(ciphertext_len))
|
||||||
poly1305.update(&mac_ctx, l_buf)
|
poly1305.update(&mac_ctx, l_buf)
|
||||||
|
|
||||||
// tag = poly1305_mac(mac_data, otk)
|
// tag = poly1305_mac(mac_data, otk)
|
||||||
|
|||||||
@@ -1,382 +0,0 @@
|
|||||||
package gost
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:mem"
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 32
|
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Gost_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Gost_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Gost_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc "contextless" (ctx: ^Gost_Context) {
|
|
||||||
sbox: [8][16]u32 = {
|
|
||||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
|
||||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
|
||||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
|
||||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
|
||||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
|
||||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
|
||||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
|
||||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for a := 0; a < 16; a += 1 {
|
|
||||||
ax := sbox[1][a] << 15
|
|
||||||
bx := sbox[3][a] << 23
|
|
||||||
cx := sbox[5][a]
|
|
||||||
cx = (cx >> 1) | (cx << 31)
|
|
||||||
dx := sbox[7][a] << 7
|
|
||||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
|
||||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
|
||||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
|
||||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
|
||||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Gost_Context, data: []byte) {
|
|
||||||
length := byte(len(data))
|
|
||||||
j: byte
|
|
||||||
|
|
||||||
i := ctx.partial_bytes
|
|
||||||
for i < 32 && j < length {
|
|
||||||
ctx.partial[i] = data[j]
|
|
||||||
i, j = i + 1, j + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < 32 {
|
|
||||||
ctx.partial_bytes = i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bytes(ctx, ctx.partial[:], 256)
|
|
||||||
|
|
||||||
for (j + 32) < length {
|
|
||||||
bytes(ctx, data[j:], 256)
|
|
||||||
j += 32
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
for j < length {
|
|
||||||
ctx.partial[i] = data[j]
|
|
||||||
i, j = i + 1, j + 1
|
|
||||||
}
|
|
||||||
ctx.partial_bytes = i
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Gost_Context, hash: []byte) {
|
|
||||||
if ctx.partial_bytes > 0 {
|
|
||||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
|
||||||
bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
compress(ctx.hash[:], ctx.len[:])
|
|
||||||
compress(ctx.hash[:], ctx.sum[:])
|
|
||||||
|
|
||||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
|
||||||
hash[j] = byte(ctx.hash[i])
|
|
||||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
|
||||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
|
||||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GOST implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
Gost_Context :: struct {
|
|
||||||
sum: [8]u32,
|
|
||||||
hash: [8]u32,
|
|
||||||
len: [8]u32,
|
|
||||||
partial: [32]byte,
|
|
||||||
partial_bytes: byte,
|
|
||||||
}
|
|
||||||
|
|
||||||
SBOX_1: [256]u32
|
|
||||||
SBOX_2: [256]u32
|
|
||||||
SBOX_3: [256]u32
|
|
||||||
SBOX_4: [256]u32
|
|
||||||
|
|
||||||
ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
|
||||||
l, r, t := l, r, t
|
|
||||||
t = (k1) + r
|
|
||||||
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
|
||||||
t = (k2) + l
|
|
||||||
r ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
|
||||||
return l, r, t
|
|
||||||
}
|
|
||||||
|
|
||||||
ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
|
||||||
l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
|
||||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
|
||||||
t = r
|
|
||||||
r = l
|
|
||||||
l = t
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
|
||||||
a, c: u32
|
|
||||||
m: [8]u32
|
|
||||||
|
|
||||||
for i, j := 0, 0; i < 8; i += 1 {
|
|
||||||
a = u32(buf[j]) | u32(buf[j + 1]) << 8 | u32(buf[j + 2]) << 16 | u32(buf[j + 3]) << 24
|
|
||||||
j += 4
|
|
||||||
m[i] = a
|
|
||||||
c = a + c + ctx.sum[i]
|
|
||||||
ctx.sum[i] = c
|
|
||||||
c = c < a ? 1 : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
compress(ctx.hash[:], m[:])
|
|
||||||
ctx.len[0] += bits
|
|
||||||
if ctx.len[0] < bits {
|
|
||||||
ctx.len[1] += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compress :: proc(h, m: []u32) {
|
|
||||||
key, u, v, w, s: [8]u32
|
|
||||||
|
|
||||||
copy(u[:], h)
|
|
||||||
copy(v[:], m)
|
|
||||||
|
|
||||||
for i := 0; i < 8; i += 2 {
|
|
||||||
w[0] = u[0] ~ v[0]
|
|
||||||
w[1] = u[1] ~ v[1]
|
|
||||||
w[2] = u[2] ~ v[2]
|
|
||||||
w[3] = u[3] ~ v[3]
|
|
||||||
w[4] = u[4] ~ v[4]
|
|
||||||
w[5] = u[5] ~ v[5]
|
|
||||||
w[6] = u[6] ~ v[6]
|
|
||||||
w[7] = u[7] ~ v[7]
|
|
||||||
|
|
||||||
key[0] = (w[0] & 0x000000ff) | (w[2] & 0x000000ff) << 8 | (w[4] & 0x000000ff) << 16 | (w[6] & 0x000000ff) << 24
|
|
||||||
key[1] = (w[0] & 0x0000ff00) >> 8 | (w[2] & 0x0000ff00) | (w[4] & 0x0000ff00) << 8 | (w[6] & 0x0000ff00) << 16
|
|
||||||
key[2] = (w[0] & 0x00ff0000) >> 16 | (w[2] & 0x00ff0000) >> 8 | (w[4] & 0x00ff0000) | (w[6] & 0x00ff0000) << 8
|
|
||||||
key[3] = (w[0] & 0xff000000) >> 24 | (w[2] & 0xff000000) >> 16 | (w[4] & 0xff000000) >> 8 | (w[6] & 0xff000000)
|
|
||||||
key[4] = (w[1] & 0x000000ff) | (w[3] & 0x000000ff) << 8 | (w[5] & 0x000000ff) << 16 | (w[7] & 0x000000ff) << 24
|
|
||||||
key[5] = (w[1] & 0x0000ff00) >> 8 | (w[3] & 0x0000ff00) | (w[5] & 0x0000ff00) << 8 | (w[7] & 0x0000ff00) << 16
|
|
||||||
key[6] = (w[1] & 0x00ff0000) >> 16 | (w[3] & 0x00ff0000) >> 8 | (w[5] & 0x00ff0000) | (w[7] & 0x00ff0000) << 8
|
|
||||||
key[7] = (w[1] & 0xff000000) >> 24 | (w[3] & 0xff000000) >> 16 | (w[5] & 0xff000000) >> 8 | (w[7] & 0xff000000)
|
|
||||||
|
|
||||||
r := h[i]
|
|
||||||
l := h[i + 1]
|
|
||||||
t: u32
|
|
||||||
l, r, t = ENCRYPT(l, r, 0, key[:])
|
|
||||||
|
|
||||||
s[i] = r
|
|
||||||
s[i + 1] = l
|
|
||||||
|
|
||||||
if i == 6 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
l = u[0] ~ u[2]
|
|
||||||
r = u[1] ~ u[3]
|
|
||||||
u[0] = u[2]
|
|
||||||
u[1] = u[3]
|
|
||||||
u[2] = u[4]
|
|
||||||
u[3] = u[5]
|
|
||||||
u[4] = u[6]
|
|
||||||
u[5] = u[7]
|
|
||||||
u[6] = l
|
|
||||||
u[7] = r
|
|
||||||
|
|
||||||
if i == 2 {
|
|
||||||
u[0] ~= 0xff00ff00
|
|
||||||
u[1] ~= 0xff00ff00
|
|
||||||
u[2] ~= 0x00ff00ff
|
|
||||||
u[3] ~= 0x00ff00ff
|
|
||||||
u[4] ~= 0x00ffff00
|
|
||||||
u[5] ~= 0xff0000ff
|
|
||||||
u[6] ~= 0x000000ff
|
|
||||||
u[7] ~= 0xff00ffff
|
|
||||||
}
|
|
||||||
|
|
||||||
l = v[0]
|
|
||||||
r = v[2]
|
|
||||||
v[0] = v[4]
|
|
||||||
v[2] = v[6]
|
|
||||||
v[4] = l ~ r
|
|
||||||
v[6] = v[0] ~ r
|
|
||||||
l = v[1]
|
|
||||||
r = v[3]
|
|
||||||
v[1] = v[5]
|
|
||||||
v[3] = v[7]
|
|
||||||
v[5] = l ~ r
|
|
||||||
v[7] = v[1] ~ r
|
|
||||||
}
|
|
||||||
|
|
||||||
u[0] = m[0] ~ s[6]
|
|
||||||
u[1] = m[1] ~ s[7]
|
|
||||||
u[2] = m[2] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff) ~
|
|
||||||
(s[1] & 0xffff) ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[6] ~ (s[6] << 16) ~
|
|
||||||
(s[7] & 0xffff0000) ~ (s[7] >> 16)
|
|
||||||
u[3] = m[3] ~ (s[0] & 0xffff) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
|
||||||
(s[1] << 16) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
|
||||||
(s[3] << 16) ~ s[6] ~ (s[6] << 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
|
||||||
(s[7] << 16) ~ (s[7] >> 16)
|
|
||||||
u[4] = m[4] ~
|
|
||||||
(s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[0] >> 16) ~
|
|
||||||
(s[1] & 0xffff0000) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
|
||||||
(s[3] << 16) ~ (s[3] >> 16) ~ (s[4] << 16) ~ (s[6] << 16) ~
|
|
||||||
(s[6] >> 16) ~(s[7] & 0xffff) ~ (s[7] << 16) ~ (s[7] >> 16)
|
|
||||||
u[5] = m[5] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff0000) ~
|
|
||||||
(s[1] & 0xffff) ~ s[2] ~ (s[2] >> 16) ~ (s[3] << 16) ~ (s[3] >> 16) ~
|
|
||||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[6] << 16) ~
|
|
||||||
(s[6] >> 16) ~ (s[7] & 0xffff0000) ~ (s[7] << 16) ~ (s[7] >> 16)
|
|
||||||
u[6] = m[6] ~ s[0] ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[3] ~ (s[3] >> 16) ~
|
|
||||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[5] >> 16) ~ s[6] ~
|
|
||||||
(s[6] << 16) ~ (s[6] >> 16) ~ (s[7] << 16)
|
|
||||||
u[7] = m[7] ~ (s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
|
||||||
(s[1] << 16) ~ (s[2] >> 16) ~ (s[3] << 16) ~ s[4] ~ (s[4] >> 16) ~
|
|
||||||
(s[5] << 16) ~ (s[5] >> 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
|
||||||
(s[7] << 16) ~ (s[7] >> 16)
|
|
||||||
|
|
||||||
v[0] = h[0] ~ (u[1] << 16) ~ (u[0] >> 16)
|
|
||||||
v[1] = h[1] ~ (u[2] << 16) ~ (u[1] >> 16)
|
|
||||||
v[2] = h[2] ~ (u[3] << 16) ~ (u[2] >> 16)
|
|
||||||
v[3] = h[3] ~ (u[4] << 16) ~ (u[3] >> 16)
|
|
||||||
v[4] = h[4] ~ (u[5] << 16) ~ (u[4] >> 16)
|
|
||||||
v[5] = h[5] ~ (u[6] << 16) ~ (u[5] >> 16)
|
|
||||||
v[6] = h[6] ~ (u[7] << 16) ~ (u[6] >> 16)
|
|
||||||
v[7] = h[7] ~ (u[0] & 0xffff0000) ~ (u[0] << 16) ~ (u[7] >> 16) ~ (u[1] & 0xffff0000) ~ (u[1] << 16) ~ (u[6] << 16) ~ (u[7] & 0xffff0000)
|
|
||||||
|
|
||||||
h[0] = (v[0] & 0xffff0000) ~ (v[0] << 16) ~ (v[0] >> 16) ~ (v[1] >> 16) ~
|
|
||||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ (v[4] << 16) ~
|
|
||||||
(v[5] >> 16) ~ v[5] ~ (v[6] >> 16) ~ (v[7] << 16) ~ (v[7] >> 16) ~
|
|
||||||
(v[7] & 0xffff)
|
|
||||||
h[1] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~ (v[1] & 0xffff) ~
|
|
||||||
v[2] ~ (v[2] >> 16) ~ (v[3] << 16) ~ (v[4] >> 16) ~ (v[5] << 16) ~
|
|
||||||
(v[6] << 16) ~ v[6] ~ (v[7] & 0xffff0000) ~ (v[7] >> 16)
|
|
||||||
h[2] = (v[0] & 0xffff) ~ (v[0] << 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
|
||||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~
|
|
||||||
(v[5] >> 16) ~ v[6] ~ (v[6] >> 16) ~ (v[7] & 0xffff) ~ (v[7] << 16) ~
|
|
||||||
(v[7] >> 16)
|
|
||||||
h[3] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~
|
|
||||||
(v[1] & 0xffff0000) ~ (v[1] >> 16) ~ (v[2] << 16) ~ (v[2] >> 16) ~ v[2] ~
|
|
||||||
(v[3] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
|
||||||
(v[7] & 0xffff) ~ (v[7] >> 16)
|
|
||||||
h[4] = (v[0] >> 16) ~ (v[1] << 16) ~ v[1] ~ (v[2] >> 16) ~ v[2] ~
|
|
||||||
(v[3] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ (v[5] >> 16) ~
|
|
||||||
v[5] ~ (v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16)
|
|
||||||
h[5] = (v[0] << 16) ~ (v[0] & 0xffff0000) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
|
||||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ v[2] ~ (v[3] >> 16) ~ v[3] ~
|
|
||||||
(v[4] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
|
||||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ (v[7] >> 16) ~ (v[7] & 0xffff0000)
|
|
||||||
h[6] = v[0] ~ v[2] ~ (v[2] >> 16) ~ v[3] ~ (v[3] << 16) ~ v[4] ~
|
|
||||||
(v[4] >> 16) ~ (v[5] << 16) ~ (v[5] >> 16) ~ v[5] ~ (v[6] << 16) ~
|
|
||||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ v[7]
|
|
||||||
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
|
|
||||||
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
|
|
||||||
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
|
|
||||||
}
|
|
||||||
@@ -1,653 +0,0 @@
|
|||||||
package groestl
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE_224 :: 28
|
|
||||||
DIGEST_SIZE_256 :: 32
|
|
||||||
DIGEST_SIZE_384 :: 48
|
|
||||||
DIGEST_SIZE_512 :: 64
|
|
||||||
|
|
||||||
// hash_string_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
|
||||||
return hash_bytes_224(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 224
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 224
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_224 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 224
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_224 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_224(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_224(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_224]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_224 :: proc {
|
|
||||||
hash_stream_224,
|
|
||||||
hash_file_224,
|
|
||||||
hash_bytes_224,
|
|
||||||
hash_string_224,
|
|
||||||
hash_bytes_to_buffer_224,
|
|
||||||
hash_string_to_buffer_224,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
|
||||||
return hash_bytes_256(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 256
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 256
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_256 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 256
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_256 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_256(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_256(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_256]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_256 :: proc {
|
|
||||||
hash_stream_256,
|
|
||||||
hash_file_256,
|
|
||||||
hash_bytes_256,
|
|
||||||
hash_string_256,
|
|
||||||
hash_bytes_to_buffer_256,
|
|
||||||
hash_string_to_buffer_256,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
|
||||||
return hash_bytes_384(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 384
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 384
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_384 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 384
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_384 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_384(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_384(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_384]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_384 :: proc {
|
|
||||||
hash_stream_384,
|
|
||||||
hash_file_384,
|
|
||||||
hash_bytes_384,
|
|
||||||
hash_string_384,
|
|
||||||
hash_bytes_to_buffer_384,
|
|
||||||
hash_string_to_buffer_384,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
|
||||||
return hash_bytes_512(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 512
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 512
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_512 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: Groestl_Context
|
|
||||||
ctx.hashbitlen = 512
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_512 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_512(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_512(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_512]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_512 :: proc {
|
|
||||||
hash_stream_512,
|
|
||||||
hash_file_512,
|
|
||||||
hash_bytes_512,
|
|
||||||
hash_string_512,
|
|
||||||
hash_bytes_to_buffer_512,
|
|
||||||
hash_string_to_buffer_512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc(ctx: ^Groestl_Context) {
|
|
||||||
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
|
|
||||||
if ctx.hashbitlen <= 256 {
|
|
||||||
ctx.rounds = 10
|
|
||||||
ctx.columns = 8
|
|
||||||
ctx.statesize = 64
|
|
||||||
} else {
|
|
||||||
ctx.rounds = 14
|
|
||||||
ctx.columns = 16
|
|
||||||
ctx.statesize = 128
|
|
||||||
}
|
|
||||||
for i := 8 - size_of(i32); i < 8; i += 1 {
|
|
||||||
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Groestl_Context, data: []byte) {
|
|
||||||
databitlen := len(data) * 8
|
|
||||||
msglen := databitlen / 8
|
|
||||||
rem := databitlen % 8
|
|
||||||
|
|
||||||
i: int
|
|
||||||
assert(ctx.bits_in_last_byte == 0)
|
|
||||||
|
|
||||||
if ctx.buf_ptr != 0 {
|
|
||||||
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
|
|
||||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.buf_ptr < ctx.statesize {
|
|
||||||
if rem != 0 {
|
|
||||||
ctx.bits_in_last_byte = rem
|
|
||||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
|
||||||
ctx.buf_ptr += 1
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.buf_ptr = 0
|
|
||||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
|
||||||
}
|
|
||||||
|
|
||||||
transform(ctx, data[i:], u32(msglen - i))
|
|
||||||
i += ((msglen - i) / ctx.statesize) * ctx.statesize
|
|
||||||
for i < msglen {
|
|
||||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
|
||||||
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if rem != 0 {
|
|
||||||
ctx.bits_in_last_byte = rem
|
|
||||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
|
||||||
ctx.buf_ptr += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Groestl_Context, hash: []byte) {
|
|
||||||
hashbytelen := ctx.hashbitlen / 8
|
|
||||||
|
|
||||||
if ctx.bits_in_last_byte != 0 {
|
|
||||||
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
|
|
||||||
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
|
|
||||||
} else {
|
|
||||||
ctx.buffer[ctx.buf_ptr] = 0x80
|
|
||||||
ctx.buf_ptr += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.buf_ptr > ctx.statesize - 8 {
|
|
||||||
for ctx.buf_ptr < ctx.statesize {
|
|
||||||
ctx.buffer[ctx.buf_ptr] = 0
|
|
||||||
ctx.buf_ptr += 1
|
|
||||||
}
|
|
||||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
|
||||||
ctx.buf_ptr = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
|
||||||
ctx.buffer[ctx.buf_ptr] = 0
|
|
||||||
ctx.buf_ptr += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.block_counter += 1
|
|
||||||
ctx.buf_ptr = ctx.statesize
|
|
||||||
|
|
||||||
for ctx.buf_ptr > ctx.statesize - 8 {
|
|
||||||
ctx.buf_ptr -= 1
|
|
||||||
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
|
|
||||||
ctx.block_counter >>= 8
|
|
||||||
}
|
|
||||||
|
|
||||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
|
||||||
output_transformation(ctx)
|
|
||||||
|
|
||||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
|
||||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GROESTL implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
SBOX := [256]byte {
|
|
||||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
|
||||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
|
||||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
|
||||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
|
||||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
|
||||||
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
|
||||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
|
||||||
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
|
||||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
|
||||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
|
||||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
|
||||||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
|
||||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
|
||||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
|
||||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
|
||||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
|
||||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
|
||||||
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
|
||||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
|
||||||
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
|
||||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
|
||||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
|
||||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
|
||||||
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
|
||||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
|
||||||
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
|
||||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
|
||||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
|
||||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
|
||||||
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
|
||||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
|
||||||
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
|
||||||
}
|
|
||||||
|
|
||||||
SHIFT := [2][2][8]int {
|
|
||||||
{{0, 1, 2, 3, 4, 5, 6, 7}, {1, 3, 5, 7, 0, 2, 4, 6}},
|
|
||||||
{{0, 1, 2, 3, 4, 5, 6, 11}, {1, 3, 5, 11, 0, 2, 4, 6}},
|
|
||||||
}
|
|
||||||
|
|
||||||
Groestl_Context :: struct {
|
|
||||||
chaining: [8][16]byte,
|
|
||||||
block_counter: u64,
|
|
||||||
hashbitlen: int,
|
|
||||||
buffer: [128]byte,
|
|
||||||
buf_ptr: int,
|
|
||||||
bits_in_last_byte: int,
|
|
||||||
columns: int,
|
|
||||||
rounds: int,
|
|
||||||
statesize: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
Groestl_Variant :: enum {
|
|
||||||
P512 = 0,
|
|
||||||
Q512 = 1,
|
|
||||||
P1024 = 2,
|
|
||||||
Q1024 = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
MUL2 :: #force_inline proc "contextless"(b: byte) -> byte {
|
|
||||||
return (b >> 7) != 0 ? (b << 1) ~ 0x1b : (b << 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
MUL3 :: #force_inline proc "contextless"(b: byte) -> byte {
|
|
||||||
return MUL2(b) ~ b
|
|
||||||
}
|
|
||||||
|
|
||||||
MUL4 :: #force_inline proc "contextless"(b: byte) -> byte {
|
|
||||||
return MUL2(MUL2(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
MUL5 :: #force_inline proc "contextless"(b: byte) -> byte {
|
|
||||||
return MUL4(b) ~ b
|
|
||||||
}
|
|
||||||
|
|
||||||
MUL6 :: #force_inline proc "contextless"(b: byte) -> byte {
|
|
||||||
return MUL4(b) ~ MUL2(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
MUL7 :: #force_inline proc "contextless"(b: byte) -> byte {
|
|
||||||
return MUL4(b) ~ MUL2(b) ~ b
|
|
||||||
}
|
|
||||||
|
|
||||||
sub_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
|
||||||
for i := 0; i < 8; i += 1 {
|
|
||||||
for j := 0; j < columns; j += 1 {
|
|
||||||
x[i][j] = SBOX[x[i][j]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shift_bytes :: #force_inline proc (x: [][16]byte, columns: int, v: Groestl_Variant) {
|
|
||||||
temp: [16]byte
|
|
||||||
R := &SHIFT[int(v) / 2][int(v) & 1]
|
|
||||||
|
|
||||||
for i := 0; i < 8; i += 1 {
|
|
||||||
for j := 0; j < columns; j += 1 {
|
|
||||||
temp[j] = x[i][(j + R[i]) % columns]
|
|
||||||
}
|
|
||||||
for j := 0; j < columns; j += 1 {
|
|
||||||
x[i][j] = temp[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mix_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
|
||||||
temp: [8]byte
|
|
||||||
|
|
||||||
for i := 0; i < columns; i += 1 {
|
|
||||||
for j := 0; j < 8; j += 1 {
|
|
||||||
temp[j] = MUL2(x[(j + 0) % 8][i]) ~
|
|
||||||
MUL2(x[(j + 1) % 8][i]) ~
|
|
||||||
MUL3(x[(j + 2) % 8][i]) ~
|
|
||||||
MUL4(x[(j + 3) % 8][i]) ~
|
|
||||||
MUL5(x[(j + 4) % 8][i]) ~
|
|
||||||
MUL3(x[(j + 5) % 8][i]) ~
|
|
||||||
MUL5(x[(j + 6) % 8][i]) ~
|
|
||||||
MUL7(x[(j + 7) % 8][i])
|
|
||||||
}
|
|
||||||
for j := 0; j < 8; j += 1 {
|
|
||||||
x[j][i] = temp[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
|
||||||
v := ctx.columns == 8 ? Groestl_Variant.P512 : Groestl_Variant.P1024
|
|
||||||
for i := 0; i < ctx.rounds; i += 1 {
|
|
||||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
|
||||||
sub_bytes(x, ctx.columns)
|
|
||||||
shift_bytes(x, ctx.columns, v)
|
|
||||||
mix_bytes(x, ctx.columns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
q :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
|
||||||
v := ctx.columns == 8 ? Groestl_Variant.Q512 : Groestl_Variant.Q1024
|
|
||||||
for i := 0; i < ctx.rounds; i += 1 {
|
|
||||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
|
||||||
sub_bytes(x, ctx.columns)
|
|
||||||
shift_bytes(x, ctx.columns, v)
|
|
||||||
mix_bytes(x, ctx.columns)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transform :: proc(ctx: ^Groestl_Context, input: []byte, msglen: u32) {
|
|
||||||
tmp1, tmp2: [8][16]byte
|
|
||||||
input, msglen := input, msglen
|
|
||||||
|
|
||||||
for msglen >= u32(ctx.statesize) {
|
|
||||||
for i := 0; i < 8; i += 1 {
|
|
||||||
for j := 0; j < ctx.columns; j += 1 {
|
|
||||||
tmp1[i][j] = ctx.chaining[i][j] ~ input[j * 8 + i]
|
|
||||||
tmp2[i][j] = input[j * 8 + i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p(ctx, tmp1[:])
|
|
||||||
q(ctx, tmp2[:])
|
|
||||||
|
|
||||||
for i := 0; i < 8; i += 1 {
|
|
||||||
for j := 0; j < ctx.columns; j += 1 {
|
|
||||||
ctx.chaining[i][j] ~= tmp1[i][j] ~ tmp2[i][j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.block_counter += 1
|
|
||||||
msglen -= u32(ctx.statesize)
|
|
||||||
input = input[ctx.statesize:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output_transformation :: proc(ctx: ^Groestl_Context) {
|
|
||||||
temp: [8][16]byte
|
|
||||||
|
|
||||||
for i := 0; i < 8; i += 1 {
|
|
||||||
for j := 0; j < ctx.columns; j += 1 {
|
|
||||||
temp[i][j] = ctx.chaining[i][j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p(ctx, temp[:])
|
|
||||||
|
|
||||||
for i := 0; i < 8; i += 1 {
|
|
||||||
for j := 0; j < ctx.columns; j += 1 {
|
|
||||||
ctx.chaining[i][j] ~= temp[i][j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_Variant) {
|
|
||||||
switch (i32(v) & 1) {
|
|
||||||
case 0:
|
|
||||||
for i := 0; i < columns; i += 1 {
|
|
||||||
x[0][i] ~= byte(i << 4) ~ round
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
for i := 0; i < columns; i += 1 {
|
|
||||||
for j := 0; j < 7; j += 1 {
|
|
||||||
x[j][i] ~= 0xff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < columns; i += 1 {
|
|
||||||
x[7][i] ~= byte(i << 4) ~ 0xff ~ round
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
package hash provides a generic interface to the supported hash algorithms.
|
||||||
|
|
||||||
|
A high-level convenience procedure group `hash` is provided to easily
|
||||||
|
accomplish common tasks.
|
||||||
|
- `hash_string` - Hash a given string and return the digest.
|
||||||
|
- `hash_bytes` - Hash a given byte slice and return the digest.
|
||||||
|
- `hash_string_to_buffer` - Hash a given string and put the digest in
|
||||||
|
the third parameter. It requires that the destination buffer
|
||||||
|
is at least as big as the digest size.
|
||||||
|
- `hash_bytes_to_buffer` - Hash a given string and put the computed
|
||||||
|
digest in the third parameter. It requires that the destination
|
||||||
|
buffer is at least as big as the digest size.
|
||||||
|
- `hash_stream` - Incrementally fully consume a `io.Stream`, and return
|
||||||
|
the computed digest.
|
||||||
|
- `hash_file` - Takes a file handle and returns the computed digest.
|
||||||
|
A third optional boolean parameter controls if the file is streamed
|
||||||
|
(default), or or read at once.
|
||||||
|
|
||||||
|
```odin
|
||||||
|
package hash_example
|
||||||
|
|
||||||
|
import "core:crypto/hash"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
input := "Feed the fire."
|
||||||
|
|
||||||
|
// Compute the digest, using the high level API.
|
||||||
|
returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
|
||||||
|
defer delete(returned_digest)
|
||||||
|
|
||||||
|
// Variant that takes a destination buffer, instead of returning
|
||||||
|
// the digest.
|
||||||
|
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
|
||||||
|
defer delete(digest)
|
||||||
|
hash.hash(hash.Algorithm.BLAKE2B, input, digest)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A generic low level API is provided supporting the init/update/final interface
|
||||||
|
that is typical with cryptographic hash function implementations.
|
||||||
|
|
||||||
|
```odin
|
||||||
|
package hash_example
|
||||||
|
|
||||||
|
import "core:crypto/hash"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
input := "Let the cinders burn."
|
||||||
|
|
||||||
|
// Compute the digest, using the low level API.
|
||||||
|
ctx: hash.Context
|
||||||
|
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
|
||||||
|
defer delete(digest)
|
||||||
|
|
||||||
|
hash.init(&ctx, hash.Algorithm.SHA3_512)
|
||||||
|
hash.update(&ctx, transmute([]byte)input)
|
||||||
|
hash.final(&ctx, digest)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
package crypto_hash
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package crypto_hash
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2021 zhibog
|
||||||
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
zhibog, dotbmp: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
// hash_bytes will hash the given input and return the computed digest
|
||||||
|
// in a newly allocated slice.
|
||||||
|
hash_string :: proc(algorithm: Algorithm, data: string, allocator := context.allocator) -> []byte {
|
||||||
|
return hash_bytes(algorithm, transmute([]byte)(data), allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash_bytes will hash the given input and return the computed digest
|
||||||
|
// in a newly allocated slice.
|
||||||
|
hash_bytes :: proc(algorithm: Algorithm, data: []byte, allocator := context.allocator) -> []byte {
|
||||||
|
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
|
||||||
|
hash_bytes_to_buffer(algorithm, data, dst)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash_string_to_buffer will hash the given input and assign the
|
||||||
|
// computed digest to the third parameter. It requires that the
|
||||||
|
// destination buffer is at least as big as the digest size.
|
||||||
|
hash_string_to_buffer :: proc(algorithm: Algorithm, data: string, hash: []byte) {
|
||||||
|
hash_bytes_to_buffer(algorithm, transmute([]byte)(data), hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash_bytes_to_buffer will hash the given input and write the
|
||||||
|
// computed digest into the third parameter. It requires that the
|
||||||
|
// destination buffer is at least as big as the digest size.
|
||||||
|
hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) {
|
||||||
|
ctx: Context
|
||||||
|
|
||||||
|
init(&ctx, algorithm)
|
||||||
|
update(&ctx, data)
|
||||||
|
final(&ctx, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash_stream will incrementally fully consume a stream, and return the
|
||||||
|
// computed digest in a newly allocated slice.
|
||||||
|
hash_stream :: proc(
|
||||||
|
algorithm: Algorithm,
|
||||||
|
s: io.Stream,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) -> (
|
||||||
|
[]byte,
|
||||||
|
io.Error,
|
||||||
|
) {
|
||||||
|
ctx: Context
|
||||||
|
|
||||||
|
buf: [MAX_BLOCK_SIZE * 4]byte
|
||||||
|
defer mem.zero_explicit(&buf, size_of(buf))
|
||||||
|
|
||||||
|
init(&ctx, algorithm)
|
||||||
|
|
||||||
|
loop: for {
|
||||||
|
n, err := io.read(s, buf[:])
|
||||||
|
if n > 0 {
|
||||||
|
// XXX/yawning: Can io.read return n > 0 and EOF?
|
||||||
|
update(&ctx, buf[:n])
|
||||||
|
}
|
||||||
|
#partial switch err {
|
||||||
|
case .None:
|
||||||
|
case .EOF:
|
||||||
|
break loop
|
||||||
|
case:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
|
||||||
|
final(&ctx, dst)
|
||||||
|
|
||||||
|
return dst, io.Error.None
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash_file will read the file provided by the given handle and return the
|
||||||
|
// computed digest in a newly allocated slice.
|
||||||
|
hash_file :: proc(
|
||||||
|
algorithm: Algorithm,
|
||||||
|
hd: os.Handle,
|
||||||
|
load_at_once := false,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) -> (
|
||||||
|
[]byte,
|
||||||
|
io.Error,
|
||||||
|
) {
|
||||||
|
if !load_at_once {
|
||||||
|
return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, ok := os.read_entire_file(hd, allocator)
|
||||||
|
if !ok {
|
||||||
|
return nil, io.Error.Unknown
|
||||||
|
}
|
||||||
|
defer delete(buf, allocator)
|
||||||
|
|
||||||
|
return hash_bytes(algorithm, buf, allocator), io.Error.None
|
||||||
|
}
|
||||||
|
|
||||||
|
hash :: proc {
|
||||||
|
hash_stream,
|
||||||
|
hash_file,
|
||||||
|
hash_bytes,
|
||||||
|
hash_string,
|
||||||
|
hash_bytes_to_buffer,
|
||||||
|
hash_string_to_buffer,
|
||||||
|
}
|
||||||
@@ -0,0 +1,353 @@
|
|||||||
|
package crypto_hash
|
||||||
|
|
||||||
|
import "core:crypto/blake2b"
|
||||||
|
import "core:crypto/blake2s"
|
||||||
|
import "core:crypto/sha2"
|
||||||
|
import "core:crypto/sha3"
|
||||||
|
import "core:crypto/sm3"
|
||||||
|
import "core:crypto/legacy/keccak"
|
||||||
|
import "core:crypto/legacy/md5"
|
||||||
|
import "core:crypto/legacy/sha1"
|
||||||
|
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
|
// MAX_DIGEST_SIZE is the maximum size digest that can be returned by any
|
||||||
|
// of the Algorithms supported via this package.
|
||||||
|
MAX_DIGEST_SIZE :: 64
|
||||||
|
// MAX_BLOCK_SIZE is the maximum block size used by any of Algorithms
|
||||||
|
// supported by this package.
|
||||||
|
MAX_BLOCK_SIZE :: sha3.BLOCK_SIZE_224
|
||||||
|
|
||||||
|
// Algorithm is the algorithm identifier associated with a given Context.
|
||||||
|
Algorithm :: enum {
|
||||||
|
Invalid,
|
||||||
|
BLAKE2B,
|
||||||
|
BLAKE2S,
|
||||||
|
SHA224,
|
||||||
|
SHA256,
|
||||||
|
SHA384,
|
||||||
|
SHA512,
|
||||||
|
SHA512_256,
|
||||||
|
SHA3_224,
|
||||||
|
SHA3_256,
|
||||||
|
SHA3_384,
|
||||||
|
SHA3_512,
|
||||||
|
SM3,
|
||||||
|
Legacy_KECCAK_224,
|
||||||
|
Legacy_KECCAK_256,
|
||||||
|
Legacy_KECCAK_384,
|
||||||
|
Legacy_KECCAK_512,
|
||||||
|
Insecure_MD5,
|
||||||
|
Insecure_SHA1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALGORITHM_NAMES is the Algorithm to algorithm name string.
|
||||||
|
ALGORITHM_NAMES := [Algorithm]string {
|
||||||
|
.Invalid = "Invalid",
|
||||||
|
.BLAKE2B = "BLAKE2b",
|
||||||
|
.BLAKE2S = "BLAKE2s",
|
||||||
|
.SHA224 = "SHA-224",
|
||||||
|
.SHA256 = "SHA-256",
|
||||||
|
.SHA384 = "SHA-384",
|
||||||
|
.SHA512 = "SHA-512",
|
||||||
|
.SHA512_256 = "SHA-512/256",
|
||||||
|
.SHA3_224 = "SHA3-224",
|
||||||
|
.SHA3_256 = "SHA3-256",
|
||||||
|
.SHA3_384 = "SHA3-384",
|
||||||
|
.SHA3_512 = "SHA3-512",
|
||||||
|
.SM3 = "SM3",
|
||||||
|
.Legacy_KECCAK_224 = "Keccak-224",
|
||||||
|
.Legacy_KECCAK_256 = "Keccak-256",
|
||||||
|
.Legacy_KECCAK_384 = "Keccak-384",
|
||||||
|
.Legacy_KECCAK_512 = "Keccak-512",
|
||||||
|
.Insecure_MD5 = "MD5",
|
||||||
|
.Insecure_SHA1 = "SHA-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
// DIGEST_SIZES is the Algorithm to digest size in bytes.
|
||||||
|
DIGEST_SIZES := [Algorithm]int {
|
||||||
|
.Invalid = 0,
|
||||||
|
.BLAKE2B = blake2b.DIGEST_SIZE,
|
||||||
|
.BLAKE2S = blake2s.DIGEST_SIZE,
|
||||||
|
.SHA224 = sha2.DIGEST_SIZE_224,
|
||||||
|
.SHA256 = sha2.DIGEST_SIZE_256,
|
||||||
|
.SHA384 = sha2.DIGEST_SIZE_384,
|
||||||
|
.SHA512 = sha2.DIGEST_SIZE_512,
|
||||||
|
.SHA512_256 = sha2.DIGEST_SIZE_512_256,
|
||||||
|
.SHA3_224 = sha3.DIGEST_SIZE_224,
|
||||||
|
.SHA3_256 = sha3.DIGEST_SIZE_256,
|
||||||
|
.SHA3_384 = sha3.DIGEST_SIZE_384,
|
||||||
|
.SHA3_512 = sha3.DIGEST_SIZE_512,
|
||||||
|
.SM3 = sm3.DIGEST_SIZE,
|
||||||
|
.Legacy_KECCAK_224 = keccak.DIGEST_SIZE_224,
|
||||||
|
.Legacy_KECCAK_256 = keccak.DIGEST_SIZE_256,
|
||||||
|
.Legacy_KECCAK_384 = keccak.DIGEST_SIZE_384,
|
||||||
|
.Legacy_KECCAK_512 = keccak.DIGEST_SIZE_512,
|
||||||
|
.Insecure_MD5 = md5.DIGEST_SIZE,
|
||||||
|
.Insecure_SHA1 = sha1.DIGEST_SIZE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// BLOCK_SIZES is the Algoritm to block size in bytes.
|
||||||
|
BLOCK_SIZES := [Algorithm]int {
|
||||||
|
.Invalid = 0,
|
||||||
|
.BLAKE2B = blake2b.BLOCK_SIZE,
|
||||||
|
.BLAKE2S = blake2s.BLOCK_SIZE,
|
||||||
|
.SHA224 = sha2.BLOCK_SIZE_256,
|
||||||
|
.SHA256 = sha2.BLOCK_SIZE_256,
|
||||||
|
.SHA384 = sha2.BLOCK_SIZE_512,
|
||||||
|
.SHA512 = sha2.BLOCK_SIZE_512,
|
||||||
|
.SHA512_256 = sha2.BLOCK_SIZE_512,
|
||||||
|
.SHA3_224 = sha3.BLOCK_SIZE_224,
|
||||||
|
.SHA3_256 = sha3.BLOCK_SIZE_256,
|
||||||
|
.SHA3_384 = sha3.BLOCK_SIZE_384,
|
||||||
|
.SHA3_512 = sha3.BLOCK_SIZE_512,
|
||||||
|
.SM3 = sm3.BLOCK_SIZE,
|
||||||
|
.Legacy_KECCAK_224 = keccak.BLOCK_SIZE_224,
|
||||||
|
.Legacy_KECCAK_256 = keccak.BLOCK_SIZE_256,
|
||||||
|
.Legacy_KECCAK_384 = keccak.BLOCK_SIZE_384,
|
||||||
|
.Legacy_KECCAK_512 = keccak.BLOCK_SIZE_512,
|
||||||
|
.Insecure_MD5 = md5.BLOCK_SIZE,
|
||||||
|
.Insecure_SHA1 = sha1.BLOCK_SIZE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is a concrete instantiation of a specific hash algorithm.
|
||||||
|
Context :: struct {
|
||||||
|
_algo: Algorithm,
|
||||||
|
_impl: union {
|
||||||
|
blake2b.Context,
|
||||||
|
blake2s.Context,
|
||||||
|
sha2.Context_256,
|
||||||
|
sha2.Context_512,
|
||||||
|
sha3.Context,
|
||||||
|
sm3.Context,
|
||||||
|
keccak.Context,
|
||||||
|
md5.Context,
|
||||||
|
sha1.Context,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_IMPL_IDS := [Algorithm]typeid {
|
||||||
|
.Invalid = nil,
|
||||||
|
.BLAKE2B = typeid_of(blake2b.Context),
|
||||||
|
.BLAKE2S = typeid_of(blake2s.Context),
|
||||||
|
.SHA224 = typeid_of(sha2.Context_256),
|
||||||
|
.SHA256 = typeid_of(sha2.Context_256),
|
||||||
|
.SHA384 = typeid_of(sha2.Context_512),
|
||||||
|
.SHA512 = typeid_of(sha2.Context_512),
|
||||||
|
.SHA512_256 = typeid_of(sha2.Context_512),
|
||||||
|
.SHA3_224 = typeid_of(sha3.Context),
|
||||||
|
.SHA3_256 = typeid_of(sha3.Context),
|
||||||
|
.SHA3_384 = typeid_of(sha3.Context),
|
||||||
|
.SHA3_512 = typeid_of(sha3.Context),
|
||||||
|
.SM3 = typeid_of(sm3.Context),
|
||||||
|
.Legacy_KECCAK_224 = typeid_of(keccak.Context),
|
||||||
|
.Legacy_KECCAK_256 = typeid_of(keccak.Context),
|
||||||
|
.Legacy_KECCAK_384 = typeid_of(keccak.Context),
|
||||||
|
.Legacy_KECCAK_512 = typeid_of(keccak.Context),
|
||||||
|
.Insecure_MD5 = typeid_of(md5.Context),
|
||||||
|
.Insecure_SHA1 = typeid_of(sha1.Context),
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context with a specific hash Algorithm.
|
||||||
|
init :: proc(ctx: ^Context, algorithm: Algorithm) {
|
||||||
|
if ctx._impl != nil {
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directly specialize the union by setting the type ID (save a copy).
|
||||||
|
reflect.set_union_variant_typeid(
|
||||||
|
ctx._impl,
|
||||||
|
_IMPL_IDS[algorithm],
|
||||||
|
)
|
||||||
|
switch algorithm {
|
||||||
|
case .BLAKE2B:
|
||||||
|
blake2b.init(&ctx._impl.(blake2b.Context))
|
||||||
|
case .BLAKE2S:
|
||||||
|
blake2s.init(&ctx._impl.(blake2s.Context))
|
||||||
|
case .SHA224:
|
||||||
|
sha2.init_224(&ctx._impl.(sha2.Context_256))
|
||||||
|
case .SHA256:
|
||||||
|
sha2.init_256(&ctx._impl.(sha2.Context_256))
|
||||||
|
case .SHA384:
|
||||||
|
sha2.init_384(&ctx._impl.(sha2.Context_512))
|
||||||
|
case .SHA512:
|
||||||
|
sha2.init_512(&ctx._impl.(sha2.Context_512))
|
||||||
|
case .SHA512_256:
|
||||||
|
sha2.init_512_256(&ctx._impl.(sha2.Context_512))
|
||||||
|
case .SHA3_224:
|
||||||
|
sha3.init_224(&ctx._impl.(sha3.Context))
|
||||||
|
case .SHA3_256:
|
||||||
|
sha3.init_256(&ctx._impl.(sha3.Context))
|
||||||
|
case .SHA3_384:
|
||||||
|
sha3.init_384(&ctx._impl.(sha3.Context))
|
||||||
|
case .SHA3_512:
|
||||||
|
sha3.init_512(&ctx._impl.(sha3.Context))
|
||||||
|
case .SM3:
|
||||||
|
sm3.init(&ctx._impl.(sm3.Context))
|
||||||
|
case .Legacy_KECCAK_224:
|
||||||
|
keccak.init_224(&ctx._impl.(keccak.Context))
|
||||||
|
case .Legacy_KECCAK_256:
|
||||||
|
keccak.init_256(&ctx._impl.(keccak.Context))
|
||||||
|
case .Legacy_KECCAK_384:
|
||||||
|
keccak.init_384(&ctx._impl.(keccak.Context))
|
||||||
|
case .Legacy_KECCAK_512:
|
||||||
|
keccak.init_512(&ctx._impl.(keccak.Context))
|
||||||
|
case .Insecure_MD5:
|
||||||
|
md5.init(&ctx._impl.(md5.Context))
|
||||||
|
case .Insecure_SHA1:
|
||||||
|
sha1.init(&ctx._impl.(sha1.Context))
|
||||||
|
case .Invalid:
|
||||||
|
panic("crypto/hash: uninitialized algorithm")
|
||||||
|
case:
|
||||||
|
panic("crypto/hash: invalid algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx._algo = algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// update adds more data to the Context.
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
switch &impl in ctx._impl {
|
||||||
|
case blake2b.Context:
|
||||||
|
blake2b.update(&impl, data)
|
||||||
|
case blake2s.Context:
|
||||||
|
blake2s.update(&impl, data)
|
||||||
|
case sha2.Context_256:
|
||||||
|
sha2.update(&impl, data)
|
||||||
|
case sha2.Context_512:
|
||||||
|
sha2.update(&impl, data)
|
||||||
|
case sha3.Context:
|
||||||
|
sha3.update(&impl, data)
|
||||||
|
case sm3.Context:
|
||||||
|
sm3.update(&impl, data)
|
||||||
|
case keccak.Context:
|
||||||
|
keccak.update(&impl, data)
|
||||||
|
case md5.Context:
|
||||||
|
md5.update(&impl, data)
|
||||||
|
case sha1.Context:
|
||||||
|
sha1.update(&impl, data)
|
||||||
|
case:
|
||||||
|
panic("crypto/hash: uninitialized algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
|
// reset on the Context.
|
||||||
|
//
|
||||||
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
|
// which is useful for for calculating rolling digests.
|
||||||
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
|
switch &impl in ctx._impl {
|
||||||
|
case blake2b.Context:
|
||||||
|
blake2b.final(&impl, hash, finalize_clone)
|
||||||
|
case blake2s.Context:
|
||||||
|
blake2s.final(&impl, hash, finalize_clone)
|
||||||
|
case sha2.Context_256:
|
||||||
|
sha2.final(&impl, hash, finalize_clone)
|
||||||
|
case sha2.Context_512:
|
||||||
|
sha2.final(&impl, hash, finalize_clone)
|
||||||
|
case sha3.Context:
|
||||||
|
sha3.final(&impl, hash, finalize_clone)
|
||||||
|
case sm3.Context:
|
||||||
|
sm3.final(&impl, hash, finalize_clone)
|
||||||
|
case keccak.Context:
|
||||||
|
keccak.final(&impl, hash, finalize_clone)
|
||||||
|
case md5.Context:
|
||||||
|
md5.final(&impl, hash, finalize_clone)
|
||||||
|
case sha1.Context:
|
||||||
|
sha1.final(&impl, hash, finalize_clone)
|
||||||
|
case:
|
||||||
|
panic("crypto/hash: uninitialized algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !finalize_clone {
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone clones the Context other into ctx.
|
||||||
|
clone :: proc(ctx, other: ^Context) {
|
||||||
|
// XXX/yawning: Maybe these cases should panic, because both cases,
|
||||||
|
// are probably bugs.
|
||||||
|
if ctx == other {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx._impl != nil {
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx._algo = other._algo
|
||||||
|
|
||||||
|
reflect.set_union_variant_typeid(
|
||||||
|
ctx._impl,
|
||||||
|
reflect.union_variant_typeid(other._impl),
|
||||||
|
)
|
||||||
|
switch &src_impl in other._impl {
|
||||||
|
case blake2b.Context:
|
||||||
|
blake2b.clone(&ctx._impl.(blake2b.Context), &src_impl)
|
||||||
|
case blake2s.Context:
|
||||||
|
blake2s.clone(&ctx._impl.(blake2s.Context), &src_impl)
|
||||||
|
case sha2.Context_256:
|
||||||
|
sha2.clone(&ctx._impl.(sha2.Context_256), &src_impl)
|
||||||
|
case sha2.Context_512:
|
||||||
|
sha2.clone(&ctx._impl.(sha2.Context_512), &src_impl)
|
||||||
|
case sha3.Context:
|
||||||
|
sha3.clone(&ctx._impl.(sha3.Context), &src_impl)
|
||||||
|
case sm3.Context:
|
||||||
|
sm3.clone(&ctx._impl.(sm3.Context), &src_impl)
|
||||||
|
case keccak.Context:
|
||||||
|
keccak.clone(&ctx._impl.(keccak.Context), &src_impl)
|
||||||
|
case md5.Context:
|
||||||
|
md5.clone(&ctx._impl.(md5.Context), &src_impl)
|
||||||
|
case sha1.Context:
|
||||||
|
sha1.clone(&ctx._impl.(sha1.Context), &src_impl)
|
||||||
|
case:
|
||||||
|
panic("crypto/hash: uninitialized algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
switch &impl in ctx._impl {
|
||||||
|
case blake2b.Context:
|
||||||
|
blake2b.reset(&impl)
|
||||||
|
case blake2s.Context:
|
||||||
|
blake2s.reset(&impl)
|
||||||
|
case sha2.Context_256:
|
||||||
|
sha2.reset(&impl)
|
||||||
|
case sha2.Context_512:
|
||||||
|
sha2.reset(&impl)
|
||||||
|
case sha3.Context:
|
||||||
|
sha3.reset(&impl)
|
||||||
|
case sm3.Context:
|
||||||
|
sm3.reset(&impl)
|
||||||
|
case keccak.Context:
|
||||||
|
keccak.reset(&impl)
|
||||||
|
case md5.Context:
|
||||||
|
md5.reset(&impl)
|
||||||
|
case sha1.Context:
|
||||||
|
sha1.reset(&impl)
|
||||||
|
case:
|
||||||
|
// Unlike clone, calling reset repeatedly is fine.
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx._algo = .Invalid
|
||||||
|
ctx._impl = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// algorithm returns the Algorithm used by a Context instance.
|
||||||
|
algorithm :: proc(ctx: ^Context) -> Algorithm {
|
||||||
|
return ctx._algo
|
||||||
|
}
|
||||||
|
|
||||||
|
// digest_size returns the digest size of a Context instance in bytes.
|
||||||
|
digest_size :: proc(ctx: ^Context) -> int {
|
||||||
|
return DIGEST_SIZES[ctx._algo]
|
||||||
|
}
|
||||||
|
|
||||||
|
// block_size returns the block size of a Context instance in bytes.
|
||||||
|
block_size :: proc(ctx: ^Context) -> int {
|
||||||
|
return BLOCK_SIZES[ctx._algo]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
package hmac implements the HMAC MAC algorithm.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf
|
||||||
|
*/
|
||||||
|
package hmac
|
||||||
|
|
||||||
|
import "core:crypto"
|
||||||
|
import "core:crypto/hash"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
// sum will compute the HMAC with the specified algorithm and key
|
||||||
|
// over msg, and write the computed digest to dst. It requires that
|
||||||
|
// the dst buffer is the tag size.
|
||||||
|
sum :: proc(algorithm: hash.Algorithm, dst, msg, key: []byte) {
|
||||||
|
ctx: Context
|
||||||
|
|
||||||
|
init(&ctx, algorithm, key)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify will verify the HMAC tag computed with the specified algorithm
|
||||||
|
// and key over msg and return true iff the tag is valid. It requires
|
||||||
|
// that the tag is correctly sized.
|
||||||
|
verify :: proc(algorithm: hash.Algorithm, tag, msg, key: []byte) -> bool {
|
||||||
|
tag_buf: [hash.MAX_DIGEST_SIZE]byte
|
||||||
|
|
||||||
|
derived_tag := tag_buf[:hash.DIGEST_SIZES[algorithm]]
|
||||||
|
sum(algorithm, derived_tag, msg, key)
|
||||||
|
|
||||||
|
return crypto.compare_constant_time(derived_tag, tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is a concrete instantiation of HMAC with a specific hash
|
||||||
|
// algorithm.
|
||||||
|
Context :: struct {
|
||||||
|
_o_hash: hash.Context, // H(k ^ ipad) (not finalized)
|
||||||
|
_i_hash: hash.Context, // H(k ^ opad) (not finalized)
|
||||||
|
_tag_sz: int,
|
||||||
|
_is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context with a specific hash Algorithm and key.
|
||||||
|
init :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
|
||||||
|
if ctx._is_initialized {
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_hashes(ctx, algorithm, key)
|
||||||
|
|
||||||
|
ctx._tag_sz = hash.DIGEST_SIZES[algorithm]
|
||||||
|
ctx._is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// update adds more data to the Context.
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
|
hash.update(&ctx._i_hash, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// final finalizes the Context, writes the tag to dst, and calls
|
||||||
|
// reset on the Context.
|
||||||
|
final :: proc(ctx: ^Context, dst: []byte) {
|
||||||
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
|
defer (reset(ctx))
|
||||||
|
|
||||||
|
if len(dst) != ctx._tag_sz {
|
||||||
|
panic("crypto/hmac: invalid destination tag size")
|
||||||
|
}
|
||||||
|
|
||||||
|
hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
|
||||||
|
|
||||||
|
hash.update(&ctx._o_hash, dst) // H((k ^ opad) || H((k ^ ipad) || text))
|
||||||
|
hash.final(&ctx._o_hash, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
if !ctx._is_initialized {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash.reset(&ctx._o_hash)
|
||||||
|
hash.reset(&ctx._i_hash)
|
||||||
|
ctx._tag_sz = 0
|
||||||
|
ctx._is_initialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// algorithm returns the Algorithm used by a Context instance.
|
||||||
|
algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
|
||||||
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
|
return hash.algorithm(&ctx._i_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag_size returns the tag size of a Context instance in bytes.
|
||||||
|
tag_size :: proc(ctx: ^Context) -> int {
|
||||||
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
|
return ctx._tag_sz
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_I_PAD :: 0x36
|
||||||
|
_O_PAD :: 0x5c
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_init_hashes :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
|
||||||
|
K0_buf: [hash.MAX_BLOCK_SIZE]byte
|
||||||
|
kPad_buf: [hash.MAX_BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
kLen := len(key)
|
||||||
|
B := hash.BLOCK_SIZES[algorithm]
|
||||||
|
K0 := K0_buf[:B]
|
||||||
|
defer mem.zero_explicit(raw_data(K0), B)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case kLen == B, kLen < B:
|
||||||
|
// If the length of K = B: set K0 = K.
|
||||||
|
//
|
||||||
|
// If the length of K < B: append zeros to the end of K to
|
||||||
|
// create a B-byte string K0 (e.g., if K is 20 bytes in
|
||||||
|
// length and B = 64, then K will be appended with 44 zero
|
||||||
|
// bytes x’00’).
|
||||||
|
//
|
||||||
|
// K0 is zero-initialized, so the copy handles both cases.
|
||||||
|
copy(K0, key)
|
||||||
|
case kLen > B:
|
||||||
|
// If the length of K > B: hash K to obtain an L byte string,
|
||||||
|
// then append (B-L) zeros to create a B-byte string K0
|
||||||
|
// (i.e., K0 = H(K) || 00...00).
|
||||||
|
tmpCtx := &ctx._o_hash // Saves allocating a hash.Context.
|
||||||
|
hash.init(tmpCtx, algorithm)
|
||||||
|
hash.update(tmpCtx, key)
|
||||||
|
hash.final(tmpCtx, K0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the hashes, and write the padded keys:
|
||||||
|
// - ctx._i_hash -> H(K0 ^ ipad)
|
||||||
|
// - ctx._o_hash -> H(K0 ^ opad)
|
||||||
|
|
||||||
|
hash.init(&ctx._o_hash, algorithm)
|
||||||
|
hash.init(&ctx._i_hash, algorithm)
|
||||||
|
|
||||||
|
kPad := kPad_buf[:B]
|
||||||
|
defer mem.zero_explicit(raw_data(kPad), B)
|
||||||
|
|
||||||
|
for v, i in K0 {
|
||||||
|
kPad[i] = v ~ _I_PAD
|
||||||
|
}
|
||||||
|
hash.update(&ctx._i_hash, kPad)
|
||||||
|
|
||||||
|
for v, i in K0 {
|
||||||
|
kPad[i] = v ~ _O_PAD
|
||||||
|
}
|
||||||
|
hash.update(&ctx._o_hash, kPad)
|
||||||
|
}
|
||||||
@@ -1,584 +0,0 @@
|
|||||||
package jh
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE_224 :: 28
|
|
||||||
DIGEST_SIZE_256 :: 32
|
|
||||||
DIGEST_SIZE_384 :: 48
|
|
||||||
DIGEST_SIZE_512 :: 64
|
|
||||||
|
|
||||||
// hash_string_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
|
||||||
return hash_bytes_224(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 224
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 224
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_224 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 224
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_224 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_224(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_224(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_224]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_224 :: proc {
|
|
||||||
hash_stream_224,
|
|
||||||
hash_file_224,
|
|
||||||
hash_bytes_224,
|
|
||||||
hash_string_224,
|
|
||||||
hash_bytes_to_buffer_224,
|
|
||||||
hash_string_to_buffer_224,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
|
||||||
return hash_bytes_256(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 256
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 256
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_256 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 256
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_256 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_256(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_256(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_256]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_256 :: proc {
|
|
||||||
hash_stream_256,
|
|
||||||
hash_file_256,
|
|
||||||
hash_bytes_256,
|
|
||||||
hash_string_256,
|
|
||||||
hash_bytes_to_buffer_256,
|
|
||||||
hash_string_to_buffer_256,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
|
||||||
return hash_bytes_384(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 384
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 384
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_384 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 384
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_384 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_384(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_384(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_384]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_384 :: proc {
|
|
||||||
hash_stream_384,
|
|
||||||
hash_file_384,
|
|
||||||
hash_bytes_384,
|
|
||||||
hash_string_384,
|
|
||||||
hash_bytes_to_buffer_384,
|
|
||||||
hash_string_to_buffer_384,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
|
||||||
return hash_bytes_512(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 512
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 512
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_512 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: Jh_Context
|
|
||||||
ctx.hashbitlen = 512
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_512 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_512(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_512(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_512]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_512 :: proc {
|
|
||||||
hash_stream_512,
|
|
||||||
hash_file_512,
|
|
||||||
hash_bytes_512,
|
|
||||||
hash_string_512,
|
|
||||||
hash_bytes_to_buffer_512,
|
|
||||||
hash_string_to_buffer_512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc(ctx: ^Jh_Context) {
|
|
||||||
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
|
|
||||||
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
|
|
||||||
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
|
|
||||||
F8(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Jh_Context, data: []byte) {
|
|
||||||
databitlen := u64(len(data)) * 8
|
|
||||||
ctx.databitlen += databitlen
|
|
||||||
i := u64(0)
|
|
||||||
|
|
||||||
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
|
|
||||||
if (databitlen & 7) == 0 {
|
|
||||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
|
||||||
} else {
|
|
||||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
|
|
||||||
}
|
|
||||||
ctx.buffer_size += databitlen
|
|
||||||
databitlen = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
|
|
||||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
|
||||||
i = 64 - (ctx.buffer_size >> 3)
|
|
||||||
databitlen = databitlen - (512 - ctx.buffer_size)
|
|
||||||
F8(ctx)
|
|
||||||
ctx.buffer_size = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for databitlen >= 512 {
|
|
||||||
copy(ctx.buffer[:], data[i:i + 64])
|
|
||||||
F8(ctx)
|
|
||||||
i += 64
|
|
||||||
databitlen -= 512
|
|
||||||
}
|
|
||||||
|
|
||||||
if databitlen > 0 {
|
|
||||||
if (databitlen & 7) == 0 {
|
|
||||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
|
|
||||||
} else {
|
|
||||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
|
|
||||||
}
|
|
||||||
ctx.buffer_size = databitlen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Jh_Context, hash: []byte) {
|
|
||||||
if ctx.databitlen & 0x1ff == 0 {
|
|
||||||
for i := 0; i < 64; i += 1 {
|
|
||||||
ctx.buffer[i] = 0
|
|
||||||
}
|
|
||||||
ctx.buffer[0] = 0x80
|
|
||||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
|
||||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
|
||||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
|
||||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
|
||||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
|
||||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
|
||||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
|
||||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
|
||||||
F8(ctx)
|
|
||||||
} else {
|
|
||||||
if ctx.buffer_size & 7 == 0 {
|
|
||||||
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
|
|
||||||
ctx.buffer[i] = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
|
|
||||||
ctx.buffer[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
|
|
||||||
F8(ctx)
|
|
||||||
for i := 0; i < 64; i += 1 {
|
|
||||||
ctx.buffer[i] = 0
|
|
||||||
}
|
|
||||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
|
||||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
|
||||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
|
||||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
|
||||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
|
||||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
|
||||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
|
||||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
|
||||||
F8(ctx)
|
|
||||||
}
|
|
||||||
switch ctx.hashbitlen {
|
|
||||||
case 224: copy(hash[:], ctx.H[100:128])
|
|
||||||
case 256: copy(hash[:], ctx.H[96:128])
|
|
||||||
case 384: copy(hash[:], ctx.H[80:128])
|
|
||||||
case 512: copy(hash[:], ctx.H[64:128])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
JH implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
ROUNDCONSTANT_ZERO := [64]byte {
|
|
||||||
0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7,
|
|
||||||
0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8,
|
|
||||||
0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6,
|
|
||||||
0xe, 0xa, 0x9, 0x5, 0x7, 0xd, 0x3, 0xe,
|
|
||||||
0x3, 0xa, 0xd, 0xe, 0xc, 0x1, 0x7, 0x5,
|
|
||||||
0x1, 0x2, 0x7, 0x7, 0x5, 0x0, 0x9, 0x9,
|
|
||||||
0xd, 0xa, 0x2, 0xf, 0x5, 0x9, 0x0, 0xb,
|
|
||||||
0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
|
|
||||||
}
|
|
||||||
|
|
||||||
SBOX := [2][16]byte {
|
|
||||||
{9, 0, 4, 11, 13, 12, 3, 15, 1, 10, 2, 6, 7, 5, 8, 14},
|
|
||||||
{3, 12, 6, 13, 5, 7, 1, 9, 15, 2, 0, 4, 11, 10, 14, 8},
|
|
||||||
}
|
|
||||||
|
|
||||||
Jh_Context :: struct {
|
|
||||||
hashbitlen: int,
|
|
||||||
databitlen: u64,
|
|
||||||
buffer_size: u64,
|
|
||||||
H: [128]byte,
|
|
||||||
A: [256]byte,
|
|
||||||
roundconstant: [64]byte,
|
|
||||||
buffer: [64]byte,
|
|
||||||
}
|
|
||||||
|
|
||||||
E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
|
||||||
t0,t1,t2,t3: byte
|
|
||||||
tem: [256]byte
|
|
||||||
for i := 0; i < 128; i += 1 {
|
|
||||||
tem[i] = ctx.A[i << 1]
|
|
||||||
tem[i + 128] = ctx.A[(i << 1) + 1]
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i += 1 {
|
|
||||||
ctx.H[i] = 0
|
|
||||||
}
|
|
||||||
for i := 0; i < 256; i += 1 {
|
|
||||||
t0 = (tem[i] >> 3) & 1
|
|
||||||
t1 = (tem[i] >> 2) & 1
|
|
||||||
t2 = (tem[i] >> 1) & 1
|
|
||||||
t3 = (tem[i] >> 0) & 1
|
|
||||||
|
|
||||||
ctx.H[uint(i) >> 3] |= t0 << (7 - (uint(i) & 7))
|
|
||||||
ctx.H[(uint(i) + 256) >> 3] |= t1 << (7 - (uint(i) & 7))
|
|
||||||
ctx.H[(uint(i) + 512) >> 3] |= t2 << (7 - (uint(i) & 7))
|
|
||||||
ctx.H[(uint(i) + 768) >> 3] |= t3 << (7 - (uint(i) & 7))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_roundconstant :: proc(ctx: ^Jh_Context) {
|
|
||||||
tem: [64]byte
|
|
||||||
t: byte
|
|
||||||
for i := 0; i < 64; i += 1 {
|
|
||||||
tem[i] = SBOX[0][ctx.roundconstant[i]]
|
|
||||||
}
|
|
||||||
for i := 0; i < 64; i += 2 {
|
|
||||||
tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
|
||||||
tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf
|
|
||||||
}
|
|
||||||
for i := 0; i < 64; i += 4 {
|
|
||||||
t = tem[i + 2]
|
|
||||||
tem[i + 2] = tem[i + 3]
|
|
||||||
tem[i + 3] = t
|
|
||||||
}
|
|
||||||
for i := 0; i < 32; i += 1 {
|
|
||||||
ctx.roundconstant[i] = tem[i << 1]
|
|
||||||
ctx.roundconstant[i + 32] = tem[(i << 1) + 1]
|
|
||||||
}
|
|
||||||
for i := 32; i < 64; i += 2 {
|
|
||||||
t = ctx.roundconstant[i]
|
|
||||||
ctx.roundconstant[i] = ctx.roundconstant[i + 1]
|
|
||||||
ctx.roundconstant[i + 1] = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
R8 :: proc(ctx: ^Jh_Context) {
|
|
||||||
t: byte
|
|
||||||
tem, roundconstant_expanded: [256]byte
|
|
||||||
for i := u32(0); i < 256; i += 1 {
|
|
||||||
roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1
|
|
||||||
}
|
|
||||||
for i := 0; i < 256; i += 1 {
|
|
||||||
tem[i] = SBOX[roundconstant_expanded[i]][ctx.A[i]]
|
|
||||||
}
|
|
||||||
for i := 0; i < 256; i += 2 {
|
|
||||||
tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
|
||||||
tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf
|
|
||||||
}
|
|
||||||
for i := 0; i < 256; i += 4 {
|
|
||||||
t = tem[i + 2]
|
|
||||||
tem[i+2] = tem[i + 3]
|
|
||||||
tem[i+3] = t
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i += 1 {
|
|
||||||
ctx.A[i] = tem[i << 1]
|
|
||||||
ctx.A[i + 128] = tem[(i << 1) + 1]
|
|
||||||
}
|
|
||||||
for i := 128; i < 256; i += 2 {
|
|
||||||
t = ctx.A[i]
|
|
||||||
ctx.A[i] = ctx.A[i + 1]
|
|
||||||
ctx.A[i + 1] = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
E8_initialgroup :: proc(ctx: ^Jh_Context) {
|
|
||||||
t0, t1, t2, t3: byte
|
|
||||||
tem: [256]byte
|
|
||||||
for i := u32(0); i < 256; i += 1 {
|
|
||||||
t0 = (ctx.H[i >> 3] >> (7 - (i & 7))) & 1
|
|
||||||
t1 = (ctx.H[(i + 256) >> 3] >> (7 - (i & 7))) & 1
|
|
||||||
t2 = (ctx.H[(i + 512) >> 3] >> (7 - (i & 7))) & 1
|
|
||||||
t3 = (ctx.H[(i + 768) >> 3] >> (7 - (i & 7))) & 1
|
|
||||||
tem[i] = (t0 << 3) | (t1 << 2) | (t2 << 1) | (t3 << 0)
|
|
||||||
}
|
|
||||||
for i := 0; i < 128; i += 1 {
|
|
||||||
ctx.A[i << 1] = tem[i]
|
|
||||||
ctx.A[(i << 1) + 1] = tem[i + 128]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
E8 :: proc(ctx: ^Jh_Context) {
|
|
||||||
for i := 0; i < 64; i += 1 {
|
|
||||||
ctx.roundconstant[i] = ROUNDCONSTANT_ZERO[i]
|
|
||||||
}
|
|
||||||
E8_initialgroup(ctx)
|
|
||||||
for i := 0; i < 42; i += 1 {
|
|
||||||
R8(ctx)
|
|
||||||
update_roundconstant(ctx)
|
|
||||||
}
|
|
||||||
E8_finaldegroup(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
F8 :: proc(ctx: ^Jh_Context) {
|
|
||||||
for i := 0; i < 64; i += 1 {
|
|
||||||
ctx.H[i] ~= ctx.buffer[i]
|
|
||||||
}
|
|
||||||
E8(ctx)
|
|
||||||
for i := 0; i < 64; i += 1 {
|
|
||||||
ctx.H[i + 64] ~= ctx.buffer[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,374 +0,0 @@
|
|||||||
package keccak
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Interface for the Keccak hashing algorithm.
|
|
||||||
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../_sha3"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE_224 :: 28
|
|
||||||
DIGEST_SIZE_256 :: 32
|
|
||||||
DIGEST_SIZE_384 :: 48
|
|
||||||
DIGEST_SIZE_512 :: 64
|
|
||||||
|
|
||||||
// hash_string_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
|
||||||
return hash_bytes_224(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_224 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_224
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_224
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_224 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_224]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_224
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
_sha3.update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_224 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_224(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_224(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_224]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_224 :: proc {
|
|
||||||
hash_stream_224,
|
|
||||||
hash_file_224,
|
|
||||||
hash_bytes_224,
|
|
||||||
hash_string_224,
|
|
||||||
hash_bytes_to_buffer_224,
|
|
||||||
hash_string_to_buffer_224,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
|
||||||
return hash_bytes_256(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_256
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_256
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_256 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_256
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
_sha3.update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_256 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_256(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_256(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_256]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_256 :: proc {
|
|
||||||
hash_stream_256,
|
|
||||||
hash_file_256,
|
|
||||||
hash_bytes_256,
|
|
||||||
hash_string_256,
|
|
||||||
hash_bytes_to_buffer_256,
|
|
||||||
hash_string_to_buffer_256,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
|
||||||
return hash_bytes_384(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_384 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_384
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_384
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_384 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_384]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_384
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
_sha3.update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_384 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_384(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_384(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_384]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_384 :: proc {
|
|
||||||
hash_stream_384,
|
|
||||||
hash_file_384,
|
|
||||||
hash_bytes_384,
|
|
||||||
hash_string_384,
|
|
||||||
hash_bytes_to_buffer_384,
|
|
||||||
hash_string_to_buffer_384,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
|
||||||
return hash_bytes_512(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_512 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_512
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_512
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
_sha3.update(&ctx, data)
|
|
||||||
_sha3.final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_512 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_512]byte
|
|
||||||
ctx: _sha3.Sha3_Context
|
|
||||||
ctx.mdlen = DIGEST_SIZE_512
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
_sha3.update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_sha3.final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_512 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_512(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_512(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_512]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_512 :: proc {
|
|
||||||
hash_stream_512,
|
|
||||||
hash_file_512,
|
|
||||||
hash_bytes_512,
|
|
||||||
hash_string_512,
|
|
||||||
hash_bytes_to_buffer_512,
|
|
||||||
hash_string_to_buffer_512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
Keccak_Context :: _sha3.Sha3_Context
|
|
||||||
|
|
||||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
|
||||||
ctx.is_keccak = true
|
|
||||||
_sha3.init(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
|
||||||
_sha3.update(ctx, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
|
||||||
_sha3.final(ctx, hash)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# crypto/legacy
|
||||||
|
|
||||||
|
These are algorithms that are shipped solely for the purpose of
|
||||||
|
interoperability with legacy systems. The use of these packages in
|
||||||
|
any other capacity is discouraged, especially those that are known
|
||||||
|
to be broken.
|
||||||
|
|
||||||
|
- keccak - The draft version of the algorithm that became SHA-3
|
||||||
|
- MD5 - Broken (https://eprint.iacr.org/2005/075)
|
||||||
|
- SHA-1 - Broken (https://eprint.iacr.org/2017/190)
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
package keccak implements the Keccak hash algorithm family.
|
||||||
|
|
||||||
|
During the SHA-3 standardization process, the padding scheme was changed
|
||||||
|
thus Keccac and SHA-3 produce different outputs. Most users should use
|
||||||
|
SHA-3 and/or SHAKE instead, however the legacy algorithm is provided for
|
||||||
|
backward compatibility purposes.
|
||||||
|
*/
|
||||||
|
package keccak
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2021 zhibog
|
||||||
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
zhibog, dotbmp: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "../../_sha3"
|
||||||
|
|
||||||
|
// DIGEST_SIZE_224 is the Keccak-224 digest size.
|
||||||
|
DIGEST_SIZE_224 :: 28
|
||||||
|
// DIGEST_SIZE_256 is the Keccak-256 digest size.
|
||||||
|
DIGEST_SIZE_256 :: 32
|
||||||
|
// DIGEST_SIZE_384 is the Keccak-384 digest size.
|
||||||
|
DIGEST_SIZE_384 :: 48
|
||||||
|
// DIGEST_SIZE_512 is the Keccak-512 digest size.
|
||||||
|
DIGEST_SIZE_512 :: 64
|
||||||
|
|
||||||
|
// BLOCK_SIZE_224 is the Keccak-224 block size in bytes.
|
||||||
|
BLOCK_SIZE_224 :: _sha3.RATE_224
|
||||||
|
// BLOCK_SIZE_256 is the Keccak-256 block size in bytes.
|
||||||
|
BLOCK_SIZE_256 :: _sha3.RATE_256
|
||||||
|
// BLOCK_SIZE_384 is the Keccak-384 block size in bytes.
|
||||||
|
BLOCK_SIZE_384 :: _sha3.RATE_384
|
||||||
|
// BLOCK_SIZE_512 is the Keccak-512 block size in bytes.
|
||||||
|
BLOCK_SIZE_512 :: _sha3.RATE_512
|
||||||
|
|
||||||
|
// Context is a Keccak instance.
|
||||||
|
Context :: distinct _sha3.Context
|
||||||
|
|
||||||
|
// init_224 initializes a Context for Keccak-224.
|
||||||
|
init_224 :: proc(ctx: ^Context) {
|
||||||
|
ctx.mdlen = DIGEST_SIZE_224
|
||||||
|
_init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init_256 initializes a Context for Keccak-256.
|
||||||
|
init_256 :: proc(ctx: ^Context) {
|
||||||
|
ctx.mdlen = DIGEST_SIZE_256
|
||||||
|
_init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init_384 initializes a Context for Keccak-384.
|
||||||
|
init_384 :: proc(ctx: ^Context) {
|
||||||
|
ctx.mdlen = DIGEST_SIZE_384
|
||||||
|
_init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init_512 initializes a Context for Keccak-512.
|
||||||
|
init_512 :: proc(ctx: ^Context) {
|
||||||
|
ctx.mdlen = DIGEST_SIZE_512
|
||||||
|
_init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_init :: proc(ctx: ^Context) {
|
||||||
|
ctx.is_keccak = true
|
||||||
|
_sha3.init(transmute(^_sha3.Context)(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update adds more data to the Context.
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
|
// reset on the Context.
|
||||||
|
//
|
||||||
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
|
// which is useful for for calculating rolling digests.
|
||||||
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
|
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone clones the Context other into ctx.
|
||||||
|
clone :: proc(ctx, other: ^Context) {
|
||||||
|
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
package md5 implements the MD5 hash algorithm.
|
||||||
|
|
||||||
|
WARNING: The MD5 algorithm is known to be insecure and should only be
|
||||||
|
used for interoperating with legacy applications.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- https://eprint.iacr.org/2005/075
|
||||||
|
- https://datatracker.ietf.org/doc/html/rfc1321
|
||||||
|
*/
|
||||||
|
package md5
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2021 zhibog
|
||||||
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
zhibog, dotbmp: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:math/bits"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
// DIGEST_SIZE is the MD5 digest size in bytes.
|
||||||
|
DIGEST_SIZE :: 16
|
||||||
|
|
||||||
|
// BLOCK_SIZE is the MD5 block size in bytes.
|
||||||
|
BLOCK_SIZE :: 64
|
||||||
|
|
||||||
|
// Context is a MD5 instance.
|
||||||
|
Context :: struct {
|
||||||
|
data: [BLOCK_SIZE]byte,
|
||||||
|
state: [4]u32,
|
||||||
|
bitlen: u64,
|
||||||
|
datalen: u32,
|
||||||
|
|
||||||
|
is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context.
|
||||||
|
init :: proc(ctx: ^Context) {
|
||||||
|
ctx.state[0] = 0x67452301
|
||||||
|
ctx.state[1] = 0xefcdab89
|
||||||
|
ctx.state[2] = 0x98badcfe
|
||||||
|
ctx.state[3] = 0x10325476
|
||||||
|
|
||||||
|
ctx.bitlen = 0
|
||||||
|
ctx.datalen = 0
|
||||||
|
|
||||||
|
ctx.is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// update adds more data to the Context.
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
assert(ctx.is_initialized)
|
||||||
|
|
||||||
|
for i := 0; i < len(data); i += 1 {
|
||||||
|
ctx.data[ctx.datalen] = data[i]
|
||||||
|
ctx.datalen += 1
|
||||||
|
if (ctx.datalen == BLOCK_SIZE) {
|
||||||
|
transform(ctx, ctx.data[:])
|
||||||
|
ctx.bitlen += 512
|
||||||
|
ctx.datalen = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
|
// reset on the Context.
|
||||||
|
//
|
||||||
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
|
// which is useful for for calculating rolling digests.
|
||||||
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
|
assert(ctx.is_initialized)
|
||||||
|
|
||||||
|
if len(hash) < DIGEST_SIZE {
|
||||||
|
panic("crypto/md5: invalid destination digest size")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := ctx
|
||||||
|
if finalize_clone {
|
||||||
|
tmp_ctx: Context
|
||||||
|
clone(&tmp_ctx, ctx)
|
||||||
|
ctx = &tmp_ctx
|
||||||
|
}
|
||||||
|
defer(reset(ctx))
|
||||||
|
|
||||||
|
i := ctx.datalen
|
||||||
|
|
||||||
|
if ctx.datalen < 56 {
|
||||||
|
ctx.data[i] = 0x80
|
||||||
|
i += 1
|
||||||
|
for i < 56 {
|
||||||
|
ctx.data[i] = 0x00
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
} else if ctx.datalen >= 56 {
|
||||||
|
ctx.data[i] = 0x80
|
||||||
|
i += 1
|
||||||
|
for i < BLOCK_SIZE {
|
||||||
|
ctx.data[i] = 0x00
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
transform(ctx, ctx.data[:])
|
||||||
|
mem.set(&ctx.data, 0, 56)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.bitlen += u64(ctx.datalen * 8)
|
||||||
|
endian.unchecked_put_u64le(ctx.data[56:], ctx.bitlen)
|
||||||
|
transform(ctx, ctx.data[:])
|
||||||
|
|
||||||
|
for i = 0; i < DIGEST_SIZE / 4; i += 1 {
|
||||||
|
endian.unchecked_put_u32le(hash[i * 4:], ctx.state[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone clones the Context other into ctx.
|
||||||
|
clone :: proc(ctx, other: ^$T) {
|
||||||
|
ctx^ = other^
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^$T) {
|
||||||
|
if !ctx.is_initialized {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.zero_explicit(ctx, size_of(ctx^))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MD5 implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
@note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH
|
||||||
|
and II respectively, instead of declaring them separately.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
FF :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||||
|
return b + bits.rotate_left32(a + ((b & c) | (~b & d)) + m + t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
GG :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||||
|
return b + bits.rotate_left32(a + ((b & d) | (c & ~d)) + m + t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
HH :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||||
|
return b + bits.rotate_left32(a + (b ~ c ~ d) + m + t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||||
|
return b + bits.rotate_left32(a + (c ~ (b | ~d)) + m + t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
transform :: proc "contextless" (ctx: ^Context, data: []byte) {
|
||||||
|
m: [DIGEST_SIZE]u32
|
||||||
|
|
||||||
|
for i := 0; i < DIGEST_SIZE; i += 1 {
|
||||||
|
m[i] = endian.unchecked_get_u32le(data[i * 4:])
|
||||||
|
}
|
||||||
|
|
||||||
|
a := ctx.state[0]
|
||||||
|
b := ctx.state[1]
|
||||||
|
c := ctx.state[2]
|
||||||
|
d := ctx.state[3]
|
||||||
|
|
||||||
|
a = FF(a, b, c, d, m[0], 7, 0xd76aa478)
|
||||||
|
d = FF(d, a, b, c, m[1], 12, 0xe8c7b756)
|
||||||
|
c = FF(c, d, a, b, m[2], 17, 0x242070db)
|
||||||
|
b = FF(b, c, d, a, m[3], 22, 0xc1bdceee)
|
||||||
|
a = FF(a, b, c, d, m[4], 7, 0xf57c0faf)
|
||||||
|
d = FF(d, a, b, c, m[5], 12, 0x4787c62a)
|
||||||
|
c = FF(c, d, a, b, m[6], 17, 0xa8304613)
|
||||||
|
b = FF(b, c, d, a, m[7], 22, 0xfd469501)
|
||||||
|
a = FF(a, b, c, d, m[8], 7, 0x698098d8)
|
||||||
|
d = FF(d, a, b, c, m[9], 12, 0x8b44f7af)
|
||||||
|
c = FF(c, d, a, b, m[10], 17, 0xffff5bb1)
|
||||||
|
b = FF(b, c, d, a, m[11], 22, 0x895cd7be)
|
||||||
|
a = FF(a, b, c, d, m[12], 7, 0x6b901122)
|
||||||
|
d = FF(d, a, b, c, m[13], 12, 0xfd987193)
|
||||||
|
c = FF(c, d, a, b, m[14], 17, 0xa679438e)
|
||||||
|
b = FF(b, c, d, a, m[15], 22, 0x49b40821)
|
||||||
|
|
||||||
|
a = GG(a, b, c, d, m[1], 5, 0xf61e2562)
|
||||||
|
d = GG(d, a, b, c, m[6], 9, 0xc040b340)
|
||||||
|
c = GG(c, d, a, b, m[11], 14, 0x265e5a51)
|
||||||
|
b = GG(b, c, d, a, m[0], 20, 0xe9b6c7aa)
|
||||||
|
a = GG(a, b, c, d, m[5], 5, 0xd62f105d)
|
||||||
|
d = GG(d, a, b, c, m[10], 9, 0x02441453)
|
||||||
|
c = GG(c, d, a, b, m[15], 14, 0xd8a1e681)
|
||||||
|
b = GG(b, c, d, a, m[4], 20, 0xe7d3fbc8)
|
||||||
|
a = GG(a, b, c, d, m[9], 5, 0x21e1cde6)
|
||||||
|
d = GG(d, a, b, c, m[14], 9, 0xc33707d6)
|
||||||
|
c = GG(c, d, a, b, m[3], 14, 0xf4d50d87)
|
||||||
|
b = GG(b, c, d, a, m[8], 20, 0x455a14ed)
|
||||||
|
a = GG(a, b, c, d, m[13], 5, 0xa9e3e905)
|
||||||
|
d = GG(d, a, b, c, m[2], 9, 0xfcefa3f8)
|
||||||
|
c = GG(c, d, a, b, m[7], 14, 0x676f02d9)
|
||||||
|
b = GG(b, c, d, a, m[12], 20, 0x8d2a4c8a)
|
||||||
|
|
||||||
|
a = HH(a, b, c, d, m[5], 4, 0xfffa3942)
|
||||||
|
d = HH(d, a, b, c, m[8], 11, 0x8771f681)
|
||||||
|
c = HH(c, d, a, b, m[11], 16, 0x6d9d6122)
|
||||||
|
b = HH(b, c, d, a, m[14], 23, 0xfde5380c)
|
||||||
|
a = HH(a, b, c, d, m[1], 4, 0xa4beea44)
|
||||||
|
d = HH(d, a, b, c, m[4], 11, 0x4bdecfa9)
|
||||||
|
c = HH(c, d, a, b, m[7], 16, 0xf6bb4b60)
|
||||||
|
b = HH(b, c, d, a, m[10], 23, 0xbebfbc70)
|
||||||
|
a = HH(a, b, c, d, m[13], 4, 0x289b7ec6)
|
||||||
|
d = HH(d, a, b, c, m[0], 11, 0xeaa127fa)
|
||||||
|
c = HH(c, d, a, b, m[3], 16, 0xd4ef3085)
|
||||||
|
b = HH(b, c, d, a, m[6], 23, 0x04881d05)
|
||||||
|
a = HH(a, b, c, d, m[9], 4, 0xd9d4d039)
|
||||||
|
d = HH(d, a, b, c, m[12], 11, 0xe6db99e5)
|
||||||
|
c = HH(c, d, a, b, m[15], 16, 0x1fa27cf8)
|
||||||
|
b = HH(b, c, d, a, m[2], 23, 0xc4ac5665)
|
||||||
|
|
||||||
|
a = II(a, b, c, d, m[0], 6, 0xf4292244)
|
||||||
|
d = II(d, a, b, c, m[7], 10, 0x432aff97)
|
||||||
|
c = II(c, d, a, b, m[14], 15, 0xab9423a7)
|
||||||
|
b = II(b, c, d, a, m[5], 21, 0xfc93a039)
|
||||||
|
a = II(a, b, c, d, m[12], 6, 0x655b59c3)
|
||||||
|
d = II(d, a, b, c, m[3], 10, 0x8f0ccc92)
|
||||||
|
c = II(c, d, a, b, m[10], 15, 0xffeff47d)
|
||||||
|
b = II(b, c, d, a, m[1], 21, 0x85845dd1)
|
||||||
|
a = II(a, b, c, d, m[8], 6, 0x6fa87e4f)
|
||||||
|
d = II(d, a, b, c, m[15], 10, 0xfe2ce6e0)
|
||||||
|
c = II(c, d, a, b, m[6], 15, 0xa3014314)
|
||||||
|
b = II(b, c, d, a, m[13], 21, 0x4e0811a1)
|
||||||
|
a = II(a, b, c, d, m[4], 6, 0xf7537e82)
|
||||||
|
d = II(d, a, b, c, m[11], 10, 0xbd3af235)
|
||||||
|
c = II(c, d, a, b, m[2], 15, 0x2ad7d2bb)
|
||||||
|
b = II(b, c, d, a, m[9], 21, 0xeb86d391)
|
||||||
|
|
||||||
|
ctx.state[0] += a
|
||||||
|
ctx.state[1] += b
|
||||||
|
ctx.state[2] += c
|
||||||
|
ctx.state[3] += d
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
package sha1 implements the SHA1 hash algorithm.
|
||||||
|
|
||||||
|
WARNING: The SHA1 algorithm is known to be insecure and should only be
|
||||||
|
used for interoperating with legacy applications.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- https://eprint.iacr.org/2017/190
|
||||||
|
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
|
||||||
|
- https://datatracker.ietf.org/doc/html/rfc3174
|
||||||
|
*/
|
||||||
|
package sha1
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2021 zhibog
|
||||||
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
zhibog, dotbmp: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:math/bits"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
// DIGEST_SIZE is the SHA1 digest size in bytes.
|
||||||
|
DIGEST_SIZE :: 20
|
||||||
|
|
||||||
|
// BLOCK_SIZE is the SHA1 block size in bytes.
|
||||||
|
BLOCK_SIZE :: 64
|
||||||
|
|
||||||
|
// Context is a SHA1 instance.
|
||||||
|
Context :: struct {
|
||||||
|
data: [BLOCK_SIZE]byte,
|
||||||
|
state: [5]u32,
|
||||||
|
k: [4]u32,
|
||||||
|
bitlen: u64,
|
||||||
|
datalen: u32,
|
||||||
|
|
||||||
|
is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context.
|
||||||
|
init :: proc(ctx: ^Context) {
|
||||||
|
ctx.state[0] = 0x67452301
|
||||||
|
ctx.state[1] = 0xefcdab89
|
||||||
|
ctx.state[2] = 0x98badcfe
|
||||||
|
ctx.state[3] = 0x10325476
|
||||||
|
ctx.state[4] = 0xc3d2e1f0
|
||||||
|
ctx.k[0] = 0x5a827999
|
||||||
|
ctx.k[1] = 0x6ed9eba1
|
||||||
|
ctx.k[2] = 0x8f1bbcdc
|
||||||
|
ctx.k[3] = 0xca62c1d6
|
||||||
|
|
||||||
|
ctx.datalen = 0
|
||||||
|
ctx.bitlen = 0
|
||||||
|
|
||||||
|
ctx.is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// update adds more data to the Context.
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
assert(ctx.is_initialized)
|
||||||
|
|
||||||
|
for i := 0; i < len(data); i += 1 {
|
||||||
|
ctx.data[ctx.datalen] = data[i]
|
||||||
|
ctx.datalen += 1
|
||||||
|
if (ctx.datalen == BLOCK_SIZE) {
|
||||||
|
transform(ctx, ctx.data[:])
|
||||||
|
ctx.bitlen += 512
|
||||||
|
ctx.datalen = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
|
// reset on the Context.
|
||||||
|
//
|
||||||
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
|
// which is useful for for calculating rolling digests.
|
||||||
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
|
assert(ctx.is_initialized)
|
||||||
|
|
||||||
|
if len(hash) < DIGEST_SIZE {
|
||||||
|
panic("crypto/sha1: invalid destination digest size")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := ctx
|
||||||
|
if finalize_clone {
|
||||||
|
tmp_ctx: Context
|
||||||
|
clone(&tmp_ctx, ctx)
|
||||||
|
ctx = &tmp_ctx
|
||||||
|
}
|
||||||
|
defer(reset(ctx))
|
||||||
|
|
||||||
|
i := ctx.datalen
|
||||||
|
|
||||||
|
if ctx.datalen < 56 {
|
||||||
|
ctx.data[i] = 0x80
|
||||||
|
i += 1
|
||||||
|
for i < 56 {
|
||||||
|
ctx.data[i] = 0x00
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.data[i] = 0x80
|
||||||
|
i += 1
|
||||||
|
for i < BLOCK_SIZE {
|
||||||
|
ctx.data[i] = 0x00
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
transform(ctx, ctx.data[:])
|
||||||
|
mem.set(&ctx.data, 0, 56)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.bitlen += u64(ctx.datalen * 8)
|
||||||
|
endian.unchecked_put_u64be(ctx.data[56:], ctx.bitlen)
|
||||||
|
transform(ctx, ctx.data[:])
|
||||||
|
|
||||||
|
for i = 0; i < DIGEST_SIZE / 4; i += 1 {
|
||||||
|
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone clones the Context other into ctx.
|
||||||
|
clone :: proc(ctx, other: ^$T) {
|
||||||
|
ctx^ = other^
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^$T) {
|
||||||
|
if !ctx.is_initialized {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.zero_explicit(ctx, size_of(ctx^))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SHA1 implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
transform :: proc "contextless" (ctx: ^Context, data: []byte) {
|
||||||
|
a, b, c, d, e, i, t: u32
|
||||||
|
m: [80]u32
|
||||||
|
|
||||||
|
for i = 0; i < 16; i += 1 {
|
||||||
|
m[i] = endian.unchecked_get_u32be(data[i * 4:])
|
||||||
|
}
|
||||||
|
for i < 80 {
|
||||||
|
m[i] = (m[i - 3] ~ m[i - 8] ~ m[i - 14] ~ m[i - 16])
|
||||||
|
m[i] = (m[i] << 1) | (m[i] >> 31)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
a = ctx.state[0]
|
||||||
|
b = ctx.state[1]
|
||||||
|
c = ctx.state[2]
|
||||||
|
d = ctx.state[3]
|
||||||
|
e = ctx.state[4]
|
||||||
|
|
||||||
|
for i = 0; i < 20; i += 1 {
|
||||||
|
t = bits.rotate_left32(a, 5) + ((b & c) ~ (~b & d)) + e + ctx.k[0] + m[i]
|
||||||
|
e = d
|
||||||
|
d = c
|
||||||
|
c = bits.rotate_left32(b, 30)
|
||||||
|
b = a
|
||||||
|
a = t
|
||||||
|
}
|
||||||
|
for i < 40 {
|
||||||
|
t = bits.rotate_left32(a, 5) + (b ~ c ~ d) + e + ctx.k[1] + m[i]
|
||||||
|
e = d
|
||||||
|
d = c
|
||||||
|
c = bits.rotate_left32(b, 30)
|
||||||
|
b = a
|
||||||
|
a = t
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
for i < 60 {
|
||||||
|
t = bits.rotate_left32(a, 5) + ((b & c) ~ (b & d) ~ (c & d)) + e + ctx.k[2] + m[i]
|
||||||
|
e = d
|
||||||
|
d = c
|
||||||
|
c = bits.rotate_left32(b, 30)
|
||||||
|
b = a
|
||||||
|
a = t
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
for i < 80 {
|
||||||
|
t = bits.rotate_left32(a, 5) + (b ~ c ~ d) + e + ctx.k[3] + m[i]
|
||||||
|
e = d
|
||||||
|
d = c
|
||||||
|
c = bits.rotate_left32(b, 30)
|
||||||
|
b = a
|
||||||
|
a = t
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.state[0] += a
|
||||||
|
ctx.state[1] += b
|
||||||
|
ctx.state[2] += c
|
||||||
|
ctx.state[3] += d
|
||||||
|
ctx.state[4] += e
|
||||||
|
}
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
package md2
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 16
|
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Md2_Context
|
|
||||||
// init(&ctx) No-op
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Md2_Context
|
|
||||||
// init(&ctx) No-op
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Md2_Context
|
|
||||||
// init(&ctx) No-op
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
@(warning="Init is a no-op for MD2")
|
|
||||||
init :: proc(ctx: ^Md2_Context) {
|
|
||||||
// No action needed here
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Md2_Context, data: []byte) {
|
|
||||||
for i := 0; i < len(data); i += 1 {
|
|
||||||
ctx.data[ctx.datalen] = data[i]
|
|
||||||
ctx.datalen += 1
|
|
||||||
if (ctx.datalen == DIGEST_SIZE) {
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
ctx.datalen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Md2_Context, hash: []byte) {
|
|
||||||
to_pad := byte(DIGEST_SIZE - ctx.datalen)
|
|
||||||
for ctx.datalen < DIGEST_SIZE {
|
|
||||||
ctx.data[ctx.datalen] = to_pad
|
|
||||||
ctx.datalen += 1
|
|
||||||
}
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
transform(ctx, ctx.checksum[:])
|
|
||||||
for i := 0; i < DIGEST_SIZE; i += 1 {
|
|
||||||
hash[i] = ctx.state[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
MD2 implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
Md2_Context :: struct {
|
|
||||||
data: [DIGEST_SIZE]byte,
|
|
||||||
state: [DIGEST_SIZE * 3]byte,
|
|
||||||
checksum: [DIGEST_SIZE]byte,
|
|
||||||
datalen: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
PI_TABLE := [?]byte {
|
|
||||||
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
|
|
||||||
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76,
|
|
||||||
130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138,
|
|
||||||
23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142,
|
|
||||||
187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16,
|
|
||||||
137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62,
|
|
||||||
204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215,
|
|
||||||
94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182,
|
|
||||||
118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135,
|
|
||||||
32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176,
|
|
||||||
185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221,
|
|
||||||
81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110,
|
|
||||||
133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55,
|
|
||||||
200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136,
|
|
||||||
149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242,
|
|
||||||
239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10,
|
|
||||||
49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131,
|
|
||||||
20,
|
|
||||||
}
|
|
||||||
|
|
||||||
transform :: proc(ctx: ^Md2_Context, data: []byte) {
|
|
||||||
j,k,t: byte
|
|
||||||
for j = 0; j < DIGEST_SIZE; j += 1 {
|
|
||||||
ctx.state[j + DIGEST_SIZE] = data[j]
|
|
||||||
ctx.state[j + DIGEST_SIZE * 2] = (ctx.state[j + DIGEST_SIZE] ~ ctx.state[j])
|
|
||||||
}
|
|
||||||
t = 0
|
|
||||||
for j = 0; j < DIGEST_SIZE + 2; j += 1 {
|
|
||||||
for k = 0; k < DIGEST_SIZE * 3; k += 1 {
|
|
||||||
ctx.state[k] ~= PI_TABLE[t]
|
|
||||||
t = ctx.state[k]
|
|
||||||
}
|
|
||||||
t = (t + j) & 0xff
|
|
||||||
}
|
|
||||||
t = ctx.checksum[DIGEST_SIZE - 1]
|
|
||||||
for j = 0; j < DIGEST_SIZE; j += 1 {
|
|
||||||
ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
|
|
||||||
t = ctx.checksum[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
package md4
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
|
||||||
|
|
||||||
Implementation of the MD4 hashing algorithm, as defined in RFC 1320 <https://datatracker.ietf.org/doc/html/rfc1320>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:mem"
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../util"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 16
|
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Md4_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Md4_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Md4_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc(ctx: ^Md4_Context) {
|
|
||||||
ctx.state[0] = 0x67452301
|
|
||||||
ctx.state[1] = 0xefcdab89
|
|
||||||
ctx.state[2] = 0x98badcfe
|
|
||||||
ctx.state[3] = 0x10325476
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Md4_Context, data: []byte) {
|
|
||||||
for i := 0; i < len(data); i += 1 {
|
|
||||||
ctx.data[ctx.datalen] = data[i]
|
|
||||||
ctx.datalen += 1
|
|
||||||
if(ctx.datalen == BLOCK_SIZE) {
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
ctx.bitlen += 512
|
|
||||||
ctx.datalen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Md4_Context, hash: []byte) {
|
|
||||||
i := ctx.datalen
|
|
||||||
if ctx.datalen < 56 {
|
|
||||||
ctx.data[i] = 0x80
|
|
||||||
i += 1
|
|
||||||
for i < 56 {
|
|
||||||
ctx.data[i] = 0x00
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
} else if ctx.datalen >= 56 {
|
|
||||||
ctx.data[i] = 0x80
|
|
||||||
i += 1
|
|
||||||
for i < BLOCK_SIZE {
|
|
||||||
ctx.data[i] = 0x00
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
mem.set(&ctx.data, 0, 56)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.bitlen += u64(ctx.datalen * 8)
|
|
||||||
ctx.data[56] = byte(ctx.bitlen)
|
|
||||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
|
||||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
|
||||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
|
||||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
|
||||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
|
||||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
|
||||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
|
|
||||||
for i = 0; i < 4; i += 1 {
|
|
||||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
|
||||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
|
||||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
|
||||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
MD4 implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
BLOCK_SIZE :: 64
|
|
||||||
|
|
||||||
Md4_Context :: struct {
|
|
||||||
data: [64]byte,
|
|
||||||
state: [4]u32,
|
|
||||||
bitlen: u64,
|
|
||||||
datalen: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@note(zh): F, G and H, as mentioned in the RFC, have been inlined into FF, GG
|
|
||||||
and HH respectively, instead of declaring them separately.
|
|
||||||
*/
|
|
||||||
|
|
||||||
FF :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
|
||||||
return util.ROTL32(a + ((b & c) | (~b & d)) + x, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
GG :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
|
||||||
return util.ROTL32(a + ((b & c) | (b & d) | (c & d)) + x + 0x5a827999, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
|
||||||
return util.ROTL32(a + (b ~ c ~ d) + x + 0x6ed9eba1, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
transform :: proc(ctx: ^Md4_Context, data: []byte) {
|
|
||||||
a, b, c, d, i, j: u32
|
|
||||||
m: [DIGEST_SIZE]u32
|
|
||||||
|
|
||||||
for i, j = 0, 0; i < DIGEST_SIZE; i += 1 {
|
|
||||||
m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24)
|
|
||||||
j += 4
|
|
||||||
}
|
|
||||||
|
|
||||||
a = ctx.state[0]
|
|
||||||
b = ctx.state[1]
|
|
||||||
c = ctx.state[2]
|
|
||||||
d = ctx.state[3]
|
|
||||||
|
|
||||||
a = FF(a, b, c, d, m[0], 3)
|
|
||||||
d = FF(d, a, b, c, m[1], 7)
|
|
||||||
c = FF(c, d, a, b, m[2], 11)
|
|
||||||
b = FF(b, c, d, a, m[3], 19)
|
|
||||||
a = FF(a, b, c, d, m[4], 3)
|
|
||||||
d = FF(d, a, b, c, m[5], 7)
|
|
||||||
c = FF(c, d, a, b, m[6], 11)
|
|
||||||
b = FF(b, c, d, a, m[7], 19)
|
|
||||||
a = FF(a, b, c, d, m[8], 3)
|
|
||||||
d = FF(d, a, b, c, m[9], 7)
|
|
||||||
c = FF(c, d, a, b, m[10], 11)
|
|
||||||
b = FF(b, c, d, a, m[11], 19)
|
|
||||||
a = FF(a, b, c, d, m[12], 3)
|
|
||||||
d = FF(d, a, b, c, m[13], 7)
|
|
||||||
c = FF(c, d, a, b, m[14], 11)
|
|
||||||
b = FF(b, c, d, a, m[15], 19)
|
|
||||||
|
|
||||||
a = GG(a, b, c, d, m[0], 3)
|
|
||||||
d = GG(d, a, b, c, m[4], 5)
|
|
||||||
c = GG(c, d, a, b, m[8], 9)
|
|
||||||
b = GG(b, c, d, a, m[12], 13)
|
|
||||||
a = GG(a, b, c, d, m[1], 3)
|
|
||||||
d = GG(d, a, b, c, m[5], 5)
|
|
||||||
c = GG(c, d, a, b, m[9], 9)
|
|
||||||
b = GG(b, c, d, a, m[13], 13)
|
|
||||||
a = GG(a, b, c, d, m[2], 3)
|
|
||||||
d = GG(d, a, b, c, m[6], 5)
|
|
||||||
c = GG(c, d, a, b, m[10], 9)
|
|
||||||
b = GG(b, c, d, a, m[14], 13)
|
|
||||||
a = GG(a, b, c, d, m[3], 3)
|
|
||||||
d = GG(d, a, b, c, m[7], 5)
|
|
||||||
c = GG(c, d, a, b, m[11], 9)
|
|
||||||
b = GG(b, c, d, a, m[15], 13)
|
|
||||||
|
|
||||||
a = HH(a, b, c, d, m[0], 3)
|
|
||||||
d = HH(d, a, b, c, m[8], 9)
|
|
||||||
c = HH(c, d, a, b, m[4], 11)
|
|
||||||
b = HH(b, c, d, a, m[12], 15)
|
|
||||||
a = HH(a, b, c, d, m[2], 3)
|
|
||||||
d = HH(d, a, b, c, m[10], 9)
|
|
||||||
c = HH(c, d, a, b, m[6], 11)
|
|
||||||
b = HH(b, c, d, a, m[14], 15)
|
|
||||||
a = HH(a, b, c, d, m[1], 3)
|
|
||||||
d = HH(d, a, b, c, m[9], 9)
|
|
||||||
c = HH(c, d, a, b, m[5], 11)
|
|
||||||
b = HH(b, c, d, a, m[13], 15)
|
|
||||||
a = HH(a, b, c, d, m[3], 3)
|
|
||||||
d = HH(d, a, b, c, m[11], 9)
|
|
||||||
c = HH(c, d, a, b, m[7], 11)
|
|
||||||
b = HH(b, c, d, a, m[15], 15)
|
|
||||||
|
|
||||||
ctx.state[0] += a
|
|
||||||
ctx.state[1] += b
|
|
||||||
ctx.state[2] += c
|
|
||||||
ctx.state[3] += d
|
|
||||||
}
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
package md5
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:mem"
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../util"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 16
|
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Md5_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Md5_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Md5_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc(ctx: ^Md5_Context) {
|
|
||||||
ctx.state[0] = 0x67452301
|
|
||||||
ctx.state[1] = 0xefcdab89
|
|
||||||
ctx.state[2] = 0x98badcfe
|
|
||||||
ctx.state[3] = 0x10325476
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Md5_Context, data: []byte) {
|
|
||||||
for i := 0; i < len(data); i += 1 {
|
|
||||||
ctx.data[ctx.datalen] = data[i]
|
|
||||||
ctx.datalen += 1
|
|
||||||
if(ctx.datalen == BLOCK_SIZE) {
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
ctx.bitlen += 512
|
|
||||||
ctx.datalen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Md5_Context, hash: []byte){
|
|
||||||
i : u32
|
|
||||||
i = ctx.datalen
|
|
||||||
|
|
||||||
if ctx.datalen < 56 {
|
|
||||||
ctx.data[i] = 0x80
|
|
||||||
i += 1
|
|
||||||
for i < 56 {
|
|
||||||
ctx.data[i] = 0x00
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
} else if ctx.datalen >= 56 {
|
|
||||||
ctx.data[i] = 0x80
|
|
||||||
i += 1
|
|
||||||
for i < BLOCK_SIZE {
|
|
||||||
ctx.data[i] = 0x00
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
mem.set(&ctx.data, 0, 56)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.bitlen += u64(ctx.datalen * 8)
|
|
||||||
ctx.data[56] = byte(ctx.bitlen)
|
|
||||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
|
||||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
|
||||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
|
||||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
|
||||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
|
||||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
|
||||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
|
|
||||||
for i = 0; i < 4; i += 1 {
|
|
||||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
|
||||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
|
||||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
|
||||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
MD4 implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
BLOCK_SIZE :: 64
|
|
||||||
|
|
||||||
Md5_Context :: struct {
|
|
||||||
data: [BLOCK_SIZE]byte,
|
|
||||||
state: [4]u32,
|
|
||||||
bitlen: u64,
|
|
||||||
datalen: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH
|
|
||||||
and II respectively, instead of declaring them separately.
|
|
||||||
*/
|
|
||||||
|
|
||||||
FF :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
|
||||||
return b + util.ROTL32(a + ((b & c) | (~b & d)) + m + t, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
GG :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
|
||||||
return b + util.ROTL32(a + ((b & d) | (c & ~d)) + m + t, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
HH :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
|
||||||
return b + util.ROTL32(a + (b ~ c ~ d) + m + t, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
|
||||||
return b + util.ROTL32(a + (c ~ (b | ~d)) + m + t, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
transform :: proc(ctx: ^Md5_Context, data: []byte) {
|
|
||||||
i, j: u32
|
|
||||||
m: [DIGEST_SIZE]u32
|
|
||||||
|
|
||||||
for i, j = 0, 0; i < DIGEST_SIZE; i+=1 {
|
|
||||||
m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24
|
|
||||||
j += 4
|
|
||||||
}
|
|
||||||
|
|
||||||
a := ctx.state[0]
|
|
||||||
b := ctx.state[1]
|
|
||||||
c := ctx.state[2]
|
|
||||||
d := ctx.state[3]
|
|
||||||
|
|
||||||
a = FF(a, b, c, d, m[0], 7, 0xd76aa478)
|
|
||||||
d = FF(d, a, b, c, m[1], 12, 0xe8c7b756)
|
|
||||||
c = FF(c, d, a, b, m[2], 17, 0x242070db)
|
|
||||||
b = FF(b, c, d, a, m[3], 22, 0xc1bdceee)
|
|
||||||
a = FF(a, b, c, d, m[4], 7, 0xf57c0faf)
|
|
||||||
d = FF(d, a, b, c, m[5], 12, 0x4787c62a)
|
|
||||||
c = FF(c, d, a, b, m[6], 17, 0xa8304613)
|
|
||||||
b = FF(b, c, d, a, m[7], 22, 0xfd469501)
|
|
||||||
a = FF(a, b, c, d, m[8], 7, 0x698098d8)
|
|
||||||
d = FF(d, a, b, c, m[9], 12, 0x8b44f7af)
|
|
||||||
c = FF(c, d, a, b, m[10], 17, 0xffff5bb1)
|
|
||||||
b = FF(b, c, d, a, m[11], 22, 0x895cd7be)
|
|
||||||
a = FF(a, b, c, d, m[12], 7, 0x6b901122)
|
|
||||||
d = FF(d, a, b, c, m[13], 12, 0xfd987193)
|
|
||||||
c = FF(c, d, a, b, m[14], 17, 0xa679438e)
|
|
||||||
b = FF(b, c, d, a, m[15], 22, 0x49b40821)
|
|
||||||
|
|
||||||
a = GG(a, b, c, d, m[1], 5, 0xf61e2562)
|
|
||||||
d = GG(d, a, b, c, m[6], 9, 0xc040b340)
|
|
||||||
c = GG(c, d, a, b, m[11], 14, 0x265e5a51)
|
|
||||||
b = GG(b, c, d, a, m[0], 20, 0xe9b6c7aa)
|
|
||||||
a = GG(a, b, c, d, m[5], 5, 0xd62f105d)
|
|
||||||
d = GG(d, a, b, c, m[10], 9, 0x02441453)
|
|
||||||
c = GG(c, d, a, b, m[15], 14, 0xd8a1e681)
|
|
||||||
b = GG(b, c, d, a, m[4], 20, 0xe7d3fbc8)
|
|
||||||
a = GG(a, b, c, d, m[9], 5, 0x21e1cde6)
|
|
||||||
d = GG(d, a, b, c, m[14], 9, 0xc33707d6)
|
|
||||||
c = GG(c, d, a, b, m[3], 14, 0xf4d50d87)
|
|
||||||
b = GG(b, c, d, a, m[8], 20, 0x455a14ed)
|
|
||||||
a = GG(a, b, c, d, m[13], 5, 0xa9e3e905)
|
|
||||||
d = GG(d, a, b, c, m[2], 9, 0xfcefa3f8)
|
|
||||||
c = GG(c, d, a, b, m[7], 14, 0x676f02d9)
|
|
||||||
b = GG(b, c, d, a, m[12], 20, 0x8d2a4c8a)
|
|
||||||
|
|
||||||
a = HH(a, b, c, d, m[5], 4, 0xfffa3942)
|
|
||||||
d = HH(d, a, b, c, m[8], 11, 0x8771f681)
|
|
||||||
c = HH(c, d, a, b, m[11], 16, 0x6d9d6122)
|
|
||||||
b = HH(b, c, d, a, m[14], 23, 0xfde5380c)
|
|
||||||
a = HH(a, b, c, d, m[1], 4, 0xa4beea44)
|
|
||||||
d = HH(d, a, b, c, m[4], 11, 0x4bdecfa9)
|
|
||||||
c = HH(c, d, a, b, m[7], 16, 0xf6bb4b60)
|
|
||||||
b = HH(b, c, d, a, m[10], 23, 0xbebfbc70)
|
|
||||||
a = HH(a, b, c, d, m[13], 4, 0x289b7ec6)
|
|
||||||
d = HH(d, a, b, c, m[0], 11, 0xeaa127fa)
|
|
||||||
c = HH(c, d, a, b, m[3], 16, 0xd4ef3085)
|
|
||||||
b = HH(b, c, d, a, m[6], 23, 0x04881d05)
|
|
||||||
a = HH(a, b, c, d, m[9], 4, 0xd9d4d039)
|
|
||||||
d = HH(d, a, b, c, m[12], 11, 0xe6db99e5)
|
|
||||||
c = HH(c, d, a, b, m[15], 16, 0x1fa27cf8)
|
|
||||||
b = HH(b, c, d, a, m[2], 23, 0xc4ac5665)
|
|
||||||
|
|
||||||
a = II(a, b, c, d, m[0], 6, 0xf4292244)
|
|
||||||
d = II(d, a, b, c, m[7], 10, 0x432aff97)
|
|
||||||
c = II(c, d, a, b, m[14], 15, 0xab9423a7)
|
|
||||||
b = II(b, c, d, a, m[5], 21, 0xfc93a039)
|
|
||||||
a = II(a, b, c, d, m[12], 6, 0x655b59c3)
|
|
||||||
d = II(d, a, b, c, m[3], 10, 0x8f0ccc92)
|
|
||||||
c = II(c, d, a, b, m[10], 15, 0xffeff47d)
|
|
||||||
b = II(b, c, d, a, m[1], 21, 0x85845dd1)
|
|
||||||
a = II(a, b, c, d, m[8], 6, 0x6fa87e4f)
|
|
||||||
d = II(d, a, b, c, m[15], 10, 0xfe2ce6e0)
|
|
||||||
c = II(c, d, a, b, m[6], 15, 0xa3014314)
|
|
||||||
b = II(b, c, d, a, m[13], 21, 0x4e0811a1)
|
|
||||||
a = II(a, b, c, d, m[4], 6, 0xf7537e82)
|
|
||||||
d = II(d, a, b, c, m[11], 10, 0xbd3af235)
|
|
||||||
c = II(c, d, a, b, m[2], 15, 0x2ad7d2bb)
|
|
||||||
b = II(b, c, d, a, m[9], 21, 0xeb86d391)
|
|
||||||
|
|
||||||
ctx.state[0] += a
|
|
||||||
ctx.state[1] += b
|
|
||||||
ctx.state[2] += c
|
|
||||||
ctx.state[3] += d
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
import "core:crypto"
|
import "core:crypto"
|
||||||
import "core:crypto/util"
|
|
||||||
import field "core:crypto/_fiat/field_poly1305"
|
import field "core:crypto/_fiat/field_poly1305"
|
||||||
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
KEY_SIZE :: 32
|
KEY_SIZE :: 32
|
||||||
@@ -23,10 +23,6 @@ verify :: proc (tag, msg, key: []byte) -> bool {
|
|||||||
ctx: Context = ---
|
ctx: Context = ---
|
||||||
derived_tag: [16]byte = ---
|
derived_tag: [16]byte = ---
|
||||||
|
|
||||||
if len(tag) != TAG_SIZE {
|
|
||||||
panic("crypto/poly1305: invalid tag size")
|
|
||||||
}
|
|
||||||
|
|
||||||
init(&ctx, key)
|
init(&ctx, key)
|
||||||
update(&ctx, msg)
|
update(&ctx, msg)
|
||||||
final(&ctx, derived_tag[:])
|
final(&ctx, derived_tag[:])
|
||||||
@@ -52,8 +48,8 @@ init :: proc (ctx: ^Context, key: []byte) {
|
|||||||
|
|
||||||
// r = le_bytes_to_num(key[0..15])
|
// r = le_bytes_to_num(key[0..15])
|
||||||
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
||||||
tmp_lo := util.U64_LE(key[0:8]) & 0x0ffffffc0fffffff
|
tmp_lo := endian.unchecked_get_u64le(key[0:]) & 0x0ffffffc0fffffff
|
||||||
tmp_hi := util.U64_LE(key[8:16]) & 0xffffffc0ffffffc
|
tmp_hi := endian.unchecked_get_u64le(key[8:]) & 0xffffffc0ffffffc
|
||||||
field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
|
field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
|
||||||
|
|
||||||
// s = le_bytes_to_num(key[16..31])
|
// s = le_bytes_to_num(key[16..31])
|
||||||
@@ -151,7 +147,7 @@ _blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
|
|||||||
data_len := len(data)
|
data_len := len(data)
|
||||||
for data_len >= _BLOCK_SIZE {
|
for data_len >= _BLOCK_SIZE {
|
||||||
// n = le_bytes_to_num(msg[((i-1)*16)..*i*16] | [0x01])
|
// n = le_bytes_to_num(msg[((i-1)*16)..*i*16] | [0x01])
|
||||||
field.fe_from_bytes(&n, data[:_BLOCK_SIZE], final_byte, false)
|
field.fe_from_bytes(&n, data[:_BLOCK_SIZE], final_byte)
|
||||||
|
|
||||||
// a += n
|
// a += n
|
||||||
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &n) // _a unreduced
|
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &n) // _a unreduced
|
||||||
|
|||||||
@@ -1,919 +0,0 @@
|
|||||||
package ripemd
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation for the RIPEMD hashing algorithm as defined in <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../util"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE_128 :: 16
|
|
||||||
DIGEST_SIZE_160 :: 20
|
|
||||||
DIGEST_SIZE_256 :: 32
|
|
||||||
DIGEST_SIZE_320 :: 40
|
|
||||||
|
|
||||||
// hash_string_128 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
|
|
||||||
return hash_bytes_128(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_128 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
|
|
||||||
hash: [DIGEST_SIZE_128]byte
|
|
||||||
ctx: Ripemd128_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_128 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_128 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Ripemd128_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_128 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_128]byte
|
|
||||||
ctx: Ripemd128_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_128 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_128(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_128(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_128]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_128 :: proc {
|
|
||||||
hash_stream_128,
|
|
||||||
hash_file_128,
|
|
||||||
hash_bytes_128,
|
|
||||||
hash_string_128,
|
|
||||||
hash_bytes_to_buffer_128,
|
|
||||||
hash_string_to_buffer_128,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_160 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
|
|
||||||
return hash_bytes_160(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_160 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
|
|
||||||
hash: [DIGEST_SIZE_160]byte
|
|
||||||
ctx: Ripemd160_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_160 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_160 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Ripemd160_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_160 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_160]byte
|
|
||||||
ctx: Ripemd160_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_160 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_160(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_160(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_160]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_160 :: proc {
|
|
||||||
hash_stream_160,
|
|
||||||
hash_file_160,
|
|
||||||
hash_bytes_160,
|
|
||||||
hash_string_160,
|
|
||||||
hash_bytes_to_buffer_160,
|
|
||||||
hash_string_to_buffer_160,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
|
||||||
return hash_bytes_256(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_256 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Ripemd256_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Ripemd256_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_256 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_256]byte
|
|
||||||
ctx: Ripemd256_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_256 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_256(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_256(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_256]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_256 :: proc {
|
|
||||||
hash_stream_256,
|
|
||||||
hash_file_256,
|
|
||||||
hash_bytes_256,
|
|
||||||
hash_string_256,
|
|
||||||
hash_bytes_to_buffer_256,
|
|
||||||
hash_string_to_buffer_256,
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_320 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string_320 :: proc(data: string) -> [DIGEST_SIZE_320]byte {
|
|
||||||
return hash_bytes_320(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_320 will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte {
|
|
||||||
hash: [DIGEST_SIZE_320]byte
|
|
||||||
ctx: Ripemd320_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer_320 will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer_320 :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer_320(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer_320 will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer_320 :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE_320, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Ripemd320_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream_320 will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE_320]byte
|
|
||||||
ctx: Ripemd320_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file_320 will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_320]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream_320(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes_320(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE_320]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_320 :: proc {
|
|
||||||
hash_stream_320,
|
|
||||||
hash_file_320,
|
|
||||||
hash_bytes_320,
|
|
||||||
hash_string_320,
|
|
||||||
hash_bytes_to_buffer_320,
|
|
||||||
hash_string_to_buffer_320,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc(ctx: ^$T) {
|
|
||||||
when T == Ripemd128_Context {
|
|
||||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
|
||||||
} else when T == Ripemd160_Context {
|
|
||||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
|
||||||
} else when T == Ripemd256_Context {
|
|
||||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
|
||||||
ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
|
|
||||||
} else when T == Ripemd320_Context {
|
|
||||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
|
||||||
ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^$T, data: []byte) {
|
|
||||||
ctx.tc += u64(len(data))
|
|
||||||
data := data
|
|
||||||
if ctx.nx > 0 {
|
|
||||||
n := len(data)
|
|
||||||
|
|
||||||
when T == Ripemd128_Context {
|
|
||||||
if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
|
|
||||||
n = RIPEMD_128_BLOCK_SIZE - ctx.nx
|
|
||||||
}
|
|
||||||
} else when T == Ripemd160_Context {
|
|
||||||
if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
|
|
||||||
n = RIPEMD_160_BLOCK_SIZE - ctx.nx
|
|
||||||
}
|
|
||||||
} else when T == Ripemd256_Context{
|
|
||||||
if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
|
|
||||||
n = RIPEMD_256_BLOCK_SIZE - ctx.nx
|
|
||||||
}
|
|
||||||
} else when T == Ripemd320_Context{
|
|
||||||
if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
|
|
||||||
n = RIPEMD_320_BLOCK_SIZE - ctx.nx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < n; i += 1 {
|
|
||||||
ctx.x[ctx.nx + i] = data[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.nx += n
|
|
||||||
when T == Ripemd128_Context {
|
|
||||||
if ctx.nx == RIPEMD_128_BLOCK_SIZE {
|
|
||||||
block(ctx, ctx.x[0:])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
} else when T == Ripemd160_Context {
|
|
||||||
if ctx.nx == RIPEMD_160_BLOCK_SIZE {
|
|
||||||
block(ctx, ctx.x[0:])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
} else when T == Ripemd256_Context{
|
|
||||||
if ctx.nx == RIPEMD_256_BLOCK_SIZE {
|
|
||||||
block(ctx, ctx.x[0:])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
} else when T == Ripemd320_Context{
|
|
||||||
if ctx.nx == RIPEMD_320_BLOCK_SIZE {
|
|
||||||
block(ctx, ctx.x[0:])
|
|
||||||
ctx.nx = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data = data[n:]
|
|
||||||
}
|
|
||||||
n := block(ctx, data)
|
|
||||||
data = data[n:]
|
|
||||||
if len(data) > 0 {
|
|
||||||
ctx.nx = copy(ctx.x[:], data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^$T, hash: []byte) {
|
|
||||||
d := ctx
|
|
||||||
tc := d.tc
|
|
||||||
tmp: [64]byte
|
|
||||||
tmp[0] = 0x80
|
|
||||||
|
|
||||||
if tc % 64 < 56 {
|
|
||||||
update(d, tmp[0:56 - tc % 64])
|
|
||||||
} else {
|
|
||||||
update(d, tmp[0:64 + 56 - tc % 64])
|
|
||||||
}
|
|
||||||
|
|
||||||
tc <<= 3
|
|
||||||
for i : u32 = 0; i < 8; i += 1 {
|
|
||||||
tmp[i] = byte(tc >> (8 * i))
|
|
||||||
}
|
|
||||||
|
|
||||||
update(d, tmp[0:8])
|
|
||||||
|
|
||||||
when T == Ripemd128_Context {
|
|
||||||
size :: RIPEMD_128_SIZE
|
|
||||||
} else when T == Ripemd160_Context {
|
|
||||||
size :: RIPEMD_160_SIZE
|
|
||||||
} else when T == Ripemd256_Context{
|
|
||||||
size :: RIPEMD_256_SIZE
|
|
||||||
} else when T == Ripemd320_Context{
|
|
||||||
size :: RIPEMD_320_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
digest: [size]byte
|
|
||||||
for s, i in d.s {
|
|
||||||
digest[i * 4] = byte(s)
|
|
||||||
digest[i * 4 + 1] = byte(s >> 8)
|
|
||||||
digest[i * 4 + 2] = byte(s >> 16)
|
|
||||||
digest[i * 4 + 3] = byte(s >> 24)
|
|
||||||
}
|
|
||||||
copy(hash[:], digest[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
RIPEMD implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
Ripemd128_Context :: struct {
|
|
||||||
s: [4]u32,
|
|
||||||
x: [RIPEMD_128_BLOCK_SIZE]byte,
|
|
||||||
nx: int,
|
|
||||||
tc: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ripemd160_Context :: struct {
|
|
||||||
s: [5]u32,
|
|
||||||
x: [RIPEMD_160_BLOCK_SIZE]byte,
|
|
||||||
nx: int,
|
|
||||||
tc: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ripemd256_Context :: struct {
|
|
||||||
s: [8]u32,
|
|
||||||
x: [RIPEMD_256_BLOCK_SIZE]byte,
|
|
||||||
nx: int,
|
|
||||||
tc: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ripemd320_Context :: struct {
|
|
||||||
s: [10]u32,
|
|
||||||
x: [RIPEMD_320_BLOCK_SIZE]byte,
|
|
||||||
nx: int,
|
|
||||||
tc: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_128_SIZE :: 16
|
|
||||||
RIPEMD_128_BLOCK_SIZE :: 64
|
|
||||||
RIPEMD_160_SIZE :: 20
|
|
||||||
RIPEMD_160_BLOCK_SIZE :: 64
|
|
||||||
RIPEMD_256_SIZE :: 32
|
|
||||||
RIPEMD_256_BLOCK_SIZE :: 64
|
|
||||||
RIPEMD_320_SIZE :: 40
|
|
||||||
RIPEMD_320_BLOCK_SIZE :: 64
|
|
||||||
|
|
||||||
S0 :: 0x67452301
|
|
||||||
S1 :: 0xefcdab89
|
|
||||||
S2 :: 0x98badcfe
|
|
||||||
S3 :: 0x10325476
|
|
||||||
S4 :: 0xc3d2e1f0
|
|
||||||
S5 :: 0x76543210
|
|
||||||
S6 :: 0xfedcba98
|
|
||||||
S7 :: 0x89abcdef
|
|
||||||
S8 :: 0x01234567
|
|
||||||
S9 :: 0x3c2d1e0f
|
|
||||||
|
|
||||||
RIPEMD_128_N0 := [64]uint {
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
|
||||||
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
|
||||||
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_128_R0 := [64]uint {
|
|
||||||
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
|
||||||
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
|
||||||
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
|
||||||
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_128_N1 := [64]uint {
|
|
||||||
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
|
||||||
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
|
||||||
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
|
||||||
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_128_R1 := [64]uint {
|
|
||||||
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
|
||||||
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
|
||||||
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
|
||||||
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_160_N0 := [80]uint {
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
||||||
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
|
||||||
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
|
||||||
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
|
||||||
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_160_R0 := [80]uint {
|
|
||||||
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
|
||||||
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
|
||||||
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
|
||||||
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
|
||||||
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_160_N1 := [80]uint {
|
|
||||||
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
|
||||||
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
|
||||||
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
|
||||||
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
|
||||||
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11,
|
|
||||||
}
|
|
||||||
|
|
||||||
RIPEMD_160_R1 := [80]uint {
|
|
||||||
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
|
||||||
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
|
||||||
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
|
||||||
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
|
||||||
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
|
|
||||||
}
|
|
||||||
|
|
||||||
block :: #force_inline proc (ctx: ^$T, p: []byte) -> int {
|
|
||||||
when T == Ripemd128_Context {
|
|
||||||
return ripemd_128_block(ctx, p)
|
|
||||||
}
|
|
||||||
else when T == Ripemd160_Context {
|
|
||||||
return ripemd_160_block(ctx, p)
|
|
||||||
}
|
|
||||||
else when T == Ripemd256_Context {
|
|
||||||
return ripemd_256_block(ctx, p)
|
|
||||||
}
|
|
||||||
else when T == Ripemd320_Context {
|
|
||||||
return ripemd_320_block(ctx, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ripemd_128_block :: proc(ctx: ^$T, p: []byte) -> int {
|
|
||||||
n := 0
|
|
||||||
x: [16]u32 = ---
|
|
||||||
alpha: u32 = ---
|
|
||||||
p := p
|
|
||||||
for len(p) >= RIPEMD_128_BLOCK_SIZE {
|
|
||||||
a, b, c, d := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3]
|
|
||||||
aa, bb, cc, dd := a, b, c, d
|
|
||||||
for i,j := 0, 0; i < 16; i, j = i+1, j+4 {
|
|
||||||
x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24
|
|
||||||
}
|
|
||||||
i := 0
|
|
||||||
for i < 16 {
|
|
||||||
alpha = a + (b ~ c ~ d) + x[RIPEMD_128_N0[i]]
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (bb & dd | cc &~ dd) + x[RIPEMD_128_N1[i]] + 0x50a28be6
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd= dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 32 {
|
|
||||||
alpha = a + (d ~ (b & (c~d))) + x[RIPEMD_128_N0[i]] + 0x5a827999
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (dd ~ (bb | ~cc)) + x[RIPEMD_128_N1[i]] + 0x5c4dd124
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd = dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 48 {
|
|
||||||
alpha = a + (d ~ (b | ~c)) + x[RIPEMD_128_N0[i]] + 0x6ed9eba1
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (dd ~ (bb & (cc~dd))) + x[RIPEMD_128_N1[i]] + 0x6d703ef3
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd = dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 64 {
|
|
||||||
alpha = a + (c ~ (d & (b~c))) + x[RIPEMD_128_N0[i]] + 0x8f1bbcdc
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_128_N1[i]]
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd = dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
c = ctx.s[1] + c + dd
|
|
||||||
ctx.s[1] = ctx.s[2] + d + aa
|
|
||||||
ctx.s[2] = ctx.s[3] + a + bb
|
|
||||||
ctx.s[3] = ctx.s[0] + b + cc
|
|
||||||
ctx.s[0] = c
|
|
||||||
p = p[RIPEMD_128_BLOCK_SIZE:]
|
|
||||||
n += RIPEMD_128_BLOCK_SIZE
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
ripemd_160_block :: proc(ctx: ^$T, p: []byte) -> int {
|
|
||||||
n := 0
|
|
||||||
x: [16]u32 = ---
|
|
||||||
alpha, beta: u32 = ---, ---
|
|
||||||
p := p
|
|
||||||
for len(p) >= RIPEMD_160_BLOCK_SIZE {
|
|
||||||
a, b, c, d, e := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4]
|
|
||||||
aa, bb, cc, dd, ee := a, b, c, d, e
|
|
||||||
for i,j := 0, 0; i < 16; i, j = i+1, j+4 {
|
|
||||||
x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24
|
|
||||||
}
|
|
||||||
i := 0
|
|
||||||
for i < 16 {
|
|
||||||
alpha = a + (b ~ c ~ d) + x[RIPEMD_160_N0[i]]
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb ~ (cc | ~dd)) + x[RIPEMD_160_N1[i]] + 0x50a28be6
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 32 {
|
|
||||||
alpha = a + (b&c | ~b&d) + x[RIPEMD_160_N0[i]] + 0x5a827999
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb&dd | cc&~dd) + x[RIPEMD_160_N1[i]] + 0x5c4dd124
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 48 {
|
|
||||||
alpha = a + (b | ~c ~ d) + x[RIPEMD_160_N0[i]] + 0x6ed9eba1
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb | ~cc ~ dd) + x[RIPEMD_160_N1[i]] + 0x6d703ef3
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 64 {
|
|
||||||
alpha = a + (b&d | c&~d) + x[RIPEMD_160_N0[i]] + 0x8f1bbcdc
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb&cc | ~bb&dd) + x[RIPEMD_160_N1[i]] + 0x7a6d76e9
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 80 {
|
|
||||||
alpha = a + (b ~ (c | ~d)) + x[RIPEMD_160_N0[i]] + 0xa953fd4e
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_160_N1[i]]
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
dd += c + ctx.s[1]
|
|
||||||
ctx.s[1] = ctx.s[2] + d + ee
|
|
||||||
ctx.s[2] = ctx.s[3] + e + aa
|
|
||||||
ctx.s[3] = ctx.s[4] + a + bb
|
|
||||||
ctx.s[4] = ctx.s[0] + b + cc
|
|
||||||
ctx.s[0] = dd
|
|
||||||
p = p[RIPEMD_160_BLOCK_SIZE:]
|
|
||||||
n += RIPEMD_160_BLOCK_SIZE
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
ripemd_256_block :: proc(ctx: ^$T, p: []byte) -> int {
|
|
||||||
n := 0
|
|
||||||
x: [16]u32 = ---
|
|
||||||
alpha: u32 = ---
|
|
||||||
p := p
|
|
||||||
for len(p) >= RIPEMD_256_BLOCK_SIZE {
|
|
||||||
a, b, c, d := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3]
|
|
||||||
aa, bb, cc, dd := ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7]
|
|
||||||
for i,j := 0, 0; i < 16; i, j = i+1, j+4 {
|
|
||||||
x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24
|
|
||||||
}
|
|
||||||
i := 0
|
|
||||||
for i < 16 {
|
|
||||||
alpha = a + (b ~ c ~ d) + x[RIPEMD_128_N0[i]]
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (bb & dd | cc &~ dd) + x[RIPEMD_128_N1[i]] + 0x50a28be6
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd= dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t := a
|
|
||||||
a = aa
|
|
||||||
aa = t
|
|
||||||
for i < 32 {
|
|
||||||
alpha = a + (d ~ (b & (c~d))) + x[RIPEMD_128_N0[i]] + 0x5a827999
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (dd ~ (bb | ~cc)) + x[RIPEMD_128_N1[i]] + 0x5c4dd124
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd = dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = b
|
|
||||||
b = bb
|
|
||||||
bb = t
|
|
||||||
for i < 48 {
|
|
||||||
alpha = a + (d ~ (b | ~c)) + x[RIPEMD_128_N0[i]] + 0x6ed9eba1
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (dd ~ (bb & (cc~dd))) + x[RIPEMD_128_N1[i]] + 0x6d703ef3
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd = dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = c
|
|
||||||
c = cc
|
|
||||||
cc = t
|
|
||||||
for i < 64 {
|
|
||||||
alpha = a + (c ~ (d & (b~c))) + x[RIPEMD_128_N0[i]] + 0x8f1bbcdc
|
|
||||||
s := int(RIPEMD_128_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
a, b, c, d = d, alpha, b, c
|
|
||||||
alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_128_N1[i]]
|
|
||||||
s = int(RIPEMD_128_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s)
|
|
||||||
aa, bb, cc, dd = dd, alpha, bb, cc
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = d
|
|
||||||
d = dd
|
|
||||||
dd = t
|
|
||||||
ctx.s[0] += a
|
|
||||||
ctx.s[1] += b
|
|
||||||
ctx.s[2] += c
|
|
||||||
ctx.s[3] += d
|
|
||||||
ctx.s[4] += aa
|
|
||||||
ctx.s[5] += bb
|
|
||||||
ctx.s[6] += cc
|
|
||||||
ctx.s[7] += dd
|
|
||||||
p = p[RIPEMD_256_BLOCK_SIZE:]
|
|
||||||
n += RIPEMD_256_BLOCK_SIZE
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int {
|
|
||||||
n := 0
|
|
||||||
x: [16]u32 = ---
|
|
||||||
alpha, beta: u32 = ---, ---
|
|
||||||
p := p
|
|
||||||
for len(p) >= RIPEMD_320_BLOCK_SIZE {
|
|
||||||
a, b, c, d, e := ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4]
|
|
||||||
aa, bb, cc, dd, ee := ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9]
|
|
||||||
for i,j := 0, 0; i < 16; i, j = i+1, j+4 {
|
|
||||||
x[i] = u32(p[j]) | u32(p[j+1])<<8 | u32(p[j+2])<<16 | u32(p[j+3])<<24
|
|
||||||
}
|
|
||||||
i := 0
|
|
||||||
for i < 16 {
|
|
||||||
alpha = a + (b ~ c ~ d) + x[RIPEMD_160_N0[i]]
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb ~ (cc | ~dd)) + x[RIPEMD_160_N1[i]] + 0x50a28be6
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t := b
|
|
||||||
b = bb
|
|
||||||
bb = t
|
|
||||||
for i < 32 {
|
|
||||||
alpha = a + (b&c | ~b&d) + x[RIPEMD_160_N0[i]] + 0x5a827999
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb&dd | cc&~dd) + x[RIPEMD_160_N1[i]] + 0x5c4dd124
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = d
|
|
||||||
d = dd
|
|
||||||
dd = t
|
|
||||||
for i < 48 {
|
|
||||||
alpha = a + (b | ~c ~ d) + x[RIPEMD_160_N0[i]] + 0x6ed9eba1
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb | ~cc ~ dd) + x[RIPEMD_160_N1[i]] + 0x6d703ef3
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = a
|
|
||||||
a = aa
|
|
||||||
aa = t
|
|
||||||
for i < 64 {
|
|
||||||
alpha = a + (b&d | c&~d) + x[RIPEMD_160_N0[i]] + 0x8f1bbcdc
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb&cc | ~bb&dd) + x[RIPEMD_160_N1[i]] + 0x7a6d76e9
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = c
|
|
||||||
c = cc
|
|
||||||
cc = t
|
|
||||||
for i < 80 {
|
|
||||||
alpha = a + (b ~ (c | ~d)) + x[RIPEMD_160_N0[i]] + 0xa953fd4e
|
|
||||||
s := int(RIPEMD_160_R0[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + e
|
|
||||||
beta = util.ROTL32(c, 10)
|
|
||||||
a, b, c, d, e = e, alpha, b, beta, d
|
|
||||||
alpha = aa + (bb ~ cc ~ dd) + x[RIPEMD_160_N1[i]]
|
|
||||||
s = int(RIPEMD_160_R1[i])
|
|
||||||
alpha = util.ROTL32(alpha, s) + ee
|
|
||||||
beta = util.ROTL32(cc, 10)
|
|
||||||
aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
t = e
|
|
||||||
e = ee
|
|
||||||
ee = t
|
|
||||||
ctx.s[0] += a
|
|
||||||
ctx.s[1] += b
|
|
||||||
ctx.s[2] += c
|
|
||||||
ctx.s[3] += d
|
|
||||||
ctx.s[4] += e
|
|
||||||
ctx.s[5] += aa
|
|
||||||
ctx.s[6] += bb
|
|
||||||
ctx.s[7] += cc
|
|
||||||
ctx.s[8] += dd
|
|
||||||
ctx.s[9] += ee
|
|
||||||
p = p[RIPEMD_320_BLOCK_SIZE:]
|
|
||||||
n += RIPEMD_320_BLOCK_SIZE
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
package sha1
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 zhibog
|
|
||||||
Made available under the BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
zhibog, dotbmp: Initial implementation.
|
|
||||||
|
|
||||||
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:mem"
|
|
||||||
import "core:os"
|
|
||||||
import "core:io"
|
|
||||||
|
|
||||||
import "../util"
|
|
||||||
|
|
||||||
/*
|
|
||||||
High level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
DIGEST_SIZE :: 20
|
|
||||||
|
|
||||||
// hash_string will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
|
||||||
return hash_bytes(transmute([]byte)(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the
|
|
||||||
// computed hash
|
|
||||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Sha1_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
|
||||||
// computed hash to the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
|
||||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
|
||||||
// computed hash into the second parameter.
|
|
||||||
// It requires that the destination buffer is at least as big as the digest size
|
|
||||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
|
||||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
|
||||||
ctx: Sha1_Context
|
|
||||||
init(&ctx)
|
|
||||||
update(&ctx, data)
|
|
||||||
final(&ctx, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_stream will read the stream in chunks and compute a
|
|
||||||
// hash from its contents
|
|
||||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
hash: [DIGEST_SIZE]byte
|
|
||||||
ctx: Sha1_Context
|
|
||||||
init(&ctx)
|
|
||||||
buf := make([]byte, 512)
|
|
||||||
defer delete(buf)
|
|
||||||
read := 1
|
|
||||||
for read > 0 {
|
|
||||||
read, _ = io.read(s, buf)
|
|
||||||
if read > 0 {
|
|
||||||
update(&ctx, buf[:read])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final(&ctx, hash[:])
|
|
||||||
return hash, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle
|
|
||||||
// and compute a hash
|
|
||||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(os.stream_from_handle(hd))
|
|
||||||
} else {
|
|
||||||
if buf, ok := os.read_entire_file(hd); ok {
|
|
||||||
return hash_bytes(buf[:]), ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [DIGEST_SIZE]byte{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Low level API
|
|
||||||
*/
|
|
||||||
|
|
||||||
init :: proc(ctx: ^Sha1_Context) {
|
|
||||||
ctx.state[0] = 0x67452301
|
|
||||||
ctx.state[1] = 0xefcdab89
|
|
||||||
ctx.state[2] = 0x98badcfe
|
|
||||||
ctx.state[3] = 0x10325476
|
|
||||||
ctx.state[4] = 0xc3d2e1f0
|
|
||||||
ctx.k[0] = 0x5a827999
|
|
||||||
ctx.k[1] = 0x6ed9eba1
|
|
||||||
ctx.k[2] = 0x8f1bbcdc
|
|
||||||
ctx.k[3] = 0xca62c1d6
|
|
||||||
}
|
|
||||||
|
|
||||||
update :: proc(ctx: ^Sha1_Context, data: []byte) {
|
|
||||||
for i := 0; i < len(data); i += 1 {
|
|
||||||
ctx.data[ctx.datalen] = data[i]
|
|
||||||
ctx.datalen += 1
|
|
||||||
if (ctx.datalen == BLOCK_SIZE) {
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
ctx.bitlen += 512
|
|
||||||
ctx.datalen = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final :: proc(ctx: ^Sha1_Context, hash: []byte) {
|
|
||||||
i := ctx.datalen
|
|
||||||
|
|
||||||
if ctx.datalen < 56 {
|
|
||||||
ctx.data[i] = 0x80
|
|
||||||
i += 1
|
|
||||||
for i < 56 {
|
|
||||||
ctx.data[i] = 0x00
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ctx.data[i] = 0x80
|
|
||||||
i += 1
|
|
||||||
for i < BLOCK_SIZE {
|
|
||||||
ctx.data[i] = 0x00
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
mem.set(&ctx.data, 0, 56)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.bitlen += u64(ctx.datalen * 8)
|
|
||||||
ctx.data[63] = u8(ctx.bitlen)
|
|
||||||
ctx.data[62] = u8(ctx.bitlen >> 8)
|
|
||||||
ctx.data[61] = u8(ctx.bitlen >> 16)
|
|
||||||
ctx.data[60] = u8(ctx.bitlen >> 24)
|
|
||||||
ctx.data[59] = u8(ctx.bitlen >> 32)
|
|
||||||
ctx.data[58] = u8(ctx.bitlen >> 40)
|
|
||||||
ctx.data[57] = u8(ctx.bitlen >> 48)
|
|
||||||
ctx.data[56] = u8(ctx.bitlen >> 56)
|
|
||||||
transform(ctx, ctx.data[:])
|
|
||||||
|
|
||||||
for j: u32 = 0; j < 4; j += 1 {
|
|
||||||
hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
|
|
||||||
hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
|
|
||||||
hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
|
|
||||||
hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
|
|
||||||
hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
SHA1 implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
BLOCK_SIZE :: 64
|
|
||||||
|
|
||||||
Sha1_Context :: struct {
|
|
||||||
data: [BLOCK_SIZE]byte,
|
|
||||||
datalen: u32,
|
|
||||||
bitlen: u64,
|
|
||||||
state: [5]u32,
|
|
||||||
k: [4]u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
transform :: proc(ctx: ^Sha1_Context, data: []byte) {
|
|
||||||
a, b, c, d, e, i, j, t: u32
|
|
||||||
m: [80]u32
|
|
||||||
|
|
||||||
for i, j = 0, 0; i < 16; i += 1 {
|
|
||||||
m[i] = u32(data[j]) << 24 + u32(data[j + 1]) << 16 + u32(data[j + 2]) << 8 + u32(data[j + 3])
|
|
||||||
j += 4
|
|
||||||
}
|
|
||||||
for i < 80 {
|
|
||||||
m[i] = (m[i - 3] ~ m[i - 8] ~ m[i - 14] ~ m[i - 16])
|
|
||||||
m[i] = (m[i] << 1) | (m[i] >> 31)
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
a = ctx.state[0]
|
|
||||||
b = ctx.state[1]
|
|
||||||
c = ctx.state[2]
|
|
||||||
d = ctx.state[3]
|
|
||||||
e = ctx.state[4]
|
|
||||||
|
|
||||||
for i = 0; i < 20; i += 1 {
|
|
||||||
t = util.ROTL32(a, 5) + ((b & c) ~ (~b & d)) + e + ctx.k[0] + m[i]
|
|
||||||
e = d
|
|
||||||
d = c
|
|
||||||
c = util.ROTL32(b, 30)
|
|
||||||
b = a
|
|
||||||
a = t
|
|
||||||
}
|
|
||||||
for i < 40 {
|
|
||||||
t = util.ROTL32(a, 5) + (b ~ c ~ d) + e + ctx.k[1] + m[i]
|
|
||||||
e = d
|
|
||||||
d = c
|
|
||||||
c = util.ROTL32(b, 30)
|
|
||||||
b = a
|
|
||||||
a = t
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 60 {
|
|
||||||
t = util.ROTL32(a, 5) + ((b & c) ~ (b & d) ~ (c & d)) + e + ctx.k[2] + m[i]
|
|
||||||
e = d
|
|
||||||
d = c
|
|
||||||
c = util.ROTL32(b, 30)
|
|
||||||
b = a
|
|
||||||
a = t
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
for i < 80 {
|
|
||||||
t = util.ROTL32(a, 5) + (b ~ c ~ d) + e + ctx.k[3] + m[i]
|
|
||||||
e = d
|
|
||||||
d = c
|
|
||||||
c = util.ROTL32(b, 30)
|
|
||||||
b = a
|
|
||||||
a = t
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.state[0] += a
|
|
||||||
ctx.state[1] += b
|
|
||||||
ctx.state[2] += c
|
|
||||||
ctx.state[3] += d
|
|
||||||
ctx.state[4] += e
|
|
||||||
}
|
|
||||||
+345
-560
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user