From dae92b9018f2870592592feb4a1e951955a44a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Thu, 12 Jul 2018 21:46:50 +0200 Subject: [PATCH] Implemented U2F, refactored Two Factor authentication, registering U2F device and authentication should work. Works on Chrome on MacOS with a virtual device. --- Cargo.lock | 263 +++++++------ Cargo.toml | 17 +- .../down.sql | 8 + .../up.sql | 15 + src/api/core/mod.rs | 6 +- src/api/core/two_factor.rs | 344 +++++++++++++++--- src/api/identity.rs | 225 ++++++++---- src/api/mod.rs | 7 +- src/api/web.rs | 18 +- src/auth.rs | 6 +- src/db/models/device.rs | 7 +- src/db/models/mod.rs | 2 + src/db/models/two_factor.rs | 112 ++++++ src/db/models/user.rs | 32 +- src/db/schema.rs | 13 + src/main.rs | 6 + src/util.rs | 7 +- 17 files changed, 816 insertions(+), 272 deletions(-) create mode 100644 migrations/2018-07-11-181453_create_u2f_twofactor/down.sql create mode 100644 migrations/2018-07-11-181453_create_u2f_twofactor/up.sql create mode 100644 src/db/models/two_factor.rs diff --git a/Cargo.lock b/Cargo.lock index 86bda9b3..79cbd51d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,14 @@ name = "ascii" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "base64" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "base64" version = "0.6.0" @@ -70,15 +78,18 @@ dependencies = [ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libsqlite3-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "multipart 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "oath 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rocket_codegen 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rocket_contrib 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "u2f 0.1.2 (git+https://github.com/wisespace-io/u2f-rs?rev=193de35093a44)", "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -117,7 +128,7 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -162,7 +173,7 @@ dependencies = [ "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -325,7 +336,7 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -450,7 +461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -458,7 +469,7 @@ name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -511,7 +522,7 @@ dependencies = [ "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -521,13 +532,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -555,7 +566,7 @@ name = "hyper-tls" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", @@ -566,7 +577,7 @@ dependencies = [ [[package]] name = "idna" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -595,7 +606,7 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -610,7 +621,7 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -621,8 +632,8 @@ dependencies = [ "chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -663,7 +674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libflate" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -676,7 +687,7 @@ name = "libsqlite3-sys" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -686,12 +697,12 @@ name = "log" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "log" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -751,7 +762,7 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -773,7 +784,7 @@ name = "mime_guess" version = "2.0.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -781,7 +792,7 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -790,7 +801,7 @@ dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -822,8 +833,8 @@ dependencies = [ "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", "nickel 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -883,7 +894,7 @@ dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -901,6 +912,17 @@ dependencies = [ "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.39" @@ -960,7 +982,7 @@ name = "openssl-sys" version = "0.9.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -981,7 +1003,7 @@ name = "pear_codegen" version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1086,7 +1108,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1180,7 +1202,7 @@ name = "relay" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1198,20 +1220,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libflate 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1248,8 +1270,8 @@ dependencies = [ "state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1260,7 +1282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1271,7 +1293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1352,17 +1374,17 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.68" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.68" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1370,9 +1392,9 @@ name = "serde_json" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1380,10 +1402,10 @@ name = "serde_urlencoded" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1462,7 +1484,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1538,14 +1560,14 @@ name = "tokio" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1556,7 +1578,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1566,10 +1588,10 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1583,17 +1605,17 @@ name = "tokio-executor" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-fs" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1602,8 +1624,8 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1611,7 +1633,7 @@ name = "tokio-proto" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1628,9 +1650,9 @@ name = "tokio-reactor" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1641,7 +1663,7 @@ name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1650,21 +1672,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-threadpool" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1675,7 +1697,7 @@ name = "tokio-timer" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1684,7 +1706,7 @@ name = "tokio-tls" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1696,9 +1718,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1709,7 +1731,7 @@ name = "toml" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1748,6 +1770,24 @@ name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "u2f" +version = "0.1.2" +source = "git+https://github.com/wisespace-io/u2f-rs?rev=193de35093a44#193de35093a44576edba6cc94d9b54f2a1cbdcd1" +dependencies = [ + "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ucd-util" version = "0.1.1" @@ -1758,7 +1798,7 @@ name = "unicase" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1766,7 +1806,7 @@ name = "unicase" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1825,10 +1865,10 @@ dependencies = [ [[package]] name = "url" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1863,7 +1903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "version_check" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1876,8 +1916,8 @@ name = "want" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1891,6 +1931,15 @@ dependencies = [ "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "webpki" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "webpki-roots" version = "0.11.0" @@ -1949,6 +1998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50" +"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" @@ -1958,7 +2008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum byte-tools 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0919189ba800c7ffe8778278116b7e0de3905ab81c72abb69c85cbfef7991279" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" "checksum bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd32989a66957d3f0cba6588f15d4281a733f4e9ffc43fcd2385f57d3bf99ff" -"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" +"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" "checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" "checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37" @@ -1982,7 +2032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum digest 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a68d759d7a66a4f63d5bd2a2b14ad7e8cf93fe8c9be227031cd4e72ab0e9ee8" "checksum digest-buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4eb92364e9f6d3da159257250532d448b218406d2acb149f724e8f48e9f5cb9a" "checksum dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d0a1279c96732bc6800ce6337b6a614697b0e74ae058dc03c62ebeb78b4d86" -"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" "checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" "checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" @@ -1999,7 +2049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" +"checksum futures 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "80599c995ed197a276e27c27f94a6346446538adde3b87c1ab384f6f8cabfed4" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum generic-array 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe043cf9b85297937897087de81f590361686e1ac2d4d471b45435de5dfb6a6" @@ -2010,21 +2060,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" "checksum hyper-sync-rustls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6df6f419a9f116cc93b5f39a5ded1161e088a2c8424c8fcd1d4049193424a4" "checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca" -"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2" "checksum isatty 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6c324313540cd4d7ba008d43dc6606a32a5579f13cc17b2804c13096f0a5c522" -"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" "checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" -"checksum libflate 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f70a41040e4ed915b462ffb9c8dd20ece3700565aa5e2e163288a0f7ca00487a" +"checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98" "checksum libsqlite3-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9eb7b8e152b6a01be6a4a2917248381875758250dc3df5d46caf9250341dda" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -"checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac" +"checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" @@ -2032,10 +2082,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum migrations_internals 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8cf7c8c4f83fa9f47440c0b4af99973502de55e6e7b875f693bd263e03f93e7e" "checksum migrations_macros 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79f12499ef7353bdeca2d081bc61edd8351dac09a33af845952009b5a3d68c1a" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -"checksum mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b28683d0b09bbc20be1c9b3f6f24854efb1356ffcffee08ea3f6e65596e85fa" +"checksum mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fe51c8699d2dc522bf8c1ebe26ea2193d151fb54bcdfd7d0318750c189994cd9" "checksum mime_guess 1.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7287ba93031813826d8974566e54eb5e49d4473752f7df21c610dab289aee8cb" "checksum mime_guess 2.0.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d1a78b5e2283080d5a8ba68216171b4fe34f6ccdd909bb29be16ce8a9a831341" -"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" +"checksum mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4fcfcb32d63961fb6f367bfd5d21e4600b92cd310f71f9dca25acae196eb1560" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" "checksum multipart 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1720cbd59d1cbcc184b66f2f74a3287caf524764eee5a8fbb3f5f0e469cd5c00" @@ -2045,6 +2095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum nickel 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22b40e35b9f46a076dcbd8193125cea0e4130b1c015f68655038010f3e826e04" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" +"checksum num-derive 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d2c31b75c36a993d30c7a13d70513cb93f02acafdd5b7ba250f9b0e18615de7" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" @@ -2095,8 +2146,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" "checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" -"checksum serde 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)" = "429fcc4efa8a11341b5422c2ace724daba276c1748467e869478f53c0ba4562e" -"checksum serde_derive 1.0.68 (registry+https://github.com/rust-lang/crates.io-index)" = "6a25ad0bf818ed2d180c89addbe29198d1de6c89ed08a48aa6a4d3d16a63cbfe" +"checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920" +"checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124" "checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e" "checksum serde_urlencoded 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e703cef904312097cfceab9ce131ff6bbe09e8c964a0703345a5f49238757bc1" "checksum sha-1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8347606816471548cd60f0abd5ef0d513a81f5202dbdab9c09f17a15b5248484" @@ -2109,7 +2160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" -"checksum syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c67da57e61ebc7b7b6fff56bb34440ca3a83db037320b0507af4c10368deda7d" +"checksum syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2beff8ebc3658f07512a413866875adddd20f4fd47b2a4e6c9da65cd281baaea" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" @@ -2121,13 +2172,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "881e9645b81c2ce95fcb799ded2c29ffb9f25ef5bef909089a420e5961dd8ccb" "checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" "checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" -"checksum tokio-fs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc42bae2f6e33865b99069d95bcddfc85c9f0849b4e7e7399eeee71956ef34d7" +"checksum tokio-fs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "40697ecbea5660df15b15d50a077386477d2f6a35002adf01ce76ff9dd9dce48" "checksum tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a5c9635ee806f26d302b8baa1e145689a280d8f5aa8d0552e7344808da54cc21" "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" "checksum tokio-reactor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e00ec63bbec2c97ce1178cb0587b2c438b2f6b09d3ee54a33c45a9cf0d530810" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" "checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" -"checksum tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c3873a6d8d0b636e024e77b9a82eaab6739578a06189ecd0e731c7308fbc5d" +"checksum tokio-threadpool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "24ab84f574027b0e875378f31575cf175360891919e93a3490f07e76e00e4efb" "checksum tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "028b94314065b90f026a21826cffd62a4e40a92cda3e5c069cc7b02e5945f5e9" "checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913" "checksum tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43eb534af6e8f37d43ab1b612660df14755c42bd003c5f8d2475ee78cc4600c0" @@ -2138,6 +2189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum u2f 0.1.2 (git+https://github.com/wisespace-io/u2f-rs?rev=193de35093a44)" = "" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" @@ -2149,15 +2201,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068" -"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" +"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f" "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" "checksum vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe533e138811704c0e3cbde65a818b35d3240409b4346256c5ede403e082474" -"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" +"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" "checksum webpki 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e499345fc4c6b7c79a5b8756d4592c4305510a13512e79efafe00dfbd67bbac6" +"checksum webpki 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "493012e46177f3f4ee9cd58fd0c81ecb77e6d6cc6ebce55989b9c33376fbe5ee" "checksum webpki-roots 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bfb3f50499f21ad2317f442845e3b5805b007f1e728f59885c99e61b8c181a7" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" diff --git a/Cargo.toml b/Cargo.toml index 67199f02..349935e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,8 @@ reqwest = "0.8.6" multipart = "0.14.2" # A generic serialization/deserialization framework -serde = "1.0.68" -serde_derive = "1.0.68" +serde = "1.0.70" +serde_derive = "1.0.70" serde_json = "1.0.22" # A safe, extensible ORM and Query builder @@ -45,11 +45,22 @@ data-encoding = "2.1.1" # JWT library jsonwebtoken = "= 4.0.1" +# U2F library +u2f = "0.1.2" + # A `dotenv` implementation for Rust dotenv = { version = "0.13.0", default-features = false } # Lazy static macro lazy_static = "1.0.1" +# Numerical libraries +num-traits = "0.2.5" +num-derive = "0.2.2" + [patch.crates-io] -jsonwebtoken = { path = "libs/jsonwebtoken" } # Make jwt use ring 0.11, to match rocket + # Make jwt use ring 0.11, to match rocket +jsonwebtoken = { path = "libs/jsonwebtoken" } + +# Version 0.1.2 from crates.io lacks a commit that fixes a certificate error +u2f = { git = 'https://github.com/wisespace-io/u2f-rs', rev = '193de35093a44' } \ No newline at end of file diff --git a/migrations/2018-07-11-181453_create_u2f_twofactor/down.sql b/migrations/2018-07-11-181453_create_u2f_twofactor/down.sql new file mode 100644 index 00000000..84b6ee63 --- /dev/null +++ b/migrations/2018-07-11-181453_create_u2f_twofactor/down.sql @@ -0,0 +1,8 @@ +UPDATE users +SET totp_secret = ( + SELECT twofactor.data FROM twofactor + WHERE twofactor.type = 0 + AND twofactor.user_uuid = users.uuid +); + +DROP TABLE twofactor; \ No newline at end of file diff --git a/migrations/2018-07-11-181453_create_u2f_twofactor/up.sql b/migrations/2018-07-11-181453_create_u2f_twofactor/up.sql new file mode 100644 index 00000000..3e78e721 --- /dev/null +++ b/migrations/2018-07-11-181453_create_u2f_twofactor/up.sql @@ -0,0 +1,15 @@ +CREATE TABLE twofactor ( + uuid TEXT NOT NULL PRIMARY KEY, + user_uuid TEXT NOT NULL REFERENCES users (uuid), + type INTEGER NOT NULL, + enabled BOOLEAN NOT NULL, + data TEXT NOT NULL, + + UNIQUE (user_uuid, type) +); + + +INSERT INTO twofactor (uuid, user_uuid, type, enabled, data) +SELECT lower(hex(randomblob(16))) , uuid, 0, 1, u.totp_secret FROM users u where u.totp_secret IS NOT NULL; + +UPDATE users SET totp_secret = NULL; -- Instead of recreating the table, just leave the columns empty \ No newline at end of file diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index 8609ed3d..89df7a1f 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -2,7 +2,7 @@ mod accounts; mod ciphers; mod folders; mod organizations; -mod two_factor; +pub(crate) mod two_factor; use self::accounts::*; use self::ciphers::*; @@ -58,9 +58,11 @@ pub fn routes() -> Vec { get_twofactor, get_recover, recover, + disable_twofactor, generate_authenticator, activate_authenticator, - disable_authenticator, + generate_u2f, + activate_u2f, get_organization, create_organization, diff --git a/src/api/core/two_factor.rs b/src/api/core/two_factor.rs index 3ecc48a0..083b8e0e 100644 --- a/src/api/core/two_factor.rs +++ b/src/api/core/two_factor.rs @@ -1,28 +1,24 @@ -use rocket_contrib::{Json, Value}; - use data_encoding::BASE32; +use rocket_contrib::{Json, Value}; +use serde_json; -use db::DbConn; +use db::{ + models::{TwoFactor, TwoFactorType, User}, + DbConn, +}; use crypto; -use api::{PasswordData, JsonResult, NumberOrString, JsonUpcase}; +use api::{ApiResult, JsonResult, JsonUpcase, NumberOrString, PasswordData}; use auth::Headers; #[get("/two-factor")] -fn get_twofactor(headers: Headers) -> JsonResult { - let data = if headers.user.totp_secret.is_none() { - Value::Null - } else { - json!([{ - "Enabled": true, - "Type": 0, - "Object": "twoFactorProvider" - }]) - }; +fn get_twofactor(headers: Headers, conn: DbConn) -> JsonResult { + let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &conn); + let twofactors_json: Vec = twofactors.iter().map(|c| c.to_json_list()).collect(); Ok(Json(json!({ - "Data": data, + "Data": twofactors_json, "Object": "list" }))) } @@ -58,7 +54,7 @@ fn recover(data: JsonUpcase, conn: DbConn) -> JsonResult { // Get the user let mut user = match User::find_by_mail(&data.Email, &conn) { Some(user) => user, - None => err!("Username or password is incorrect. Try again.") + None => err!("Username or password is incorrect. Try again."), }; // Check password @@ -71,24 +67,69 @@ fn recover(data: JsonUpcase, conn: DbConn) -> JsonResult { err!("Recovery code is incorrect. Try again.") } - user.totp_secret = None; + // Remove all twofactors from the user + for twofactor in TwoFactor::find_by_user(&user.uuid, &conn) { + twofactor.delete(&conn).expect("Error deleting twofactor"); + } + + // Remove the recovery code, not needed without twofactors user.totp_recover = None; user.save(&conn); Ok(Json(json!({}))) } +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct DisableTwoFactorData { + MasterPasswordHash: String, + Type: NumberOrString, +} + +#[post("/two-factor/disable", data = "")] +fn disable_twofactor( + data: JsonUpcase, + headers: Headers, + conn: DbConn, +) -> JsonResult { + let data: DisableTwoFactorData = data.into_inner().data; + let password_hash = data.MasterPasswordHash; + + if !headers.user.check_valid_password(&password_hash) { + err!("Invalid password"); + } + + let type_ = data.Type.into_i32().expect("Invalid type"); + + if let Some(twofactor) = TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn) { + twofactor.delete(&conn).expect("Error deleting twofactor"); + } + + Ok(Json(json!({ + "Enabled": false, + "Type": type_, + "Object": "twoFactorProvider" + }))) +} + #[post("/two-factor/get-authenticator", data = "")] -fn generate_authenticator(data: JsonUpcase, headers: Headers) -> JsonResult { +fn generate_authenticator( + data: JsonUpcase, + headers: Headers, + conn: DbConn, +) -> JsonResult { let data: PasswordData = data.into_inner().data; if !headers.user.check_valid_password(&data.MasterPasswordHash) { err!("Invalid password"); } - let (enabled, key) = match headers.user.totp_secret { - Some(secret) => (true, secret), - _ => (false, BASE32.encode(&crypto::get_random(vec![0u8; 20]))) + let type_ = TwoFactorType::Authenticator as i32; + let twofactor = TwoFactor::find_by_user_and_type(&headers.user.uuid, type_, &conn); + + let (enabled, key) = match twofactor { + Some(tf) => (true, tf.data), + _ => (false, BASE32.encode(&crypto::get_random(vec![0u8; 20]))), }; Ok(Json(json!({ @@ -100,20 +141,24 @@ fn generate_authenticator(data: JsonUpcase, headers: Headers) -> J #[derive(Deserialize, Debug)] #[allow(non_snake_case)] -struct EnableTwoFactorData { +struct EnableAuthenticatorData { MasterPasswordHash: String, Key: String, Token: NumberOrString, } #[post("/two-factor/authenticator", data = "")] -fn activate_authenticator(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - let data: EnableTwoFactorData = data.into_inner().data; +fn activate_authenticator( + data: JsonUpcase, + headers: Headers, + conn: DbConn, +) -> JsonResult { + let data: EnableAuthenticatorData = data.into_inner().data; let password_hash = data.MasterPasswordHash; let key = data.Key; let token = match data.Token.into_i32() { Some(n) => n as u64, - None => err!("Malformed token") + None => err!("Malformed token"), }; if !headers.user.check_valid_password(&password_hash) { @@ -123,27 +168,24 @@ fn activate_authenticator(data: JsonUpcase, headers: Header // Validate key as base32 and 20 bytes length let decoded_key: Vec = match BASE32.decode(key.as_bytes()) { Ok(decoded) => decoded, - _ => err!("Invalid totp secret") + _ => err!("Invalid totp secret"), }; if decoded_key.len() != 20 { err!("Invalid key length") } - // Set key in user.totp_secret - let mut user = headers.user; - user.totp_secret = Some(key.to_uppercase()); + let type_ = TwoFactorType::Authenticator; + let twofactor = TwoFactor::new(headers.user.uuid.clone(), type_, key.to_uppercase()); // Validate the token provided with the key - if !user.check_totp_code(token) { + if !twofactor.check_totp_code(token) { err!("Invalid totp code") } - // Generate totp_recover - let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20])); - user.totp_recover = Some(totp_recover); - - user.save(&conn); + let mut user = headers.user; + _generate_recover_code(&mut user, &conn); + twofactor.save(&conn).expect("Error saving twofactor"); Ok(Json(json!({ "Enabled": true, @@ -152,32 +194,228 @@ fn activate_authenticator(data: JsonUpcase, headers: Header }))) } -#[derive(Deserialize)] -#[allow(non_snake_case)] -struct DisableTwoFactorData { - MasterPasswordHash: String, - Type: NumberOrString, +fn _generate_recover_code(user: &mut User, conn: &DbConn) { + if user.totp_recover.is_none() { + let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20])); + user.totp_recover = Some(totp_recover); + user.save(conn); + } } -#[post("/two-factor/disable", data = "")] -fn disable_authenticator(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - let data: DisableTwoFactorData = data.into_inner().data; - let password_hash = data.MasterPasswordHash; - let _type = data.Type; +use u2f::messages::{RegisterResponse, SignResponse, U2fSignRequest}; +use u2f::protocol::{Challenge, U2f}; +use u2f::register::Registration; - if !headers.user.check_valid_password(&password_hash) { +use CONFIG; + +const U2F_VERSION: &str = "U2F_V2"; + +lazy_static! { + static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain); + static ref U2F: U2f = U2f::new(APP_ID.clone()); +} + +#[post("/two-factor/get-u2f", data = "")] +fn generate_u2f(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { + let data: PasswordData = data.into_inner().data; + + if !headers.user.check_valid_password(&data.MasterPasswordHash) { err!("Invalid password"); } - let mut user = headers.user; - user.totp_secret = None; - user.totp_recover = None; + let user_uuid = &headers.user.uuid; - user.save(&conn); + let u2f_type = TwoFactorType::U2f as i32; + let register_type = TwoFactorType::U2fRegisterChallenge; + let (enabled, challenge) = match TwoFactor::find_by_user_and_type(user_uuid, u2f_type, &conn) { + Some(_) => (true, String::new()), + None => { + let c = _create_u2f_challenge(user_uuid, register_type, &conn); + (false, c.challenge) + } + }; Ok(Json(json!({ - "Enabled": false, - "Type": 0, - "Object": "twoFactorProvider" + "Enabled": enabled, + "Challenge": { + "UserId": headers.user.uuid, + "AppId": APP_ID.to_string(), + "Challenge": challenge, + "Version": U2F_VERSION, + }, + "Object": "twoFactorU2f" }))) } + +#[derive(Deserialize, Debug)] +#[allow(non_snake_case)] +struct EnableU2FData { + MasterPasswordHash: String, + DeviceResponse: String, +} + +#[post("/two-factor/u2f", data = "")] +fn activate_u2f(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { + let data: EnableU2FData = data.into_inner().data; + + if !headers.user.check_valid_password(&data.MasterPasswordHash) { + err!("Invalid password"); + } + + let tf_challenge = TwoFactor::find_by_user_and_type( + &headers.user.uuid, + TwoFactorType::U2fRegisterChallenge as i32, + &conn, + ); + + if let Some(tf_challenge) = tf_challenge { + let challenge: Challenge = serde_json::from_str(&tf_challenge.data).unwrap(); + tf_challenge + .delete(&conn) + .expect("Error deleting U2F register challenge"); + + let response: RegisterResponse = serde_json::from_str(&data.DeviceResponse).unwrap(); + + match U2F.register_response(challenge.clone(), response) { + Ok(registration) => { + // TODO: Allow more than one U2F device + let mut registrations = Vec::new(); + registrations.push(registration); + + let tf_registration = TwoFactor::new( + headers.user.uuid.clone(), + TwoFactorType::U2f, + serde_json::to_string(®istrations).unwrap(), + ); + tf_registration + .save(&conn) + .expect("Error saving U2F registration"); + + let mut user = headers.user; + _generate_recover_code(&mut user, &conn); + + Ok(Json(json!({ + "Enabled": true, + "Challenge": { + "UserId": user.uuid, + "AppId": APP_ID.to_string(), + "Challenge": challenge, + "Version": U2F_VERSION, + }, + "Object": "twoFactorU2f" + }))) + } + Err(e) => { + println!("Error: {:#?}", e); + err!("Error activating u2f") + } + } + } else { + err!("Can't recover challenge") + } +} + +fn _create_u2f_challenge(user_uuid: &str, type_: TwoFactorType, conn: &DbConn) -> Challenge { + let challenge = U2F.generate_challenge().unwrap(); + + TwoFactor::new( + user_uuid.into(), + type_, + serde_json::to_string(&challenge).unwrap(), + ).save(conn) + .expect("Error saving challenge"); + + challenge +} + +// This struct is copied from the U2F lib +// because it doesn't implement Deserialize +#[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RegistrationCopy { + pub key_handle: Vec, + pub pub_key: Vec, + pub attestation_cert: Option>, +} + +impl Into for RegistrationCopy { + fn into(self) -> Registration { + Registration { + key_handle: self.key_handle, + pub_key: self.pub_key, + attestation_cert: self.attestation_cert, + } + } +} + +fn _parse_registrations(registations: &str) -> Vec { + let registrations_copy: Vec = serde_json::from_str(registations).unwrap(); + + registrations_copy.into_iter().map(Into::into).collect() +} + +pub fn generate_u2f_login(user_uuid: &str, conn: &DbConn) -> ApiResult { + let challenge = _create_u2f_challenge(user_uuid, TwoFactorType::U2fLoginChallenge, conn); + + let type_ = TwoFactorType::U2f as i32; + let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, type_, conn) { + Some(tf) => tf, + None => err!("No U2F devices registered"), + }; + + let registrations = _parse_registrations(&twofactor.data); + let signed_request: U2fSignRequest = U2F.sign_request(challenge, registrations); + + Ok(signed_request) +} + +pub fn validate_u2f_login(user_uuid: &str, response: &str, conn: &DbConn) -> ApiResult<()> { + let challenge_type = TwoFactorType::U2fLoginChallenge as i32; + let u2f_type = TwoFactorType::U2f as i32; + + let tf_challenge = TwoFactor::find_by_user_and_type(user_uuid, challenge_type, &conn); + + let challenge = match tf_challenge { + Some(tf_challenge) => { + let challenge: Challenge = serde_json::from_str(&tf_challenge.data).unwrap(); + tf_challenge + .delete(&conn) + .expect("Error deleting U2F login challenge"); + challenge + } + None => err!("Can't recover login challenge"), + }; + + let twofactor = match TwoFactor::find_by_user_and_type(user_uuid, u2f_type, conn) { + Some(tf) => tf, + None => err!("No U2F devices registered"), + }; + + let registrations = _parse_registrations(&twofactor.data); + + println!("response {:#?}", response); + + let response: SignResponse = serde_json::from_str(response).unwrap(); + + println!("client_data {:#?}", response.client_data); + println!("key_handle {:#?}", response.key_handle); + println!("signature_data {:#?}", response.signature_data); + + let mut _counter: u32 = 0; + for registration in registrations { + let response = + U2F.sign_response(challenge.clone(), registration, response.clone(), _counter); + match response { + Ok(new_counter) => { + _counter = new_counter; + println!("O {:#}", new_counter); + return Ok(()); + } + Err(e) => { + println!("E {:#}", e); + break; + } + } + } + err!("error verifying response") +} diff --git a/src/api/identity.rs b/src/api/identity.rs index e06eb1f4..b14c1984 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -1,19 +1,21 @@ use std::collections::HashMap; -use rocket::{Route, Outcome}; -use rocket::request::{self, Request, FromRequest, Form, FormItems, FromForm}; +use rocket::request::{self, Form, FormItems, FromForm, FromRequest, Request}; +use rocket::{Outcome, Route}; use rocket_contrib::{Json, Value}; -use db::DbConn; +use num_traits::FromPrimitive; + use db::models::*; +use db::DbConn; -use util; +use util::{self, JsonMap}; -use api::JsonResult; +use api::{ApiResult, JsonResult}; pub fn routes() -> Vec { - routes![ login] + routes![login] } #[post("/connect/token", data = "")] @@ -21,8 +23,8 @@ fn login(connect_data: Form, device_type: DeviceType, conn: DbConn) let data = connect_data.get(); match data.grant_type { - GrantType::RefreshToken =>_refresh_login(data, device_type, conn), - GrantType::Password => _password_login(data, device_type, conn) + GrantType::RefreshToken => _refresh_login(data, device_type, conn), + GrantType::Password => _password_login(data, device_type, conn), } } @@ -33,7 +35,7 @@ fn _refresh_login(data: &ConnectData, _device_type: DeviceType, conn: DbConn) -> // Get device by refresh token let mut device = match Device::find_by_refresh_token(token, &conn) { Some(device) => device, - None => err!("Invalid refresh token") + None => err!("Invalid refresh token"), }; // COMMON @@ -64,7 +66,7 @@ fn _password_login(data: &ConnectData, device_type: DeviceType, conn: DbConn) -> let username = data.get("username"); let user = match User::find_by_mail(username, &conn) { Some(user) => user, - None => err!("Username or password is incorrect. Try again.") + None => err!("Username or password is incorrect. Try again."), }; // Check password @@ -72,7 +74,7 @@ fn _password_login(data: &ConnectData, device_type: DeviceType, conn: DbConn) -> if !user.check_valid_password(password) { err!("Username or password is incorrect. Try again.") } - + // Let's only use the header and ignore the 'devicetype' parameter let device_type_num = device_type.0; @@ -102,42 +104,7 @@ fn _password_login(data: &ConnectData, device_type: DeviceType, conn: DbConn) -> } }; - let twofactor_token = if user.requires_twofactor() { - let twofactor_provider = util::parse_option_string(data.get_opt("twoFactorProvider")).unwrap_or(0); - let twofactor_code = match data.get_opt("twoFactorToken") { - Some(code) => code, - None => err_json!(_json_err_twofactor()) - }; - - match twofactor_provider { - 0 /* TOTP */ => { - let totp_code: u64 = match twofactor_code.parse() { - Ok(code) => code, - Err(_) => err!("Invalid Totp code") - }; - - if !user.check_totp_code(totp_code) { - err_json!(_json_err_twofactor()) - } - - if util::parse_option_string(data.get_opt("twoFactorRemember")).unwrap_or(0) == 1 { - device.refresh_twofactor_remember(); - device.twofactor_remember.clone() - } else { - device.delete_twofactor_remember(); - None - } - }, - 5 /* Remember */ => { - match device.twofactor_remember { - Some(ref remember) if remember == twofactor_code => (), - _ => err_json!(_json_err_twofactor()) - }; - None // No twofactor token needed here - }, - _ => err!("Invalid two factor provider"), - } - } else { None }; // No twofactor token if twofactor is disabled + let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, &conn)?; // Common let user = User::find_by_uuid(&device.user_uuid, &conn).unwrap(); @@ -163,13 +130,124 @@ fn _password_login(data: &ConnectData, device_type: DeviceType, conn: DbConn) -> Ok(Json(result)) } -fn _json_err_twofactor() -> Value { - json!({ +fn twofactor_auth( + user_uuid: &str, + data: &ConnectData, + device: &mut Device, + conn: &DbConn, +) -> ApiResult> { + let twofactors_raw = TwoFactor::find_by_user(user_uuid, conn); + // Remove u2f challenge twofactors (impl detail) + let twofactors: Vec<_> = twofactors_raw.iter().filter(|tf| tf.type_ < 1000).collect(); + + let providers: Vec<_> = twofactors.iter().map(|tf| tf.type_).collect(); + + // No twofactor token if twofactor is disabled + if twofactors.len() == 0 { + return Ok(None); + } + + let provider = match util::parse_option_string(data.get_opt("twoFactorProvider")) { + Some(provider) => provider, + None => providers[0], // If we aren't given a two factor provider, asume the first one + }; + + let twofactor_code = match data.get_opt("twoFactorToken") { + Some(code) => code, + None => err_json!(_json_err_twofactor(&providers, user_uuid, conn)?), + }; + + let twofactor = twofactors.iter().filter(|tf| tf.type_ == provider).nth(0); + + match TwoFactorType::from_i32(provider) { + Some(TwoFactorType::Remember) => { + match &device.twofactor_remember { + Some(remember) if remember == twofactor_code => return Ok(None), // No twofactor token needed here + _ => err_json!(_json_err_twofactor(&providers, user_uuid, conn)?), + } + } + + Some(TwoFactorType::Authenticator) => { + let twofactor = match twofactor { + Some(tf) => tf, + None => err!("TOTP not enabled"), + }; + + let totp_code: u64 = match twofactor_code.parse() { + Ok(code) => code, + _ => err!("Invalid TOTP code"), + }; + + if !twofactor.check_totp_code(totp_code) { + err_json!(_json_err_twofactor(&providers, user_uuid, conn)?) + } + } + + Some(TwoFactorType::U2f) => { + use api::core::two_factor; + + two_factor::validate_u2f_login(user_uuid, twofactor_code, conn)?; + } + + _ => err!("Invalid two factor provider"), + } + + if util::parse_option_string(data.get_opt("twoFactorRemember")).unwrap_or(0) == 1 { + Ok(Some(device.refresh_twofactor_remember())) + } else { + device.delete_twofactor_remember(); + Ok(None) + } +} + +fn _json_err_twofactor(providers: &[i32], user_uuid: &str, conn: &DbConn) -> ApiResult { + use api::core::two_factor; + + let mut result = json!({ "error" : "invalid_grant", "error_description" : "Two factor required.", - "TwoFactorProviders" : [ 0 ], - "TwoFactorProviders2" : { "0" : null } - }) + "TwoFactorProviders" : providers, + "TwoFactorProviders2" : {} // { "0" : null } + }); + + for provider in providers { + result["TwoFactorProviders2"][provider.to_string()] = Value::Null; + + match TwoFactorType::from_i32(*provider) { + Some(TwoFactorType::Authenticator) => { /* Nothing to do for TOTP */ } + + Some(TwoFactorType::U2f) => { + let request = two_factor::generate_u2f_login(user_uuid, conn)?; + let mut challenge_list = Vec::new(); + + for key in request.registered_keys { + let mut challenge_map = JsonMap::new(); + + challenge_map.insert("appId".into(), Value::String(request.app_id.clone())); + challenge_map + .insert("challenge".into(), Value::String(request.challenge.clone())); + challenge_map.insert("version".into(), Value::String(key.version)); + challenge_map.insert( + "keyHandle".into(), + Value::String(key.key_handle.unwrap_or_default()), + ); + + challenge_list.push(Value::Object(challenge_map)); + } + + let mut map = JsonMap::new(); + use serde_json; + let challenge_list_str = serde_json::to_string(&challenge_list).unwrap(); + + map.insert("Challenges".into(), Value::String(challenge_list_str)); + result["TwoFactorProviders2"][provider.to_string()] = Value::Object(map); + } + + _ => {} + } + } + + Ok(result) } #[derive(Clone, Copy)] @@ -187,7 +265,6 @@ impl<'a, 'r> FromRequest<'a, 'r> for DeviceType { } } - #[derive(Debug)] struct ConnectData { grant_type: GrantType, @@ -196,7 +273,10 @@ struct ConnectData { } #[derive(Debug, Copy, Clone)] -enum GrantType { RefreshToken, Password } +enum GrantType { + RefreshToken, + Password, +} impl ConnectData { fn get(&self, key: &str) -> &String { @@ -227,25 +307,28 @@ impl<'f> FromForm<'f> for ConnectData { } // Validate needed values - let (grant_type, is_device) = - match data.get("grant_type").map(String::as_ref) { - Some("refresh_token") => { - check_values(&data, &VALUES_REFRESH)?; - (GrantType::RefreshToken, false) // Device doesn't matter here - } - Some("password") => { - check_values(&data, &VALUES_PASSWORD)?; + let (grant_type, is_device) = match data.get("grant_type").map(String::as_ref) { + Some("refresh_token") => { + check_values(&data, &VALUES_REFRESH)?; + (GrantType::RefreshToken, false) // Device doesn't matter here + } + Some("password") => { + check_values(&data, &VALUES_PASSWORD)?; - let is_device = match data["client_id"].as_ref() { - "browser" | "mobile" => check_values(&data, &VALUES_DEVICE)?, - _ => false - }; - (GrantType::Password, is_device) - } - _ => return Err("Grant type not supported".to_string()) - }; + let is_device = match data["client_id"].as_ref() { + "browser" | "mobile" => check_values(&data, &VALUES_DEVICE)?, + _ => false, + }; + (GrantType::Password, is_device) + } + _ => return Err("Grant type not supported".to_string()), + }; - Ok(ConnectData { grant_type, is_device, data }) + Ok(ConnectData { + grant_type, + is_device, + data, + }) } } diff --git a/src/api/mod.rs b/src/api/mod.rs index f56e6091..657b3e84 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,4 +1,4 @@ -mod core; +pub(crate) mod core; mod icons; mod identity; mod web; @@ -12,8 +12,9 @@ use rocket::response::status::BadRequest; use rocket_contrib::Json; // Type aliases for API methods results -type JsonResult = Result>; -type EmptyResult = Result<(), BadRequest>; +type ApiResult = Result>; +type JsonResult = ApiResult; +type EmptyResult = ApiResult<()>; use util; type JsonUpcase = Json>; diff --git a/src/api/web.rs b/src/api/web.rs index 3866eac6..505ec120 100644 --- a/src/api/web.rs +++ b/src/api/web.rs @@ -4,13 +4,13 @@ use std::path::{Path, PathBuf}; use rocket::request::Request; use rocket::response::{self, NamedFile, Responder}; use rocket::Route; -use rocket_contrib::Json; +use rocket_contrib::{Json, Value}; use CONFIG; pub fn routes() -> Vec { if CONFIG.web_vault_enabled { - routes![web_index, web_files, attachments, alive] + routes![web_index, app_id, web_files, attachments, alive] } else { routes![attachments, alive] } @@ -22,6 +22,20 @@ fn web_index() -> WebHeaders> { web_files("index.html".into()) } +#[get("/app-id.json")] +fn app_id() -> WebHeaders> { + WebHeaders(Json(json!({ + "trustedFacets": [ + { + "version": { "major": 1, "minor": 0 }, + "ids": [ + &CONFIG.domain, + "ios:bundle-id:com.8bit.bitwarden", + "android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI" ] + }] + }))) +} + #[get("/", rank = 1)] // Only match this if the other routes don't match fn web_files(p: PathBuf) -> WebHeaders> { WebHeaders(NamedFile::open(Path::new(&CONFIG.web_vault_folder).join(p))) diff --git a/src/auth.rs b/src/auth.rs index 186fbf48..0e2a440b 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -11,11 +11,11 @@ use serde::ser::Serialize; use CONFIG; const JWT_ALGORITHM: jwt::Algorithm = jwt::Algorithm::RS256; - // TODO: This isn't used, but we should make sure it represents the correct address -pub const JWT_ISSUER: &str = "localhost:8000/identity"; lazy_static! { pub static ref DEFAULT_VALIDITY: Duration = Duration::hours(2); + pub static ref JWT_ISSUER: String = CONFIG.domain.clone(); + static ref JWT_HEADER: jwt::Header = jwt::Header::new(JWT_ALGORITHM); static ref PRIVATE_RSA_KEY: Vec = match read_file(&CONFIG.private_rsa_key) { @@ -43,7 +43,7 @@ pub fn decode_jwt(token: &str) -> Result { validate_iat: true, validate_nbf: true, aud: None, - iss: Some(JWT_ISSUER.into()), + iss: Some(JWT_ISSUER.clone()), sub: None, algorithms: vec![JWT_ALGORITHM], }; diff --git a/src/db/models/device.rs b/src/db/models/device.rs index ee3f595c..8063e9ee 100644 --- a/src/db/models/device.rs +++ b/src/db/models/device.rs @@ -43,11 +43,14 @@ impl Device { } } - pub fn refresh_twofactor_remember(&mut self) { + pub fn refresh_twofactor_remember(&mut self) -> String { use data_encoding::BASE64; use crypto; - self.twofactor_remember = Some(BASE64.encode(&crypto::get_random(vec![0u8; 180]))); + let twofactor_remember = BASE64.encode(&crypto::get_random(vec![0u8; 180])); + self.twofactor_remember = Some(twofactor_remember.clone()); + + twofactor_remember } pub fn delete_twofactor_remember(&mut self) { diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index cb5c00a7..7032a391 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -6,6 +6,7 @@ mod user; mod collection; mod organization; +mod two_factor; pub use self::attachment::Attachment; pub use self::cipher::Cipher; @@ -15,3 +16,4 @@ pub use self::user::User; pub use self::organization::Organization; pub use self::organization::{UserOrganization, UserOrgStatus, UserOrgType}; pub use self::collection::{Collection, CollectionUser, CollectionCipher}; +pub use self::two_factor::{TwoFactor, TwoFactorType}; \ No newline at end of file diff --git a/src/db/models/two_factor.rs b/src/db/models/two_factor.rs new file mode 100644 index 00000000..772887bf --- /dev/null +++ b/src/db/models/two_factor.rs @@ -0,0 +1,112 @@ +use serde_json::Value as JsonValue; + +use uuid::Uuid; + +use super::User; + +#[derive(Debug, Identifiable, Queryable, Insertable, Associations)] +#[table_name = "twofactor"] +#[belongs_to(User, foreign_key = "user_uuid")] +#[primary_key(uuid)] +pub struct TwoFactor { + pub uuid: String, + pub user_uuid: String, + pub type_: i32, + pub enabled: bool, + pub data: String, +} + +#[allow(dead_code)] +#[derive(FromPrimitive, ToPrimitive)] +pub enum TwoFactorType { + Authenticator = 0, + Email = 1, + Duo = 2, + YubiKey = 3, + U2f = 4, + Remember = 5, + OrganizationDuo = 6, + + // These are implementation details + U2fRegisterChallenge = 1000, + U2fLoginChallenge = 1001, +} + +/// Local methods +impl TwoFactor { + pub fn new(user_uuid: String, type_: TwoFactorType, data: String) -> Self { + Self { + uuid: Uuid::new_v4().to_string(), + user_uuid, + type_: type_ as i32, + enabled: true, + data, + } + } + + pub fn check_totp_code(&self, totp_code: u64) -> bool { + let totp_secret = self.data.as_bytes(); + + use data_encoding::BASE32; + use oath::{totp_raw_now, HashType}; + + let decoded_secret = match BASE32.decode(totp_secret) { + Ok(s) => s, + Err(_) => return false + }; + + let generated = totp_raw_now(&decoded_secret, 6, 0, 30, &HashType::SHA1); + generated == totp_code + } + + pub fn to_json(&self) -> JsonValue { + json!({ + "Enabled": self.enabled, + "Key": "", // This key and value vary + "Object": "twoFactorAuthenticator" // This value varies + }) + } + + pub fn to_json_list(&self) -> JsonValue { + json!({ + "Enabled": self.enabled, + "Type": self.type_, + "Object": "twoFactorProvider" + }) + } +} + +use diesel; +use diesel::prelude::*; +use db::DbConn; +use db::schema::twofactor; + +/// Database methods +impl TwoFactor { + pub fn save(&self, conn: &DbConn) -> QueryResult { + diesel::replace_into(twofactor::table) + .values(self) + .execute(&**conn) + } + + pub fn delete(self, conn: &DbConn) -> QueryResult { + diesel::delete( + twofactor::table.filter( + twofactor::uuid.eq(self.uuid) + ) + ).execute(&**conn) + } + + pub fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec { + twofactor::table + .filter(twofactor::user_uuid.eq(user_uuid)) + .load::(&**conn).expect("Error loading twofactor") + } + + pub fn find_by_user_and_type(user_uuid: &str, type_: i32, conn: &DbConn) -> Option { + twofactor::table + .filter(twofactor::user_uuid.eq(user_uuid)) + .filter(twofactor::type_.eq(type_)) + .first::(&**conn).ok() + } +} \ No newline at end of file diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 891088b4..2c30c52d 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -27,7 +27,8 @@ pub struct User { pub private_key: Option, pub public_key: Option, - pub totp_secret: Option, + #[column_name = "totp_secret"] + _totp_secret: Option, pub totp_recover: Option, pub security_stamp: String, @@ -64,7 +65,7 @@ impl User { private_key: None, public_key: None, - totp_secret: None, + _totp_secret: None, totp_recover: None, equivalent_domains: "[]".to_string(), @@ -97,28 +98,6 @@ impl User { pub fn reset_security_stamp(&mut self) { self.security_stamp = Uuid::new_v4().to_string(); } - - pub fn requires_twofactor(&self) -> bool { - self.totp_secret.is_some() - } - - pub fn check_totp_code(&self, totp_code: u64) -> bool { - if let Some(ref totp_secret) = self.totp_secret { - // Validate totp - use data_encoding::BASE32; - use oath::{totp_raw_now, HashType}; - - let decoded_secret = match BASE32.decode(totp_secret.as_bytes()) { - Ok(s) => s, - Err(_) => return false - }; - - let generated = totp_raw_now(&decoded_secret, 6, 0, 30, &HashType::SHA1); - generated == totp_code - } else { - true - } - } } use diesel; @@ -130,10 +109,13 @@ use db::schema::users; impl User { pub fn to_json(&self, conn: &DbConn) -> JsonValue { use super::UserOrganization; + use super::TwoFactor; let orgs = UserOrganization::find_by_user(&self.uuid, conn); let orgs_json: Vec = orgs.iter().map(|c| c.to_json(&conn)).collect(); + let twofactor_enabled = TwoFactor::find_by_user(&self.uuid, conn).len() > 0; + json!({ "Id": self.uuid, "Name": self.name, @@ -142,7 +124,7 @@ impl User { "Premium": true, "MasterPasswordHint": self.password_hint, "Culture": "en-US", - "TwoFactorEnabled": self.totp_secret.is_some(), + "TwoFactorEnabled": twofactor_enabled, "Key": self.key, "PrivateKey": self.private_key, "SecurityStamp": self.security_stamp, diff --git a/src/db/schema.rs b/src/db/schema.rs index baf9e6d2..f39701b1 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -79,6 +79,17 @@ table! { } } +table! { + twofactor (uuid) { + uuid -> Text, + user_uuid -> Text, + #[sql_name = "type"] + type_ -> Integer, + enabled -> Bool, + data -> Text, + } +} + table! { users (uuid) { uuid -> Text, @@ -132,6 +143,7 @@ joinable!(devices -> users (user_uuid)); joinable!(folders -> users (user_uuid)); joinable!(folders_ciphers -> ciphers (cipher_uuid)); joinable!(folders_ciphers -> folders (folder_uuid)); +joinable!(twofactor -> users (user_uuid)); joinable!(users_collections -> collections (collection_uuid)); joinable!(users_collections -> users (user_uuid)); joinable!(users_organizations -> organizations (org_uuid)); @@ -146,6 +158,7 @@ allow_tables_to_appear_in_same_query!( folders, folders_ciphers, organizations, + twofactor, users, users_collections, users_organizations, diff --git a/src/main.rs b/src/main.rs index 99941732..48f8a47d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,9 +19,13 @@ extern crate chrono; extern crate oath; extern crate data_encoding; extern crate jsonwebtoken as jwt; +extern crate u2f; extern crate dotenv; #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate num_derive; +extern crate num_traits; use std::{env, path::Path, process::{exit, Command}}; use rocket::Rocket; @@ -160,6 +164,7 @@ pub struct Config { local_icon_extractor: bool, signups_allowed: bool, password_iterations: i32, + domain: String, } impl Config { @@ -184,6 +189,7 @@ impl Config { local_icon_extractor: util::parse_option_string(env::var("LOCAL_ICON_EXTRACTOR").ok()).unwrap_or(false), signups_allowed: util::parse_option_string(env::var("SIGNUPS_ALLOWED").ok()).unwrap_or(true), password_iterations: util::parse_option_string(env::var("PASSWORD_ITERATIONS").ok()).unwrap_or(100_000), + domain: env::var("DOMAIN").unwrap_or("https://localhost".into()), } } } diff --git a/src/util.rs b/src/util.rs index e29d1bdb..78d169a6 100644 --- a/src/util.rs +++ b/src/util.rs @@ -132,7 +132,9 @@ pub fn format_date(date: &NaiveDateTime) -> String { use std::fmt; use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, SeqAccess, Visitor}; -use serde_json::Value; +use serde_json::{self, Value}; + +pub type JsonMap = serde_json::Map; #[derive(PartialEq, Serialize, Deserialize)] pub struct UpCase { @@ -162,8 +164,7 @@ impl<'de> Visitor<'de> for UpCaseVisitor { fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de> { - use serde_json::Map; - let mut result_map = Map::::new(); + let mut result_map = JsonMap::new(); while let Some((key, value)) = map.next_entry()? { result_map.insert(upcase_first(key), upcase_value(&value));