From 5cb07b9780d54f30e7aeb55d5a2721b0e4d7aa6a Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Mon, 21 Nov 2022 11:56:32 +0100 Subject: [PATCH] dependencies: update opaque_ke to v2 This is a breaking change: it changes the way the password data is serialized in the DB, so any saved password is no longer readable (all passwords have to get reset). --- CHANGELOG.md | 4 + Cargo.lock | 462 ++++++++++++++------- app/src/components/change_password.rs | 52 +-- app/src/components/create_user.rs | 12 +- app/src/components/login.rs | 40 +- app/src/components/reset_password_step2.rs | 17 +- auth/Cargo.toml | 5 +- auth/src/opaque.rs | 73 ++-- migration-tool/src/lldap.rs | 2 +- server/Cargo.toml | 3 +- server/src/domain/handler.rs | 2 + server/src/domain/sql_backend_handler.rs | 3 +- server/src/domain/sql_migrations.rs | 43 +- server/src/domain/sql_opaque_handler.rs | 48 ++- server/src/domain/sql_tables.rs | 29 +- server/src/infra/ldap_handler.rs | 86 +--- server/src/infra/tcp_backend_handler.rs | 1 + server/src/main.rs | 2 +- 18 files changed, 545 insertions(+), 339 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a42a54..b09fdb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Breaking + + - The password storage mechanism has changed, the passwords cannot be ported over from the old version. All passwords have to be reset. + ## [0.4.1] - 2022-10-10 ### Added diff --git a/Cargo.lock b/Cargo.lock index f8e8d6c..45df20d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,7 +102,7 @@ dependencies = [ "rand 0.8.5", "regex", "serde", - "sha-1", + "sha-1 0.9.8", "smallvec", "time 0.2.27", "tokio", @@ -364,10 +364,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" [[package]] -name = "arrayref" -version = "0.3.6" +name = "argon2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] [[package]] name = "arrayvec" @@ -395,7 +400,7 @@ checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", - "displaydoc 0.2.3", + "displaydoc", "nom 7.1.1", "num-traits", "rusticata-macros", @@ -452,7 +457,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -466,15 +471,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -502,6 +498,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base64" version = "0.13.0" @@ -530,14 +532,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "blake2b_simd" -version = "0.5.11" +name = "blake2" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +checksum = "b12e5fd123190ce1c2e559308a94c9bacad77907d4c6005d9e58fe1a0689e55e" dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", + "digest 0.10.6", ] [[package]] @@ -549,6 +549,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "boolinator" version = "2.4.0" @@ -732,9 +741,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "const-oid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" [[package]] name = "const_fn" @@ -742,12 +757,6 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "convert_case" version = "0.4.0" @@ -882,13 +891,34 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.2.11" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", "rand_core 0.6.3", "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", ] [[package]] @@ -901,16 +931,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ct-codecs" version = "1.1.1" @@ -919,14 +939,26 @@ checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" [[package]] name = "curve25519-dalek" -version = "3.2.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", + "digest 0.9.0", "rand_core 0.5.1", - "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4033478fbf70d6acf2655ac70da91ee65852d69daf7a67bf7a2f518fb47aafcf" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.3", "subtle", "zeroize", ] @@ -974,12 +1006,22 @@ checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" [[package]] name = "der" -version = "0.4.5" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" dependencies = [ - "const-oid", - "crypto-bigint", + "const-oid 0.7.1", + "crypto-bigint 0.3.2", + "pem-rfc7468", +] + +[[package]] +name = "der" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dd2ae565c0a381dde7fade45fce95984c568bdcb4700a4fdbe3175e0380b2f" +dependencies = [ + "const-oid 0.9.1", ] [[package]] @@ -989,13 +1031,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" dependencies = [ "asn1-rs", - "displaydoc 0.2.3", + "displaydoc", "nom 7.1.1", "num-bigint 0.4.3", "num-traits", "rusticata-macros", ] +[[package]] +name = "derive-where" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d322f2907b2abad3117790c1a54d8f2d64574ba0fbea54cb6c6e66a0e50d99a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_builder" version = "0.10.2" @@ -1066,6 +1119,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + [[package]] name = "dirs" version = "4.0.0" @@ -1092,17 +1156,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" -[[package]] -name = "displaydoc" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc2ab4d5a16117f9029e9a6b5e4e79f4c67f6519bc134210d4d4a04ba31f41b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "displaydoc" version = "0.2.3" @@ -1132,6 +1185,25 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint 0.4.9", + "der 0.6.0", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "rand_core 0.6.3", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "email-encoding" version = "0.1.3" @@ -1157,6 +1229,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "failure" version = "0.1.8" @@ -1188,6 +1266,16 @@ dependencies = [ "instant", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.3", + "subtle", +] + [[package]] name = "figment" version = "0.10.6" @@ -1392,15 +1480,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "generic-bytes" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6638d839bbd1cea640d8c5348dd82e0d545dbd364f3c2a251646eaf2ef0773b" -dependencies = [ - "generic-array", -] - [[package]] name = "getrandom" version = "0.1.16" @@ -1602,6 +1681,17 @@ dependencies = [ "syn", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.3", + "subtle", +] + [[package]] name = "h2" version = "0.3.13" @@ -1677,12 +1767,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.11.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" dependencies = [ - "digest", - "hmac 0.11.0", + "hmac 0.12.1", ] [[package]] @@ -1691,18 +1780,17 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ - "crypto-mac 0.10.1", - "digest", + "crypto-mac", + "digest 0.9.0", ] [[package]] name = "hmac" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "crypto-mac 0.11.1", - "digest", + "digest 0.10.6", ] [[package]] @@ -1825,7 +1913,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown 0.9.1", "serde", ] @@ -1956,12 +2044,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86e46349d67dc03bdbdb28da0337a355a53ca1d5156452722c36fe21d0e6389b" dependencies = [ "base64", - "crypto-mac 0.10.1", - "digest", + "crypto-mac", + "digest 0.9.0", "hmac 0.10.1", "serde", "serde_json", - "sha2", + "sha2 0.9.9", ] [[package]] @@ -2083,9 +2171,9 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[package]] name = "libsqlite3-sys" -version = "0.23.2" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" +checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" dependencies = [ "cc", "pkg-config", @@ -2147,7 +2235,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "sha2", + "sha2 0.9.9", "sqlx", "sqlx-core", "thiserror", @@ -2198,16 +2286,16 @@ dependencies = [ name = "lldap_auth" version = "0.3.0-alpha.1" dependencies = [ + "argon2", "chrono", - "curve25519-dalek", - "digest", + "curve25519-dalek 3.2.0", + "digest 0.9.0", "generic-array", "getrandom 0.2.7", "opaque-ke", "rand 0.8.5", - "rust-argon2", "serde", - "sha2", + "sha2 0.9.9", "thiserror", ] @@ -2235,7 +2323,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -2265,13 +2353,11 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "md-5" -version = "0.9.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "block-buffer", - "digest", - "opaque-debug", + "digest 0.10.6", ] [[package]] @@ -2443,7 +2529,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -2454,18 +2540,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-bigint-dig" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" +checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905" dependencies = [ - "autocfg 0.1.8", "byteorder", "lazy_static", "libm", @@ -2483,7 +2568,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -2493,7 +2578,7 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -2504,7 +2589,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] @@ -2515,7 +2600,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", "libm", ] @@ -2570,22 +2655,23 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "opaque-ke" -version = "0.6.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507fdf0b89eabfde58445f88807f57f253f72236e44960ebf690d897803cd18d" +checksum = "76d410412d23781909d90c3900c5783e830586765f2277bccc78167da8af81a5" dependencies = [ - "base64", - "curve25519-dalek", - "digest", - "displaydoc 0.1.7", + "curve25519-dalek 4.0.0-pre.1", + "derive-where", + "digest 0.10.6", + "displaydoc", + "elliptic-curve", "generic-array", - "generic-bytes", + "getrandom 0.2.7", "hkdf", - "hmac 0.11.0", + "hmac 0.12.1", "rand 0.8.5", "serde", "subtle", - "thiserror", + "voprf", "zeroize", ] @@ -2661,6 +2747,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.3", + "subtle", +] + [[package]] name = "paste" version = "1.0.7" @@ -2692,9 +2789,9 @@ dependencies = [ [[package]] name = "pem-rfc7468" -version = "0.2.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e93a3b1cc0510b03020f33f21e62acdde3dcaef432edc95bea377fbd4c2cd4" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" dependencies = [ "base64ct", ] @@ -2739,24 +2836,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs1" -version = "0.2.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "116bee8279d783c0cf370efa1a94632f2108e5ef0bb32df31f051647810a4e2c" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" dependencies = [ - "der", - "pem-rfc7468", + "der 0.5.1", + "pkcs8", "zeroize", ] [[package]] name = "pkcs8" -version = "0.7.6" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" dependencies = [ - "der", - "pem-rfc7468", - "pkcs1", + "der 0.5.1", "spki", "zeroize", ] @@ -3075,36 +3170,24 @@ dependencies = [ [[package]] name = "rsa" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c2603e2823634ab331437001b411b9ed11660fbc4066f3908c84a9439260d" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" dependencies = [ "byteorder", - "digest", - "lazy_static", + "digest 0.10.6", "num-bigint-dig", "num-integer", "num-iter", "num-traits", "pkcs1", "pkcs8", - "rand 0.8.5", + "rand_core 0.6.3", + "smallvec", "subtle", "zeroize", ] -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - [[package]] name = "rustc-demangle" version = "0.1.21" @@ -3271,6 +3354,19 @@ dependencies = [ "syn", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der 0.6.0", + "generic-array", + "subtle", + "zeroize", +] + [[package]] name = "secstr" version = "0.5.0" @@ -3384,13 +3480,24 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sha1" version = "0.6.1" @@ -3412,13 +3519,24 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -3466,9 +3584,9 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smartstring" @@ -3512,11 +3630,12 @@ dependencies = [ [[package]] name = "spki" -version = "0.4.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" dependencies = [ - "der", + "base64ct", + "der 0.5.1", ] [[package]] @@ -3542,9 +3661,9 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195183bf6ff8328bb82c0511a83faf60aacf75840103388851db61d7a9854ae3" +checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ "ahash", "atoi", @@ -3555,9 +3674,10 @@ dependencies = [ "chrono", "crc", "crossbeam-queue", - "digest", + "digest 0.10.6", "dirs", "either", + "event-listener", "flume", "futures-channel", "futures-core", @@ -3567,7 +3687,8 @@ dependencies = [ "generic-array", "hashlink", "hex", - "hmac 0.11.0", + "hkdf", + "hmac 0.12.1", "indexmap", "itoa 1.0.2", "libc", @@ -3584,8 +3705,8 @@ dependencies = [ "rustls 0.19.1", "serde", "serde_json", - "sha-1", - "sha2", + "sha-1 0.10.0", + "sha2 0.10.6", "smallvec", "sqlformat", "sqlx-rt", @@ -3610,7 +3731,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2", + "sha2 0.9.9", "sqlx-core", "sqlx-rt", "syn", @@ -4291,6 +4412,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "voprf" +version = "0.4.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081acbe8fcf05d5e8e2aad8ef3d40e02eddeaec07c75a9770d862a0fc0874322" +dependencies = [ + "curve25519-dalek 4.0.0-pre.1", + "derive-where", + "digest 0.10.6", + "displaydoc", + "elliptic-curve", + "generic-array", + "rand_core 0.6.3", + "serde", + "sha2 0.10.6", + "subtle", + "zeroize", +] + [[package]] name = "want" version = "0.3.0" @@ -4671,9 +4811,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.1.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ "zeroize_derive", ] diff --git a/app/src/components/change_password.rs b/app/src/components/change_password.rs index 91e1673..a0d7fd9 100644 --- a/app/src/components/change_password.rs +++ b/app/src/components/change_password.rs @@ -73,9 +73,12 @@ pub struct Props { pub enum Msg { FormUpdate, Submit, - AuthenticationStartResponse(Result>), + AuthenticationStartResponse(String, Result>), SubmitNewPassword, - RegistrationStartResponse(Result>), + RegistrationStartResponse( + String, + Result>, + ), RegistrationFinishResponse(Result<()>), } @@ -103,29 +106,28 @@ impl CommonComponent for ChangePasswordForm { username: self.common.username.clone(), login_start_request: login_start_request.message, }; - self.common.call_backend( - HostService::login_start, - req, - Msg::AuthenticationStartResponse, - )?; + self.common + .call_backend(HostService::login_start, req, |r| { + Msg::AuthenticationStartResponse(old_password, r) + })?; Ok(true) } } - Msg::AuthenticationStartResponse(res) => { + Msg::AuthenticationStartResponse(old_password, res) => { let res = res.context("Could not initiate login")?; match self.opaque_data.take() { OpaqueData::Login(l) => { - opaque::client::login::finish_login(l, res.credential_response).map_err( - |e| { - // Common error, we want to print a full error to the console but only a - // simple one to the user. - ConsoleService::error(&format!( - "Invalid username or password: {}", - e - )); - anyhow!("Invalid username or password") - }, - )?; + opaque::client::login::finish_login( + &old_password, + l, + res.credential_response, + ) + .map_err(|e| { + // Common error, we want to print a full error to the console but only a + // simple one to the user. + ConsoleService::error(&format!("Invalid username or password: {}", e)); + anyhow!("Invalid username or password") + })?; } _ => panic!("Unexpected data in opaque_data field"), }; @@ -142,20 +144,20 @@ impl CommonComponent for ChangePasswordForm { registration_start_request: registration_start_request.message, }; self.opaque_data = OpaqueData::Registration(registration_start_request.state); - self.common.call_backend( - HostService::register_start, - req, - Msg::RegistrationStartResponse, - )?; + self.common + .call_backend(HostService::register_start, req, |r| { + Msg::RegistrationStartResponse(new_password, r) + })?; Ok(true) } - Msg::RegistrationStartResponse(res) => { + Msg::RegistrationStartResponse(new_password, res) => { let res = res.context("Could not initiate password change")?; match self.opaque_data.take() { OpaqueData::Registration(registration) => { let mut rng = rand::rngs::OsRng; let registration_finish = opaque::client::registration::finish_registration( + &new_password, registration, res.registration_response, &mut rng, diff --git a/app/src/components/create_user.rs b/app/src/components/create_user.rs index b167bef..b7d7607 100644 --- a/app/src/components/create_user.rs +++ b/app/src/components/create_user.rs @@ -65,10 +65,9 @@ pub enum Msg { CreateUserResponse(Result), SuccessfulCreation, RegistrationStartResponse( - ( - opaque::client::registration::ClientRegistration, - Result>, - ), + String, + opaque::client::registration::ClientRegistration, + Result>, ), RegistrationFinishResponse(Result<()>), } @@ -124,7 +123,7 @@ impl CommonComponent for CreateUserForm { }; self.common .call_backend(HostService::register_start, req, move |r| { - Msg::RegistrationStartResponse((state, r)) + Msg::RegistrationStartResponse(password, state, r) }) .context("Error trying to create user")?; } else { @@ -132,10 +131,11 @@ impl CommonComponent for CreateUserForm { } Ok(false) } - Msg::RegistrationStartResponse((registration_start, response)) => { + Msg::RegistrationStartResponse(password, registration_start, response) => { let response = response?; let mut rng = rand::rngs::OsRng; let registration_upload = opaque::client::registration::finish_registration( + &password, registration_start, response.registration_response, &mut rng, diff --git a/app/src/components/login.rs b/app/src/components/login.rs index 0503e95..d403ca3 100644 --- a/app/src/components/login.rs +++ b/app/src/components/login.rs @@ -32,15 +32,15 @@ pub struct Props { pub on_logged_in: Callback<(String, bool)>, } +#[allow(clippy::large_enum_variant)] pub enum Msg { Update, Submit, AuthenticationRefreshResponse(Result<(String, bool)>), AuthenticationStartResponse( - ( - opaque::client::login::ClientLogin, - Result>, - ), + opaque::client::login::ClientLogin, + String, + Result>, ), AuthenticationFinishResponse(Result<(String, bool)>), } @@ -64,25 +64,27 @@ impl CommonComponent for LoginForm { }; self.common .call_backend(HostService::login_start, req, move |r| { - Msg::AuthenticationStartResponse((state, r)) + Msg::AuthenticationStartResponse(state, password, r) })?; Ok(true) } - Msg::AuthenticationStartResponse((login_start, res)) => { + Msg::AuthenticationStartResponse(login_start, password, res) => { let res = res.context("Could not log in (invalid response to login start)")?; - let login_finish = - match opaque::client::login::finish_login(login_start, res.credential_response) - { - Err(e) => { - // Common error, we want to print a full error to the console but only a - // simple one to the user. - ConsoleService::error(&format!("Invalid username or password: {}", e)); - self.common.error = Some(anyhow!("Invalid username or password")); - self.common.cancel_task(); - return Ok(true); - } - Ok(l) => l, - }; + let login_finish = match opaque::client::login::finish_login( + &password, + login_start, + res.credential_response, + ) { + Err(e) => { + // Common error, we want to print a full error to the console but only a + // simple one to the user. + ConsoleService::error(&format!("Invalid username or password: {}", e)); + self.common.error = Some(anyhow!("Invalid username or password")); + self.common.cancel_task(); + return Ok(true); + } + Ok(l) => l, + }; let req = login::ClientLoginFinishRequest { server_data: res.server_data, credential_finalization: login_finish.message, diff --git a/app/src/components/reset_password_step2.rs b/app/src/components/reset_password_step2.rs index a14b36b..6def690 100644 --- a/app/src/components/reset_password_step2.rs +++ b/app/src/components/reset_password_step2.rs @@ -45,7 +45,10 @@ pub enum Msg { ValidateTokenResponse(Result), FormUpdate, Submit, - RegistrationStartResponse(Result>), + RegistrationStartResponse( + String, + Result>, + ), RegistrationFinishResponse(Result<()>), } @@ -72,18 +75,18 @@ impl CommonComponent for ResetPasswordStep2Form { registration_start_request: registration_start_request.message, }; self.opaque_data = Some(registration_start_request.state); - self.common.call_backend( - HostService::register_start, - req, - Msg::RegistrationStartResponse, - )?; + self.common + .call_backend(HostService::register_start, req, |r| { + Msg::RegistrationStartResponse(new_password, r) + })?; Ok(true) } - Msg::RegistrationStartResponse(res) => { + Msg::RegistrationStartResponse(new_password, res) => { let res = res.context("Could not initiate password change")?; let registration = self.opaque_data.take().expect("Missing registration data"); let mut rng = rand::rngs::OsRng; let registration_finish = opaque_registration::finish_registration( + &new_password, registration, res.registration_response, &mut rng, diff --git a/auth/Cargo.toml b/auth/Cargo.toml index 8940df6..a4d5d39 100644 --- a/auth/Cargo.toml +++ b/auth/Cargo.toml @@ -11,7 +11,7 @@ opaque_client = [] js = [] [dependencies] -rust-argon2 = "0.8" +argon2 = "*" curve25519-dalek = "3" digest = "0.9" generic-array = "*" @@ -21,7 +21,8 @@ sha2 = "0.9" thiserror = "*" [dependencies.opaque-ke] -version = "0.6" +version = "2" +features = ["std"] [dependencies.chrono] version = "*" diff --git a/auth/src/opaque.rs b/auth/src/opaque.rs index 571c3b8..11f6f07 100644 --- a/auth/src/opaque.rs +++ b/auth/src/opaque.rs @@ -10,40 +10,46 @@ pub enum AuthenticationError { pub type AuthenticationResult = std::result::Result; pub use opaque_ke::keypair::{PrivateKey, PublicKey}; -pub type KeyPair = opaque_ke::keypair::KeyPair<::Group>; +pub type KeyPair = opaque_ke::keypair::KeyPair<::KeGroup>; /// A wrapper around argon2 to provide the [`opaque_ke::slow_hash::SlowHash`] trait. -pub struct ArgonHasher; +pub struct ArgonHasher { + hasher: argon2::Argon2<'static>, +} /// The Argon hasher used for bruteforce protection. /// /// Note that it isn't used to "hash the passwords", so it doesn't need a variable salt. Instead, /// it's used as part of the OPAQUE protocol to add a slow hashing method, making bruteforce /// attacks prohibitively more expensive. -impl ArgonHasher { - /// Fixed salt, doesn't affect the security. It is only used to make attacks more - /// computationally intensive, it doesn't serve any security purpose. - const SALT: &'static [u8] = b"lldap_opaque_salt"; - /// Config for the argon hasher. Security enthusiasts may want to tweak this for their system. - const CONFIG: &'static argon2::Config<'static> = &argon2::Config { - ad: &[], - hash_length: 128, - lanes: 1, - mem_cost: 50 * 1024, // 50 MB, in KB - secret: &[], - thread_mode: argon2::ThreadMode::Sequential, - time_cost: 1, - variant: argon2::Variant::Argon2id, - version: argon2::Version::Version13, - }; +impl Default for ArgonHasher { + fn default() -> Self { + ArgonHasher { + hasher: argon2::Argon2::new( + argon2::Algorithm::Argon2id, + argon2::Version::default(), + argon2::Params::new( + 50 * 1024, // 50 MB, in KB + 1, + 1, + Some(64), + ) + .unwrap(), + ), + } + } } -impl opaque_ke::slow_hash::SlowHash for ArgonHasher { - fn hash( - input: generic_array::GenericArray::OutputSize>, - ) -> Result, opaque_ke::errors::InternalPakeError> { - argon2::hash_raw(&input, Self::SALT, Self::CONFIG) - .map_err(|_| opaque_ke::errors::InternalPakeError::HashingFailure) +impl opaque_ke::ksf::Ksf for ArgonHasher { + fn hash>( + &self, + input: generic_array::GenericArray, + ) -> Result, opaque_ke::errors::InternalError> { + let mut output = generic_array::GenericArray::::default(); + self.hasher + .hash_password_into(&input, &[0; 16], &mut output) + .map_err(|_| opaque_ke::errors::InternalError::KsfError)?; + Ok(output) } } @@ -52,11 +58,11 @@ impl opaque_ke::slow_hash::SlowHash for ArgonHasher #[allow(dead_code)] pub struct DefaultSuite; impl CipherSuite for DefaultSuite { - type Group = curve25519_dalek::ristretto::RistrettoPoint; - type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; - type Hash = sha2::Sha512; - /// Use argon2 as the slow hashing algorithm for our CipherSuite. - type SlowHash = ArgonHasher; + type OprfCs = opaque_ke::Ristretto255; + type KeGroup = opaque_ke::Ristretto255; + type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; + + type Ksf = ArgonHasher; } /// Client-side code for OPAQUE protocol handling, to register a new user and login. All methods' @@ -85,12 +91,14 @@ pub mod client { /// Finalize the registration negotiation. pub fn finish_registration( + password: &str, registration_start: ClientRegistration, registration_response: RegistrationResponse, rng: &mut R, ) -> AuthenticationResult { Ok(registration_start.finish( rng, + password.as_bytes(), registration_response, ClientRegistrationFinishParameters::default(), )?) @@ -117,10 +125,15 @@ pub mod client { /// Finalize the client login negotiation. pub fn finish_login( + password: &str, login_start: ClientLogin, login_response: CredentialResponse, ) -> AuthenticationResult { - Ok(login_start.finish(login_response, ClientLoginFinishParameters::default())?) + Ok(login_start.finish( + password.as_bytes(), + login_response, + ClientLoginFinishParameters::default(), + )?) } } } diff --git a/migration-tool/src/lldap.rs b/migration-tool/src/lldap.rs index 7f61c57..697232c 100644 --- a/migration-tool/src/lldap.rs +++ b/migration-tool/src/lldap.rs @@ -151,7 +151,7 @@ fn try_login( ); } let login_start_response = response.json::()?; - let login_finish = finish_login(state, login_start_response.credential_response)?; + let login_finish = finish_login(password, state, login_start_response.credential_response)?; let req = ClientLoginFinishRequest { server_data: login_start_response.server_data, credential_finalization: login_finish.message, diff --git a/server/Cargo.toml b/server/Cargo.toml index 44a6a32..6c54ccb 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -94,7 +94,8 @@ version = "0.1" features = ["with-chrono", "sqlx-sqlite", "sqlx-any"] [dependencies.opaque-ke] -version = "0.6" +version = "2" +features = ["std"] [dependencies.rand] features = ["small_rng", "getrandom"] diff --git a/server/src/domain/handler.rs b/server/src/domain/handler.rs index 8b1a622..11da226 100644 --- a/server/src/domain/handler.rs +++ b/server/src/domain/handler.rs @@ -261,6 +261,7 @@ pub struct UpdateGroupRequest { #[async_trait] pub trait LoginHandler: Clone + Send { async fn bind(&self, request: BindRequest) -> Result<()>; + async fn set_password(&self, request: BindRequest) -> Result<()>; } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] @@ -339,6 +340,7 @@ mockall::mock! { #[async_trait] impl LoginHandler for TestBackendHandler { async fn bind(&self, request: BindRequest) -> Result<()>; + async fn set_password(&self, request: BindRequest) -> Result<()>; } } diff --git a/server/src/domain/sql_backend_handler.rs b/server/src/domain/sql_backend_handler.rs index 0b8478d..dd2cf18 100644 --- a/server/src/domain/sql_backend_handler.rs +++ b/server/src/domain/sql_backend_handler.rs @@ -34,7 +34,7 @@ pub mod tests { pub async fn get_initialized_db() -> Pool { let sql_pool = get_in_memory_db().await; - init_table(&sql_pool).await.unwrap(); + init_table(&sql_pool, &get_default_config()).await.unwrap(); sql_pool } @@ -52,6 +52,7 @@ pub mod tests { .await .unwrap(); let registration_upload = opaque::client::registration::finish_registration( + pass, client_registration_start.state, response.registration_response, &mut rng, diff --git a/server/src/domain/sql_migrations.rs b/server/src/domain/sql_migrations.rs index 85d2b82..41ea634 100644 --- a/server/src/domain/sql_migrations.rs +++ b/server/src/domain/sql_migrations.rs @@ -1,5 +1,8 @@ +use crate::infra::configuration::Configuration; + use super::{ - handler::{GroupId, UserId, Uuid}, + handler::{GroupId, LoginHandler, UserId, Uuid}, + sql_backend_handler::SqlBackendHandler, sql_tables::{ DbQueryBuilder, DbRow, Groups, Memberships, Metadata, Pool, SchemaVersion, Users, }, @@ -7,7 +10,7 @@ use super::{ use sea_query::*; use sea_query_binder::SqlxBinder; use sqlx::Row; -use tracing::{debug, warn}; +use tracing::{debug, info, warn}; pub async fn create_group(group_name: &str, pool: &Pool) -> sqlx::Result<()> { let now = chrono::Utc::now(); @@ -45,6 +48,7 @@ pub async fn get_schema_version(pool: &Pool) -> Option { } pub async fn upgrade_to_v1(pool: &Pool) -> sqlx::Result<()> { + info!("Upgrading DB schema from version 0->1"); // SQLite needs this pragma to be turned on. Other DB might not understand this, so ignore the // error. let _ = sqlx::query("PRAGMA foreign_keys = ON").execute(pool).await; @@ -296,8 +300,39 @@ pub async fn upgrade_to_v1(pool: &Pool) -> sqlx::Result<()> { Ok(()) } -pub async fn migrate_from_version(_pool: &Pool, version: SchemaVersion) -> anyhow::Result<()> { - if version.0 > 1 { +pub async fn migrate_from_version( + pool: &Pool, + version: SchemaVersion, + config: &Configuration, +) -> anyhow::Result<()> { + if version.0 < 2 { + info!("Upgrading DB schema from version 1->2"); + sqlx::query( + &Table::alter() + .table(Users::Table) + .add_column(ColumnDef::new(Users::PasswordHashV2).binary()) + .to_string(DbQueryBuilder {}), + ) + .execute(pool) + .await?; + let backend_handler = SqlBackendHandler::new(config.clone(), pool.clone()); + backend_handler + .set_password(super::handler::BindRequest { + name: config.ldap_user_dn.clone(), + password: config.ldap_user_pass.unsecure().to_owned(), + }) + .await?; + } + sqlx::query( + &Query::update() + .table(Metadata::Table) + .values(vec![(Metadata::Version, SchemaVersion(2).into())]) + .to_string(DbQueryBuilder {}), + ) + .execute(pool) + .await?; + info!("Successfully upgraded DB to schema version 2"); + if version.0 > 2 { anyhow::bail!("DB version downgrading is not supported"); } Ok(()) diff --git a/server/src/domain/sql_opaque_handler.rs b/server/src/domain/sql_opaque_handler.rs index 3df4aed..cfc9c88 100644 --- a/server/src/domain/sql_opaque_handler.rs +++ b/server/src/domain/sql_opaque_handler.rs @@ -36,6 +36,7 @@ fn passwords_match( username.as_str(), )?; client::login::finish_login( + clear_password, client_login_start_result.state, server_login_start_result.message, )?; @@ -44,8 +45,13 @@ fn passwords_match( impl SqlBackendHandler { fn get_orion_secret_key(&self) -> Result { + use opaque_ke::keypair::SecretKey; Ok(orion::aead::SecretKey::from_slice( - self.config.get_server_keys().private(), + self.config + .get_server_keys() + .private() + .serialize() + .as_slice(), )?) } @@ -57,7 +63,7 @@ impl SqlBackendHandler { // Fetch the previously registered password file from the DB. let password_file_bytes = { let (query, values) = Query::select() - .column(Users::PasswordHash) + .column(Users::PasswordHashV2) .from(Users::Table) .cond_where(Expr::col(Users::UserId).eq(username)) .build_sqlx(DbQueryBuilder {}); @@ -66,7 +72,7 @@ impl SqlBackendHandler { .await? { if let Some(bytes) = - row.get::>, _>(&*Users::PasswordHash.to_string()) + row.get::>, _>(&*Users::PasswordHashV2.to_string()) { bytes } else { @@ -91,7 +97,7 @@ impl LoginHandler for SqlBackendHandler { #[instrument(skip_all, level = "debug", err)] async fn bind(&self, request: BindRequest) -> Result<()> { let (query, values) = Query::select() - .column(Users::PasswordHash) + .column(Users::PasswordHashV2) .from(Users::Table) .cond_where(Expr::col(Users::UserId).eq(&request.name)) .build_sqlx(DbQueryBuilder {}); @@ -100,7 +106,7 @@ impl LoginHandler for SqlBackendHandler { .await { if let Some(password_hash) = - row.get::>, _>(&*Users::PasswordHash.to_string()) + row.get::>, _>(&*Users::PasswordHashV2.to_string()) { if let Err(e) = passwords_match( &password_hash, @@ -123,6 +129,31 @@ impl LoginHandler for SqlBackendHandler { request.name ))) } + + #[instrument(skip_all, level = "debug", err)] + async fn set_password(&self, request: BindRequest) -> Result<()> { + use lldap_auth::*; + let mut rng = rand::rngs::OsRng; + let registration_start_request = + opaque::client::registration::start_registration(&request.password, &mut rng)?; + let req = registration::ClientRegistrationStartRequest { + username: request.name.to_string(), + registration_start_request: registration_start_request.message, + }; + let registration_start_response = self.registration_start(req).await?; + let registration_finish = opaque::client::registration::finish_registration( + &request.password, + registration_start_request.state, + registration_start_response.registration_response, + &mut rng, + )?; + let req = registration::ClientRegistrationFinishRequest { + server_data: registration_start_response.server_data, + registration_upload: registration_finish.message, + }; + self.registration_finish(req).await?; + Ok(()) + } } #[async_trait] @@ -214,7 +245,10 @@ impl OpaqueHandler for SqlOpaqueHandler { // Set the user password to the new password. let (update_query, values) = Query::update() .table(Users::Table) - .value(Users::PasswordHash, password_file.serialize().into()) + .value( + Users::PasswordHashV2, + password_file.serialize().as_slice().into(), + ) .cond_where(Expr::col(Users::UserId).eq(username)) .build_sqlx(DbQueryBuilder {}); sqlx::query_with(update_query.as_str(), values) @@ -243,6 +277,7 @@ pub(crate) async fn register_password( }) .await?; let registration_finish = opaque::client::registration::finish_registration( + password.unsecure(), registration_start.state, start_response.registration_response, &mut rng, @@ -275,6 +310,7 @@ mod tests { }) .await?; let login_finish = opaque::client::login::finish_login( + password, login_start.state, start_response.credential_response, )?; diff --git a/server/src/domain/sql_tables.rs b/server/src/domain/sql_tables.rs index b409a1b..1413302 100644 --- a/server/src/domain/sql_tables.rs +++ b/server/src/domain/sql_tables.rs @@ -1,3 +1,5 @@ +use crate::infra::configuration::Configuration; + use super::{ handler::{GroupId, UserId, Uuid}, sql_migrations::{get_schema_version, migrate_from_version, upgrade_to_v1}, @@ -62,7 +64,9 @@ pub enum Users { LastName, Avatar, CreationDate, + // Deprecated PasswordHash, + PasswordHashV2, TotpSecret, MfaType, Uuid, @@ -96,7 +100,7 @@ pub enum Metadata { Version, } -pub async fn init_table(pool: &Pool) -> anyhow::Result<()> { +pub async fn init_table(pool: &Pool, config: &Configuration) -> anyhow::Result<()> { let version = { if let Some(version) = get_schema_version(pool).await { version @@ -105,20 +109,27 @@ pub async fn init_table(pool: &Pool) -> anyhow::Result<()> { SchemaVersion(1) } }; - migrate_from_version(pool, version).await?; + migrate_from_version(pool, version, config).await?; Ok(()) } #[cfg(test)] mod tests { use super::*; + use crate::infra::configuration::ConfigurationBuilder; use chrono::prelude::*; use sqlx::{Column, Row}; + async fn init_tables_for_test(pool: &Pool) { + init_table(&pool, &ConfigurationBuilder::for_tests()) + .await + .unwrap(); + } + #[tokio::test] async fn test_init_table() { let sql_pool = PoolOptions::new().connect("sqlite::memory:").await.unwrap(); - init_table(&sql_pool).await.unwrap(); + init_tables_for_test(&sql_pool).await; sqlx::query(r#"INSERT INTO users (user_id, email, display_name, first_name, last_name, creation_date, password_hash, uuid) VALUES ("bôb", "böb@bob.bob", "Bob Bobbersön", "Bob", "Bobberson", "1970-01-01 00:00:00", "bob00", "abc")"#).execute(&sql_pool).await.unwrap(); @@ -138,8 +149,8 @@ mod tests { #[tokio::test] async fn test_already_init_table() { let sql_pool = PoolOptions::new().connect("sqlite::memory:").await.unwrap(); - init_table(&sql_pool).await.unwrap(); - init_table(&sql_pool).await.unwrap(); + init_tables_for_test(&sql_pool).await; + init_tables_for_test(&sql_pool).await; } #[tokio::test] @@ -168,7 +179,7 @@ mod tests { .execute(&sql_pool) .await .unwrap(); - init_table(&sql_pool).await.unwrap(); + init_tables_for_test(&sql_pool).await; sqlx::query( r#"INSERT INTO groups (display_name, creation_date, uuid) VALUES ("test", "1970-01-01 00:00:00", "abc")"#, @@ -210,7 +221,7 @@ mod tests { .fetch_one(&sql_pool) .await .unwrap(), - SchemaVersion(1) + SchemaVersion(2) ); } @@ -228,6 +239,8 @@ mod tests { .execute(&sql_pool) .await .unwrap(); - assert!(init_table(&sql_pool).await.is_err()); + assert!(init_table(&sql_pool, &ConfigurationBuilder::for_tests()) + .await + .is_err()); } } diff --git a/server/src/infra/ldap_handler.rs b/server/src/infra/ldap_handler.rs index 382e5f1..95f0363 100644 --- a/server/src/infra/ldap_handler.rs +++ b/server/src/infra/ldap_handler.rs @@ -17,7 +17,6 @@ use crate::{ }, infra::auth_service::{Permission, ValidationResults}, }; -use anyhow::Result; use ldap3_proto::proto::{ LdapAddRequest, LdapBindCred, LdapBindRequest, LdapBindResponse, LdapExtendedRequest, LdapExtendedResponse, LdapFilter, LdapOp, LdapPartialAttribute, LdapPasswordModifyRequest, @@ -238,29 +237,6 @@ impl LdapHandler Result<()> { - use lldap_auth::*; - let mut rng = rand::rngs::OsRng; - let registration_start_request = - opaque::client::registration::start_registration(password, &mut rng)?; - let req = registration::ClientRegistrationStartRequest { - username: user.to_string(), - registration_start_request: registration_start_request.message, - }; - let registration_start_response = self.backend_handler.registration_start(req).await?; - let registration_finish = opaque::client::registration::finish_registration( - registration_start_request.state, - registration_start_response.registration_response, - &mut rng, - )?; - let req = registration::ClientRegistrationFinishRequest { - server_data: registration_start_response.server_data, - registration_upload: registration_finish.message, - }; - self.backend_handler.registration_finish(req).await?; - Ok(()) - } - async fn do_password_modification( &mut self, request: &LdapPasswordModifyRequest, @@ -298,7 +274,14 @@ impl LdapHandler Result<()>; + async fn set_password(&self, request: BindRequest) -> Result<()>; } #[async_trait] impl GroupBackendHandler for TestBackendHandler { @@ -1815,28 +1799,12 @@ mod tests { mock.expect_get_user_groups() .with(eq(UserId::new("bob"))) .returning(|_| Ok(HashSet::new())); - use lldap_auth::*; - let mut rng = rand::rngs::OsRng; - let registration_start_request = - opaque::client::registration::start_registration("password", &mut rng).unwrap(); - let request = registration::ClientRegistrationStartRequest { - username: "bob".to_string(), - registration_start_request: registration_start_request.message, - }; - let start_response = opaque::server::registration::start_registration( - &opaque::server::ServerSetup::new(&mut rng), - request.registration_start_request, - &request.username, - ) - .unwrap(); - mock.expect_registration_start().times(1).return_once(|_| { - Ok(registration::ServerRegistrationStartResponse { - server_data: "".to_string(), - registration_response: start_response.message, - }) - }); - mock.expect_registration_finish() + mock.expect_set_password() .times(1) + .with(eq(BindRequest { + name: UserId::new("bob"), + password: "password".to_owned(), + })) .return_once(|_| Ok(())); let mut ldap_handler = setup_bound_admin_handler(mock).await; let request = LdapOp::ExtendedRequest( @@ -1862,28 +1830,12 @@ mod tests { mock.expect_get_user_groups() .with(eq(UserId::new("bob"))) .returning(|_| Ok(HashSet::new())); - use lldap_auth::*; - let mut rng = rand::rngs::OsRng; - let registration_start_request = - opaque::client::registration::start_registration("password", &mut rng).unwrap(); - let request = registration::ClientRegistrationStartRequest { - username: "bob".to_string(), - registration_start_request: registration_start_request.message, - }; - let start_response = opaque::server::registration::start_registration( - &opaque::server::ServerSetup::new(&mut rng), - request.registration_start_request, - &request.username, - ) - .unwrap(); - mock.expect_registration_start().times(1).return_once(|_| { - Ok(registration::ServerRegistrationStartResponse { - server_data: "".to_string(), - registration_response: start_response.message, - }) - }); - mock.expect_registration_finish() + mock.expect_set_password() .times(1) + .with(eq(BindRequest { + name: UserId::new("bob"), + password: "password".to_owned(), + })) .return_once(|_| Ok(())); let mut ldap_handler = setup_bound_password_manager_handler(mock).await; let request = LdapOp::ExtendedRequest( diff --git a/server/src/infra/tcp_backend_handler.rs b/server/src/infra/tcp_backend_handler.rs index 8bdf97d..a132cde 100644 --- a/server/src/infra/tcp_backend_handler.rs +++ b/server/src/infra/tcp_backend_handler.rs @@ -32,6 +32,7 @@ mockall::mock! { #[async_trait] impl LoginHandler for TestTcpBackendHandler { async fn bind(&self, request: BindRequest) -> Result<()>; + async fn set_password(&self, request: BindRequest) -> Result<()>; } #[async_trait] impl GroupBackendHandler for TestTcpBackendHandler { diff --git a/server/src/main.rs b/server/src/main.rs index 87735dc..a805f56 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -58,7 +58,7 @@ async fn set_up_server(config: Configuration) -> Result { .connect(&config.database_url) .await .context("while connecting to the DB")?; - domain::sql_tables::init_table(&sql_pool) + domain::sql_tables::init_table(&sql_pool, &config) .await .context("while creating the tables")?; let backend_handler = SqlBackendHandler::new(config.clone(), sql_pool.clone());