feat(webauthn): socle — vérifieur, clé de scellement, quota d'échecsUnverified
WEBAUTHN_ORIGIN explicite (absente = feature éteinte), état de cérémonie scellé HMAC dans un cookie éphémère (motif csrf.rs + expiration + étanchéité reg/auth), Quota 10 échecs/h/IP.
aeae9bb parent: 7f1719b modified
Cargo.lock +357 -17 | @@ -147,6 +147,45 @@ dependencies = [ | ||
| 147 | 147 | "winnow", |
| 148 | 148 | ] |
| 149 | 149 | |
| 150 | +[[package]] | |
| 151 | +name = "asn1-rs" | |
| 152 | +version = "0.6.2" | |
| 153 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 154 | +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" | |
| 155 | +dependencies = [ | |
| 156 | + "asn1-rs-derive", | |
| 157 | + "asn1-rs-impl", | |
| 158 | + "displaydoc", | |
| 159 | + "nom", | |
| 160 | + "num-traits", | |
| 161 | + "rusticata-macros", | |
| 162 | + "thiserror 1.0.69", | |
| 163 | + "time", | |
| 164 | +] | |
| 165 | + | |
| 166 | +[[package]] | |
| 167 | +name = "asn1-rs-derive" | |
| 168 | +version = "0.5.1" | |
| 169 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 170 | +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" | |
| 171 | +dependencies = [ | |
| 172 | + "proc-macro2", | |
| 173 | + "quote", | |
| 174 | + "syn 2.0.119", | |
| 175 | + "synstructure", | |
| 176 | +] | |
| 177 | + | |
| 178 | +[[package]] | |
| 179 | +name = "asn1-rs-impl" | |
| 180 | +version = "0.2.0" | |
| 181 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 182 | +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" | |
| 183 | +dependencies = [ | |
| 184 | + "proc-macro2", | |
| 185 | + "quote", | |
| 186 | + "syn 2.0.119", | |
| 187 | +] | |
| 188 | + | |
| 150 | 189 | [[package]] |
| 151 | 190 | name = "atoi" |
| 152 | 191 | version = "2.0.0" |
| @@ -254,6 +293,12 @@ version = "0.13.1" | ||
| 254 | 293 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 255 | 294 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" |
| 256 | 295 | |
| 296 | +[[package]] | |
| 297 | +name = "base64" | |
| 298 | +version = "0.21.7" | |
| 299 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 300 | +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" | |
| 301 | + | |
| 257 | 302 | [[package]] |
| 258 | 303 | name = "base64" |
| 259 | 304 | version = "0.22.1" |
| @@ -266,6 +311,17 @@ version = "1.8.3" | ||
| 266 | 311 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 267 | 312 | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" |
| 268 | 313 | |
| 314 | +[[package]] | |
| 315 | +name = "base64urlsafedata" | |
| 316 | +version = "0.5.5" | |
| 317 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 318 | +checksum = "b08e33815c87d8cadcddb1e74ac307368a3751fbe40c961538afa21a1899f21c" | |
| 319 | +dependencies = [ | |
| 320 | + "base64 0.21.7", | |
| 321 | + "pastey", | |
| 322 | + "serde", | |
| 323 | +] | |
| 324 | + | |
| 269 | 325 | [[package]] |
| 270 | 326 | name = "basic-toml" |
| 271 | 327 | version = "0.1.10" |
| @@ -294,8 +350,8 @@ dependencies = [ | ||
| 294 | 350 | "pkcs8 0.9.0", |
| 295 | 351 | "prost", |
| 296 | 352 | "prost-types", |
| 297 | - "rand", | |
| 298 | - "rand_core", | |
| 353 | + "rand 0.8.7", | |
| 354 | + "rand_core 0.6.4", | |
| 299 | 355 | "regex", |
| 300 | 356 | "serde_json", |
| 301 | 357 | "sha2 0.9.9", |
| @@ -533,6 +589,12 @@ version = "0.8.22" | ||
| 533 | 589 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 534 | 590 | checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" |
| 535 | 591 | |
| 592 | +[[package]] | |
| 593 | +name = "crunchy" | |
| 594 | +version = "0.2.4" | |
| 595 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 596 | +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" | |
| 597 | + | |
| 536 | 598 | [[package]] |
| 537 | 599 | name = "crypto-bigint" |
| 538 | 600 | version = "0.5.5" |
| @@ -540,7 +602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 540 | 602 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" |
| 541 | 603 | dependencies = [ |
| 542 | 604 | "generic-array", |
| 543 | - "rand_core", | |
| 605 | + "rand_core 0.6.4", | |
| 544 | 606 | "subtle", |
| 545 | 607 | "zeroize", |
| 546 | 608 | ] |
| @@ -582,6 +644,12 @@ dependencies = [ | ||
| 582 | 644 | "syn 2.0.119", |
| 583 | 645 | ] |
| 584 | 646 | |
| 647 | +[[package]] | |
| 648 | +name = "data-encoding" | |
| 649 | +version = "2.11.1" | |
| 650 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 651 | +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" | |
| 652 | + | |
| 585 | 653 | [[package]] |
| 586 | 654 | name = "der" |
| 587 | 655 | version = "0.6.1" |
| @@ -602,6 +670,20 @@ dependencies = [ | ||
| 602 | 670 | "zeroize", |
| 603 | 671 | ] |
| 604 | 672 | |
| 673 | +[[package]] | |
| 674 | +name = "der-parser" | |
| 675 | +version = "9.0.0" | |
| 676 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 677 | +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" | |
| 678 | +dependencies = [ | |
| 679 | + "asn1-rs", | |
| 680 | + "displaydoc", | |
| 681 | + "nom", | |
| 682 | + "num-bigint", | |
| 683 | + "num-traits", | |
| 684 | + "rusticata-macros", | |
| 685 | +] | |
| 686 | + | |
| 605 | 687 | [[package]] |
| 606 | 688 | name = "deranged" |
| 607 | 689 | version = "0.5.8" |
| @@ -679,7 +761,7 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" | ||
| 679 | 761 | dependencies = [ |
| 680 | 762 | "curve25519-dalek", |
| 681 | 763 | "ed25519", |
| 682 | - "rand_core", | |
| 764 | + "rand_core 0.6.4", | |
| 683 | 765 | "serde", |
| 684 | 766 | "sha2 0.10.9", |
| 685 | 767 | "subtle", |
| @@ -709,7 +791,7 @@ dependencies = [ | ||
| 709 | 791 | "group", |
| 710 | 792 | "pem-rfc7468", |
| 711 | 793 | "pkcs8 0.10.2", |
| 712 | - "rand_core", | |
| 794 | + "rand_core 0.6.4", | |
| 713 | 795 | "sec1", |
| 714 | 796 | "serdect", |
| 715 | 797 | "subtle", |
| @@ -762,6 +844,7 @@ dependencies = [ | ||
| 762 | 844 | "askama", |
| 763 | 845 | "axum", |
| 764 | 846 | "axum-extra", |
| 847 | + "base64 0.22.1", | |
| 765 | 848 | "biscuit-auth", |
| 766 | 849 | "chrono", |
| 767 | 850 | "chrono-tz", |
| @@ -770,6 +853,7 @@ dependencies = [ | ||
| 770 | 853 | "hmac", |
| 771 | 854 | "rpassword", |
| 772 | 855 | "serde", |
| 856 | + "serde_json", | |
| 773 | 857 | "sha2 0.10.9", |
| 774 | 858 | "sqlx", |
| 775 | 859 | "subtle", |
| @@ -779,6 +863,7 @@ dependencies = [ | ||
| 779 | 863 | "tracing", |
| 780 | 864 | "tracing-subscriber", |
| 781 | 865 | "uuid", |
| 866 | + "webauthn-rs", | |
| 782 | 867 | ] |
| 783 | 868 | |
| 784 | 869 | [[package]] |
| @@ -787,7 +872,7 @@ version = "0.13.1" | ||
| 787 | 872 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 788 | 873 | checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" |
| 789 | 874 | dependencies = [ |
| 790 | - "rand_core", | |
| 875 | + "rand_core 0.6.4", | |
| 791 | 876 | "subtle", |
| 792 | 877 | ] |
| 793 | 878 | |
| @@ -820,6 +905,21 @@ version = "0.1.5" | ||
| 820 | 905 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 821 | 906 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" |
| 822 | 907 | |
| 908 | +[[package]] | |
| 909 | +name = "foreign-types" | |
| 910 | +version = "0.3.2" | |
| 911 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 912 | +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" | |
| 913 | +dependencies = [ | |
| 914 | + "foreign-types-shared", | |
| 915 | +] | |
| 916 | + | |
| 917 | +[[package]] | |
| 918 | +name = "foreign-types-shared" | |
| 919 | +version = "0.1.1" | |
| 920 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 921 | +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" | |
| 922 | + | |
| 823 | 923 | [[package]] |
| 824 | 924 | name = "form_urlencoded" |
| 825 | 925 | version = "1.2.2" |
| @@ -922,6 +1022,18 @@ dependencies = [ | ||
| 922 | 1022 | "wasi", |
| 923 | 1023 | ] |
| 924 | 1024 | |
| 1025 | +[[package]] | |
| 1026 | +name = "getrandom" | |
| 1027 | +version = "0.3.4" | |
| 1028 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1029 | +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" | |
| 1030 | +dependencies = [ | |
| 1031 | + "cfg-if", | |
| 1032 | + "libc", | |
| 1033 | + "r-efi 5.3.0", | |
| 1034 | + "wasip2", | |
| 1035 | +] | |
| 1036 | + | |
| 925 | 1037 | [[package]] |
| 926 | 1038 | name = "getrandom" |
| 927 | 1039 | version = "0.4.3" |
| @@ -930,7 +1042,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" | ||
| 930 | 1042 | dependencies = [ |
| 931 | 1043 | "cfg-if", |
| 932 | 1044 | "libc", |
| 933 | - "r-efi", | |
| 1045 | + "r-efi 6.0.0", | |
| 934 | 1046 | ] |
| 935 | 1047 | |
| 936 | 1048 | [[package]] |
| @@ -946,10 +1058,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 946 | 1058 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" |
| 947 | 1059 | dependencies = [ |
| 948 | 1060 | "ff", |
| 949 | - "rand_core", | |
| 1061 | + "rand_core 0.6.4", | |
| 950 | 1062 | "subtle", |
| 951 | 1063 | ] |
| 952 | 1064 | |
| 1065 | +[[package]] | |
| 1066 | +name = "half" | |
| 1067 | +version = "2.7.1" | |
| 1068 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1069 | +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" | |
| 1070 | +dependencies = [ | |
| 1071 | + "cfg-if", | |
| 1072 | + "crunchy", | |
| 1073 | + "zerocopy", | |
| 1074 | +] | |
| 1075 | + | |
| 953 | 1076 | [[package]] |
| 954 | 1077 | name = "hashbrown" |
| 955 | 1078 | version = "0.15.5" |
| @@ -1401,6 +1524,16 @@ dependencies = [ | ||
| 1401 | 1524 | "windows-sys 0.61.2", |
| 1402 | 1525 | ] |
| 1403 | 1526 | |
| 1527 | +[[package]] | |
| 1528 | +name = "num-bigint" | |
| 1529 | +version = "0.4.8" | |
| 1530 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1531 | +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" | |
| 1532 | +dependencies = [ | |
| 1533 | + "num-integer", | |
| 1534 | + "num-traits", | |
| 1535 | +] | |
| 1536 | + | |
| 1404 | 1537 | [[package]] |
| 1405 | 1538 | name = "num-bigint-dig" |
| 1406 | 1539 | version = "0.8.6" |
| @@ -1412,7 +1545,7 @@ dependencies = [ | ||
| 1412 | 1545 | "num-integer", |
| 1413 | 1546 | "num-iter", |
| 1414 | 1547 | "num-traits", |
| 1415 | - "rand", | |
| 1548 | + "rand 0.8.7", | |
| 1416 | 1549 | "smallvec", |
| 1417 | 1550 | "zeroize", |
| 1418 | 1551 | ] |
| @@ -1452,6 +1585,15 @@ dependencies = [ | ||
| 1452 | 1585 | "libm", |
| 1453 | 1586 | ] |
| 1454 | 1587 | |
| 1588 | +[[package]] | |
| 1589 | +name = "oid-registry" | |
| 1590 | +version = "0.7.1" | |
| 1591 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1592 | +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" | |
| 1593 | +dependencies = [ | |
| 1594 | + "asn1-rs", | |
| 1595 | +] | |
| 1596 | + | |
| 1455 | 1597 | [[package]] |
| 1456 | 1598 | name = "once_cell" |
| 1457 | 1599 | version = "1.21.4" |
| @@ -1470,6 +1612,43 @@ version = "0.3.1" | ||
| 1470 | 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 1471 | 1613 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" |
| 1472 | 1614 | |
| 1615 | +[[package]] | |
| 1616 | +name = "openssl" | |
| 1617 | +version = "0.10.81" | |
| 1618 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1619 | +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" | |
| 1620 | +dependencies = [ | |
| 1621 | + "bitflags", | |
| 1622 | + "cfg-if", | |
| 1623 | + "foreign-types", | |
| 1624 | + "libc", | |
| 1625 | + "openssl-macros", | |
| 1626 | + "openssl-sys", | |
| 1627 | +] | |
| 1628 | + | |
| 1629 | +[[package]] | |
| 1630 | +name = "openssl-macros" | |
| 1631 | +version = "0.1.1" | |
| 1632 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1633 | +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" | |
| 1634 | +dependencies = [ | |
| 1635 | + "proc-macro2", | |
| 1636 | + "quote", | |
| 1637 | + "syn 2.0.119", | |
| 1638 | +] | |
| 1639 | + | |
| 1640 | +[[package]] | |
| 1641 | +name = "openssl-sys" | |
| 1642 | +version = "0.9.117" | |
| 1643 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1644 | +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" | |
| 1645 | +dependencies = [ | |
| 1646 | + "cc", | |
| 1647 | + "libc", | |
| 1648 | + "pkg-config", | |
| 1649 | + "vcpkg", | |
| 1650 | +] | |
| 1651 | + | |
| 1473 | 1652 | [[package]] |
| 1474 | 1653 | name = "p256" |
| 1475 | 1654 | version = "0.13.2" |
| @@ -1518,10 +1697,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1518 | 1697 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" |
| 1519 | 1698 | dependencies = [ |
| 1520 | 1699 | "base64ct", |
| 1521 | - "rand_core", | |
| 1700 | + "rand_core 0.6.4", | |
| 1522 | 1701 | "subtle", |
| 1523 | 1702 | ] |
| 1524 | 1703 | |
| 1704 | +[[package]] | |
| 1705 | +name = "pastey" | |
| 1706 | +version = "0.1.1" | |
| 1707 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1708 | +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" | |
| 1709 | + | |
| 1525 | 1710 | [[package]] |
| 1526 | 1711 | name = "pem-rfc7468" |
| 1527 | 1712 | version = "0.7.0" |
| @@ -1710,6 +1895,12 @@ dependencies = [ | ||
| 1710 | 1895 | "proc-macro2", |
| 1711 | 1896 | ] |
| 1712 | 1897 | |
| 1898 | +[[package]] | |
| 1899 | +name = "r-efi" | |
| 1900 | +version = "5.3.0" | |
| 1901 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1902 | +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" | |
| 1903 | + | |
| 1713 | 1904 | [[package]] |
| 1714 | 1905 | name = "r-efi" |
| 1715 | 1906 | version = "6.0.0" |
| @@ -1723,8 +1914,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1723 | 1914 | checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" |
| 1724 | 1915 | dependencies = [ |
| 1725 | 1916 | "libc", |
| 1726 | - "rand_chacha", | |
| 1727 | - "rand_core", | |
| 1917 | + "rand_chacha 0.3.1", | |
| 1918 | + "rand_core 0.6.4", | |
| 1919 | +] | |
| 1920 | + | |
| 1921 | +[[package]] | |
| 1922 | +name = "rand" | |
| 1923 | +version = "0.9.5" | |
| 1924 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1925 | +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" | |
| 1926 | +dependencies = [ | |
| 1927 | + "rand_chacha 0.9.0", | |
| 1928 | + "rand_core 0.9.5", | |
| 1728 | 1929 | ] |
| 1729 | 1930 | |
| 1730 | 1931 | [[package]] |
| @@ -1734,7 +1935,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1734 | 1935 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" |
| 1735 | 1936 | dependencies = [ |
| 1736 | 1937 | "ppv-lite86", |
| 1737 | - "rand_core", | |
| 1938 | + "rand_core 0.6.4", | |
| 1939 | +] | |
| 1940 | + | |
| 1941 | +[[package]] | |
| 1942 | +name = "rand_chacha" | |
| 1943 | +version = "0.9.0" | |
| 1944 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1945 | +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" | |
| 1946 | +dependencies = [ | |
| 1947 | + "ppv-lite86", | |
| 1948 | + "rand_core 0.9.5", | |
| 1738 | 1949 | ] |
| 1739 | 1950 | |
| 1740 | 1951 | [[package]] |
| @@ -1746,6 +1957,15 @@ dependencies = [ | ||
| 1746 | 1957 | "getrandom 0.2.17", |
| 1747 | 1958 | ] |
| 1748 | 1959 | |
| 1960 | +[[package]] | |
| 1961 | +name = "rand_core" | |
| 1962 | +version = "0.9.5" | |
| 1963 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1964 | +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" | |
| 1965 | +dependencies = [ | |
| 1966 | + "getrandom 0.3.4", | |
| 1967 | +] | |
| 1968 | + | |
| 1749 | 1969 | [[package]] |
| 1750 | 1970 | name = "redox_syscall" |
| 1751 | 1971 | version = "0.5.18" |
| @@ -1841,7 +2061,7 @@ dependencies = [ | ||
| 1841 | 2061 | "num-traits", |
| 1842 | 2062 | "pkcs1", |
| 1843 | 2063 | "pkcs8 0.10.2", |
| 1844 | - "rand_core", | |
| 2064 | + "rand_core 0.6.4", | |
| 1845 | 2065 | "signature", |
| 1846 | 2066 | "spki 0.7.3", |
| 1847 | 2067 | "subtle", |
| @@ -1873,6 +2093,15 @@ dependencies = [ | ||
| 1873 | 2093 | "semver", |
| 1874 | 2094 | ] |
| 1875 | 2095 | |
| 2096 | +[[package]] | |
| 2097 | +name = "rusticata-macros" | |
| 2098 | +version = "4.1.0" | |
| 2099 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2100 | +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" | |
| 2101 | +dependencies = [ | |
| 2102 | + "nom", | |
| 2103 | +] | |
| 2104 | + | |
| 1876 | 2105 | [[package]] |
| 1877 | 2106 | name = "rustls" |
| 1878 | 2107 | version = "0.23.43" |
| @@ -1956,6 +2185,16 @@ dependencies = [ | ||
| 1956 | 2185 | "serde_derive", |
| 1957 | 2186 | ] |
| 1958 | 2187 | |
| 2188 | +[[package]] | |
| 2189 | +name = "serde_cbor_2" | |
| 2190 | +version = "0.13.0" | |
| 2191 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2192 | +checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" | |
| 2193 | +dependencies = [ | |
| 2194 | + "half", | |
| 2195 | + "serde", | |
| 2196 | +] | |
| 2197 | + | |
| 1959 | 2198 | [[package]] |
| 1960 | 2199 | name = "serde_core" |
| 1961 | 2200 | version = "1.0.229" |
| @@ -2089,7 +2328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2089 | 2328 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" |
| 2090 | 2329 | dependencies = [ |
| 2091 | 2330 | "digest 0.10.7", |
| 2092 | - "rand_core", | |
| 2331 | + "rand_core 0.6.4", | |
| 2093 | 2332 | ] |
| 2094 | 2333 | |
| 2095 | 2334 | [[package]] |
| @@ -2270,7 +2509,7 @@ dependencies = [ | ||
| 2270 | 2509 | "memchr", |
| 2271 | 2510 | "once_cell", |
| 2272 | 2511 | "percent-encoding", |
| 2273 | - "rand", | |
| 2512 | + "rand 0.8.7", | |
| 2274 | 2513 | "rsa", |
| 2275 | 2514 | "serde", |
| 2276 | 2515 | "sha1", |
| @@ -2310,7 +2549,7 @@ dependencies = [ | ||
| 2310 | 2549 | "md-5", |
| 2311 | 2550 | "memchr", |
| 2312 | 2551 | "once_cell", |
| 2313 | - "rand", | |
| 2552 | + "rand 0.8.7", | |
| 2314 | 2553 | "serde", |
| 2315 | 2554 | "serde_json", |
| 2316 | 2555 | "sha2 0.10.9", |
| @@ -2709,6 +2948,7 @@ dependencies = [ | ||
| 2709 | 2948 | "idna", |
| 2710 | 2949 | "percent-encoding", |
| 2711 | 2950 | "serde", |
| 2951 | + "serde_derive", | |
| 2712 | 2952 | ] |
| 2713 | 2953 | |
| 2714 | 2954 | [[package]] |
| @@ -2759,6 +2999,15 @@ version = "0.11.1+wasi-snapshot-preview1" | ||
| 2759 | 2999 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2760 | 3000 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" |
| 2761 | 3001 | |
| 3002 | +[[package]] | |
| 3003 | +name = "wasip2" | |
| 3004 | +version = "1.0.4+wasi-0.2.12" | |
| 3005 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3006 | +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" | |
| 3007 | +dependencies = [ | |
| 3008 | + "wit-bindgen", | |
| 3009 | +] | |
| 3010 | + | |
| 2762 | 3011 | [[package]] |
| 2763 | 3012 | name = "wasite" |
| 2764 | 3013 | version = "0.1.0" |
| @@ -2810,6 +3059,74 @@ dependencies = [ | ||
| 2810 | 3059 | "unicode-ident", |
| 2811 | 3060 | ] |
| 2812 | 3061 | |
| 3062 | +[[package]] | |
| 3063 | +name = "webauthn-attestation-ca" | |
| 3064 | +version = "0.5.5" | |
| 3065 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3066 | +checksum = "6475c0bbd1a3f04afaa3e98880408c5be61680c5e6bd3c6f8c250990d5d3e18e" | |
| 3067 | +dependencies = [ | |
| 3068 | + "base64urlsafedata", | |
| 3069 | + "openssl", | |
| 3070 | + "openssl-sys", | |
| 3071 | + "serde", | |
| 3072 | + "tracing", | |
| 3073 | + "uuid", | |
| 3074 | +] | |
| 3075 | + | |
| 3076 | +[[package]] | |
| 3077 | +name = "webauthn-rs" | |
| 3078 | +version = "0.5.5" | |
| 3079 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3080 | +checksum = "6c548915e0e92ee946bbf2aecf01ea21bef53d974b0793cc6732ba81a03fc422" | |
| 3081 | +dependencies = [ | |
| 3082 | + "base64urlsafedata", | |
| 3083 | + "serde", | |
| 3084 | + "tracing", | |
| 3085 | + "url", | |
| 3086 | + "uuid", | |
| 3087 | + "webauthn-rs-core", | |
| 3088 | +] | |
| 3089 | + | |
| 3090 | +[[package]] | |
| 3091 | +name = "webauthn-rs-core" | |
| 3092 | +version = "0.5.5" | |
| 3093 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3094 | +checksum = "296d2d501feb715d80b8e186fb88bab1073bca17f460303a1013d17b673bea6a" | |
| 3095 | +dependencies = [ | |
| 3096 | + "base64 0.21.7", | |
| 3097 | + "base64urlsafedata", | |
| 3098 | + "der-parser", | |
| 3099 | + "hex", | |
| 3100 | + "nom", | |
| 3101 | + "openssl", | |
| 3102 | + "openssl-sys", | |
| 3103 | + "rand 0.9.5", | |
| 3104 | + "rand_chacha 0.9.0", | |
| 3105 | + "serde", | |
| 3106 | + "serde_cbor_2", | |
| 3107 | + "serde_json", | |
| 3108 | + "thiserror 1.0.69", | |
| 3109 | + "tracing", | |
| 3110 | + "url", | |
| 3111 | + "uuid", | |
| 3112 | + "webauthn-attestation-ca", | |
| 3113 | + "webauthn-rs-proto", | |
| 3114 | + "x509-parser", | |
| 3115 | +] | |
| 3116 | + | |
| 3117 | +[[package]] | |
| 3118 | +name = "webauthn-rs-proto" | |
| 3119 | +version = "0.5.5" | |
| 3120 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3121 | +checksum = "c37393beac9c1ed1ca6dbb30b1e01783fb316ab3a45d90ecd48c99052dd7ef1e" | |
| 3122 | +dependencies = [ | |
| 3123 | + "base64 0.21.7", | |
| 3124 | + "base64urlsafedata", | |
| 3125 | + "serde", | |
| 3126 | + "serde_json", | |
| 3127 | + "url", | |
| 3128 | +] | |
| 3129 | + | |
| 2813 | 3130 | [[package]] |
| 2814 | 3131 | name = "webpki-roots" |
| 2815 | 3132 | version = "0.26.11" |
| @@ -3063,12 +3380,35 @@ dependencies = [ | ||
| 3063 | 3380 | "memchr", |
| 3064 | 3381 | ] |
| 3065 | 3382 | |
| 3383 | +[[package]] | |
| 3384 | +name = "wit-bindgen" | |
| 3385 | +version = "0.57.1" | |
| 3386 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3387 | +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" | |
| 3388 | + | |
| 3066 | 3389 | [[package]] |
| 3067 | 3390 | name = "writeable" |
| 3068 | 3391 | version = "0.6.3" |
| 3069 | 3392 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 3070 | 3393 | checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" |
| 3071 | 3394 | |
| 3395 | +[[package]] | |
| 3396 | +name = "x509-parser" | |
| 3397 | +version = "0.16.0" | |
| 3398 | +source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3399 | +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" | |
| 3400 | +dependencies = [ | |
| 3401 | + "asn1-rs", | |
| 3402 | + "data-encoding", | |
| 3403 | + "der-parser", | |
| 3404 | + "lazy_static", | |
| 3405 | + "nom", | |
| 3406 | + "oid-registry", | |
| 3407 | + "rusticata-macros", | |
| 3408 | + "thiserror 1.0.69", | |
| 3409 | + "time", | |
| 3410 | +] | |
| 3411 | + | |
| 3072 | 3412 | [[package]] |
| 3073 | 3413 | name = "yoke" |
| 3074 | 3414 | version = "0.8.3" |
| @@ -147,6 +147,45 @@ dependencies = [ | |||
| 147 | "winnow", | 147 | "winnow", |
| 148 | ] | 148 | ] |
| 149 | 149 | ||
| 150 | +[[package]] | ||
| 151 | +name = "asn1-rs" | ||
| 152 | +version = "0.6.2" | ||
| 153 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 154 | +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" | ||
| 155 | +dependencies = [ | ||
| 156 | + "asn1-rs-derive", | ||
| 157 | + "asn1-rs-impl", | ||
| 158 | + "displaydoc", | ||
| 159 | + "nom", | ||
| 160 | + "num-traits", | ||
| 161 | + "rusticata-macros", | ||
| 162 | + "thiserror 1.0.69", | ||
| 163 | + "time", | ||
| 164 | +] | ||
| 165 | + | ||
| 166 | +[[package]] | ||
| 167 | +name = "asn1-rs-derive" | ||
| 168 | +version = "0.5.1" | ||
| 169 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 170 | +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" | ||
| 171 | +dependencies = [ | ||
| 172 | + "proc-macro2", | ||
| 173 | + "quote", | ||
| 174 | + "syn 2.0.119", | ||
| 175 | + "synstructure", | ||
| 176 | +] | ||
| 177 | + | ||
| 178 | +[[package]] | ||
| 179 | +name = "asn1-rs-impl" | ||
| 180 | +version = "0.2.0" | ||
| 181 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 182 | +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" | ||
| 183 | +dependencies = [ | ||
| 184 | + "proc-macro2", | ||
| 185 | + "quote", | ||
| 186 | + "syn 2.0.119", | ||
| 187 | +] | ||
| 188 | + | ||
| 150 | [[package]] | 189 | [[package]] |
| 151 | name = "atoi" | 190 | name = "atoi" |
| 152 | version = "2.0.0" | 191 | version = "2.0.0" |
| @@ -254,6 +293,12 @@ version = "0.13.1" | |||
| 254 | source = "registry+https://github.com/rust-lang/crates.io-index" | 293 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 255 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" | 294 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" |
| 256 | 295 | ||
| 296 | +[[package]] | ||
| 297 | +name = "base64" | ||
| 298 | +version = "0.21.7" | ||
| 299 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 300 | +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" | ||
| 301 | + | ||
| 257 | [[package]] | 302 | [[package]] |
| 258 | name = "base64" | 303 | name = "base64" |
| 259 | version = "0.22.1" | 304 | version = "0.22.1" |
| @@ -266,6 +311,17 @@ version = "1.8.3" | |||
| 266 | source = "registry+https://github.com/rust-lang/crates.io-index" | 311 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 267 | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" | 312 | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" |
| 268 | 313 | ||
| 314 | +[[package]] | ||
| 315 | +name = "base64urlsafedata" | ||
| 316 | +version = "0.5.5" | ||
| 317 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 318 | +checksum = "b08e33815c87d8cadcddb1e74ac307368a3751fbe40c961538afa21a1899f21c" | ||
| 319 | +dependencies = [ | ||
| 320 | + "base64 0.21.7", | ||
| 321 | + "pastey", | ||
| 322 | + "serde", | ||
| 323 | +] | ||
| 324 | + | ||
| 269 | [[package]] | 325 | [[package]] |
| 270 | name = "basic-toml" | 326 | name = "basic-toml" |
| 271 | version = "0.1.10" | 327 | version = "0.1.10" |
| @@ -294,8 +350,8 @@ dependencies = [ | |||
| 294 | "pkcs8 0.9.0", | 350 | "pkcs8 0.9.0", |
| 295 | "prost", | 351 | "prost", |
| 296 | "prost-types", | 352 | "prost-types", |
| 297 | - "rand", | 353 | + "rand 0.8.7", |
| 298 | - "rand_core", | 354 | + "rand_core 0.6.4", |
| 299 | "regex", | 355 | "regex", |
| 300 | "serde_json", | 356 | "serde_json", |
| 301 | "sha2 0.9.9", | 357 | "sha2 0.9.9", |
| @@ -533,6 +589,12 @@ version = "0.8.22" | |||
| 533 | source = "registry+https://github.com/rust-lang/crates.io-index" | 589 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 534 | checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" | 590 | checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" |
| 535 | 591 | ||
| 592 | +[[package]] | ||
| 593 | +name = "crunchy" | ||
| 594 | +version = "0.2.4" | ||
| 595 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 596 | +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" | ||
| 597 | + | ||
| 536 | [[package]] | 598 | [[package]] |
| 537 | name = "crypto-bigint" | 599 | name = "crypto-bigint" |
| 538 | version = "0.5.5" | 600 | version = "0.5.5" |
| @@ -540,7 +602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 540 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" | 602 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" |
| 541 | dependencies = [ | 603 | dependencies = [ |
| 542 | "generic-array", | 604 | "generic-array", |
| 543 | - "rand_core", | 605 | + "rand_core 0.6.4", |
| 544 | "subtle", | 606 | "subtle", |
| 545 | "zeroize", | 607 | "zeroize", |
| 546 | ] | 608 | ] |
| @@ -582,6 +644,12 @@ dependencies = [ | |||
| 582 | "syn 2.0.119", | 644 | "syn 2.0.119", |
| 583 | ] | 645 | ] |
| 584 | 646 | ||
| 647 | +[[package]] | ||
| 648 | +name = "data-encoding" | ||
| 649 | +version = "2.11.1" | ||
| 650 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 651 | +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" | ||
| 652 | + | ||
| 585 | [[package]] | 653 | [[package]] |
| 586 | name = "der" | 654 | name = "der" |
| 587 | version = "0.6.1" | 655 | version = "0.6.1" |
| @@ -602,6 +670,20 @@ dependencies = [ | |||
| 602 | "zeroize", | 670 | "zeroize", |
| 603 | ] | 671 | ] |
| 604 | 672 | ||
| 673 | +[[package]] | ||
| 674 | +name = "der-parser" | ||
| 675 | +version = "9.0.0" | ||
| 676 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 677 | +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" | ||
| 678 | +dependencies = [ | ||
| 679 | + "asn1-rs", | ||
| 680 | + "displaydoc", | ||
| 681 | + "nom", | ||
| 682 | + "num-bigint", | ||
| 683 | + "num-traits", | ||
| 684 | + "rusticata-macros", | ||
| 685 | +] | ||
| 686 | + | ||
| 605 | [[package]] | 687 | [[package]] |
| 606 | name = "deranged" | 688 | name = "deranged" |
| 607 | version = "0.5.8" | 689 | version = "0.5.8" |
| @@ -679,7 +761,7 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" | |||
| 679 | dependencies = [ | 761 | dependencies = [ |
| 680 | "curve25519-dalek", | 762 | "curve25519-dalek", |
| 681 | "ed25519", | 763 | "ed25519", |
| 682 | - "rand_core", | 764 | + "rand_core 0.6.4", |
| 683 | "serde", | 765 | "serde", |
| 684 | "sha2 0.10.9", | 766 | "sha2 0.10.9", |
| 685 | "subtle", | 767 | "subtle", |
| @@ -709,7 +791,7 @@ dependencies = [ | |||
| 709 | "group", | 791 | "group", |
| 710 | "pem-rfc7468", | 792 | "pem-rfc7468", |
| 711 | "pkcs8 0.10.2", | 793 | "pkcs8 0.10.2", |
| 712 | - "rand_core", | 794 | + "rand_core 0.6.4", |
| 713 | "sec1", | 795 | "sec1", |
| 714 | "serdect", | 796 | "serdect", |
| 715 | "subtle", | 797 | "subtle", |
| @@ -762,6 +844,7 @@ dependencies = [ | |||
| 762 | "askama", | 844 | "askama", |
| 763 | "axum", | 845 | "axum", |
| 764 | "axum-extra", | 846 | "axum-extra", |
| 847 | + "base64 0.22.1", | ||
| 765 | "biscuit-auth", | 848 | "biscuit-auth", |
| 766 | "chrono", | 849 | "chrono", |
| 767 | "chrono-tz", | 850 | "chrono-tz", |
| @@ -770,6 +853,7 @@ dependencies = [ | |||
| 770 | "hmac", | 853 | "hmac", |
| 771 | "rpassword", | 854 | "rpassword", |
| 772 | "serde", | 855 | "serde", |
| 856 | + "serde_json", | ||
| 773 | "sha2 0.10.9", | 857 | "sha2 0.10.9", |
| 774 | "sqlx", | 858 | "sqlx", |
| 775 | "subtle", | 859 | "subtle", |
| @@ -779,6 +863,7 @@ dependencies = [ | |||
| 779 | "tracing", | 863 | "tracing", |
| 780 | "tracing-subscriber", | 864 | "tracing-subscriber", |
| 781 | "uuid", | 865 | "uuid", |
| 866 | + "webauthn-rs", | ||
| 782 | ] | 867 | ] |
| 783 | 868 | ||
| 784 | [[package]] | 869 | [[package]] |
| @@ -787,7 +872,7 @@ version = "0.13.1" | |||
| 787 | source = "registry+https://github.com/rust-lang/crates.io-index" | 872 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 788 | checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" | 873 | checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" |
| 789 | dependencies = [ | 874 | dependencies = [ |
| 790 | - "rand_core", | 875 | + "rand_core 0.6.4", |
| 791 | "subtle", | 876 | "subtle", |
| 792 | ] | 877 | ] |
| 793 | 878 | ||
| @@ -820,6 +905,21 @@ version = "0.1.5" | |||
| 820 | source = "registry+https://github.com/rust-lang/crates.io-index" | 905 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 821 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" | 906 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" |
| 822 | 907 | ||
| 908 | +[[package]] | ||
| 909 | +name = "foreign-types" | ||
| 910 | +version = "0.3.2" | ||
| 911 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 912 | +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" | ||
| 913 | +dependencies = [ | ||
| 914 | + "foreign-types-shared", | ||
| 915 | +] | ||
| 916 | + | ||
| 917 | +[[package]] | ||
| 918 | +name = "foreign-types-shared" | ||
| 919 | +version = "0.1.1" | ||
| 920 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 921 | +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" | ||
| 922 | + | ||
| 823 | [[package]] | 923 | [[package]] |
| 824 | name = "form_urlencoded" | 924 | name = "form_urlencoded" |
| 825 | version = "1.2.2" | 925 | version = "1.2.2" |
| @@ -922,6 +1022,18 @@ dependencies = [ | |||
| 922 | "wasi", | 1022 | "wasi", |
| 923 | ] | 1023 | ] |
| 924 | 1024 | ||
| 1025 | +[[package]] | ||
| 1026 | +name = "getrandom" | ||
| 1027 | +version = "0.3.4" | ||
| 1028 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1029 | +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" | ||
| 1030 | +dependencies = [ | ||
| 1031 | + "cfg-if", | ||
| 1032 | + "libc", | ||
| 1033 | + "r-efi 5.3.0", | ||
| 1034 | + "wasip2", | ||
| 1035 | +] | ||
| 1036 | + | ||
| 925 | [[package]] | 1037 | [[package]] |
| 926 | name = "getrandom" | 1038 | name = "getrandom" |
| 927 | version = "0.4.3" | 1039 | version = "0.4.3" |
| @@ -930,7 +1042,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" | |||
| 930 | dependencies = [ | 1042 | dependencies = [ |
| 931 | "cfg-if", | 1043 | "cfg-if", |
| 932 | "libc", | 1044 | "libc", |
| 933 | - "r-efi", | 1045 | + "r-efi 6.0.0", |
| 934 | ] | 1046 | ] |
| 935 | 1047 | ||
| 936 | [[package]] | 1048 | [[package]] |
| @@ -946,10 +1058,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 946 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" | 1058 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" |
| 947 | dependencies = [ | 1059 | dependencies = [ |
| 948 | "ff", | 1060 | "ff", |
| 949 | - "rand_core", | 1061 | + "rand_core 0.6.4", |
| 950 | "subtle", | 1062 | "subtle", |
| 951 | ] | 1063 | ] |
| 952 | 1064 | ||
| 1065 | +[[package]] | ||
| 1066 | +name = "half" | ||
| 1067 | +version = "2.7.1" | ||
| 1068 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1069 | +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" | ||
| 1070 | +dependencies = [ | ||
| 1071 | + "cfg-if", | ||
| 1072 | + "crunchy", | ||
| 1073 | + "zerocopy", | ||
| 1074 | +] | ||
| 1075 | + | ||
| 953 | [[package]] | 1076 | [[package]] |
| 954 | name = "hashbrown" | 1077 | name = "hashbrown" |
| 955 | version = "0.15.5" | 1078 | version = "0.15.5" |
| @@ -1401,6 +1524,16 @@ dependencies = [ | |||
| 1401 | "windows-sys 0.61.2", | 1524 | "windows-sys 0.61.2", |
| 1402 | ] | 1525 | ] |
| 1403 | 1526 | ||
| 1527 | +[[package]] | ||
| 1528 | +name = "num-bigint" | ||
| 1529 | +version = "0.4.8" | ||
| 1530 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1531 | +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" | ||
| 1532 | +dependencies = [ | ||
| 1533 | + "num-integer", | ||
| 1534 | + "num-traits", | ||
| 1535 | +] | ||
| 1536 | + | ||
| 1404 | [[package]] | 1537 | [[package]] |
| 1405 | name = "num-bigint-dig" | 1538 | name = "num-bigint-dig" |
| 1406 | version = "0.8.6" | 1539 | version = "0.8.6" |
| @@ -1412,7 +1545,7 @@ dependencies = [ | |||
| 1412 | "num-integer", | 1545 | "num-integer", |
| 1413 | "num-iter", | 1546 | "num-iter", |
| 1414 | "num-traits", | 1547 | "num-traits", |
| 1415 | - "rand", | 1548 | + "rand 0.8.7", |
| 1416 | "smallvec", | 1549 | "smallvec", |
| 1417 | "zeroize", | 1550 | "zeroize", |
| 1418 | ] | 1551 | ] |
| @@ -1452,6 +1585,15 @@ dependencies = [ | |||
| 1452 | "libm", | 1585 | "libm", |
| 1453 | ] | 1586 | ] |
| 1454 | 1587 | ||
| 1588 | +[[package]] | ||
| 1589 | +name = "oid-registry" | ||
| 1590 | +version = "0.7.1" | ||
| 1591 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1592 | +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" | ||
| 1593 | +dependencies = [ | ||
| 1594 | + "asn1-rs", | ||
| 1595 | +] | ||
| 1596 | + | ||
| 1455 | [[package]] | 1597 | [[package]] |
| 1456 | name = "once_cell" | 1598 | name = "once_cell" |
| 1457 | version = "1.21.4" | 1599 | version = "1.21.4" |
| @@ -1470,6 +1612,43 @@ version = "0.3.1" | |||
| 1470 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 1471 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" | 1613 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" |
| 1472 | 1614 | ||
| 1615 | +[[package]] | ||
| 1616 | +name = "openssl" | ||
| 1617 | +version = "0.10.81" | ||
| 1618 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1619 | +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" | ||
| 1620 | +dependencies = [ | ||
| 1621 | + "bitflags", | ||
| 1622 | + "cfg-if", | ||
| 1623 | + "foreign-types", | ||
| 1624 | + "libc", | ||
| 1625 | + "openssl-macros", | ||
| 1626 | + "openssl-sys", | ||
| 1627 | +] | ||
| 1628 | + | ||
| 1629 | +[[package]] | ||
| 1630 | +name = "openssl-macros" | ||
| 1631 | +version = "0.1.1" | ||
| 1632 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1633 | +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" | ||
| 1634 | +dependencies = [ | ||
| 1635 | + "proc-macro2", | ||
| 1636 | + "quote", | ||
| 1637 | + "syn 2.0.119", | ||
| 1638 | +] | ||
| 1639 | + | ||
| 1640 | +[[package]] | ||
| 1641 | +name = "openssl-sys" | ||
| 1642 | +version = "0.9.117" | ||
| 1643 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1644 | +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" | ||
| 1645 | +dependencies = [ | ||
| 1646 | + "cc", | ||
| 1647 | + "libc", | ||
| 1648 | + "pkg-config", | ||
| 1649 | + "vcpkg", | ||
| 1650 | +] | ||
| 1651 | + | ||
| 1473 | [[package]] | 1652 | [[package]] |
| 1474 | name = "p256" | 1653 | name = "p256" |
| 1475 | version = "0.13.2" | 1654 | version = "0.13.2" |
| @@ -1518,10 +1697,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 1518 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" | 1697 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" |
| 1519 | dependencies = [ | 1698 | dependencies = [ |
| 1520 | "base64ct", | 1699 | "base64ct", |
| 1521 | - "rand_core", | 1700 | + "rand_core 0.6.4", |
| 1522 | "subtle", | 1701 | "subtle", |
| 1523 | ] | 1702 | ] |
| 1524 | 1703 | ||
| 1704 | +[[package]] | ||
| 1705 | +name = "pastey" | ||
| 1706 | +version = "0.1.1" | ||
| 1707 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1708 | +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" | ||
| 1709 | + | ||
| 1525 | [[package]] | 1710 | [[package]] |
| 1526 | name = "pem-rfc7468" | 1711 | name = "pem-rfc7468" |
| 1527 | version = "0.7.0" | 1712 | version = "0.7.0" |
| @@ -1710,6 +1895,12 @@ dependencies = [ | |||
| 1710 | "proc-macro2", | 1895 | "proc-macro2", |
| 1711 | ] | 1896 | ] |
| 1712 | 1897 | ||
| 1898 | +[[package]] | ||
| 1899 | +name = "r-efi" | ||
| 1900 | +version = "5.3.0" | ||
| 1901 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1902 | +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" | ||
| 1903 | + | ||
| 1713 | [[package]] | 1904 | [[package]] |
| 1714 | name = "r-efi" | 1905 | name = "r-efi" |
| 1715 | version = "6.0.0" | 1906 | version = "6.0.0" |
| @@ -1723,8 +1914,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 1723 | checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" | 1914 | checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" |
| 1724 | dependencies = [ | 1915 | dependencies = [ |
| 1725 | "libc", | 1916 | "libc", |
| 1726 | - "rand_chacha", | 1917 | + "rand_chacha 0.3.1", |
| 1727 | - "rand_core", | 1918 | + "rand_core 0.6.4", |
| 1919 | +] | ||
| 1920 | + | ||
| 1921 | +[[package]] | ||
| 1922 | +name = "rand" | ||
| 1923 | +version = "0.9.5" | ||
| 1924 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1925 | +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" | ||
| 1926 | +dependencies = [ | ||
| 1927 | + "rand_chacha 0.9.0", | ||
| 1928 | + "rand_core 0.9.5", | ||
| 1728 | ] | 1929 | ] |
| 1729 | 1930 | ||
| 1730 | [[package]] | 1931 | [[package]] |
| @@ -1734,7 +1935,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 1734 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" | 1935 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" |
| 1735 | dependencies = [ | 1936 | dependencies = [ |
| 1736 | "ppv-lite86", | 1937 | "ppv-lite86", |
| 1737 | - "rand_core", | 1938 | + "rand_core 0.6.4", |
| 1939 | +] | ||
| 1940 | + | ||
| 1941 | +[[package]] | ||
| 1942 | +name = "rand_chacha" | ||
| 1943 | +version = "0.9.0" | ||
| 1944 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1945 | +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" | ||
| 1946 | +dependencies = [ | ||
| 1947 | + "ppv-lite86", | ||
| 1948 | + "rand_core 0.9.5", | ||
| 1738 | ] | 1949 | ] |
| 1739 | 1950 | ||
| 1740 | [[package]] | 1951 | [[package]] |
| @@ -1746,6 +1957,15 @@ dependencies = [ | |||
| 1746 | "getrandom 0.2.17", | 1957 | "getrandom 0.2.17", |
| 1747 | ] | 1958 | ] |
| 1748 | 1959 | ||
| 1960 | +[[package]] | ||
| 1961 | +name = "rand_core" | ||
| 1962 | +version = "0.9.5" | ||
| 1963 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 1964 | +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" | ||
| 1965 | +dependencies = [ | ||
| 1966 | + "getrandom 0.3.4", | ||
| 1967 | +] | ||
| 1968 | + | ||
| 1749 | [[package]] | 1969 | [[package]] |
| 1750 | name = "redox_syscall" | 1970 | name = "redox_syscall" |
| 1751 | version = "0.5.18" | 1971 | version = "0.5.18" |
| @@ -1841,7 +2061,7 @@ dependencies = [ | |||
| 1841 | "num-traits", | 2061 | "num-traits", |
| 1842 | "pkcs1", | 2062 | "pkcs1", |
| 1843 | "pkcs8 0.10.2", | 2063 | "pkcs8 0.10.2", |
| 1844 | - "rand_core", | 2064 | + "rand_core 0.6.4", |
| 1845 | "signature", | 2065 | "signature", |
| 1846 | "spki 0.7.3", | 2066 | "spki 0.7.3", |
| 1847 | "subtle", | 2067 | "subtle", |
| @@ -1873,6 +2093,15 @@ dependencies = [ | |||
| 1873 | "semver", | 2093 | "semver", |
| 1874 | ] | 2094 | ] |
| 1875 | 2095 | ||
| 2096 | +[[package]] | ||
| 2097 | +name = "rusticata-macros" | ||
| 2098 | +version = "4.1.0" | ||
| 2099 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2100 | +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" | ||
| 2101 | +dependencies = [ | ||
| 2102 | + "nom", | ||
| 2103 | +] | ||
| 2104 | + | ||
| 1876 | [[package]] | 2105 | [[package]] |
| 1877 | name = "rustls" | 2106 | name = "rustls" |
| 1878 | version = "0.23.43" | 2107 | version = "0.23.43" |
| @@ -1956,6 +2185,16 @@ dependencies = [ | |||
| 1956 | "serde_derive", | 2185 | "serde_derive", |
| 1957 | ] | 2186 | ] |
| 1958 | 2187 | ||
| 2188 | +[[package]] | ||
| 2189 | +name = "serde_cbor_2" | ||
| 2190 | +version = "0.13.0" | ||
| 2191 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 2192 | +checksum = "34aec2709de9078e077090abd848e967abab63c9fb3fdb5d4799ad359d8d482c" | ||
| 2193 | +dependencies = [ | ||
| 2194 | + "half", | ||
| 2195 | + "serde", | ||
| 2196 | +] | ||
| 2197 | + | ||
| 1959 | [[package]] | 2198 | [[package]] |
| 1960 | name = "serde_core" | 2199 | name = "serde_core" |
| 1961 | version = "1.0.229" | 2200 | version = "1.0.229" |
| @@ -2089,7 +2328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| 2089 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" | 2328 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" |
| 2090 | dependencies = [ | 2329 | dependencies = [ |
| 2091 | "digest 0.10.7", | 2330 | "digest 0.10.7", |
| 2092 | - "rand_core", | 2331 | + "rand_core 0.6.4", |
| 2093 | ] | 2332 | ] |
| 2094 | 2333 | ||
| 2095 | [[package]] | 2334 | [[package]] |
| @@ -2270,7 +2509,7 @@ dependencies = [ | |||
| 2270 | "memchr", | 2509 | "memchr", |
| 2271 | "once_cell", | 2510 | "once_cell", |
| 2272 | "percent-encoding", | 2511 | "percent-encoding", |
| 2273 | - "rand", | 2512 | + "rand 0.8.7", |
| 2274 | "rsa", | 2513 | "rsa", |
| 2275 | "serde", | 2514 | "serde", |
| 2276 | "sha1", | 2515 | "sha1", |
| @@ -2310,7 +2549,7 @@ dependencies = [ | |||
| 2310 | "md-5", | 2549 | "md-5", |
| 2311 | "memchr", | 2550 | "memchr", |
| 2312 | "once_cell", | 2551 | "once_cell", |
| 2313 | - "rand", | 2552 | + "rand 0.8.7", |
| 2314 | "serde", | 2553 | "serde", |
| 2315 | "serde_json", | 2554 | "serde_json", |
| 2316 | "sha2 0.10.9", | 2555 | "sha2 0.10.9", |
| @@ -2709,6 +2948,7 @@ dependencies = [ | |||
| 2709 | "idna", | 2948 | "idna", |
| 2710 | "percent-encoding", | 2949 | "percent-encoding", |
| 2711 | "serde", | 2950 | "serde", |
| 2951 | + "serde_derive", | ||
| 2712 | ] | 2952 | ] |
| 2713 | 2953 | ||
| 2714 | [[package]] | 2954 | [[package]] |
| @@ -2759,6 +2999,15 @@ version = "0.11.1+wasi-snapshot-preview1" | |||
| 2759 | source = "registry+https://github.com/rust-lang/crates.io-index" | 2999 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 2760 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" | 3000 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" |
| 2761 | 3001 | ||
| 3002 | +[[package]] | ||
| 3003 | +name = "wasip2" | ||
| 3004 | +version = "1.0.4+wasi-0.2.12" | ||
| 3005 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3006 | +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" | ||
| 3007 | +dependencies = [ | ||
| 3008 | + "wit-bindgen", | ||
| 3009 | +] | ||
| 3010 | + | ||
| 2762 | [[package]] | 3011 | [[package]] |
| 2763 | name = "wasite" | 3012 | name = "wasite" |
| 2764 | version = "0.1.0" | 3013 | version = "0.1.0" |
| @@ -2810,6 +3059,74 @@ dependencies = [ | |||
| 2810 | "unicode-ident", | 3059 | "unicode-ident", |
| 2811 | ] | 3060 | ] |
| 2812 | 3061 | ||
| 3062 | +[[package]] | ||
| 3063 | +name = "webauthn-attestation-ca" | ||
| 3064 | +version = "0.5.5" | ||
| 3065 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3066 | +checksum = "6475c0bbd1a3f04afaa3e98880408c5be61680c5e6bd3c6f8c250990d5d3e18e" | ||
| 3067 | +dependencies = [ | ||
| 3068 | + "base64urlsafedata", | ||
| 3069 | + "openssl", | ||
| 3070 | + "openssl-sys", | ||
| 3071 | + "serde", | ||
| 3072 | + "tracing", | ||
| 3073 | + "uuid", | ||
| 3074 | +] | ||
| 3075 | + | ||
| 3076 | +[[package]] | ||
| 3077 | +name = "webauthn-rs" | ||
| 3078 | +version = "0.5.5" | ||
| 3079 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3080 | +checksum = "6c548915e0e92ee946bbf2aecf01ea21bef53d974b0793cc6732ba81a03fc422" | ||
| 3081 | +dependencies = [ | ||
| 3082 | + "base64urlsafedata", | ||
| 3083 | + "serde", | ||
| 3084 | + "tracing", | ||
| 3085 | + "url", | ||
| 3086 | + "uuid", | ||
| 3087 | + "webauthn-rs-core", | ||
| 3088 | +] | ||
| 3089 | + | ||
| 3090 | +[[package]] | ||
| 3091 | +name = "webauthn-rs-core" | ||
| 3092 | +version = "0.5.5" | ||
| 3093 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3094 | +checksum = "296d2d501feb715d80b8e186fb88bab1073bca17f460303a1013d17b673bea6a" | ||
| 3095 | +dependencies = [ | ||
| 3096 | + "base64 0.21.7", | ||
| 3097 | + "base64urlsafedata", | ||
| 3098 | + "der-parser", | ||
| 3099 | + "hex", | ||
| 3100 | + "nom", | ||
| 3101 | + "openssl", | ||
| 3102 | + "openssl-sys", | ||
| 3103 | + "rand 0.9.5", | ||
| 3104 | + "rand_chacha 0.9.0", | ||
| 3105 | + "serde", | ||
| 3106 | + "serde_cbor_2", | ||
| 3107 | + "serde_json", | ||
| 3108 | + "thiserror 1.0.69", | ||
| 3109 | + "tracing", | ||
| 3110 | + "url", | ||
| 3111 | + "uuid", | ||
| 3112 | + "webauthn-attestation-ca", | ||
| 3113 | + "webauthn-rs-proto", | ||
| 3114 | + "x509-parser", | ||
| 3115 | +] | ||
| 3116 | + | ||
| 3117 | +[[package]] | ||
| 3118 | +name = "webauthn-rs-proto" | ||
| 3119 | +version = "0.5.5" | ||
| 3120 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3121 | +checksum = "c37393beac9c1ed1ca6dbb30b1e01783fb316ab3a45d90ecd48c99052dd7ef1e" | ||
| 3122 | +dependencies = [ | ||
| 3123 | + "base64 0.21.7", | ||
| 3124 | + "base64urlsafedata", | ||
| 3125 | + "serde", | ||
| 3126 | + "serde_json", | ||
| 3127 | + "url", | ||
| 3128 | +] | ||
| 3129 | + | ||
| 2813 | [[package]] | 3130 | [[package]] |
| 2814 | name = "webpki-roots" | 3131 | name = "webpki-roots" |
| 2815 | version = "0.26.11" | 3132 | version = "0.26.11" |
| @@ -3063,12 +3380,35 @@ dependencies = [ | |||
| 3063 | "memchr", | 3380 | "memchr", |
| 3064 | ] | 3381 | ] |
| 3065 | 3382 | ||
| 3383 | +[[package]] | ||
| 3384 | +name = "wit-bindgen" | ||
| 3385 | +version = "0.57.1" | ||
| 3386 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3387 | +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" | ||
| 3388 | + | ||
| 3066 | [[package]] | 3389 | [[package]] |
| 3067 | name = "writeable" | 3390 | name = "writeable" |
| 3068 | version = "0.6.3" | 3391 | version = "0.6.3" |
| 3069 | source = "registry+https://github.com/rust-lang/crates.io-index" | 3392 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 3070 | checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" | 3393 | checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" |
| 3071 | 3394 | ||
| 3395 | +[[package]] | ||
| 3396 | +name = "x509-parser" | ||
| 3397 | +version = "0.16.0" | ||
| 3398 | +source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 3399 | +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" | ||
| 3400 | +dependencies = [ | ||
| 3401 | + "asn1-rs", | ||
| 3402 | + "data-encoding", | ||
| 3403 | + "der-parser", | ||
| 3404 | + "lazy_static", | ||
| 3405 | + "nom", | ||
| 3406 | + "oid-registry", | ||
| 3407 | + "rusticata-macros", | ||
| 3408 | + "thiserror 1.0.69", | ||
| 3409 | + "time", | ||
| 3410 | +] | ||
| 3411 | + | ||
| 3072 | [[package]] | 3412 | [[package]] |
| 3073 | name = "yoke" | 3413 | name = "yoke" |
| 3074 | version = "0.8.3" | 3414 | version = "0.8.3" |
modified
Cargo.toml +7 -1 | @@ -9,8 +9,12 @@ axum = "0.8" | ||
| 9 | 9 | axum-extra = { version = "0.12", features = ["cookie"] } |
| 10 | 10 | tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] } |
| 11 | 11 | askama = "0.16" |
| 12 | -sqlx = { version = "0.8", features = ["runtime-tokio", "tls-rustls-ring", "postgres", "chrono", "migrate", "uuid"] } | |
| 12 | +sqlx = { version = "0.8", features = ["runtime-tokio", "tls-rustls-ring", "postgres", "chrono", "migrate", "uuid", "json"] } | |
| 13 | 13 | uuid = { version = "1", features = ["v7", "serde"] } |
| 14 | +# conditional-ui : login usernameless (discoverable credentials) ; | |
| 15 | +# danger-allow-state-serialisation : l'état de cérémonie part dans un cookie | |
| 16 | +# signé éphémère (l'app est stateless), pas dans une table. | |
| 17 | +webauthn-rs = { version = "0.5", features = ["conditional-ui", "danger-allow-state-serialisation"] } | |
| 14 | 18 | # NOTE : tire biscuit-quote → proc-macro-error2 (non maintenue, future-incompat |
| 15 | 19 | # E0365) via la feature par défaut datalog-macro. On n'utilise pas ces macros, |
| 16 | 20 | # mais biscuit-auth 6.0.0 ne compile pas en default-features = false (bug de |
| @@ -29,6 +33,8 @@ subtle = "2" | ||
| 29 | 33 | time = "0.3" |
| 30 | 34 | rpassword = "7" |
| 31 | 35 | serde = { version = "1", features = ["derive"] } |
| 36 | +serde_json = "1" | |
| 37 | +base64 = "0.22" | |
| 32 | 38 | tracing = "0.1" |
| 33 | 39 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } |
| 34 | 40 | |
| @@ -9,8 +9,12 @@ axum = "0.8" | |||
| 9 | axum-extra = { version = "0.12", features = ["cookie"] } | 9 | axum-extra = { version = "0.12", features = ["cookie"] } |
| 10 | tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] } | 10 | tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] } |
| 11 | askama = "0.16" | 11 | askama = "0.16" |
| 12 | -sqlx = { version = "0.8", features = ["runtime-tokio", "tls-rustls-ring", "postgres", "chrono", "migrate", "uuid"] } | 12 | +sqlx = { version = "0.8", features = ["runtime-tokio", "tls-rustls-ring", "postgres", "chrono", "migrate", "uuid", "json"] } |
| 13 | uuid = { version = "1", features = ["v7", "serde"] } | 13 | uuid = { version = "1", features = ["v7", "serde"] } |
| 14 | +# conditional-ui : login usernameless (discoverable credentials) ; | ||
| 15 | +# danger-allow-state-serialisation : l'état de cérémonie part dans un cookie | ||
| 16 | +# signé éphémère (l'app est stateless), pas dans une table. | ||
| 17 | +webauthn-rs = { version = "0.5", features = ["conditional-ui", "danger-allow-state-serialisation"] } | ||
| 14 | # NOTE : tire biscuit-quote → proc-macro-error2 (non maintenue, future-incompat | 18 | # NOTE : tire biscuit-quote → proc-macro-error2 (non maintenue, future-incompat |
| 15 | # E0365) via la feature par défaut datalog-macro. On n'utilise pas ces macros, | 19 | # E0365) via la feature par défaut datalog-macro. On n'utilise pas ces macros, |
| 16 | # mais biscuit-auth 6.0.0 ne compile pas en default-features = false (bug de | 20 | # mais biscuit-auth 6.0.0 ne compile pas en default-features = false (bug de |
| @@ -29,6 +33,8 @@ subtle = "2" | |||
| 29 | time = "0.3" | 33 | time = "0.3" |
| 30 | rpassword = "7" | 34 | rpassword = "7" |
| 31 | serde = { version = "1", features = ["derive"] } | 35 | serde = { version = "1", features = ["derive"] } |
| 36 | +serde_json = "1" | ||
| 37 | +base64 = "0.22" | ||
| 32 | tracing = "0.1" | 38 | tracing = "0.1" |
| 33 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } | 39 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } |
| 34 | 40 | ||
modified
src/auth/mod.rs +1 -0 | @@ -4,6 +4,7 @@ pub mod invite; | ||
| 4 | 4 | pub mod password; |
| 5 | 5 | pub mod revocation; |
| 6 | 6 | pub mod token; |
| 7 | +pub mod webauthn; | |
| 7 | 8 | |
| 8 | 9 | pub const SESSION_COOKIE: &str = "tt_session"; |
| 9 | 10 | /// ~6 mois. |
| @@ -4,6 +4,7 @@ pub mod invite; | |||
| 4 | pub mod password; | 4 | pub mod password; |
| 5 | pub mod revocation; | 5 | pub mod revocation; |
| 6 | pub mod token; | 6 | pub mod token; |
| 7 | +pub mod webauthn; | ||
| 7 | 8 | ||
| 8 | pub const SESSION_COOKIE: &str = "tt_session"; | 9 | pub const SESSION_COOKIE: &str = "tt_session"; |
| 9 | /// ~6 mois. | 10 | /// ~6 mois. |
added
src/auth/webauthn.rs +228 -0 | new file mode 100644 | ||
| @@ -0,0 +1,228 @@ | ||
| 1 | +//! Connexion par clé d'accès (WebAuthn/passkey) : construction du vérifieur | |
| 2 | +//! et scellement de l'état de cérémonie dans un cookie éphémère. | |
| 3 | +//! | |
| 4 | +//! Une cérémonie WebAuthn tient en deux requêtes : le serveur émet un | |
| 5 | +//! challenge, le navigateur le fait signer par l'authenticator, le serveur | |
| 6 | +//! vérifie. L'état entre les deux (`PasskeyRegistration` / | |
| 7 | +//! `DiscoverableAuthentication`) part dans un cookie **signé** plutôt qu'en | |
| 8 | +//! base : l'app est stateless côté sessions (biscuit + CSRF HMAC) et un | |
| 9 | +//! déploiement ne doit pas casser une cérémonie en cours. Le contenu n'est pas | |
| 10 | +//! secret — c'est exactement ce qui vient d'être envoyé au client — seule | |
| 11 | +//! l'intégrité compte : même recette que `csrf.rs` (HMAC-SHA256 sur une clé | |
| 12 | +//! dérivée de la clé privée biscuit), plus une expiration et une étanchéité | |
| 13 | +//! entre les deux types de cérémonie (le préfixe de domaine entre dans le MAC, | |
| 14 | +//! comme les faits distincts d'`invite.rs`). | |
| 15 | +//! | |
| 16 | +//! Taille : l'état ≈ challenge + politique + ~100 o par clé exclue à | |
| 17 | +//! l'enregistrement — très loin des ~4 Ko que tolère un cookie. | |
| 18 | + | |
| 19 | +use std::time::{SystemTime, UNIX_EPOCH}; | |
| 20 | + | |
| 21 | +use anyhow::{Context, Result, bail}; | |
| 22 | +use base64::Engine; | |
| 23 | +use base64::engine::general_purpose::URL_SAFE_NO_PAD; | |
| 24 | +use hmac::{Hmac, Mac}; | |
| 25 | +use serde::Serialize; | |
| 26 | +use serde::de::DeserializeOwned; | |
| 27 | +use sha2::Sha256; | |
| 28 | +use subtle::ConstantTimeEq; | |
| 29 | +use uuid::Uuid; | |
| 30 | +use webauthn_rs::prelude::{DiscoverableAuthentication, PasskeyRegistration, Url, Webauthn}; | |
| 31 | +use webauthn_rs::WebauthnBuilder; | |
| 32 | + | |
| 33 | +/// Cookie portant l'état d'un enregistrement de clé (utilisateur connecté). | |
| 34 | +pub const REG_COOKIE: &str = "tt_webauthn_reg"; | |
| 35 | +/// Cookie portant l'état d'une connexion par clé (anonyme). | |
| 36 | +pub const AUTH_COOKIE: &str = "tt_webauthn_auth"; | |
| 37 | +/// Une cérémonie a 5 minutes pour aboutir — large pour taper un PIN, | |
| 38 | +/// court pour un cookie qui traîne. | |
| 39 | +pub const CEREMONY_SECONDS: i64 = 300; | |
| 40 | + | |
| 41 | +/// rp_id WebAuthn = l'hôte de l'origine, sans schéma ni port. | |
| 42 | +pub fn rp_id_of(origin: &str) -> Result<String> { | |
| 43 | + let url = Url::parse(origin).with_context(|| format!("WEBAUTHN_ORIGIN invalide : {origin}"))?; | |
| 44 | + let host = url | |
| 45 | + .host_str() | |
| 46 | + .with_context(|| format!("WEBAUTHN_ORIGIN sans hôte : {origin}"))?; | |
| 47 | + Ok(host.to_string()) | |
| 48 | +} | |
| 49 | + | |
| 50 | +/// Construit le vérifieur pour une origine. Une origine invalide est une | |
| 51 | +/// erreur franche : mieux vaut refuser de démarrer que servir un rp_id faux | |
| 52 | +/// (les credentials enregistrés seraient liés au mauvais domaine). | |
| 53 | +pub fn build(origin: &str) -> Result<Webauthn> { | |
| 54 | + let rp_id = rp_id_of(origin)?; | |
| 55 | + let url = Url::parse(origin).expect("déjà validée par rp_id_of"); | |
| 56 | + let webauthn = WebauthnBuilder::new(&rp_id, &url) | |
| 57 | + .context("configuration WebAuthn refusée")? | |
| 58 | + .rp_name("Ex Ego") | |
| 59 | + .build() | |
| 60 | + .context("construction WebAuthn impossible")?; | |
| 61 | + Ok(webauthn) | |
| 62 | +} | |
| 63 | + | |
| 64 | +/// État d'un enregistrement : lié au compte (la session peut changer entre | |
| 65 | +/// début et fin) et porteur du libellé choisi — signé avec le reste, rien à | |
| 66 | +/// rejouer ni à falsifier au finish. | |
| 67 | +#[derive(serde::Serialize, serde::Deserialize)] | |
| 68 | +pub struct RegCeremony { | |
| 69 | + pub user_id: Uuid, | |
| 70 | + pub label: String, | |
| 71 | + pub state: PasskeyRegistration, | |
| 72 | +} | |
| 73 | + | |
| 74 | +pub fn seal_reg(key: &[u8; 32], ceremony: &RegCeremony) -> Result<String> { | |
| 75 | + seal_at(key, b"reg", ceremony, expiry_now()) | |
| 76 | +} | |
| 77 | + | |
| 78 | +pub fn unseal_reg(key: &[u8; 32], cookie: &str) -> Result<RegCeremony> { | |
| 79 | + unseal_at(key, b"reg", cookie, unix_now()) | |
| 80 | +} | |
| 81 | + | |
| 82 | +pub fn seal_auth(key: &[u8; 32], state: &DiscoverableAuthentication) -> Result<String> { | |
| 83 | + seal_at(key, b"auth", state, expiry_now()) | |
| 84 | +} | |
| 85 | + | |
| 86 | +pub fn unseal_auth(key: &[u8; 32], cookie: &str) -> Result<DiscoverableAuthentication> { | |
| 87 | + unseal_at(key, b"auth", cookie, unix_now()) | |
| 88 | +} | |
| 89 | + | |
| 90 | +// ─── mécanique ─── | |
| 91 | + | |
| 92 | +#[derive(serde::Serialize, serde::Deserialize)] | |
| 93 | +struct Sealed<T> { | |
| 94 | + exp: u64, | |
| 95 | + body: T, | |
| 96 | +} | |
| 97 | + | |
| 98 | +fn unix_now() -> u64 { | |
| 99 | + SystemTime::now() | |
| 100 | + .duration_since(UNIX_EPOCH) | |
| 101 | + .expect("horloge avant 1970") | |
| 102 | + .as_secs() | |
| 103 | +} | |
| 104 | + | |
| 105 | +fn expiry_now() -> u64 { | |
| 106 | + unix_now() + CEREMONY_SECONDS as u64 | |
| 107 | +} | |
| 108 | + | |
| 109 | +/// `base64url(json({exp, body})) + "." + hex(HMAC(key, purpose ‖ "." ‖ gauche))`. | |
| 110 | +/// Le purpose n'est pas transmis : il entre dans le MAC, donc un cookie scellé | |
| 111 | +/// pour un enregistrement est illisible comme connexion (et inversement). | |
| 112 | +fn seal_at<T: Serialize>(key: &[u8; 32], purpose: &[u8], body: &T, exp: u64) -> Result<String> { | |
| 113 | + let payload = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&Sealed { exp, body })?); | |
| 114 | + Ok(format!("{payload}.{}", tag_for(key, purpose, &payload))) | |
| 115 | +} | |
| 116 | + | |
| 117 | +fn unseal_at<T: DeserializeOwned>( | |
| 118 | + key: &[u8; 32], | |
| 119 | + purpose: &[u8], | |
| 120 | + cookie: &str, | |
| 121 | + now: u64, | |
| 122 | +) -> Result<T> { | |
| 123 | + let Some((payload, tag)) = cookie.rsplit_once('.') else { | |
| 124 | + bail!("cookie de cérémonie malformé"); | |
| 125 | + }; | |
| 126 | + let expected = tag_for(key, purpose, payload); | |
| 127 | + if !bool::from(expected.as_bytes().ct_eq(tag.as_bytes())) { | |
| 128 | + bail!("cookie de cérémonie invalide"); | |
| 129 | + } | |
| 130 | + let sealed: Sealed<T> = serde_json::from_slice( | |
| 131 | + &URL_SAFE_NO_PAD | |
| 132 | + .decode(payload) | |
| 133 | + .context("cookie de cérémonie illisible")?, | |
| 134 | + )?; | |
| 135 | + if now > sealed.exp { | |
| 136 | + bail!("cérémonie expirée"); | |
| 137 | + } | |
| 138 | + Ok(sealed.body) | |
| 139 | +} | |
| 140 | + | |
| 141 | +fn tag_for(key: &[u8; 32], purpose: &[u8], payload: &str) -> String { | |
| 142 | + let mut mac = Hmac::<Sha256>::new_from_slice(key).expect("HMAC accepte toute taille"); | |
| 143 | + mac.update(purpose); | |
| 144 | + mac.update(b"."); | |
| 145 | + mac.update(payload.as_bytes()); | |
| 146 | + hex::encode(mac.finalize().into_bytes()) | |
| 147 | +} | |
| 148 | + | |
| 149 | +#[cfg(test)] | |
| 150 | +mod tests { | |
| 151 | + use super::*; | |
| 152 | + | |
| 153 | + const KEY: [u8; 32] = [7u8; 32]; | |
| 154 | + | |
| 155 | + fn testbed() -> Webauthn { | |
| 156 | + build("https://example.com").expect("origine de test valide") | |
| 157 | + } | |
| 158 | + | |
| 159 | + fn reg_ceremony() -> RegCeremony { | |
| 160 | + let (_, state) = testbed() | |
| 161 | + .start_passkey_registration(Uuid::now_v7(), "alex", "alex", None) | |
| 162 | + .expect("challenge d'enregistrement"); | |
| 163 | + RegCeremony { | |
| 164 | + user_id: Uuid::now_v7(), | |
| 165 | + label: "Yubikey du bureau".into(), | |
| 166 | + state, | |
| 167 | + } | |
| 168 | + } | |
| 169 | + | |
| 170 | + #[test] | |
| 171 | + fn rp_id_is_the_bare_host() { | |
| 172 | + assert_eq!(rp_id_of("https://ex-ego.durillon.family").unwrap(), "ex-ego.durillon.family"); | |
| 173 | + assert_eq!(rp_id_of("http://localhost:8090").unwrap(), "localhost"); | |
| 174 | + assert!(rp_id_of("pas une url").is_err()); | |
| 175 | + assert!(rp_id_of("mailto:x@y.z").is_err(), "URL sans hôte refusée"); | |
| 176 | + } | |
| 177 | + | |
| 178 | + #[test] | |
| 179 | + fn reg_roundtrip() { | |
| 180 | + let c = reg_ceremony(); | |
| 181 | + let cookie = seal_reg(&KEY, &c).unwrap(); | |
| 182 | + let back = unseal_reg(&KEY, &cookie).unwrap(); | |
| 183 | + assert_eq!(back.user_id, c.user_id); | |
| 184 | + assert_eq!(back.label, c.label); | |
| 185 | + } | |
| 186 | + | |
| 187 | + #[test] | |
| 188 | + fn auth_roundtrip() { | |
| 189 | + let (_, state) = testbed() | |
| 190 | + .start_discoverable_authentication() | |
| 191 | + .expect("challenge de connexion"); | |
| 192 | + let cookie = seal_auth(&KEY, &state).unwrap(); | |
| 193 | + assert!(unseal_auth(&KEY, &cookie).is_ok()); | |
| 194 | + } | |
| 195 | + | |
| 196 | + /// Un cookie d'enregistrement ne vaut rien comme connexion, et | |
| 197 | + /// réciproquement — même garantie que les faits distincts d'invite.rs. | |
| 198 | + #[test] | |
| 199 | + fn purposes_are_sealed_off() { | |
| 200 | + let cookie = seal_reg(&KEY, ®_ceremony()).unwrap(); | |
| 201 | + assert!(unseal_auth(&KEY, &cookie).is_err()); | |
| 202 | + | |
| 203 | + let (_, state) = testbed().start_discoverable_authentication().unwrap(); | |
| 204 | + let cookie = seal_auth(&KEY, &state).unwrap(); | |
| 205 | + assert!(unseal_reg(&KEY, &cookie).is_err()); | |
| 206 | + } | |
| 207 | + | |
| 208 | + #[test] | |
| 209 | + fn expiry_tampering_and_wrong_key_are_refused() { | |
| 210 | + let c = reg_ceremony(); | |
| 211 | + | |
| 212 | + // Expirée : scellée avec une échéance déjà passée. | |
| 213 | + let stale = seal_at(&KEY, b"reg", &c, unix_now() - 1).unwrap(); | |
| 214 | + assert!(unseal_at::<RegCeremony>(&KEY, b"reg", &stale, unix_now()).is_err()); | |
| 215 | + | |
| 216 | + let cookie = seal_reg(&KEY, &c).unwrap(); | |
| 217 | + // Charge trafiquée : le MAC ne colle plus. | |
| 218 | + let mut forged = format!("A{}", &cookie[1..]); | |
| 219 | + if forged == cookie { | |
| 220 | + forged = format!("B{}", &cookie[1..]); | |
| 221 | + } | |
| 222 | + assert!(unseal_reg(&KEY, &forged).is_err()); | |
| 223 | + // Autre clé serveur. | |
| 224 | + assert!(unseal_reg(&[8u8; 32], &cookie).is_err()); | |
| 225 | + // Pas de séparateur. | |
| 226 | + assert!(unseal_reg(&KEY, "nimportequoi").is_err()); | |
| 227 | + } | |
| 228 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | +//! Connexion par clé d'accès (WebAuthn/passkey) : construction du vérifieur | ||
| 2 | +//! et scellement de l'état de cérémonie dans un cookie éphémère. | ||
| 3 | +//! | ||
| 4 | +//! Une cérémonie WebAuthn tient en deux requêtes : le serveur émet un | ||
| 5 | +//! challenge, le navigateur le fait signer par l'authenticator, le serveur | ||
| 6 | +//! vérifie. L'état entre les deux (`PasskeyRegistration` / | ||
| 7 | +//! `DiscoverableAuthentication`) part dans un cookie **signé** plutôt qu'en | ||
| 8 | +//! base : l'app est stateless côté sessions (biscuit + CSRF HMAC) et un | ||
| 9 | +//! déploiement ne doit pas casser une cérémonie en cours. Le contenu n'est pas | ||
| 10 | +//! secret — c'est exactement ce qui vient d'être envoyé au client — seule | ||
| 11 | +//! l'intégrité compte : même recette que `csrf.rs` (HMAC-SHA256 sur une clé | ||
| 12 | +//! dérivée de la clé privée biscuit), plus une expiration et une étanchéité | ||
| 13 | +//! entre les deux types de cérémonie (le préfixe de domaine entre dans le MAC, | ||
| 14 | +//! comme les faits distincts d'`invite.rs`). | ||
| 15 | +//! | ||
| 16 | +//! Taille : l'état ≈ challenge + politique + ~100 o par clé exclue à | ||
| 17 | +//! l'enregistrement — très loin des ~4 Ko que tolère un cookie. | ||
| 18 | + | ||
| 19 | +use std::time::{SystemTime, UNIX_EPOCH}; | ||
| 20 | + | ||
| 21 | +use anyhow::{Context, Result, bail}; | ||
| 22 | +use base64::Engine; | ||
| 23 | +use base64::engine::general_purpose::URL_SAFE_NO_PAD; | ||
| 24 | +use hmac::{Hmac, Mac}; | ||
| 25 | +use serde::Serialize; | ||
| 26 | +use serde::de::DeserializeOwned; | ||
| 27 | +use sha2::Sha256; | ||
| 28 | +use subtle::ConstantTimeEq; | ||
| 29 | +use uuid::Uuid; | ||
| 30 | +use webauthn_rs::prelude::{DiscoverableAuthentication, PasskeyRegistration, Url, Webauthn}; | ||
| 31 | +use webauthn_rs::WebauthnBuilder; | ||
| 32 | + | ||
| 33 | +/// Cookie portant l'état d'un enregistrement de clé (utilisateur connecté). | ||
| 34 | +pub const REG_COOKIE: &str = "tt_webauthn_reg"; | ||
| 35 | +/// Cookie portant l'état d'une connexion par clé (anonyme). | ||
| 36 | +pub const AUTH_COOKIE: &str = "tt_webauthn_auth"; | ||
| 37 | +/// Une cérémonie a 5 minutes pour aboutir — large pour taper un PIN, | ||
| 38 | +/// court pour un cookie qui traîne. | ||
| 39 | +pub const CEREMONY_SECONDS: i64 = 300; | ||
| 40 | + | ||
| 41 | +/// rp_id WebAuthn = l'hôte de l'origine, sans schéma ni port. | ||
| 42 | +pub fn rp_id_of(origin: &str) -> Result<String> { | ||
| 43 | + let url = Url::parse(origin).with_context(|| format!("WEBAUTHN_ORIGIN invalide : {origin}"))?; | ||
| 44 | + let host = url | ||
| 45 | + .host_str() | ||
| 46 | + .with_context(|| format!("WEBAUTHN_ORIGIN sans hôte : {origin}"))?; | ||
| 47 | + Ok(host.to_string()) | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +/// Construit le vérifieur pour une origine. Une origine invalide est une | ||
| 51 | +/// erreur franche : mieux vaut refuser de démarrer que servir un rp_id faux | ||
| 52 | +/// (les credentials enregistrés seraient liés au mauvais domaine). | ||
| 53 | +pub fn build(origin: &str) -> Result<Webauthn> { | ||
| 54 | + let rp_id = rp_id_of(origin)?; | ||
| 55 | + let url = Url::parse(origin).expect("déjà validée par rp_id_of"); | ||
| 56 | + let webauthn = WebauthnBuilder::new(&rp_id, &url) | ||
| 57 | + .context("configuration WebAuthn refusée")? | ||
| 58 | + .rp_name("Ex Ego") | ||
| 59 | + .build() | ||
| 60 | + .context("construction WebAuthn impossible")?; | ||
| 61 | + Ok(webauthn) | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +/// État d'un enregistrement : lié au compte (la session peut changer entre | ||
| 65 | +/// début et fin) et porteur du libellé choisi — signé avec le reste, rien à | ||
| 66 | +/// rejouer ni à falsifier au finish. | ||
| 67 | +#[derive(serde::Serialize, serde::Deserialize)] | ||
| 68 | +pub struct RegCeremony { | ||
| 69 | + pub user_id: Uuid, | ||
| 70 | + pub label: String, | ||
| 71 | + pub state: PasskeyRegistration, | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +pub fn seal_reg(key: &[u8; 32], ceremony: &RegCeremony) -> Result<String> { | ||
| 75 | + seal_at(key, b"reg", ceremony, expiry_now()) | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +pub fn unseal_reg(key: &[u8; 32], cookie: &str) -> Result<RegCeremony> { | ||
| 79 | + unseal_at(key, b"reg", cookie, unix_now()) | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +pub fn seal_auth(key: &[u8; 32], state: &DiscoverableAuthentication) -> Result<String> { | ||
| 83 | + seal_at(key, b"auth", state, expiry_now()) | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +pub fn unseal_auth(key: &[u8; 32], cookie: &str) -> Result<DiscoverableAuthentication> { | ||
| 87 | + unseal_at(key, b"auth", cookie, unix_now()) | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +// ─── mécanique ─── | ||
| 91 | + | ||
| 92 | +#[derive(serde::Serialize, serde::Deserialize)] | ||
| 93 | +struct Sealed<T> { | ||
| 94 | + exp: u64, | ||
| 95 | + body: T, | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +fn unix_now() -> u64 { | ||
| 99 | + SystemTime::now() | ||
| 100 | + .duration_since(UNIX_EPOCH) | ||
| 101 | + .expect("horloge avant 1970") | ||
| 102 | + .as_secs() | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +fn expiry_now() -> u64 { | ||
| 106 | + unix_now() + CEREMONY_SECONDS as u64 | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +/// `base64url(json({exp, body})) + "." + hex(HMAC(key, purpose ‖ "." ‖ gauche))`. | ||
| 110 | +/// Le purpose n'est pas transmis : il entre dans le MAC, donc un cookie scellé | ||
| 111 | +/// pour un enregistrement est illisible comme connexion (et inversement). | ||
| 112 | +fn seal_at<T: Serialize>(key: &[u8; 32], purpose: &[u8], body: &T, exp: u64) -> Result<String> { | ||
| 113 | + let payload = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&Sealed { exp, body })?); | ||
| 114 | + Ok(format!("{payload}.{}", tag_for(key, purpose, &payload))) | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +fn unseal_at<T: DeserializeOwned>( | ||
| 118 | + key: &[u8; 32], | ||
| 119 | + purpose: &[u8], | ||
| 120 | + cookie: &str, | ||
| 121 | + now: u64, | ||
| 122 | +) -> Result<T> { | ||
| 123 | + let Some((payload, tag)) = cookie.rsplit_once('.') else { | ||
| 124 | + bail!("cookie de cérémonie malformé"); | ||
| 125 | + }; | ||
| 126 | + let expected = tag_for(key, purpose, payload); | ||
| 127 | + if !bool::from(expected.as_bytes().ct_eq(tag.as_bytes())) { | ||
| 128 | + bail!("cookie de cérémonie invalide"); | ||
| 129 | + } | ||
| 130 | + let sealed: Sealed<T> = serde_json::from_slice( | ||
| 131 | + &URL_SAFE_NO_PAD | ||
| 132 | + .decode(payload) | ||
| 133 | + .context("cookie de cérémonie illisible")?, | ||
| 134 | + )?; | ||
| 135 | + if now > sealed.exp { | ||
| 136 | + bail!("cérémonie expirée"); | ||
| 137 | + } | ||
| 138 | + Ok(sealed.body) | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +fn tag_for(key: &[u8; 32], purpose: &[u8], payload: &str) -> String { | ||
| 142 | + let mut mac = Hmac::<Sha256>::new_from_slice(key).expect("HMAC accepte toute taille"); | ||
| 143 | + mac.update(purpose); | ||
| 144 | + mac.update(b"."); | ||
| 145 | + mac.update(payload.as_bytes()); | ||
| 146 | + hex::encode(mac.finalize().into_bytes()) | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +#[cfg(test)] | ||
| 150 | +mod tests { | ||
| 151 | + use super::*; | ||
| 152 | + | ||
| 153 | + const KEY: [u8; 32] = [7u8; 32]; | ||
| 154 | + | ||
| 155 | + fn testbed() -> Webauthn { | ||
| 156 | + build("https://example.com").expect("origine de test valide") | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + fn reg_ceremony() -> RegCeremony { | ||
| 160 | + let (_, state) = testbed() | ||
| 161 | + .start_passkey_registration(Uuid::now_v7(), "alex", "alex", None) | ||
| 162 | + .expect("challenge d'enregistrement"); | ||
| 163 | + RegCeremony { | ||
| 164 | + user_id: Uuid::now_v7(), | ||
| 165 | + label: "Yubikey du bureau".into(), | ||
| 166 | + state, | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + #[test] | ||
| 171 | + fn rp_id_is_the_bare_host() { | ||
| 172 | + assert_eq!(rp_id_of("https://ex-ego.durillon.family").unwrap(), "ex-ego.durillon.family"); | ||
| 173 | + assert_eq!(rp_id_of("http://localhost:8090").unwrap(), "localhost"); | ||
| 174 | + assert!(rp_id_of("pas une url").is_err()); | ||
| 175 | + assert!(rp_id_of("mailto:x@y.z").is_err(), "URL sans hôte refusée"); | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + #[test] | ||
| 179 | + fn reg_roundtrip() { | ||
| 180 | + let c = reg_ceremony(); | ||
| 181 | + let cookie = seal_reg(&KEY, &c).unwrap(); | ||
| 182 | + let back = unseal_reg(&KEY, &cookie).unwrap(); | ||
| 183 | + assert_eq!(back.user_id, c.user_id); | ||
| 184 | + assert_eq!(back.label, c.label); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + #[test] | ||
| 188 | + fn auth_roundtrip() { | ||
| 189 | + let (_, state) = testbed() | ||
| 190 | + .start_discoverable_authentication() | ||
| 191 | + .expect("challenge de connexion"); | ||
| 192 | + let cookie = seal_auth(&KEY, &state).unwrap(); | ||
| 193 | + assert!(unseal_auth(&KEY, &cookie).is_ok()); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + /// Un cookie d'enregistrement ne vaut rien comme connexion, et | ||
| 197 | + /// réciproquement — même garantie que les faits distincts d'invite.rs. | ||
| 198 | + #[test] | ||
| 199 | + fn purposes_are_sealed_off() { | ||
| 200 | + let cookie = seal_reg(&KEY, ®_ceremony()).unwrap(); | ||
| 201 | + assert!(unseal_auth(&KEY, &cookie).is_err()); | ||
| 202 | + | ||
| 203 | + let (_, state) = testbed().start_discoverable_authentication().unwrap(); | ||
| 204 | + let cookie = seal_auth(&KEY, &state).unwrap(); | ||
| 205 | + assert!(unseal_reg(&KEY, &cookie).is_err()); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + #[test] | ||
| 209 | + fn expiry_tampering_and_wrong_key_are_refused() { | ||
| 210 | + let c = reg_ceremony(); | ||
| 211 | + | ||
| 212 | + // Expirée : scellée avec une échéance déjà passée. | ||
| 213 | + let stale = seal_at(&KEY, b"reg", &c, unix_now() - 1).unwrap(); | ||
| 214 | + assert!(unseal_at::<RegCeremony>(&KEY, b"reg", &stale, unix_now()).is_err()); | ||
| 215 | + | ||
| 216 | + let cookie = seal_reg(&KEY, &c).unwrap(); | ||
| 217 | + // Charge trafiquée : le MAC ne colle plus. | ||
| 218 | + let mut forged = format!("A{}", &cookie[1..]); | ||
| 219 | + if forged == cookie { | ||
| 220 | + forged = format!("B{}", &cookie[1..]); | ||
| 221 | + } | ||
| 222 | + assert!(unseal_reg(&KEY, &forged).is_err()); | ||
| 223 | + // Autre clé serveur. | ||
| 224 | + assert!(unseal_reg(&[8u8; 32], &cookie).is_err()); | ||
| 225 | + // Pas de séparateur. | ||
| 226 | + assert!(unseal_reg(&KEY, "nimportequoi").is_err()); | ||
| 227 | + } | ||
| 228 | +} | ||
modified
src/config.rs +14 -0 | @@ -46,6 +46,20 @@ pub fn signup_key() -> Option<String> { | ||
| 46 | 46 | .filter(|k| !k.is_empty()) |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | +/// Origine WebAuthn explicite (`WEBAUTHN_ORIGIN`, ex. | |
| 50 | +/// `https://ex-ego.durillon.family` ; `http://localhost:8090` en local). | |
| 51 | +/// `None` = variable absente = **connexion par clé d'accès éteinte partout** | |
| 52 | +/// (bouton masqué, endpoints en 404), sur le modèle de `SIGNUP_KEY`. | |
| 53 | +/// | |
| 54 | +/// Volontairement PAS `public_origin()` : le rp_id lie cryptographiquement les | |
| 55 | +/// credentials au domaine, il ne doit jamais dériver d'en-têtes forgeables. | |
| 56 | +pub fn webauthn_origin() -> Option<String> { | |
| 57 | + std::env::var("WEBAUTHN_ORIGIN") | |
| 58 | + .ok() | |
| 59 | + .map(|o| o.trim().trim_end_matches('/').to_string()) | |
| 60 | + .filter(|o| !o.is_empty()) | |
| 61 | +} | |
| 62 | + | |
| 49 | 63 | /// Commit déployé — `COMMIT_ID`, posée par Clever Cloud dans l'environnement |
| 50 | 64 | /// de l'app. Tronqué à 8 caractères ; « dev » en local (variable absente). |
| 51 | 65 | pub fn commit_id() -> String { |
| @@ -46,6 +46,20 @@ pub fn signup_key() -> Option<String> { | |||
| 46 | .filter(|k| !k.is_empty()) | 46 | .filter(|k| !k.is_empty()) |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | +/// Origine WebAuthn explicite (`WEBAUTHN_ORIGIN`, ex. | ||
| 50 | +/// `https://ex-ego.durillon.family` ; `http://localhost:8090` en local). | ||
| 51 | +/// `None` = variable absente = **connexion par clé d'accès éteinte partout** | ||
| 52 | +/// (bouton masqué, endpoints en 404), sur le modèle de `SIGNUP_KEY`. | ||
| 53 | +/// | ||
| 54 | +/// Volontairement PAS `public_origin()` : le rp_id lie cryptographiquement les | ||
| 55 | +/// credentials au domaine, il ne doit jamais dériver d'en-têtes forgeables. | ||
| 56 | +pub fn webauthn_origin() -> Option<String> { | ||
| 57 | + std::env::var("WEBAUTHN_ORIGIN") | ||
| 58 | + .ok() | ||
| 59 | + .map(|o| o.trim().trim_end_matches('/').to_string()) | ||
| 60 | + .filter(|o| !o.is_empty()) | ||
| 61 | +} | ||
| 62 | + | ||
| 49 | /// Commit déployé — `COMMIT_ID`, posée par Clever Cloud dans l'environnement | 63 | /// Commit déployé — `COMMIT_ID`, posée par Clever Cloud dans l'environnement |
| 50 | /// de l'app. Tronqué à 8 caractères ; « dev » en local (variable absente). | 64 | /// de l'app. Tronqué à 8 caractères ; « dev » en local (variable absente). |
| 51 | pub fn commit_id() -> String { | 65 | pub fn commit_id() -> String { |
modified
src/ratelimit.rs +5 -0 | @@ -52,6 +52,11 @@ impl Quota { | ||
| 52 | 52 | Self::new(10, DAY) |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | + /// 10 connexions par clé d'accès échouées par heure. | |
| 56 | + pub fn webauthn_fails() -> Self { | |
| 57 | + Self::new(10, HOUR) | |
| 58 | + } | |
| 59 | + | |
| 55 | 60 | /// L'IP a-t-elle encore du quota ? N'enregistre rien. |
| 56 | 61 | pub fn allows(&self, ip: &str) -> bool { |
| 57 | 62 | self.allows_at(ip, Instant::now()) |
| @@ -52,6 +52,11 @@ impl Quota { | |||
| 52 | Self::new(10, DAY) | 52 | Self::new(10, DAY) |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | + /// 10 connexions par clé d'accès échouées par heure. | ||
| 56 | + pub fn webauthn_fails() -> Self { | ||
| 57 | + Self::new(10, HOUR) | ||
| 58 | + } | ||
| 59 | + | ||
| 55 | /// L'IP a-t-elle encore du quota ? N'enregistre rien. | 60 | /// L'IP a-t-elle encore du quota ? N'enregistre rien. |
| 56 | pub fn allows(&self, ip: &str) -> bool { | 61 | pub fn allows(&self, ip: &str) -> bool { |
| 57 | self.allows_at(ip, Instant::now()) | 62 | self.allows_at(ip, Instant::now()) |
modified
src/state.rs +25 -0 | @@ -5,6 +5,8 @@ use biscuit_auth::{KeyPair, PrivateKey, PublicKey, builder::Algorithm}; | ||
| 5 | 5 | use sha2::{Digest, Sha256}; |
| 6 | 6 | use sqlx::PgPool; |
| 7 | 7 | |
| 8 | +use webauthn_rs::prelude::Webauthn; | |
| 9 | + | |
| 8 | 10 | use crate::auth::revocation::RevocationCache; |
| 9 | 11 | use crate::ratelimit::Quota; |
| 10 | 12 | |
| @@ -19,6 +21,13 @@ pub struct AppState { | ||
| 19 | 21 | pub signups: Quota, |
| 20 | 22 | /// Clés d'inscription fausses par IP (10/jour) — frein à la force brute. |
| 21 | 23 | pub bad_keys: Quota, |
| 24 | + /// Vérifieur WebAuthn — `None` = `WEBAUTHN_ORIGIN` absente = connexion | |
| 25 | + /// par clé d'accès éteinte (motif `SIGNUP_KEY`). | |
| 26 | + pub webauthn: Option<Arc<Webauthn>>, | |
| 27 | + /// Clé de scellement des cookies de cérémonie WebAuthn (dérivée comme `csrf_key`). | |
| 28 | + pub webauthn_key: [u8; 32], | |
| 29 | + /// Connexions par clé échouées par IP (10/h) — frein au rejeu de credentials. | |
| 30 | + pub webauthn_fails: Quota, | |
| 22 | 31 | } |
| 23 | 32 | |
| 24 | 33 | impl AppState { |
| @@ -33,6 +42,19 @@ impl AppState { | ||
| 33 | 42 | hasher.update(private.to_bytes().as_slice()); |
| 34 | 43 | let csrf_key: [u8; 32] = hasher.finalize().into(); |
| 35 | 44 | |
| 45 | + // Même recette, autre domaine : la clé des cookies de cérémonie WebAuthn. | |
| 46 | + let mut hasher = Sha256::new(); | |
| 47 | + hasher.update(b"webauthn"); | |
| 48 | + hasher.update(private.to_bytes().as_slice()); | |
| 49 | + let webauthn_key: [u8; 32] = hasher.finalize().into(); | |
| 50 | + | |
| 51 | + // Origine absente = feature éteinte ; origine invalide = refus de | |
| 52 | + // démarrer (une misconfiguration doit se voir, pas s'ignorer). | |
| 53 | + let webauthn = match crate::config::webauthn_origin() { | |
| 54 | + Some(origin) => Some(Arc::new(crate::auth::webauthn::build(&origin)?)), | |
| 55 | + None => None, | |
| 56 | + }; | |
| 57 | + | |
| 36 | 58 | Ok(Self { |
| 37 | 59 | pool, |
| 38 | 60 | root: Arc::new(root), |
| @@ -41,6 +63,9 @@ impl AppState { | ||
| 41 | 63 | revocations: RevocationCache::default(), |
| 42 | 64 | signups: Quota::signups(), |
| 43 | 65 | bad_keys: Quota::bad_keys(), |
| 66 | + webauthn, | |
| 67 | + webauthn_key, | |
| 68 | + webauthn_fails: Quota::webauthn_fails(), | |
| 44 | 69 | }) |
| 45 | 70 | } |
| 46 | 71 | } |
| @@ -5,6 +5,8 @@ use biscuit_auth::{KeyPair, PrivateKey, PublicKey, builder::Algorithm}; | |||
| 5 | use sha2::{Digest, Sha256}; | 5 | use sha2::{Digest, Sha256}; |
| 6 | use sqlx::PgPool; | 6 | use sqlx::PgPool; |
| 7 | 7 | ||
| 8 | +use webauthn_rs::prelude::Webauthn; | ||
| 9 | + | ||
| 8 | use crate::auth::revocation::RevocationCache; | 10 | use crate::auth::revocation::RevocationCache; |
| 9 | use crate::ratelimit::Quota; | 11 | use crate::ratelimit::Quota; |
| 10 | 12 | ||
| @@ -19,6 +21,13 @@ pub struct AppState { | |||
| 19 | pub signups: Quota, | 21 | pub signups: Quota, |
| 20 | /// Clés d'inscription fausses par IP (10/jour) — frein à la force brute. | 22 | /// Clés d'inscription fausses par IP (10/jour) — frein à la force brute. |
| 21 | pub bad_keys: Quota, | 23 | pub bad_keys: Quota, |
| 24 | + /// Vérifieur WebAuthn — `None` = `WEBAUTHN_ORIGIN` absente = connexion | ||
| 25 | + /// par clé d'accès éteinte (motif `SIGNUP_KEY`). | ||
| 26 | + pub webauthn: Option<Arc<Webauthn>>, | ||
| 27 | + /// Clé de scellement des cookies de cérémonie WebAuthn (dérivée comme `csrf_key`). | ||
| 28 | + pub webauthn_key: [u8; 32], | ||
| 29 | + /// Connexions par clé échouées par IP (10/h) — frein au rejeu de credentials. | ||
| 30 | + pub webauthn_fails: Quota, | ||
| 22 | } | 31 | } |
| 23 | 32 | ||
| 24 | impl AppState { | 33 | impl AppState { |
| @@ -33,6 +42,19 @@ impl AppState { | |||
| 33 | hasher.update(private.to_bytes().as_slice()); | 42 | hasher.update(private.to_bytes().as_slice()); |
| 34 | let csrf_key: [u8; 32] = hasher.finalize().into(); | 43 | let csrf_key: [u8; 32] = hasher.finalize().into(); |
| 35 | 44 | ||
| 45 | + // Même recette, autre domaine : la clé des cookies de cérémonie WebAuthn. | ||
| 46 | + let mut hasher = Sha256::new(); | ||
| 47 | + hasher.update(b"webauthn"); | ||
| 48 | + hasher.update(private.to_bytes().as_slice()); | ||
| 49 | + let webauthn_key: [u8; 32] = hasher.finalize().into(); | ||
| 50 | + | ||
| 51 | + // Origine absente = feature éteinte ; origine invalide = refus de | ||
| 52 | + // démarrer (une misconfiguration doit se voir, pas s'ignorer). | ||
| 53 | + let webauthn = match crate::config::webauthn_origin() { | ||
| 54 | + Some(origin) => Some(Arc::new(crate::auth::webauthn::build(&origin)?)), | ||
| 55 | + None => None, | ||
| 56 | + }; | ||
| 57 | + | ||
| 36 | Ok(Self { | 58 | Ok(Self { |
| 37 | pool, | 59 | pool, |
| 38 | root: Arc::new(root), | 60 | root: Arc::new(root), |
| @@ -41,6 +63,9 @@ impl AppState { | |||
| 41 | revocations: RevocationCache::default(), | 63 | revocations: RevocationCache::default(), |
| 42 | signups: Quota::signups(), | 64 | signups: Quota::signups(), |
| 43 | bad_keys: Quota::bad_keys(), | 65 | bad_keys: Quota::bad_keys(), |
| 66 | + webauthn, | ||
| 67 | + webauthn_key, | ||
| 68 | + webauthn_fails: Quota::webauthn_fails(), | ||
| 44 | }) | 69 | }) |
| 45 | } | 70 | } |
| 46 | } | 71 | } |