diff --git a/Cargo.lock b/Cargo.lock index de00aa2..1762f7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,68 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aes" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "asn1-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "base64" version = "0.21.0" @@ -71,19 +9,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] -name = "block-padding" -version = "0.3.3" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bounded-integer" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc54ef154ce8654be9ee10ed33106bdfd5bff6ee7735c25a160af2290ea938ab" +dependencies = [ + "serde", +] [[package]] name = "bounded-vec" @@ -95,27 +33,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "bumpalo" -version = "3.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.0.79" @@ -155,22 +72,6 @@ dependencies = [ "half", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "cookie-factory" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" - [[package]] name = "coset" version = "0.3.4" @@ -181,95 +82,41 @@ dependencies = [ "ciborium-io", ] -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", -] - [[package]] name = "credential-management-proto" version = "0.1.0" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctap-hid-fido2" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3726fa3f7f978ce0a222ea73c13490115f95e7a31db3061d7c4c91bea58ea01a" -dependencies = [ - "aes", - "anyhow", - "base64", - "byteorder", - "cbc", - "hex", - "hidapi", - "num", - "pad", - "ring", - "serde", - "serde_cbor", - "strum", - "strum_macros", - "x509-parser", -] - [[package]] name = "ctap2-proto" version = "0.1.0" dependencies = [ "bounded-integer", "bounded-vec", - "cookie-factory", "coset", - "ctap-hid-fido2", "fido-common", - "nom", "serde", ] [[package]] -name = "data-encoding" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" - -[[package]] -name = "der-parser" -version = "8.2.0" +name = "ctaphid" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +checksum = "aa622743e1747e48d25170854b552b3ddc753125a39a6732e57c5c7a5ff7fe11" dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", + "ctaphid-types", + "hex", + "log", + "rand_core", + "tap", ] [[package]] -name = "displaydoc" -version = "0.2.4" +name = "ctaphid-types" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "4714cdd86d5134532b9decaa6774db0a6851ecd07e96a2f239332ae1f3239350" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", + "bitflags", ] [[package]] @@ -277,17 +124,20 @@ name = "fido-common" version = "0.1.0" dependencies = [ "bounded-vec", + "ciborium", + "coset", "serde", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "getrandom" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ - "typenum", - "version_check", + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -296,18 +146,24 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hid-ctap2-proto" +version = "0.1.0" +dependencies = [ + "ciborium", + "ciborium-io", + "ctap2-proto", + "ctaphid", + "hidapi", + "serde", +] + [[package]] name = "hidapi" version = "1.5.0" @@ -320,37 +176,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "js-sys" -version = "0.3.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.144" @@ -366,128 +191,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width", -] - [[package]] name = "pkg-config" version = "0.3.27" @@ -513,99 +216,32 @@ dependencies = [ ] [[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "nom", + "getrandom", ] -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - [[package]] name = "serde" -version = "1.0.162" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "syn", ] [[package]] @@ -620,16 +256,10 @@ dependencies = [ ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" @@ -648,42 +278,9 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "time" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" -dependencies = [ - "time-core", + "syn", ] -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - [[package]] name = "unicode-ident" version = "1.0.8" @@ -691,92 +288,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.7.1" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasm-bindgen" -version = "0.2.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.15", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" - -[[package]] -name = "web-sys" -version = "0.3.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "webauthn3-proto" @@ -809,20 +324,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "x509-parser" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab0c2f54ae1d92f4fcb99c0b7ccf0b1e3451cbd395e5f115ccbdbcb18d4f634" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] diff --git a/crates/ctap2-proto/Cargo.toml b/crates/ctap2-proto/Cargo.toml index 4e272c3..3623e10 100644 --- a/crates/ctap2-proto/Cargo.toml +++ b/crates/ctap2-proto/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] fido-common = { path = "../fido-common" } -bounded-integer = { version = "0.5.3", features = ["types", "std"] } +bounded-integer = { version = "0.5.3", features = ["types", "std", "serde1"] } bounded-vec = "0.7.1" coset = "0.3.3" serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/crates/ctap2-proto/examples/libfido2.rs b/crates/ctap2-proto/examples/libfido2.rs new file mode 100644 index 0000000..715afbd --- /dev/null +++ b/crates/ctap2-proto/examples/libfido2.rs @@ -0,0 +1,187 @@ +use std::collections::BTreeMap; +use std::ops::Deref; + +use ctap2_proto::extensions; +use ctap2_proto::prelude::*; +use ctap2_proto::Ctap2_2Authenticator; + +extern crate ctap2_proto; + +struct FidoKey(ctap_hid_fido2::FidoKeyHid); + +impl Deref for FidoKey { + type Target = ctap_hid_fido2::FidoKeyHid; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Ctap2_2Authenticator for FidoKey { + fn make_credential(&mut self, request: make::Request) -> Result { + let args = MakeCredentialArgsBuilder::new( + &request.relying_party.id, + request.client_data_hash.as_ref(), + ) + .build(); + + let attestation = match self.make_credential_with_args(&args) { + Ok(attestation) => attestation, + Err(e) => { + todo!("unhandled error: {e}") // anyhow::Error requires manually + // mapping + } + }; + + let format = if !attestation.flags_attested_credential_data_included { + FormatIdentifier::None + } else { + unimplemented!("do not support attestation yet") + }; + + let authenticator_data = attestation.auth_data.as_slice().try_into().unwrap(); + + let unsigned_extension_outputs: BTreeMap<_, _> = attestation + .extensions + .into_iter() + .filter_map(|extension| -> Option<(extensions::Identifier, Vec)> { + match extension { + CredentialExtension::CredBlob(_) => { + todo!() + } + CredentialExtension::CredProtect(_) => { + todo!() + } + CredentialExtension::HmacSecret(_) => { + todo!() + } + CredentialExtension::LargeBlobKey(_) => { + todo!() + } + CredentialExtension::MinPinLength(_) => { + todo!() + } + } + }) + .collect(); + + let unsigned_extension_outputs = if !unsigned_extension_outputs.is_empty() { + Some(unsigned_extension_outputs) + } else { + None + }; + + Ok(make::Response { + format, + authenticator_data, + enterprise_attestation: None, + large_blob_key: None, + unsigned_extension_outputs, + }) + } + + fn get_assertion(request: get::Request) -> Result { + todo!() + } + + fn get_info(&self) -> device::Info { + todo!() + } + + fn client_pin(request: client_pin::Request) -> Result { + todo!() + } + + fn reset() -> Result<(), reset::Error> { + todo!() + } + + fn selection() -> Result<(), ctap2_proto::authenticator::selection::Error> { + todo!() + } +} + +use ctap_hid_fido2::fidokey::CredentialExtension; +use ctap_hid_fido2::{ + fidokey::{GetAssertionArgsBuilder, MakeCredentialArgsBuilder}, + verifier, Cfg, FidoKeyHidFactory, +}; +use fido_common::attestation::FormatIdentifier; + +fn main() { + let rpid = "reg-auth-example-app"; + let pin = get_input_with_message("input PIN:"); + + println!("Register"); + // create `challenge` + let challenge = verifier::create_challenge(); + + // create `MakeCredentialArgs` + let make_credential_args = MakeCredentialArgsBuilder::new(rpid, &challenge) + .pin(&pin) + .build(); + + let mut cfg = Cfg::init(); + cfg.enable_log = false; + + // create `FidoKeyHid` + let device = FidoKeyHidFactory::create(&cfg).unwrap(); + + if device.get_info().unwrap().force_pin_change { + device.set_new_pin("1234").unwrap(); + } + + // get `Attestation` Object + let attestation = device + .make_credential_with_args(&make_credential_args) + .unwrap(); + println!("- Register Success"); + + // verify `Attestation` Object + let verify_result = verifier::verify_attestation(rpid, &challenge, &attestation); + if !verify_result.is_success { + println!("- ! Verify Failed"); + return; + } + + // store Credential Id and Publickey + let userdata_credential_id = verify_result.credential_id; + let userdata_credential_public_key = verify_result.credential_public_key; + + println!("Authenticate"); + // create `challenge` + let challenge = verifier::create_challenge(); + + // create `GetAssertionArgs` + let get_assertion_args = GetAssertionArgsBuilder::new(rpid, &challenge) + .pin(&pin) + .credential_id(&userdata_credential_id) + .build(); + + // get `Assertion` Object + let assertions = device.get_assertion_with_args(&get_assertion_args).unwrap(); + println!("- Authenticate Success"); + + // verify `Assertion` Object + if !verifier::verify_assertion( + rpid, + &userdata_credential_public_key, + &challenge, + &assertions[0], + ) { + println!("- ! Verify Assertion Failed"); + } +} + +pub fn get_input() -> String { + let mut word = String::new(); + std::io::stdin().read_line(&mut word).ok(); + return word.trim().to_string(); +} + +pub fn get_input_with_message(message: &str) -> String { + println!("{}", message); + let input = get_input(); + println!(); + input +} diff --git a/crates/ctap2-proto/examples/mock.rs b/crates/ctap2-proto/examples/mock.rs new file mode 100644 index 0000000..118ad58 --- /dev/null +++ b/crates/ctap2-proto/examples/mock.rs @@ -0,0 +1,266 @@ +#![feature(let_chains)] +extern crate ctap2_proto; + +<<<<<<< Updated upstream +use std::collections::HashMap; +======= +use std::collections::BTreeMap; +>>>>>>> Stashed changes + +use bounded_vec::BoundedVec; +use ctap2_proto::authenticator::Sha256Hash; +// Recommended +use ctap2_proto::prelude::*; + +use ctap2_proto::{ + authenticator, + prelude::{ + client_pin::AuthProtocolVersion, + device::{Aaguid, Version}, + }, +}; +use fido_common::credential::public_key::{RelyingPartyEntity, UserEntity}; +use fido_common::{ + attestation, + credential::{self, public_key::Parameters}, + registry::algorithms::Signature, + Transport, +}; + +const AAGUID: Aaguid = Aaguid::from(*b"\xed\xeeZow\xdc\xf5\xadZ\xe3\xd7\xb8\xf5\xf6\xf7\xd7"); +const VERSION: usize = 1; +const SUPPORTED_AUTH_PROTOCOL_VERSIONS: [AuthProtocolVersion; 1] = [AuthProtocolVersion::Two]; +const SUPPORTED_PUBLIC_KEY_ALGORITHMS: [Parameters; 1] = [Parameters { + credential_type: credential::Type::PublicKey, + algorithm: Signature::Ed25519EddsaSha512Raw, +}]; + +struct VirtualAuthenticator +where + R: std::io::Read + Send, + W: std::io::Write + Send, +{ + pin: Option>, + input: R, + output: W, +} + +enum Presence { + Present, + NotPresent, +} + +impl VirtualAuthenticator +where + R: std::io::Read + Send, + W: std::io::Write + Send, +{ + fn verify_presence(reader: &mut R, writer: &mut W) -> Result { + write!(writer, "Allow this operation? [Y/n]:")?; + let mut input = <[u8; 1]>::default(); + reader.read_exact(&mut input)?; + writeln!(writer)?; + if input[0] == b'Y' { + Ok(Presence::Present) + } else { + Ok(Presence::NotPresent) + } + } +} + +impl Ctap2_2Authenticator for VirtualAuthenticator +where + R: std::io::Read + Send, + W: std::io::Write + Send, +{ + fn make_credential(&mut self, request: make::Request) -> Result { + // If authenticator supports either pinUvAuthToken or clientPin features and the + // platform sends a zero length pinUvAuthParam: + if let Some(options) = self.get_info().options && + (options.contains_key(&device::OptionId::PinUvAuthToken) || options.contains_key(&device::OptionId::ClientPin)) && request.pin_uv_auth_param.len() == 0 + { + // Request evidence of user interaction in an authenticator-specific way (e.g., flash + // the LED light). + // If the user declines permission, or the operation times out, then end the operation + // by returning CTAP2_ERR_OPERATION_DENIED. + let Ok(Presence::Present) = Self::verify_presence(&mut self.input, &mut self.output) else { + return Err(make::Error::OperationDenied); + }; + + // If evidence of user interaction is provided in this step then return either + // CTAP2_ERR_PIN_NOT_SET if PIN is not set or CTAP2_ERR_PIN_INVALID if PIN has been + // set. + if self.pin.is_some() { + return Err(make::Error::PinInvalid); + } else { + return Err(make::Error::PinNotSet); + } + } + + // If the pinUvAuthParam parameter is present: + debug_assert!(request.pin_uv_auth_param.len() > 0); + + // If the pinUvAuthProtocol parameter’s value is not supported, return + // CTAP1_ERR_INVALID_PARAMETER error. + // If the pinUvAuthProtocol parameter is absent, return + // CTAP2_ERR_MISSING_PARAMETER error. + let Some(auth_protocol_version) = request.pin_uv_auth_protocol_version else { return Err(make::Error::MissingParameter) }; + if !SUPPORTED_AUTH_PROTOCOL_VERSIONS.contains(&auth_protocol_version) { + return Err(make::Error::InvalidParameter); + } + + // Filter out any invalid or unsupported params, then take the first valid one. + + // TODO: The following NOTE from the specs refers to the following loop, however + // this loop only iterates over the algorithms until a match is found. + // + // The only risk I can imagine is enumerating the supported algorithms of the + // authenticator, which doesn't seem that important to protect against. + // + // NOTE: This loop chooses the first occurrence of an algorithm identifier + // supported by this authenticator but always iterates over every + // element of pubKeyCredParams to validate them. + + // Validate pubKeyCredParams with the following steps: + // 1. For each element of pubKeyCredParams: + let Some(public_key_algorithm) = request.public_key_credential_params.into_iter().filter(|params| { + // TODO: Ensure that invalid params are unrepresentable + // If the element is missing required members, including members that are mandatory + // only for the specific type, then return an error, for example + // CTAP2_ERR_INVALID_CBOR. + + // TODO: What would it mean for a credential to have the wrong type? + // If the values of any known members have the wrong type then return an error, for + // example CTAP2_ERR_CBOR_UNEXPECTED_TYPE. + + // If the element specifies an algorithm that is supported by the authenticator, and no + // algorithm has yet been chosen by this loop, then let the algorithm specified by the + // current element be the chosen algorithm. + return SUPPORTED_PUBLIC_KEY_ALGORITHMS.contains(params); + }).next() else { + // If the loop completes and no algorithm was chosen then return + // CTAP2_ERR_UNSUPPORTED_ALGORITHM. + return Err(make::Error::UnsupportedAlgorithm); + }; + + // No attestation + let format = attestation::FormatIdentifier::None; + // No enterprise attestation + let enterprise_attestation = None; + + // If the "uv" option is absent, let the "uv" option be treated as being present + // with the value false. (This is the default) + let mut user_verification = false; + let mut discoverable_credential = false; + let mut user_presence = true; + + let authenticator_data = authenticator::Data { + relying_party_id_hash: todo!(), + user_is_present: todo!(), + user_is_verified: todo!(), + signature_counter: todo!(), + attested_credential_data: todo!(), + extensions: None, + }; + + if let Some(req_options) = request.options { + // If the pinUvAuthParam is present, let the "uv" option be treated as being + // present with the value false. + if let Some(true) = req_options.get(&make::OptionKey::UserVerification) {} + } + + Ok(make::Response { + format, + authenticator_data, + enterprise_attestation, + large_blob_key: None, + unsigned_extension_outputs: None, + }) + } + + fn get_assertion(request: get::Request) -> Result { + todo!() + } + + fn get_info(&self) -> device::Info { + device::Info { + versions: [Version::Fido2_1].into_iter().collect(), + extensions: None, + aaguid: AAGUID, + options: None, + max_message_size: None, + pin_uv_auth_protocols: Some(SUPPORTED_AUTH_PROTOCOL_VERSIONS.into()), + max_credential_count_in_list: None, + max_credential_id_length: None, + transports: Some([Transport::Internal].into_iter().collect()), + algorithms: Some(SUPPORTED_PUBLIC_KEY_ALGORITHMS.into()), + max_serialized_large_blob_array_size: None, + force_pin_change: Some(self.pin.is_none()), + min_pin_length: None, + firmware_version: Some(VERSION), + max_cred_blob_length: None, + max_rpids_for_set_min_pin_length: None, + preferred_platform_uv_attempts: None, + uv_modality: None, + certifications: None, + remaining_discoverable_credentials: None, + vendor_prototype_config_commands: None, + } + } + + fn client_pin(request: client_pin::Request) -> Result { + todo!() + } + + fn reset() -> Result<(), reset::Error> { + todo!() + } + + fn selection() -> Result<(), selection::Error> { + todo!() + } +} + +fn main() { + let pin = None; + + let authenticator = VirtualAuthenticator { + pin, + input: std::io::stdin(), + output: std::io::stdout(), + }; + + let client_data_hash = Sha256Hash([0; 32]); + let relying_party = RelyingPartyEntity { + id: "example.com".into(), + name: Some("Example Inc.".into()), + }; + let user_entity = UserEntity { + id: BoundedVec::from_vec([1u8; 64].into()).unwrap(), + name: Some("user@example.com".into()), + display_name: Some("Example User".into()), + }; + + // Get authenticator info + let info = authenticator.get_info(); + + // Make a new discoverable credential + let options = HashMap::from([ + (make::OptionKey::UserVerification, true), + (make::OptionKey::Discoverable, true), + (make::OptionKey::UserPresence, false), + ]); + let request = make::Request { + client_data_hash: &client_data_hash, + relying_party: &relying_party, + user: &user_entity, + public_key_credential_params: &SUPPORTED_PUBLIC_KEY_ALGORITHMS, + exclude_list: None, + extensions: None, + options: Some(&options), + pin_uv_auth_param: (), + pin_uv_auth_protocol_version: (), + enterprise_attestation: None, + }; + authenticator.make_credential(request); +} diff --git a/crates/ctap2-proto/src/authenticator/client_pin/mod.rs b/crates/ctap2-proto/src/authenticator/client_pin/mod.rs index a674826..b6e9dc2 100644 --- a/crates/ctap2-proto/src/authenticator/client_pin/mod.rs +++ b/crates/ctap2-proto/src/authenticator/client_pin/mod.rs @@ -1,8 +1,11 @@ +use bounded_integer::BoundedUsize; use std::collections::BTreeSet; -use bounded_integer::BoundedUsize; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AuthProtocolVersion { One, Two, diff --git a/crates/ctap2-proto/src/authenticator/credential/make.rs b/crates/ctap2-proto/src/authenticator/credential/make.rs index 0419cc0..88b2c4e 100644 --- a/crates/ctap2-proto/src/authenticator/credential/make.rs +++ b/crates/ctap2-proto/src/authenticator/credential/make.rs @@ -5,6 +5,9 @@ use crate::{ use fido_common::{attestation, credential::public_key}; use std::collections::{BTreeMap, HashMap}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub enum Error { OperationDenied, PinNotSet, @@ -25,6 +28,7 @@ pub enum Error { /// > The following option keys are defined for use in /// > `authenticatorMakeCredential`'s `options` parameter. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum OptionKey { /// > Specifies whether this credential is to be discoverable or /// > not. @@ -41,6 +45,7 @@ pub enum OptionKey { /// Input parameters for [`Ctap2Device::make_credential`] operation. #[derive(Clone, Copy)] +#[cfg_attr(feature = "serde", derive(Serialize))] pub struct Request<'a> { /// > Hash of the ClientData contextual binding specified by host. pub client_data_hash: &'a Sha256Hash, @@ -84,6 +89,7 @@ pub struct Request<'a> { pub enterprise_attestation: Option, } +#[cfg_attr(feature = "serde", derive(Deserialize))] pub struct Response { pub format: fido_common::attestation::FormatIdentifier, pub authenticator_data: authenticator::Data, diff --git a/crates/ctap2-proto/src/authenticator/device.rs b/crates/ctap2-proto/src/authenticator/device.rs index 015fdb3..363991d 100644 --- a/crates/ctap2-proto/src/authenticator/device.rs +++ b/crates/ctap2-proto/src/authenticator/device.rs @@ -8,8 +8,12 @@ use std::collections::{BTreeMap, BTreeSet}; use std::num::NonZeroUsize; use std::usize; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// A usize with a minimum value of N -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct UsizeN(bounded_integer::BoundedUsize); /// > data type byte string and identifying the authenticator model, i.e. @@ -18,6 +22,7 @@ pub struct UsizeN(bounded_integer::BoundedUsize `AAGUID` via appropriate methods. Platforms may alter their behaviour /// > based on these hints such as selecting a PIN protocol or `credProtect` /// > level. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Certification { /// > The [FIPS140-2] Cryptographic-Module-Validation-Program overall /// > certification level. @@ -44,6 +51,8 @@ pub enum Certification { } #[repr(usize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FipsCryptoValidation2Level { Level1 = 1, Level2 = 2, @@ -52,6 +61,8 @@ pub enum FipsCryptoValidation2Level { } #[repr(usize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FipsCryptoValidation3Level { Level1 = 1, Level2 = 2, @@ -60,6 +71,8 @@ pub enum FipsCryptoValidation3Level { } #[repr(usize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FipsPhysicalCryptoValidation2Level { Level1 = 1, Level2 = 2, @@ -68,6 +81,8 @@ pub enum FipsPhysicalCryptoValidation2Level { } #[repr(usize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FipsPhysicalCryptoValidation3Level { Level1 = 1, Level2 = 2, @@ -78,6 +93,8 @@ pub enum FipsPhysicalCryptoValidation3Level { /// > Common Criteria Evaluation Assurance Level [CC1V3-1R5]. This is a integer /// > from 1 to 7. The intermediate-plus levels are not represented. #[repr(usize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum CommonCriterialLevel { EAL1 = 1, EAL2 = 2, @@ -92,6 +109,8 @@ pub enum CommonCriterialLevel { /// > numbered levels are mapped to the odd numbers, with the plus levels mapped /// > to the even numbers e.g., level 3+ is mapped to 6. #[repr(usize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FidoLevel { L1 = 1, L1Plus = 2, @@ -103,6 +122,7 @@ pub enum FidoLevel { /// These options describe properties of a CTAP device. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum OptionId { /// > Indicates that the device is attached to the client and therefore /// > can’t be removed and used on another client. @@ -182,6 +202,8 @@ pub enum OptionId { /// > list of its supported protocol versions and extensions, its AAGUID, and /// > other aspects of its overall capabilities. Platforms should use this /// > information to tailor their command parameters choices. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug)] pub struct Info { /// > List of supported CTAP versions. pub versions: BTreeSet, diff --git a/crates/fido-common/Cargo.toml b/crates/fido-common/Cargo.toml index 27e2a1c..3af9167 100644 --- a/crates/fido-common/Cargo.toml +++ b/crates/fido-common/Cargo.toml @@ -7,8 +7,9 @@ edition = "2021" [dependencies] bounded-vec = { version = "0.7.1", features = ["serde"] } +ciborium = { version = "0.2.1", default-features = false, optional = true } coset = { version = "0.3.4", default-features = false } serde = { version = "1", features = ["derive"], optional = true } [features] -serde = ["dep:serde", "bounded-vec/serde"] +serde = ["dep:serde", "bounded-vec/serde", "dep:ciborium"] diff --git a/crates/fido-common/src/attestation.rs b/crates/fido-common/src/attestation.rs index 22b2d27..f8bd76d 100644 --- a/crates/fido-common/src/attestation.rs +++ b/crates/fido-common/src/attestation.rs @@ -1,3 +1,4 @@ +use serde::ser::Serializer; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -71,8 +72,12 @@ pub enum FormatIdentifier { } pub mod enterprise { + #[cfg(feature = "serde")] + use serde::{Deserialize, Serialize}; + #[repr(usize)] #[derive(Clone, Copy)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Kind { /// > In this case, an enterprise attestation capable authenticator, on /// > which enterprise attestation is enabled, upon receiving the @@ -97,14 +102,41 @@ pub mod enterprise { } } +use coset::CborSerializable; /// > Attested credential data is a variable-length byte array added to the /// > authenticator data when generating an attestation object for a given /// > credential. +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct CredentialData { /// > The AAGUID of the authenticator. pub aaguid: [u8; 16], /// The ID of the credential. pub id: Vec, /// The public key of the credential. + #[cfg_attr( + feature = "serde", + serde( + deserialize_with = "deserialize_coset_key", + serialize_with = "serialize_cose_key" + ) + )] pub public_key: coset::CoseKey, } + +pub fn serialize_cose_key(key: &coset::CoseKey, s: S) -> Result { + use serde::ser::Error; + let bytes = key + .clone() + .to_vec() + .map_err(|e| Error::custom(e.to_string()))?; + + s.serialize_bytes(&bytes) +} + +#[cfg(feature = "serde")] +fn deserialize_coset_key<'de, D>(deserializer: D) -> Result +where + D: serde::de::Deserializer<'de>, +{ + todo!() +} diff --git a/crates/fido-common/src/authenticator.rs b/crates/fido-common/src/authenticator.rs index 60932d2..902ef49 100644 --- a/crates/fido-common/src/authenticator.rs +++ b/crates/fido-common/src/authenticator.rs @@ -1,6 +1,9 @@ use crate::{attestation, extensions, Sha256Hash}; use std::collections::BTreeMap; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + pub enum Flags {} /// > The authenticator data structure encodes contextual bindings made by the @@ -13,6 +16,7 @@ pub enum Flags {} /// > software, connected to the client over a secure channel. In both cases, /// > the Relying Party receives the authenticator data in the same format, and /// > uses its knowledge of the authenticator to make trust decisions. +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Data { /// > SHA-256 hash of the RP ID the credential is scoped to. pub relying_party_id_hash: Sha256Hash, @@ -42,9 +46,6 @@ impl TryFrom<&[u8]> for Data { } } -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// > Authenticators may implement various transports for communicating with /// > clients. This enumeration defines hints as to how clients might /// > communicate with a particular authenticator in order to obtain an diff --git a/crates/fido-common/src/extensions.rs b/crates/fido-common/src/extensions.rs index dd32853..d997622 100644 --- a/crates/fido-common/src/extensions.rs +++ b/crates/fido-common/src/extensions.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + /// > Extensions are identified by a string, called an extension identifier, /// > chosen by the extension author. /// > @@ -18,7 +21,8 @@ /// > Extensions that may exist in multiple versions should take care to include /// > a version in their identifier. In effect, different versions are thus /// > treated as different extensions, e.g., `myCompany_extension_01` -#[derive(PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Identifier { AppId, TransactionAuthSimple, diff --git a/crates/fido-common/src/registry.rs b/crates/fido-common/src/registry.rs index f83e7ec..44abb47 100644 --- a/crates/fido-common/src/registry.rs +++ b/crates/fido-common/src/registry.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; /// > representation (in quotes), which is used in the authoritative metadata /// > for FIDO authenticators. #[repr(u32)] -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum UserVerify { /// > This flag MUST be set if the authenticator is able to confirm user diff --git a/crates/hid-ctap2-proto/Cargo.toml b/crates/hid-ctap2-proto/Cargo.toml new file mode 100644 index 0000000..12c6185 --- /dev/null +++ b/crates/hid-ctap2-proto/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "hid-ctap2-proto" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ciborium = "0.2.1" +ciborium-io = "0.2.1" +ctap2-proto = { path = "../ctap2-proto", features = ["serde"] } +ctaphid = { version = "0.3.1", default_features = false } +serde = "1.0.163" + +[dev-dependencies] +hidapi = { version = "^1.2.6", default-features = false, features = ["linux-shared-hidraw"] } diff --git a/crates/hid-ctap2-proto/src/lib.rs b/crates/hid-ctap2-proto/src/lib.rs new file mode 100644 index 0000000..b8110cd --- /dev/null +++ b/crates/hid-ctap2-proto/src/lib.rs @@ -0,0 +1,185 @@ +use ctap2_proto::{prelude::*, Ctap2_2Authenticator}; +use ctaphid::types::Command; + +pub struct Device(ctaphid::Device); + +impl Device { + fn send_raw(&self, command: Command, bytes: &[u8]) -> Result, ctaphid::error::Error> { + self.0.ctap2(command.into(), bytes) + } + + fn send(&self, req: Req) -> Result + where + Req: serde::Serialize, + Res: for<'de> serde::Deserialize<'de>, + { + let command = Command::Cbor; + let mut data: Vec = Vec::new(); + ciborium::ser::into_writer(&req, &mut data).map_err(|e| match e { + ciborium::ser::Error::Io(_) => ctaphid::error::RequestError::IncompleteWrite, + ciborium::ser::Error::Value(desc) => { + ctaphid::error::RequestError::PacketSendingFailed(desc.into()) + } + })?; + + let response = self.0.ctap2(command.into(), &data)?; + + match ciborium::de::from_reader(response.as_slice()) { + Ok(res) => Ok(res), + Err(e) => match e { + ciborium::de::Error::Io(_) => todo!(), + ciborium::de::Error::Syntax(_) => todo!(), + ciborium::de::Error::Semantic(_, _) => todo!(), + ciborium::de::Error::RecursionLimitExceeded => todo!(), + }, + } + } +} + +impl Ctap2_2Authenticator for Device +where + D: ctaphid::HidDevice, +{ + fn make_credential(&mut self, request: make::Request) -> Result { + // TODO: How the heck am i supposed to handle errors??? + self.send(&request).map_err(|e| match e { + ctaphid::error::Error::CommandError(e) => match e { + ctaphid::error::CommandError::CborError(_) => todo!(), + ctaphid::error::CommandError::InvalidPingData => todo!(), + ctaphid::error::CommandError::NotSupported(_) => todo!(), + }, + ctaphid::error::Error::RequestError(e) => match e { + ctaphid::error::RequestError::IncompleteWrite => todo!(), + ctaphid::error::RequestError::MessageFragmentationFailed(_) => todo!(), + ctaphid::error::RequestError::PacketSendingFailed(_) => todo!(), + ctaphid::error::RequestError::PacketSerializationFailed(_) => todo!(), + }, + ctaphid::error::Error::ResponseError(e) => match e { + ctaphid::error::ResponseError::CommandFailed(_) => todo!(), + ctaphid::error::ResponseError::MessageDefragmentationFailed(_) => todo!(), + ctaphid::error::ResponseError::PacketParsingFailed(_) => todo!(), + ctaphid::error::ResponseError::PacketReceivingFailed(_) => todo!(), + ctaphid::error::ResponseError::Timeout => todo!(), + ctaphid::error::ResponseError::MissingErrorCode => todo!(), + ctaphid::error::ResponseError::UnexpectedCommand { expected, actual } => todo!(), + ctaphid::error::ResponseError::UnexpectedKeepAlive(_) => todo!(), + ctaphid::error::ResponseError::UnexpectedResponseData(_) => todo!(), + }, + }) + } + + fn get_assertion(request: get::Request) -> Result { + todo!() + } + + fn get_info(&self) -> device::Info { + let info = self.send_raw(Command::Cbor, &[1u8, 4]).unwrap(); + println!("info: {info:#?}"); + let info: device::Info = + ciborium::de::from_reader::(info.as_slice()).unwrap(); + info + } + + fn client_pin(request: client_pin::Request) -> Result { + todo!() + } + + fn reset() -> Result<(), reset::Error> { + todo!() + } + + fn selection() -> Result<(), ctap2_proto::authenticator::selection::Error> { + todo!() + } +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use ctap2_proto::Ctap2_2Authenticator; + use ctaphid::{ + error::{RequestError, ResponseError}, + types::Command, + }; + struct HidDevice(hidapi::HidDevice); + #[derive(Debug)] + struct HidDeviceInfoMy(hidapi::DeviceInfo); + + impl ctaphid::HidDeviceInfo for HidDeviceInfoMy { + fn vendor_id(&self) -> u16 { + hidapi::DeviceInfo::vendor_id(&self.0) + } + + fn product_id(&self) -> u16 { + hidapi::DeviceInfo::product_id(&self.0) + } + + fn path(&self) -> std::borrow::Cow<'_, str> { + let cstr: &CStr = hidapi::DeviceInfo::path(&self.0); + let s = cstr.to_str().unwrap(); + std::borrow::Cow::from(s) + } + } + + impl ctaphid::HidDevice for HidDevice { + type Info = HidDeviceInfoMy; + + fn send(&self, data: &[u8]) -> Result<(), ctaphid::error::RequestError> { + println!("sending bytes: {data:#?}"); + hidapi::HidDevice::write(&self.0, data) + .map_err(|e| RequestError::PacketSendingFailed(e.into()))?; + Ok(()) + } + + fn receive<'a>( + &self, + buffer: &'a mut [u8], + timeout: Option, + ) -> Result<&'a [u8], ctaphid::error::ResponseError> { + println!("reading bytes"); + let duration = if let Some(timeout) = timeout { + i32::try_from(timeout.as_millis()) + .map_err(|err| ResponseError::PacketReceivingFailed(err.into()))? + } else { + -1 + }; + let n = self + .0 + .read_timeout(buffer, duration) + .map_err(|err| ResponseError::PacketReceivingFailed(err.into()))?; + if n == buffer.len() { + Ok(&buffer[1..n]) + } else if n == 0 { + Err(ResponseError::Timeout) + } else { + Ok(&buffer[..n]) + } + } + } + + #[test] + fn get_info() { + let hidapi = hidapi::HidApi::new().unwrap(); + let devices = hidapi.device_list(); + for device_info in devices { + let hid_device = hidapi::DeviceInfo::open_device(&device_info, &hidapi); + let hid_device = match hid_device { + Ok(hid_device) => hid_device, + Err(e) => { + println!("error: {e:#?}"); + continue; + } + }; + let device = HidDevice(hid_device); + let device = + ctaphid::Device::new(device, HidDeviceInfoMy(device_info.to_owned())).unwrap(); + let device = super::Device(device); + println!("info: {:#?}", device.0.ctap2(Command::Cbor.into(), &[])); + } + assert!(false); + } + + #[test] + fn quickstart() {} +}