Switch to libfido2 bindings

This commit is contained in:
pjht 2023-08-28 09:14:24 -05:00
parent 6680f4c274
commit 129e3187be
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
25 changed files with 3117 additions and 478 deletions

515
Cargo.lock generated
View File

@ -2,17 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "anstream" name = "anstream"
version = "0.3.2" version = "0.3.2"
@ -68,45 +57,6 @@ version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" 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]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -119,12 +69,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.6.0" version = "1.6.0"
@ -152,15 +96,6 @@ dependencies = [
"generic-array", "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]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.13.0" version = "3.13.0"
@ -173,15 +108,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.82" version = "1.0.82"
@ -239,7 +165,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.28", "syn",
] ]
[[package]] [[package]]
@ -304,29 +230,6 @@ dependencies = [
"typenum", "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]] [[package]]
name = "ctrlc" name = "ctrlc"
version = "3.4.0" version = "3.4.0"
@ -361,15 +264,9 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "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]] [[package]]
name = "der" name = "der"
version = "0.7.8" version = "0.7.8"
@ -377,29 +274,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [ dependencies = [
"const-oid", "const-oid",
"pem-rfc7468",
"zeroize", "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]] [[package]]
name = "dialoguer" name = "dialoguer"
version = "0.10.4" version = "0.10.4"
@ -424,17 +302,6 @@ dependencies = [
"subtle", "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]] [[package]]
name = "ecdsa" name = "ecdsa"
version = "0.16.8" version = "0.16.8"
@ -481,6 +348,7 @@ dependencies = [
"ff", "ff",
"generic-array", "generic-array",
"group", "group",
"pem-rfc7468",
"pkcs8", "pkcs8",
"rand_core", "rand_core",
"sec1", "sec1",
@ -537,6 +405,17 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77"
[[package]]
name = "fido2-rs"
version = "0.1.0"
dependencies = [
"bitflags 1.3.2",
"foreign-types",
"libfido2-sys",
"openssl",
"thiserror",
]
[[package]] [[package]]
name = "fido_ssh_maker" name = "fido_ssh_maker"
version = "0.1.0" version = "0.1.0"
@ -544,15 +423,32 @@ dependencies = [
"anyhow", "anyhow",
"bitflags 2.4.0", "bitflags 2.4.0",
"clap", "clap",
"ctap-hid-fido2",
"ctrlc", "ctrlc",
"dialoguer", "dialoguer",
"fido2-rs",
"gethostname", "gethostname",
"p256",
"rand",
"ssh-encoding", "ssh-encoding",
"ssh-key", "ssh-key",
"whoami", "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]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@ -596,12 +492,6 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
@ -614,24 +504,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" 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]] [[package]]
name = "hmac" name = "hmac"
version = "0.12.1" version = "0.12.1"
@ -647,7 +519,6 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [ dependencies = [
"block-padding",
"generic-array", "generic-array",
] ]
@ -662,12 +533,6 @@ dependencies = [
"windows-sys 0.48.0", "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]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.64" version = "0.3.64"
@ -692,6 +557,16 @@ version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libfido2-sys"
version = "0.2.0"
dependencies = [
"anyhow",
"cfg-if",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "libm" name = "libm"
version = "0.2.7" version = "0.2.7"
@ -710,18 +585,6 @@ version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 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]] [[package]]
name = "nix" name = "nix"
version = "0.26.2" version = "0.26.2"
@ -734,41 +597,6 @@ dependencies = [
"static_assertions", "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]] [[package]]
name = "num-bigint-dig" name = "num-bigint-dig"
version = "0.8.4" version = "0.8.4"
@ -786,15 +614,6 @@ dependencies = [
"zeroize", "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]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.45" version = "0.1.45"
@ -816,18 +635,6 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.16" version = "0.2.16"
@ -838,21 +645,50 @@ dependencies = [
"libm", "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]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.18.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 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]] [[package]]
name = "p256" name = "p256"
version = "0.13.2" version = "0.13.2"
@ -877,15 +713,6 @@ dependencies = [
"sha2", "sha2",
] ]
[[package]]
name = "pad"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "pem-rfc7468" name = "pem-rfc7468"
version = "0.7.0" version = "0.7.0"
@ -967,6 +794,7 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [ dependencies = [
"libc",
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
] ]
@ -1009,21 +837,6 @@ dependencies = [
"subtle", "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]] [[package]]
name = "rsa" name = "rsa"
version = "0.9.2" version = "0.9.2"
@ -1056,15 +869,6 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.8" version = "0.38.8"
@ -1078,12 +882,6 @@ dependencies = [
"windows-sys 0.48.0", "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]] [[package]]
name = "sec1" name = "sec1"
version = "0.7.3" version = "0.7.3"
@ -1104,22 +902,6 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" 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]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.7" version = "0.10.7"
@ -1222,42 +1004,12 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 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]] [[package]]
name = "subtle" name = "subtle"
version = "2.5.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 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]] [[package]]
name = "syn" name = "syn"
version = "2.0.28" version = "2.0.28"
@ -1269,18 +1021,6 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.8.0" version = "3.8.0"
@ -1311,35 +1051,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.28", "syn",
]
[[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",
] ]
[[package]] [[package]]
@ -1360,24 +1072,18 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 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]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -1411,7 +1117,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.28", "syn",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1433,7 +1139,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.28", "syn",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1464,28 +1170,6 @@ dependencies = [
"web-sys", "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]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"
@ -1618,23 +1302,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 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]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.6.0" version = "1.6.0"

View File

@ -9,10 +9,12 @@ edition = "2021"
anyhow = "1.0.72" anyhow = "1.0.72"
bitflags = "2.4.0" bitflags = "2.4.0"
clap = { version = "4.3.21", features = ["derive"] } clap = { version = "4.3.21", features = ["derive"] }
ctap-hid-fido2 = "3.5.0"
ctrlc = "3.4.0" ctrlc = "3.4.0"
dialoguer = "0.10.4" dialoguer = "0.10.4"
fido2-rs = { version = "0.1.0", path = "fido-rs/fido2-rs" }
gethostname = "0.4.3" gethostname = "0.4.3"
p256 = "0.13.2"
rand = "0.8.5"
ssh-encoding = { version = "0.2.0" } ssh-encoding = { version = "0.2.0" }
ssh-key = { version = "0.6.0", features = ["ed25519"] } ssh-key = { version = "0.6.0", features = ["ed25519"] }
whoami = "1.4.1" whoami = "1.4.1"

3
fido-rs/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
target/
Cargo.lock

2
fido-rs/Cargo.toml Normal file
View File

@ -0,0 +1,2 @@
[workspace]
members = ["libfido2-sys", "fido2-rs"]

36
fido-rs/README.MD Normal file
View File

@ -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`

View File

@ -0,0 +1,20 @@
[package]
name = "fido2-rs"
version = "0.1.0"
authors = ["tyan boot <tyanboot@outlook.com>"]
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"

21
fido-rs/fido2-rs/LICENSE Normal file
View File

@ -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.

View File

@ -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(())
}
```

View File

@ -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<str>) -> 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<ffi::fido_assert_t>,
}
/// A single FIDO assertion.
pub struct Assertion<'a> {
ptr: NonNull<ffi::fido_assert_t>,
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<Public>) -> 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<Item = Assertion> {
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<Self::Item> {
if self.idx >= self.count {
None
} else {
let item = Assertion {
ptr: self.asserts.ptr,
idx: self.idx,
_p: PhantomData,
};
self.idx += 1;
Some(item)
}
}
}

View File

@ -0,0 +1,195 @@
use std::collections::HashMap;
use std::ffi::CStr;
use std::ptr::NonNull;
pub struct CBORInfo {
pub(crate) ptr: NonNull<ffi::fido_cbor_info_t>,
}
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());
}
}
}

View File

@ -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<ffi::fido_cred_t>);
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<Protection> {
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<AttestationFormat> {
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<str>, name: impl AsRef<str>) -> 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<str>,
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<i32> for CoseType {
type Error = i32;
fn try_from(value: i32) -> Result<Self, Self::Error> {
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;
}
}

View File

@ -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<ffi::fido_dev_info_t>,
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<Self::Item> {
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<Device> {
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<fido_dev_t>);
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<fido_dev_t>,
}
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<str>) -> Result<Device> {
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<CBORInfo> {
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<Assertions> {
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,
}

View File

@ -0,0 +1,51 @@
use std::ffi::CStr;
use std::fmt::{Debug, Display, Formatter};
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// 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 {
<Self as Display>::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 {}

View File

@ -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<Self, Self::Error> {
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<PKey<Public>> = 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<PKey<Public>> = 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<EcKey<Public>> = 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<EcKey<Public>> = ffi::es384_pk_from_EC_KEY;
fn drop = ffi::es384_pk_free;
pub struct ES384;
}

View File

@ -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;

View File

@ -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)
}
};
}

View File

@ -0,0 +1,25 @@
[package]
name = "libfido2-sys"
version = "0.2.0"
authors = ["tyan boot <tyanboot@outlook.com>"]
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"

View File

@ -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.

View File

@ -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)

View File

@ -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(())
}

View File

@ -0,0 +1,5 @@
#include <fido.h>
#include <fido/rs256.h>
#include <fido/es256.h>
#include <fido/es384.h>
#include <fido/eddsa.h>

Binary file not shown.

View File

@ -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<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>;
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<fido_dev_io> = ::std::mem::MaybeUninit::uninit();
let ptr = UNINIT.as_ptr();
assert_eq!(
::std::mem::size_of::<fido_dev_io>(),
32usize,
concat!("Size of: ", stringify!(fido_dev_io))
);
assert_eq!(
::std::mem::align_of::<fido_dev_io>(),
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<fido_dev_transport> = ::std::mem::MaybeUninit::uninit();
let ptr = UNINIT.as_ptr();
assert_eq!(
::std::mem::size_of::<fido_dev_transport>(),
16usize,
concat!("Size of: ", stringify!(fido_dev_transport))
);
assert_eq!(
::std::mem::align_of::<fido_dev_transport>(),
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<unsafe extern "C" fn(arg1: *const ::std::os::raw::c_char)>;
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;
}

View File

@ -0,0 +1,4 @@
#[allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
mod ffi;
pub use ffi::*;

View File

@ -1,7 +1,6 @@
use std::{ use std::{
fmt::Display, fmt::Display,
fs::Permissions, fs::Permissions,
io::Write,
os::unix::prelude::PermissionsExt, os::unix::prelude::PermissionsExt,
path::{Path, PathBuf}, path::{Path, PathBuf},
process, process,
@ -10,12 +9,13 @@ use std::{
use anyhow::anyhow; use anyhow::anyhow;
use bitflags::bitflags; use bitflags::bitflags;
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use ctap_hid_fido2::{
fidokey::{CredentialSupportedKeyType, MakeCredentialArgsBuilder},
get_fidokey_devices, verifier, FidoKeyHid, LibCfg,
};
use dialoguer::{console, Confirm, Password, Select}; use dialoguer::{console, Confirm, Password, Select};
use fido2_rs::{
credentials::{CoseType, Credential, Opt},
device::DeviceList,
};
use gethostname::gethostname; use gethostname::gethostname;
use rand::RngCore;
use ssh_encoding::{Decode, Encode, LineEnding}; use ssh_encoding::{Decode, Encode, LineEnding};
use ssh_key::{private, PrivateKey}; use ssh_key::{private, PrivateKey};
@ -57,11 +57,11 @@ enum KeyTypeArg {
EcdsaSk, EcdsaSk,
} }
impl From<KeyTypeArg> for CredentialSupportedKeyType { impl From<KeyTypeArg> for CoseType {
fn from(value: KeyTypeArg) -> Self { fn from(value: KeyTypeArg) -> Self {
match value { match value {
KeyTypeArg::Ed25519Sk => Self::Ed25519, KeyTypeArg::Ed25519Sk => Self::EDDSA,
KeyTypeArg::EcdsaSk => Self::Ecdsa256, 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::<Vec<_>>();
let device = if devices.len() > 1 { let device = if devices.len() > 1 {
let idx = Select::new() Select::new()
.with_prompt("Select FIDO2 key") .with_prompt("Select FIDO2 key")
.items( .items(
devices devices
.iter() .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::<Vec<_>>() .collect::<Vec<_>>()
.as_slice(), .as_slice(),
) )
.default(0) .default(0)
.interact()?; .interact()?
&devices[idx]
} else { } else {
&devices[0] 0
}; };
let device = &devices[device].open()?;
let mut libcfg = LibCfg::init(); let device_has_pin = device.info().map_or(false, |info| {
libcfg.keep_alive_msg = "Touch the authenticator now.".into(); info.options().get("clientPin").copied().unwrap_or(false)
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 pin = if device_has_pin { let pin = if device_has_pin {
Password::new() Some(Password::new().with_prompt("FIDO2 PIN").interact()?)
.with_prompt("FIDO2 PIN")
.report(false)
.interact()?
} else { } else {
String::new() None
};
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()
}; };
let attestation = device.make_credential_with_args(&make_credential_args)?; let mut credential = Credential::new();
let verify_result = verifier::verify_attestation("ssh:", &challenge, &attestation); credential.set_client_data(&challenge)?;
if !verify_result.is_success { credential.set_cose_type(args.key_type.into())?;
return Err(anyhow!("Failed to verify attestation")); 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(); let mut privkey_bytes = Vec::new();
if args.key_type == KeyTypeArg::EcdsaSk { if args.key_type == KeyTypeArg::EcdsaSk {
"nistp256".encode(&mut privkey_bytes)?; "nistp256".encode(&mut privkey_bytes)?;
} }
verify_result
.credential_public_key match args.key_type {
.der 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)?; .encode(&mut privkey_bytes)?;
}
}
"ssh:".encode(&mut privkey_bytes)?; "ssh:".encode(&mut privkey_bytes)?;
let mut flags = SshSkFlags::UserPresenceRequired; let mut flags = SshSkFlags::UserPresenceRequired;
flags.set(SshSkFlags::UserVerificationRequired, args.user_verify); flags.set(SshSkFlags::UserVerificationRequired, args.user_verify);
privkey_bytes.push(flags.bits()); privkey_bytes.push(flags.bits());
verify_result.credential_id.encode(&mut privkey_bytes)?; credential.id().encode(&mut privkey_bytes)?;
"".encode(&mut privkey_bytes)?; "".encode(&mut privkey_bytes)?;
let comment = if let Some(comment) = args.comment { let comment = if let Some(comment) = args.comment {
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::write(&privkey_path, &*privkey.to_openssh(LineEnding::default())?)?;
std::fs::set_permissions(&privkey_path, Permissions::from_mode(0o600))?; std::fs::set_permissions(&privkey_path, Permissions::from_mode(0o600))?;
println!( println!(
@ -211,6 +219,15 @@ fn main() -> anyhow::Result<()> {
pubkey_path.to_string_lossy() pubkey_path.to_string_lossy()
); );
if args.write_attestation { 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)?; std::fs::write(&attest_info_path, &ssh_attest)?;
println!( println!(
"Your FIDO attestation certificate has been saved in {}", "Your FIDO attestation certificate has been saved in {}",