diff --git a/Cargo.lock b/Cargo.lock index bbfec9f..abfa879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "anstream" version = "0.3.2" @@ -68,45 +57,6 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" -[[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" @@ -119,12 +69,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - [[package]] name = "base64ct" version = "1.6.0" @@ -152,15 +96,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "bumpalo" version = "3.13.0" @@ -173,15 +108,6 @@ 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.82" @@ -239,7 +165,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.28", + "syn", ] [[package]] @@ -304,29 +230,6 @@ dependencies = [ "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 = "ctrlc" version = "3.4.0" @@ -361,15 +264,9 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn", ] -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - [[package]] name = "der" version = "0.7.8" @@ -377,29 +274,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] -[[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "deranged" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" - [[package]] name = "dialoguer" version = "0.10.4" @@ -424,17 +302,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - [[package]] name = "ecdsa" version = "0.16.8" @@ -481,6 +348,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core", "sec1", @@ -537,6 +405,17 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" +[[package]] +name = "fido2-rs" +version = "0.1.0" +dependencies = [ + "bitflags 1.3.2", + "foreign-types", + "libfido2-sys", + "openssl", + "thiserror", +] + [[package]] name = "fido_ssh_maker" version = "0.1.0" @@ -544,15 +423,32 @@ dependencies = [ "anyhow", "bitflags 2.4.0", "clap", - "ctap-hid-fido2", "ctrlc", "dialoguer", + "fido2-rs", "gethostname", + "p256", + "rand", "ssh-encoding", "ssh-key", "whoami", ] +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "generic-array" version = "0.14.7" @@ -596,12 +492,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - [[package]] name = "heck" version = "0.4.1" @@ -614,24 +504,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hidapi" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798154e4b6570af74899d71155fb0072d5b17e6aa12f39c8ef22c60fb8ec99e7" -dependencies = [ - "cc", - "libc", - "pkg-config", - "winapi", -] - [[package]] name = "hmac" version = "0.12.1" @@ -647,7 +519,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "block-padding", "generic-array", ] @@ -662,12 +533,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - [[package]] name = "js-sys" version = "0.3.64" @@ -692,6 +557,16 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "libfido2-sys" +version = "0.2.0" +dependencies = [ + "anyhow", + "cfg-if", + "pkg-config", + "vcpkg", +] + [[package]] name = "libm" version = "0.2.7" @@ -710,18 +585,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[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 = "nix" version = "0.26.2" @@ -734,41 +597,6 @@ dependencies = [ "static_assertions", ] -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" -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-bigint-dig" version = "0.8.4" @@ -786,15 +614,6 @@ dependencies = [ "zeroize", ] -[[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" @@ -816,18 +635,6 @@ dependencies = [ "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.16" @@ -838,21 +645,50 @@ dependencies = [ "libm", ] -[[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.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl" +version = "0.10.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "p256" version = "0.13.2" @@ -877,15 +713,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -967,6 +794,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", "rand_chacha", "rand_core", ] @@ -1009,21 +837,6 @@ dependencies = [ "subtle", ] -[[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 = "rsa" version = "0.9.2" @@ -1056,15 +869,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - [[package]] name = "rustix" version = "0.38.8" @@ -1078,12 +882,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "sec1" version = "0.7.3" @@ -1104,22 +902,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" -[[package]] -name = "serde" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" - -[[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 = "sha2" version = "0.10.7" @@ -1222,42 +1004,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[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 = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[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", -] - [[package]] name = "syn" version = "2.0.28" @@ -1269,18 +1021,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "tempfile" version = "3.8.0" @@ -1311,35 +1051,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", -] - -[[package]] -name = "time" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" -dependencies = [ - "deranged", - "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.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" -dependencies = [ - "time-core", + "syn", ] [[package]] @@ -1360,24 +1072,18 @@ 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -1411,7 +1117,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn", "wasm-bindgen-shared", ] @@ -1433,7 +1139,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1464,28 +1170,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -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 = "windows-sys" version = "0.45.0" @@ -1618,23 +1302,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" -[[package]] -name = "x509-parser" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 43cc08f..6a83102 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,12 @@ edition = "2021" anyhow = "1.0.72" bitflags = "2.4.0" clap = { version = "4.3.21", features = ["derive"] } -ctap-hid-fido2 = "3.5.0" ctrlc = "3.4.0" dialoguer = "0.10.4" +fido2-rs = { version = "0.1.0", path = "fido-rs/fido2-rs" } gethostname = "0.4.3" +p256 = "0.13.2" +rand = "0.8.5" ssh-encoding = { version = "0.2.0" } ssh-key = { version = "0.6.0", features = ["ed25519"] } whoami = "1.4.1" diff --git a/fido-rs/.gitignore b/fido-rs/.gitignore new file mode 100644 index 0000000..2150fcb --- /dev/null +++ b/fido-rs/.gitignore @@ -0,0 +1,3 @@ +.idea +target/ +Cargo.lock \ No newline at end of file diff --git a/fido-rs/Cargo.toml b/fido-rs/Cargo.toml new file mode 100644 index 0000000..5dbaa60 --- /dev/null +++ b/fido-rs/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["libfido2-sys", "fido2-rs"] \ No newline at end of file diff --git a/fido-rs/README.MD b/fido-rs/README.MD new file mode 100644 index 0000000..f31079f --- /dev/null +++ b/fido-rs/README.MD @@ -0,0 +1,36 @@ +# fido-rs + +[libfido2](https://github.com/Yubico/libfido2) bindings for the Rust programming language. + +# Example + +1. Make a credential +```rust +use fido2_rs::device::Device; +use fido2_rs::credentials::Credential; +use fido2_rs::credentials::CoseType; +use anyhow::Result; +fn main() -> Result<()> { + let dev = Device::open("windows://hello").expect("unable open windows hello"); + + let mut cred = Credential::new(); + cred.set_client_data(&[1, 2, 3, 4, 5, 6])?; + cred.set_rp("fido_rs", "fido example")?; + cred.set_user(&[1, 2, 3, 4, 5, 6], "alice", Some("alice"), None)?; + cred.set_cose_type(CoseType::RS256)?; + + let _ = dev.make_credential(&mut cred, None)?; + dbg!(cred.id()); + + Ok(()) +} +``` + +# Support platform +* Windows (MSVC and MinGW) +* Linux + +# TODO + +* [ ] more doc +* [x] full bindings to `fido_cred_t` and `fido_assert_t` \ No newline at end of file diff --git a/fido-rs/fido2-rs/Cargo.toml b/fido-rs/fido2-rs/Cargo.toml new file mode 100644 index 0000000..e9d0f32 --- /dev/null +++ b/fido-rs/fido2-rs/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "fido2-rs" +version = "0.1.0" +authors = ["tyan boot "] +license = "MIT" +description = "Rust bindings to Yubico fido2" +repository = "https://github.com/tyan-boot/fido-rs" +keywords = ["fido2", "webauthn"] +categories = ["authentication", "api-bindings", "hardware-support"] +edition = "2021" + +[dependencies] +thiserror = "1.0.37" +bitflags = "1.3.2" +libfido2-sys = { version = "0.2.0", path = "../libfido2-sys" } +openssl = "0.10.45" +foreign-types = "0.3" + +[dev-dependencies] +anyhow = "1.0.66" \ No newline at end of file diff --git a/fido-rs/fido2-rs/LICENSE b/fido-rs/fido2-rs/LICENSE new file mode 100644 index 0000000..4fa01fb --- /dev/null +++ b/fido-rs/fido2-rs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 tyan boot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/fido-rs/fido2-rs/README.MD b/fido-rs/fido2-rs/README.MD new file mode 100644 index 0000000..a061706 --- /dev/null +++ b/fido-rs/fido2-rs/README.MD @@ -0,0 +1,30 @@ +# fido2-rs + +[![crates.io](https://img.shields.io/crates/v/fido2-rs?style=flat-square)](https://crates.io/crates/fido2-rs) +[![MIT](https://img.shields.io/crates/l/libfido2-sys?style=flat-square)](./LICENSE) + +[libfido2](https://github.com/Yubico/libfido2) bindings for the Rust programming language. + +For more details, see [tyan-boot/fido-rs](https://github.com/tyan-boot/fido-rs) + +```rust +use fido2_rs::device::Device; +use fido2_rs::credentials::Credential; +use fido2_rs::credentials::CoseType; +use anyhow::Result; + +fn main() -> Result<()> { + let dev = Device::open("windows://hello").expect("unable open windows hello"); + + let mut cred = Credential::new(); + cred.set_client_data(&[1, 2, 3, 4, 5, 6])?; + cred.set_rp("fido_rs", "fido example")?; + cred.set_user(&[1, 2, 3, 4, 5, 6], "alice", Some("alice"), None)?; + cred.set_cose_type(CoseType::RS256)?; + + let _ = dev.make_credential(&mut cred, None)?; + dbg!(cred.id()); + + Ok(()) +} +``` \ No newline at end of file diff --git a/fido-rs/fido2-rs/src/assertion.rs b/fido-rs/fido2-rs/src/assertion.rs new file mode 100644 index 0000000..6a4336f --- /dev/null +++ b/fido-rs/fido2-rs/src/assertion.rs @@ -0,0 +1,465 @@ +use crate::credentials::{CoseType, Opt}; +use crate::error::{FidoError, Result}; +use crate::key::{Eddsa, Rsa, ES256, ES384}; +use crate::utils::check; +use ffi::FIDO_ERR_INVALID_ARGUMENT; +use openssl::nid::Nid; +use openssl::pkey::{Id, PKey, Public}; +use std::ffi::CString; +use std::marker::PhantomData; +use std::ptr::NonNull; + +macro_rules! impl_assertion_set { + ($ty:ty, $($f:tt).*) => { + impl $ty { + /// Set the client data hash of assert by specifying the assertion's unhashed client data. + /// + /// This is required by Windows Hello, which calculates the client data hash internally. + /// + /// For compatibility with Windows Hello, applications should use [AssertRequestBuilder::client_data] + /// instead of [AssertRequestBuilder::client_data_hash]. + pub fn set_client_data(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + unsafe { + check(ffi::fido_assert_set_clientdata( + self.$($f).*.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// See [AssertRequestBuilder::client_data] + pub fn set_client_data_hash(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + unsafe { + check(ffi::fido_assert_set_clientdata_hash( + self.$($f).*.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Set the relying party id of assert. + pub fn set_rp(&mut self, id: impl AsRef) -> Result<()> { + let id = CString::new(id.as_ref())?; + + unsafe { + check(ffi::fido_assert_set_rp(self.$($f).*.as_ptr(), id.as_ptr()))?; + } + + Ok(()) + } + + /// Set the up (user presence) attribute of assert. + /// + /// **Default to [Opt::Omit]** + pub fn set_up(&mut self, up: Opt) -> Result<()> { + unsafe { + check(ffi::fido_assert_set_up(self.$($f).*.as_ptr(), up as _))?; + } + + Ok(()) + } + + /// Set the uv (user verification) attribute of assert. + /// + /// **Default to [Opt::Omit]** + pub fn set_uv(&mut self, uv: Opt) -> Result<()> { + unsafe { + check(ffi::fido_assert_set_uv(self.$($f).*.as_ptr(), uv as _))?; + } + + Ok(()) + } + + /// Set the extensions of assert to the bitmask flags. + /// + /// At the moment, only the FIDO_EXT_CRED_BLOB, FIDO_EXT_HMAC_SECRET, and FIDO_EXT_LARGEBLOB_KEY extensions are supported. + pub fn set_extensions(&mut self, flags: crate::credentials::Extensions) -> Result<()> { + unsafe { + check(ffi::fido_assert_set_extensions( + self.$($f).*.as_ptr(), + flags.bits(), + ))?; + } + + Ok(()) + } + + /// Allow a credential in a FIDO2 assertion. + /// + /// Add id to the list of credentials allowed in assert. + /// + /// If fails, the existing list of allowed credentials is preserved. + pub fn set_allow_credential(&mut self, id: impl AsRef<[u8]>) -> Result<()> { + let id = id.as_ref(); + + unsafe { + check(ffi::fido_assert_allow_cred( + self.$($f).*.as_ptr(), + id.as_ptr(), + id.len(), + ))?; + } + + Ok(()) + } + } + }; +} + +/// FIDO assertions from device, contains one or more assertion. +pub struct Assertions { + pub(crate) ptr: NonNull, +} + +/// A single FIDO assertion. +pub struct Assertion<'a> { + ptr: NonNull, + idx: usize, + _p: PhantomData<&'a ()>, +} + +/// Request to get a assertion. +pub struct AssertRequest(pub(crate) Assertions); + +impl_assertion_set!(AssertRequest, 0.ptr); + +impl AssertRequest { + /// Return a [AssertRequest] + #[allow(clippy::new_without_default)] + pub fn new() -> AssertRequest { + unsafe { + let assert = ffi::fido_assert_new(); + + AssertRequest(Assertions { + ptr: NonNull::new_unchecked(assert), + }) + } + } +} + +/// helper for verify an exist single assertion +pub struct AssertVerifier(Assertions); + +impl_assertion_set!(AssertVerifier, 0.ptr); + +impl AssertVerifier { + /// Return a [AssertVerifier] for verify. + #[allow(clippy::new_without_default)] + pub fn new() -> AssertVerifier { + unsafe { + let assert = ffi::fido_assert_new(); + ffi::fido_assert_set_count(assert, 1); + + AssertVerifier(Assertions { + ptr: NonNull::new_unchecked(assert), + }) + } + } + + /// Set the authenticator data part of the statement. + /// + /// A copy of data is made, and no references to the passed data are kept. + /// + /// The authenticator data passed to [AssertVerifier::set_auth_data] must be a CBOR-encoded byte string, + /// as obtained from [Assertion::auth_data]. + /// + /// Alternatively, a raw binary blob may be passed to [AssertVerifier::set_auth_data_raw] + pub fn set_auth_data(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + + unsafe { + check(ffi::fido_assert_set_authdata( + self.0.ptr.as_ptr(), + 0, + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Set the raw binary authenticator data part of the statement. + pub fn set_auth_data_raw(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + + unsafe { + check(ffi::fido_assert_set_authdata_raw( + self.0.ptr.as_ptr(), + 0, + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Set the signature part of the statement. + pub fn set_signature(&mut self, signature: impl AsRef<[u8]>) -> Result<()> { + let signature = signature.as_ref(); + + unsafe { + check(ffi::fido_assert_set_sig( + self.0.ptr.as_ptr(), + 0, + signature.as_ptr(), + signature.len(), + ))?; + } + + Ok(()) + } + + /// Verify whether the signature contained in statement of assert matches the parameters of the assertion. + /// + /// And verify whether the client data hash, relying party ID, user presence and user verification + /// attributes of assert have been attested by the holder of the private counterpart of the public key. + /// + /// The `public_key` is a public key of type COSE_ES256, COSE_ES384, COSE_RS256, or COSE_EDDSA. + /// + /// # Return + /// On verify success, this method return Ok(()), otherwise return Err. + pub fn verify(&self, public_key: PKey) -> Result<()> { + match public_key.id() { + Id::ED25519 => { + let pk = Eddsa::try_from(public_key)?; + + unsafe { + check(ffi::fido_assert_verify( + self.0.ptr.as_ptr(), + 0, + CoseType::EDDSA as i32, + pk.as_ptr().cast(), + ))?; + } + } + Id::RSA => { + let pk = Rsa::try_from(public_key)?; + + unsafe { + check(ffi::fido_assert_verify( + self.0.ptr.as_ptr(), + 0, + CoseType::EDDSA as i32, + pk.as_ptr().cast(), + ))?; + } + } + Id::EC => { + let ec_key = public_key.ec_key()?; + let group = ec_key.group(); + let curve = group + .curve_name() + .ok_or(FidoError::new(FIDO_ERR_INVALID_ARGUMENT))?; + match curve { + Nid::X9_62_PRIME256V1 => { + let pk = ES256::try_from(ec_key)?; + + unsafe { + check(ffi::fido_assert_verify( + self.0.ptr.as_ptr(), + 0, + CoseType::ES256 as i32, + pk.as_ptr().cast(), + ))?; + } + } + Nid::SECP384R1 => { + let pk = ES384::try_from(ec_key)?; + + unsafe { + check(ffi::fido_assert_verify( + self.0.ptr.as_ptr(), + 0, + CoseType::ES384 as i32, + pk.as_ptr().cast(), + ))?; + } + } + _ => { + return Err(FidoError::new(FIDO_ERR_INVALID_ARGUMENT))?; + } + } + } + _ => { + return Err(FidoError::new(FIDO_ERR_INVALID_ARGUMENT))?; + } + } + + Ok(()) + } +} + +impl Drop for Assertions { + fn drop(&mut self) { + let mut ptr = self.ptr.as_ptr(); + + unsafe { + ffi::fido_assert_free(&mut ptr); + } + + let _ = std::mem::replace(&mut self.ptr, NonNull::dangling()); + } +} + +impl Assertion<'_> { + /// Return relying party ID of assert. + pub fn rp_id(&self) -> Option<&str> { + let rp_id = unsafe { ffi::fido_assert_rp_id(self.ptr.as_ptr()) }; + str_or_none!(rp_id) + } + + /// Return user display name of assert. + pub fn user_display_name(&self) -> Option<&str> { + let display_name = + unsafe { ffi::fido_assert_user_display_name(self.ptr.as_ptr(), self.idx) }; + + str_or_none!(display_name) + } + + /// Return user icon of assert. + pub fn user_icon(&self) -> Option<&str> { + let icon = unsafe { ffi::fido_assert_user_icon(self.ptr.as_ptr(), self.idx) }; + + str_or_none!(icon) + } + + /// Return user name of assert. + pub fn user_name(&self) -> Option<&str> { + let name = unsafe { ffi::fido_assert_user_name(self.ptr.as_ptr(), self.idx) }; + + str_or_none!(name) + } + + /// Return CBOR-encoded authenticator data + pub fn auth_data(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_authdata_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_authdata_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return client data hash. + pub fn client_data_hash(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_clientdata_hash_len(self.ptr.as_ptr()) }; + let ptr = unsafe { ffi::fido_assert_clientdata_hash_ptr(self.ptr.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return the credBlob attribute. + pub fn blob(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_blob_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_blob_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return the hmac-secret attribute. + /// + /// The HMAC Secret Extension (hmac-secret) is a CTAP 2.0 extension. + /// + /// Note that the resulting hmac-secret varies according to whether user verification was performed by the authenticator. + pub fn hmac_secret(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_hmac_secret_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_hmac_secret_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return largeBlobKey attribute. + pub fn large_blob_key(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_largeblob_key_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_largeblob_key_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return user ID. + pub fn user_id(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_user_id_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_user_id_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return signature + pub fn signature(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_sig_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_sig_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return credential ID + pub fn id(&self) -> &[u8] { + let len = unsafe { ffi::fido_assert_id_len(self.ptr.as_ptr(), self.idx) }; + let ptr = unsafe { ffi::fido_assert_id_ptr(self.ptr.as_ptr(), self.idx) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return signature count. + pub fn counter(&self) -> u32 { + unsafe { ffi::fido_assert_sigcount(self.ptr.as_ptr(), self.idx) } + } + + /// Return authenticator data flags. + pub fn flags(&self) -> u8 { + unsafe { ffi::fido_assert_flags(self.ptr.as_ptr(), self.idx) } + } +} + +impl Assertions { + /// Return the number of assertion. + pub fn count(&self) -> usize { + unsafe { ffi::fido_assert_count(self.ptr.as_ptr()) } + } + + /// Return a iterator of contained assertion + pub fn iter(&self) -> impl Iterator { + let count = self.count(); + + AssertionIter { + asserts: self, + idx: 0, + count, + } + } +} + +/// Iterator of assertion +pub struct AssertionIter<'a> { + asserts: &'a Assertions, + idx: usize, + count: usize, +} + +impl<'a> Iterator for AssertionIter<'a> { + type Item = Assertion<'a>; + + fn next(&mut self) -> Option { + if self.idx >= self.count { + None + } else { + let item = Assertion { + ptr: self.asserts.ptr, + idx: self.idx, + _p: PhantomData, + }; + + self.idx += 1; + + Some(item) + } + } +} diff --git a/fido-rs/fido2-rs/src/cbor.rs b/fido-rs/fido2-rs/src/cbor.rs new file mode 100644 index 0000000..3af0a78 --- /dev/null +++ b/fido-rs/fido2-rs/src/cbor.rs @@ -0,0 +1,195 @@ +use std::collections::HashMap; +use std::ffi::CStr; +use std::ptr::NonNull; + +pub struct CBORInfo { + pub(crate) ptr: NonNull, +} + +impl CBORInfo { + pub(crate) fn new() -> CBORInfo { + unsafe { + CBORInfo { + ptr: NonNull::new_unchecked(ffi::fido_cbor_info_new()), + } + } + } + + pub fn aaguid(&self) -> &[u8] { + unsafe { + let len = ffi::fido_cbor_info_aaguid_len(self.ptr.as_ptr()); + let ptr = ffi::fido_cbor_info_aaguid_ptr(self.ptr.as_ptr()); + + std::slice::from_raw_parts(ptr, len) + } + } + + pub fn extensions(&self) -> Vec<&str> { + unsafe { + let len = ffi::fido_cbor_info_extensions_len(self.ptr.as_ptr()); + let ptr = ffi::fido_cbor_info_extensions_ptr(self.ptr.as_ptr()); + + let exts = std::slice::from_raw_parts(ptr, len); + + exts.iter() + .map(|it| CStr::from_ptr(*it)) + .map(|it| it.to_str().expect("invalid utf8")) + .collect() + } + } + + pub fn protocols(&self) -> &[u8] { + unsafe { + let len = ffi::fido_cbor_info_protocols_len(self.ptr.as_ptr()); + let ptr = ffi::fido_cbor_info_protocols_ptr(self.ptr.as_ptr()); + + std::slice::from_raw_parts(ptr, len) + } + } + + pub fn transports(&self) -> Vec<&str> { + unsafe { + let len = ffi::fido_cbor_info_transports_len(self.ptr.as_ptr()); + let ptr = ffi::fido_cbor_info_transports_ptr(self.ptr.as_ptr()); + + let txs = std::slice::from_raw_parts(ptr, len); + + txs.iter() + .map(|it| CStr::from_ptr(*it)) + .map(|it| it.to_str().expect("invalid utf8")) + .collect() + } + } + + pub fn versions(&self) -> Vec<&str> { + unsafe { + let len = ffi::fido_cbor_info_versions_len(self.ptr.as_ptr()); + let ptr = ffi::fido_cbor_info_versions_ptr(self.ptr.as_ptr()); + + let versions = std::slice::from_raw_parts(ptr, len); + + versions + .iter() + .map(|it| CStr::from_ptr(*it)) + .map(|it| it.to_str().expect("invalid utf8")) + .collect() + } + } + + pub fn options(&self) -> HashMap<&str, bool> { + unsafe { + let len = ffi::fido_cbor_info_options_len(self.ptr.as_ptr()); + let names = ffi::fido_cbor_info_options_name_ptr(self.ptr.as_ptr()); + let values = ffi::fido_cbor_info_options_value_ptr(self.ptr.as_ptr()); + + let names = std::slice::from_raw_parts(names, len); + let values = std::slice::from_raw_parts(values, len); + + names + .iter() + .map(|it| CStr::from_ptr(*it)) + .map(|it| it.to_str().expect("invalid utf8")) + .zip(values) + .map(|(k, v)| (k, *v)) + .collect() + } + } + + pub fn algorithms(&self) -> Vec<(&str, i32)> { + unsafe { + let count = ffi::fido_cbor_info_algorithm_count(self.ptr.as_ptr()); + + let mut rets = Vec::with_capacity(count); + + for idx in 0..count { + let algo_type = ffi::fido_cbor_info_algorithm_type(self.ptr.as_ptr(), idx); + let algo_cose = ffi::fido_cbor_info_algorithm_cose(self.ptr.as_ptr(), idx); + + let algo_type = CStr::from_ptr(algo_type).to_str().expect("invalid utf8"); + + rets.push((algo_type, algo_cose)) + } + + rets + } + } + + pub fn certs(&self) -> HashMap<&str, u64> { + unsafe { + let len = ffi::fido_cbor_info_certs_len(self.ptr.as_ptr()); + + let names = ffi::fido_cbor_info_certs_name_ptr(self.ptr.as_ptr()); + let values = ffi::fido_cbor_info_certs_value_ptr(self.ptr.as_ptr()); + + let names = std::slice::from_raw_parts(names, len); + let values = std::slice::from_raw_parts(values, len); + + names + .iter() + .map(|it| CStr::from_ptr(*it)) + .map(|it| it.to_str().expect("invalid utf8")) + .zip(values) + .map(|(k, v)| (k, *v)) + .collect() + } + } + + pub fn max_msg_size(&self) -> u64 { + unsafe { ffi::fido_cbor_info_maxmsgsiz(self.ptr.as_ptr()) } + } + + pub fn max_cred_blob_len(&self) -> u64 { + unsafe { ffi::fido_cbor_info_maxcredbloblen(self.ptr.as_ptr()) } + } + + pub fn max_cred_count_list(&self) -> u64 { + unsafe { ffi::fido_cbor_info_maxcredcntlst(self.ptr.as_ptr()) } + } + + pub fn max_cred_id_len(&self) -> u64 { + unsafe { ffi::fido_cbor_info_maxcredidlen(self.ptr.as_ptr()) } + } + + pub fn max_large_blob(&self) -> u64 { + unsafe { ffi::fido_cbor_info_maxlargeblob(self.ptr.as_ptr()) } + } + + pub fn max_rp_id_minpinlen(&self) -> u64 { + unsafe { ffi::fido_cbor_info_maxrpid_minpinlen(self.ptr.as_ptr()) } + } + + pub fn min_pin_len(&self) -> u64 { + unsafe { ffi::fido_cbor_info_minpinlen(self.ptr.as_ptr()) } + } + + pub fn fw_version(&self) -> u64 { + unsafe { ffi::fido_cbor_info_fwversion(self.ptr.as_ptr()) } + } + + pub fn uv_attempts(&self) -> u64 { + unsafe { ffi::fido_cbor_info_uv_attempts(self.ptr.as_ptr()) } + } + + pub fn uv_modality(&self) -> u64 { + unsafe { ffi::fido_cbor_info_uv_modality(self.ptr.as_ptr()) } + } + + pub fn rk_remaining(&self) -> i64 { + unsafe { ffi::fido_cbor_info_rk_remaining(self.ptr.as_ptr()) } + } + + pub fn new_pin_required(&self) -> bool { + unsafe { ffi::fido_cbor_info_new_pin_required(self.ptr.as_ptr()) } + } +} + +impl Drop for CBORInfo { + fn drop(&mut self) { + unsafe { + let mut ptr = self.ptr.as_ptr(); + ffi::fido_cbor_info_free(&mut ptr); + + let _ = std::mem::replace(&mut self.ptr, NonNull::dangling()); + } + } +} diff --git a/fido-rs/fido2-rs/src/credentials.rs b/fido-rs/fido2-rs/src/credentials.rs new file mode 100644 index 0000000..d0941bb --- /dev/null +++ b/fido-rs/fido2-rs/src/credentials.rs @@ -0,0 +1,578 @@ +use crate::error::Result; +use crate::utils::check; +use bitflags::bitflags; +use std::ffi::{CStr, CString}; +use std::ptr::NonNull; + +/// FIDO credential +pub struct Credential(pub(crate) NonNull); + +impl Drop for Credential { + fn drop(&mut self) { + unsafe { + // `fido_cred_free` set this ptr to `NULL` + ffi::fido_cred_free(&mut self.0.as_ptr()); + } + } +} + +impl Credential { + /// Create a new credential + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + unsafe { + let cred = ffi::fido_cred_new(); + + Credential(NonNull::new_unchecked(cred)) + } + } + /// If the CTAP 2.1 FIDO_EXT_MINPINLEN extension is enabled on cred, then this function returns + /// the minimum PIN length of cred. + /// + /// Otherwise, returns zero. + pub fn pin_min_len(&self) -> usize { + unsafe { ffi::fido_cred_pin_minlen(self.0.as_ptr()) } + } + + /// If the CTAP 2.1 FIDO_EXT_CRED_PROTECT extension is enabled on cred, then this function returns + /// the protection of cred. + /// + /// Otherwise, returns [None] + pub fn protection(&self) -> Option { + unsafe { + let prot = ffi::fido_cred_prot(self.0.as_ptr()); + + match prot { + ffi::FIDO_CRED_PROT_UV_OPTIONAL => Some(Protection::UvOptional), + ffi::FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID => Some(Protection::UvOptionalWithId), + ffi::FIDO_CRED_PROT_UV_REQUIRED => Some(Protection::UvRequired), + _ => None, + } + } + } + + /// Return the attestation statement format identifier of cred, or [None] if cred does not have a format set. + pub fn attestation_format(&self) -> Option { + let fmt = unsafe { ffi::fido_cred_fmt(self.0.as_ptr()) }; + + if fmt.is_null() { + None + } else { + let fmt = unsafe { CStr::from_ptr(fmt).to_str().expect("invalid utf8") }; + + match fmt { + "packed" => Some(AttestationFormat::Packed), + "fido-u2f" => Some(AttestationFormat::FidoU2f), + "tpm" => Some(AttestationFormat::Tpm), + "none" => Some(AttestationFormat::None), + _ => None, + } + } + } + + /// Return relying party ID, or [None] if is not set. + pub fn rp_id(&self) -> Option<&str> { + let rp_id = unsafe { ffi::fido_cred_rp_id(self.0.as_ptr()) }; + str_or_none!(rp_id) + } + + /// Return relying party name, or [None] if is not set. + pub fn rp_name(&self) -> Option<&str> { + let rp_name = unsafe { ffi::fido_cred_rp_name(self.0.as_ptr()) }; + str_or_none!(rp_name) + } + + /// Return user name, or [None] if is not set. + pub fn user_name(&self) -> Option<&str> { + let user_name = unsafe { ffi::fido_cred_rp_id(self.0.as_ptr()) }; + str_or_none!(user_name) + } + + /// Return user display name, or [None] if is not set. + pub fn display_name(&self) -> Option<&str> { + let display_name = unsafe { ffi::fido_cred_rp_id(self.0.as_ptr()) }; + str_or_none!(display_name) + } + + /// Return CBOR-encoded authenticator data. + /// + /// The slice len will be 0 if is not set. + pub fn auth_data(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_authdata_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_authdata_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return raw authenticator data. + /// + /// The slice len will be 0 if is not set. + pub fn auth_data_raw(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_authdata_raw_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_authdata_raw_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return client data hash + /// + /// The slice len will be 0 if is not set. + pub fn client_data_hash(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_clientdata_hash_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_clientdata_hash_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return credential ID + /// + /// The slice len will be 0 if is not set. + pub fn id(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_id_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_id_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return authenticator attestation GUID + /// + /// The slice len will be 0 if is not set. + pub fn attestation_guid(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_aaguid_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_aaguid_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return "largeBlobKey". + /// + /// The slice len will be 0 if is not set. + pub fn large_blob_key(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_largeblob_key_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_largeblob_key_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return public key. + /// + /// The slice len will be 0 if is not set. + pub fn public_key(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_pubkey_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_pubkey_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return signature. + /// + /// The slice len will be 0 if is not set. + pub fn signature(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_sig_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_sig_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return user ID. + /// + /// The slice len will be 0 if is not set. + pub fn user_id(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_user_id_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_user_id_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return X509 certificate. + /// + /// The slice len will be 0 if is not set. + pub fn certificate(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_x5c_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_x5c_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return attestation statement. + /// + /// The slice len will be 0 if is not set. + pub fn attestation(&self) -> &[u8] { + let len = unsafe { ffi::fido_cred_attstmt_len(self.0.as_ptr()) }; + let ptr = unsafe { ffi::fido_cred_attstmt_ptr(self.0.as_ptr()) }; + + unsafe { std::slice::from_raw_parts(ptr, len) } + } + + /// Return the COSE algorithm of cred. + pub fn cose_type(&self) -> CoseType { + unsafe { + let cred_type = ffi::fido_cred_type(self.0.as_ptr()); + + CoseType::try_from(cred_type).unwrap_or(CoseType::UNSPEC) + } + } + + /// Return the authenticator data flags of cred. + pub fn flags(&self) -> u8 { + unsafe { ffi::fido_cred_flags(self.0.as_ptr()) } + } + + /// Return the authenticator data signature counter of cred. + pub fn counter(&self) -> u32 { + unsafe { ffi::fido_cred_sigcount(self.0.as_ptr()) } + } + + /// Verifies whether the client data hash, relying party ID, credential ID, type, protection policy, + /// minimum PIN length, and resident/discoverable key and user verification attributes of cred + /// have been attested by the holder of the private counterpart of the public key contained in the credential's x509 certificate. + /// + /// Please note that the x509 certificate itself is not verified. + /// + /// The attestation statement formats supported by [Credential::verify] are packed, fido-u2f, and tpm. + /// + /// The attestation type implemented by [Credential::verify] is Basic Attestation. + pub fn verify(&self) -> Result<()> { + unsafe { + check(ffi::fido_cred_verify(self.0.as_ptr()))?; + } + + Ok(()) + } + + /// verifies whether the client data hash, relying party ID, credential ID, type, protection policy, + /// minimum PIN length, and resident/discoverable key and user verification attributes of cred + /// have been attested by the holder of the credential's private key. + /// + /// The attestation statement formats supported by [Credential::verify_self] are packed and fido-u2f. + /// + /// The attestation type implemented by [Credential::verify_self] is Self Attestation. + pub fn verify_self(&self) -> Result<()> { + unsafe { + check(ffi::fido_cred_verify_self(self.0.as_ptr()))?; + } + + Ok(()) + } + + /// Set CBOR-encoded authenticator data + pub fn set_auth_data(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + dbg!(data.as_ref().len()); + let data = data.as_ref(); + unsafe { + check(ffi::fido_cred_set_authdata( + self.0.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Set raw authenticator data + pub fn set_auth_data_raw(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + unsafe { + check(ffi::fido_cred_set_authdata_raw( + self.0.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Set X509 certificate + pub fn set_certificate(&mut self, cert: impl AsRef<[u8]>) -> Result<()> { + let cert = cert.as_ref(); + unsafe { + check(ffi::fido_cred_set_x509( + self.0.as_ptr(), + cert.as_ptr(), + cert.len(), + ))?; + } + + Ok(()) + } + + /// Set the client data hash of cred by specifying the credential's unhashed client data. + /// + /// This is required by Windows Hello, which calculates the client data hash internally. + /// + /// For compatibility with Windows Hello, applications should use [CredentialRequestBuilder::client_data] instead of [CredentialRequestBuilder::client_data_hash] + pub fn set_client_data(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + unsafe { + check(ffi::fido_cred_set_clientdata( + self.0.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// See [CredentialRequestBuilder::client_data] + pub fn set_client_data_hash(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + unsafe { + check(ffi::fido_cred_set_clientdata_hash( + self.0.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Set credential ID + pub fn set_id(&mut self, id: impl AsRef<[u8]>) -> Result<()> { + let id = id.as_ref(); + unsafe { + check(ffi::fido_cred_set_id( + self.0.as_ptr(), + id.as_ptr(), + id.len(), + ))?; + } + + Ok(()) + } + + /// Set the relying party id and name parameters of cred + pub fn set_rp(&mut self, id: impl AsRef, name: impl AsRef) -> Result<()> { + let id = CString::new(id.as_ref())?; + let name = CString::new(name.as_ref())?; + + unsafe { + check(ffi::fido_cred_set_rp( + self.0.as_ptr(), + id.as_ptr(), + name.as_ptr(), + ))?; + } + + Ok(()) + } + + /// See [CredentialRequestBuilder::client_data] + pub fn set_signature(&mut self, sig: impl AsRef<[u8]>) -> Result<()> { + let sig = sig.as_ref(); + unsafe { + check(ffi::fido_cred_set_sig( + self.0.as_ptr(), + sig.as_ptr(), + sig.len(), + ))?; + } + + Ok(()) + } + + /// Sets the user attributes of cred. + /// + /// Previously set user attributes are flushed + pub fn set_user( + &mut self, + id: impl AsRef<[u8]>, + name: impl AsRef, + display_name: Option<&str>, + icon: Option<&str>, + ) -> Result<()> { + let id = id.as_ref(); + let name = CString::new(name.as_ref())?; + let display_name = display_name.map(CString::new).transpose()?; + let icon = icon.map(CString::new).transpose()?; + + let display_name_ptr = match &display_name { + Some(it) => it.as_ptr(), + None => std::ptr::null(), + }; + + let icon_ptr = match &icon { + Some(it) => it.as_ptr(), + None => std::ptr::null(), + }; + + unsafe { + check(ffi::fido_cred_set_user( + self.0.as_ptr(), + id.as_ptr(), + id.len(), + name.as_ptr(), + display_name_ptr, + icon_ptr, + ))?; + } + + Ok(()) + } + + /// Sets the extensions of cred to the bitmask flags. + /// + /// Only the FIDO_EXT_CRED_BLOB, FIDO_EXT_CRED_PROTECT, FIDO_EXT_HMAC_SECRET, + /// FIDO_EXT_MINPINLEN, and FIDO_EXT_LARGEBLOB_KEY extensions are supported. + /// + /// See [Extensions] + pub fn set_extension(&mut self, flags: Extensions) -> Result<()> { + unsafe { + check(ffi::fido_cred_set_extensions(self.0.as_ptr(), flags.bits))?; + } + + Ok(()) + } + + /// Sets the “credBlob” to be stored with cred. + pub fn set_blob(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); + unsafe { + check(ffi::fido_cred_set_blob( + self.0.as_ptr(), + data.as_ptr(), + data.len(), + ))?; + } + + Ok(()) + } + + /// Enable the CTAP 2.1 FIDO_EXT_MINPINLEN extension on cred and sets the expected minimum PIN length of cred to len. + /// + /// If len is zero, the FIDO_EXT_MINPINLEN extension is disabled on cred. + pub fn set_pin_min_len(&mut self, len: usize) -> Result<()> { + unsafe { + check(ffi::fido_cred_set_pin_minlen(self.0.as_ptr(), len))?; + } + + Ok(()) + } + + /// Enables the CTAP 2.1 FIDO_EXT_CRED_PROTECT extension on cred and sets the protection of cred to the scalar prot. + /// + /// At the moment, only the FIDO_CRED_PROT_UV_OPTIONAL, FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID, and FIDO_CRED_PROT_UV_REQUIRED protections are supported. + /// + /// See [Protection] + pub fn set_protection(&mut self, prot: Protection) -> Result<()> { + unsafe { + check(ffi::fido_cred_set_prot(self.0.as_ptr(), prot as i32))?; + } + + Ok(()) + } + + /// Set the rk (resident/discoverable key) attribute of cred. + pub fn set_rk(&mut self, rk: Opt) -> Result<()> { + unsafe { + check(ffi::fido_cred_set_rk(self.0.as_ptr(), rk as _))?; + } + + Ok(()) + } + + /// Set the uv (user verification) attribute of cred. + pub fn set_uv(&mut self, uv: Opt) -> Result<()> { + unsafe { + check(ffi::fido_cred_set_uv(self.0.as_ptr(), uv as _))?; + } + + Ok(()) + } + + /// Sets the attestation statement format identifier of cred. + /// + /// Note that not all authenticators support FIDO2 and therefore may only be able to generate fido-u2f attestation statements. + pub fn set_attestation_format(&mut self, fmt: AttestationFormat) -> Result<()> { + let fmt = match fmt { + AttestationFormat::Packed => CString::new("packed"), + AttestationFormat::FidoU2f => CString::new("fido-u2f"), + AttestationFormat::Tpm => CString::new("tpm"), + AttestationFormat::None => CString::new("none"), + }; + let fmt = fmt.unwrap(); + + unsafe { + check(ffi::fido_cred_set_fmt(self.0.as_ptr(), fmt.as_ptr()))?; + } + + Ok(()) + } + + /// Sets the type of cred. + /// + /// The `type` of a credential may only be set once. + /// + /// Note that not all authenticators support COSE_RS256, COSE_ES384, or COSE_EDDSA. + pub fn set_cose_type(&mut self, ty: CoseType) -> Result<()> { + unsafe { + check(ffi::fido_cred_set_type(self.0.as_ptr(), ty as i32))?; + } + + Ok(()) + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(i32)] +pub enum Opt { + Omit = 0, + False = 1, + True = 2, +} + +#[derive(Copy, Clone, Debug)] +#[repr(i32)] +pub enum Protection { + UvOptional = ffi::FIDO_CRED_PROT_UV_OPTIONAL, + UvOptionalWithId = ffi::FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID, + UvRequired = ffi::FIDO_CRED_PROT_UV_REQUIRED, +} + +/// Attestation statement format +#[derive(Copy, Clone, Debug)] +pub enum AttestationFormat { + Packed, + FidoU2f, + Tpm, + None, +} + +/// COSE Algorithms type +#[repr(i32)] +pub enum CoseType { + ES256 = ffi::COSE_ES256, + ES384 = ffi::COSE_ES384, + RS256 = ffi::COSE_RS256, + EDDSA = ffi::COSE_EDDSA, + UNSPEC = ffi::COSE_UNSPEC, +} + +impl TryFrom for CoseType { + type Error = i32; + + fn try_from(value: i32) -> Result { + match value { + ffi::COSE_UNSPEC => Ok(CoseType::UNSPEC), + ffi::COSE_ES256 => Ok(CoseType::ES256), + ffi::COSE_ES384 => Ok(CoseType::ES384), + ffi::COSE_RS256 => Ok(CoseType::RS256), + ffi::COSE_EDDSA => Ok(CoseType::EDDSA), + _ => Err(value), + } + } +} + +bitflags! { + /// FIDO extensions + pub struct Extensions: i32 { + const CRED_BLOB = ffi::FIDO_EXT_CRED_BLOB; + const CRED_PROTECT = ffi::FIDO_EXT_CRED_PROTECT; + const HMAC_SECRET = ffi::FIDO_EXT_HMAC_SECRET; + const MIN_PINLEN = ffi::FIDO_EXT_MINPINLEN; + const LARGEBLOB_KEY = ffi::FIDO_EXT_LARGEBLOB_KEY; + } +} diff --git a/fido-rs/fido2-rs/src/device.rs b/fido-rs/fido2-rs/src/device.rs new file mode 100644 index 0000000..7a300d4 --- /dev/null +++ b/fido-rs/fido2-rs/src/device.rs @@ -0,0 +1,390 @@ +use crate::assertion::{AssertRequest, Assertions}; +use crate::cbor::CBORInfo; +use crate::credentials::Credential; +use crate::error::Result; +use crate::utils::check; +use bitflags::bitflags; +use ffi::fido_dev_t; +use std::ffi::{CStr, CString}; +use std::marker::PhantomData; +use std::ptr::NonNull; + +/// Device list. +/// +/// contain fido devices found by the underlying operating system. +/// +/// user can call [DeviceList::list_devices] to start enumerate fido devices. +pub struct DeviceList<'a> { + ptr: NonNull, + idx: usize, + found: usize, + _p: PhantomData<&'a ()>, +} + +impl<'a> DeviceList<'a> { + /// Enumerate up to `max` fido devices found by the underlying operating system. + /// + /// Currently only USB HID devices are supported + pub fn list_devices(max: usize) -> DeviceList<'a> { + unsafe { + let mut found = 0; + let ptr = ffi::fido_dev_info_new(max); + + ffi::fido_dev_info_manifest(ptr, max, &mut found); + + DeviceList { + ptr: NonNull::new_unchecked(ptr), + idx: 0, + found, + _p: PhantomData, + } + } + } +} + +impl<'a> Iterator for DeviceList<'a> { + type Item = DeviceInfo<'a>; + + fn next(&mut self) -> Option { + if self.idx >= self.found { + return None; + } + + unsafe { + let ptr = self.ptr.as_ptr(); + let info = ffi::fido_dev_info_ptr(ptr, self.idx); + + let path = ffi::fido_dev_info_path(info); + let path = CStr::from_ptr(path); + + let product_id = ffi::fido_dev_info_product(info); + let vendor_id = ffi::fido_dev_info_vendor(info); + + let manufacturer = ffi::fido_dev_info_manufacturer_string(info); + let manufacturer = CStr::from_ptr(manufacturer); + + let product = ffi::fido_dev_info_product_string(info); + let product = CStr::from_ptr(product); + self.idx += 1; + + Some(DeviceInfo { + path, + product_id, + vendor_id, + manufacturer, + product, + }) + } + } +} + +impl<'a> ExactSizeIterator for DeviceList<'a> { + fn len(&self) -> usize { + self.found + } +} + +impl<'a> Drop for DeviceList<'a> { + fn drop(&mut self) { + unsafe { + ffi::fido_dev_info_free(&mut self.ptr.as_ptr(), self.found); + } + } +} + +/// Device info obtained from [DeviceList] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct DeviceInfo<'a> { + pub path: &'a CStr, + pub product_id: i16, + pub vendor_id: i16, + pub manufacturer: &'a CStr, + pub product: &'a CStr, +} + +impl<'a> DeviceInfo<'a> { + /// Open the device specified by this [DeviceInfo] + pub fn open(&self) -> Result { + unsafe { + let ptr = ffi::fido_dev_new(); + check(ffi::fido_dev_open(ptr, self.path.as_ptr()))?; + + let ptr = NonNull::new_unchecked(ptr); + + Ok(Device { ptr }) + } + } +} + +/// A cancel handle to device, used to cancel a pending requests. +/// +/// This handle can be copy/clone. +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct DeviceCancel(NonNull); + +impl DeviceCancel { + /// Cancel any pending requests on device. + pub fn cancel(&self) { + unsafe { + ffi::fido_dev_cancel(self.0.as_ptr()); + } + } +} + +/// A fido device. +pub struct Device { + ptr: NonNull, +} + +impl Device { + /// Open the device pointed to by `path`. + /// + /// If dev claims to be FIDO2, libfido2 will attempt to speak FIDO2 to dev. + /// If that fails, libfido2 will fallback to U2F unless the FIDO_DISABLE_U2F_FALLBACK flag + /// was set in fido_init(3). + pub fn open(path: impl AsRef) -> Result { + let path = CString::new(path.as_ref())?; + unsafe { + let dev = ffi::fido_dev_new(); + assert!(!dev.is_null()); + + check(ffi::fido_dev_open(dev, path.as_ptr()))?; + + Ok(Device { + ptr: NonNull::new_unchecked(dev), + }) + } + } + + /// Get a handle of this device for cancel. + pub fn cancel_handle(&self) -> DeviceCancel { + DeviceCancel(self.ptr) + } + + /// can be used to force CTAP2 communication with dev + pub fn force_u2f(&self) { + unsafe { + ffi::fido_dev_force_u2f(self.ptr.as_ptr()); + } + } + + /// Can be used to force CTAP1 (U2F) communication with dev + pub fn force_fido2(&self) { + unsafe { + ffi::fido_dev_force_fido2(self.ptr.as_ptr()); + } + } + + /// Returns true if dev is a FIDO2 device. + pub fn is_fido2(&self) -> bool { + unsafe { ffi::fido_dev_is_fido2(self.ptr.as_ptr()) } + } + + /// Returns true if dev is a Windows Hello device. + pub fn is_winhello(&self) -> bool { + unsafe { ffi::fido_dev_is_winhello(self.ptr.as_ptr()) } + } + + /// Returns true if dev supports CTAP 2.1 Credential Management. + pub fn supports_credman(&self) -> bool { + unsafe { ffi::fido_dev_supports_credman(self.ptr.as_ptr()) } + } + + /// Returns true if dev supports CTAP 2.1 Credential Protection. + pub fn supports_cred_prot(&self) -> bool { + unsafe { ffi::fido_dev_supports_cred_prot(self.ptr.as_ptr()) } + } + + /// Returns true if dev supports CTAP 2.1 UV token permissions. + pub fn supports_permission(&self) -> bool { + unsafe { ffi::fido_dev_supports_permissions(self.ptr.as_ptr()) } + } + + /// Returns true if dev supports CTAP 2.0 Client PINs. + pub fn supports_pin(&self) -> bool { + unsafe { ffi::fido_dev_supports_pin(self.ptr.as_ptr()) } + } + + /// Returns true if dev supports a built-in user verification method. + pub fn supports_uv(&self) -> bool { + unsafe { ffi::fido_dev_supports_uv(self.ptr.as_ptr()) } + } + + /// Returns true if dev has a CTAP 2.0 Client PIN set. + pub fn has_pin(&self) -> bool { + unsafe { ffi::fido_dev_has_pin(self.ptr.as_ptr()) } + } + + /// Returns true if dev supports built-in user verification and its user verification feature is configured. + pub fn has_uv(&self) -> bool { + unsafe { ffi::fido_dev_has_uv(self.ptr.as_ptr()) } + } + + /// Return CTAPHID protocol info. + pub fn ctap_protocol(&self) -> CTAPHIDInfo { + unsafe { + let protocol = ffi::fido_dev_protocol(self.ptr.as_ptr()); + let build = ffi::fido_dev_build(self.ptr.as_ptr()); + let flags = ffi::fido_dev_flags(self.ptr.as_ptr()); + let flags = CTAPHIDFlags::from_bits_truncate(flags); + let major = ffi::fido_dev_major(self.ptr.as_ptr()); + let minor = ffi::fido_dev_minor(self.ptr.as_ptr()); + + CTAPHIDInfo { + protocol, + build, + flags, + major, + minor, + } + } + } + + /// Return device info. + pub fn info(&self) -> Result { + let info = CBORInfo::new(); + + unsafe { + check(ffi::fido_dev_get_cbor_info( + self.ptr.as_ptr(), + info.ptr.as_ptr(), + ))?; + } + + Ok(info) + } + + /// Generates a new credential on a FIDO2 device. + /// + /// Ask the FIDO2 device represented by dev to generate a new credential according to the following parameters defined in cred: + /// * type + /// * client data hash + /// * relying party + /// * user attributes + /// * list of excluded credential IDs + /// * resident/discoverable key and user verification attributes + /// + /// If a PIN is not needed to authenticate the request against dev, then pin may be [None]. + /// + /// **Please note that fido_dev_make_cred() is synchronous and will block if necessary.** + /// + /// # Example + /// ```rust,no_run + /// use fido2_rs::credentials::Credential; + /// use fido2_rs::device::Device; + /// use fido2_rs::credentials::CoseType; + /// + /// fn main() -> anyhow::Result<()> { + /// let dev = Device::open("windows://hello").expect("unable open device"); + /// let mut cred = Credential::new(); + /// cred.set_client_data(&[1, 2, 3, 4, 5, 6])?; + /// cred.set_rp("fido_rs", "fido example")?; + /// cred.set_user(&[1, 2, 3, 4, 5, 6], "alice", Some("alice"), None)?; + /// cred.set_cose_type(CoseType::RS256)?; + /// + /// let _ = dev.make_credential(&mut cred, None)?; // and not require pin.. + /// + /// dbg!(cred.id()); + /// Ok(()) + /// } + /// ``` + pub fn make_credential(&self, credential: &mut Credential, pin: Option<&str>) -> Result<()> { + let pin = pin.map(CString::new).transpose()?; + let pin_ptr = match &pin { + Some(pin) => pin.as_ptr(), + None => std::ptr::null(), + }; + + unsafe { + check(ffi::fido_dev_make_cred( + self.ptr.as_ptr(), + credential.0.as_ptr(), + pin_ptr, + ))?; + } + + Ok(()) + } + + /// Obtains an assertion from a FIDO2 device. + /// + /// Ask the FIDO2 device represented by dev for an assertion according to the following parameters defined in assert: + /// * relying party ID + /// * client data hash + /// * list of allowed credential IDs + /// * user presence and user verification attributes + /// + /// If a PIN is not needed to authenticate the request against dev, then pin may be NULL. + /// + /// **Please note that fido_dev_get_assert() is synchronous and will block if necessary.** + /// + /// # Example + /// ```rust,no_run + /// use fido2_rs::assertion::AssertRequest; + /// use fido2_rs::credentials::Opt; + /// use fido2_rs::device::Device; + /// + /// fn main() -> anyhow::Result<()> { + /// let dev = Device::open("windows://hello")?; + /// let mut request = AssertRequest::new(); /// + /// + /// request.set_rp("fido_rs")?; + /// request.set_client_data(&[1, 2, 3, 4, 5, 6])?; + /// request.set_uv(Opt::True)?; + /// + /// let _assertions = dev.get_assertion(request, None)?; + /// Ok(()) + /// } + /// ``` + pub fn get_assertion(&self, request: AssertRequest, pin: Option<&str>) -> Result { + let pin = pin.map(CString::new).transpose()?; + let pin_ptr = match &pin { + Some(pin) => pin.as_ptr(), + None => std::ptr::null(), + }; + + unsafe { + check(ffi::fido_dev_get_assert( + self.ptr.as_ptr(), + request.0.ptr.as_ptr(), + pin_ptr, + ))?; + } + + Ok(request.0) + } +} + +impl Drop for Device { + fn drop(&mut self) { + unsafe { + let _ = ffi::fido_dev_close(self.ptr.as_ptr()); + ffi::fido_dev_free(&mut self.ptr.as_ptr()); + } + } +} + +bitflags! { + /// CTAPHID capabilities + pub struct CTAPHIDFlags: u8 { + const WINK = ffi::FIDO_CAP_WINK as u8; + const CBOR = ffi::FIDO_CAP_CBOR as u8; + const NMSG = ffi::FIDO_CAP_NMSG as u8; + } +} + +/// For the format and meaning of the CTAPHID parameters, +/// please refer to the FIDO Client to Authenticator Protocol (CTAP) specification. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct CTAPHIDInfo { + /// CTAPHID protocol version identifier of dev + pub protocol: u8, + /// CTAPHID build version number of dev. + pub build: u8, + /// CTAPHID capabilities flags of dev. + pub flags: CTAPHIDFlags, + /// CTAPHID major version number of dev. + pub major: u8, + /// CTAPHID minor version number of dev. + pub minor: u8, +} diff --git a/fido-rs/fido2-rs/src/error.rs b/fido-rs/fido2-rs/src/error.rs new file mode 100644 index 0000000..f29750a --- /dev/null +++ b/fido-rs/fido2-rs/src/error.rs @@ -0,0 +1,51 @@ +use std::ffi::CStr; +use std::fmt::{Debug, Display, Formatter}; + +pub type Result = std::result::Result; + +/// Error type of fido2-rs +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("libfido2: {0}")] + Fido(#[from] FidoError), + + #[error("{0}")] + NulError(#[from] std::ffi::NulError), + + #[error("openssl {0}")] + Openssl(#[from] openssl::error::ErrorStack), +} + +/// Error from libfido2 +pub struct FidoError { + /// the origin error code + pub code: i32, +} + +impl FidoError { + pub(crate) const fn new(code: i32) -> FidoError { + FidoError { code } + } +} + +impl Debug for FidoError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + ::fmt(self, f) + } +} + +impl Display for FidoError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let err = unsafe { + let err = ffi::fido_strerr(self.code); + CStr::from_ptr(err) + }; + + f.debug_struct("Error") + .field("code", &self.code) + .field("message", &err) + .finish() + } +} + +impl std::error::Error for FidoError {} diff --git a/fido-rs/fido2-rs/src/key.rs b/fido-rs/fido2-rs/src/key.rs new file mode 100644 index 0000000..cf71856 --- /dev/null +++ b/fido-rs/fido2-rs/src/key.rs @@ -0,0 +1,85 @@ +use crate::error::FidoError; +use openssl::ec::EcKey; +use openssl::pkey::{PKey, Public}; +use std::ptr::NonNull; + +macro_rules! impl_key { + ( + type CType = $ctype:ty; + fn new = $new:expr; + $(fn from<$t:ty> = $from:expr;)* + fn drop = $drop:expr; + + pub struct $ty:ident; + ) => { + pub struct $ty(NonNull<$ctype>); + + $(impl TryFrom<$t> for $ty { + type Error = FidoError; + + fn try_from(value: $t) -> Result { + use foreign_types::ForeignType; + + unsafe { + let pk = $new(); + crate::utils::check($from(pk, value.as_ptr() as _))?; + + Ok($ty(NonNull::new_unchecked(pk))) + } + } + })* + + impl Drop for $ty { + fn drop(&mut self) { + let mut ptr = self.0.as_ptr(); + unsafe { + $drop(&mut ptr); + } + + let _ = std::mem::replace(&mut self.0, NonNull::dangling()); + } + } + + impl $ty { + pub(crate) fn as_ptr(&self) -> *const $ctype { + self.0.as_ptr() + } + } + }; +} + +impl_key! { + type CType = ffi::eddsa_pk_t; + fn new = ffi::eddsa_pk_new; + fn from> = ffi::eddsa_pk_from_EVP_PKEY; + fn drop = ffi::eddsa_pk_free; + + pub struct Eddsa; +} + +impl_key! { + type CType = ffi::rs256_pk_t; + fn new = ffi::rs256_pk_new; + fn from> = ffi::rs256_pk_from_EVP_PKEY; + fn drop = ffi::rs256_pk_free; + + pub struct Rsa; +} + +impl_key! { + type CType = ffi::es256_pk_t; + fn new = ffi::es256_pk_new; + fn from> = ffi::es256_pk_from_EC_KEY; + fn drop = ffi::es256_pk_free; + + pub struct ES256; +} + +impl_key! { + type CType = ffi::es384_pk_t; + fn new = ffi::es384_pk_new; + fn from> = ffi::es384_pk_from_EC_KEY; + fn drop = ffi::es384_pk_free; + + pub struct ES384; +} diff --git a/fido-rs/fido2-rs/src/lib.rs b/fido-rs/fido2-rs/src/lib.rs new file mode 100644 index 0000000..2310fb4 --- /dev/null +++ b/fido-rs/fido2-rs/src/lib.rs @@ -0,0 +1,79 @@ +//! Bindings to Yubico libfido2 +//! +//! This crate provides a safe interface to the Yubico libfido2 library. +//! +//! # Building +//! +//! There are multiple options available to locate libfido2. +//! +//! ## Pre-build MSVC binary. +//! +//! If the rust toolchain is msvc, the `libfido2-sys` crate will download a pre-build binary dll from +//! Yubico release. +//! +//! ## Build from source. +//! +//! If the target is not msvc(mingw on windows or linux), this crate will build a static library from source. +//! +//! The build process requires a C compiler, cmake, libcbor, zlib, libcrypto. +//! +//! ## Automatic +//! +//! The `libfido2-sys` crate can automatically detect libfido2 installations via vcpkg on Windows and `pkg-config` on Linux. +//! +//! This method can be enabled by set environment variable `FIDO2_USE_PKG_CONFIG` to any non empty value. +//! +//! ## Manual +//! +//! A `FIDO2_LIB_DIR` environment variable can be used to help `libfido2-sys` to find a libfido2 installation. +//! +//! The directory should contains the libfido2 libraries. +//! +//! The other dependency like libcbor, libcrypto, zlib will use system version. Currently there is no way to +//! set these library directory, but you can put them together in `FIDO2_LIB_DIR`. +//! +//! # Example +//! +//! ## Enumerate fido devices on system +//! ```rust,no_run +//! use fido2_rs::device::DeviceList; +//! +//! let list = DeviceList::list_devices(4); +//! for dev in list { +//! println!("{:?}", dev.path); +//! } +//! +//! ``` +//! +//! ## Make a credential +//! ```rust,no_run +//! use fido2_rs::device::Device; +//! use fido2_rs::credentials::Credential; +//! use fido2_rs::credentials::CoseType; +//! use anyhow::Result; +//! fn main() -> Result<()> { +//! let dev = Device::open("windows://hello").expect("unable open windows hello"); +//! +//! let mut cred = Credential::new(); +//! cred.set_client_data(&[1, 2, 3, 4, 5, 6])?; +//! cred.set_rp("fido_rs", "fido example")?; +//! cred.set_user(&[1, 2, 3, 4, 5, 6], "alice", Some("alice"), None)?; +//! cred.set_cose_type(CoseType::RS256)?; +//! +//! let _ = dev.make_credential(&mut cred, None)?; +//! dbg!(cred.id()); +//! +//! Ok(()) +//! } +//! ``` +extern crate libfido2_sys as ffi; + +#[macro_use] +mod utils; + +pub mod assertion; +mod cbor; +pub mod credentials; +pub mod device; +pub mod error; +mod key; diff --git a/fido-rs/fido2-rs/src/utils.rs b/fido-rs/fido2-rs/src/utils.rs new file mode 100644 index 0000000..f364107 --- /dev/null +++ b/fido-rs/fido2-rs/src/utils.rs @@ -0,0 +1,24 @@ +use crate::error::FidoError; + +pub(crate) const fn check(code: i32) -> Result<(), FidoError> { + match code { + 0 => Ok(()), + _ => Err(FidoError::new(code)), + } +} + +macro_rules! str_or_none { + ($ptr:ident) => { + if $ptr.is_null() { + None + } else { + let $ptr = unsafe { + std::ffi::CStr::from_ptr($ptr) + .to_str() + .expect("invalid utf8") + }; + + Some($ptr) + } + }; +} diff --git a/fido-rs/libfido2-sys/Cargo.toml b/fido-rs/libfido2-sys/Cargo.toml new file mode 100644 index 0000000..d1f8894 --- /dev/null +++ b/fido-rs/libfido2-sys/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "libfido2-sys" +version = "0.2.0" +authors = ["tyan boot "] +license = "MIT" +description = "FFI bindings to Yubico fido2" +repository = "https://github.com/tyan-boot/fido-rs" +keywords = ["fido2", "webauthn"] +categories = ["external-ffi-bindings"] +edition = "2021" +links = "fido2" + +build = "build.rs" + +[dependencies] + +[build-dependencies] +anyhow = "1.0.66" +cfg-if = "1.0.0" + +[target.'cfg(target_env = "msvc")'.build-dependencies] +vcpkg = "0.2.15" + +[target.'cfg(not(target_env = "msvc"))'.build-dependencies] +pkg-config = "0.3.26" diff --git a/fido-rs/libfido2-sys/LICENSE b/fido-rs/libfido2-sys/LICENSE new file mode 100644 index 0000000..4fa01fb --- /dev/null +++ b/fido-rs/libfido2-sys/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 tyan boot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/fido-rs/libfido2-sys/README.MD b/fido-rs/libfido2-sys/README.MD new file mode 100644 index 0000000..2b69a18 --- /dev/null +++ b/fido-rs/libfido2-sys/README.MD @@ -0,0 +1,6 @@ +# libfido2-sys + +[![crates.io](https://img.shields.io/crates/v/libfido2-sys?style=flat-square)](https://crates.io/crates/libfido2-sys) +[![MIT](https://img.shields.io/crates/l/libfido2-sys?style=flat-square)](./LICENSE) + +FFI bindings to Yubico [libfido2](https://github.com/Yubico/libfido2) \ No newline at end of file diff --git a/fido-rs/libfido2-sys/build.rs b/fido-rs/libfido2-sys/build.rs new file mode 100644 index 0000000..65f6207 --- /dev/null +++ b/fido-rs/libfido2-sys/build.rs @@ -0,0 +1,57 @@ +use anyhow::Result; +use cfg_if::cfg_if; +use std::env; + +fn main() -> Result<()> { + println!("cargo:rerun-if-env-changed=FIDO2_LIB_DIR"); + println!("cargo:rerun-if-env-changed=FIDO2_USE_PKG_CONFIG"); + + if let Ok(dir) = env::var("FIDO2_LIB_DIR") { + println!("cargo:rustc-link-search={}", dir); + println!("cargo:rustc-link-lib=static=fido2"); + + if cfg!(windows) { + println!("cargo:rustc-link-lib=hid"); + println!("cargo:rustc-link-lib=user32"); + println!("cargo:rustc-link-lib=setupapi"); + println!("cargo:rustc-link-lib=crypt32"); + } + + cfg_if! { + if #[cfg(all(windows, target_env = "msvc"))] { + // link to pre-build cbor,zlib,crypto + println!("cargo:rustc-link-lib=cbor"); + println!("cargo:rustc-link-lib=zlib1"); + println!("cargo:rustc-link-lib=crypto-49"); + } else { + println!("cargo:rustc-link-lib=cbor"); + println!("cargo:rustc-link-lib=z"); + println!("cargo:rustc-link-lib=crypto"); + } + } + + return Ok(()); + } + + find_pkg() + +} + +#[cfg(not(target_env = "msvc"))] +fn find_pkg() -> Result<()> { + let _lib = pkg_config::probe_library("libfido2")?; + + Ok(()) +} + +#[cfg(all(windows, target_env = "msvc"))] +fn find_pkg() -> Result<()> { + let _lib = vcpkg::find_package("libfido2")?; + + println!("cargo:rustc-link-lib=hid"); + println!("cargo:rustc-link-lib=user32"); + println!("cargo:rustc-link-lib=setupapi"); + println!("cargo:rustc-link-lib=crypt32"); + + Ok(()) +} diff --git a/fido-rs/libfido2-sys/fido2_wrapper.h b/fido-rs/libfido2-sys/fido2_wrapper.h new file mode 100644 index 0000000..ef4c8f1 --- /dev/null +++ b/fido-rs/libfido2-sys/fido2_wrapper.h @@ -0,0 +1,5 @@ +#include +#include +#include +#include +#include diff --git a/fido-rs/libfido2-sys/help.txt b/fido-rs/libfido2-sys/help.txt new file mode 100644 index 0000000..7334de0 Binary files /dev/null and b/fido-rs/libfido2-sys/help.txt differ diff --git a/fido-rs/libfido2-sys/src/ffi.rs b/fido-rs/libfido2-sys/src/ffi.rs new file mode 100644 index 0000000..2225c90 --- /dev/null +++ b/fido-rs/libfido2-sys/src/ffi.rs @@ -0,0 +1,856 @@ +/* automatically generated by rust-bindgen 0.63.0 */ + +pub const FIDO_ERR_SUCCESS: i32 = 0; +pub const FIDO_ERR_INVALID_COMMAND: i32 = 1; +pub const FIDO_ERR_INVALID_PARAMETER: i32 = 2; +pub const FIDO_ERR_INVALID_LENGTH: i32 = 3; +pub const FIDO_ERR_INVALID_SEQ: i32 = 4; +pub const FIDO_ERR_TIMEOUT: i32 = 5; +pub const FIDO_ERR_CHANNEL_BUSY: i32 = 6; +pub const FIDO_ERR_LOCK_REQUIRED: i32 = 10; +pub const FIDO_ERR_INVALID_CHANNEL: i32 = 11; +pub const FIDO_ERR_CBOR_UNEXPECTED_TYPE: i32 = 17; +pub const FIDO_ERR_INVALID_CBOR: i32 = 18; +pub const FIDO_ERR_MISSING_PARAMETER: i32 = 20; +pub const FIDO_ERR_LIMIT_EXCEEDED: i32 = 21; +pub const FIDO_ERR_UNSUPPORTED_EXTENSION: i32 = 22; +pub const FIDO_ERR_FP_DATABASE_FULL: i32 = 23; +pub const FIDO_ERR_LARGEBLOB_STORAGE_FULL: i32 = 24; +pub const FIDO_ERR_CREDENTIAL_EXCLUDED: i32 = 25; +pub const FIDO_ERR_PROCESSING: i32 = 33; +pub const FIDO_ERR_INVALID_CREDENTIAL: i32 = 34; +pub const FIDO_ERR_USER_ACTION_PENDING: i32 = 35; +pub const FIDO_ERR_OPERATION_PENDING: i32 = 36; +pub const FIDO_ERR_NO_OPERATIONS: i32 = 37; +pub const FIDO_ERR_UNSUPPORTED_ALGORITHM: i32 = 38; +pub const FIDO_ERR_OPERATION_DENIED: i32 = 39; +pub const FIDO_ERR_KEY_STORE_FULL: i32 = 40; +pub const FIDO_ERR_NOT_BUSY: i32 = 41; +pub const FIDO_ERR_NO_OPERATION_PENDING: i32 = 42; +pub const FIDO_ERR_UNSUPPORTED_OPTION: i32 = 43; +pub const FIDO_ERR_INVALID_OPTION: i32 = 44; +pub const FIDO_ERR_KEEPALIVE_CANCEL: i32 = 45; +pub const FIDO_ERR_NO_CREDENTIALS: i32 = 46; +pub const FIDO_ERR_USER_ACTION_TIMEOUT: i32 = 47; +pub const FIDO_ERR_NOT_ALLOWED: i32 = 48; +pub const FIDO_ERR_PIN_INVALID: i32 = 49; +pub const FIDO_ERR_PIN_BLOCKED: i32 = 50; +pub const FIDO_ERR_PIN_AUTH_INVALID: i32 = 51; +pub const FIDO_ERR_PIN_AUTH_BLOCKED: i32 = 52; +pub const FIDO_ERR_PIN_NOT_SET: i32 = 53; +pub const FIDO_ERR_PIN_REQUIRED: i32 = 54; +pub const FIDO_ERR_PIN_POLICY_VIOLATION: i32 = 55; +pub const FIDO_ERR_PIN_TOKEN_EXPIRED: i32 = 56; +pub const FIDO_ERR_REQUEST_TOO_LARGE: i32 = 57; +pub const FIDO_ERR_ACTION_TIMEOUT: i32 = 58; +pub const FIDO_ERR_UP_REQUIRED: i32 = 59; +pub const FIDO_ERR_UV_BLOCKED: i32 = 60; +pub const FIDO_ERR_UV_INVALID: i32 = 63; +pub const FIDO_ERR_UNAUTHORIZED_PERM: i32 = 64; +pub const FIDO_ERR_ERR_OTHER: i32 = 127; +pub const FIDO_ERR_SPEC_LAST: i32 = 223; +pub const FIDO_OK: i32 = 0; +pub const FIDO_ERR_TX: i32 = -1; +pub const FIDO_ERR_RX: i32 = -2; +pub const FIDO_ERR_RX_NOT_CBOR: i32 = -3; +pub const FIDO_ERR_RX_INVALID_CBOR: i32 = -4; +pub const FIDO_ERR_INVALID_PARAM: i32 = -5; +pub const FIDO_ERR_INVALID_SIG: i32 = -6; +pub const FIDO_ERR_INVALID_ARGUMENT: i32 = -7; +pub const FIDO_ERR_USER_PRESENCE_REQUIRED: i32 = -8; +pub const FIDO_ERR_INTERNAL: i32 = -9; +pub const FIDO_ERR_NOTFOUND: i32 = -10; +pub const FIDO_ERR_COMPRESS: i32 = -11; +pub const CTAP_AUTHDATA_USER_PRESENT: i32 = 1; +pub const CTAP_AUTHDATA_USER_VERIFIED: i32 = 4; +pub const CTAP_AUTHDATA_ATT_CRED: i32 = 64; +pub const CTAP_AUTHDATA_EXT_DATA: i32 = 128; +pub const CTAP_CMD_PING: i32 = 1; +pub const CTAP_CMD_MSG: i32 = 3; +pub const CTAP_CMD_LOCK: i32 = 4; +pub const CTAP_CMD_INIT: i32 = 6; +pub const CTAP_CMD_WINK: i32 = 8; +pub const CTAP_CMD_CBOR: i32 = 16; +pub const CTAP_CMD_CANCEL: i32 = 17; +pub const CTAP_KEEPALIVE: i32 = 59; +pub const CTAP_FRAME_INIT: i32 = 128; +pub const CTAP_CBOR_MAKECRED: i32 = 1; +pub const CTAP_CBOR_ASSERT: i32 = 2; +pub const CTAP_CBOR_GETINFO: i32 = 4; +pub const CTAP_CBOR_CLIENT_PIN: i32 = 6; +pub const CTAP_CBOR_RESET: i32 = 7; +pub const CTAP_CBOR_NEXT_ASSERT: i32 = 8; +pub const CTAP_CBOR_LARGEBLOB: i32 = 12; +pub const CTAP_CBOR_CONFIG: i32 = 13; +pub const CTAP_CBOR_BIO_ENROLL_PRE: i32 = 64; +pub const CTAP_CBOR_CRED_MGMT_PRE: i32 = 65; +pub const CTAP_PIN_PROTOCOL1: i32 = 1; +pub const CTAP_PIN_PROTOCOL2: i32 = 2; +pub const U2F_CMD_REGISTER: i32 = 1; +pub const U2F_CMD_AUTH: i32 = 2; +pub const U2F_AUTH_SIGN: i32 = 3; +pub const U2F_AUTH_CHECK: i32 = 7; +pub const CTAP_CID_BROADCAST: i64 = 4294967295; +pub const CTAP_INIT_HEADER_LEN: i32 = 7; +pub const CTAP_CONT_HEADER_LEN: i32 = 5; +pub const CTAP_MAX_REPORT_LEN: i32 = 64; +pub const CTAP_MIN_REPORT_LEN: i32 = 8; +pub const FIDO_RANDOM_DEV: &[u8; 13usize] = b"/dev/urandom\0"; +pub const FIDO_MAXMSG: i32 = 2048; +pub const FIDO_CAP_WINK: i32 = 1; +pub const FIDO_CAP_CBOR: i32 = 4; +pub const FIDO_CAP_NMSG: i32 = 8; +pub const COSE_UNSPEC: i32 = 0; +pub const COSE_ES256: i32 = -7; +pub const COSE_EDDSA: i32 = -8; +pub const COSE_ECDH_ES256: i32 = -25; +pub const COSE_ES384: i32 = -35; +pub const COSE_RS256: i32 = -257; +pub const COSE_RS1: i32 = -65535; +pub const COSE_KTY_OKP: i32 = 1; +pub const COSE_KTY_EC2: i32 = 2; +pub const COSE_KTY_RSA: i32 = 3; +pub const COSE_P256: i32 = 1; +pub const COSE_P384: i32 = 2; +pub const COSE_ED25519: i32 = 6; +pub const FIDO_EXT_HMAC_SECRET: i32 = 1; +pub const FIDO_EXT_CRED_PROTECT: i32 = 2; +pub const FIDO_EXT_LARGEBLOB_KEY: i32 = 4; +pub const FIDO_EXT_CRED_BLOB: i32 = 8; +pub const FIDO_EXT_MINPINLEN: i32 = 16; +pub const FIDO_CRED_PROT_UV_OPTIONAL: i32 = 1; +pub const FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID: i32 = 2; +pub const FIDO_CRED_PROT_UV_REQUIRED: i32 = 3; +pub const FIDO_UV_MODE_TUP: i32 = 1; +pub const FIDO_UV_MODE_FP: i32 = 2; +pub const FIDO_UV_MODE_PIN: i32 = 4; +pub const FIDO_UV_MODE_VOICE: i32 = 8; +pub const FIDO_UV_MODE_FACE: i32 = 16; +pub const FIDO_UV_MODE_LOCATION: i32 = 32; +pub const FIDO_UV_MODE_EYE: i32 = 64; +pub const FIDO_UV_MODE_DRAWN: i32 = 128; +pub const FIDO_UV_MODE_HAND: i32 = 256; +pub const FIDO_UV_MODE_NONE: i32 = 512; +pub const FIDO_UV_MODE_ALL: i32 = 1024; +pub const FIDO_UV_MODE_EXT_PIN: i32 = 2048; +pub const FIDO_UV_MODE_EXT_DRAWN: i32 = 4096; +pub const FIDO_DEBUG: i32 = 1; +pub const FIDO_DISABLE_U2F_FALLBACK: i32 = 2; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct evp_pkey_st { + _unused: [u8; 0], +} +pub type EVP_PKEY = evp_pkey_st; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct rsa_st { + _unused: [u8; 0], +} +pub type RSA = rsa_st; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ec_key_st { + _unused: [u8; 0], +} +pub type EC_KEY = ec_key_st; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_dev { + _unused: [u8; 0], +} +pub type fido_dev_io_open_t = ::std::option::Option< + unsafe extern "C" fn(arg1: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_void, +>; +pub type fido_dev_io_close_t = + ::std::option::Option; +pub type fido_dev_io_read_t = ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: *mut ::std::os::raw::c_uchar, + arg3: usize, + arg4: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, +>; +pub type fido_dev_io_write_t = ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut ::std::os::raw::c_void, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int, +>; +pub type fido_dev_rx_t = ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut fido_dev, + arg2: u8, + arg3: *mut ::std::os::raw::c_uchar, + arg4: usize, + arg5: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, +>; +pub type fido_dev_tx_t = ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut fido_dev, + arg2: u8, + arg3: *const ::std::os::raw::c_uchar, + arg4: usize, + ) -> ::std::os::raw::c_int, +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_dev_io { + pub open: fido_dev_io_open_t, + pub close: fido_dev_io_close_t, + pub read: fido_dev_io_read_t, + pub write: fido_dev_io_write_t, +} +#[test] +fn bindgen_test_layout_fido_dev_io() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 32usize, + concat!("Size of: ", stringify!(fido_dev_io)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(fido_dev_io)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).open) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(fido_dev_io), + "::", + stringify!(open) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).close) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(fido_dev_io), + "::", + stringify!(close) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).read) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(fido_dev_io), + "::", + stringify!(read) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).write) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(fido_dev_io), + "::", + stringify!(write) + ) + ); +} +pub type fido_dev_io_t = fido_dev_io; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_dev_transport { + pub rx: fido_dev_rx_t, + pub tx: fido_dev_tx_t, +} +#[test] +fn bindgen_test_layout_fido_dev_transport() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(fido_dev_transport)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(fido_dev_transport)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rx) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(fido_dev_transport), + "::", + stringify!(rx) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).tx) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(fido_dev_transport), + "::", + stringify!(tx) + ) + ); +} +pub type fido_dev_transport_t = fido_dev_transport; +pub const fido_opt_t_FIDO_OPT_OMIT: fido_opt_t = 0; +pub const fido_opt_t_FIDO_OPT_FALSE: fido_opt_t = 1; +pub const fido_opt_t_FIDO_OPT_TRUE: fido_opt_t = 2; +pub type fido_opt_t = ::std::os::raw::c_int; +pub type fido_log_handler_t = + ::std::option::Option; +pub type fido_sigset_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_assert { + _unused: [u8; 0], +} +pub type fido_assert_t = fido_assert; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_cbor_info { + _unused: [u8; 0], +} +pub type fido_cbor_info_t = fido_cbor_info; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_cred { + _unused: [u8; 0], +} +pub type fido_cred_t = fido_cred; +pub type fido_dev_t = fido_dev; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fido_dev_info { + _unused: [u8; 0], +} +pub type fido_dev_info_t = fido_dev_info; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct es256_pk { + _unused: [u8; 0], +} +pub type es256_pk_t = es256_pk; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct es384_pk { + _unused: [u8; 0], +} +pub type es384_pk_t = es384_pk; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct rs256_pk { + _unused: [u8; 0], +} +pub type rs256_pk_t = rs256_pk; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct eddsa_pk { + _unused: [u8; 0], +} +pub type eddsa_pk_t = eddsa_pk; +extern "C" { + pub fn fido_strerr(arg1: ::std::os::raw::c_int) -> *const ::std::os::raw::c_char; + pub fn fido_assert_new() -> *mut fido_assert_t; + pub fn fido_cred_new() -> *mut fido_cred_t; + pub fn fido_dev_new() -> *mut fido_dev_t; + pub fn fido_dev_new_with_info(arg1: *const fido_dev_info_t) -> *mut fido_dev_t; + pub fn fido_dev_info_new(arg1: usize) -> *mut fido_dev_info_t; + pub fn fido_cbor_info_new() -> *mut fido_cbor_info_t; + pub fn fido_dev_io_handle(arg1: *const fido_dev_t) -> *mut ::std::os::raw::c_void; + pub fn fido_assert_free(arg1: *mut *mut fido_assert_t); + pub fn fido_cbor_info_free(arg1: *mut *mut fido_cbor_info_t); + pub fn fido_cred_free(arg1: *mut *mut fido_cred_t); + pub fn fido_dev_force_fido2(arg1: *mut fido_dev_t); + pub fn fido_dev_force_u2f(arg1: *mut fido_dev_t); + pub fn fido_dev_free(arg1: *mut *mut fido_dev_t); + pub fn fido_dev_info_free(arg1: *mut *mut fido_dev_info_t, arg2: usize); + pub fn fido_init(arg1: ::std::os::raw::c_int); + pub fn fido_set_log_handler(arg1: fido_log_handler_t); + pub fn fido_assert_authdata_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_clientdata_hash_ptr( + arg1: *const fido_assert_t, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_hmac_secret_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_id_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_largeblob_key_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_sig_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_user_id_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_blob_ptr( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_cbor_info_certs_name_ptr( + arg1: *const fido_cbor_info_t, + ) -> *mut *mut ::std::os::raw::c_char; + pub fn fido_cbor_info_extensions_ptr( + arg1: *const fido_cbor_info_t, + ) -> *mut *mut ::std::os::raw::c_char; + pub fn fido_cbor_info_options_name_ptr( + arg1: *const fido_cbor_info_t, + ) -> *mut *mut ::std::os::raw::c_char; + pub fn fido_cbor_info_transports_ptr( + arg1: *const fido_cbor_info_t, + ) -> *mut *mut ::std::os::raw::c_char; + pub fn fido_cbor_info_versions_ptr( + arg1: *const fido_cbor_info_t, + ) -> *mut *mut ::std::os::raw::c_char; + pub fn fido_cbor_info_options_value_ptr(arg1: *const fido_cbor_info_t) -> *const bool; + pub fn fido_assert_rp_id(arg1: *const fido_assert_t) -> *const ::std::os::raw::c_char; + pub fn fido_assert_user_display_name( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_char; + pub fn fido_assert_user_icon( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_char; + pub fn fido_assert_user_name( + arg1: *const fido_assert_t, + arg2: usize, + ) -> *const ::std::os::raw::c_char; + pub fn fido_cbor_info_algorithm_type( + arg1: *const fido_cbor_info_t, + arg2: usize, + ) -> *const ::std::os::raw::c_char; + pub fn fido_cred_display_name(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_char; + pub fn fido_cred_fmt(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_char; + pub fn fido_cred_rp_id(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_char; + pub fn fido_cred_rp_name(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_char; + pub fn fido_cred_user_name(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_char; + pub fn fido_dev_info_manufacturer_string( + arg1: *const fido_dev_info_t, + ) -> *const ::std::os::raw::c_char; + pub fn fido_dev_info_path(arg1: *const fido_dev_info_t) -> *const ::std::os::raw::c_char; + pub fn fido_dev_info_product_string( + arg1: *const fido_dev_info_t, + ) -> *const ::std::os::raw::c_char; + pub fn fido_dev_info_ptr(arg1: *const fido_dev_info_t, arg2: usize) -> *const fido_dev_info_t; + pub fn fido_cbor_info_protocols_ptr(arg1: *const fido_cbor_info_t) -> *const u8; + pub fn fido_cbor_info_certs_value_ptr(arg1: *const fido_cbor_info_t) -> *const u64; + pub fn fido_cbor_info_aaguid_ptr( + arg1: *const fido_cbor_info_t, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_aaguid_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_attstmt_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_authdata_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_authdata_raw_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_clientdata_hash_ptr( + arg1: *const fido_cred_t, + ) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_id_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_largeblob_key_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_pubkey_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_sig_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_user_id_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_cred_x5c_ptr(arg1: *const fido_cred_t) -> *const ::std::os::raw::c_uchar; + pub fn fido_assert_allow_cred( + arg1: *mut fido_assert_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_authdata( + arg1: *mut fido_assert_t, + arg2: usize, + arg3: *const ::std::os::raw::c_uchar, + arg4: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_authdata_raw( + arg1: *mut fido_assert_t, + arg2: usize, + arg3: *const ::std::os::raw::c_uchar, + arg4: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_clientdata( + arg1: *mut fido_assert_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_clientdata_hash( + arg1: *mut fido_assert_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_count(arg1: *mut fido_assert_t, arg2: usize) -> ::std::os::raw::c_int; + pub fn fido_assert_set_extensions( + arg1: *mut fido_assert_t, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_hmac_salt( + arg1: *mut fido_assert_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_hmac_secret( + arg1: *mut fido_assert_t, + arg2: usize, + arg3: *const ::std::os::raw::c_uchar, + arg4: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_options( + arg1: *mut fido_assert_t, + arg2: bool, + arg3: bool, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_rp( + arg1: *mut fido_assert_t, + arg2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_set_up(arg1: *mut fido_assert_t, arg2: fido_opt_t) -> ::std::os::raw::c_int; + pub fn fido_assert_set_uv(arg1: *mut fido_assert_t, arg2: fido_opt_t) -> ::std::os::raw::c_int; + pub fn fido_assert_set_sig( + arg1: *mut fido_assert_t, + arg2: usize, + arg3: *const ::std::os::raw::c_uchar, + arg4: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_verify( + arg1: *const fido_assert_t, + arg2: usize, + arg3: ::std::os::raw::c_int, + arg4: *const ::std::os::raw::c_void, + ) -> ::std::os::raw::c_int; + pub fn fido_cbor_info_algorithm_cose( + arg1: *const fido_cbor_info_t, + arg2: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_exclude( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_prot(arg1: *const fido_cred_t) -> ::std::os::raw::c_int; + pub fn fido_cred_set_attstmt( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_authdata( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_authdata_raw( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_blob( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_clientdata( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_clientdata_hash( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_extensions( + arg1: *mut fido_cred_t, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_fmt( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_id( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_options( + arg1: *mut fido_cred_t, + arg2: bool, + arg3: bool, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_pin_minlen(arg1: *mut fido_cred_t, arg2: usize) -> ::std::os::raw::c_int; + pub fn fido_cred_set_prot( + arg1: *mut fido_cred_t, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_rk(arg1: *mut fido_cred_t, arg2: fido_opt_t) -> ::std::os::raw::c_int; + pub fn fido_cred_set_rp( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_char, + arg3: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_sig( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_type( + arg1: *mut fido_cred_t, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_uv(arg1: *mut fido_cred_t, arg2: fido_opt_t) -> ::std::os::raw::c_int; + pub fn fido_cred_type(arg1: *const fido_cred_t) -> ::std::os::raw::c_int; + pub fn fido_cred_set_user( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + arg4: *const ::std::os::raw::c_char, + arg5: *const ::std::os::raw::c_char, + arg6: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_set_x509( + arg1: *mut fido_cred_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn fido_cred_verify(arg1: *const fido_cred_t) -> ::std::os::raw::c_int; + pub fn fido_cred_verify_self(arg1: *const fido_cred_t) -> ::std::os::raw::c_int; + pub fn fido_dev_set_sigmask( + arg1: *mut fido_dev_t, + arg2: *const fido_sigset_t, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_cancel(arg1: *mut fido_dev_t) -> ::std::os::raw::c_int; + pub fn fido_dev_close(arg1: *mut fido_dev_t) -> ::std::os::raw::c_int; + pub fn fido_dev_get_assert( + arg1: *mut fido_dev_t, + arg2: *mut fido_assert_t, + arg3: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_get_cbor_info( + arg1: *mut fido_dev_t, + arg2: *mut fido_cbor_info_t, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_get_retry_count( + arg1: *mut fido_dev_t, + arg2: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_get_uv_retry_count( + arg1: *mut fido_dev_t, + arg2: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_get_touch_begin(arg1: *mut fido_dev_t) -> ::std::os::raw::c_int; + pub fn fido_dev_get_touch_status( + arg1: *mut fido_dev_t, + arg2: *mut ::std::os::raw::c_int, + arg3: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_info_manifest( + arg1: *mut fido_dev_info_t, + arg2: usize, + arg3: *mut usize, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_info_set( + arg1: *mut fido_dev_info_t, + arg2: usize, + arg3: *const ::std::os::raw::c_char, + arg4: *const ::std::os::raw::c_char, + arg5: *const ::std::os::raw::c_char, + arg6: *const fido_dev_io_t, + arg7: *const fido_dev_transport_t, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_make_cred( + arg1: *mut fido_dev_t, + arg2: *mut fido_cred_t, + arg3: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_open_with_info(arg1: *mut fido_dev_t) -> ::std::os::raw::c_int; + pub fn fido_dev_open( + arg1: *mut fido_dev_t, + arg2: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_reset(arg1: *mut fido_dev_t) -> ::std::os::raw::c_int; + pub fn fido_dev_set_io_functions( + arg1: *mut fido_dev_t, + arg2: *const fido_dev_io_t, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_set_pin( + arg1: *mut fido_dev_t, + arg2: *const ::std::os::raw::c_char, + arg3: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_set_transport_functions( + arg1: *mut fido_dev_t, + arg2: *const fido_dev_transport_t, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_set_timeout( + arg1: *mut fido_dev_t, + arg2: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; + pub fn fido_assert_authdata_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_assert_clientdata_hash_len(arg1: *const fido_assert_t) -> usize; + pub fn fido_assert_count(arg1: *const fido_assert_t) -> usize; + pub fn fido_assert_hmac_secret_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_assert_id_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_assert_largeblob_key_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_assert_sig_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_assert_user_id_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_assert_blob_len(arg1: *const fido_assert_t, arg2: usize) -> usize; + pub fn fido_cbor_info_aaguid_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_algorithm_count(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_certs_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_extensions_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_options_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_protocols_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_transports_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cbor_info_versions_len(arg1: *const fido_cbor_info_t) -> usize; + pub fn fido_cred_aaguid_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_attstmt_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_authdata_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_authdata_raw_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_clientdata_hash_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_id_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_largeblob_key_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_pin_minlen(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_pubkey_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_sig_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_user_id_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_cred_x5c_len(arg1: *const fido_cred_t) -> usize; + pub fn fido_assert_flags(arg1: *const fido_assert_t, arg2: usize) -> u8; + pub fn fido_assert_sigcount(arg1: *const fido_assert_t, arg2: usize) -> u32; + pub fn fido_cred_flags(arg1: *const fido_cred_t) -> u8; + pub fn fido_cred_sigcount(arg1: *const fido_cred_t) -> u32; + pub fn fido_dev_protocol(arg1: *const fido_dev_t) -> u8; + pub fn fido_dev_major(arg1: *const fido_dev_t) -> u8; + pub fn fido_dev_minor(arg1: *const fido_dev_t) -> u8; + pub fn fido_dev_build(arg1: *const fido_dev_t) -> u8; + pub fn fido_dev_flags(arg1: *const fido_dev_t) -> u8; + pub fn fido_dev_info_vendor(arg1: *const fido_dev_info_t) -> i16; + pub fn fido_dev_info_product(arg1: *const fido_dev_info_t) -> i16; + pub fn fido_cbor_info_fwversion(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_maxcredbloblen(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_maxcredcntlst(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_maxcredidlen(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_maxlargeblob(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_maxmsgsiz(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_maxrpid_minpinlen(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_minpinlen(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_uv_attempts(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_uv_modality(arg1: *const fido_cbor_info_t) -> u64; + pub fn fido_cbor_info_rk_remaining(arg1: *const fido_cbor_info_t) -> i64; + pub fn fido_dev_has_pin(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_has_uv(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_is_fido2(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_is_winhello(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_supports_credman(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_supports_cred_prot(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_supports_permissions(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_supports_pin(arg1: *const fido_dev_t) -> bool; + pub fn fido_dev_supports_uv(arg1: *const fido_dev_t) -> bool; + pub fn fido_cbor_info_new_pin_required(arg1: *const fido_cbor_info_t) -> bool; + pub fn fido_dev_largeblob_get( + arg1: *mut fido_dev_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + arg4: *mut *mut ::std::os::raw::c_uchar, + arg5: *mut usize, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_largeblob_set( + arg1: *mut fido_dev_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + arg4: *const ::std::os::raw::c_uchar, + arg5: usize, + arg6: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_largeblob_remove( + arg1: *mut fido_dev_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + arg4: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_largeblob_get_array( + arg1: *mut fido_dev_t, + arg2: *mut *mut ::std::os::raw::c_uchar, + arg3: *mut usize, + ) -> ::std::os::raw::c_int; + pub fn fido_dev_largeblob_set_array( + arg1: *mut fido_dev_t, + arg2: *const ::std::os::raw::c_uchar, + arg3: usize, + arg4: *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; + pub fn rs256_pk_new() -> *mut rs256_pk_t; + pub fn rs256_pk_free(arg1: *mut *mut rs256_pk_t); + pub fn rs256_pk_to_EVP_PKEY(arg1: *const rs256_pk_t) -> *mut EVP_PKEY; + pub fn rs256_pk_from_EVP_PKEY( + arg1: *mut rs256_pk_t, + arg2: *const EVP_PKEY, + ) -> ::std::os::raw::c_int; + pub fn rs256_pk_from_RSA(arg1: *mut rs256_pk_t, arg2: *const RSA) -> ::std::os::raw::c_int; + pub fn rs256_pk_from_ptr( + arg1: *mut rs256_pk_t, + arg2: *const ::std::os::raw::c_void, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn es256_pk_new() -> *mut es256_pk_t; + pub fn es256_pk_free(arg1: *mut *mut es256_pk_t); + pub fn es256_pk_to_EVP_PKEY(arg1: *const es256_pk_t) -> *mut EVP_PKEY; + pub fn es256_pk_from_EC_KEY( + arg1: *mut es256_pk_t, + arg2: *const EC_KEY, + ) -> ::std::os::raw::c_int; + pub fn es256_pk_from_EVP_PKEY( + arg1: *mut es256_pk_t, + arg2: *const EVP_PKEY, + ) -> ::std::os::raw::c_int; + pub fn es256_pk_from_ptr( + arg1: *mut es256_pk_t, + arg2: *const ::std::os::raw::c_void, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn es384_pk_new() -> *mut es384_pk_t; + pub fn es384_pk_free(arg1: *mut *mut es384_pk_t); + pub fn es384_pk_to_EVP_PKEY(arg1: *const es384_pk_t) -> *mut EVP_PKEY; + pub fn es384_pk_from_EC_KEY( + arg1: *mut es384_pk_t, + arg2: *const EC_KEY, + ) -> ::std::os::raw::c_int; + pub fn es384_pk_from_EVP_PKEY( + arg1: *mut es384_pk_t, + arg2: *const EVP_PKEY, + ) -> ::std::os::raw::c_int; + pub fn es384_pk_from_ptr( + arg1: *mut es384_pk_t, + arg2: *const ::std::os::raw::c_void, + arg3: usize, + ) -> ::std::os::raw::c_int; + pub fn eddsa_pk_new() -> *mut eddsa_pk_t; + pub fn eddsa_pk_free(arg1: *mut *mut eddsa_pk_t); + pub fn eddsa_pk_to_EVP_PKEY(arg1: *const eddsa_pk_t) -> *mut EVP_PKEY; + pub fn eddsa_pk_from_EVP_PKEY( + arg1: *mut eddsa_pk_t, + arg2: *const EVP_PKEY, + ) -> ::std::os::raw::c_int; + pub fn eddsa_pk_from_ptr( + arg1: *mut eddsa_pk_t, + arg2: *const ::std::os::raw::c_void, + arg3: usize, + ) -> ::std::os::raw::c_int; +} diff --git a/fido-rs/libfido2-sys/src/lib.rs b/fido-rs/libfido2-sys/src/lib.rs new file mode 100644 index 0000000..f5865ab --- /dev/null +++ b/fido-rs/libfido2-sys/src/lib.rs @@ -0,0 +1,4 @@ +#[allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] +mod ffi; + +pub use ffi::*; diff --git a/src/main.rs b/src/main.rs index f036819..1d7ff0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use std::{ fmt::Display, fs::Permissions, - io::Write, os::unix::prelude::PermissionsExt, path::{Path, PathBuf}, process, @@ -10,12 +9,13 @@ use std::{ use anyhow::anyhow; use bitflags::bitflags; use clap::{Parser, ValueEnum}; -use ctap_hid_fido2::{ - fidokey::{CredentialSupportedKeyType, MakeCredentialArgsBuilder}, - get_fidokey_devices, verifier, FidoKeyHid, LibCfg, -}; use dialoguer::{console, Confirm, Password, Select}; +use fido2_rs::{ + credentials::{CoseType, Credential, Opt}, + device::DeviceList, +}; use gethostname::gethostname; +use rand::RngCore; use ssh_encoding::{Decode, Encode, LineEnding}; use ssh_key::{private, PrivateKey}; @@ -57,11 +57,11 @@ enum KeyTypeArg { EcdsaSk, } -impl From for CredentialSupportedKeyType { +impl From for CoseType { fn from(value: KeyTypeArg) -> Self { match value { - KeyTypeArg::Ed25519Sk => Self::Ed25519, - KeyTypeArg::EcdsaSk => Self::Ecdsa256, + KeyTypeArg::Ed25519Sk => Self::EDDSA, + KeyTypeArg::EcdsaSk => Self::ES256, } } } @@ -104,72 +104,88 @@ fn main() -> anyhow::Result<()> { } } - let challenge = verifier::create_challenge(); + let mut challenge = [0; 32]; + rand::thread_rng().fill_bytes(&mut challenge); - let devices = get_fidokey_devices(); + // Must be leaked to prevent DeviceList from prematurely freeing memory on drop and allowing + // references to freed memory. + // Note: Report unsoundness bug + let devices_iter = Box::leak(Box::new(DeviceList::list_devices(12))); + let devices = devices_iter.collect::>(); let device = if devices.len() > 1 { - let idx = Select::new() + Select::new() .with_prompt("Select FIDO2 key") .items( devices .iter() - .map(|dev| format!("{}", dev.product_string)) + .map(|dev| { + let product = dev.product.to_string_lossy(); + if product.is_empty() { + "Unknown".to_string() + } else { + format!("{}", dev.product.to_string_lossy()) + } + }) .collect::>() .as_slice(), ) .default(0) - .interact()?; - &devices[idx] + .interact()? } else { - &devices[0] + 0 }; + let device = &devices[device].open()?; - let mut libcfg = LibCfg::init(); - libcfg.keep_alive_msg = "Touch the authenticator now.".into(); - let device = FidoKeyHid::new(&[device.param.clone()], &libcfg)?; - let device_has_pin = device.get_info().map_or(false, |info| { - info.options.contains(&("clientPin".into(), true)) + let device_has_pin = device.info().map_or(false, |info| { + info.options().get("clientPin").copied().unwrap_or(false) }); let pin = if device_has_pin { - Password::new() - .with_prompt("FIDO2 PIN") - .report(false) - .interact()? + Some(Password::new().with_prompt("FIDO2 PIN").interact()?) } else { - String::new() - }; - let make_credential_args = if device_has_pin { - MakeCredentialArgsBuilder::new("ssh:", &challenge) - .pin(&pin) - .key_type(args.key_type.into()) - .build() - } else { - MakeCredentialArgsBuilder::new("ssh:", &challenge) - .without_pin_and_uv() - .key_type(args.key_type.into()) - .build() + None }; - let attestation = device.make_credential_with_args(&make_credential_args)?; - let verify_result = verifier::verify_attestation("ssh:", &challenge, &attestation); - if !verify_result.is_success { - return Err(anyhow!("Failed to verify attestation")); + let mut credential = Credential::new(); + credential.set_client_data(&challenge)?; + credential.set_cose_type(args.key_type.into())?; + credential.set_rk(Opt::Omit)?; + credential.set_rp("ssh:", "")?; + credential.set_user([0; 32], "openssh", Some("openssh"), None)?; + + if args.user_verify { + credential.set_protection(fido2_rs::credentials::Protection::UvRequired)?; } + println!("Touch your authenticator now."); + device.make_credential(&mut credential, pin.as_deref())?; + + credential.verify()?; + let mut privkey_bytes = Vec::new(); if args.key_type == KeyTypeArg::EcdsaSk { "nistp256".encode(&mut privkey_bytes)?; } - verify_result - .credential_public_key - .der - .encode(&mut privkey_bytes)?; + + match args.key_type { + KeyTypeArg::Ed25519Sk => { + credential.public_key().encode(&mut privkey_bytes)?; + } + KeyTypeArg::EcdsaSk => { + p256::EncodedPoint::from_affine_coordinates( + (&credential.public_key()[0..32]).into(), + (&credential.public_key()[32..64]).into(), + false, + ) + .to_bytes() + .encode(&mut privkey_bytes)?; + } + } "ssh:".encode(&mut privkey_bytes)?; let mut flags = SshSkFlags::UserPresenceRequired; flags.set(SshSkFlags::UserVerificationRequired, args.user_verify); privkey_bytes.push(flags.bits()); - verify_result.credential_id.encode(&mut privkey_bytes)?; + credential.id().encode(&mut privkey_bytes)?; "".encode(&mut privkey_bytes)?; let comment = if let Some(comment) = args.comment { comment @@ -191,14 +207,6 @@ fn main() -> anyhow::Result<()> { )?, }; - let mut ssh_attest = Vec::new(); - "ssh-sk-attest-v01".encode(&mut ssh_attest)?; - attestation.attstmt_x5c[0].encode(&mut ssh_attest)?; - attestation.attstmt_sig.encode(&mut ssh_attest)?; - attestation.auth_data.encode(&mut ssh_attest)?; - 0u32.encode(&mut ssh_attest)?; - "".encode(&mut ssh_attest)?; - std::fs::write(&privkey_path, &*privkey.to_openssh(LineEnding::default())?)?; std::fs::set_permissions(&privkey_path, Permissions::from_mode(0o600))?; println!( @@ -211,6 +219,15 @@ fn main() -> anyhow::Result<()> { pubkey_path.to_string_lossy() ); if args.write_attestation { + let mut ssh_attest = Vec::new(); + dbg!(credential.auth_data()); + "ssh-sk-attest-v01".encode(&mut ssh_attest)?; + credential.certificate().encode(&mut ssh_attest)?; + credential.signature().encode(&mut ssh_attest)?; + credential.auth_data().encode(&mut ssh_attest)?; + 0u32.encode(&mut ssh_attest)?; + "".encode(&mut ssh_attest)?; + std::fs::write(&attest_info_path, &ssh_attest)?; println!( "Your FIDO attestation certificate has been saved in {}",