From 8d1eb8ab19abadcf225098e178b39364bc9d88be Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 12 Nov 2021 12:19:55 +0100 Subject: [PATCH 01/73] Pull in captcha lib --- Cargo.lock | 458 ++++++++++++++++++++++++++++++- Cargo.toml | 3 + src/internet_identity/Cargo.toml | 2 + 3 files changed, 453 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1fe0d4ec0..d58cae416a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,29 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom 0.2.2", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.15" @@ -111,6 +134,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "blake2b_simd" version = "0.5.11" @@ -131,6 +160,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bytemuck" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" + [[package]] name = "byteorder" version = "1.4.3" @@ -177,6 +212,31 @@ dependencies = [ ] [[package]] +<<<<<<< HEAD +======= +name = "captcha" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29256038744434f6d0e1328d7c9050f14aa5fc8562ff065b9e9481ac293ba5bc" +dependencies = [ + "base64", + "hound", + "image", + "lodepng", + "rand 0.7.3", + "serde_json", +] + +[[package]] +name = "certified_map" +version = "0.1.0" +dependencies = [ + "hashtree", + "hex", +] + +[[package]] +>>>>>>> 9e862d6 (Pull in captcha lib) name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -192,6 +252,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -213,6 +279,53 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + [[package]] name = "crossbeam-utils" version = "0.8.3" @@ -272,6 +385,16 @@ dependencies = [ "syn", ] +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + [[package]] name = "derivative" version = "2.2.0" @@ -324,12 +447,33 @@ dependencies = [ "log", ] +[[package]] +name = "fallible_collections" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaefd4190151d458f16f0793d3452d7f13aeb3701566a4cefc4c37598876cc00" +dependencies = [ + "hashbrown 0.11.2", +] + [[package]] name = "fixedbitset" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + [[package]] name = "fnv" version = "1.0.7" @@ -368,6 +512,16 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gif" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "half" version = "1.7.1" @@ -381,6 +535,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] +<<<<<<< HEAD +======= +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashtree" +version = "0.1.0" +dependencies = [ + "hex", + "serde", + "serde_bytes", + "serde_cbor", + "sha2", +] + +[[package]] +>>>>>>> 9e862d6 (Pull in captcha lib) name = "hermit-abi" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -414,6 +591,12 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "hound" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" + [[package]] name = "ic-cdk" version = "0.3.3" @@ -484,6 +667,25 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.6.2" @@ -491,7 +693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.9.1", ] [[package]] @@ -499,6 +701,11 @@ name = "internet_identity" version = "0.1.0" dependencies = [ "base64", +<<<<<<< HEAD +======= + "captcha", + "certified_map", +>>>>>>> 9e862d6 (Pull in captcha lib) "cubehash", "hex", "hex-literal", @@ -506,7 +713,7 @@ dependencies = [ "ic-cdk-macros", "ic-certified-map", "ic-types 0.1.2", - "rand", + "rand 0.8.3", "serde", "serde_bytes", "serde_cbor", @@ -523,6 +730,21 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + [[package]] name = "lalrpop" version = "0.19.5" @@ -569,9 +791,20 @@ checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" [[package]] name = "libc" -version = "0.2.93" +version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" + +[[package]] +name = "lodepng" +version = "3.4.6" +source = "git+https://github.com/nmattia/lodepng-rust?branch=nm-wasm32#304bcfc415ffa204ec9e82d5ec535708ed1bcca8" +dependencies = [ + "fallible_collections", + "flate2", + "libc", + "rgb", +] [[package]] name = "log" @@ -606,12 +839,46 @@ dependencies = [ "utf8-ranges", ] +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -639,6 +906,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -648,6 +937,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.1" @@ -670,6 +969,12 @@ dependencies = [ "syn", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -707,6 +1012,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6" +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -762,6 +1079,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + [[package]] name = "rand" version = "0.8.3" @@ -769,9 +1099,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -781,7 +1121,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -793,13 +1142,47 @@ dependencies = [ "getrandom 0.2.2", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "rand_hc" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core", + "rand_core 0.6.2", +] + +[[package]] +name = "rayon" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils 0.7.2", + "lazy_static", + "num_cpus", ] [[package]] @@ -836,6 +1219,15 @@ version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +[[package]] +name = "rgb" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27fa03bb1e3e2941f52d4a555a395a72bf79b0a85fbbaab79447050c97d978c" +dependencies = [ + "bytemuck", +] + [[package]] name = "rust-argon2" version = "0.8.3" @@ -845,7 +1237,7 @@ dependencies = [ "base64", "blake2b_simd", "constant_time_eq", - "crossbeam-utils", + "crossbeam-utils 0.8.3", ] [[package]] @@ -854,6 +1246,24 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.125" @@ -893,6 +1303,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_tokenstream" version = "0.1.2" @@ -1015,6 +1436,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.4", + "weezl", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -1081,6 +1513,12 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "weezl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index f8e5b533c8..6cc25f1954 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,6 @@ members = [ [profile.release] lto = true opt-level = 'z' + +[patch.crates-io] +lodepng = { git = 'https://github.com/nmattia/lodepng-rust', branch = 'nm-wasm32'} diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index ddfa4c8fbc..3a07f70530 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -5,6 +5,8 @@ edition = "2018" [dependencies] base64 = "0.13.0" +captcha = "0.0.8" +certified_map = { path = "../certified_map" } cubehash = { path = "../cubehash" } hex = "0.4" ic-cdk = "0.3.2" From e95cef8747529deed7db8e2e6a62c4d8ab5ff3cd Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 15 Nov 2021 10:38:04 +0100 Subject: [PATCH 02/73] wip --- .../generated/internet_identity_idl.js | 2 ++ .../generated/internet_identity_types.d.ts | 3 ++ src/frontend/src/index.ts | 31 +++++++++++++++++ src/frontend/src/utils/iiConnection.ts | 5 +++ src/internet_identity/internet_identity.did | 6 ++++ src/internet_identity/src/main.rs | 34 +++++++++++++++++++ 6 files changed, 81 insertions(+) diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index 4428e4d02c..224f9e1330 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -23,6 +23,7 @@ export const idlFactory = ({ IDL }) => { 'purpose' : Purpose, 'credential_id' : IDL.Opt(CredentialId), }); + const CaptchaResponse = IDL.Variant({ 'png' : IDL.Text, 'error' : IDL.Null }); const FrontendHostname = IDL.Text; const SessionKey = PublicKey; const Timestamp = IDL.Nat64; @@ -82,6 +83,7 @@ export const idlFactory = ({ IDL }) => { }); return IDL.Service({ 'add' : IDL.Func([UserNumber, DeviceData], [], []), + 'get_captcha' : IDL.Func([], [CaptchaResponse], ['query']), 'get_delegation' : IDL.Func( [UserNumber, FrontendHostname, SessionKey, Timestamp], [GetDelegationResponse], diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index 0cf3941f79..ce9d870390 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -1,4 +1,6 @@ import type { Principal } from '@dfinity/principal'; +export type CaptchaResponse = { 'png' : string } | + { 'error' : null }; export type CredentialId = Array; export interface Delegation { 'pubkey' : PublicKey, @@ -64,6 +66,7 @@ export type UserKey = PublicKey; export type UserNumber = bigint; export interface _SERVICE { 'add' : (arg_0: UserNumber, arg_1: DeviceData) => Promise, + 'get_captcha' : () => Promise, 'get_delegation' : ( arg_0: UserNumber, arg_1: FrontendHostname, diff --git a/src/frontend/src/index.ts b/src/frontend/src/index.ts index 7a56390394..9bf4c7ec14 100644 --- a/src/frontend/src/index.ts +++ b/src/frontend/src/index.ts @@ -2,6 +2,7 @@ import "./styles/main.css"; import { login } from "./flows/login"; import auth from "./auth"; import { addDevice } from "./flows/addDevice"; +import { hasOwnProperty } from "./utils/utils"; import { renderManage } from "./flows/manage"; import { compatibilityNotice } from "./flows/compatibilityNotice"; import { aboutView } from "./flows/about"; @@ -9,6 +10,9 @@ import { faqView } from "./flows/faq"; import { intentFromUrl } from "./utils/userIntent"; import { hasRequiredFeatures } from "./utils/featureDetection"; import { recoveryWizard } from "./flows/recovery/recoveryWizard"; +import { + IIConnection, +} from "./utils/iiConnection"; const init = async () => { const url = new URL(document.URL); @@ -28,9 +32,36 @@ const init = async () => { const userIntent = intentFromUrl(url); + IIConnection.getCaptcha().then((e) => { + console.log("Got captcha"); + console.log(e); + if(hasOwnProperty(e, "png")) { + console.log(e.png); + } + //let n = e.png.length; + //let bytes: Blob[] = []; + //for(let i = 0; i < n; i++) { + //bytes.push(String.fromCharCode(e.png[i])); + //} + + //var blob = new Blob ( bytes , { type: "image/png" } ); + //} + }).catch((e) => { + console.log("Failed to load captcha"); + console.log(e); + }); + // Go through the login flow, potentially creating an anchor. const { userNumber, connection } = await login(userIntent); + IIConnection.lookupAll(userNumber).then((e) => { + console.log("got lookup"); + }).catch((e) => { + console.log("Failed to lookup all"); + console.log(e); + }); + + // From here on, the user is authenticated to II. // Here, if the user doesn't have any recovery device, we prompt them to add diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 03946507e6..6cb301d8ee 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -11,6 +11,7 @@ import { PublicKey, SessionKey, CredentialId, + CaptchaResponse, UserNumber, FrontendHostname, Timestamp, @@ -207,6 +208,10 @@ export class IIConnection { return await baseActor.lookup(userNumber); } + static async getCaptcha() : Promise { + return await baseActor.get_captcha(); + } + static async lookupAuthenticators( userNumber: UserNumber ): Promise { diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index cd3ba19dba..9f6dbca084 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -49,6 +49,11 @@ type KeyType = variant { seed_phrase; }; +type CaptchaResponse = variant { + error; + png: text; +}; + type DeviceData = record { pubkey : DeviceKey; alias : text; @@ -100,6 +105,7 @@ type ProofOfWork = record { service : (opt InternetIdentityInit) -> { init_salt: () -> (); register : (DeviceData, ProofOfWork) -> (RegisterResponse); + get_captcha: () -> (CaptchaResponse) query; add : (UserNumber, DeviceData) -> (); remove : (UserNumber, DeviceKey) -> (); lookup : (UserNumber) -> (vec DeviceData) query; diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index d72faceaa2..3f0cf1ca13 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -21,6 +21,9 @@ const fn secs_to_nanos(secs: u64) -> u64 { secs * 1_000_000_000 } +use captcha::{gen, Difficulty, Captcha}; +use captcha::filters::{Noise, Wave, Dots}; + // 30 mins const DEFAULT_EXPIRATION_PERIOD_NS: u64 = secs_to_nanos(30 * 60); // 8 days @@ -72,6 +75,14 @@ struct DeviceData { key_type: KeyType, } +#[derive(Clone, Debug, CandidType, Deserialize)] +enum CaptchaResponse { + #[serde(rename = "error")] + Error, + #[serde(rename = "png")] + Png(String) +} + /// This is an internal version of `DeviceData` primarily useful to provide a /// backwards compatible level between older device data stored in stable memory /// (that might not contain purpose or key_type) and new ones added. @@ -367,6 +378,29 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { }) } +#[query] +fn get_captcha() -> CaptchaResponse { + let mut captcha = captcha::Captcha::new(); + let chars = "2fJU".chars().collect::>(); + let captcha = captcha.set_chars(&chars) + //.apply_filter(Wave::new(2.0, 20.0).horizontal()) + //.apply_filter(Wave::new(2.0, 20.0).vertical()) + .view(1024, 1024); + + + match captcha.as_base64() { + Some(foo) => { + + return CaptchaResponse::Png(foo); + } + None => { + return CaptchaResponse::Error; + } + } + //println!("Supported chars:"); + //panic!("chars: {}", captcha.supported_chars().iter().collect::()); +} + #[query] fn lookup(user_number: UserNumber) -> Vec { STATE.with(|s| { From d569f46d798e2cc02432941d81dc056203036be3 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 16 Nov 2021 14:36:33 +0100 Subject: [PATCH 03/73] Seed captcha with raw_rand --- Cargo.lock | 21 ++++---- Cargo.toml | 2 +- src/frontend/src/flows/showCaptcha.ts | 73 +++++++++++++++++++++++++++ src/frontend/src/index.ts | 19 ++----- src/internet_identity/Cargo.toml | 4 +- src/internet_identity/src/main.rs | 55 +++++++++++++++----- 6 files changed, 134 insertions(+), 40 deletions(-) create mode 100644 src/frontend/src/flows/showCaptcha.ts diff --git a/Cargo.lock b/Cargo.lock index d58cae416a..cab4872ae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,8 +216,7 @@ dependencies = [ ======= name = "captcha" version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29256038744434f6d0e1328d7c9050f14aa5fc8562ff065b9e9481ac293ba5bc" +source = "git+https://github.com/nmattia/captcha?branch=nm-set-rng#4751b6fa4e56229c2af5b9a28195ff039551d8e7" dependencies = [ "base64", "hound", @@ -714,6 +713,8 @@ dependencies = [ "ic-certified-map", "ic-types 0.1.2", "rand 0.8.3", + "rand_chacha 0.2.2", + "rand_core 0.5.1", "serde", "serde_bytes", "serde_cbor", @@ -1099,8 +1100,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.2", + "rand_chacha 0.3.1", + "rand_core 0.6.3", "rand_hc 0.3.0", ] @@ -1116,12 +1117,12 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.2", + "rand_core 0.6.3", ] [[package]] @@ -1135,9 +1136,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ "getrandom 0.2.2", ] @@ -1157,7 +1158,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.2", + "rand_core 0.6.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6cc25f1954..fc2b8fb7b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ lto = true opt-level = 'z' [patch.crates-io] -lodepng = { git = 'https://github.com/nmattia/lodepng-rust', branch = 'nm-wasm32'} +lodepng = { git = 'https://github.com/nmattia/lodepng-rust', branch = 'nm-wasm32' } diff --git a/src/frontend/src/flows/showCaptcha.ts b/src/frontend/src/flows/showCaptcha.ts new file mode 100644 index 0000000000..0ac4526adc --- /dev/null +++ b/src/frontend/src/flows/showCaptcha.ts @@ -0,0 +1,73 @@ +import { html, render } from "lit-html"; +import { + IIConnection, +} from "../utils/iiConnection"; +import { hasOwnProperty } from "../utils/utils"; + +// The FAQ page +const pageContent = html` + +
+

This will show a captcha

+ + +
+`; + +// Open the anchor with id="foo" if the page hash is "#foo" +const openAnchor = (): void => { + const hash = location.hash.substring(1); + + if (hash !== "") { + const details = document.getElementById(hash); + console.log(details); + + if (details) { + details.setAttribute("open", ""); + } + } +}; + +export const showCaptcha = (): Promise => { + document.title = "show captcha"; + const container = document.getElementById("pageContent") as HTMLElement; + render(pageContent, container); + openAnchor(); // needs to happen after DOM was rendered + + IIConnection.getCaptcha().then((e) => { + console.log("Got captcha"); + if(hasOwnProperty(e, "png")) { + console.log("captcha has png"); + const img = document.querySelector("img.captcha"); + if(img) { + console.log("got img"); + img.setAttribute('src', `data:image/png;base64, ${e.png}`); + } + } + }).catch((e) => { + console.log("Failed to load captcha"); + console.log(e); + }); + + return new Promise((resolve) => { + console.log("waiting"); + }); +}; diff --git a/src/frontend/src/index.ts b/src/frontend/src/index.ts index 9bf4c7ec14..79e34a48c1 100644 --- a/src/frontend/src/index.ts +++ b/src/frontend/src/index.ts @@ -7,6 +7,7 @@ import { renderManage } from "./flows/manage"; import { compatibilityNotice } from "./flows/compatibilityNotice"; import { aboutView } from "./flows/about"; import { faqView } from "./flows/faq"; +import { showCaptcha } from "./flows/showCaptcha"; import { intentFromUrl } from "./utils/userIntent"; import { hasRequiredFeatures } from "./utils/featureDetection"; import { recoveryWizard } from "./flows/recovery/recoveryWizard"; @@ -32,20 +33,14 @@ const init = async () => { const userIntent = intentFromUrl(url); + await showCaptcha(); + IIConnection.getCaptcha().then((e) => { console.log("Got captcha"); console.log(e); if(hasOwnProperty(e, "png")) { console.log(e.png); } - //let n = e.png.length; - //let bytes: Blob[] = []; - //for(let i = 0; i < n; i++) { - //bytes.push(String.fromCharCode(e.png[i])); - //} - - //var blob = new Blob ( bytes , { type: "image/png" } ); - //} }).catch((e) => { console.log("Failed to load captcha"); console.log(e); @@ -54,14 +49,6 @@ const init = async () => { // Go through the login flow, potentially creating an anchor. const { userNumber, connection } = await login(userIntent); - IIConnection.lookupAll(userNumber).then((e) => { - console.log("got lookup"); - }).catch((e) => { - console.log("Failed to lookup all"); - console.log(e); - }); - - // From here on, the user is authenticated to II. // Here, if the user doesn't have any recovery device, we prompt them to add diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index 3a07f70530..0fe01741de 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -5,7 +5,6 @@ edition = "2018" [dependencies] base64 = "0.13.0" -captcha = "0.0.8" certified_map = { path = "../certified_map" } cubehash = { path = "../cubehash" } hex = "0.4" @@ -18,6 +17,9 @@ serde_bytes = "0.11" serde_cbor = "0.11" serde_with = "1.6.2" sha2 = "0.9.1" +rand_core = "0.5.1" +rand_chacha = "0.2.2" +captcha = { git = 'https://github.com/nmattia/captcha', branch = 'nm-set-rng' } [dev-dependencies] hex-literal = "0.2.1" diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 3f0cf1ca13..0b804e0a1d 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -14,6 +14,9 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::convert::TryInto; use storage::{Salt, Storage}; +use rand_chacha::rand_core::SeedableRng; + +use rand_core::{impls, Error}; mod assets; @@ -21,8 +24,8 @@ const fn secs_to_nanos(secs: u64) -> u64 { secs * 1_000_000_000 } -use captcha::{gen, Difficulty, Captcha}; -use captcha::filters::{Noise, Wave, Dots}; +//use captcha::{gen, Difficulty, Captcha}; +use captcha::filters::{Wave}; // 30 mins const DEFAULT_EXPIRATION_PERIOD_NS: u64 = secs_to_nanos(30 * 60); @@ -83,6 +86,25 @@ enum CaptchaResponse { Png(String) } + +struct StaticRng(); + +impl rand_core::RngCore for StaticRng { + fn next_u32(&mut self) -> u32 { + 0 + } + fn next_u64(&mut self) -> u64 { + 0 + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + /// This is an internal version of `DeviceData` primarily useful to provide a /// backwards compatible level between older device data stored in stable memory /// (that might not contain purpose or key_type) and new ones added. @@ -379,26 +401,35 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { } #[query] -fn get_captcha() -> CaptchaResponse { - let mut captcha = captcha::Captcha::new(); - let chars = "2fJU".chars().collect::>(); - let captcha = captcha.set_chars(&chars) - //.apply_filter(Wave::new(2.0, 20.0).horizontal()) - //.apply_filter(Wave::new(2.0, 20.0).vertical()) - .view(1024, 1024); +async fn get_captcha() -> CaptchaResponse { + let res: Vec = match call(Principal::management_canister(), "raw_rand", ()).await { + Ok((res,)) => res, + Err((_, err)) => trap(&format!("failed to get seed: {}", err)), + }; + + let seed: Salt = res[..].try_into().unwrap_or_else(|_| { + trap(&format!( + "when creating seed from raw_rand output, expected raw randomness to be of length 32, got {}", + res.len() + )); + }); + + let rng = rand_chacha::ChaCha20Rng::from_seed(seed); + let mut captcha = captcha::RngCaptcha::from_rng(rng); + let captcha = captcha.add_chars(5) + .apply_filter(Wave::new(2.0, 20.0).horizontal()) + .apply_filter(Wave::new(2.0, 20.0).vertical()) + .view(220, 120); match captcha.as_base64() { Some(foo) => { - return CaptchaResponse::Png(foo); } None => { return CaptchaResponse::Error; } } - //println!("Supported chars:"); - //panic!("chars: {}", captcha.supported_chars().iter().collect::()); } #[query] From 4738a415d4a1f377a9016284b66b87d44741a575 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 16 Nov 2021 14:58:44 +0100 Subject: [PATCH 04/73] wip --- src/frontend/generated/internet_identity_idl.js | 2 +- src/internet_identity/internet_identity.did | 2 +- src/internet_identity/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index 224f9e1330..40d384ea4b 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -83,7 +83,7 @@ export const idlFactory = ({ IDL }) => { }); return IDL.Service({ 'add' : IDL.Func([UserNumber, DeviceData], [], []), - 'get_captcha' : IDL.Func([], [CaptchaResponse], ['query']), + 'get_captcha' : IDL.Func([], [CaptchaResponse], []), 'get_delegation' : IDL.Func( [UserNumber, FrontendHostname, SessionKey, Timestamp], [GetDelegationResponse], diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index 9f6dbca084..d288469ed6 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -105,7 +105,7 @@ type ProofOfWork = record { service : (opt InternetIdentityInit) -> { init_salt: () -> (); register : (DeviceData, ProofOfWork) -> (RegisterResponse); - get_captcha: () -> (CaptchaResponse) query; + get_captcha: () -> (CaptchaResponse); add : (UserNumber, DeviceData) -> (); remove : (UserNumber, DeviceKey) -> (); lookup : (UserNumber) -> (vec DeviceData) query; diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 0b804e0a1d..2fe051a97e 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -400,7 +400,7 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { }) } -#[query] +#[update] async fn get_captcha() -> CaptchaResponse { let res: Vec = match call(Principal::management_canister(), "raw_rand", ()).await { From 028f29361c44d5a6cc1b841b54f3a92429110150 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 16 Nov 2021 16:49:10 +0100 Subject: [PATCH 05/73] Add backend code for generating captcha challenges --- .../generated/internet_identity_idl.js | 14 ++- .../generated/internet_identity_types.d.ts | 14 ++- src/frontend/src/flows/showCaptcha.ts | 7 ++ src/frontend/src/utils/iiConnection.ts | 13 ++- src/internet_identity/internet_identity.did | 16 ++- src/internet_identity/src/main.rs | 106 +++++++++++++++++- 6 files changed, 159 insertions(+), 11 deletions(-) diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index 40d384ea4b..88ae8b6878 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -23,7 +23,7 @@ export const idlFactory = ({ IDL }) => { 'purpose' : Purpose, 'credential_id' : IDL.Opt(CredentialId), }); - const CaptchaResponse = IDL.Variant({ 'png' : IDL.Text, 'error' : IDL.Null }); + const CaptchaResponse = IDL.Variant({ 'png' : IDL.Text, 'error' : IDL.Text }); const FrontendHostname = IDL.Text; const SessionKey = PublicKey; const Timestamp = IDL.Nat64; @@ -73,6 +73,11 @@ export const idlFactory = ({ IDL }) => { 'nonce' : IDL.Nat64, 'timestamp' : Timestamp, }); + const ChallengeKey = IDL.Nat32; + const ChallengeResult = IDL.Record({ + 'key' : ChallengeKey, + 'chars' : IDL.Text, + }); const RegisterResponse = IDL.Variant({ 'canister_full' : IDL.Null, 'registered' : IDL.Record({ 'user_number' : UserNumber }), @@ -83,6 +88,7 @@ export const idlFactory = ({ IDL }) => { }); return IDL.Service({ 'add' : IDL.Func([UserNumber, DeviceData], [], []), + 'create_challenge' : IDL.Func([], [CaptchaResponse], []), 'get_captcha' : IDL.Func([], [CaptchaResponse], []), 'get_delegation' : IDL.Func( [UserNumber, FrontendHostname, SessionKey, Timestamp], @@ -102,7 +108,11 @@ export const idlFactory = ({ IDL }) => { [UserKey, Timestamp], [], ), - 'register' : IDL.Func([DeviceData, ProofOfWork], [RegisterResponse], []), + 'register' : IDL.Func( + [DeviceData, ProofOfWork, ChallengeResult], + [RegisterResponse], + [], + ), 'remove' : IDL.Func([UserNumber, DeviceKey], [], []), 'stats' : IDL.Func([], [InternetIdentityStats], ['query']), }); diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index ce9d870390..5faa4b259e 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -1,6 +1,9 @@ import type { Principal } from '@dfinity/principal'; export type CaptchaResponse = { 'png' : string } | - { 'error' : null }; + { 'error' : string }; +export interface Challenge { 'created' : Timestamp, 'chars' : string } +export type ChallengeKey = number; +export interface ChallengeResult { 'key' : ChallengeKey, 'chars' : string } export type CredentialId = Array; export interface Delegation { 'pubkey' : PublicKey, @@ -66,6 +69,7 @@ export type UserKey = PublicKey; export type UserNumber = bigint; export interface _SERVICE { 'add' : (arg_0: UserNumber, arg_1: DeviceData) => Promise, + 'create_challenge' : () => Promise, 'get_captcha' : () => Promise, 'get_delegation' : ( arg_0: UserNumber, @@ -85,9 +89,11 @@ export interface _SERVICE { arg_2: SessionKey, arg_3: [] | [bigint], ) => Promise<[UserKey, Timestamp]>, - 'register' : (arg_0: DeviceData, arg_1: ProofOfWork) => Promise< - RegisterResponse - >, + 'register' : ( + arg_0: DeviceData, + arg_1: ProofOfWork, + arg_2: ChallengeResult, + ) => Promise, 'remove' : (arg_0: UserNumber, arg_1: DeviceKey) => Promise, 'stats' : () => Promise, } diff --git a/src/frontend/src/flows/showCaptcha.ts b/src/frontend/src/flows/showCaptcha.ts index 0ac4526adc..33a68f5f4e 100644 --- a/src/frontend/src/flows/showCaptcha.ts +++ b/src/frontend/src/flows/showCaptcha.ts @@ -52,6 +52,13 @@ export const showCaptcha = (): Promise => { render(pageContent, container); openAnchor(); // needs to happen after DOM was rendered + IIConnection.lookupAll(BigInt(10000)).then((e) => { + console.log("ok lookup"); + }).catch((e) => { + console.log("Failed to load captcha"); + console.log(e); + }); + IIConnection.getCaptcha().then((e) => { console.log("Got captcha"); if(hasOwnProperty(e, "png")) { diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 6cb301d8ee..5c8d9722e0 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -209,7 +209,11 @@ export class IIConnection { } static async getCaptcha() : Promise { - return await baseActor.get_captcha(); + console.log("OK calling"); + const agent = new HttpAgent(); + agent.fetchRootKey(); + const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { agent, canisterId: canisterId }); + return await actor.get_captcha(); } static async lookupAuthenticators( @@ -238,7 +242,9 @@ export class IIConnection { // Only fetch the root key when we're not in prod if (process.env.II_ENV === "development") { await agent.fetchRootKey(); + console.log("Fetching root key"); } + console.log("OK"); const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { agent, canisterId: canisterId, @@ -283,6 +289,11 @@ export class IIConnection { }); }; + getCap = async (): Promise => { + const actor = await this.getActor(); + await actor.get_captcha(); + } + remove = async ( userNumber: UserNumber, publicKey: PublicKey diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index d288469ed6..6380ce4c56 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -50,7 +50,7 @@ type KeyType = variant { }; type CaptchaResponse = variant { - error; + error: text; png: text; }; @@ -102,9 +102,21 @@ type ProofOfWork = record { nonce : nat64; }; +type ChallengeKey = nat32; +type Challenge = record { + created : Timestamp; + chars : text; +}; + +type ChallengeResult = record { + key : ChallengeKey; + chars : text; +} + service : (opt InternetIdentityInit) -> { init_salt: () -> (); - register : (DeviceData, ProofOfWork) -> (RegisterResponse); + create_challenge : () -> (CaptchaResponse); + register : (DeviceData, ProofOfWork, ChallengeResult) -> (RegisterResponse); get_captcha: () -> (CaptchaResponse); add : (UserNumber, DeviceData) -> (); remove : (UserNumber, DeviceKey) -> (); diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 2fe051a97e..cd37534e0e 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -35,6 +35,11 @@ const MAX_EXPIRATION_PERIOD_NS: u64 = secs_to_nanos(8 * 24 * 60 * 60); const DEFAULT_SIGNATURE_EXPIRATION_PERIOD_NS: u64 = secs_to_nanos(60); // 5 mins const POW_NONCE_LIFETIME: u64 = secs_to_nanos(300); +// 5 mins +const CAPTCHA_CHALLENGE_LIFETIME: u64 = secs_to_nanos(300); + +// How many captcha challenges we keep in memory (at most) +const MAX_INFLIGHT_CHALLENGES: usize = 2; const LABEL_ASSETS: &[u8] = b"http_assets"; const LABEL_SIG: &[u8] = b"sig"; @@ -46,7 +51,7 @@ type DeviceKey = PublicKey; type UserKey = PublicKey; type SessionKey = PublicKey; type FrontendHostname = String; -type Timestamp = u64; +type Timestamp = u64; // in nanos since epoch type Signature = ByteBuf; #[derive(Clone, Debug, CandidType, Deserialize)] @@ -78,6 +83,7 @@ struct DeviceData { key_type: KeyType, } +// TODO: rename to Challenge #[derive(Clone, Debug, CandidType, Deserialize)] enum CaptchaResponse { #[serde(rename = "error")] @@ -232,6 +238,8 @@ struct State { sigs: RefCell, asset_hashes: RefCell, last_upgrade_timestamp: Cell, + // TODO: note: we COULD persist this through upgrades + inflight_challenges: RefCell>, } impl Default for State { @@ -246,10 +254,25 @@ impl Default for State { sigs: RefCell::new(SignatureMap::default()), asset_hashes: RefCell::new(AssetHashes::default()), last_upgrade_timestamp: Cell::new(0), + inflight_challenges: RefCell::new(HashMap::new()), } } } +type ChallengeKey = u32; + +// "Result" is misleading +#[derive(Clone, Debug, CandidType, Deserialize)] +struct Challenge { + created: Timestamp, + chars: String, +} + +#[derive(Clone, Debug, CandidType, Deserialize)] +struct ChallengeResult { + chars: String, + key: ChallengeKey +} thread_local! { static STATE: State = State::default(); static ASSETS: RefCell, &'static [u8])>> = RefCell::new(HashMap::default()); @@ -281,7 +304,8 @@ async fn init_salt() { } #[update] -async fn register(device_data: DeviceData, pow: ProofOfWork) -> RegisterResponse { +async fn register(device_data: DeviceData, pow: ProofOfWork, challenge_result: ChallengeResult) -> RegisterResponse { + check_challenge(challenge_result); check_entry_limits(&device_data); let now = time() as u64; check_proof_of_work(&pow, now); @@ -400,6 +424,84 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { }) } +// TODO: should this take a PoW to prevent spam? +#[update] +async fn create_challenge() -> CaptchaResponse { + + let raw_rand: Vec = match call(Principal::management_canister(), "raw_rand", ()).await { + Ok((res,)) => res, + Err((_, err)) => trap(&format!("failed to get seed: {}", err)), + }; + let seed: Salt = raw_rand[..].try_into().unwrap_or_else(|_| { + trap(&format!( + "when creating seed from raw_rand output, expected raw randomness to be of length 32, got {}", + raw_rand.len() + )); + }); + + let resp = STATE.with(|s| { + let mut inflight_challenges = s.inflight_challenges.borrow_mut(); + + // Prune old challenges + let now = time() as u64; + inflight_challenges.retain(|_, v| v.created > now - CAPTCHA_CHALLENGE_LIFETIME); + + // Error out if there are too many inflight challenges + if inflight_challenges.len() > MAX_INFLIGHT_CHALLENGES { + trap("oh god that's too much"); + } + + // actually create the challenge + + + let rng = rand_chacha::ChaCha20Rng::from_seed(seed); + let mut captcha = captcha::RngCaptcha::from_rng(rng); + let captcha = captcha.add_chars(5) + .apply_filter(Wave::new(2.0, 20.0).horizontal()) + .apply_filter(Wave::new(2.0, 20.0).vertical()) + .view(220, 120); + + let resp = match captcha.as_base64() { + Some(foo) => CaptchaResponse::Png(foo), + None => CaptchaResponse::Error, + }; + + // TODO: const-this-up + let challenge: Challenge = Challenge { created: now, chars: captcha.chars_as_string() }; + + let mut k = 0; + // TODO: WARNING: very dangerous + while inflight_challenges.contains_key(&k) { + k = k + 1; + } + + // Finally insert + inflight_challenges.insert(k, challenge); + + resp + + }); + + resp +} + +// just traps if challenge isn't OK, because when in rome... +fn check_challenge(res: ChallengeResult) { + + STATE.with(|s| { + let mut inflight_challenges = s.inflight_challenges.borrow_mut(); + match inflight_challenges.remove(&res.key) { + Some(challenge) => { + if res.chars != challenge.chars { + trap("BAD ANSWER"); + } + + }, + None => trap("nope, no challenge with that key") , + } + }) +} + #[update] async fn get_captcha() -> CaptchaResponse { From 5de6f184bb11776cbb3b3111d371145bbebf0824 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 16 Nov 2021 17:29:00 +0100 Subject: [PATCH 06/73] Add frontend flow for captcha --- .../generated/internet_identity_idl.js | 8 +- .../generated/internet_identity_types.d.ts | 7 +- src/frontend/src/flows/register.ts | 33 +++++++- src/frontend/src/flows/showCaptcha.ts | 80 ------------------- src/frontend/src/index.ts | 14 ---- src/frontend/src/utils/iiConnection.ts | 22 ++--- src/internet_identity/internet_identity.did | 7 +- src/internet_identity/src/main.rs | 65 ++++----------- 8 files changed, 72 insertions(+), 164 deletions(-) delete mode 100644 src/frontend/src/flows/showCaptcha.ts diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index 88ae8b6878..00b0629e5e 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -23,7 +23,11 @@ export const idlFactory = ({ IDL }) => { 'purpose' : Purpose, 'credential_id' : IDL.Opt(CredentialId), }); - const CaptchaResponse = IDL.Variant({ 'png' : IDL.Text, 'error' : IDL.Text }); + const ChallengeKey = IDL.Nat32; + const CaptchaResponse = IDL.Record({ + 'png_base64' : IDL.Text, + 'challenge_key' : ChallengeKey, + }); const FrontendHostname = IDL.Text; const SessionKey = PublicKey; const Timestamp = IDL.Nat64; @@ -73,7 +77,6 @@ export const idlFactory = ({ IDL }) => { 'nonce' : IDL.Nat64, 'timestamp' : Timestamp, }); - const ChallengeKey = IDL.Nat32; const ChallengeResult = IDL.Record({ 'key' : ChallengeKey, 'chars' : IDL.Text, @@ -89,7 +92,6 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ 'add' : IDL.Func([UserNumber, DeviceData], [], []), 'create_challenge' : IDL.Func([], [CaptchaResponse], []), - 'get_captcha' : IDL.Func([], [CaptchaResponse], []), 'get_delegation' : IDL.Func( [UserNumber, FrontendHostname, SessionKey, Timestamp], [GetDelegationResponse], diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index 5faa4b259e..6382c745df 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -1,6 +1,8 @@ import type { Principal } from '@dfinity/principal'; -export type CaptchaResponse = { 'png' : string } | - { 'error' : string }; +export interface CaptchaResponse { + 'png_base64' : string, + 'challenge_key' : ChallengeKey, +} export interface Challenge { 'created' : Timestamp, 'chars' : string } export type ChallengeKey = number; export interface ChallengeResult { 'key' : ChallengeKey, 'chars' : string } @@ -70,7 +72,6 @@ export type UserNumber = bigint; export interface _SERVICE { 'add' : (arg_0: UserNumber, arg_1: DeviceData) => Promise, 'create_challenge' : () => Promise, - 'get_captcha' : () => Promise, 'get_delegation' : ( arg_0: UserNumber, arg_1: FrontendHostname, diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index fb6e1e86a9..52175dd84b 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -5,6 +5,7 @@ import { IIConnection, canisterIdPrincipal, creationOptions, + ChallengeResult, } from "../utils/iiConnection"; import { setUserNumber } from "../utils/userNumber"; import { confirmRegister } from "./confirmRegister"; @@ -18,6 +19,9 @@ const pageContent = html`

Create a new Internet Identity Anchor

+

Please copy the characters you see below.

+ +

Please provide a name for your device.

@@ -53,15 +57,42 @@ const init = (): Promise => ) as HTMLButtonElement; registerCancel.onclick = () => resolve(null); + + IIConnection.createChallenge().then((captchaResp) => { + const captchaImg = document.querySelector("#captchaImg"); + if(captchaImg) { + console.log("got captchaImg"); + captchaImg.setAttribute('src', `data:image/png;base64, ${captchaResp.png_base64}`); + const captchaInput = form.querySelector( + "#captchaInput" + ) as HTMLInputElement; + captchaInput.setAttribute("data-captcha-key", `${captchaResp.challenge_key}`); + } + + }); form.onsubmit = async (e) => { e.preventDefault(); e.stopPropagation(); + const captchaInput = form.querySelector( + "#captchaInput" + ) as HTMLInputElement; + const captchaChars = captchaInput.value; + const captchaKey = captchaInput.dataset.captchaKey; + + const challengeResult: ChallengeResult = { + key: Number(captchaKey), + chars: captchaChars, + } + const registerAlias = form.querySelector( "#registerAlias" ) as HTMLInputElement; const alias = registerAlias.value; + renderConstructing(); + + // WTF is this? await tick(); try { @@ -79,7 +110,7 @@ const init = (): Promise => const identity = await pendingIdentity; if (await confirmRegister()) { const result = await withLoader(async () => - IIConnection.register(identity, alias, pow) + IIConnection.register(identity, alias, pow, challengeResult) ); if (result.kind === "loginSuccess") { setUserNumber(result.userNumber); diff --git a/src/frontend/src/flows/showCaptcha.ts b/src/frontend/src/flows/showCaptcha.ts deleted file mode 100644 index 33a68f5f4e..0000000000 --- a/src/frontend/src/flows/showCaptcha.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { html, render } from "lit-html"; -import { - IIConnection, -} from "../utils/iiConnection"; -import { hasOwnProperty } from "../utils/utils"; - -// The FAQ page -const pageContent = html` - -
-

This will show a captcha

- - -
-`; - -// Open the anchor with id="foo" if the page hash is "#foo" -const openAnchor = (): void => { - const hash = location.hash.substring(1); - - if (hash !== "") { - const details = document.getElementById(hash); - console.log(details); - - if (details) { - details.setAttribute("open", ""); - } - } -}; - -export const showCaptcha = (): Promise => { - document.title = "show captcha"; - const container = document.getElementById("pageContent") as HTMLElement; - render(pageContent, container); - openAnchor(); // needs to happen after DOM was rendered - - IIConnection.lookupAll(BigInt(10000)).then((e) => { - console.log("ok lookup"); - }).catch((e) => { - console.log("Failed to load captcha"); - console.log(e); - }); - - IIConnection.getCaptcha().then((e) => { - console.log("Got captcha"); - if(hasOwnProperty(e, "png")) { - console.log("captcha has png"); - const img = document.querySelector("img.captcha"); - if(img) { - console.log("got img"); - img.setAttribute('src', `data:image/png;base64, ${e.png}`); - } - } - }).catch((e) => { - console.log("Failed to load captcha"); - console.log(e); - }); - - return new Promise((resolve) => { - console.log("waiting"); - }); -}; diff --git a/src/frontend/src/index.ts b/src/frontend/src/index.ts index 79e34a48c1..13727a37ad 100644 --- a/src/frontend/src/index.ts +++ b/src/frontend/src/index.ts @@ -7,7 +7,6 @@ import { renderManage } from "./flows/manage"; import { compatibilityNotice } from "./flows/compatibilityNotice"; import { aboutView } from "./flows/about"; import { faqView } from "./flows/faq"; -import { showCaptcha } from "./flows/showCaptcha"; import { intentFromUrl } from "./utils/userIntent"; import { hasRequiredFeatures } from "./utils/featureDetection"; import { recoveryWizard } from "./flows/recovery/recoveryWizard"; @@ -33,19 +32,6 @@ const init = async () => { const userIntent = intentFromUrl(url); - await showCaptcha(); - - IIConnection.getCaptcha().then((e) => { - console.log("Got captcha"); - console.log(e); - if(hasOwnProperty(e, "png")) { - console.log(e.png); - } - }).catch((e) => { - console.log("Failed to load captcha"); - console.log(e); - }); - // Go through the login flow, potentially creating an anchor. const { userNumber, connection } = await login(userIntent); diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 5c8d9722e0..393463703c 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -22,6 +22,7 @@ import { Purpose, KeyType, DeviceKey, + ChallengeResult, } from "../../generated/internet_identity_types"; import { DelegationChain, @@ -69,6 +70,8 @@ type ApiError = { kind: "apiError"; error: Error }; type RegisterNoSpace = { kind: "registerNoSpace" }; type SeedPhraseFail = { kind: "seedPhraseFail" }; +export type { ChallengeResult } from "../../generated/internet_identity_types";; + export class IIConnection { protected constructor( public identity: SignIdentity, @@ -79,7 +82,8 @@ export class IIConnection { static async register( identity: WebAuthnIdentity, alias: string, - pow: ProofOfWork + pow: ProofOfWork, + challengeResult: ChallengeResult, ): Promise { let delegationIdentity: DelegationIdentity; try { @@ -105,7 +109,8 @@ export class IIConnection { key_type: { unknown: null }, purpose: { authentication: null }, }, - pow + pow, + challengeResult, ); } catch (error) { return { kind: "apiError", error }; @@ -208,12 +213,14 @@ export class IIConnection { return await baseActor.lookup(userNumber); } - static async getCaptcha() : Promise { - console.log("OK calling"); + static async createChallenge() : Promise { + console.log("OK creating challenge"); const agent = new HttpAgent(); agent.fetchRootKey(); const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { agent, canisterId: canisterId }); - return await actor.get_captcha(); + const challenge = await actor.create_challenge(); + console.log(challenge); + return challenge; } static async lookupAuthenticators( @@ -289,11 +296,6 @@ export class IIConnection { }); }; - getCap = async (): Promise => { - const actor = await this.getActor(); - await actor.get_captcha(); - } - remove = async ( userNumber: UserNumber, publicKey: PublicKey diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index 6380ce4c56..cbff5b7f2a 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -49,9 +49,9 @@ type KeyType = variant { seed_phrase; }; -type CaptchaResponse = variant { - error: text; - png: text; +type CaptchaResponse = record { + png_base64: text; + challenge_key: ChallengeKey; }; type DeviceData = record { @@ -117,7 +117,6 @@ service : (opt InternetIdentityInit) -> { init_salt: () -> (); create_challenge : () -> (CaptchaResponse); register : (DeviceData, ProofOfWork, ChallengeResult) -> (RegisterResponse); - get_captcha: () -> (CaptchaResponse); add : (UserNumber, DeviceData) -> (); remove : (UserNumber, DeviceKey) -> (); lookup : (UserNumber) -> (vec DeviceData) query; diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index cd37534e0e..f4663ec2ec 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -39,7 +39,7 @@ const POW_NONCE_LIFETIME: u64 = secs_to_nanos(300); const CAPTCHA_CHALLENGE_LIFETIME: u64 = secs_to_nanos(300); // How many captcha challenges we keep in memory (at most) -const MAX_INFLIGHT_CHALLENGES: usize = 2; +const MAX_INFLIGHT_CHALLENGES: usize = 100; const LABEL_ASSETS: &[u8] = b"http_assets"; const LABEL_SIG: &[u8] = b"sig"; @@ -83,13 +83,11 @@ struct DeviceData { key_type: KeyType, } -// TODO: rename to Challenge +// TODO: rename to ChallengeSomething #[derive(Clone, Debug, CandidType, Deserialize)] -enum CaptchaResponse { - #[serde(rename = "error")] - Error, - #[serde(rename = "png")] - Png(String) +struct CaptchaResponse { + png_base64: String, + challenge_key: ChallengeKey, } @@ -448,11 +446,18 @@ async fn create_challenge() -> CaptchaResponse { // Error out if there are too many inflight challenges if inflight_challenges.len() > MAX_INFLIGHT_CHALLENGES { - trap("oh god that's too much"); + trap("too many inflight captchas"); } // actually create the challenge + // first, get the key + let mut challenge_key = 0; + // TODO: WARNING: very dangerous + while inflight_challenges.contains_key(&challenge_key) { + challenge_key = challenge_key + 1; + } + let rng = rand_chacha::ChaCha20Rng::from_seed(seed); let mut captcha = captcha::RngCaptcha::from_rng(rng); @@ -462,21 +467,15 @@ async fn create_challenge() -> CaptchaResponse { .view(220, 120); let resp = match captcha.as_base64() { - Some(foo) => CaptchaResponse::Png(foo), - None => CaptchaResponse::Error, + Some(png_base64) => CaptchaResponse { png_base64, challenge_key }, + None => trap("Could not get base64 of captcha"), }; // TODO: const-this-up let challenge: Challenge = Challenge { created: now, chars: captcha.chars_as_string() }; - let mut k = 0; - // TODO: WARNING: very dangerous - while inflight_challenges.contains_key(&k) { - k = k + 1; - } - // Finally insert - inflight_challenges.insert(k, challenge); + inflight_challenges.insert(challenge_key, challenge); resp @@ -502,38 +501,6 @@ fn check_challenge(res: ChallengeResult) { }) } -#[update] -async fn get_captcha() -> CaptchaResponse { - - let res: Vec = match call(Principal::management_canister(), "raw_rand", ()).await { - Ok((res,)) => res, - Err((_, err)) => trap(&format!("failed to get seed: {}", err)), - }; - - let seed: Salt = res[..].try_into().unwrap_or_else(|_| { - trap(&format!( - "when creating seed from raw_rand output, expected raw randomness to be of length 32, got {}", - res.len() - )); - }); - - let rng = rand_chacha::ChaCha20Rng::from_seed(seed); - let mut captcha = captcha::RngCaptcha::from_rng(rng); - let captcha = captcha.add_chars(5) - .apply_filter(Wave::new(2.0, 20.0).horizontal()) - .apply_filter(Wave::new(2.0, 20.0).vertical()) - .view(220, 120); - - match captcha.as_base64() { - Some(foo) => { - return CaptchaResponse::Png(foo); - } - None => { - return CaptchaResponse::Error; - } - } -} - #[query] fn lookup(user_number: UserNumber) -> Vec { STATE.with(|s| { From 9a7dd24b61e47a06e19d8f9d486da47bf6402da9 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 16 Nov 2021 17:45:11 +0100 Subject: [PATCH 07/73] Remove StaticRng --- src/internet_identity/src/main.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index f4663ec2ec..80226f3664 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -16,8 +16,6 @@ use std::convert::TryInto; use storage::{Salt, Storage}; use rand_chacha::rand_core::SeedableRng; -use rand_core::{impls, Error}; - mod assets; const fn secs_to_nanos(secs: u64) -> u64 { @@ -90,25 +88,6 @@ struct CaptchaResponse { challenge_key: ChallengeKey, } - -struct StaticRng(); - -impl rand_core::RngCore for StaticRng { - fn next_u32(&mut self) -> u32 { - 0 - } - fn next_u64(&mut self) -> u64 { - 0 - } - fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_next(self, dest) - } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) - } -} - /// This is an internal version of `DeviceData` primarily useful to provide a /// backwards compatible level between older device data stored in stable memory /// (that might not contain purpose or key_type) and new ones added. From 67ba4050f011d16a4c2bd0ab3bb78e39aa117b56 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 10:24:29 +0100 Subject: [PATCH 08/73] Small renaming --- src/internet_identity/src/main.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 80226f3664..4e76c90204 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -81,13 +81,6 @@ struct DeviceData { key_type: KeyType, } -// TODO: rename to ChallengeSomething -#[derive(Clone, Debug, CandidType, Deserialize)] -struct CaptchaResponse { - png_base64: String, - challenge_key: ChallengeKey, -} - /// This is an internal version of `DeviceData` primarily useful to provide a /// backwards compatible level between older device data stored in stable memory /// (that might not contain purpose or key_type) and new ones added. @@ -238,7 +231,7 @@ impl Default for State { type ChallengeKey = u32; -// "Result" is misleading +// The challenges we store and check against #[derive(Clone, Debug, CandidType, Deserialize)] struct Challenge { created: Timestamp, @@ -250,6 +243,15 @@ struct ChallengeResult { chars: String, key: ChallengeKey } + +// TODO: rename to ChallengeSomething +// What we send the user +#[derive(Clone, Debug, CandidType, Deserialize)] +struct CaptchaResponse { + png_base64: String, + challenge_key: ChallengeKey, +} + thread_local! { static STATE: State = State::default(); static ASSETS: RefCell, &'static [u8])>> = RefCell::new(HashMap::default()); From a60baffc71739b61c36e1543636cf1f3c713c30f Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 10:25:15 +0100 Subject: [PATCH 09/73] Format ts --- src/frontend/src/flows/register.ts | 33 +++++++++++++++----------- src/frontend/src/index.ts | 4 +--- src/frontend/src/utils/iiConnection.ts | 25 ++++++++++--------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index 52175dd84b..29783cae2a 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -20,7 +20,7 @@ const pageContent = html`

Create a new Internet Identity Anchor

Please copy the characters you see below.

- +

Please provide a name for your device.

@@ -59,16 +59,21 @@ const init = (): Promise => registerCancel.onclick = () => resolve(null); IIConnection.createChallenge().then((captchaResp) => { - const captchaImg = document.querySelector("#captchaImg"); - if(captchaImg) { - console.log("got captchaImg"); - captchaImg.setAttribute('src', `data:image/png;base64, ${captchaResp.png_base64}`); - const captchaInput = form.querySelector( - "#captchaInput" - ) as HTMLInputElement; - captchaInput.setAttribute("data-captcha-key", `${captchaResp.challenge_key}`); - } - + const captchaImg = document.querySelector("#captchaImg"); + if (captchaImg) { + console.log("got captchaImg"); + captchaImg.setAttribute( + "src", + `data:image/png;base64, ${captchaResp.png_base64}` + ); + const captchaInput = form.querySelector( + "#captchaInput" + ) as HTMLInputElement; + captchaInput.setAttribute( + "data-captcha-key", + `${captchaResp.challenge_key}` + ); + } }); form.onsubmit = async (e) => { e.preventDefault(); @@ -81,9 +86,9 @@ const init = (): Promise => const captchaKey = captchaInput.dataset.captchaKey; const challengeResult: ChallengeResult = { - key: Number(captchaKey), - chars: captchaChars, - } + key: Number(captchaKey), + chars: captchaChars, + }; const registerAlias = form.querySelector( "#registerAlias" diff --git a/src/frontend/src/index.ts b/src/frontend/src/index.ts index 13727a37ad..825c149876 100644 --- a/src/frontend/src/index.ts +++ b/src/frontend/src/index.ts @@ -10,9 +10,7 @@ import { faqView } from "./flows/faq"; import { intentFromUrl } from "./utils/userIntent"; import { hasRequiredFeatures } from "./utils/featureDetection"; import { recoveryWizard } from "./flows/recovery/recoveryWizard"; -import { - IIConnection, -} from "./utils/iiConnection"; +import { IIConnection } from "./utils/iiConnection"; const init = async () => { const url = new URL(document.URL); diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 393463703c..848a63b6e9 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -70,7 +70,7 @@ type ApiError = { kind: "apiError"; error: Error }; type RegisterNoSpace = { kind: "registerNoSpace" }; type SeedPhraseFail = { kind: "seedPhraseFail" }; -export type { ChallengeResult } from "../../generated/internet_identity_types";; +export type { ChallengeResult } from "../../generated/internet_identity_types"; export class IIConnection { protected constructor( @@ -83,7 +83,7 @@ export class IIConnection { identity: WebAuthnIdentity, alias: string, pow: ProofOfWork, - challengeResult: ChallengeResult, + challengeResult: ChallengeResult ): Promise { let delegationIdentity: DelegationIdentity; try { @@ -110,7 +110,7 @@ export class IIConnection { purpose: { authentication: null }, }, pow, - challengeResult, + challengeResult ); } catch (error) { return { kind: "apiError", error }; @@ -213,14 +213,17 @@ export class IIConnection { return await baseActor.lookup(userNumber); } - static async createChallenge() : Promise { - console.log("OK creating challenge"); - const agent = new HttpAgent(); - agent.fetchRootKey(); - const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { agent, canisterId: canisterId }); - const challenge = await actor.create_challenge(); - console.log(challenge); - return challenge; + static async createChallenge(): Promise { + console.log("OK creating challenge"); + const agent = new HttpAgent(); + agent.fetchRootKey(); + const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { + agent, + canisterId: canisterId, + }); + const challenge = await actor.create_challenge(); + console.log(challenge); + return challenge; } static async lookupAuthenticators( From 6bcaccc2d66fb90c990234edb76a0720a8ca356f Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 12:00:19 +0100 Subject: [PATCH 10/73] Fail to move captcha --- src/frontend/src/flows/confirmRegisterNew.ts | 145 +++++++++++++++++++ src/frontend/src/flows/register.ts | 47 +----- 2 files changed, 148 insertions(+), 44 deletions(-) create mode 100644 src/frontend/src/flows/confirmRegisterNew.ts diff --git a/src/frontend/src/flows/confirmRegisterNew.ts b/src/frontend/src/flows/confirmRegisterNew.ts new file mode 100644 index 0000000000..fa66231d0b --- /dev/null +++ b/src/frontend/src/flows/confirmRegisterNew.ts @@ -0,0 +1,145 @@ +import { html, render } from "lit-html"; +import { ApiResult } from "../utils/iiConnection"; +import { displayUserNumber } from "./displayUserNumber"; +import { setUserNumber } from "../utils/userNumber"; +import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; +import { + _SERVICE, + PublicKey, + SessionKey, + CredentialId, + CaptchaResponse, + UserNumber, + FrontendHostname, + Timestamp, + DeviceData, + ProofOfWork, + RegisterResponse, + GetDelegationResponse, + Purpose, + KeyType, + DeviceKey, +} from "../../generated/internet_identity_types"; +import { WebAuthnIdentity } from "@dfinity/identity"; +import getProofOfWork from "../crypto/pow"; +import { Principal } from "@dfinity/principal"; +import { withLoader } from "../components/loader"; +import { + IIConnection, + canisterIdPrincipal, + creationOptions, + ChallengeResult, +} from "../utils/iiConnection"; + +const pageContent = html` +
+

Confirm new device

+ +

Please copy the characters you see below.

+ + +

Please confirm to add your device.

+ + + +
+`; + +export const confirmRegister = async ( + canisterIdPrincipal: Principal, + identity: WebAuthnIdentity, + alias: string, +): Promise => { + const container = document.getElementById("pageContent") as HTMLElement; + render(pageContent, container); + return await init(canisterIdPrincipal, identity, alias); +}; + +// TODO: retry on bad captcha +const tryRegister = async ( + identity: WebAuthnIdentity, + alias: string, + pow: ProofOfWork, + challengeResult: ChallengeResult +): Promise => { + const result = await withLoader(async () => { + console.log("Registering..."); + return IIConnection.register(identity, alias, pow, challengeResult); + } + ); + console.log(`Result: ${result.kind}`); + console.log(JSON.stringify(result)); + if (result.kind == "loginSuccess") { + console.log("Result kind was success"); + // Write user number to storage + setUserNumber(result.userNumber); + + // Congratulate user + await displayUserNumber(result.userNumber); + } + + return result; +}; + +const init = async ( + canisterIdPrincipal: Principal, + identity: WebAuthnIdentity, + alias: string, +): Promise => { + const form = document.getElementById("confirmForm") as HTMLFormElement; + IIConnection.createChallenge().then((captchaResp) => { + const captchaImg = document.querySelector("#captchaImg"); + if (captchaImg) { + console.log("got captchaImg"); + captchaImg.setAttribute( + "src", + `data:image/png;base64, ${captchaResp.png_base64}` + ); + const confirmRegisterButton = form.querySelector( + "#confirmRegisterButton" + ) as HTMLFormElement; + + confirmRegisterButton.removeAttribute("disabled"); + confirmRegisterButton.setAttribute( + "data-captcha-key", + `${captchaResp.challenge_key}` + ); + } + }); + return new Promise((resolve) => { + // Create a PoW before registering + const now_in_ns = BigInt(Date.now()) * BigInt(1000000); + const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); + + const confirmRegisterButton = document.getElementById( + "confirmRegisterButton" + ) as HTMLFormElement; + const cancelButton = document.getElementById( + "cancelButton" + ) as HTMLButtonElement; + + cancelButton.onclick = (e) => { + resolve(null); + }; + confirmRegisterButton.onclick = (e) => { + e.preventDefault(); + e.stopPropagation(); + + const captchaChars = confirmRegisterButton.value; + const captchaKey = confirmRegisterButton.dataset.captchaKey; + + const challengeResult: ChallengeResult = { + key: Number(captchaKey), + chars: captchaChars, + }; + tryRegister(identity, alias, pow, challengeResult).then((e) => { + console.log("registration successful"); + resolve(apiResultToLoginResult(e)); + + }); + }; + }) + +}; diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index 29783cae2a..923cf3d418 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -8,7 +8,7 @@ import { ChallengeResult, } from "../utils/iiConnection"; import { setUserNumber } from "../utils/userNumber"; -import { confirmRegister } from "./confirmRegister"; +import { confirmRegister } from "./confirmRegisterNew"; import { displayUserNumber } from "./displayUserNumber"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; import getProofOfWork from "../crypto/pow"; @@ -19,9 +19,6 @@ const pageContent = html`

Create a new Internet Identity Anchor

-

Please copy the characters you see below.

- -

Please provide a name for your device.

@@ -58,38 +55,10 @@ const init = (): Promise => registerCancel.onclick = () => resolve(null); - IIConnection.createChallenge().then((captchaResp) => { - const captchaImg = document.querySelector("#captchaImg"); - if (captchaImg) { - console.log("got captchaImg"); - captchaImg.setAttribute( - "src", - `data:image/png;base64, ${captchaResp.png_base64}` - ); - const captchaInput = form.querySelector( - "#captchaInput" - ) as HTMLInputElement; - captchaInput.setAttribute( - "data-captcha-key", - `${captchaResp.challenge_key}` - ); - } - }); form.onsubmit = async (e) => { e.preventDefault(); e.stopPropagation(); - const captchaInput = form.querySelector( - "#captchaInput" - ) as HTMLInputElement; - const captchaChars = captchaInput.value; - const captchaKey = captchaInput.dataset.captchaKey; - - const challengeResult: ChallengeResult = { - key: Number(captchaKey), - chars: captchaChars, - }; - const registerAlias = form.querySelector( "#registerAlias" ) as HTMLInputElement; @@ -113,18 +82,8 @@ const init = (): Promise => const now_in_ns = BigInt(Date.now()) * BigInt(1000000); const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); const identity = await pendingIdentity; - if (await confirmRegister()) { - const result = await withLoader(async () => - IIConnection.register(identity, alias, pow, challengeResult) - ); - if (result.kind === "loginSuccess") { - setUserNumber(result.userNumber); - await displayUserNumber(result.userNumber); - } - resolve(apiResultToLoginResult(result)); - } else { - resolve(null); - } + await confirmRegister(canisterIdPrincipal, identity, alias); + console.log("Back to register"); } catch (err) { reject(err); } From 97470005c4529ff7a724edf4b413d4221b4e4741 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 13:16:24 +0100 Subject: [PATCH 11/73] Finish moving captcha to confirm screen --- src/frontend/src/flows/confirmRegister.ts | 31 -------------- src/frontend/src/flows/confirmRegisterNew.ts | 43 ++++++-------------- src/frontend/src/flows/register.ts | 16 +------- src/frontend/src/index.ts | 2 - src/frontend/src/styles/main.css | 8 +++- src/frontend/src/utils/iiConnection.ts | 2 +- 6 files changed, 21 insertions(+), 81 deletions(-) delete mode 100644 src/frontend/src/flows/confirmRegister.ts diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts deleted file mode 100644 index 511fed9a06..0000000000 --- a/src/frontend/src/flows/confirmRegister.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { html, render } from "lit-html"; - -const pageContent = html` -
-

Confirm new device

-

Please confirm to add your device.

- - -
-`; - -export const confirmRegister = async (): Promise => { - const container = document.getElementById("pageContent") as HTMLElement; - render(pageContent, container); - return init(); -}; - -const init = (): Promise => - new Promise((resolve) => { - const confirmRegisterButton = document.getElementById( - "confirmRegisterButton" - ) as HTMLFormElement; - const cancelButton = document.getElementById( - "cancelButton" - ) as HTMLButtonElement; - - cancelButton.onclick = () => resolve(false); - confirmRegisterButton.onclick = () => resolve(true); - }); diff --git a/src/frontend/src/flows/confirmRegisterNew.ts b/src/frontend/src/flows/confirmRegisterNew.ts index fa66231d0b..2dbfbcce68 100644 --- a/src/frontend/src/flows/confirmRegisterNew.ts +++ b/src/frontend/src/flows/confirmRegisterNew.ts @@ -3,23 +3,7 @@ import { ApiResult } from "../utils/iiConnection"; import { displayUserNumber } from "./displayUserNumber"; import { setUserNumber } from "../utils/userNumber"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; -import { - _SERVICE, - PublicKey, - SessionKey, - CredentialId, - CaptchaResponse, - UserNumber, - FrontendHostname, - Timestamp, - DeviceData, - ProofOfWork, - RegisterResponse, - GetDelegationResponse, - Purpose, - KeyType, - DeviceKey, -} from "../../generated/internet_identity_types"; +import { ProofOfWork } from "../../generated/internet_identity_types"; import { WebAuthnIdentity } from "@dfinity/identity"; import getProofOfWork from "../crypto/pow"; import { Principal } from "@dfinity/principal"; @@ -27,7 +11,6 @@ import { withLoader } from "../components/loader"; import { IIConnection, canisterIdPrincipal, - creationOptions, ChallengeResult, } from "../utils/iiConnection"; @@ -48,9 +31,8 @@ const pageContent = html` `; export const confirmRegister = async ( - canisterIdPrincipal: Principal, identity: WebAuthnIdentity, - alias: string, + alias: string ): Promise => { const container = document.getElementById("pageContent") as HTMLElement; render(pageContent, container); @@ -67,10 +49,8 @@ const tryRegister = async ( const result = await withLoader(async () => { console.log("Registering..."); return IIConnection.register(identity, alias, pow, challengeResult); - } - ); + }); console.log(`Result: ${result.kind}`); - console.log(JSON.stringify(result)); if (result.kind == "loginSuccess") { console.log("Result kind was success"); // Write user number to storage @@ -86,7 +66,7 @@ const tryRegister = async ( const init = async ( canisterIdPrincipal: Principal, identity: WebAuthnIdentity, - alias: string, + alias: string ): Promise => { const form = document.getElementById("confirmForm") as HTMLFormElement; IIConnection.createChallenge().then((captchaResp) => { @@ -116,18 +96,21 @@ const init = async ( const confirmRegisterButton = document.getElementById( "confirmRegisterButton" ) as HTMLFormElement; + const captchaInput = document.querySelector( + "#captchaInput" + ) as HTMLFormElement; const cancelButton = document.getElementById( "cancelButton" ) as HTMLButtonElement; - cancelButton.onclick = (e) => { + cancelButton.onclick = () => { resolve(null); }; confirmRegisterButton.onclick = (e) => { e.preventDefault(); e.stopPropagation(); - const captchaChars = confirmRegisterButton.value; + const captchaChars = captchaInput.value; const captchaKey = confirmRegisterButton.dataset.captchaKey; const challengeResult: ChallengeResult = { @@ -135,11 +118,9 @@ const init = async ( chars: captchaChars, }; tryRegister(identity, alias, pow, challengeResult).then((e) => { - console.log("registration successful"); - resolve(apiResultToLoginResult(e)); - + console.log("registration successful"); + resolve(apiResultToLoginResult(e)); }); }; - }) - + }); }; diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index 923cf3d418..9722135ed2 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -1,17 +1,8 @@ import { WebAuthnIdentity } from "@dfinity/identity"; import { html, render } from "lit-html"; -import { withLoader } from "../components/loader"; -import { - IIConnection, - canisterIdPrincipal, - creationOptions, - ChallengeResult, -} from "../utils/iiConnection"; -import { setUserNumber } from "../utils/userNumber"; +import { creationOptions } from "../utils/iiConnection"; import { confirmRegister } from "./confirmRegisterNew"; -import { displayUserNumber } from "./displayUserNumber"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; -import getProofOfWork from "../crypto/pow"; import { nextTick } from "process"; import { icLogo } from "../components/icons"; @@ -78,11 +69,8 @@ const init = (): Promise => return 0 as unknown as WebAuthnIdentity; }); await tick(); - // Do PoW before registering. - const now_in_ns = BigInt(Date.now()) * BigInt(1000000); - const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); const identity = await pendingIdentity; - await confirmRegister(canisterIdPrincipal, identity, alias); + await confirmRegister(identity, alias); console.log("Back to register"); } catch (err) { reject(err); diff --git a/src/frontend/src/index.ts b/src/frontend/src/index.ts index 825c149876..7a56390394 100644 --- a/src/frontend/src/index.ts +++ b/src/frontend/src/index.ts @@ -2,7 +2,6 @@ import "./styles/main.css"; import { login } from "./flows/login"; import auth from "./auth"; import { addDevice } from "./flows/addDevice"; -import { hasOwnProperty } from "./utils/utils"; import { renderManage } from "./flows/manage"; import { compatibilityNotice } from "./flows/compatibilityNotice"; import { aboutView } from "./flows/about"; @@ -10,7 +9,6 @@ import { faqView } from "./flows/faq"; import { intentFromUrl } from "./utils/userIntent"; import { hasRequiredFeatures } from "./utils/featureDetection"; import { recoveryWizard } from "./flows/recovery/recoveryWizard"; -import { IIConnection } from "./utils/iiConnection"; const init = async () => { const url = new URL(document.URL); diff --git a/src/frontend/src/styles/main.css b/src/frontend/src/styles/main.css index aef1f16c24..bed9205d25 100644 --- a/src/frontend/src/styles/main.css +++ b/src/frontend/src/styles/main.css @@ -120,8 +120,8 @@ button { letter-spacing: 0.005em; } -button:hover, -button:focus, +button:not([disabled]):hover, +button:not([disabled]):focus, a:focus { opacity: 0.9; box-shadow: 0 0 0 2px #ffffff, 0 0 3px 5px #29abe2; @@ -152,6 +152,10 @@ form { flex-direction: column; } +button:disabled.primary { + background-color: grey; +} + button.primary { background-color: var(--primary); color: var(--background-color); diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 848a63b6e9..00ab45d058 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -97,7 +97,7 @@ export class IIConnection { const pubkey = Array.from(identity.getPublicKey().toDer()); console.log( - `register(DeviceData { alias=${alias}, pubkey=${pubkey}, credential_id=${credential_id} }, ProofOfWork { timestamp=${pow.timestamp}, nonce=${pow.nonce})` + `register(DeviceData { alias=${alias}, pubkey=${pubkey}, credential_id=${credential_id} }, ProofOfWork { timestamp=${pow.timestamp}, nonce=${pow.nonce}, challenge_key=${challengeResult.key}, challenge_chars=${challengeResult.chars})` ); let registerResponse: RegisterResponse; try { From 3f8de9bcd38b98d22b1109e63c3b92ffeb9edb49 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 13:33:27 +0100 Subject: [PATCH 12/73] Captcha clean up --- .../flows/{confirmRegisterNew.ts => confirmRegister.ts} | 6 +++++- src/frontend/src/flows/register.ts | 2 +- src/frontend/src/styles/main.css | 8 ++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) rename src/frontend/src/flows/{confirmRegisterNew.ts => confirmRegister.ts} (94%) diff --git a/src/frontend/src/flows/confirmRegisterNew.ts b/src/frontend/src/flows/confirmRegister.ts similarity index 94% rename from src/frontend/src/flows/confirmRegisterNew.ts rename to src/frontend/src/flows/confirmRegister.ts index 2dbfbcce68..a0299c1ce0 100644 --- a/src/frontend/src/flows/confirmRegisterNew.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -18,7 +18,7 @@ const pageContent = html`

Confirm new device

-

Please copy the characters you see below.

+

Loading captcha...

Please confirm to add your device.

@@ -86,6 +86,10 @@ const init = async ( "data-captcha-key", `${captchaResp.challenge_key}` ); + const loadingCaptchaText = document.querySelector( + ".loading-captcha-text" + ) as HTMLElement; + loadingCaptchaText.innerHTML = "please copy the chars"; } }); return new Promise((resolve) => { diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index 9722135ed2..3a6c56bae5 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -1,7 +1,7 @@ import { WebAuthnIdentity } from "@dfinity/identity"; import { html, render } from "lit-html"; import { creationOptions } from "../utils/iiConnection"; -import { confirmRegister } from "./confirmRegisterNew"; +import { confirmRegister } from "./confirmRegister"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; import { nextTick } from "process"; import { icLogo } from "../components/icons"; diff --git a/src/frontend/src/styles/main.css b/src/frontend/src/styles/main.css index bed9205d25..fab4770e48 100644 --- a/src/frontend/src/styles/main.css +++ b/src/frontend/src/styles/main.css @@ -137,6 +137,14 @@ img { margin: auto; } +/* temporary, to work around the img { ... } above */ +#captchaImg { + height: 120px; + width: 220px; + min-width: 0px; + max-width: 220px; +} + ul { width: 100%; padding: 0; From 462dbcfac826fd0a95d8c572b5bea036230e3203 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 13:45:25 +0100 Subject: [PATCH 13/73] Retry captcha on failure --- src/frontend/src/flows/confirmRegister.ts | 32 ++++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index a0299c1ce0..cd98b5ce67 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -39,7 +39,6 @@ export const confirmRegister = async ( return await init(canisterIdPrincipal, identity, alias); }; -// TODO: retry on bad captcha const tryRegister = async ( identity: WebAuthnIdentity, alias: string, @@ -58,16 +57,18 @@ const tryRegister = async ( // Congratulate user await displayUserNumber(result.userNumber); + } else if (result.kind == "apiError") { + console.log("Got a bad captcha, retrying"); + // TODO: shouldn't be apiError but "badCaptcha" or similar + requestCaptcha(); } return result; }; -const init = async ( - canisterIdPrincipal: Principal, - identity: WebAuthnIdentity, - alias: string -): Promise => { +// TODO: disable confirm button +// TODO: add message about loading captcha +const requestCaptcha = () => { const form = document.getElementById("confirmForm") as HTMLFormElement; IIConnection.createChallenge().then((captchaResp) => { const captchaImg = document.querySelector("#captchaImg"); @@ -92,24 +93,35 @@ const init = async ( loadingCaptchaText.innerHTML = "please copy the chars"; } }); + + +}; + +const init = async ( + canisterIdPrincipal: Principal, + identity: WebAuthnIdentity, + alias: string +): Promise => { + requestCaptcha(); return new Promise((resolve) => { // Create a PoW before registering const now_in_ns = BigInt(Date.now()) * BigInt(1000000); const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); - const confirmRegisterButton = document.getElementById( - "confirmRegisterButton" + const confirmRegisterButton = document.querySelector( + "#confirmRegisterButton" ) as HTMLFormElement; const captchaInput = document.querySelector( "#captchaInput" ) as HTMLFormElement; - const cancelButton = document.getElementById( - "cancelButton" + const cancelButton = document.querySelector( + "#cancelButton" ) as HTMLButtonElement; cancelButton.onclick = () => { resolve(null); }; + confirmRegisterButton.onclick = (e) => { e.preventDefault(); e.stopPropagation(); From 3ebb377ab8fdfa65605887cbd4742fff7ce9bce8 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 14:34:29 +0100 Subject: [PATCH 14/73] Clean up captcha retry --- src/frontend/src/flows/confirmRegister.ts | 46 ++++++++++----------- src/frontend/src/flows/displayUserNumber.ts | 5 ++- src/frontend/src/flows/register.ts | 3 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index cd98b5ce67..5ea5f0bb77 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -1,5 +1,4 @@ import { html, render } from "lit-html"; -import { ApiResult } from "../utils/iiConnection"; import { displayUserNumber } from "./displayUserNumber"; import { setUserNumber } from "../utils/userNumber"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; @@ -39,31 +38,33 @@ export const confirmRegister = async ( return await init(canisterIdPrincipal, identity, alias); }; -const tryRegister = async ( +const tryRegister = ( identity: WebAuthnIdentity, alias: string, pow: ProofOfWork, - challengeResult: ChallengeResult -): Promise => { - const result = await withLoader(async () => { + challengeResult: ChallengeResult, + func: (result: LoginResult) => void +) => { + withLoader(async () => { console.log("Registering..."); return IIConnection.register(identity, alias, pow, challengeResult); - }); - console.log(`Result: ${result.kind}`); - if (result.kind == "loginSuccess") { - console.log("Result kind was success"); - // Write user number to storage - setUserNumber(result.userNumber); + }).then((result) => { + console.log(`Result: ${result.kind}`); + if (result.kind == "loginSuccess") { + console.log("Result kind was success"); + // Write user number to storage + setUserNumber(result.userNumber); - // Congratulate user - await displayUserNumber(result.userNumber); - } else if (result.kind == "apiError") { - console.log("Got a bad captcha, retrying"); - // TODO: shouldn't be apiError but "badCaptcha" or similar + // Congratulate user + displayUserNumber(result.userNumber).then(() => { + func(apiResultToLoginResult(result)); + }); + } else { + // TODO: should we only get here on specific result.kind? like badCaptcha? + console.log("Something didn't work, retrying"); requestCaptcha(); - } - - return result; + } + }); }; // TODO: disable confirm button @@ -93,8 +94,6 @@ const requestCaptcha = () => { loadingCaptchaText.innerHTML = "please copy the chars"; } }); - - }; const init = async ( @@ -133,10 +132,7 @@ const init = async ( key: Number(captchaKey), chars: captchaChars, }; - tryRegister(identity, alias, pow, challengeResult).then((e) => { - console.log("registration successful"); - resolve(apiResultToLoginResult(e)); - }); + tryRegister(identity, alias, pow, challengeResult, resolve); }; }); }; diff --git a/src/frontend/src/flows/displayUserNumber.ts b/src/frontend/src/flows/displayUserNumber.ts index 398bdbef56..2f9e64a2f3 100644 --- a/src/frontend/src/flows/displayUserNumber.ts +++ b/src/frontend/src/flows/displayUserNumber.ts @@ -35,5 +35,8 @@ const init = (): Promise => const displayUserContinue = document.getElementById( "displayUserContinue" ) as HTMLButtonElement; - displayUserContinue.onclick = () => resolve(); + displayUserContinue.onclick = () => { + console.log("User resolving congratulations"); + resolve(); + }; }); diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index 3a6c56bae5..e52bcf17d0 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -70,8 +70,9 @@ const init = (): Promise => }); await tick(); const identity = await pendingIdentity; - await confirmRegister(identity, alias); + const result = await confirmRegister(identity, alias); console.log("Back to register"); + resolve(result); } catch (err) { reject(err); } From 01f19dd34abaf9f2ccdd49bf2b0adcff8fb65a40 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 14:59:02 +0100 Subject: [PATCH 15/73] Update backend-test.hs did --- src/frontend/src/flows/confirmRegister.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 5ea5f0bb77..144635c774 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -29,13 +29,13 @@ const pageContent = html`
`; -export const confirmRegister = async ( +export const confirmRegister = ( identity: WebAuthnIdentity, alias: string ): Promise => { const container = document.getElementById("pageContent") as HTMLElement; render(pageContent, container); - return await init(canisterIdPrincipal, identity, alias); + return init(canisterIdPrincipal, identity, alias); }; const tryRegister = ( @@ -96,12 +96,16 @@ const requestCaptcha = () => { }); }; -const init = async ( +const init = ( canisterIdPrincipal: Principal, identity: WebAuthnIdentity, alias: string ): Promise => { requestCaptcha(); + + // since the index expects to regain control we unfortunately have to wrap + // this whole logic in a promise that then resolves (giving control back to + // the caller) return new Promise((resolve) => { // Create a PoW before registering const now_in_ns = BigInt(Date.now()) * BigInt(1000000); From 2de894d587a3aa369c1be7443aa06e6196ebd2d3 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 15:32:57 +0100 Subject: [PATCH 16/73] Fix did file --- src/internet_identity/internet_identity.did | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index cbff5b7f2a..a438bd7285 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -111,7 +111,7 @@ type Challenge = record { type ChallengeResult = record { key : ChallengeKey; chars : text; -} +}; service : (opt InternetIdentityInit) -> { init_salt: () -> (); From 8064cd6818773fd329978664707d148d93355db2 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 15:38:55 +0100 Subject: [PATCH 17/73] Bump haskell-candid and update hs deps --- backend-tests/cabal.project | 2 +- backend-tests/cabal.project.freeze | 26 +++++++++++--------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/backend-tests/cabal.project b/backend-tests/cabal.project index 25e0bc5b3b..b642d02340 100644 --- a/backend-tests/cabal.project +++ b/backend-tests/cabal.project @@ -8,7 +8,7 @@ source-repository-package source-repository-package type: git location: https://github.com/nomeata/haskell-candid - tag: 98f580c191e4584973717866981af393531a34eb + tag: 2b6a470de71c37293eed6026b39e225f5cfc5a62 source-repository-package type: git diff --git a/backend-tests/cabal.project.freeze b/backend-tests/cabal.project.freeze index 1a7d14f6df..de436f774c 100644 --- a/backend-tests/cabal.project.freeze +++ b/backend-tests/cabal.project.freeze @@ -1,5 +1,5 @@ active-repositories: hackage.haskell.org:merge -constraints: any.Cabal ==3.0.1.0, +constraints: any.Cabal ==3.2.1.0, any.FloatingHex ==0.5, any.HUnit ==1.6.2.0, any.MonadRandom ==0.5.3, @@ -24,19 +24,17 @@ constraints: any.Cabal ==3.0.1.0, any.attoparsec ==0.13.2.5, attoparsec -developer, any.auto-update ==0.1.6, - any.base ==4.13.0.0, + any.base ==4.14.1.0, any.base-compat ==0.11.2, any.base-compat-batteries ==0.11.2, any.base-orphans ==0.8.6, - any.base-unicode-symbols ==0.2.4.2, - base-unicode-symbols +base-4-8 -old-base, any.base16-bytestring ==1.0.2.0, any.base32 ==0.2.1.0, any.base64-bytestring ==1.2.1.0, any.basement ==0.0.12, any.bifunctors ==5.5.7, bifunctors +semigroups +tagged, - any.binary ==0.8.7.0, + any.binary ==0.8.8.0, any.bindings-DSL ==1.0.25, any.blaze-builder ==0.4.2.2, any.blaze-html ==0.9.1.2, @@ -44,7 +42,7 @@ constraints: any.Cabal ==3.0.1.0, any.bsb-http-chunked ==0.0.0.4, any.byte-order ==0.1.2.0, any.byteorder ==1.0.4, - any.bytestring ==0.10.10.1, + any.bytestring ==0.10.12.0, any.cabal-doctest ==1.0.9, any.call-stack ==0.4.0, any.candid ==0.3, @@ -81,7 +79,6 @@ constraints: any.Cabal ==3.0.1.0, any.ed25519 ==0.0.5.0, ed25519 +no-donna +test-doctests +test-hlint +test-properties, any.exceptions ==0.10.4, - exceptions +transformers-0-4, any.fast-logger ==3.0.5, any.file-embed ==0.0.15.0, any.filepath ==1.4.2.1, @@ -89,9 +86,9 @@ constraints: any.Cabal ==3.0.1.0, generic-deriving +base-4-9, any.generic-lens ==2.2.0.0, any.generic-lens-core ==2.2.0.0, - any.ghc-boot-th ==8.8.4, + any.ghc-boot-th ==8.10.4, any.ghc-byteorder ==4.11.0.0.10, - any.ghc-prim ==0.5.3, + any.ghc-prim ==0.6.1, any.half ==0.3.1, any.hashable ==1.3.5.0, hashable +integer-gmp -random-initial-seed, @@ -110,7 +107,7 @@ constraints: any.Cabal ==3.0.1.0, ic-hs +library -release, any.indexed-profunctors ==0.1.1, any.indexed-traversable ==0.1.2, - any.integer-gmp ==1.0.2.0, + any.integer-gmp ==1.0.3.0, any.integer-logarithms ==1.0.3.1, integer-logarithms -check-bounds +integer-gmp, any.iproute ==1.7.12, @@ -185,9 +182,9 @@ constraints: any.Cabal ==3.0.1.0, any.tasty-hunit ==0.10.0.3, any.tasty-quickcheck ==0.10.1.2, any.tasty-rerun ==1.1.18, - any.template-haskell ==2.15.0.0, + any.template-haskell ==2.16.0.0, any.temporary ==1.3, - any.text ==1.2.4.0, + any.text ==1.2.4.1, any.text-short ==0.1.4, text-short -asserts, any.th-abstraction ==0.3.2.0, @@ -223,11 +220,10 @@ constraints: any.Cabal ==3.0.1.0, any.vector ==0.12.3.1, vector +boundschecks -internalchecks -unsafechecks -wall, any.wai ==3.2.3, - any.wai-cors ==0.2.7, any.wai-extra ==3.1.7, wai-extra -build-example, any.wai-logger ==2.3.6, - any.warp ==3.3.18, + any.warp ==3.3.17, warp +allow-sendfilefd -network-bytestring -warp-debug, any.wcwidth ==0.0.2, wcwidth -cli +split-base, @@ -240,4 +236,4 @@ constraints: any.Cabal ==3.0.1.0, any.xml ==1.3.14, any.zlib ==0.6.2.3, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config -index-state: hackage.haskell.org 2021-11-19T13:35:38Z +index-state: hackage.haskell.org 2021-11-18T14:26:30Z From e85b4216f5a27ff3b1d74db049bbcdcd65eb32a5 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 17:14:56 +0100 Subject: [PATCH 18/73] Fix build issues in backend-tests Though tests are unlikely to pass --- backend-tests/backend-tests.hs | 57 ++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 60ca578dea..02851b364f 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -133,6 +133,12 @@ type ProofOfWork = [Candid.candidType|record { nonce : nat64; }|] +type ChallengeResult = [Candid.candidType|record { + key : nat32; + chars: text; + +}|] + type HttpRequest = [Candid.candidType|record { method : text; url : text; @@ -150,6 +156,9 @@ type HttpResponse = [Candid.candidType|record { mkPOW :: Word64 -> Word64 -> ProofOfWork mkPOW t n = #timestamp .== t .+ #nonce .== n +mkChallengeResult :: ChallengeResult +mkChallengeResult = #key .== 0 .+ #chars .== T.pack "chars" + httpGet :: String -> HttpRequest httpGet url = #method .== T.pack "GET" .+ #url .== T.pack url @@ -494,19 +503,19 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ , withoutUpgrade $ iiTest "installs and upgrade" $ \ cid -> doUpgrade cid , withoutUpgrade $ iiTest "register with wrong user fails" $ \cid -> do - callIIRejectWith cid dummyUserId #register (device1, powAt cid 1) "[a-z0-9-]+ could not be authenticated against" + callIIRejectWith cid dummyUserId #register (device1, powAt cid 1, mkChallengeResult) "[a-z0-9-]+ could not be authenticated against" , withoutUpgrade $ iiTest "register with bad pow fails" $ \cid -> do - callIIRejectWith cid webauthID #register (device1, invalidPOW) "proof of work hash check failed" + callIIRejectWith cid webauthID #register (device1, invalidPOW, mkChallengeResult) "proof of work hash check failed" , withoutUpgrade $ iiTest "register with future pow fails" $ \cid -> do callIIRejectWith cid webauthID #register (device1, powAt cid (20*60*1000_000_000)) "proof of work timestamp [0-9]+ is too far in future, current time: [0-9]+" , withoutUpgrade $ iiTest "register with past pow fails" $ \cid -> do setCanisterTimeTo cid (20*60*1000_000_000) - callIIRejectWith cid webauthID #register (device1, powAt cid 1) "proof of work timestamp [0-9]+ is too old, current time: [0-9]+" + callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "proof of work timestamp [0-9]+ is too old, current time: [0-9]+" , withoutUpgrade $ iiTest "register with repeated pow fails" $ \cid -> do _ <- callII cid webauthID #register (device1, powAt cid 1) - callIIRejectWith cid webauthID #register (device1, powAt cid 1) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" + callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" , withoutUpgrade $ iiTest "get delegation without authorization" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) @@ -520,38 +529,38 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid 123 [] , withUpgrade $ \should_upgrade -> iiTest "register and lookup" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber assertStats cid 1 when should_upgrade $ doUpgrade cid assertStats cid 1 lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and lookup (with credential id)" $ \cid -> do - user_number <- callII cid webauth2ID #register (device2, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauth2ID #register (device2, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid lookupIs cid user_number [device2] , withUpgrade $ \should_upgrade -> iiTest "register add lookup" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callII cid webauthID #add (user_number, device2) when should_upgrade $ doUpgrade cid lookupIs cid user_number [device1, device2] , withUpgrade $ \should_upgrade -> iiTest "register and add with wrong user" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callIIReject cid webauth2ID #add (user_number, device2) lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and get principal with wrong user" $ \cid -> do queryIIReject cid webauth2ID #get_principal (10000, "front.end.com") - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid queryIIReject cid webauth2ID #get_principal (user_number, "front.end.com") , withUpgrade $ \should_upgrade -> iiTest "get delegation and validate" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -582,7 +591,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ Right () -> return () , withUpgrade $ \should_upgrade -> iiTest "get delegation with wrong user" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ do doUpgrade cid @@ -592,7 +601,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callIIRejectWith cid webauth2ID #prepare_delegation delegationArgs "[a-z0-9-]+ could not be authenticated." , withUpgrade $ \should_upgrade -> iiTest "get multiple delegations and validate" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -620,7 +629,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 , withoutUpgrade $ iiTest "get multiple delegations and expire" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -648,7 +657,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 , withUpgrade $ \should_upgrade -> iiTest "user identities differ" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -669,7 +678,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ assertFailure "User identities coincide for different frontends" , withUpgrade $ \should_upgrade -> iiTest "remove()" $ \cid -> do - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber lookupIs cid user_number [device1] callII cid webauthID #add (user_number, device2) lookupIs cid user_number [device1, device2] @@ -681,7 +690,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callII cid webauth2ID #remove (user_number, webauth2PK) when should_upgrade $ doUpgrade cid lookupIs cid user_number [] - user_number2 <- callII cid webauthID #register (device1, powAt cid 1) >>= mustGetUserNumber + user_number2 <- callII cid webauthID #register (device1, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid when (user_number == user_number2) $ lift $ assertFailure "Identity Anchor re-used" @@ -691,10 +700,10 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ s .! #assigned_user_number_range @?= (100, 103) assertStats cid 0 - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber liftIO $ user_number @?= 100 assertStats cid 1 - user_number <- callII cid webauthID #register (device1, powAt cid 1) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber liftIO $ user_number @?= 101 assertStats cid 2 @@ -706,20 +715,20 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ let expected_upper_bound = if should_upgrade then 100 + 3_774_873 else 103 lift $ s .! #assigned_user_number_range @?= (100, expected_upper_bound) - user_number <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber liftIO $ user_number @?= 102 assertStats cid 3 - callIIReject cid webauthID #register (device1, powAt cid 0) + callIIReject cid webauthID #register (device1, powAt cid 0, mkChallengeResult) assertStats cid 3 , withoutUpgrade $ iiTestWithInit "empty init range" (100, 100) $ \cid -> do s <- queryII cid dummyUserId #stats () lift $ s .! #assigned_user_number_range @?= (100, 100) - response <- callII cid webauthID #register (device1, powAt cid 0) + response <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) assertVariant #canister_full response , withUpgrade $ \should_upgrade -> iiTest "metrics endpoint" $ \cid -> do - _ <- callII cid webauth2ID #register (device2, powAt cid 1) >>= mustGetUserNumber + _ <- callII cid webauth2ID #register (device2, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber metrics <- callII cid webauth2ID #http_request (httpGet "/metrics") >>= mustParseMetrics assertMetric metrics "internet_identity_user_count" 1.0 @@ -727,7 +736,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ when should_upgrade $ doUpgrade cid - userNumber <- callII cid webauthID #register (device1, powAt cid 0) >>= mustGetUserNumber + userNumber <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (userNumber, "front.end.com", sessionPK, Nothing) From 4b64b7ab0fb70cc3baed815b8d5099d806c0ce44 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 18 Nov 2021 18:50:40 +0100 Subject: [PATCH 19/73] Bump GHC version on CI --- .github/workflows/backend-tests.yml | 2 +- backend-tests/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 99f782076a..425692599f 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -14,7 +14,7 @@ jobs: os: [ ubuntu-latest, macos-latest ] env: RUSTC_VERSION: 1.51.0 - GHC_VERSION: 8.8.4 + GHC_VERSION: 8.10.4 steps: - uses: actions/checkout@v2 diff --git a/backend-tests/README.md b/backend-tests/README.md index 051f71c44e..7ee57806e3 100644 --- a/backend-tests/README.md +++ b/backend-tests/README.md @@ -8,7 +8,7 @@ run a simulated IC environment in a process. Setup ----- - * Install ghc-8.8.4 and `cabal` (e.g. using https://www.haskell.org/ghcup/) + * Install ghc-8.10.4 and `cabal` (e.g. using https://www.haskell.org/ghcup/) * Run `cabal build` Running From 5dc380ecf913731cb9ac164542c7ec1d8696e915 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 11:20:10 +0100 Subject: [PATCH 20/73] Revert GHC to 884 and fix freeze accordingly --- .github/workflows/backend-tests.yml | 2 +- backend-tests/README.md | 2 +- backend-tests/cabal.project.freeze | 40 ++++++++++++++--------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 425692599f..99f782076a 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -14,7 +14,7 @@ jobs: os: [ ubuntu-latest, macos-latest ] env: RUSTC_VERSION: 1.51.0 - GHC_VERSION: 8.10.4 + GHC_VERSION: 8.8.4 steps: - uses: actions/checkout@v2 diff --git a/backend-tests/README.md b/backend-tests/README.md index 7ee57806e3..051f71c44e 100644 --- a/backend-tests/README.md +++ b/backend-tests/README.md @@ -8,7 +8,7 @@ run a simulated IC environment in a process. Setup ----- - * Install ghc-8.10.4 and `cabal` (e.g. using https://www.haskell.org/ghcup/) + * Install ghc-8.8.4 and `cabal` (e.g. using https://www.haskell.org/ghcup/) * Run `cabal build` Running diff --git a/backend-tests/cabal.project.freeze b/backend-tests/cabal.project.freeze index de436f774c..ddded07fec 100644 --- a/backend-tests/cabal.project.freeze +++ b/backend-tests/cabal.project.freeze @@ -1,12 +1,12 @@ active-repositories: hackage.haskell.org:merge -constraints: any.Cabal ==3.2.1.0, +constraints: any.Cabal ==3.0.1.0, any.FloatingHex ==0.5, any.HUnit ==1.6.2.0, any.MonadRandom ==0.5.3, any.QuickCheck ==2.14.2, QuickCheck -old-random +templatehaskell, any.StateVar ==1.2.2, - any.aeson ==1.5.2.0, + any.aeson ==1.4.6.0, aeson -bytestring-builder -cffi -developer -fast, any.ansi-terminal ==0.11, ansi-terminal -example, @@ -21,20 +21,19 @@ constraints: any.Cabal ==3.2.1.0, any.async ==2.2.4, async -bench, any.atomic-write ==0.2.0.7, - any.attoparsec ==0.13.2.5, + any.attoparsec ==0.14.2, attoparsec -developer, any.auto-update ==0.1.6, - any.base ==4.14.1.0, - any.base-compat ==0.11.2, - any.base-compat-batteries ==0.11.2, + any.base ==4.13.0.0, + any.base-compat ==0.12.1, any.base-orphans ==0.8.6, any.base16-bytestring ==1.0.2.0, any.base32 ==0.2.1.0, any.base64-bytestring ==1.2.1.0, any.basement ==0.0.12, - any.bifunctors ==5.5.7, + any.bifunctors ==5.5.11, bifunctors +semigroups +tagged, - any.binary ==0.8.8.0, + any.binary ==0.8.7.0, any.bindings-DSL ==1.0.25, any.blaze-builder ==0.4.2.2, any.blaze-html ==0.9.1.2, @@ -42,7 +41,7 @@ constraints: any.Cabal ==3.2.1.0, any.bsb-http-chunked ==0.0.0.4, any.byte-order ==0.1.2.0, any.byteorder ==1.0.4, - any.bytestring ==0.10.12.0, + any.bytestring ==0.10.10.1, any.cabal-doctest ==1.0.9, any.call-stack ==0.4.0, any.candid ==0.3, @@ -79,16 +78,17 @@ constraints: any.Cabal ==3.2.1.0, any.ed25519 ==0.0.5.0, ed25519 +no-donna +test-doctests +test-hlint +test-properties, any.exceptions ==0.10.4, + exceptions +transformers-0-4, any.fast-logger ==3.0.5, any.file-embed ==0.0.15.0, any.filepath ==1.4.2.1, - any.generic-deriving ==1.13.1, + any.generic-deriving ==1.14.1, generic-deriving +base-4-9, any.generic-lens ==2.2.0.0, any.generic-lens-core ==2.2.0.0, - any.ghc-boot-th ==8.10.4, + any.ghc-boot-th ==8.8.4, any.ghc-byteorder ==4.11.0.0.10, - any.ghc-prim ==0.6.1, + any.ghc-prim ==0.5.3, any.half ==0.3.1, any.hashable ==1.3.5.0, hashable +integer-gmp -random-initial-seed, @@ -107,7 +107,7 @@ constraints: any.Cabal ==3.2.1.0, ic-hs +library -release, any.indexed-profunctors ==0.1.1, any.indexed-traversable ==0.1.2, - any.integer-gmp ==1.0.3.0, + any.integer-gmp ==1.0.2.0, any.integer-logarithms ==1.0.3.1, integer-logarithms -check-bounds +integer-gmp, any.iproute ==1.7.12, @@ -121,7 +121,7 @@ constraints: any.Cabal ==3.2.1.0, any.microlens-ghc ==0.4.13.1, any.microlens-mtl ==0.2.0.1, any.microlens-platform ==0.4.2.1, - any.microlens-th ==0.4.3.6, + any.microlens-th ==0.4.3.10, any.mime-types ==0.1.0.9, any.monad-control ==1.0.3.1, any.mtl ==2.2.2, @@ -146,7 +146,7 @@ constraints: any.Cabal ==3.2.1.0, any.primitive ==0.7.3.0, any.primitive-unaligned ==0.1.1.1, any.process ==1.6.9.0, - any.profunctors ==5.6, + any.profunctors ==5.6.2, any.psqueues ==0.2.7.3, any.quickcheck-io ==0.2.0, any.random ==1.2.1, @@ -182,12 +182,12 @@ constraints: any.Cabal ==3.2.1.0, any.tasty-hunit ==0.10.0.3, any.tasty-quickcheck ==0.10.1.2, any.tasty-rerun ==1.1.18, - any.template-haskell ==2.16.0.0, + any.template-haskell ==2.15.0.0, any.temporary ==1.3, - any.text ==1.2.4.1, + any.text ==1.2.4.0, any.text-short ==0.1.4, text-short -asserts, - any.th-abstraction ==0.3.2.0, + any.th-abstraction ==0.4.3.0, any.th-compat ==0.1.3, any.these ==1.1.1.1, these +assoc, @@ -223,7 +223,7 @@ constraints: any.Cabal ==3.2.1.0, any.wai-extra ==3.1.7, wai-extra -build-example, any.wai-logger ==2.3.6, - any.warp ==3.3.17, + any.warp ==3.3.18, warp +allow-sendfilefd -network-bytestring -warp-debug, any.wcwidth ==0.0.2, wcwidth -cli +split-base, @@ -236,4 +236,4 @@ constraints: any.Cabal ==3.2.1.0, any.xml ==1.3.14, any.zlib ==0.6.2.3, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config -index-state: hackage.haskell.org 2021-11-18T14:26:30Z +index-state: hackage.haskell.org 2021-11-19T09:31:56Z From 038c1fc06119246386201a245f95a20bef94bfeb Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 11:40:06 +0100 Subject: [PATCH 21/73] Make sure aeson is recent enough --- backend-tests/backend-tests.cabal | 3 ++- backend-tests/cabal.project.freeze | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/backend-tests/backend-tests.cabal b/backend-tests/backend-tests.cabal index 08b1324f1e..a81e20d56e 100644 --- a/backend-tests/backend-tests.cabal +++ b/backend-tests/backend-tests.cabal @@ -15,7 +15,8 @@ executable backend-tests other-modules: Prometheus - build-depends: aeson + -- must use aeson >=1.4.7 for ic-hs to build + build-depends: aeson >=1.4.7 build-depends: attoparsec build-depends: base >=4.12 && <5 build-depends: base64-bytestring diff --git a/backend-tests/cabal.project.freeze b/backend-tests/cabal.project.freeze index ddded07fec..082b10c506 100644 --- a/backend-tests/cabal.project.freeze +++ b/backend-tests/cabal.project.freeze @@ -6,7 +6,7 @@ constraints: any.Cabal ==3.0.1.0, any.QuickCheck ==2.14.2, QuickCheck -old-random +templatehaskell, any.StateVar ==1.2.2, - any.aeson ==1.4.6.0, + any.aeson ==1.5.2.0, aeson -bytestring-builder -cffi -developer -fast, any.ansi-terminal ==0.11, ansi-terminal -example, @@ -21,17 +21,18 @@ constraints: any.Cabal ==3.0.1.0, any.async ==2.2.4, async -bench, any.atomic-write ==0.2.0.7, - any.attoparsec ==0.14.2, + any.attoparsec ==0.13.2.5, attoparsec -developer, any.auto-update ==0.1.6, any.base ==4.13.0.0, - any.base-compat ==0.12.1, + any.base-compat ==0.11.2, + any.base-compat-batteries ==0.11.2, any.base-orphans ==0.8.6, any.base16-bytestring ==1.0.2.0, any.base32 ==0.2.1.0, any.base64-bytestring ==1.2.1.0, any.basement ==0.0.12, - any.bifunctors ==5.5.11, + any.bifunctors ==5.5.7, bifunctors +semigroups +tagged, any.binary ==0.8.7.0, any.bindings-DSL ==1.0.25, @@ -82,7 +83,7 @@ constraints: any.Cabal ==3.0.1.0, any.fast-logger ==3.0.5, any.file-embed ==0.0.15.0, any.filepath ==1.4.2.1, - any.generic-deriving ==1.14.1, + any.generic-deriving ==1.13.1, generic-deriving +base-4-9, any.generic-lens ==2.2.0.0, any.generic-lens-core ==2.2.0.0, @@ -121,7 +122,7 @@ constraints: any.Cabal ==3.0.1.0, any.microlens-ghc ==0.4.13.1, any.microlens-mtl ==0.2.0.1, any.microlens-platform ==0.4.2.1, - any.microlens-th ==0.4.3.10, + any.microlens-th ==0.4.3.6, any.mime-types ==0.1.0.9, any.monad-control ==1.0.3.1, any.mtl ==2.2.2, @@ -146,7 +147,7 @@ constraints: any.Cabal ==3.0.1.0, any.primitive ==0.7.3.0, any.primitive-unaligned ==0.1.1.1, any.process ==1.6.9.0, - any.profunctors ==5.6.2, + any.profunctors ==5.6, any.psqueues ==0.2.7.3, any.quickcheck-io ==0.2.0, any.random ==1.2.1, @@ -187,7 +188,7 @@ constraints: any.Cabal ==3.0.1.0, any.text ==1.2.4.0, any.text-short ==0.1.4, text-short -asserts, - any.th-abstraction ==0.4.3.0, + any.th-abstraction ==0.3.2.0, any.th-compat ==0.1.3, any.these ==1.1.1.1, these +assoc, From 02059be4c866a4e28540b6db254ef4f2546e7da2 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 14:21:44 +0100 Subject: [PATCH 22/73] Reset canister randomness before register --- backend-tests/backend-tests.hs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 02851b364f..4ad82db943 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -219,6 +219,10 @@ setCanisterTimeTo cid new_time = modify $ \ic -> ic { canisters = M.adjust (\cs -> cs { time = new_time }) (EntityId cid) (canisters ic) } +resetCanisterRandomness :: M () +resetCanisterRandomness = + modify $ + \ic -> ic { rng = mkStdGen 0 } callManagement :: forall s a b. HasCallStack => @@ -515,6 +519,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ _ <- callII cid webauthID #register (device1, powAt cid 1) callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" , withoutUpgrade $ iiTest "get delegation without authorization" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -529,6 +534,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid 123 [] , withUpgrade $ \should_upgrade -> iiTest "register and lookup" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber assertStats cid 1 when should_upgrade $ doUpgrade cid @@ -536,11 +542,13 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and lookup (with credential id)" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauth2ID #register (device2, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid lookupIs cid user_number [device2] , withUpgrade $ \should_upgrade -> iiTest "register add lookup" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callII cid webauthID #add (user_number, device2) @@ -548,6 +556,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid user_number [device1, device2] , withUpgrade $ \should_upgrade -> iiTest "register and add with wrong user" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callIIReject cid webauth2ID #add (user_number, device2) @@ -555,11 +564,13 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ , withUpgrade $ \should_upgrade -> iiTest "register and get principal with wrong user" $ \cid -> do queryIIReject cid webauth2ID #get_principal (10000, "front.end.com") + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid queryIIReject cid webauth2ID #get_principal (user_number, "front.end.com") , withUpgrade $ \should_upgrade -> iiTest "get delegation and validate" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" @@ -591,6 +602,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ Right () -> return () , withUpgrade $ \should_upgrade -> iiTest "get delegation with wrong user" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ do doUpgrade cid @@ -601,6 +613,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callIIRejectWith cid webauth2ID #prepare_delegation delegationArgs "[a-z0-9-]+ could not be authenticated." , withUpgrade $ \should_upgrade -> iiTest "get multiple delegations and validate" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" @@ -629,6 +642,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 , withoutUpgrade $ iiTest "get multiple delegations and expire" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" @@ -657,6 +671,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 , withUpgrade $ \should_upgrade -> iiTest "user identities differ" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" @@ -678,6 +693,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ assertFailure "User identities coincide for different frontends" , withUpgrade $ \should_upgrade -> iiTest "remove()" $ \cid -> do + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber lookupIs cid user_number [device1] callII cid webauthID #add (user_number, device2) @@ -690,6 +706,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callII cid webauth2ID #remove (user_number, webauth2PK) when should_upgrade $ doUpgrade cid lookupIs cid user_number [] + resetCanisterRandomness user_number2 <- callII cid webauthID #register (device1, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid when (user_number == user_number2) $ @@ -700,9 +717,11 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ s .! #assigned_user_number_range @?= (100, 103) assertStats cid 0 + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber liftIO $ user_number @?= 100 assertStats cid 1 + resetCanisterRandomness user_number <- callII cid webauthID #register (device1, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber liftIO $ user_number @?= 101 assertStats cid 2 @@ -718,16 +737,19 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber liftIO $ user_number @?= 102 assertStats cid 3 + resetCanisterRandomness callIIReject cid webauthID #register (device1, powAt cid 0, mkChallengeResult) assertStats cid 3 , withoutUpgrade $ iiTestWithInit "empty init range" (100, 100) $ \cid -> do s <- queryII cid dummyUserId #stats () lift $ s .! #assigned_user_number_range @?= (100, 100) + resetCanisterRandomness response <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) assertVariant #canister_full response , withUpgrade $ \should_upgrade -> iiTest "metrics endpoint" $ \cid -> do + resetCanisterRandomness _ <- callII cid webauth2ID #register (device2, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber metrics <- callII cid webauth2ID #http_request (httpGet "/metrics") >>= mustParseMetrics @@ -736,6 +758,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ when should_upgrade $ doUpgrade cid + resetCanisterRandomness userNumber <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK From 12a4b1c6af58ea9628f6b97f3d56caf962b7c761 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 16:24:55 +0100 Subject: [PATCH 23/73] Simplify captcha for tests --- backend-tests/backend-tests.hs | 74 +++++++++++-------------------- src/internet_identity/src/main.rs | 12 +++-- 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 4ad82db943..0d61581e10 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -7,6 +7,7 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} @@ -156,9 +157,6 @@ type HttpResponse = [Candid.candidType|record { mkPOW :: Word64 -> Word64 -> ProofOfWork mkPOW t n = #timestamp .== t .+ #nonce .== n -mkChallengeResult :: ChallengeResult -mkChallengeResult = #key .== 0 .+ #chars .== T.pack "chars" - httpGet :: String -> HttpRequest httpGet url = #method .== T.pack "GET" .+ #url .== T.pack url @@ -219,11 +217,6 @@ setCanisterTimeTo cid new_time = modify $ \ic -> ic { canisters = M.adjust (\cs -> cs { time = new_time }) (EntityId cid) (canisters ic) } -resetCanisterRandomness :: M () -resetCanisterRandomness = - modify $ - \ic -> ic { rng = mkStdGen 0 } - callManagement :: forall s a b. HasCallStack => KnownSymbol s => @@ -519,8 +512,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ _ <- callII cid webauthID #register (device1, powAt cid 1) callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" , withoutUpgrade $ iiTest "get delegation without authorization" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) @@ -534,44 +526,38 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid 123 [] , withUpgrade $ \should_upgrade -> iiTest "register and lookup" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber assertStats cid 1 when should_upgrade $ doUpgrade cid assertStats cid 1 lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and lookup (with credential id)" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauth2ID #register (device2, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device2 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid lookupIs cid user_number [device2] , withUpgrade $ \should_upgrade -> iiTest "register add lookup" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callII cid webauthID #add (user_number, device2) when should_upgrade $ doUpgrade cid lookupIs cid user_number [device1, device2] , withUpgrade $ \should_upgrade -> iiTest "register and add with wrong user" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callIIReject cid webauth2ID #add (user_number, device2) lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and get principal with wrong user" $ \cid -> do queryIIReject cid webauth2ID #get_principal (10000, "front.end.com") - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid queryIIReject cid webauth2ID #get_principal (user_number, "front.end.com") , withUpgrade $ \should_upgrade -> iiTest "get delegation and validate" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -602,8 +588,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ Right () -> return () , withUpgrade $ \should_upgrade -> iiTest "get delegation with wrong user" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ do doUpgrade cid @@ -613,8 +598,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callIIRejectWith cid webauth2ID #prepare_delegation delegationArgs "[a-z0-9-]+ could not be authenticated." , withUpgrade $ \should_upgrade -> iiTest "get multiple delegations and validate" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -642,8 +626,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 , withoutUpgrade $ iiTest "get multiple delegations and expire" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -671,8 +654,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 , withUpgrade $ \should_upgrade -> iiTest "user identities differ" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK @@ -693,8 +675,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ assertFailure "User identities coincide for different frontends" , withUpgrade $ \should_upgrade -> iiTest "remove()" $ \cid -> do - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber lookupIs cid user_number [device1] callII cid webauthID #add (user_number, device2) lookupIs cid user_number [device1, device2] @@ -706,8 +687,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callII cid webauth2ID #remove (user_number, webauth2PK) when should_upgrade $ doUpgrade cid lookupIs cid user_number [] - resetCanisterRandomness - user_number2 <- callII cid webauthID #register (device1, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber + user_number2 <- register cid device1 (powAt cid 1) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid when (user_number == user_number2) $ lift $ assertFailure "Identity Anchor re-used" @@ -717,12 +697,10 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ s .! #assigned_user_number_range @?= (100, 103) assertStats cid 0 - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber liftIO $ user_number @?= 100 assertStats cid 1 - resetCanisterRandomness - user_number <- callII cid webauthID #register (device1, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 1) >>= mustGetUserNumber liftIO $ user_number @?= 101 assertStats cid 2 @@ -734,23 +712,20 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ let expected_upper_bound = if should_upgrade then 100 + 3_774_873 else 103 lift $ s .! #assigned_user_number_range @?= (100, expected_upper_bound) - user_number <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber liftIO $ user_number @?= 102 assertStats cid 3 - resetCanisterRandomness - callIIReject cid webauthID #register (device1, powAt cid 0, mkChallengeResult) + getChallenge cid >>= callIIReject cid webauthID #register . (device1, powAt cid 0,) assertStats cid 3 , withoutUpgrade $ iiTestWithInit "empty init range" (100, 100) $ \cid -> do s <- queryII cid dummyUserId #stats () lift $ s .! #assigned_user_number_range @?= (100, 100) - resetCanisterRandomness - response <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) + response <- register cid device1 (powAt cid 0) assertVariant #canister_full response , withUpgrade $ \should_upgrade -> iiTest "metrics endpoint" $ \cid -> do - resetCanisterRandomness - _ <- callII cid webauth2ID #register (device2, powAt cid 1, mkChallengeResult) >>= mustGetUserNumber + _ <- register cid device2 (powAt cid 1) >>= mustGetUserNumber metrics <- callII cid webauth2ID #http_request (httpGet "/metrics") >>= mustParseMetrics assertMetric metrics "internet_identity_user_count" 1.0 @@ -758,8 +733,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ when should_upgrade $ doUpgrade cid - resetCanisterRandomness - userNumber <- callII cid webauthID #register (device1, powAt cid 0, mkChallengeResult) >>= mustGetUserNumber + userNumber <- register cid device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (userNumber, "front.end.com", sessionPK, Nothing) @@ -865,6 +839,12 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ .+ #wasm_module .== wasm .+ #arg .== Candid.encode () + getChallenge cid = do + challenge <- callII cid webauthID #create_challenge () + pure $ #key .== challenge .! #challenge_key .+ #chars .== T.pack "a" + + register cid device pow = + getChallenge cid >>= callII cid webauthID #register . (device, pow,) asHex :: Blob -> String asHex = T.unpack . H.encodeHex . BS.toStrict diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 4e76c90204..f5fc51f337 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -442,10 +442,14 @@ async fn create_challenge() -> CaptchaResponse { let rng = rand_chacha::ChaCha20Rng::from_seed(seed); let mut captcha = captcha::RngCaptcha::from_rng(rng); - let captcha = captcha.add_chars(5) - .apply_filter(Wave::new(2.0, 20.0).horizontal()) - .apply_filter(Wave::new(2.0, 20.0).vertical()) - .view(220, 120); + // TODO: use this in production: + //let captcha = captcha.add_chars(5) + //.apply_filter(Wave::new(2.0, 20.0).horizontal()) + //.apply_filter(Wave::new(2.0, 20.0).vertical()) + //.view(220, 120); + + let captcha = captcha.set_chars(&vec!['a']).add_chars(1) + .view(10,10); let resp = match captcha.as_base64() { Some(png_base64) => CaptchaResponse { png_base64, challenge_key }, From dfaab87594cf483d6d6bb17ffef357673fbb50d9 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 16:51:25 +0100 Subject: [PATCH 24/73] Revert cabal files --- backend-tests/backend-tests.cabal | 3 +- backend-tests/cabal.project | 2 +- backend-tests/cabal.project.freeze | 131 ++++++++++++++--------------- 3 files changed, 65 insertions(+), 71 deletions(-) diff --git a/backend-tests/backend-tests.cabal b/backend-tests/backend-tests.cabal index a81e20d56e..08b1324f1e 100644 --- a/backend-tests/backend-tests.cabal +++ b/backend-tests/backend-tests.cabal @@ -15,8 +15,7 @@ executable backend-tests other-modules: Prometheus - -- must use aeson >=1.4.7 for ic-hs to build - build-depends: aeson >=1.4.7 + build-depends: aeson build-depends: attoparsec build-depends: base >=4.12 && <5 build-depends: base64-bytestring diff --git a/backend-tests/cabal.project b/backend-tests/cabal.project index b642d02340..88b4c5e4a3 100644 --- a/backend-tests/cabal.project +++ b/backend-tests/cabal.project @@ -8,7 +8,7 @@ source-repository-package source-repository-package type: git location: https://github.com/nomeata/haskell-candid - tag: 2b6a470de71c37293eed6026b39e225f5cfc5a62 + tag: 01157c50ec29b1f8ab35ca27f38dc592ed5406f9 source-repository-package type: git diff --git a/backend-tests/cabal.project.freeze b/backend-tests/cabal.project.freeze index 082b10c506..c71403643b 100644 --- a/backend-tests/cabal.project.freeze +++ b/backend-tests/cabal.project.freeze @@ -2,10 +2,10 @@ active-repositories: hackage.haskell.org:merge constraints: any.Cabal ==3.0.1.0, any.FloatingHex ==0.5, any.HUnit ==1.6.2.0, - any.MonadRandom ==0.5.3, + any.MonadRandom ==0.5.2, any.QuickCheck ==2.14.2, QuickCheck -old-random +templatehaskell, - any.StateVar ==1.2.2, + any.StateVar ==1.2.1, any.aeson ==1.5.2.0, aeson -bytestring-builder -cffi -developer -fast, any.ansi-terminal ==0.11, @@ -18,7 +18,7 @@ constraints: any.Cabal ==3.0.1.0, any.asn1-parse ==0.9.5, any.asn1-types ==0.3.4, any.assoc ==1.0.2, - any.async ==2.2.4, + any.async ==2.2.3, async -bench, any.atomic-write ==0.2.0.7, any.attoparsec ==0.13.2.5, @@ -27,46 +27,46 @@ constraints: any.Cabal ==3.0.1.0, any.base ==4.13.0.0, any.base-compat ==0.11.2, any.base-compat-batteries ==0.11.2, - any.base-orphans ==0.8.6, - any.base16-bytestring ==1.0.2.0, - any.base32 ==0.2.1.0, - any.base64-bytestring ==1.2.1.0, - any.basement ==0.0.12, + any.base-orphans ==0.8.4, + any.base16-bytestring ==1.0.1.0, + any.base32 ==0.2.0.0, + any.base64-bytestring ==1.2.0.1, + any.basement ==0.0.11, any.bifunctors ==5.5.7, bifunctors +semigroups +tagged, any.binary ==0.8.7.0, any.bindings-DSL ==1.0.25, - any.blaze-builder ==0.4.2.2, + any.blaze-builder ==0.4.2.1, any.blaze-html ==0.9.1.2, any.blaze-markup ==0.8.2.8, any.bsb-http-chunked ==0.0.0.4, any.byte-order ==0.1.2.0, any.byteorder ==1.0.4, any.bytestring ==0.10.10.1, - any.cabal-doctest ==1.0.9, - any.call-stack ==0.4.0, - any.candid ==0.3, + any.cabal-doctest ==1.0.8, + any.call-stack ==0.3.0, + any.candid ==0.2, any.case-insensitive ==1.2.1.0, - any.cborg ==0.2.6.0, + any.cborg ==0.2.4.0, cborg +optimize-gmp, - any.cereal ==0.5.8.2, + any.cereal ==0.5.8.1, cereal -bytestring-builder, any.clock ==0.8.2, clock -llvm, any.cmdargs ==0.10.21, cmdargs +quotation -testprog, - any.colour ==2.3.6, + any.colour ==2.3.5, any.comonad ==5.0.8, comonad +containers +distributive +indexed-traversable, any.connection ==0.3.1, - any.constraints ==0.13.2, + any.constraints ==0.12, any.containers ==0.6.2.1, - any.contravariant ==1.5.5, + any.contravariant ==1.5.3, contravariant +semigroups +statevar +tagged, any.cookie ==0.4.5, any.crc ==0.1.1.1, crc -lib-werror, - any.cryptonite ==0.29, + any.cryptonite ==0.28, cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes, any.data-default-class ==0.1.2.0, any.data-fix ==0.2.1, @@ -80,55 +80,54 @@ constraints: any.Cabal ==3.0.1.0, ed25519 +no-donna +test-doctests +test-hlint +test-properties, any.exceptions ==0.10.4, exceptions +transformers-0-4, - any.fast-logger ==3.0.5, - any.file-embed ==0.0.15.0, + any.fast-logger ==3.0.3, any.filepath ==1.4.2.1, any.generic-deriving ==1.13.1, generic-deriving +base-4-9, - any.generic-lens ==2.2.0.0, - any.generic-lens-core ==2.2.0.0, + any.generic-lens ==2.1.0.0, + any.generic-lens-core ==2.1.0.0, any.ghc-boot-th ==8.8.4, any.ghc-byteorder ==4.11.0.0.10, any.ghc-prim ==0.5.3, any.half ==0.3.1, - any.hashable ==1.3.5.0, - hashable +integer-gmp -random-initial-seed, + any.hashable ==1.3.1.0, + hashable +integer-gmp, any.hex-text ==0.1.0.4, any.hourglass ==0.2.12, - any.hsc2hs ==0.68.8, + any.hsc2hs ==0.68.7, hsc2hs -in-ghc-tree, - any.http-client ==0.7.9, + any.http-client ==0.7.6, http-client +network-uri, any.http-client-tls ==0.3.5.3, any.http-date ==0.0.11, any.http-types ==0.12.3, - any.http2 ==3.0.2, - http2 -devel -doc -h2spec, + any.http2 ==2.0.6, + http2 -devel, any.ic-hs ==0.0.1, - ic-hs +library -release, - any.indexed-profunctors ==0.1.1, - any.indexed-traversable ==0.1.2, + ic-hs -release, + any.indexed-profunctors ==0.1, + any.indexed-traversable ==0.1.1, any.integer-gmp ==1.0.2.0, any.integer-logarithms ==1.0.3.1, integer-logarithms -check-bounds +integer-gmp, - any.iproute ==1.7.12, + any.iproute ==1.7.11, any.leb128-cereal ==1.2, any.lifted-base ==0.2.3.12, - any.megaparsec ==9.2.0, + any.megaparsec ==9.0.1, megaparsec -dev, - any.memory ==0.16.0, + any.memory ==0.15.0, memory +support_basement +support_bytestring +support_deepseq +support_foundation, any.microlens ==0.4.12.0, - any.microlens-ghc ==0.4.13.1, + any.microlens-ghc ==0.4.13, any.microlens-mtl ==0.2.0.1, - any.microlens-platform ==0.4.2.1, + any.microlens-platform ==0.4.2, any.microlens-th ==0.4.3.6, any.mime-types ==0.1.0.9, - any.monad-control ==1.0.3.1, + any.monad-control ==1.0.2.3, any.mtl ==2.2.2, any.nats ==1.1.2, nats +binary +hashable +template-haskell, - any.network ==3.1.2.5, + any.network ==3.1.2.1, network -devel, any.network-byte-order ==0.1.6, any.network-uri ==2.6.4.1, @@ -142,41 +141,39 @@ constraints: any.Cabal ==3.0.1.0, parser-combinators -dev, any.pem ==0.2.4, any.pretty ==1.1.3.6, - any.prettyprinter ==1.7.1, - prettyprinter -buildreadme +text, - any.primitive ==0.7.3.0, + any.prettyprinter ==1.7.0, + prettyprinter -buildreadme, + any.primitive ==0.7.1.0, any.primitive-unaligned ==0.1.1.1, any.process ==1.6.9.0, any.profunctors ==5.6, - any.psqueues ==0.2.7.3, + any.psqueues ==0.2.7.2, any.quickcheck-io ==0.2.0, - any.random ==1.2.1, - any.regex-base ==0.94.0.2, - any.regex-tdfa ==1.3.1.1, + any.random ==1.2.0, + any.regex-base ==0.94.0.1, + any.regex-tdfa ==1.3.1.0, regex-tdfa -force-o2, - any.resourcet ==1.2.4.3, - any.row-types ==1.0.1.2, + any.resourcet ==1.2.4.2, + any.row-types ==1.0.1.0, any.rts ==1.0, - any.scientific ==0.3.7.0, + any.scientific ==0.3.6.2, scientific -bytestring-builder -integer-simple, - any.semigroups ==0.20, + any.semigroups ==0.19.1, semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers, - any.serialise ==0.2.4.0, + any.serialise ==0.2.3.0, serialise +newtime15, any.simple-sendfile ==0.2.30, simple-sendfile +allow-bsd, any.socks ==0.6.1, any.split ==0.2.3.4, - any.splitmix ==0.1.0.4, + any.splitmix ==0.1.0.3, splitmix -optimised-mixer, any.stm ==2.5.0.0, - any.streaming-commons ==0.2.2.2, + any.streaming-commons ==0.2.2.1, streaming-commons -use-bytestring-builder, - any.strict ==0.4.0.1, - strict +assoc, any.tagged ==0.8.6.1, tagged +deepseq +transformers, - any.tasty ==1.4.2, + any.tasty ==1.4.1, tasty +clock +unix, any.tasty-ant-xml ==1.1.8, any.tasty-html ==0.4.1.4, @@ -186,45 +183,43 @@ constraints: any.Cabal ==3.0.1.0, any.template-haskell ==2.15.0.0, any.temporary ==1.3, any.text ==1.2.4.0, - any.text-short ==0.1.4, + any.text-short ==0.1.3, text-short -asserts, any.th-abstraction ==0.3.2.0, - any.th-compat ==0.1.3, + any.th-compat ==0.1.2, any.these ==1.1.1.1, these +assoc, any.time ==1.9.3, - any.time-compat ==1.9.6.1, + any.time-compat ==1.9.5, time-compat -old-locale, any.time-manager ==0.0.0, any.tls ==1.5.5, tls +compat -hans +network, any.transformers ==0.5.6.2, - any.transformers-base ==0.4.6, + any.transformers-base ==0.4.5.2, transformers-base +orphaninstances, - any.transformers-compat ==0.7.1, + any.transformers-compat ==0.6.6, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, any.type-equality ==1, - any.uglymemo ==0.1.0.1, any.unbounded-delays ==0.1.1.1, any.unix ==2.7.2.2, any.unix-compat ==0.5.3, unix-compat -old-time, any.unix-time ==0.4.7, - any.unliftio ==0.2.20, any.unliftio-core ==0.2.0.1, - any.unordered-containers ==0.2.15.0, + any.unordered-containers ==0.2.13.0, unordered-containers -debug, any.utf8-string ==1.0.2, - any.uuid-types ==1.0.5, + any.uuid-types ==1.0.4, any.vault ==0.3.1.5, vault +useghc, - any.vector ==0.12.3.1, + any.vector ==0.12.2.0, vector +boundschecks -internalchecks -unsafechecks -wall, any.wai ==3.2.3, - any.wai-extra ==3.1.7, + any.wai-extra ==3.1.6, wai-extra -build-example, any.wai-logger ==2.3.6, - any.warp ==3.3.18, + any.warp ==3.3.14, warp +allow-sendfilefd -network-bytestring -warp-debug, any.wcwidth ==0.0.2, wcwidth -cli +split-base, @@ -237,4 +232,4 @@ constraints: any.Cabal ==3.0.1.0, any.xml ==1.3.14, any.zlib ==0.6.2.3, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config -index-state: hackage.haskell.org 2021-11-19T09:31:56Z +index-state: hackage.haskell.org 2021-04-06T14:17:41Z From 9f472f35f6bcaf5b32b8e5de8c20dc773491d783 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 18:05:11 +0100 Subject: [PATCH 25/73] Use correct webauthPK in backend-tests --- backend-tests/backend-tests.hs | 156 ++++++++++++++++----------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 0d61581e10..5bfb34863b 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -325,21 +325,21 @@ callIIRejectWith cid user_id l x expectedMessagePattern = do -- (e.g. with 'createSecretKeyWebAuthnECDSA'). This ensures the key contents -- are stable. -- See also: https://github.com/dfinity/ic-hs/issues/59 -webauthPK :: PublicKey -webauthPK = "0^0\f\ACK\n+\ACK\SOH\EOT\SOH\131\184C\SOH\SOH\ETXN\NUL\165\SOH\STX\ETX& \SOH!X lR\190\173]\245, \138\155\FS{\224\166\bGW>[\228\172O\224\142\164\128\&6\208\186\GS*\207\"X \179=\174\184;\201\199}\138\215b\253h\227\234\176\134\132\228c\196\147Q\179\171*\DC4\164\NUL\DC3\131\135" +webauth1PK :: PublicKey +webauth1PK = "0^0\f\ACK\n+\ACK\SOH\EOT\SOH\131\184C\SOH\SOH\ETXN\NUL\165\SOH\STX\ETX& \SOH!X lR\190\173]\245, \138\155\FS{\224\166\bGW>[\228\172O\224\142\164\128\&6\208\186\GS*\207\"X \179=\174\184;\201\199}\138\215b\253h\227\234\176\134\132\228c\196\147Q\179\171*\DC4\164\NUL\DC3\131\135" webauthID :: EntityId -webauthID = EntityId $ mkSelfAuthenticatingId webauthPK +webauthID = EntityId $ mkSelfAuthenticatingId webauth1PK device1 :: DeviceData device1 = empty .+ #alias .== "device1" - .+ #pubkey .== webauthPK + .+ #pubkey .== webauth1PK .+ #credential_id .== Nothing .+ #purpose .== enum #authentication .+ #key_type .== enum #cross_platform webauth2SK :: SecretKey webauth2SK = createSecretKeyWebAuthnRSA "foobar2" --- The content here doesn't matter as long as it's different from webauthPK +-- The content here doesn't matter as long as it's different from webauth1PK webauth2PK = toPublicKey webauth2SK webauth2PK :: PublicKey webauth2ID :: EntityId @@ -382,8 +382,8 @@ addTS :: (a, b, c, d) -> e -> (a, b, c, e) addTS (a,b,c, _) ts = (a,b,c,ts) getAndValidate :: HasCallStack => Blob -> Blob -> Blob -> EntityId -> (Word64, T.Text, Blob, Maybe Word64) -> Word64 -> M () -getAndValidate cid sessionPK userPK webauthID delegationArgs ts = do - sd <- queryII cid webauthID #get_delegation (addTS delegationArgs ts) >>= \case +getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts = do + sd <- queryII cid webauth1ID #get_delegation (addTS delegationArgs ts) >>= \case V.IsJust (V.Label :: Label "signed_delegation") sd -> return sd V.IsJust (V.Label :: Label "no_such_delegation") () -> liftIO $ assertFailure "Got unexpected no_such_delegation" @@ -398,8 +398,8 @@ getAndValidate cid sessionPK userPK webauthID delegationArgs ts = do Right () -> return () getButNotThere :: HasCallStack => Blob -> EntityId -> (Word64, T.Text, Blob, Maybe Word64) -> Word64 -> M () -getButNotThere cid webauthID delegationArgs ts = do - queryII cid webauthID #get_delegation (addTS delegationArgs ts) >>= \case +getButNotThere cid webauth1ID delegationArgs ts = do + queryII cid webauth1ID #get_delegation (addTS delegationArgs ts) >>= \case V.IsJust (V.Label :: Label "signed_delegation") _ -> liftIO $ assertFailure "Unexpected delegation" V.IsJust (V.Label :: Label "no_such_delegation") () -> return () @@ -512,7 +512,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ _ <- callII cid webauthID #register (device1, powAt cid 1) callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" , withoutUpgrade $ iiTest "get delegation without authorization" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) @@ -526,58 +526,58 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid 123 [] , withUpgrade $ \should_upgrade -> iiTest "register and lookup" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber assertStats cid 1 when should_upgrade $ doUpgrade cid assertStats cid 1 lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and lookup (with credential id)" $ \cid -> do - user_number <- register cid device2 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth2ID device2 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid lookupIs cid user_number [device2] , withUpgrade $ \should_upgrade -> iiTest "register add lookup" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid - callII cid webauthID #add (user_number, device2) + callII cid webauth1ID #add (user_number, device2) when should_upgrade $ doUpgrade cid lookupIs cid user_number [device1, device2] , withUpgrade $ \should_upgrade -> iiTest "register and add with wrong user" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid callIIReject cid webauth2ID #add (user_number, device2) lookupIs cid user_number [device1] , withUpgrade $ \should_upgrade -> iiTest "register and get principal with wrong user" $ \cid -> do queryIIReject cid webauth2ID #get_principal (10000, "front.end.com") - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid queryIIReject cid webauth2ID #get_principal (user_number, "front.end.com") , withUpgrade $ \should_upgrade -> iiTest "get delegation and validate" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) -- prepare delegation - (userPK, ts) <- callII cid webauthID #prepare_delegation delegationArgs + (userPK, ts) <- callII cid webauth1ID #prepare_delegation delegationArgs ts <- if should_upgrade then do doUpgrade cid -- after upgrade, no signature is available V.IsJust (V.Label :: Label "no_such_delegation") () - <- queryII cid webauthID #get_delegation (addTS delegationArgs ts) + <- queryII cid webauth1ID #get_delegation (addTS delegationArgs ts) -- so request it again - (userPK', ts') <- callII cid webauthID #prepare_delegation delegationArgs + (userPK', ts') <- callII cid webauth1ID #prepare_delegation delegationArgs lift $ userPK' @?= userPK return ts' else return ts V.IsJust (V.Label :: Label "signed_delegation") sd - <- queryII cid webauthID #get_delegation (addTS delegationArgs ts) + <- queryII cid webauth1ID #get_delegation (addTS delegationArgs ts) let delegation = sd .! #delegation let sig = sd .! #signature lift $ delegation .! #pubkey @?= sessionPK @@ -588,7 +588,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ Right () -> return () , withUpgrade $ \should_upgrade -> iiTest "get delegation with wrong user" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber when should_upgrade $ do doUpgrade cid @@ -598,96 +598,96 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ callIIRejectWith cid webauth2ID #prepare_delegation delegationArgs "[a-z0-9-]+ could not be authenticated." , withUpgrade $ \should_upgrade -> iiTest "get multiple delegations and validate" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) -- request a few delegations - (userPK, ts1) <- callII cid webauthID #prepare_delegation delegationArgs - getAndValidate cid sessionPK userPK webauthID delegationArgs ts1 + (userPK, ts1) <- callII cid webauth1ID #prepare_delegation delegationArgs + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts1 - (userPK, ts2) <- callII cid webauthID #prepare_delegation delegationArgs - getAndValidate cid sessionPK userPK webauthID delegationArgs ts1 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts2 + (userPK, ts2) <- callII cid webauth1ID #prepare_delegation delegationArgs + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts1 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts2 when should_upgrade $ do doUpgrade cid - (userPK, ts3) <- callII cid webauthID #prepare_delegation delegationArgs - unless should_upgrade $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts1 - unless should_upgrade $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts2 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts3 + (userPK, ts3) <- callII cid webauth1ID #prepare_delegation delegationArgs + unless should_upgrade $ getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts1 + unless should_upgrade $ getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts2 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts3 - (userPK, ts4) <- callII cid webauthID #prepare_delegation delegationArgs - unless should_upgrade $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts1 - unless should_upgrade $ getAndValidate cid sessionPK userPK webauthID delegationArgs ts2 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts3 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 + (userPK, ts4) <- callII cid webauth1ID #prepare_delegation delegationArgs + unless should_upgrade $ getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts1 + unless should_upgrade $ getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts2 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts3 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts4 , withoutUpgrade $ iiTest "get multiple delegations and expire" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) -- request a few delegations - (userPK, ts1) <- callII cid webauthID #prepare_delegation delegationArgs - getAndValidate cid sessionPK userPK webauthID delegationArgs ts1 + (userPK, ts1) <- callII cid webauth1ID #prepare_delegation delegationArgs + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts1 setCanisterTimeTo cid (30*1000_000_000) - (userPK, ts2) <- callII cid webauthID #prepare_delegation delegationArgs - getAndValidate cid sessionPK userPK webauthID delegationArgs ts1 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts2 + (userPK, ts2) <- callII cid webauth1ID #prepare_delegation delegationArgs + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts1 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts2 setCanisterTimeTo cid (70*1000_000_000) - (userPK, ts3) <- callII cid webauthID #prepare_delegation delegationArgs - getButNotThere cid webauthID delegationArgs ts1 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts2 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts3 + (userPK, ts3) <- callII cid webauth1ID #prepare_delegation delegationArgs + getButNotThere cid webauth1ID delegationArgs ts1 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts2 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts3 setCanisterTimeTo cid (120*1000_000_000) - (userPK, ts4) <- callII cid webauthID #prepare_delegation delegationArgs - getButNotThere cid webauthID delegationArgs ts1 - getButNotThere cid webauthID delegationArgs ts2 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts3 - getAndValidate cid sessionPK userPK webauthID delegationArgs ts4 + (userPK, ts4) <- callII cid webauth1ID #prepare_delegation delegationArgs + getButNotThere cid webauth1ID delegationArgs ts1 + getButNotThere cid webauth1ID delegationArgs ts2 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts3 + getAndValidate cid sessionPK userPK webauth1ID delegationArgs ts4 , withUpgrade $ \should_upgrade -> iiTest "user identities differ" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs1 = (user_number, "front.end.com", sessionPK, Nothing) - (user1PK, _exp) <- callII cid webauthID #prepare_delegation delegationArgs1 - Principal user1Principal <- queryII cid webauthID #get_principal (user_number, "front.end.com") + (user1PK, _exp) <- callII cid webauth1ID #prepare_delegation delegationArgs1 + Principal user1Principal <- queryII cid webauth1ID #get_principal (user_number, "front.end.com") lift $ user1Principal @?= mkSelfAuthenticatingId user1PK when should_upgrade $ do doUpgrade cid let delegationArgs2 = (user_number, "other-front.end.com", sessionPK, Nothing) - (user2PK, _exp) <- callII cid webauthID #prepare_delegation delegationArgs2 - Principal user2Principal <- queryII cid webauthID #get_principal (user_number, "other-front.end.com") + (user2PK, _exp) <- callII cid webauth1ID #prepare_delegation delegationArgs2 + Principal user2Principal <- queryII cid webauth1ID #get_principal (user_number, "other-front.end.com") lift $ user2Principal @?= mkSelfAuthenticatingId user2PK when (user1PK == user2PK) $ lift $ assertFailure "User identities coincide for different frontends" , withUpgrade $ \should_upgrade -> iiTest "remove()" $ \cid -> do - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber lookupIs cid user_number [device1] - callII cid webauthID #add (user_number, device2) + callII cid webauth1ID #add (user_number, device2) lookupIs cid user_number [device1, device2] -- NB: removing device that is signing this: - callII cid webauthID #remove (user_number, webauthPK) + callII cid webauth1ID #remove (user_number, webauth1PK) lookupIs cid user_number [device2] when should_upgrade $ doUpgrade cid lookupIs cid user_number [device2] callII cid webauth2ID #remove (user_number, webauth2PK) when should_upgrade $ doUpgrade cid lookupIs cid user_number [] - user_number2 <- register cid device1 (powAt cid 1) >>= mustGetUserNumber + user_number2 <- register cid webauth1ID device1 (powAt cid 1) >>= mustGetUserNumber when should_upgrade $ doUpgrade cid when (user_number == user_number2) $ lift $ assertFailure "Identity Anchor re-used" @@ -697,10 +697,10 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lift $ s .! #assigned_user_number_range @?= (100, 103) assertStats cid 0 - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber liftIO $ user_number @?= 100 assertStats cid 1 - user_number <- register cid device1 (powAt cid 1) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 1) >>= mustGetUserNumber liftIO $ user_number @?= 101 assertStats cid 2 @@ -712,20 +712,20 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ let expected_upper_bound = if should_upgrade then 100 + 3_774_873 else 103 lift $ s .! #assigned_user_number_range @?= (100, expected_upper_bound) - user_number <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber liftIO $ user_number @?= 102 assertStats cid 3 - getChallenge cid >>= callIIReject cid webauthID #register . (device1, powAt cid 0,) + getChallenge cid webauth1ID >>= callIIReject cid webauth1ID #register . (device1, powAt cid 0,) assertStats cid 3 , withoutUpgrade $ iiTestWithInit "empty init range" (100, 100) $ \cid -> do s <- queryII cid dummyUserId #stats () lift $ s .! #assigned_user_number_range @?= (100, 100) - response <- register cid device1 (powAt cid 0) + response <- register cid webauth1ID device1 (powAt cid 0) assertVariant #canister_full response , withUpgrade $ \should_upgrade -> iiTest "metrics endpoint" $ \cid -> do - _ <- register cid device2 (powAt cid 1) >>= mustGetUserNumber + _ <- register cid webauth2ID device2 (powAt cid 1) >>= mustGetUserNumber metrics <- callII cid webauth2ID #http_request (httpGet "/metrics") >>= mustParseMetrics assertMetric metrics "internet_identity_user_count" 1.0 @@ -733,13 +733,13 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ when should_upgrade $ doUpgrade cid - userNumber <- register cid device1 (powAt cid 0) >>= mustGetUserNumber + userNumber <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (userNumber, "front.end.com", sessionPK, Nothing) - _ <- callII cid webauthID #prepare_delegation delegationArgs + _ <- callII cid webauth1ID #prepare_delegation delegationArgs - metrics <- callII cid webauthID #http_request (httpGet "/metrics") >>= mustParseMetrics + metrics <- callII cid webauth1ID #http_request (httpGet "/metrics") >>= mustParseMetrics assertMetric metrics "internet_identity_user_count" 2.0 assertMetric metrics "internet_identity_signature_count" 1.0 @@ -781,13 +781,13 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ lookupIs cid 10_002 [#alias .== "andrew-mbp" .+ #credential_id .== Just "\SOH\191#%\217u\247\178L-K\182\254\249J.m\187\179_I\ACK\137\244`\163o\SI\150qz\197Hz\214\&8\153\239\213\159\208\RS\243\138\171\138\"\139\173\170\ESC\148\205\129\149ri\\Dn,7\151\146\175\DEL" .+ #pubkey .== "0^0\f\ACK\n+\ACK\SOH\EOT\SOH\131\184C\SOH\SOH\ETXN\NUL\165\SOH\STX\ETX& \SOH!X rMm*\229BDe\SOH4\228u\170\206\216\216-ER\v\166r\217\137,\141\227M*@\230\243\"X \225\248\159\191\242\224Z>\241\163\\\GS\155\178\222\139^\136V\253q\v\SUBSJ\bA\131\\\183\147\170" .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown,#alias .== "andrew phone chrome" .+ #credential_id .== Just ",\235x\NUL\a\140`~\148\248\233C/\177\205\158\ETX0\129\167" .+ #pubkey .== "0^0\f\ACK\n+\ACK\SOH\EOT\SOH\131\184C\SOH\SOH\ETXN\NUL\165\SOH\STX\ETX& \SOH!X \140\169\203@\ETX\CAN\ETB,\177\153\179\223/|`\US\STX\252r\190s(.\188\136\171\SI\181V*\174@\"X \245<\174AbV\225\234\ENQ\146\247\129\245\ACK\200\205\217\250g\219\179)\197\252\164i\172kXh\180\205" .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown] lookupIs cid 10_029 [#alias .== "Pixel" .+ #credential_id .== Just "\SOH\146\238\160b\223\132\205\231b\239\243F\170\163\167\251D\241\170\EM\216\136\174@r\149\183|LuKu[+{\144\217\ETBL\f\244\GS>\179\146\143\RS\179\DLE\227\179\164\188\NULDQy\223\SI\132\183\248\177\219" .+ #pubkey .== "0^0\f\ACK\n+\ACK\SOH\EOT\SOH\131\184C\SOH\SOH\ETXN\NUL\165\SOH\STX\ETX& \SOH!X \200B>\DEL\GS\248\220\145\245\153\221\&6\131\243uAQCAd>\145k\nw\233\&5\218\SUB~_\244\"X O]7\167=n\ESC\SUB\198\235\208\215s\158\191Gz\143\136\237i\146\203\&6\182\196\129\239\238\SOH\180b" .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown] -- This user record has been created manullay with dfx and our test - -- webauthPK has been added, so that we can actually log into this now + -- webauth1PK has been added, so that we can actually log into this now let dfxPK = "0*0\ENQ\ACK\ETX+ep\ETX!\NUL\241\186;\128\206$\243\130\250\&2\253\a#<\235\142\&0]W\218\254j\211\209\192\SO@\DC3\NAKi&1" - lookupIs cid 10_030 [#alias .== "dfx" .+ #credential_id .== Nothing .+ #pubkey .== dfxPK .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown,#alias .== "testkey" .+ #credential_id .== Nothing .+ #pubkey .== webauthPK .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown] - callII cid webauthID #remove (10_030, dfxPK) - lookupIs cid 10_030 [#alias .== "testkey" .+ #credential_id .== Nothing .+ #pubkey .== webauthPK .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown] + lookupIs cid 10_030 [#alias .== "dfx" .+ #credential_id .== Nothing .+ #pubkey .== dfxPK .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown,#alias .== "testkey" .+ #credential_id .== Nothing .+ #pubkey .== webauth1PK .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown] + callII cid webauth1ID #remove (10_030, dfxPK) + lookupIs cid 10_030 [#alias .== "testkey" .+ #credential_id .== Nothing .+ #pubkey .== webauth1PK .+ #purpose .== enum #authentication .+ #key_type .== enum #unknown] let delegationArgs = (10_030, "example.com", "dummykey", Nothing) - (userPK,_) <- callII cid webauthID #prepare_delegation delegationArgs + (userPK,_) <- callII cid webauth1ID #prepare_delegation delegationArgs -- Check that we get the same user key; this proves that the salt was -- recovered from the backup lift $ userPK @?= "0<0\x0c\&\x06\&\x0a\&+\x06\&\x01\&\x04\&\x01\&\x83\&\xb8\&C\x01\&\x02\&\x03\&,\x00\&\x0a\&\x00\&\x00\&\x00\&\x00\&\x00\&\x00\&\x00\&\x07\&\x01\&\x01\&:\x89\&&\x91\&M\xd1\&\xc8\&6\xec\&g\xba\&f\xac\&d%\xc2\&\x1d\&\xff\&\xd3\&\xca\&\x5c\&Yh\x85\&_\x87\&x\x0a\&\x1e\&\xc5\&y\x85\&" @@ -839,12 +839,12 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ .+ #wasm_module .== wasm .+ #arg .== Candid.encode () - getChallenge cid = do + getChallenge cid webauthID = do challenge <- callII cid webauthID #create_challenge () pure $ #key .== challenge .! #challenge_key .+ #chars .== T.pack "a" - register cid device pow = - getChallenge cid >>= callII cid webauthID #register . (device, pow,) + register cid webauthID device pow = + getChallenge cid webauthID >>= callII cid webauthID #register . (device, pow,) asHex :: Blob -> String asHex = T.unpack . H.encodeHex . BS.toStrict From f2eaa33735cfd9f33c396254873096936c74f9e4 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 19 Nov 2021 18:54:48 +0100 Subject: [PATCH 26/73] Add captcha input in e2e tests --- src/frontend/src/test-e2e/views.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index a2be0d3a71..a9152ad892 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -50,9 +50,12 @@ export class RegisterView extends View { await this.browser .$("#confirmRegisterButton") .waitForDisplayed({ timeout: 25_000 }); + await this.browser.$("#captchaInput").waitForDisplayed({ timeout: 25_000 }); } async confirmRegisterConfirm(): Promise { + // In tests, the captchas are hard-coded to the following string: "a" + await this.browser.$("#captchaInput").setValue("a"); await this.browser.$("#confirmRegisterButton").click(); } From 32c78db22d30be2090de86b51e9ee19f719f7a53 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 22 Nov 2021 14:31:57 +0100 Subject: [PATCH 27/73] Show text when captcha didn't work --- src/frontend/src/flows/confirmRegister.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 144635c774..ffebd0cbfe 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -62,6 +62,10 @@ const tryRegister = ( } else { // TODO: should we only get here on specific result.kind? like badCaptcha? console.log("Something didn't work, retrying"); + const loadingCaptchaText = document.querySelector( + ".loading-captcha-text" + ) as HTMLElement; + loadingCaptchaText.innerHTML = "Something didn't work, please retry"; requestCaptcha(); } }); From 91a4474a6792d72504ca9774bd9631f256899931 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 22 Nov 2021 15:13:18 +0100 Subject: [PATCH 28/73] Try debugging lost request --- src/frontend/src/flows/confirmRegister.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index ffebd0cbfe..82701975ed 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -133,6 +133,11 @@ const init = ( e.preventDefault(); e.stopPropagation(); + const loadingCaptchaText = document.querySelector( + ".loading-captcha-text" + ) as HTMLElement; + loadingCaptchaText.innerHTML = "Checking ..."; + const captchaChars = captchaInput.value; const captchaKey = confirmRegisterButton.dataset.captchaKey; From 079c761089838568d0106cf6dd4d05af86715f75 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 22 Nov 2021 17:42:25 +0100 Subject: [PATCH 29/73] Dummy commit to trigger Actions --- src/frontend/src/flows/confirmRegister.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 82701975ed..f4e73e151e 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -96,6 +96,8 @@ const requestCaptcha = () => { ".loading-captcha-text" ) as HTMLElement; loadingCaptchaText.innerHTML = "please copy the chars"; + + confirmRegisterButton.removeAttribute('disabled'); } }); }; @@ -136,7 +138,9 @@ const init = ( const loadingCaptchaText = document.querySelector( ".loading-captcha-text" ) as HTMLElement; - loadingCaptchaText.innerHTML = "Checking ..."; + loadingCaptchaText.innerHTML = "Checking …"; + confirmRegisterButton.setAttribute('disabled', ''); + const captchaChars = captchaInput.value; const captchaKey = confirmRegisterButton.dataset.captchaKey; From 74251463d86d115f02bfde16f8a6f0bbd8cb5992 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 22 Nov 2021 17:57:47 +0100 Subject: [PATCH 30/73] Run formatter --- src/frontend/src/flows/confirmRegister.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index f4e73e151e..a41f887c95 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -97,7 +97,7 @@ const requestCaptcha = () => { ) as HTMLElement; loadingCaptchaText.innerHTML = "please copy the chars"; - confirmRegisterButton.removeAttribute('disabled'); + confirmRegisterButton.removeAttribute("disabled"); } }); }; @@ -139,8 +139,7 @@ const init = ( ".loading-captcha-text" ) as HTMLElement; loadingCaptchaText.innerHTML = "Checking …"; - confirmRegisterButton.setAttribute('disabled', ''); - + confirmRegisterButton.setAttribute("disabled", ""); const captchaChars = captchaInput.value; const captchaKey = confirmRegisterButton.dataset.captchaKey; From 85bb672246d1bb155d7b0b52c0b992317421bec0 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 22 Nov 2021 19:52:26 +0100 Subject: [PATCH 31/73] Fix backend-tests files --- backend-tests/cabal.project | 2 +- backend-tests/cabal.project.freeze | 134 +++++++++++++++-------------- 2 files changed, 72 insertions(+), 64 deletions(-) diff --git a/backend-tests/cabal.project b/backend-tests/cabal.project index 88b4c5e4a3..25e0bc5b3b 100644 --- a/backend-tests/cabal.project +++ b/backend-tests/cabal.project @@ -8,7 +8,7 @@ source-repository-package source-repository-package type: git location: https://github.com/nomeata/haskell-candid - tag: 01157c50ec29b1f8ab35ca27f38dc592ed5406f9 + tag: 98f580c191e4584973717866981af393531a34eb source-repository-package type: git diff --git a/backend-tests/cabal.project.freeze b/backend-tests/cabal.project.freeze index c71403643b..1a7d14f6df 100644 --- a/backend-tests/cabal.project.freeze +++ b/backend-tests/cabal.project.freeze @@ -2,10 +2,10 @@ active-repositories: hackage.haskell.org:merge constraints: any.Cabal ==3.0.1.0, any.FloatingHex ==0.5, any.HUnit ==1.6.2.0, - any.MonadRandom ==0.5.2, + any.MonadRandom ==0.5.3, any.QuickCheck ==2.14.2, QuickCheck -old-random +templatehaskell, - any.StateVar ==1.2.1, + any.StateVar ==1.2.2, any.aeson ==1.5.2.0, aeson -bytestring-builder -cffi -developer -fast, any.ansi-terminal ==0.11, @@ -18,7 +18,7 @@ constraints: any.Cabal ==3.0.1.0, any.asn1-parse ==0.9.5, any.asn1-types ==0.3.4, any.assoc ==1.0.2, - any.async ==2.2.3, + any.async ==2.2.4, async -bench, any.atomic-write ==0.2.0.7, any.attoparsec ==0.13.2.5, @@ -27,46 +27,48 @@ constraints: any.Cabal ==3.0.1.0, any.base ==4.13.0.0, any.base-compat ==0.11.2, any.base-compat-batteries ==0.11.2, - any.base-orphans ==0.8.4, - any.base16-bytestring ==1.0.1.0, - any.base32 ==0.2.0.0, - any.base64-bytestring ==1.2.0.1, - any.basement ==0.0.11, + any.base-orphans ==0.8.6, + any.base-unicode-symbols ==0.2.4.2, + base-unicode-symbols +base-4-8 -old-base, + any.base16-bytestring ==1.0.2.0, + any.base32 ==0.2.1.0, + any.base64-bytestring ==1.2.1.0, + any.basement ==0.0.12, any.bifunctors ==5.5.7, bifunctors +semigroups +tagged, any.binary ==0.8.7.0, any.bindings-DSL ==1.0.25, - any.blaze-builder ==0.4.2.1, + any.blaze-builder ==0.4.2.2, any.blaze-html ==0.9.1.2, any.blaze-markup ==0.8.2.8, any.bsb-http-chunked ==0.0.0.4, any.byte-order ==0.1.2.0, any.byteorder ==1.0.4, any.bytestring ==0.10.10.1, - any.cabal-doctest ==1.0.8, - any.call-stack ==0.3.0, - any.candid ==0.2, + any.cabal-doctest ==1.0.9, + any.call-stack ==0.4.0, + any.candid ==0.3, any.case-insensitive ==1.2.1.0, - any.cborg ==0.2.4.0, + any.cborg ==0.2.6.0, cborg +optimize-gmp, - any.cereal ==0.5.8.1, + any.cereal ==0.5.8.2, cereal -bytestring-builder, any.clock ==0.8.2, clock -llvm, any.cmdargs ==0.10.21, cmdargs +quotation -testprog, - any.colour ==2.3.5, + any.colour ==2.3.6, any.comonad ==5.0.8, comonad +containers +distributive +indexed-traversable, any.connection ==0.3.1, - any.constraints ==0.12, + any.constraints ==0.13.2, any.containers ==0.6.2.1, - any.contravariant ==1.5.3, + any.contravariant ==1.5.5, contravariant +semigroups +statevar +tagged, any.cookie ==0.4.5, any.crc ==0.1.1.1, crc -lib-werror, - any.cryptonite ==0.28, + any.cryptonite ==0.29, cryptonite -check_alignment +integer-gmp -old_toolchain_inliner +support_aesni +support_deepseq -support_pclmuldq +support_rdrand -support_sse +use_target_attributes, any.data-default-class ==0.1.2.0, any.data-fix ==0.2.1, @@ -80,54 +82,55 @@ constraints: any.Cabal ==3.0.1.0, ed25519 +no-donna +test-doctests +test-hlint +test-properties, any.exceptions ==0.10.4, exceptions +transformers-0-4, - any.fast-logger ==3.0.3, + any.fast-logger ==3.0.5, + any.file-embed ==0.0.15.0, any.filepath ==1.4.2.1, any.generic-deriving ==1.13.1, generic-deriving +base-4-9, - any.generic-lens ==2.1.0.0, - any.generic-lens-core ==2.1.0.0, + any.generic-lens ==2.2.0.0, + any.generic-lens-core ==2.2.0.0, any.ghc-boot-th ==8.8.4, any.ghc-byteorder ==4.11.0.0.10, any.ghc-prim ==0.5.3, any.half ==0.3.1, - any.hashable ==1.3.1.0, - hashable +integer-gmp, + any.hashable ==1.3.5.0, + hashable +integer-gmp -random-initial-seed, any.hex-text ==0.1.0.4, any.hourglass ==0.2.12, - any.hsc2hs ==0.68.7, + any.hsc2hs ==0.68.8, hsc2hs -in-ghc-tree, - any.http-client ==0.7.6, + any.http-client ==0.7.9, http-client +network-uri, any.http-client-tls ==0.3.5.3, any.http-date ==0.0.11, any.http-types ==0.12.3, - any.http2 ==2.0.6, - http2 -devel, + any.http2 ==3.0.2, + http2 -devel -doc -h2spec, any.ic-hs ==0.0.1, - ic-hs -release, - any.indexed-profunctors ==0.1, - any.indexed-traversable ==0.1.1, + ic-hs +library -release, + any.indexed-profunctors ==0.1.1, + any.indexed-traversable ==0.1.2, any.integer-gmp ==1.0.2.0, any.integer-logarithms ==1.0.3.1, integer-logarithms -check-bounds +integer-gmp, - any.iproute ==1.7.11, + any.iproute ==1.7.12, any.leb128-cereal ==1.2, any.lifted-base ==0.2.3.12, - any.megaparsec ==9.0.1, + any.megaparsec ==9.2.0, megaparsec -dev, - any.memory ==0.15.0, + any.memory ==0.16.0, memory +support_basement +support_bytestring +support_deepseq +support_foundation, any.microlens ==0.4.12.0, - any.microlens-ghc ==0.4.13, + any.microlens-ghc ==0.4.13.1, any.microlens-mtl ==0.2.0.1, - any.microlens-platform ==0.4.2, + any.microlens-platform ==0.4.2.1, any.microlens-th ==0.4.3.6, any.mime-types ==0.1.0.9, - any.monad-control ==1.0.2.3, + any.monad-control ==1.0.3.1, any.mtl ==2.2.2, any.nats ==1.1.2, nats +binary +hashable +template-haskell, - any.network ==3.1.2.1, + any.network ==3.1.2.5, network -devel, any.network-byte-order ==0.1.6, any.network-uri ==2.6.4.1, @@ -141,39 +144,41 @@ constraints: any.Cabal ==3.0.1.0, parser-combinators -dev, any.pem ==0.2.4, any.pretty ==1.1.3.6, - any.prettyprinter ==1.7.0, - prettyprinter -buildreadme, - any.primitive ==0.7.1.0, + any.prettyprinter ==1.7.1, + prettyprinter -buildreadme +text, + any.primitive ==0.7.3.0, any.primitive-unaligned ==0.1.1.1, any.process ==1.6.9.0, any.profunctors ==5.6, - any.psqueues ==0.2.7.2, + any.psqueues ==0.2.7.3, any.quickcheck-io ==0.2.0, - any.random ==1.2.0, - any.regex-base ==0.94.0.1, - any.regex-tdfa ==1.3.1.0, + any.random ==1.2.1, + any.regex-base ==0.94.0.2, + any.regex-tdfa ==1.3.1.1, regex-tdfa -force-o2, - any.resourcet ==1.2.4.2, - any.row-types ==1.0.1.0, + any.resourcet ==1.2.4.3, + any.row-types ==1.0.1.2, any.rts ==1.0, - any.scientific ==0.3.6.2, + any.scientific ==0.3.7.0, scientific -bytestring-builder -integer-simple, - any.semigroups ==0.19.1, + any.semigroups ==0.20, semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers, - any.serialise ==0.2.3.0, + any.serialise ==0.2.4.0, serialise +newtime15, any.simple-sendfile ==0.2.30, simple-sendfile +allow-bsd, any.socks ==0.6.1, any.split ==0.2.3.4, - any.splitmix ==0.1.0.3, + any.splitmix ==0.1.0.4, splitmix -optimised-mixer, any.stm ==2.5.0.0, - any.streaming-commons ==0.2.2.1, + any.streaming-commons ==0.2.2.2, streaming-commons -use-bytestring-builder, + any.strict ==0.4.0.1, + strict +assoc, any.tagged ==0.8.6.1, tagged +deepseq +transformers, - any.tasty ==1.4.1, + any.tasty ==1.4.2, tasty +clock +unix, any.tasty-ant-xml ==1.1.8, any.tasty-html ==0.4.1.4, @@ -183,43 +188,46 @@ constraints: any.Cabal ==3.0.1.0, any.template-haskell ==2.15.0.0, any.temporary ==1.3, any.text ==1.2.4.0, - any.text-short ==0.1.3, + any.text-short ==0.1.4, text-short -asserts, any.th-abstraction ==0.3.2.0, - any.th-compat ==0.1.2, + any.th-compat ==0.1.3, any.these ==1.1.1.1, these +assoc, any.time ==1.9.3, - any.time-compat ==1.9.5, + any.time-compat ==1.9.6.1, time-compat -old-locale, any.time-manager ==0.0.0, any.tls ==1.5.5, tls +compat -hans +network, any.transformers ==0.5.6.2, - any.transformers-base ==0.4.5.2, + any.transformers-base ==0.4.6, transformers-base +orphaninstances, - any.transformers-compat ==0.6.6, + any.transformers-compat ==0.7.1, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, any.type-equality ==1, + any.uglymemo ==0.1.0.1, any.unbounded-delays ==0.1.1.1, any.unix ==2.7.2.2, any.unix-compat ==0.5.3, unix-compat -old-time, any.unix-time ==0.4.7, + any.unliftio ==0.2.20, any.unliftio-core ==0.2.0.1, - any.unordered-containers ==0.2.13.0, + any.unordered-containers ==0.2.15.0, unordered-containers -debug, any.utf8-string ==1.0.2, - any.uuid-types ==1.0.4, + any.uuid-types ==1.0.5, any.vault ==0.3.1.5, vault +useghc, - any.vector ==0.12.2.0, + any.vector ==0.12.3.1, vector +boundschecks -internalchecks -unsafechecks -wall, any.wai ==3.2.3, - any.wai-extra ==3.1.6, + any.wai-cors ==0.2.7, + any.wai-extra ==3.1.7, wai-extra -build-example, any.wai-logger ==2.3.6, - any.warp ==3.3.14, + any.warp ==3.3.18, warp +allow-sendfilefd -network-bytestring -warp-debug, any.wcwidth ==0.0.2, wcwidth -cli +split-base, @@ -232,4 +240,4 @@ constraints: any.Cabal ==3.0.1.0, any.xml ==1.3.14, any.zlib ==0.6.2.3, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config -index-state: hackage.haskell.org 2021-04-06T14:17:41Z +index-state: hackage.haskell.org 2021-11-19T13:35:38Z From 385a64e277fa0521ca27471272a572d699de04c5 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 22 Nov 2021 19:53:56 +0100 Subject: [PATCH 32/73] Try fixing e2e-tests --- src/frontend/src/test-e2e/views.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index a9152ad892..a5fa97c99f 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -56,6 +56,7 @@ export class RegisterView extends View { async confirmRegisterConfirm(): Promise { // In tests, the captchas are hard-coded to the following string: "a" await this.browser.$("#captchaInput").setValue("a"); + await this.browser.$("#confirmRegisterButton").waitForEnabled(); await this.browser.$("#confirmRegisterButton").click(); } From a66d126bb82f4c0f86a49a93c802a8a43e303e2c Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 10:48:17 +0100 Subject: [PATCH 33/73] Update backend-tests post rebase --- backend-tests/backend-tests.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 5bfb34863b..854c991910 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -327,8 +327,8 @@ callIIRejectWith cid user_id l x expectedMessagePattern = do -- See also: https://github.com/dfinity/ic-hs/issues/59 webauth1PK :: PublicKey webauth1PK = "0^0\f\ACK\n+\ACK\SOH\EOT\SOH\131\184C\SOH\SOH\ETXN\NUL\165\SOH\STX\ETX& \SOH!X lR\190\173]\245, \138\155\FS{\224\166\bGW>[\228\172O\224\142\164\128\&6\208\186\GS*\207\"X \179=\174\184;\201\199}\138\215b\253h\227\234\176\134\132\228c\196\147Q\179\171*\DC4\164\NUL\DC3\131\135" -webauthID :: EntityId -webauthID = EntityId $ mkSelfAuthenticatingId webauth1PK +webauth1ID :: EntityId +webauth1ID = EntityId $ mkSelfAuthenticatingId webauth1PK device1 :: DeviceData device1 = empty .+ #alias .== "device1" From f20a5ae71b07daf3a8768bbe74635aee23ac9daa Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 10:48:27 +0100 Subject: [PATCH 34/73] Change disabled logic in confirmRegister --- src/frontend/src/flows/confirmRegister.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index a41f887c95..05395924d3 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -86,8 +86,6 @@ const requestCaptcha = () => { const confirmRegisterButton = form.querySelector( "#confirmRegisterButton" ) as HTMLFormElement; - - confirmRegisterButton.removeAttribute("disabled"); confirmRegisterButton.setAttribute( "data-captcha-key", `${captchaResp.challenge_key}` @@ -96,8 +94,8 @@ const requestCaptcha = () => { ".loading-captcha-text" ) as HTMLElement; loadingCaptchaText.innerHTML = "please copy the chars"; + confirmRegisterButton.disabled = false; - confirmRegisterButton.removeAttribute("disabled"); } }); }; @@ -139,7 +137,7 @@ const init = ( ".loading-captcha-text" ) as HTMLElement; loadingCaptchaText.innerHTML = "Checking …"; - confirmRegisterButton.setAttribute("disabled", ""); + confirmRegisterButton.disabled = true; const captchaChars = captchaInput.value; const captchaKey = confirmRegisterButton.dataset.captchaKey; From 69aba2f5b33eda415a43786b4c2b35279fa13193 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 10:58:07 +0100 Subject: [PATCH 35/73] npm run format --- src/frontend/src/flows/confirmRegister.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 05395924d3..fc17fd2801 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -95,7 +95,6 @@ const requestCaptcha = () => { ) as HTMLElement; loadingCaptchaText.innerHTML = "please copy the chars"; confirmRegisterButton.disabled = false; - } }); }; From 37bff2b2b8e0cdad17494c0426b394c6d4c45e92 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 11:04:46 +0100 Subject: [PATCH 36/73] Bump captcha waitForEnabled timeout to 10s --- src/frontend/src/test-e2e/views.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index a5fa97c99f..2ff03d163c 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -56,7 +56,9 @@ export class RegisterView extends View { async confirmRegisterConfirm(): Promise { // In tests, the captchas are hard-coded to the following string: "a" await this.browser.$("#captchaInput").setValue("a"); - await this.browser.$("#confirmRegisterButton").waitForEnabled(); + await this.browser + .$("#confirmRegisterButton") + .waitForEnabled({ timeout: 10_000 }); await this.browser.$("#confirmRegisterButton").click(); } From 7c328ca2f8332d0718de6d12a2d6a5a45be19b4b Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 11:10:54 +0100 Subject: [PATCH 37/73] Disabled e2e tests to populate cache --- .github/workflows/selenium.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index a909b0b16d..9d54949afc 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -92,9 +92,9 @@ jobs: - name: install selenium webdrivers run: npm run install-webdrivers - - run: rm -v -f screenshots/*-${{ matrix.device }}.png - - run: npm test - - run: npm run test:e2e-${{ matrix.device }} + #- run: rm -v -f screenshots/*-${{ matrix.device }}.png + #- run: npm test + #- run: npm run test:e2e-${{ matrix.device }} - run: dfx stop - name: Archive test logs From 7c3b56ec037f5af8dd51055be486b6d733e34b64 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 11:11:10 +0100 Subject: [PATCH 38/73] Re-enable e2e-tests --- .github/workflows/selenium.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index 9d54949afc..a909b0b16d 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -92,9 +92,9 @@ jobs: - name: install selenium webdrivers run: npm run install-webdrivers - #- run: rm -v -f screenshots/*-${{ matrix.device }}.png - #- run: npm test - #- run: npm run test:e2e-${{ matrix.device }} + - run: rm -v -f screenshots/*-${{ matrix.device }}.png + - run: npm test + - run: npm run test:e2e-${{ matrix.device }} - run: dfx stop - name: Archive test logs From e5b20423bc764b217b810685cb5b0f5b6d8565c5 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 11:56:57 +0100 Subject: [PATCH 39/73] Disable emulator e2e tests --- .github/workflows/selenium.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index a909b0b16d..140e024779 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - start-flag: [ '', '--emulator' ] + start-flag: [ '' ] device: [ 'desktop', 'mobile' ] # Make sure that one failing test does not cancel all other matrix jobs fail-fast: false From 82cd5471c378d39afdcb2b5a7fc8e58b85c6df04 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 14:03:33 +0100 Subject: [PATCH 40/73] Add conditional compilation for emulator --- Cargo.lock | 20 ++++++++---- src/internet_identity/Cargo.toml | 4 +++ src/internet_identity/src/main.rs | 54 ++++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cab4872ae9..b959dfa386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,12 @@ dependencies = [ [[package]] >>>>>>> 9e862d6 (Pull in captcha lib) name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" @@ -275,7 +281,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -332,7 +338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "lazy_static", ] @@ -495,7 +501,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -506,7 +512,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -603,7 +609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "606276ed1ce363eb9ccaf492e36fb40425417dcd4598f261d47e0ed6a1309faa" dependencies = [ "candid", - "cfg-if", + "cfg-if 1.0.0", "serde", ] @@ -813,7 +819,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1356,7 +1362,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" dependencies = [ "block-buffer", - "cfg-if", + "cfg-if 1.0.0", "cpuid-bool", "digest", "opaque-debug", diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index 0fe01741de..adef64bb1b 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2018" [dependencies] +rand = "0.8.3" base64 = "0.13.0" certified_map = { path = "../certified_map" } cubehash = { path = "../cubehash" } @@ -27,3 +28,6 @@ rand = "0.8.3" [build-dependencies] sha2 = "0.9.1" + +[features] +ic_backend_emulated = [] diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index f5fc51f337..6c76d67b90 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -14,7 +14,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::convert::TryInto; use storage::{Salt, Storage}; -use rand_chacha::rand_core::SeedableRng; +use rand_chacha::rand_core::{SeedableRng, RngCore}; mod assets; @@ -22,7 +22,7 @@ const fn secs_to_nanos(secs: u64) -> u64 { secs * 1_000_000_000 } -//use captcha::{gen, Difficulty, Captcha}; +#[cfg(not(feature = "ic_backend_emulated"))] use captcha::filters::{Wave}; // 30 mins @@ -52,6 +52,8 @@ type FrontendHostname = String; type Timestamp = u64; // in nanos since epoch type Signature = ByteBuf; +struct Base64(String); + #[derive(Clone, Debug, CandidType, Deserialize)] enum Purpose { #[serde(rename = "recovery")] @@ -441,32 +443,52 @@ async fn create_challenge() -> CaptchaResponse { let rng = rand_chacha::ChaCha20Rng::from_seed(seed); - let mut captcha = captcha::RngCaptcha::from_rng(rng); - // TODO: use this in production: - //let captcha = captcha.add_chars(5) - //.apply_filter(Wave::new(2.0, 20.0).horizontal()) - //.apply_filter(Wave::new(2.0, 20.0).vertical()) - //.view(220, 120); + let (Base64(png_base64), chars) = create_captcha(rng); + let resp = CaptchaResponse { png_base64, challenge_key }; + + // TODO: const-this-up + let challenge: Challenge = Challenge { created: now, chars: chars}; + + // Finally insert + inflight_challenges.insert(challenge_key, challenge); + + resp + + }); + + resp +} +#[cfg(feature = "ic_backend_emulated")] +fn create_captcha(rng: T) -> (Base64, String) { + + let mut captcha = captcha::RngCaptcha::from_rng(rng); let captcha = captcha.set_chars(&vec!['a']).add_chars(1) .view(10,10); let resp = match captcha.as_base64() { - Some(png_base64) => CaptchaResponse { png_base64, challenge_key }, + Some(png_base64) => Base64(png_base64), None => trap("Could not get base64 of captcha"), }; - // TODO: const-this-up - let challenge: Challenge = Challenge { created: now, chars: captcha.chars_as_string() }; + return (resp, captcha.chars_as_string()); +} - // Finally insert - inflight_challenges.insert(challenge_key, challenge); +#[cfg(not(feature = "ic_backend_emulated"))] +fn create_captcha(rng: T) -> (Base64, String) { - resp + let mut captcha = captcha::RngCaptcha::from_rng(rng); + let captcha = captcha.add_chars(5) + .apply_filter(Wave::new(2.0, 20.0).horizontal()) + .apply_filter(Wave::new(2.0, 20.0).vertical()) + .view(220, 120); - }); + let resp = match captcha.as_base64() { + Some(png_base64) => Base64(png_base64), + None => trap("Could not get base64 of captcha"), + }; - resp + return (resp, captcha.chars_as_string()); } // just traps if challenge isn't OK, because when in rome... From 9ac57ced84e91a616b98b7cd6031232954def3cd Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 14:06:59 +0100 Subject: [PATCH 41/73] Rename captcha feature and use in tests --- .github/workflows/backend-tests.yml | 4 ++-- src/internet_identity/Cargo.toml | 2 +- src/internet_identity/src/main.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 99f782076a..2a59f460a2 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -25,7 +25,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-1 + key: ${{ runner.os }}-cargo-dummy-captcha-${{ hashFiles('**/Cargo.lock') }}-1 - name: Cache ~/.cabal/store uses: actions/cache@v2 @@ -66,7 +66,7 @@ jobs: - name: Build backend canister run: | - cargo build --target wasm32-unknown-unknown --release + cargo build --features dummy_captcha --target wasm32-unknown-unknown --release - name: Run Tests shell: bash diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index adef64bb1b..0b078ba3df 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -30,4 +30,4 @@ rand = "0.8.3" sha2 = "0.9.1" [features] -ic_backend_emulated = [] +dummy_captcha = [] diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 6c76d67b90..c81eecb078 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -22,7 +22,7 @@ const fn secs_to_nanos(secs: u64) -> u64 { secs * 1_000_000_000 } -#[cfg(not(feature = "ic_backend_emulated"))] +#[cfg(not(feature = "dummy_captcha"))] use captcha::filters::{Wave}; // 30 mins @@ -459,7 +459,7 @@ async fn create_challenge() -> CaptchaResponse { resp } -#[cfg(feature = "ic_backend_emulated")] +#[cfg(feature = "dummy_captcha")] fn create_captcha(rng: T) -> (Base64, String) { let mut captcha = captcha::RngCaptcha::from_rng(rng); @@ -474,7 +474,7 @@ fn create_captcha(rng: T) -> (Base64, String) { return (resp, captcha.chars_as_string()); } -#[cfg(not(feature = "ic_backend_emulated"))] +#[cfg(not(feature = "dummy_captcha"))] fn create_captcha(rng: T) -> (Base64, String) { let mut captcha = captcha::RngCaptcha::from_rng(rng); From 0e84f74b4e0228f3815a9c0abdbc9f33567606a2 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 14:18:10 +0100 Subject: [PATCH 42/73] Add conditional USE_DUMMY_CAPTCHA build --- .github/workflows/selenium.yml | 1 + src/internet_identity/Cargo.toml | 1 - src/internet_identity/build.sh | 16 +++++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index 140e024779..c65b3e4bde 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -68,6 +68,7 @@ jobs: - name: Deploy Internet Identity run: | export II_ENV=development + export USE_DUMMY_CAPTCHA=1 dfx deploy --no-wallet --argument '(null)' - name: Deploy whoami canister diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index 0b078ba3df..cd1981fc1e 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2018" [dependencies] -rand = "0.8.3" base64 = "0.13.0" certified_map = { path = "../certified_map" } cubehash = { path = "../cubehash" } diff --git a/src/internet_identity/build.sh b/src/internet_identity/build.sh index 66429c35cc..7286845b90 100755 --- a/src/internet_identity/build.sh +++ b/src/internet_identity/build.sh @@ -8,7 +8,21 @@ npm run build II_DIR="$(dirname "$0")" TARGET="wasm32-unknown-unknown" -cargo build --manifest-path "$II_DIR/Cargo.toml" --target $TARGET --release -j1 +cargo_build_args=( + --manifest-path "$II_DIR/Cargo.toml" + --target "$TARGET" + --release + -j1 + ) + +if [ "${USE_DUMMY_CAPTCHA:-}" == "1" ] +then + cargo_build_args+=( --features dummy_captcha ) +fi + +echo Running cargo build "${cargo_build_args[@]}" + +cargo build "${cargo_build_args[@]}" # keep version in sync with Dockerfile cargo install ic-cdk-optimizer --version 0.3.1 --root "$II_DIR"/../../target From efd4d6ea76814cd0e9dd65e547922fb3a9779c5e Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 14:51:11 +0100 Subject: [PATCH 43/73] Re-enable selenium emulator tests --- .github/workflows/selenium.yml | 2 +- src/frontend/src/test-e2e/views.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index c65b3e4bde..48264e9c6f 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - start-flag: [ '' ] + start-flag: [ '', '--emulator' ] device: [ 'desktop', 'mobile' ] # Make sure that one failing test does not cancel all other matrix jobs fail-fast: false diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index 2ff03d163c..c1dd4ce8c6 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -58,7 +58,9 @@ export class RegisterView extends View { await this.browser.$("#captchaInput").setValue("a"); await this.browser .$("#confirmRegisterButton") - .waitForEnabled({ timeout: 10_000 }); + // this is a huge timeout because generating the captcha takes a while on + // the emulator + .waitForEnabled({ timeout: 30_000 }); await this.browser.$("#confirmRegisterButton").click(); } From b6f2836024a01d72ebe5d9bc3347330aeccc223c Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 15:41:14 +0100 Subject: [PATCH 44/73] Clean up rust CAPTCHA code --- src/internet_identity/src/main.rs | 65 +++++++++++++++++++------------ 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index c81eecb078..5a9ce0b4e2 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -409,56 +409,73 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { #[update] async fn create_challenge() -> CaptchaResponse { - let raw_rand: Vec = match call(Principal::management_canister(), "raw_rand", ()).await { - Ok((res,)) => res, - Err((_, err)) => trap(&format!("failed to get seed: {}", err)), - }; - let seed: Salt = raw_rand[..].try_into().unwrap_or_else(|_| { - trap(&format!( - "when creating seed from raw_rand output, expected raw randomness to be of length 32, got {}", - raw_rand.len() - )); - }); + let rng = make_rng().await; let resp = STATE.with(|s| { let mut inflight_challenges = s.inflight_challenges.borrow_mut(); - // Prune old challenges + // Prune old challenges. This drops all challenges that are older than + // CAPTCHA_CHALLENGE_LIFETIME + // TODO: test this let now = time() as u64; inflight_challenges.retain(|_, v| v.created > now - CAPTCHA_CHALLENGE_LIFETIME); // Error out if there are too many inflight challenges - if inflight_challenges.len() > MAX_INFLIGHT_CHALLENGES { + // TODO: test this + if inflight_challenges.len() >= MAX_INFLIGHT_CHALLENGES { trap("too many inflight captchas"); } // actually create the challenge - // first, get the key - let mut challenge_key = 0; - // TODO: WARNING: very dangerous - while inflight_challenges.contains_key(&challenge_key) { - challenge_key = challenge_key + 1; + // first, try to find a key. We've already checked that we have fewer keys than + // MAX_INFLIGHT_CHALLENGES but just to make sure we use MAX_INFLIGHT_CHALLENGES as an upper + // bound of attempts. + let mut challenge_key: Option = None; + let mut attempt: u32 = 0; + while attempt < MAX_INFLIGHT_CHALLENGES as u32 { + if !inflight_challenges.contains_key(&attempt) { + challenge_key = Some(attempt); + break; + } + attempt += 1; } + let challenge_key = match challenge_key { + Some(challenge_key) => challenge_key, + None => trap(&format!("Impossible: Could not find a free key for challenge. Size/Capacity: {}/{}", inflight_challenges.len(), MAX_INFLIGHT_CHALLENGES)), + }; - let rng = rand_chacha::ChaCha20Rng::from_seed(seed); + // Then we create the CAPTCHA let (Base64(png_base64), chars) = create_captcha(rng); - let resp = CaptchaResponse { png_base64, challenge_key }; - - // TODO: const-this-up - let challenge: Challenge = Challenge { created: now, chars: chars}; // Finally insert - inflight_challenges.insert(challenge_key, challenge); + inflight_challenges.insert(challenge_key, Challenge { created: now, chars }); - resp + CaptchaResponse { png_base64, challenge_key } }); resp } +// Get a random number generator based on 'raw_rand' +async fn make_rng() -> rand_chacha::ChaCha20Rng { + + let raw_rand: Vec = match call(Principal::management_canister(), "raw_rand", ()).await { + Ok((res,)) => res, + Err((_, err)) => trap(&format!("failed to get seed: {}", err)), + }; + let seed: Salt = raw_rand[..].try_into().unwrap_or_else(|_| { + trap(&format!( + "when creating seed from raw_rand output, expected raw randomness to be of length 32, got {}", + raw_rand.len() + )); + }); + + rand_chacha::ChaCha20Rng::from_seed(seed) +} + #[cfg(feature = "dummy_captcha")] fn create_captcha(rng: T) -> (Base64, String) { From 330760a8b8835d5ab75af3e2e919cce203a3c7e4 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 15:47:39 +0100 Subject: [PATCH 45/73] Document dummy_captcha --- .github/workflows/backend-tests.yml | 2 ++ .github/workflows/selenium.yml | 2 ++ src/internet_identity/Cargo.toml | 2 ++ src/internet_identity/build.sh | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 2a59f460a2..6a38b4cec9 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -66,6 +66,8 @@ jobs: - name: Build backend canister run: | + # we use the dummy_captcha feature which ensures the captcha string + # is always "a" cargo build --features dummy_captcha --target wasm32-unknown-unknown --release - name: Run Tests diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index 48264e9c6f..5c6c0b6989 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -68,6 +68,8 @@ jobs: - name: Deploy Internet Identity run: | export II_ENV=development + # we use the dummy_captcha feature which ensures the captcha string + # is always "a" export USE_DUMMY_CAPTCHA=1 dfx deploy --no-wallet --argument '(null)' diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index cd1981fc1e..8cb507a237 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -29,4 +29,6 @@ rand = "0.8.3" sha2 = "0.9.1" [features] +# the dummy_captcha feature which ensures the captcha string is always "a" +# (needed for tests) dummy_captcha = [] diff --git a/src/internet_identity/build.sh b/src/internet_identity/build.sh index 7286845b90..868703bba0 100755 --- a/src/internet_identity/build.sh +++ b/src/internet_identity/build.sh @@ -15,6 +15,10 @@ cargo_build_args=( -j1 ) +# This enables the "dummy_captcha" feature which makes sure the captcha string +# is always "a". +# WARNING: this MUST be opt-in, because we DO NOT want this in production, +# EVAR. if [ "${USE_DUMMY_CAPTCHA:-}" == "1" ] then cargo_build_args+=( --features dummy_captcha ) From e572ca0edd492089b5930c3f9308614ed9e28c8c Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 15:56:15 +0100 Subject: [PATCH 46/73] Clean up frontend code --- src/frontend/src/flows/confirmRegister.ts | 30 ++++++++++------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index fc17fd2801..6ad55311ab 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -17,7 +17,7 @@ const pageContent = html`

Confirm new device

-

Loading captcha...

+

…

Please confirm to add your device.

@@ -46,12 +46,9 @@ const tryRegister = ( func: (result: LoginResult) => void ) => { withLoader(async () => { - console.log("Registering..."); return IIConnection.register(identity, alias, pow, challengeResult); }).then((result) => { - console.log(`Result: ${result.kind}`); if (result.kind == "loginSuccess") { - console.log("Result kind was success"); // Write user number to storage setUserNumber(result.userNumber); @@ -60,25 +57,27 @@ const tryRegister = ( func(apiResultToLoginResult(result)); }); } else { + // TODO; here add "badCaptcha". If we see anything else, return result. // TODO: should we only get here on specific result.kind? like badCaptcha? - console.log("Something didn't work, retrying"); - const loadingCaptchaText = document.querySelector( - ".loading-captcha-text" + const captchaStatusText = document.querySelector( + ".captcha-status-text" ) as HTMLElement; - loadingCaptchaText.innerHTML = "Something didn't work, please retry"; + captchaStatusText.innerHTML = "Something didn't work, please retry"; requestCaptcha(); } }); }; -// TODO: disable confirm button // TODO: add message about loading captcha const requestCaptcha = () => { const form = document.getElementById("confirmForm") as HTMLFormElement; + const captchaStatusText = document.querySelector( + ".captcha-status-text" + ) as HTMLElement; + captchaStatusText.innerHTML = "Creating CAPTCHA challenge…"; IIConnection.createChallenge().then((captchaResp) => { const captchaImg = document.querySelector("#captchaImg"); if (captchaImg) { - console.log("got captchaImg"); captchaImg.setAttribute( "src", `data:image/png;base64, ${captchaResp.png_base64}` @@ -90,10 +89,7 @@ const requestCaptcha = () => { "data-captcha-key", `${captchaResp.challenge_key}` ); - const loadingCaptchaText = document.querySelector( - ".loading-captcha-text" - ) as HTMLElement; - loadingCaptchaText.innerHTML = "please copy the chars"; + captchaStatusText.innerHTML = "Please type in the characters you see."; confirmRegisterButton.disabled = false; } }); @@ -132,10 +128,10 @@ const init = ( e.preventDefault(); e.stopPropagation(); - const loadingCaptchaText = document.querySelector( - ".loading-captcha-text" + const captchaStatusText = document.querySelector( + ".captcha-status-text" ) as HTMLElement; - loadingCaptchaText.innerHTML = "Checking …"; + captchaStatusText.innerHTML = "Checking CAPTCHA challenge…"; confirmRegisterButton.disabled = true; const captchaChars = captchaInput.value; From bfae49f3ec7b3a5a4a166d8c479d64ec42e26993 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 15:59:23 +0100 Subject: [PATCH 47/73] Add some TODOs --- src/internet_identity/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 5a9ce0b4e2..9586cb31a7 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -154,6 +154,7 @@ enum RegisterResponse { Registered { user_number: UserNumber }, #[serde(rename = "canister_full")] CanisterFull, + // TODO: add BadCaptcha here } mod hash; @@ -247,6 +248,7 @@ struct ChallengeResult { } // TODO: rename to ChallengeSomething +// TODO: add response if no more keys // What we send the user #[derive(Clone, Debug, CandidType, Deserialize)] struct CaptchaResponse { From 87a6b34c7ea5d82aa916a1a5879cf502e92bf80d Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 16:53:27 +0100 Subject: [PATCH 48/73] Update lodepng-rust --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b959dfa386..8b7047e8d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -805,7 +805,7 @@ checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "lodepng" version = "3.4.6" -source = "git+https://github.com/nmattia/lodepng-rust?branch=nm-wasm32#304bcfc415ffa204ec9e82d5ec535708ed1bcca8" +source = "git+https://github.com/nmattia/lodepng-rust?rev=3619fb04d1b0cce5be4b0ecc1c46710296a1cb95#3619fb04d1b0cce5be4b0ecc1c46710296a1cb95" dependencies = [ "fallible_collections", "flate2", diff --git a/Cargo.toml b/Cargo.toml index fc2b8fb7b3..be0f0d5c84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ lto = true opt-level = 'z' [patch.crates-io] -lodepng = { git = 'https://github.com/nmattia/lodepng-rust', branch = 'nm-wasm32' } +lodepng = { git = 'https://github.com/nmattia/lodepng-rust', rev = '3619fb04d1b0cce5be4b0ecc1c46710296a1cb95' } From 3353904cd1f11ec81691e3fa40a8989bead382ca Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 17:04:48 +0100 Subject: [PATCH 49/73] Clean up backend-tests --- backend-tests/backend-tests.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 854c991910..b594342ef5 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -137,7 +137,6 @@ type ProofOfWork = [Candid.candidType|record { type ChallengeResult = [Candid.candidType|record { key : nat32; chars: text; - }|] type HttpRequest = [Candid.candidType|record { @@ -843,6 +842,9 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ challenge <- callII cid webauthID #create_challenge () pure $ #key .== challenge .! #challenge_key .+ #chars .== T.pack "a" + -- Go through a challenge request/registration flow for this device. + -- NOTE: this (dummily) solves the challenge with the string "a", which is + -- returned by the backend when compiled with USE_DUMMY_CAPTCHA. register cid webauthID device pow = getChallenge cid webauthID >>= callII cid webauthID #register . (device, pow,) From 678f75abf07829636a05839bffb0359e9b52a8c0 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 17:11:55 +0100 Subject: [PATCH 50/73] Rename CaptchaResponse --- .../generated/internet_identity_idl.js | 4 +-- .../generated/internet_identity_types.d.ts | 5 ++-- src/frontend/src/utils/iiConnection.ts | 4 +-- src/internet_identity/internet_identity.did | 8 ++---- src/internet_identity/src/main.rs | 25 +++++++++---------- 5 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index 00b0629e5e..3ff49592bf 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -24,7 +24,7 @@ export const idlFactory = ({ IDL }) => { 'credential_id' : IDL.Opt(CredentialId), }); const ChallengeKey = IDL.Nat32; - const CaptchaResponse = IDL.Record({ + const Challenge = IDL.Record({ 'png_base64' : IDL.Text, 'challenge_key' : ChallengeKey, }); @@ -91,7 +91,7 @@ export const idlFactory = ({ IDL }) => { }); return IDL.Service({ 'add' : IDL.Func([UserNumber, DeviceData], [], []), - 'create_challenge' : IDL.Func([], [CaptchaResponse], []), + 'create_challenge' : IDL.Func([], [Challenge], []), 'get_delegation' : IDL.Func( [UserNumber, FrontendHostname, SessionKey, Timestamp], [GetDelegationResponse], diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index 6382c745df..7f29fa8fe0 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -1,9 +1,8 @@ import type { Principal } from '@dfinity/principal'; -export interface CaptchaResponse { +export interface Challenge { 'png_base64' : string, 'challenge_key' : ChallengeKey, } -export interface Challenge { 'created' : Timestamp, 'chars' : string } export type ChallengeKey = number; export interface ChallengeResult { 'key' : ChallengeKey, 'chars' : string } export type CredentialId = Array; @@ -71,7 +70,7 @@ export type UserKey = PublicKey; export type UserNumber = bigint; export interface _SERVICE { 'add' : (arg_0: UserNumber, arg_1: DeviceData) => Promise, - 'create_challenge' : () => Promise, + 'create_challenge' : () => Promise, 'get_delegation' : ( arg_0: UserNumber, arg_1: FrontendHostname, diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 00ab45d058..a5fec8c042 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -11,7 +11,7 @@ import { PublicKey, SessionKey, CredentialId, - CaptchaResponse, + Challenge, UserNumber, FrontendHostname, Timestamp, @@ -213,7 +213,7 @@ export class IIConnection { return await baseActor.lookup(userNumber); } - static async createChallenge(): Promise { + static async createChallenge(): Promise { console.log("OK creating challenge"); const agent = new HttpAgent(); agent.fetchRootKey(); diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index a438bd7285..385f03da50 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -49,7 +49,7 @@ type KeyType = variant { seed_phrase; }; -type CaptchaResponse = record { +type Challenge = record { png_base64: text; challenge_key: ChallengeKey; }; @@ -103,10 +103,6 @@ type ProofOfWork = record { }; type ChallengeKey = nat32; -type Challenge = record { - created : Timestamp; - chars : text; -}; type ChallengeResult = record { key : ChallengeKey; @@ -115,7 +111,7 @@ type ChallengeResult = record { service : (opt InternetIdentityInit) -> { init_salt: () -> (); - create_challenge : () -> (CaptchaResponse); + create_challenge : () -> (Challenge); register : (DeviceData, ProofOfWork, ChallengeResult) -> (RegisterResponse); add : (UserNumber, DeviceData) -> (); remove : (UserNumber, DeviceKey) -> (); diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 9586cb31a7..966fde229f 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -212,7 +212,7 @@ struct State { asset_hashes: RefCell, last_upgrade_timestamp: Cell, // TODO: note: we COULD persist this through upgrades - inflight_challenges: RefCell>, + inflight_challenges: RefCell>, } impl Default for State { @@ -232,26 +232,25 @@ impl Default for State { } } -type ChallengeKey = u32; - // The challenges we store and check against -#[derive(Clone, Debug, CandidType, Deserialize)] -struct Challenge { +struct ChallengeInfo { created: Timestamp, chars: String, } +type ChallengeKey = u32; + +// The user's attempt #[derive(Clone, Debug, CandidType, Deserialize)] -struct ChallengeResult { +struct ChallengeAttempt { chars: String, key: ChallengeKey } -// TODO: rename to ChallengeSomething // TODO: add response if no more keys // What we send the user #[derive(Clone, Debug, CandidType, Deserialize)] -struct CaptchaResponse { +struct Challenge { png_base64: String, challenge_key: ChallengeKey, } @@ -287,7 +286,7 @@ async fn init_salt() { } #[update] -async fn register(device_data: DeviceData, pow: ProofOfWork, challenge_result: ChallengeResult) -> RegisterResponse { +async fn register(device_data: DeviceData, pow: ProofOfWork, challenge_result: ChallengeAttempt) -> RegisterResponse { check_challenge(challenge_result); check_entry_limits(&device_data); let now = time() as u64; @@ -409,7 +408,7 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { // TODO: should this take a PoW to prevent spam? #[update] -async fn create_challenge() -> CaptchaResponse { +async fn create_challenge() -> Challenge { let rng = make_rng().await; @@ -452,9 +451,9 @@ async fn create_challenge() -> CaptchaResponse { let (Base64(png_base64), chars) = create_captcha(rng); // Finally insert - inflight_challenges.insert(challenge_key, Challenge { created: now, chars }); + inflight_challenges.insert(challenge_key, ChallengeInfo { created: now, chars }); - CaptchaResponse { png_base64, challenge_key } + Challenge { png_base64, challenge_key } }); @@ -511,7 +510,7 @@ fn create_captcha(rng: T) -> (Base64, String) { } // just traps if challenge isn't OK, because when in rome... -fn check_challenge(res: ChallengeResult) { +fn check_challenge(res: ChallengeAttempt) { STATE.with(|s| { let mut inflight_challenges = s.inflight_challenges.borrow_mut(); From d0ee991b994c995cefdeb7735792317ae3d5e334 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 17:19:28 +0100 Subject: [PATCH 51/73] Clean up frontend code more --- src/frontend/src/flows/confirmRegister.ts | 4 ++-- src/frontend/src/flows/displayUserNumber.ts | 5 +---- src/frontend/src/flows/register.ts | 4 ---- src/frontend/src/utils/iiConnection.ts | 4 ---- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 6ad55311ab..8e9f9d9967 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -68,8 +68,8 @@ const tryRegister = ( }); }; -// TODO: add message about loading captcha -const requestCaptcha = () => { +// Request a captcha and, when received, update the DOM elements accordingly. +const requestCaptcha = (): void => { const form = document.getElementById("confirmForm") as HTMLFormElement; const captchaStatusText = document.querySelector( ".captcha-status-text" diff --git a/src/frontend/src/flows/displayUserNumber.ts b/src/frontend/src/flows/displayUserNumber.ts index 2f9e64a2f3..398bdbef56 100644 --- a/src/frontend/src/flows/displayUserNumber.ts +++ b/src/frontend/src/flows/displayUserNumber.ts @@ -35,8 +35,5 @@ const init = (): Promise => const displayUserContinue = document.getElementById( "displayUserContinue" ) as HTMLButtonElement; - displayUserContinue.onclick = () => { - console.log("User resolving congratulations"); - resolve(); - }; + displayUserContinue.onclick = () => resolve(); }); diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index e52bcf17d0..d5375ddfd4 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -45,7 +45,6 @@ const init = (): Promise => ) as HTMLButtonElement; registerCancel.onclick = () => resolve(null); - form.onsubmit = async (e) => { e.preventDefault(); e.stopPropagation(); @@ -54,10 +53,7 @@ const init = (): Promise => "#registerAlias" ) as HTMLInputElement; const alias = registerAlias.value; - renderConstructing(); - - // WTF is this? await tick(); try { diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index a5fec8c042..969fd2ea1d 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -214,7 +214,6 @@ export class IIConnection { } static async createChallenge(): Promise { - console.log("OK creating challenge"); const agent = new HttpAgent(); agent.fetchRootKey(); const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { @@ -222,7 +221,6 @@ export class IIConnection { canisterId: canisterId, }); const challenge = await actor.create_challenge(); - console.log(challenge); return challenge; } @@ -252,9 +250,7 @@ export class IIConnection { // Only fetch the root key when we're not in prod if (process.env.II_ENV === "development") { await agent.fetchRootKey(); - console.log("Fetching root key"); } - console.log("OK"); const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { agent, canisterId: canisterId, From 4a32f63252875ed638954b2cc2e0ee8be330cfce Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 23 Nov 2021 17:23:31 +0100 Subject: [PATCH 52/73] Improve CAPTCHA trap messages --- src/internet_identity/src/main.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 966fde229f..fbbf811577 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -238,6 +238,7 @@ struct ChallengeInfo { chars: String, } +// TODO: should this be a string? type ChallengeKey = u32; // The user's attempt @@ -517,11 +518,13 @@ fn check_challenge(res: ChallengeAttempt) { match inflight_challenges.remove(&res.key) { Some(challenge) => { if res.chars != challenge.chars { - trap("BAD ANSWER"); + // NOTE: we _could_ show the expected chars here (the key has been + // removed already so the user won't be able to re-submit the answer using that + // key). + trap("CAPTCHA challenge failed"); } - }, - None => trap("nope, no challenge with that key") , + None => trap("Could not find a CAPTCHA challenge with that key") , } }) } From a9a163fb84b51b08efcc569cf1c24fc2601f8f5c Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 25 Nov 2021 18:18:49 +0100 Subject: [PATCH 53/73] Move ProofOfWork to create_challenge --- backend-tests/README.md | 2 + backend-tests/backend-tests.hs | 8 +-- .../generated/internet_identity_idl.js | 14 ++--- .../generated/internet_identity_types.d.ts | 10 ++-- src/frontend/src/flows/confirmRegister.ts | 53 ++++++++++--------- src/frontend/src/utils/iiConnection.ts | 13 +++-- src/internet_identity/internet_identity.did | 4 +- src/internet_identity/src/main.rs | 35 ++++++------ 8 files changed, 74 insertions(+), 65 deletions(-) diff --git a/backend-tests/README.md b/backend-tests/README.md index 051f71c44e..28e0596634 100644 --- a/backend-tests/README.md +++ b/backend-tests/README.md @@ -14,6 +14,8 @@ Setup Running ------- +TODO: note about dummy captcha build + * Build the top-level directory, build the backend canister (`dfx build internet_identity`) * In the present directory, run ``` diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index b594342ef5..597db20567 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -714,7 +714,7 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber liftIO $ user_number @?= 102 assertStats cid 3 - getChallenge cid webauth1ID >>= callIIReject cid webauth1ID #register . (device1, powAt cid 0,) + callIIReject cid webauth1ID #create_challenge (powAt cid 0) assertStats cid 3 , withoutUpgrade $ iiTestWithInit "empty init range" (100, 100) $ \cid -> do @@ -838,15 +838,15 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ .+ #wasm_module .== wasm .+ #arg .== Candid.encode () - getChallenge cid webauthID = do - challenge <- callII cid webauthID #create_challenge () + getChallenge cid webauthID pow = do + challenge <- callII cid webauthID #create_challenge pow pure $ #key .== challenge .! #challenge_key .+ #chars .== T.pack "a" -- Go through a challenge request/registration flow for this device. -- NOTE: this (dummily) solves the challenge with the string "a", which is -- returned by the backend when compiled with USE_DUMMY_CAPTCHA. register cid webauthID device pow = - getChallenge cid webauthID >>= callII cid webauthID #register . (device, pow,) + getChallenge cid webauthID pow >>= callII cid webauthID #register . (device,) asHex :: Blob -> String asHex = T.unpack . H.encodeHex . BS.toStrict diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index 3ff49592bf..e5e9a93e53 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -23,6 +23,11 @@ export const idlFactory = ({ IDL }) => { 'purpose' : Purpose, 'credential_id' : IDL.Opt(CredentialId), }); + const Timestamp = IDL.Nat64; + const ProofOfWork = IDL.Record({ + 'nonce' : IDL.Nat64, + 'timestamp' : Timestamp, + }); const ChallengeKey = IDL.Nat32; const Challenge = IDL.Record({ 'png_base64' : IDL.Text, @@ -30,7 +35,6 @@ export const idlFactory = ({ IDL }) => { }); const FrontendHostname = IDL.Text; const SessionKey = PublicKey; - const Timestamp = IDL.Nat64; const Delegation = IDL.Record({ 'pubkey' : PublicKey, 'targets' : IDL.Opt(IDL.Vec(IDL.Principal)), @@ -73,10 +77,6 @@ export const idlFactory = ({ IDL }) => { 'status_code' : IDL.Nat16, }); const UserKey = PublicKey; - const ProofOfWork = IDL.Record({ - 'nonce' : IDL.Nat64, - 'timestamp' : Timestamp, - }); const ChallengeResult = IDL.Record({ 'key' : ChallengeKey, 'chars' : IDL.Text, @@ -91,7 +91,7 @@ export const idlFactory = ({ IDL }) => { }); return IDL.Service({ 'add' : IDL.Func([UserNumber, DeviceData], [], []), - 'create_challenge' : IDL.Func([], [Challenge], []), + 'create_challenge' : IDL.Func([ProofOfWork], [Challenge], []), 'get_delegation' : IDL.Func( [UserNumber, FrontendHostname, SessionKey, Timestamp], [GetDelegationResponse], @@ -111,7 +111,7 @@ export const idlFactory = ({ IDL }) => { [], ), 'register' : IDL.Func( - [DeviceData, ProofOfWork, ChallengeResult], + [DeviceData, ChallengeResult], [RegisterResponse], [], ), diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index 7f29fa8fe0..e9653356de 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -70,7 +70,7 @@ export type UserKey = PublicKey; export type UserNumber = bigint; export interface _SERVICE { 'add' : (arg_0: UserNumber, arg_1: DeviceData) => Promise, - 'create_challenge' : () => Promise, + 'create_challenge' : (arg_0: ProofOfWork) => Promise, 'get_delegation' : ( arg_0: UserNumber, arg_1: FrontendHostname, @@ -89,11 +89,9 @@ export interface _SERVICE { arg_2: SessionKey, arg_3: [] | [bigint], ) => Promise<[UserKey, Timestamp]>, - 'register' : ( - arg_0: DeviceData, - arg_1: ProofOfWork, - arg_2: ChallengeResult, - ) => Promise, + 'register' : (arg_0: DeviceData, arg_1: ChallengeResult) => Promise< + RegisterResponse + >, 'remove' : (arg_0: UserNumber, arg_1: DeviceKey) => Promise, 'stats' : () => Promise, } diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 8e9f9d9967..ecfa9820eb 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -41,12 +41,11 @@ export const confirmRegister = ( const tryRegister = ( identity: WebAuthnIdentity, alias: string, - pow: ProofOfWork, challengeResult: ChallengeResult, func: (result: LoginResult) => void ) => { withLoader(async () => { - return IIConnection.register(identity, alias, pow, challengeResult); + return IIConnection.register(identity, alias, challengeResult); }).then((result) => { if (result.kind == "loginSuccess") { // Write user number to storage @@ -75,24 +74,34 @@ const requestCaptcha = (): void => { ".captcha-status-text" ) as HTMLElement; captchaStatusText.innerHTML = "Creating CAPTCHA challenge…"; - IIConnection.createChallenge().then((captchaResp) => { - const captchaImg = document.querySelector("#captchaImg"); - if (captchaImg) { - captchaImg.setAttribute( - "src", - `data:image/png;base64, ${captchaResp.png_base64}` - ); - const confirmRegisterButton = form.querySelector( - "#confirmRegisterButton" - ) as HTMLFormElement; - confirmRegisterButton.setAttribute( - "data-captcha-key", - `${captchaResp.challenge_key}` - ); - captchaStatusText.innerHTML = "Please type in the characters you see."; - confirmRegisterButton.disabled = false; - } + + // Wrap this in a promise to avoid slowing things down + const makePow: Promise = new Promise((resolve) => { + const now_in_ns = BigInt(Date.now()) * BigInt(1000000); + const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); + resolve(pow); }); + + makePow + .then((pow) => IIConnection.createChallenge(pow)) + .then((captchaResp) => { + const captchaImg = document.querySelector("#captchaImg"); + if (captchaImg) { + captchaImg.setAttribute( + "src", + `data:image/png;base64, ${captchaResp.png_base64}` + ); + const confirmRegisterButton = form.querySelector( + "#confirmRegisterButton" + ) as HTMLFormElement; + confirmRegisterButton.setAttribute( + "data-captcha-key", + `${captchaResp.challenge_key}` + ); + captchaStatusText.innerHTML = "Please type in the characters you see."; + confirmRegisterButton.disabled = false; + } + }); }; const init = ( @@ -106,10 +115,6 @@ const init = ( // this whole logic in a promise that then resolves (giving control back to // the caller) return new Promise((resolve) => { - // Create a PoW before registering - const now_in_ns = BigInt(Date.now()) * BigInt(1000000); - const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); - const confirmRegisterButton = document.querySelector( "#confirmRegisterButton" ) as HTMLFormElement; @@ -141,7 +146,7 @@ const init = ( key: Number(captchaKey), chars: captchaChars, }; - tryRegister(identity, alias, pow, challengeResult, resolve); + tryRegister(identity, alias, challengeResult, resolve); }; }); }; diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 969fd2ea1d..f67a342c4f 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -82,7 +82,6 @@ export class IIConnection { static async register( identity: WebAuthnIdentity, alias: string, - pow: ProofOfWork, challengeResult: ChallengeResult ): Promise { let delegationIdentity: DelegationIdentity; @@ -96,9 +95,6 @@ export class IIConnection { const credential_id = Array.from(identity.rawId); const pubkey = Array.from(identity.getPublicKey().toDer()); - console.log( - `register(DeviceData { alias=${alias}, pubkey=${pubkey}, credential_id=${credential_id} }, ProofOfWork { timestamp=${pow.timestamp}, nonce=${pow.nonce}, challenge_key=${challengeResult.key}, challenge_chars=${challengeResult.chars})` - ); let registerResponse: RegisterResponse; try { registerResponse = await actor.register( @@ -109,7 +105,7 @@ export class IIConnection { key_type: { unknown: null }, purpose: { authentication: null }, }, - pow, + //pow, challengeResult ); } catch (error) { @@ -213,14 +209,17 @@ export class IIConnection { return await baseActor.lookup(userNumber); } - static async createChallenge(): Promise { + static async createChallenge(pow: ProofOfWork): Promise { const agent = new HttpAgent(); agent.fetchRootKey(); const actor = Actor.createActor<_SERVICE>(internet_identity_idl, { agent, canisterId: canisterId, }); - const challenge = await actor.create_challenge(); + console.log( + `createChallenge(ProofOfWork { timestamp=${pow.timestamp}, nonce=${pow.nonce} })` + ); + const challenge = await actor.create_challenge(pow); return challenge; } diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index 385f03da50..31362f7b8c 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -111,8 +111,8 @@ type ChallengeResult = record { service : (opt InternetIdentityInit) -> { init_salt: () -> (); - create_challenge : () -> (Challenge); - register : (DeviceData, ProofOfWork, ChallengeResult) -> (RegisterResponse); + create_challenge : (ProofOfWork) -> (Challenge); + register : (DeviceData, ChallengeResult) -> (RegisterResponse); add : (UserNumber, DeviceData) -> (); remove : (UserNumber, DeviceKey) -> (); lookup : (UserNumber) -> (vec DeviceData) query; diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index fbbf811577..19e57f0471 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -287,11 +287,9 @@ async fn init_salt() { } #[update] -async fn register(device_data: DeviceData, pow: ProofOfWork, challenge_result: ChallengeAttempt) -> RegisterResponse { +async fn register(device_data: DeviceData, challenge_result: ChallengeAttempt) -> RegisterResponse { check_challenge(challenge_result); check_entry_limits(&device_data); - let now = time() as u64; - check_proof_of_work(&pow, now); if caller() != Principal::self_authenticating(device_data.pubkey.clone()) { ic_cdk::trap(&format!( @@ -304,14 +302,6 @@ async fn register(device_data: DeviceData, pow: ProofOfWork, challenge_result: C ensure_salt_set().await; STATE.with(|s| { - let mut nonce_cache = s.nonce_cache.borrow_mut(); - if nonce_cache.contains(pow.timestamp, pow.nonce) { - trap(&format!( - "the combination of timestamp {} and nonce {} has already been used", - pow.timestamp, pow.nonce, - )); - } - nonce_cache.prune_expired(now.saturating_sub(POW_NONCE_LIFETIME)); prune_expired_signatures(&s.asset_hashes.borrow(), &mut s.sigs.borrow_mut()); let mut store = s.storage.borrow_mut(); @@ -322,7 +312,6 @@ async fn register(device_data: DeviceData, pow: ProofOfWork, challenge_result: C .unwrap_or_else(|err| { trap(&format!("failed to store user device data: {}", err)) }); - nonce_cache.add(pow.timestamp, pow.nonce); RegisterResponse::Registered { user_number } } None => RegisterResponse::CanisterFull, @@ -407,19 +396,35 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { }) } -// TODO: should this take a PoW to prevent spam? #[update] -async fn create_challenge() -> Challenge { +async fn create_challenge(pow: ProofOfWork) -> Challenge { let rng = make_rng().await; let resp = STATE.with(|s| { + let mut nonce_cache = s.nonce_cache.borrow_mut(); + if nonce_cache.contains(pow.timestamp, pow.nonce) { + trap(&format!( + "the combination of timestamp {} and nonce {} has already been used", + pow.timestamp, pow.nonce, + )); + } + + let now = time() as u64; + + nonce_cache.prune_expired(now.saturating_sub(POW_NONCE_LIFETIME)); + prune_expired_signatures(&s.asset_hashes.borrow(), &mut s.sigs.borrow_mut()); + + check_proof_of_work(&pow, now); + + nonce_cache.add(pow.timestamp, pow.nonce); + + let mut inflight_challenges = s.inflight_challenges.borrow_mut(); // Prune old challenges. This drops all challenges that are older than // CAPTCHA_CHALLENGE_LIFETIME // TODO: test this - let now = time() as u64; inflight_challenges.retain(|_, v| v.created > now - CAPTCHA_CHALLENGE_LIFETIME); // Error out if there are too many inflight challenges From 8a65e6c2479b055869b10d80c12af39c82dc3393 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 12:55:44 +0100 Subject: [PATCH 54/73] Make ChallengeKey a string --- backend-tests/backend-tests.hs | 2 +- .../generated/internet_identity_idl.js | 2 +- .../generated/internet_identity_types.d.ts | 2 +- src/frontend/src/flows/confirmRegister.ts | 9 ++- src/internet_identity/internet_identity.did | 2 +- src/internet_identity/src/main.rs | 68 ++++++++++++------- 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 597db20567..3e08fdd0f7 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -135,7 +135,7 @@ type ProofOfWork = [Candid.candidType|record { }|] type ChallengeResult = [Candid.candidType|record { - key : nat32; + key : text; chars: text; }|] diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index e5e9a93e53..e8d0bf5ff3 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -28,7 +28,7 @@ export const idlFactory = ({ IDL }) => { 'nonce' : IDL.Nat64, 'timestamp' : Timestamp, }); - const ChallengeKey = IDL.Nat32; + const ChallengeKey = IDL.Text; const Challenge = IDL.Record({ 'png_base64' : IDL.Text, 'challenge_key' : ChallengeKey, diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index e9653356de..c6cbdcf783 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -3,7 +3,7 @@ export interface Challenge { 'png_base64' : string, 'challenge_key' : ChallengeKey, } -export type ChallengeKey = number; +export type ChallengeKey = string; export interface ChallengeResult { 'key' : ChallengeKey, 'chars' : string } export type CredentialId = Array; export interface Delegation { diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index ecfa9820eb..5013c53d1d 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -142,10 +142,17 @@ const init = ( const captchaChars = captchaInput.value; const captchaKey = confirmRegisterButton.dataset.captchaKey; + if (captchaKey === undefined) { + console.log("Something went wrong: no captcha key found"); + requestCaptcha(); + return; + } + const challengeResult: ChallengeResult = { - key: Number(captchaKey), + key: captchaKey, chars: captchaChars, }; + tryRegister(identity, alias, challengeResult, resolve); }; }); diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index 31362f7b8c..fba7609a68 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -102,7 +102,7 @@ type ProofOfWork = record { nonce : nat64; }; -type ChallengeKey = nat32; +type ChallengeKey = text; type ChallengeResult = record { key : ChallengeKey; diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 19e57f0471..01c3e67576 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -211,7 +211,8 @@ struct State { sigs: RefCell, asset_hashes: RefCell, last_upgrade_timestamp: Cell, - // TODO: note: we COULD persist this through upgrades + // note: we COULD persist this through upgrades, although this is currently NOT persisted + // through upgrades inflight_challenges: RefCell>, } @@ -238,8 +239,7 @@ struct ChallengeInfo { chars: String, } -// TODO: should this be a string? -type ChallengeKey = u32; +type ChallengeKey = String; // The user's attempt #[derive(Clone, Debug, CandidType, Deserialize)] @@ -399,7 +399,7 @@ async fn remove(user_number: UserNumber, device_key: DeviceKey) { #[update] async fn create_challenge(pow: ProofOfWork) -> Challenge { - let rng = make_rng().await; + let mut rng = make_rng().await; let resp = STATE.with(|s| { let mut nonce_cache = s.nonce_cache.borrow_mut(); @@ -435,37 +435,53 @@ async fn create_challenge(pow: ProofOfWork) -> Challenge { // actually create the challenge - // first, try to find a key. We've already checked that we have fewer keys than - // MAX_INFLIGHT_CHALLENGES but just to make sure we use MAX_INFLIGHT_CHALLENGES as an upper - // bound of attempts. - let mut challenge_key: Option = None; - let mut attempt: u32 = 0; - while attempt < MAX_INFLIGHT_CHALLENGES as u32 { - if !inflight_challenges.contains_key(&attempt) { - challenge_key = Some(attempt); - break; - } - attempt += 1; - } + // First, we try to find a new (unique) challenge key. It's unlikely we'll have collisions + // when generating the key, but to err on the safe side we try up to 10 times. + const MAX_TRIES: u8= 10; - let challenge_key = match challenge_key { - Some(challenge_key) => challenge_key, - None => trap(&format!("Impossible: Could not find a free key for challenge. Size/Capacity: {}/{}", inflight_challenges.len(), MAX_INFLIGHT_CHALLENGES)), - }; + for _ in 0..MAX_TRIES { + let challenge_key = random_string(&mut rng, 10); + if !inflight_challenges.contains_key(&challenge_key) { + // Then we create the CAPTCHA + let (Base64(png_base64), chars) = create_captcha(rng); - // Then we create the CAPTCHA - let (Base64(png_base64), chars) = create_captcha(rng); + // Finally insert + inflight_challenges.insert(challenge_key.clone(), ChallengeInfo { created: now, chars }); - // Finally insert - inflight_challenges.insert(challenge_key, ChallengeInfo { created: now, chars }); - - Challenge { png_base64, challenge_key } + return Challenge { png_base64, challenge_key } + } + } + trap(&format!("Could not find a new key after {} tries", MAX_TRIES)); }); resp } +// Generate an n-char long string of random characters. The characters are sampled from the rang +// a-z. +// +// NOTE: The 'rand' crate (currently) does not build on wasm32-unknown-unknown so we have to +// make-do with the RngCore trait (as opposed to Rng), therefore we have to implement this +// ourselves as opposed to using one of rand's distributions. +fn random_string(rng: &mut T, n: usize) -> String { + + let mut chars: Vec = vec![]; + + // The range + let a: u8 = 'a' as u8; + let z: u8 = 'z' as u8; + + // n times, get a random number as u32, then shrink to u8, and finally shrink to the size of + // our range. Finally, offset by the start of our range. + for _ in 0..n { + let next: u8 = rng.next_u32() as u8 % (z - a) + a; + chars.push(next); + } + + return String::from_utf8_lossy(&chars).to_string(); +} + // Get a random number generator based on 'raw_rand' async fn make_rng() -> rand_chacha::ChaCha20Rng { From e982c9e57e2ae986422cafe2fea9c226e364f788 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 16:05:39 +0100 Subject: [PATCH 55/73] Show error if captcha is wrong --- src/frontend/src/flows/confirmRegister.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 5013c53d1d..860582cd91 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -1,5 +1,6 @@ import { html, render } from "lit-html"; import { displayUserNumber } from "./displayUserNumber"; +import { displayError } from "../components/displayError"; import { setUserNumber } from "../utils/userNumber"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; import { ProofOfWork } from "../../generated/internet_identity_types"; @@ -33,8 +34,10 @@ export const confirmRegister = ( identity: WebAuthnIdentity, alias: string ): Promise => { + console.log("ok, rendering"); const container = document.getElementById("pageContent") as HTMLElement; render(pageContent, container); + console.log("ok, rendered"); return init(canisterIdPrincipal, identity, alias); }; @@ -56,13 +59,19 @@ const tryRegister = ( func(apiResultToLoginResult(result)); }); } else { - // TODO; here add "badCaptcha". If we see anything else, return result. - // TODO: should we only get here on specific result.kind? like badCaptcha? - const captchaStatusText = document.querySelector( - ".captcha-status-text" - ) as HTMLElement; - captchaStatusText.innerHTML = "Something didn't work, please retry"; - requestCaptcha(); + // Currently if something goes wrong we only tell the user that + // something went wrong and then reload the page. + displayError({ + title: "Something went wrong", + message: + "We could not create an identity anchor. You will find the full error message below. Click 'ok' to reload.", + primaryButton: "Ok", + detail: JSON.stringify(result), + }) + .then(() => { + window.location.reload(); + }) + .then(() => requestCaptcha()); } }); }; From a239453bacd2572b8fbfe2ccb45268c4a21c08af Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 16:06:42 +0100 Subject: [PATCH 56/73] Remove leftover --- src/frontend/src/utils/iiConnection.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index f67a342c4f..8162db1c26 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -105,7 +105,6 @@ export class IIConnection { key_type: { unknown: null }, purpose: { authentication: null }, }, - //pow, challengeResult ); } catch (error) { From 7e814a2203e3df97ea369bd36cee743a44e43b08 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 16:50:06 +0100 Subject: [PATCH 57/73] Return 'BadChallenge' on bad CAPTCHA --- .../generated/internet_identity_idl.js | 1 + .../generated/internet_identity_types.d.ts | 3 ++- src/frontend/src/flows/confirmRegister.ts | 24 +++++++++++++++---- src/frontend/src/flows/loginUnknown.ts | 8 +++++++ src/frontend/src/utils/iiConnection.ts | 7 +++++- src/internet_identity/internet_identity.did | 2 ++ src/internet_identity/src/main.rs | 18 +++++++------- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index e8d0bf5ff3..bed3fb426a 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -82,6 +82,7 @@ export const idlFactory = ({ IDL }) => { 'chars' : IDL.Text, }); const RegisterResponse = IDL.Variant({ + 'bad_challenge' : IDL.Null, 'canister_full' : IDL.Null, 'registered' : IDL.Record({ 'user_number' : UserNumber }), }); diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index c6cbdcf783..5f34b793a3 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -50,7 +50,8 @@ export interface ProofOfWork { 'nonce' : bigint, 'timestamp' : Timestamp } export type PublicKey = Array; export type Purpose = { 'authentication' : null } | { 'recovery' : null }; -export type RegisterResponse = { 'canister_full' : null } | +export type RegisterResponse = { 'bad_challenge' : null } | + { 'canister_full' : null } | { 'registered' : { 'user_number' : UserNumber } }; export type SessionKey = PublicKey; export interface SignedDelegation { diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index 860582cd91..de018adbaa 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -21,7 +21,7 @@ const pageContent = html`

…

-

Please confirm to add your device.

+

Please confirm to add your device.

@@ -58,6 +58,13 @@ const tryRegister = ( displayUserNumber(result.userNumber).then(() => { func(apiResultToLoginResult(result)); }); + } else if (result.kind == "badChallenge") { + const confirmParagraph = document.querySelector( + ".confirm-paragraph" + ) as HTMLElement; + confirmParagraph.innerHTML = + "The value you entered is incorrect. A new challenge is generated."; + requestCaptcha(); } else { // Currently if something goes wrong we only tell the user that // something went wrong and then reload the page. @@ -84,6 +91,16 @@ const requestCaptcha = (): void => { ) as HTMLElement; captchaStatusText.innerHTML = "Creating CAPTCHA challenge…"; + const captchaInput = document.querySelector( + "#captchaInput" + ) as HTMLFormElement; + captchaInput.disabled = true; + + const confirmRegisterButton = form.querySelector( + "#confirmRegisterButton" + ) as HTMLFormElement; + confirmRegisterButton.disabled = true; + // Wrap this in a promise to avoid slowing things down const makePow: Promise = new Promise((resolve) => { const now_in_ns = BigInt(Date.now()) * BigInt(1000000); @@ -100,15 +117,14 @@ const requestCaptcha = (): void => { "src", `data:image/png;base64, ${captchaResp.png_base64}` ); - const confirmRegisterButton = form.querySelector( - "#confirmRegisterButton" - ) as HTMLFormElement; confirmRegisterButton.setAttribute( "data-captcha-key", `${captchaResp.challenge_key}` ); captchaStatusText.innerHTML = "Please type in the characters you see."; confirmRegisterButton.disabled = false; + captchaInput.disabled = false; + captchaInput.value = ""; } }); }; diff --git a/src/frontend/src/flows/loginUnknown.ts b/src/frontend/src/flows/loginUnknown.ts index 89731459b2..1f3e15fd1c 100644 --- a/src/frontend/src/flows/loginUnknown.ts +++ b/src/frontend/src/flows/loginUnknown.ts @@ -217,6 +217,14 @@ export const apiResultToLoginResult = (result: ApiResult): LoginResult => { "Failed to register with Internet Identity, because there is no space left at the moment. We're working on increasing the capacity.", }; } + case "badChallenge": { + return { + tag: "err", + title: "Failed to register", + message: + "Failed to register with Internet Identity, because the challenge wasn't successful", + }; + } case "seedPhraseFail": { return { tag: "err", diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 8162db1c26..b9f0f36591 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -57,13 +57,16 @@ export type RegisterResult = | LoginSuccess | AuthFail | ApiError - | RegisterNoSpace; + | RegisterNoSpace + | BadChallenge; type LoginSuccess = { kind: "loginSuccess"; connection: IIConnection; userNumber: bigint; }; + +type BadChallenge = { kind: "badChallenge" }; type UnknownUser = { kind: "unknownUser"; userNumber: bigint }; type AuthFail = { kind: "authFail"; error: Error }; type ApiError = { kind: "apiError"; error: Error }; @@ -121,6 +124,8 @@ export class IIConnection { connection: new IIConnection(identity, delegationIdentity, actor), userNumber, }; + } else if (hasOwnProperty(registerResponse, "bad_challenge")) { + return { kind: "badChallenge" }; } else { console.error("unexpected register response", registerResponse); throw Error("unexpected register response"); diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index fba7609a68..eae2cc0c04 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -67,6 +67,8 @@ type RegisterResponse = variant { registered: record { user_number: UserNumber; }; // No more registrations are possible in this instance of the II service canister. canister_full; + // The challenge was not successful. + bad_challenge; }; type Delegation = record { diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 01c3e67576..7d4bf15b45 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -154,7 +154,8 @@ enum RegisterResponse { Registered { user_number: UserNumber }, #[serde(rename = "canister_full")] CanisterFull, - // TODO: add BadCaptcha here + #[serde(rename = "bad_challenge")] + BadChallenge, } mod hash; @@ -288,7 +289,10 @@ async fn init_salt() { #[update] async fn register(device_data: DeviceData, challenge_result: ChallengeAttempt) -> RegisterResponse { - check_challenge(challenge_result); + if let Err(()) = check_challenge(challenge_result) { + return RegisterResponse::BadChallenge; + } + check_entry_limits(&device_data); if caller() != Principal::self_authenticating(device_data.pubkey.clone()) { @@ -532,20 +536,18 @@ fn create_captcha(rng: T) -> (Base64, String) { } // just traps if challenge isn't OK, because when in rome... -fn check_challenge(res: ChallengeAttempt) { +fn check_challenge(res: ChallengeAttempt) -> Result<(),()> { STATE.with(|s| { let mut inflight_challenges = s.inflight_challenges.borrow_mut(); match inflight_challenges.remove(&res.key) { Some(challenge) => { if res.chars != challenge.chars { - // NOTE: we _could_ show the expected chars here (the key has been - // removed already so the user won't be able to re-submit the answer using that - // key). - trap("CAPTCHA challenge failed"); + return Err(()); } + return Ok(()); }, - None => trap("Could not find a CAPTCHA challenge with that key") , + None => Err(()), } }) } From 835d667556b010c0ec23775367ad907f226c68cb Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 17:04:00 +0100 Subject: [PATCH 58/73] Post rebase fix --- Cargo.lock | 29 ----------------------------- backend-tests/backend-tests.hs | 24 +++++++++++++----------- src/internet_identity/Cargo.toml | 1 - 3 files changed, 13 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b7047e8d5..84b97adf95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,8 +212,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -======= name = "captcha" version = "0.0.8" source = "git+https://github.com/nmattia/captcha?branch=nm-set-rng#4751b6fa4e56229c2af5b9a28195ff039551d8e7" @@ -227,15 +225,6 @@ dependencies = [ ] [[package]] -name = "certified_map" -version = "0.1.0" -dependencies = [ - "hashtree", - "hex", -] - -[[package]] ->>>>>>> 9e862d6 (Pull in captcha lib) name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -540,8 +529,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] -<<<<<<< HEAD -======= name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -551,18 +538,6 @@ dependencies = [ ] [[package]] -name = "hashtree" -version = "0.1.0" -dependencies = [ - "hex", - "serde", - "serde_bytes", - "serde_cbor", - "sha2", -] - -[[package]] ->>>>>>> 9e862d6 (Pull in captcha lib) name = "hermit-abi" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -706,11 +681,7 @@ name = "internet_identity" version = "0.1.0" dependencies = [ "base64", -<<<<<<< HEAD -======= "captcha", - "certified_map", ->>>>>>> 9e862d6 (Pull in captcha lib) "cubehash", "hex", "hex-literal", diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index 3e08fdd0f7..eee799f013 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -98,6 +98,7 @@ type RegisterResponse = [Candid.candidType| variant { registered: record { user_number: nat64; }; canister_full; + bad_challenge; } |] @@ -499,23 +500,24 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ , withoutUpgrade $ iiTest "installs and upgrade" $ \ cid -> doUpgrade cid , withoutUpgrade $ iiTest "register with wrong user fails" $ \cid -> do - callIIRejectWith cid dummyUserId #register (device1, powAt cid 1, mkChallengeResult) "[a-z0-9-]+ could not be authenticated against" - , withoutUpgrade $ iiTest "register with bad pow fails" $ \cid -> do - callIIRejectWith cid webauthID #register (device1, invalidPOW, mkChallengeResult) "proof of work hash check failed" - , withoutUpgrade $ iiTest "register with future pow fails" $ \cid -> do - callIIRejectWith cid webauthID #register (device1, powAt cid (20*60*1000_000_000)) "proof of work timestamp [0-9]+ is too far in future, current time: [0-9]+" - , withoutUpgrade $ iiTest "register with past pow fails" $ \cid -> do + let callIIRejectWith' str = \c d m a -> callIIRejectWith c d m a str + getChallenge cid dummyUserId (powAt cid 1) >>= callIIRejectWith' "[a-z0-9-]+ could not be authenticated against" cid dummyUserId #register . (device1,) + , withoutUpgrade $ iiTest "create_challenge with bad pow fails" $ \cid -> do + callIIRejectWith cid webauth1ID #create_challenge invalidPOW "proof of work hash check failed" + , withoutUpgrade $ iiTest "create_challenge with future pow fails" $ \cid -> do + callIIRejectWith cid webauth1ID #create_challenge (powAt cid (20*60*1000_000_000)) "proof of work timestamp [0-9]+ is too far in future, current time: [0-9]+" + , withoutUpgrade $ iiTest "create_challenge with past pow fails" $ \cid -> do setCanisterTimeTo cid (20*60*1000_000_000) - callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "proof of work timestamp [0-9]+ is too old, current time: [0-9]+" - , withoutUpgrade $ iiTest "register with repeated pow fails" $ \cid -> do - _ <- callII cid webauthID #register (device1, powAt cid 1) - callIIRejectWith cid webauthID #register (device1, powAt cid 1, mkChallengeResult) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" + callIIRejectWith cid webauth1ID #create_challenge (powAt cid 1) "proof of work timestamp [0-9]+ is too old, current time: [0-9]+" + , withoutUpgrade $ iiTest "create_challenge with repeated pow fails" $ \cid -> do + _ <- register cid webauth1ID device1 (powAt cid 1) + callIIRejectWith cid webauth1ID #create_challenge (powAt cid 1) "the combination of timestamp [0-9]+ and nonce [0-9]+ has already been used" , withoutUpgrade $ iiTest "get delegation without authorization" $ \cid -> do user_number <- register cid webauth1ID device1 (powAt cid 0) >>= mustGetUserNumber let sessionSK = createSecretKeyEd25519 "hohoho" let sessionPK = toPublicKey sessionSK let delegationArgs = (user_number, "front.end.com", sessionPK, Nothing) - (_, ts) <- callII cid webauthID #prepare_delegation delegationArgs + (_, ts) <- callII cid webauth1ID #prepare_delegation delegationArgs queryIIRejectWith cid dummyUserId #get_delegation (addTS delegationArgs ts) "[a-z0-9-]+ could not be authenticated" , withUpgrade $ \should_upgrade -> iiTest "lookup on fresh" $ \cid -> do diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index 8cb507a237..ee87a0e11a 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -5,7 +5,6 @@ edition = "2018" [dependencies] base64 = "0.13.0" -certified_map = { path = "../certified_map" } cubehash = { path = "../cubehash" } hex = "0.4" ic-cdk = "0.3.2" From 636ad73b72f5477cc265a420be1843663e0de71d Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 17:13:03 +0100 Subject: [PATCH 59/73] Clean up --- backend-tests/backend-tests.hs | 6 +++--- src/frontend/src/flows/register.ts | 1 - src/frontend/src/styles/main.css | 1 - src/internet_identity/src/main.rs | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/backend-tests/backend-tests.hs b/backend-tests/backend-tests.hs index eee799f013..39b67c752c 100644 --- a/backend-tests/backend-tests.hs +++ b/backend-tests/backend-tests.hs @@ -313,7 +313,7 @@ callIIRejectWith cid user_id l x expectedMessagePattern = do r <- submitAndRun $ CallRequest (EntityId cid) user_id (symbolVal l) (Candid.encode x) case r of - Rejected (_code, msg) -> + Rejected (_code, msg) -> if not (msg =~ expectedMessagePattern) then liftIO $ assertFailure $ printf "expected error matching %s, got: %s" (show expectedMessagePattern) (show msg) else return () @@ -500,8 +500,8 @@ tests wasm_file = testGroup "Tests" $ upgradeGroups $ , withoutUpgrade $ iiTest "installs and upgrade" $ \ cid -> doUpgrade cid , withoutUpgrade $ iiTest "register with wrong user fails" $ \cid -> do - let callIIRejectWith' str = \c d m a -> callIIRejectWith c d m a str - getChallenge cid dummyUserId (powAt cid 1) >>= callIIRejectWith' "[a-z0-9-]+ could not be authenticated against" cid dummyUserId #register . (device1,) + challenge <- getChallenge cid dummyUserId (powAt cid 1) + callIIRejectWith cid dummyUserId #register (device1, challenge) "[a-z0-9-]+ could not be authenticated against" , withoutUpgrade $ iiTest "create_challenge with bad pow fails" $ \cid -> do callIIRejectWith cid webauth1ID #create_challenge invalidPOW "proof of work hash check failed" , withoutUpgrade $ iiTest "create_challenge with future pow fails" $ \cid -> do diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index d5375ddfd4..e2f06a9a3f 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -67,7 +67,6 @@ const init = (): Promise => await tick(); const identity = await pendingIdentity; const result = await confirmRegister(identity, alias); - console.log("Back to register"); resolve(result); } catch (err) { reject(err); diff --git a/src/frontend/src/styles/main.css b/src/frontend/src/styles/main.css index fab4770e48..9735c0a052 100644 --- a/src/frontend/src/styles/main.css +++ b/src/frontend/src/styles/main.css @@ -137,7 +137,6 @@ img { margin: auto; } -/* temporary, to work around the img { ... } above */ #captchaImg { height: 120px; width: 220px; diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index 7d4bf15b45..a31138e812 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -249,7 +249,6 @@ struct ChallengeAttempt { key: ChallengeKey } -// TODO: add response if no more keys // What we send the user #[derive(Clone, Debug, CandidType, Deserialize)] struct Challenge { From ca3b601569edfe209f6022a10736b7dbbab8e0d4 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 26 Nov 2021 17:19:15 +0100 Subject: [PATCH 60/73] Add about USE_DUMMY_BUILD in backend tests --- backend-tests/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend-tests/README.md b/backend-tests/README.md index 28e0596634..4c846a1905 100644 --- a/backend-tests/README.md +++ b/backend-tests/README.md @@ -14,7 +14,6 @@ Setup Running ------- -TODO: note about dummy captcha build * Build the top-level directory, build the backend canister (`dfx build internet_identity`) * In the present directory, run @@ -29,7 +28,8 @@ By default, this tests the wasm file in ../target/wasm32-unknown-unknown/release/internet_identity.wasm -to use a different one, pass the `--wasm` flag to `backend-tests` +to use a different one, pass the `--wasm` flag to `backend-tests`. You will +need to build with `USE_DUMMY_CAPTCHA` as the tests use a preset CAPTCHA value. You can select tests to run using `-p`, e.g. From d6b458fc06be27276e8ad8813f4dceb77c10a7c3 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 13:04:38 +0100 Subject: [PATCH 61/73] Remove fake comment --- src/internet_identity/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index a31138e812..ccd0a6cbab 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -534,7 +534,7 @@ fn create_captcha(rng: T) -> (Base64, String) { return (resp, captcha.chars_as_string()); } -// just traps if challenge isn't OK, because when in rome... +// Check whether the CAPTCHA challenge was solved fn check_challenge(res: ChallengeAttempt) -> Result<(),()> { STATE.with(|s| { From 499cc360156dacf9d64f4c7615f00de283360f66 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 13:05:23 +0100 Subject: [PATCH 62/73] Bump number of inflight challenges to 500 --- src/internet_identity/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index ccd0a6cbab..db52fb8547 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -37,7 +37,7 @@ const POW_NONCE_LIFETIME: u64 = secs_to_nanos(300); const CAPTCHA_CHALLENGE_LIFETIME: u64 = secs_to_nanos(300); // How many captcha challenges we keep in memory (at most) -const MAX_INFLIGHT_CHALLENGES: usize = 100; +const MAX_INFLIGHT_CHALLENGES: usize = 500; const LABEL_ASSETS: &[u8] = b"http_assets"; const LABEL_SIG: &[u8] = b"sig"; From 1ac9466d0aa3bd591f507d9b6e1ce5839a6531cb Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 13:06:51 +0100 Subject: [PATCH 63/73] Clarify error message --- src/frontend/src/flows/loginUnknown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/flows/loginUnknown.ts b/src/frontend/src/flows/loginUnknown.ts index 1f3e15fd1c..d794a2f17d 100644 --- a/src/frontend/src/flows/loginUnknown.ts +++ b/src/frontend/src/flows/loginUnknown.ts @@ -222,7 +222,7 @@ export const apiResultToLoginResult = (result: ApiResult): LoginResult => { tag: "err", title: "Failed to register", message: - "Failed to register with Internet Identity, because the challenge wasn't successful", + "Failed to register with Internet Identity, because the CAPTCHA challenge wasn't successful", }; } case "seedPhraseFail": { From e2ea0fcd5ac2229e463ba6084a7668db83369e7b Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 13:09:11 +0100 Subject: [PATCH 64/73] Remove old logs --- src/frontend/src/flows/confirmRegister.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index de018adbaa..a350da2e45 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -34,10 +34,8 @@ export const confirmRegister = ( identity: WebAuthnIdentity, alias: string ): Promise => { - console.log("ok, rendering"); const container = document.getElementById("pageContent") as HTMLElement; render(pageContent, container); - console.log("ok, rendered"); return init(canisterIdPrincipal, identity, alias); }; From 025d50f6528b666465addfa9d8d2ccb3d3bee024 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 13:15:44 +0100 Subject: [PATCH 65/73] Link to tickets for updating libs --- Cargo.toml | 2 ++ src/internet_identity/Cargo.toml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index be0f0d5c84..b6316d14fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,6 @@ lto = true opt-level = 'z' [patch.crates-io] +# We need a custom lodepng-rust that builds on wasm32-unknown-unknown +# https://github.com/dfinity/internet-identity/issues/471 lodepng = { git = 'https://github.com/nmattia/lodepng-rust', rev = '3619fb04d1b0cce5be4b0ecc1c46710296a1cb95' } diff --git a/src/internet_identity/Cargo.toml b/src/internet_identity/Cargo.toml index ee87a0e11a..d5318304fa 100644 --- a/src/internet_identity/Cargo.toml +++ b/src/internet_identity/Cargo.toml @@ -18,7 +18,9 @@ serde_with = "1.6.2" sha2 = "0.9.1" rand_core = "0.5.1" rand_chacha = "0.2.2" -captcha = { git = 'https://github.com/nmattia/captcha', branch = 'nm-set-rng' } +# We need a custom captcha that allows using custom RNGs +# https://github.com/dfinity/internet-identity/issues/472 +captcha = { git = 'https://github.com/nmattia/captcha', rev = '4751b6fa4e56229c2af5b9a28195ff039551d8e7' } [dev-dependencies] hex-literal = "0.2.1" From e4c41390776e77fcbed70167bf6df505e2bac1ce Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 13:51:22 +0100 Subject: [PATCH 66/73] Update lodepng-rust --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84b97adf95..c05e1162f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,7 +214,7 @@ dependencies = [ [[package]] name = "captcha" version = "0.0.8" -source = "git+https://github.com/nmattia/captcha?branch=nm-set-rng#4751b6fa4e56229c2af5b9a28195ff039551d8e7" +source = "git+https://github.com/nmattia/captcha?rev=4751b6fa4e56229c2af5b9a28195ff039551d8e7#4751b6fa4e56229c2af5b9a28195ff039551d8e7" dependencies = [ "base64", "hound", @@ -776,7 +776,7 @@ checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "lodepng" version = "3.4.6" -source = "git+https://github.com/nmattia/lodepng-rust?rev=3619fb04d1b0cce5be4b0ecc1c46710296a1cb95#3619fb04d1b0cce5be4b0ecc1c46710296a1cb95" +source = "git+https://github.com/nmattia/lodepng-rust?rev=4056af7276662af352aaf113aa91d2af76655167#4056af7276662af352aaf113aa91d2af76655167" dependencies = [ "fallible_collections", "flate2", diff --git a/Cargo.toml b/Cargo.toml index b6316d14fd..19deba41fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ opt-level = 'z' [patch.crates-io] # We need a custom lodepng-rust that builds on wasm32-unknown-unknown # https://github.com/dfinity/internet-identity/issues/471 -lodepng = { git = 'https://github.com/nmattia/lodepng-rust', rev = '3619fb04d1b0cce5be4b0ecc1c46710296a1cb95' } +lodepng = { git = 'https://github.com/nmattia/lodepng-rust', rev = '4056af7276662af352aaf113aa91d2af76655167' } From e8c3df20cab2a9a00d573037ab39705870be40ef Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 14:16:02 +0100 Subject: [PATCH 67/73] Disable selenium tests to populate GH cache --- .github/workflows/selenium.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index 5c6c0b6989..594195dbbd 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -95,9 +95,9 @@ jobs: - name: install selenium webdrivers run: npm run install-webdrivers - - run: rm -v -f screenshots/*-${{ matrix.device }}.png - - run: npm test - - run: npm run test:e2e-${{ matrix.device }} + #- run: rm -v -f screenshots/*-${{ matrix.device }}.png + #- run: npm test + #- run: npm run test:e2e-${{ matrix.device }} - run: dfx stop - name: Archive test logs From 2754e6b51df59241cc7f448914263ebd71fda3d1 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 15:04:38 +0100 Subject: [PATCH 68/73] Re-enable tests --- .github/workflows/selenium.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/selenium.yml b/.github/workflows/selenium.yml index 594195dbbd..5c6c0b6989 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/selenium.yml @@ -95,9 +95,9 @@ jobs: - name: install selenium webdrivers run: npm run install-webdrivers - #- run: rm -v -f screenshots/*-${{ matrix.device }}.png - #- run: npm test - #- run: npm run test:e2e-${{ matrix.device }} + - run: rm -v -f screenshots/*-${{ matrix.device }}.png + - run: npm test + - run: npm run test:e2e-${{ matrix.device }} - run: dfx stop - name: Archive test logs From 740cc63f7b83adb1c6035aa06fafc582d63cb68e Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 15:19:04 +0100 Subject: [PATCH 69/73] Wait for enabled captchaInput --- src/frontend/src/test-e2e/views.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index c1dd4ce8c6..808d36f391 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -54,6 +54,7 @@ export class RegisterView extends View { } async confirmRegisterConfirm(): Promise { + await this.browser.$("#captchaInput").waitForEnabled({ timeout: 30_000 }); // In tests, the captchas are hard-coded to the following string: "a" await this.browser.$("#captchaInput").setValue("a"); await this.browser From cba229811d53315bf95757e46b0de7f65e9ae4ca Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 29 Nov 2021 15:35:36 +0100 Subject: [PATCH 70/73] Bump CAPTCHA timeout --- src/frontend/src/test-e2e/views.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index 808d36f391..4ea4de9ad4 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -54,7 +54,7 @@ export class RegisterView extends View { } async confirmRegisterConfirm(): Promise { - await this.browser.$("#captchaInput").waitForEnabled({ timeout: 30_000 }); + await this.browser.$("#captchaInput").waitForEnabled({ timeout: 40_000 }); // In tests, the captchas are hard-coded to the following string: "a" await this.browser.$("#captchaInput").setValue("a"); await this.browser From 2ad96f71892c04ea81ea5c93f6f296f0e02e4efc Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 30 Nov 2021 09:28:33 +0100 Subject: [PATCH 71/73] Clarify USE_DUMMY_CAPTCHA usage --- backend-tests/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend-tests/README.md b/backend-tests/README.md index 4c846a1905..9c29b42e71 100644 --- a/backend-tests/README.md +++ b/backend-tests/README.md @@ -28,8 +28,9 @@ By default, this tests the wasm file in ../target/wasm32-unknown-unknown/release/internet_identity.wasm -to use a different one, pass the `--wasm` flag to `backend-tests`. You will -need to build with `USE_DUMMY_CAPTCHA` as the tests use a preset CAPTCHA value. +to use a different one, pass the `--wasm` flag to `backend-tests`. The tests +use a preset CAPTCHA value, so you will need to build with the following +environment variable: `USE_DUMMY_CAPTCHA=1`. You can select tests to run using `-p`, e.g. From 8171f11a0aea64896bd43669479fc869a8c76342 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Fri, 3 Dec 2021 10:24:25 +0100 Subject: [PATCH 72/73] Request CAPTCHA during Identity creation --- Cargo.lock | 455 +++++++++++----------- Cargo.toml | 2 +- src/frontend/src/flows/confirmRegister.ts | 69 ++-- src/frontend/src/flows/register.ts | 7 +- src/frontend/src/utils/iiConnection.ts | 1 + 5 files changed, 275 insertions(+), 259 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05e1162f6..226906ebf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,35 +16,29 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.15" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.44" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203" [[package]] name = "arrayvec" @@ -54,9 +48,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "ascii-canvas" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term", ] @@ -92,9 +86,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "beef" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6736e2428df2ca2848d846c43e88745121a6654696e349ce0054a420815a7409" +checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736" [[package]] name = "binread" @@ -140,17 +134,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -174,9 +157,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "candid" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c06d715bc063c90124f5bdec5029ed537c564f037fc64617c71a67fe107543" +checksum = "1d7577605c33073dcafc17a5ed6373aa0cb7005e7d4e4b7cd40ca01cb2385533" dependencies = [ "anyhow", "binread", @@ -184,7 +167,7 @@ dependencies = [ "candid_derive", "codespan-reporting", "hex", - "ic-types 0.2.1", + "ic-types 0.2.2", "lalrpop", "lalrpop-util", "leb128", @@ -224,12 +207,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -253,81 +230,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "cpuid-bool" -version = "0.1.2" +name = "cpufeatures" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ - "crossbeam-utils 0.7.2", - "maybe-uninit", + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ + "cfg-if", "crossbeam-epoch", - "crossbeam-utils 0.7.2", - "maybe-uninit", + "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.8.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", + "cfg-if", + "crossbeam-utils", "lazy_static", - "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -346,9 +306,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d6ddad5866bb2170686ed03f6839d31a76e5407d80b1c334a2c24618543ffa" +checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" dependencies = [ "darling_core", "darling_macro", @@ -356,9 +316,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ced1fd13dc386d5a8315899de465708cf34ee2a6d9394654515214e67bb846" +checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" dependencies = [ "fnv", "ident_case", @@ -370,9 +330,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7a1445d54b2f9792e3b31a3e715feabbace393f38dc4ffd49d94ee9bc487d5" +checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" dependencies = [ "darling_core", "quote", @@ -416,10 +376,20 @@ dependencies = [ ] [[package]] -name = "dirs" -version = "1.0.5" +name = "dirs-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", @@ -447,7 +417,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaefd4190151d458f16f0793d3452d7f13aeb3701566a4cefc4c37598876cc00" dependencies = [ - "hashbrown 0.11.2", + "hashbrown", ] [[package]] @@ -462,7 +432,7 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crc32fast", "libc", "miniz_oxide 0.4.4", @@ -490,18 +460,18 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -518,15 +488,9 @@ dependencies = [ [[package]] name = "half" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" - -[[package]] -name = "hashbrown" -version = "0.9.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" @@ -539,9 +503,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -584,7 +548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "606276ed1ce363eb9ccaf492e36fb40425417dcd4598f261d47e0ed6a1309faa" dependencies = [ "candid", - "cfg-if 1.0.0", + "cfg-if", "serde", ] @@ -615,22 +579,24 @@ dependencies = [ [[package]] name = "ic-types" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968dbc1cc04bf93e94c2ffd3cb370c451d2136425dd38e9ed4cc5ca425d737af" +checksum = "94dd8ec75019bc7159efe6ca2de5392e14a8e82c02ec6dae6d1b32af5337acd1" dependencies = [ "base32", "crc32fast", + "hex", "serde", + "serde_bytes", "sha2", "thiserror", ] [[package]] name = "ic-types" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471541b20b3d2bb26dd81ac0c44c66eabeaa2ba3641e96606b6c66f86a035a27" +checksum = "b2c021c11ae1d716f45d783f5764f418a11f12aea1fdc4fc8a2b2242e0dae708" dependencies = [ "base32", "crc32fast", @@ -668,12 +634,21 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", - "hashbrown 0.9.1", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", ] [[package]] @@ -688,8 +663,8 @@ dependencies = [ "ic-cdk", "ic-cdk-macros", "ic-certified-map", - "ic-types 0.1.2", - "rand 0.8.3", + "ic-types 0.1.5", + "rand 0.8.4", "rand_chacha 0.2.2", "rand_core 0.5.1", "serde", @@ -701,9 +676,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] @@ -725,9 +700,9 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46962a8c71b91c3524b117dfdd70844d4265a173c4c9109f98171aebdcf1195f" +checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" dependencies = [ "ascii-canvas", "atty", @@ -748,9 +723,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a708007b751af124d09e9c5d97515257902bc6b486a56b40bcafd939e8ff467" +checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" dependencies = [ "regex", ] @@ -763,20 +738,29 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "leb128" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] [[package]] name = "lodepng" -version = "3.4.6" -source = "git+https://github.com/nmattia/lodepng-rust?rev=4056af7276662af352aaf113aa91d2af76655167#4056af7276662af352aaf113aa91d2af76655167" +version = "3.4.7" +source = "git+https://github.com/kornelski/lodepng-rust?rev=8ceb5b1ffc9c1ab1f8c7ace9bde5f01a5be0aaa2#8ceb5b1ffc9c1ab1f8c7ace9bde5f01a5be0aaa2" dependencies = [ "fallible_collections", "flate2", @@ -790,7 +774,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -817,23 +801,17 @@ dependencies = [ "utf8-ranges", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" -version = "2.3.4" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memoffset" -version = "0.5.6" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg", ] @@ -865,9 +843,9 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "num-bigint" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -927,9 +905,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" dependencies = [ "derivative", "num_enum_derive", @@ -937,9 +915,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -959,11 +937,36 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "paste" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" [[package]] name = "petgraph" @@ -986,9 +989,9 @@ dependencies = [ [[package]] name = "pico-args" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "png" @@ -1004,9 +1007,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "precomputed-hash" @@ -1026,10 +1029,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "0.1.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" dependencies = [ + "thiserror", "toml", ] @@ -1041,18 +1045,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -1072,14 +1076,14 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.3", - "rand_hc 0.3.0", + "rand_hc 0.3.1", ] [[package]] @@ -1117,7 +1121,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.2", + "getrandom 0.2.3", ] [[package]] @@ -1131,18 +1135,18 @@ dependencies = [ [[package]] name = "rand_hc" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ "rand_core 0.6.3", ] [[package]] name = "rayon" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", @@ -1152,39 +1156,41 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "lazy_static", "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] [[package]] name = "redox_users" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ - "getrandom 0.1.16", + "getrandom 0.2.3", "redox_syscall", - "rust-argon2", ] [[package]] name = "regex" -version = "1.4.5" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", @@ -1193,9 +1199,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.23" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rgb" @@ -1206,29 +1212,17 @@ dependencies = [ "bytemuck", ] -[[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 0.8.3", -] - [[package]] name = "rustversion" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" [[package]] name = "scoped_threadpool" @@ -1244,9 +1238,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] @@ -1262,9 +1256,9 @@ dependencies = [ [[package]] name = "serde_cbor" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", @@ -1272,9 +1266,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", "quote", @@ -1283,9 +1277,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.69" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" +checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" dependencies = [ "itoa", "ryu", @@ -1305,9 +1299,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e557c650adfb38b32a5aec07082053253c703bc3cec654b27a5dbcf61995bb9b" +checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" dependencies = [ "rustversion", "serde", @@ -1316,9 +1310,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48b35457e9d855d3dc05ef32a73e0df1e2c0fd72c38796a4ee909160c8eeec2" +checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" dependencies = [ "darling", "proc-macro2", @@ -1328,31 +1322,38 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.3" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer", - "cfg-if 1.0.0", - "cpuid-bool", + "cfg-if", + "cpufeatures", "digest", "opaque-debug", ] [[package]] name = "siphasher" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "string_cache" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" +checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6" dependencies = [ "lazy_static", "new_debug_unreachable", + "parking_lot", "phf_shared", "precomputed-hash", ] @@ -1365,9 +1366,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.69" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", @@ -1376,12 +1377,12 @@ dependencies = [ [[package]] name = "term" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "byteorder", - "dirs", + "dirs-next", + "rustversion", "winapi", ] @@ -1396,18 +1397,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.24" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -1451,21 +1452,21 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" [[package]] name = "typenum" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "utf8-ranges" diff --git a/Cargo.toml b/Cargo.toml index 19deba41fd..469dd6387d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ opt-level = 'z' [patch.crates-io] # We need a custom lodepng-rust that builds on wasm32-unknown-unknown # https://github.com/dfinity/internet-identity/issues/471 -lodepng = { git = 'https://github.com/nmattia/lodepng-rust', rev = '4056af7276662af352aaf113aa91d2af76655167' } +lodepng = { git = 'https://github.com/kornelski/lodepng-rust', rev = '8ceb5b1ffc9c1ab1f8c7ace9bde5f01a5be0aaa2' } diff --git a/src/frontend/src/flows/confirmRegister.ts b/src/frontend/src/flows/confirmRegister.ts index a350da2e45..b5afd15efc 100644 --- a/src/frontend/src/flows/confirmRegister.ts +++ b/src/frontend/src/flows/confirmRegister.ts @@ -3,7 +3,7 @@ import { displayUserNumber } from "./displayUserNumber"; import { displayError } from "../components/displayError"; import { setUserNumber } from "../utils/userNumber"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; -import { ProofOfWork } from "../../generated/internet_identity_types"; +import { Challenge } from "../../generated/internet_identity_types"; import { WebAuthnIdentity } from "@dfinity/identity"; import getProofOfWork from "../crypto/pow"; import { Principal } from "@dfinity/principal"; @@ -31,12 +31,13 @@ const pageContent = html` `; export const confirmRegister = ( + captcha: Promise, identity: WebAuthnIdentity, alias: string ): Promise => { const container = document.getElementById("pageContent") as HTMLElement; render(pageContent, container); - return init(canisterIdPrincipal, identity, alias); + return init(canisterIdPrincipal, identity, alias, captcha); }; const tryRegister = ( @@ -82,7 +83,7 @@ const tryRegister = ( }; // Request a captcha and, when received, update the DOM elements accordingly. -const requestCaptcha = (): void => { +const requestCaptcha = (captcha?: Promise): void => { const form = document.getElementById("confirmForm") as HTMLFormElement; const captchaStatusText = document.querySelector( ".captcha-status-text" @@ -99,40 +100,50 @@ const requestCaptcha = (): void => { ) as HTMLFormElement; confirmRegisterButton.disabled = true; - // Wrap this in a promise to avoid slowing things down - const makePow: Promise = new Promise((resolve) => { - const now_in_ns = BigInt(Date.now()) * BigInt(1000000); - const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); - resolve(pow); + captcha = captcha || makeCaptcha(); + + captcha.then((captchaResp) => { + const captchaImg = document.querySelector("#captchaImg"); + if (captchaImg) { + captchaImg.setAttribute( + "src", + `data:image/png;base64, ${captchaResp.png_base64}` + ); + confirmRegisterButton.setAttribute( + "data-captcha-key", + `${captchaResp.challenge_key}` + ); + captchaStatusText.innerHTML = "Please type in the characters you see."; + confirmRegisterButton.disabled = false; + captchaInput.disabled = false; + captchaInput.value = ""; + } }); +}; - makePow - .then((pow) => IIConnection.createChallenge(pow)) - .then((captchaResp) => { - const captchaImg = document.querySelector("#captchaImg"); - if (captchaImg) { - captchaImg.setAttribute( - "src", - `data:image/png;base64, ${captchaResp.png_base64}` - ); - confirmRegisterButton.setAttribute( - "data-captcha-key", - `${captchaResp.challenge_key}` - ); - captchaStatusText.innerHTML = "Please type in the characters you see."; - confirmRegisterButton.disabled = false; - captchaInput.disabled = false; - captchaInput.value = ""; - } +// This computes a PoW and requests a challenge from the II backend. +// NOTE: The Proof-of-Work is computed in one go (one run of the event loop) so +// nothing else will happen during that time. Better have a loading screen +// shown to the user, or have all buttons disabled, because no other javascript +// will run for a few seconds. +export const makeCaptcha = (): Promise => + new Promise((resolve) => { + setTimeout(() => { + const now_in_ns = BigInt(Date.now()) * BigInt(1000000); + const pow = getProofOfWork(now_in_ns, canisterIdPrincipal); + IIConnection.createChallenge(pow).then((cha) => { + resolve(cha); + }); }); -}; + }); const init = ( canisterIdPrincipal: Principal, identity: WebAuthnIdentity, - alias: string + alias: string, + captcha: Promise ): Promise => { - requestCaptcha(); + requestCaptcha(captcha); // since the index expects to regain control we unfortunately have to wrap // this whole logic in a promise that then resolves (giving control back to diff --git a/src/frontend/src/flows/register.ts b/src/frontend/src/flows/register.ts index e2f06a9a3f..e86d55b018 100644 --- a/src/frontend/src/flows/register.ts +++ b/src/frontend/src/flows/register.ts @@ -1,7 +1,7 @@ import { WebAuthnIdentity } from "@dfinity/identity"; import { html, render } from "lit-html"; import { creationOptions } from "../utils/iiConnection"; -import { confirmRegister } from "./confirmRegister"; +import { confirmRegister, makeCaptcha } from "./confirmRegister"; import { apiResultToLoginResult, LoginResult } from "./loginUnknown"; import { nextTick } from "process"; import { icLogo } from "../components/icons"; @@ -65,8 +65,11 @@ const init = (): Promise => return 0 as unknown as WebAuthnIdentity; }); await tick(); + // Kick-start both the captcha creation and the identity + const captcha = makeCaptcha(); const identity = await pendingIdentity; - const result = await confirmRegister(identity, alias); + await captcha; + const result = await confirmRegister(captcha, identity, alias); resolve(result); } catch (err) { reject(err); diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index b9f0f36591..d5dea37321 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -224,6 +224,7 @@ export class IIConnection { `createChallenge(ProofOfWork { timestamp=${pow.timestamp}, nonce=${pow.nonce} })` ); const challenge = await actor.create_challenge(pow); + console.log("Challenge Created"); return challenge; } From 46de5588e7e814235fd46e8f6f3bcbda3a423f82 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 6 Dec 2021 15:56:46 +0100 Subject: [PATCH 73/73] Add metric for inflight challenges --- src/internet_identity/src/main.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index db52fb8547..b2d54874cd 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -712,6 +712,11 @@ fn encode_metrics(w: &mut MetricsEncoder>) -> std::io::Result<()> { s.last_upgrade_timestamp.get() as f64, "The most recent IC time (in nanos) when this canister was successfully upgraded.", )?; + w.encode_gauge( + "internet_identity_inflight_challenges", + s.inflight_challenges.borrow().len() as f64, + "The number of inflight CAPTCHA challenges", + )?; Ok(()) }) }