keyfork-shard: add keyfork-qrcode
This commit is contained in:
parent
b8c1fc1a93
commit
2220faf865
|
@ -58,6 +58,18 @@ version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0453232ace82dee0dd0b4c87a59bd90f7b53b314f3e0f61fe2ee7c8a16482289"
|
checksum = "0453232ace82dee0dd0b4c87a59bd90f7b53b314f3e0f61fe2ee7c8a16482289"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -343,6 +355,29 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.65.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"peeking_take_while",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"syn 2.0.48",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.68.1"
|
version = "0.68.1"
|
||||||
|
@ -360,7 +395,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -453,6 +488,12 @@ version = "3.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -598,7 +639,7 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -607,6 +648,12 @@ version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -742,7 +789,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1091,7 +1138,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1130,6 +1177,34 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "g2gen"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc2c7625b2fc250dd90b63f7887a6bb0f7ec1d714c8278415bea2669ef20820e"
|
||||||
|
dependencies = [
|
||||||
|
"g2poly",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "g2p"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc36d9bdc3d2da057775a9f4fa7d7b09edab3e0eda7a92cc353358fa63b8519e"
|
||||||
|
dependencies = [
|
||||||
|
"g2gen",
|
||||||
|
"g2poly",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "g2poly"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af6a86e750338603ea2c14b1c0bfe58cd61f87ca67a0021d9334996024608e12"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
|
@ -1205,7 +1280,16 @@ version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash 0.4.8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash 0.8.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1262,6 +1346,15 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.59"
|
version = "0.1.59"
|
||||||
|
@ -1295,6 +1388,20 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -1382,6 +1489,12 @@ version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jpeg-decoder"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.66"
|
version = "0.3.66"
|
||||||
|
@ -1539,6 +1652,16 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keyfork-qrcode"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"image",
|
||||||
|
"rqrr",
|
||||||
|
"thiserror",
|
||||||
|
"v4l",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-shard"
|
name = "keyfork-shard"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1551,6 +1674,7 @@ dependencies = [
|
||||||
"keyfork-derive-openpgp",
|
"keyfork-derive-openpgp",
|
||||||
"keyfork-mnemonic-util",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-prompt",
|
"keyfork-prompt",
|
||||||
|
"keyfork-qrcode",
|
||||||
"openpgp-card",
|
"openpgp-card",
|
||||||
"openpgp-card-sequoia",
|
"openpgp-card-sequoia",
|
||||||
"sequoia-openpgp",
|
"sequoia-openpgp",
|
||||||
|
@ -1735,6 +1859,15 @@ dependencies = [
|
||||||
"value-bag",
|
"value-bag",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.13.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1802,7 +1935,7 @@ version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b495053a10a19a80e3a26bf1212e92e29350797b5f5bdc58268c3f3f818e66ec"
|
checksum = "b495053a10a19a80e3a26bf1212e92e29350797b5f5bdc58268c3f3f818e66ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen 0.68.1",
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
|
@ -1874,6 +2007,17 @@ 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-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -2060,7 +2204,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2184,6 +2328,16 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.76"
|
version = "1.0.76"
|
||||||
|
@ -2357,6 +2511,17 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rqrr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a8b87d1f9f69bb1a6c77e20fd303f9617b2b68dcff87cd9bcbfff2ced4b8a0b"
|
||||||
|
dependencies = [
|
||||||
|
"g2p",
|
||||||
|
"image",
|
||||||
|
"lru",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
@ -2511,7 +2676,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2547,7 +2712,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2739,6 +2904,17 @@ 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.48"
|
version = "2.0.48"
|
||||||
|
@ -2811,7 +2987,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2876,7 +3052,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2927,7 +3103,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3034,6 +3210,26 @@ 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 = "v4l"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8fbfea44a46799d62c55323f3c55d06df722fbe577851d848d328a1041c3403"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"libc",
|
||||||
|
"v4l2-sys-mit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "v4l2-sys-mit"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen 0.65.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -3091,7 +3287,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3125,7 +3321,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -3146,6 +3342,18 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "4.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"once_cell",
|
||||||
|
"rustix 0.38.28",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -3336,6 +3544,26 @@ dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -3353,5 +3581,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
|
@ -15,8 +15,14 @@ members = [
|
||||||
"keyfork-plumbing",
|
"keyfork-plumbing",
|
||||||
"keyfork-shard",
|
"keyfork-shard",
|
||||||
"keyfork-slip10-test-data",
|
"keyfork-slip10-test-data",
|
||||||
|
"keyfork-qrcode",
|
||||||
"keyforkd",
|
"keyforkd",
|
||||||
"keyforkd-client",
|
"keyforkd-client",
|
||||||
"keyforkd-models",
|
"keyforkd-models",
|
||||||
"smex",
|
"smex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.dev.package.keyfork-qrcode]
|
||||||
|
opt-level = 3
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,8 @@ license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["mnemonic", "qrencode"]
|
default = ["mnemonic"]
|
||||||
mnemonic = ["keyfork-mnemonic-util"]
|
mnemonic = ["keyfork-mnemonic-util"]
|
||||||
qrencode = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyfork-crossterm = { version = "0.27.1", path = "../keyfork-crossterm", default-features = false, features = ["use-dev-tty", "events", "bracketed-paste"] }
|
keyfork-crossterm = { version = "0.27.1", path = "../keyfork-crossterm", default-features = false, features = ["use-dev-tty", "events", "bracketed-paste"] }
|
||||||
|
|
|
@ -7,9 +7,6 @@ pub mod terminal;
|
||||||
pub mod validators;
|
pub mod validators;
|
||||||
pub use terminal::{Terminal, DefaultTerminal, default_terminal};
|
pub use terminal::{Terminal, DefaultTerminal, default_terminal};
|
||||||
|
|
||||||
#[cfg(feature = "qrencode")]
|
|
||||||
pub mod qrencode;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("The given handler is not a TTY")]
|
#[error("The given handler is not a TTY")]
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
use std::{
|
|
||||||
io::Write,
|
|
||||||
process::{Command, Stdio},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum QrGenerationError {
|
|
||||||
#[error("{0}")]
|
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
|
|
||||||
#[error("{0}")]
|
|
||||||
StringParse(#[from] std::string::FromUtf8Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a terminal-printable QR code for a given string. Uses the `qrencode` CLI utility.
|
|
||||||
pub fn qrencode(text: &str) -> Result<String, QrGenerationError> {
|
|
||||||
let mut qrencode = Command::new("qrencode")
|
|
||||||
.arg("-t")
|
|
||||||
.arg("ansiutf8")
|
|
||||||
.arg("-m")
|
|
||||||
.arg("2")
|
|
||||||
.stdin(Stdio::piped())
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.spawn()?;
|
|
||||||
if let Some(stdin) = qrencode.stdin.as_mut() {
|
|
||||||
stdin.write_all(text.as_bytes())?;
|
|
||||||
}
|
|
||||||
let output = qrencode.wait_with_output()?;
|
|
||||||
let result = String::from_utf8(output.stdout)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "keyfork-qrcode"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
image = { version = "0.24.7", default-features = false, features = ["jpeg"] }
|
||||||
|
rqrr = "0.6.0"
|
||||||
|
thiserror = "1.0.56"
|
||||||
|
v4l = "0.14.0"
|
|
@ -0,0 +1,123 @@
|
||||||
|
use image::io::Reader as ImageReader;
|
||||||
|
use rqrr::PreparedImage;
|
||||||
|
use std::{
|
||||||
|
io::{Cursor, Write},
|
||||||
|
time::{Duration, SystemTime},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
use v4l::{
|
||||||
|
buffer::Type,
|
||||||
|
io::{mmap::Stream, traits::CaptureStream},
|
||||||
|
video::Capture,
|
||||||
|
Device, FourCC,
|
||||||
|
};
|
||||||
|
|
||||||
|
static MJPEG: &[u8; 4] = b"MJPG";
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum QRGenerationError {
|
||||||
|
#[error("{0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Could not decode output of qrencode (this is a bug!): {0}")]
|
||||||
|
StringParse(#[from] std::string::FromUtf8Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum QRCodeScanError {
|
||||||
|
#[error("Camera could not use {expected} format, instead used {actual}")]
|
||||||
|
CameraGaveBadFormat {
|
||||||
|
expected: String,
|
||||||
|
actual: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Unable to interface with camera: {0}")]
|
||||||
|
CameraIO(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("Could not decode image: {0}")]
|
||||||
|
ImageDecode(#[from] image::ImageError),
|
||||||
|
|
||||||
|
#[error("Could not format FourCC as string (this is a bug!): {0}")]
|
||||||
|
FourCC(#[from] std::string::FromUtf8Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum ErrorCorrection {
|
||||||
|
#[default]
|
||||||
|
Lowest,
|
||||||
|
Medium,
|
||||||
|
Quartile,
|
||||||
|
Highest,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a terminal-printable QR code for a given string. Uses the `qrencode` CLI utility.
|
||||||
|
pub fn qrencode(
|
||||||
|
text: &str,
|
||||||
|
error_correction: impl Into<Option<ErrorCorrection>>,
|
||||||
|
) -> Result<String, QRGenerationError> {
|
||||||
|
let error_correction_arg = match error_correction.into().unwrap_or_default() {
|
||||||
|
ErrorCorrection::Lowest => "L",
|
||||||
|
ErrorCorrection::Medium => "M",
|
||||||
|
ErrorCorrection::Quartile => "Q",
|
||||||
|
ErrorCorrection::Highest => "H",
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut qrencode = Command::new("qrencode")
|
||||||
|
.arg("-t")
|
||||||
|
.arg("ansiutf8")
|
||||||
|
.arg("-m")
|
||||||
|
.arg("2")
|
||||||
|
.arg("-l")
|
||||||
|
.arg(error_correction_arg)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
if let Some(stdin) = qrencode.stdin.as_mut() {
|
||||||
|
stdin.write_all(text.as_bytes())?;
|
||||||
|
}
|
||||||
|
let output = qrencode.wait_with_output()?;
|
||||||
|
let result = String::from_utf8(output.stdout)?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QRCodeScanError> {
|
||||||
|
let device = Device::new(index)?;
|
||||||
|
|
||||||
|
let mut format = device.format()?;
|
||||||
|
format.width = 1280;
|
||||||
|
format.height = 720;
|
||||||
|
format.fourcc = FourCC::new(MJPEG);
|
||||||
|
let format = device.set_format(&format)?;
|
||||||
|
|
||||||
|
if MJPEG != &format.fourcc.repr {
|
||||||
|
return Err(QRCodeScanError::CameraGaveBadFormat {
|
||||||
|
expected: String::from_utf8(MJPEG.to_vec())?,
|
||||||
|
actual: String::from_utf8(format.fourcc.repr.to_vec())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stream = Stream::with_buffers(&device, Type::VideoCapture, 4)?;
|
||||||
|
|
||||||
|
let start = SystemTime::now();
|
||||||
|
|
||||||
|
while SystemTime::now()
|
||||||
|
.duration_since(start)
|
||||||
|
.unwrap_or(Duration::from_secs(0))
|
||||||
|
< timeout
|
||||||
|
{
|
||||||
|
let (buffer, _) = stream.next()?;
|
||||||
|
let image = ImageReader::new(Cursor::new(buffer))
|
||||||
|
.with_guessed_format()?
|
||||||
|
.decode()?
|
||||||
|
.to_luma8();
|
||||||
|
let mut image = PreparedImage::prepare(image);
|
||||||
|
for grid in image.detect_grids() {
|
||||||
|
if let Ok((_, content)) = grid.decode() {
|
||||||
|
return Ok(Some(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
|
@ -7,13 +7,14 @@ license = "AGPL-3.0-only"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["openpgp", "openpgp-card"]
|
default = ["openpgp", "openpgp-card", "qrcode"]
|
||||||
openpgp = ["sequoia-openpgp", "prompt", "anyhow"]
|
openpgp = ["sequoia-openpgp", "anyhow"]
|
||||||
openpgp-card = ["openpgp-card-sequoia", "card-backend-pcsc", "card-backend", "dep:openpgp-card"]
|
openpgp-card = ["openpgp-card-sequoia", "card-backend-pcsc", "card-backend", "dep:openpgp-card"]
|
||||||
prompt = ["keyfork-prompt"]
|
qrcode = ["keyfork-qrcode"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyfork-prompt = { version = "0.1.0", path = "../keyfork-prompt", optional = true }
|
keyfork-prompt = { version = "0.1.0", path = "../keyfork-prompt", default-features = false, features = ["mnemonic"] }
|
||||||
|
keyfork-qrcode = { version = "0.1.0", path = "../keyfork-qrcode", optional = true }
|
||||||
smex = { version = "0.1.0", path = "../smex" }
|
smex = { version = "0.1.0", path = "../smex" }
|
||||||
|
|
||||||
sharks = "0.5.0"
|
sharks = "0.5.0"
|
||||||
|
|
|
@ -7,9 +7,8 @@ use aes_gcm::{
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use keyfork_mnemonic_util::{Mnemonic, Wordlist};
|
use keyfork_mnemonic_util::{Mnemonic, Wordlist};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
qrencode,
|
|
||||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||||
Message as PromptMessage, Terminal, PromptHandler
|
Message as PromptMessage, PromptHandler, Terminal,
|
||||||
};
|
};
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use sharks::{Share, Sharks};
|
use sharks::{Share, Sharks};
|
||||||
|
@ -28,8 +27,8 @@ pub enum SharksError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("Mnemonic did not store enough data")]
|
#[error("Mnemonic or QR code did not store enough data")]
|
||||||
pub struct InvalidMnemonicData;
|
pub struct InvalidData;
|
||||||
|
|
||||||
/// Decrypt hunk version 1:
|
/// Decrypt hunk version 1:
|
||||||
/// 1 byte: Version
|
/// 1 byte: Version
|
||||||
|
@ -58,36 +57,66 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
||||||
let our_key = EphemeralSecret::random();
|
let our_key = EphemeralSecret::random();
|
||||||
let key_mnemonic =
|
let key_mnemonic =
|
||||||
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
|
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
|
||||||
let combined_mnemonic = format!("{nonce_mnemonic} {key_mnemonic}");
|
|
||||||
pm.prompt_message(PromptMessage::Text(format!(
|
|
||||||
"Our words: {combined_mnemonic}"
|
|
||||||
)))?;
|
|
||||||
|
|
||||||
if let Ok(qrcode) = qrencode::qrencode(&combined_mnemonic) {
|
#[cfg(feature = "qrcode")]
|
||||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
{
|
||||||
|
use keyfork_qrcode::{qrencode, ErrorCorrection};
|
||||||
|
let mut qrcode_data = nonce_mnemonic.entropy();
|
||||||
|
qrcode_data.extend(key_mnemonic.entropy());
|
||||||
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Medium) {
|
||||||
|
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let validator = MnemonicSetValidator {
|
pm.prompt_message(PromptMessage::Text(format!(
|
||||||
word_lengths: [24, 48],
|
"Our words: {nonce_mnemonic} {key_mnemonic}"
|
||||||
|
)))?;
|
||||||
|
|
||||||
|
let mut pubkey_data: Option<[u8; 32]> = None;
|
||||||
|
let mut payload_data = None;
|
||||||
|
|
||||||
|
#[cfg(feature = "qrcode")]
|
||||||
|
{
|
||||||
|
pm.prompt_message(PromptMessage::Text(
|
||||||
|
"Press enter, then present QR code to camera".to_string(),
|
||||||
|
))?;
|
||||||
|
if let Ok(Some(hex)) =
|
||||||
|
keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0)
|
||||||
|
{
|
||||||
|
let decoded_data = smex::decode(&hex)?;
|
||||||
|
let _ = pubkey_data.insert(decoded_data[..32].try_into().map_err(|_| InvalidData)?);
|
||||||
|
let _ = payload_data.insert(decoded_data[32..].to_vec());
|
||||||
|
} else {
|
||||||
|
pm.prompt_message(PromptMessage::Text(
|
||||||
|
"Unable to detect QR code, falling back to text".to_string(),
|
||||||
|
))?;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let (pubkey, payload) = match (pubkey_data, payload_data) {
|
||||||
|
(Some(pubkey), Some(payload)) => (pubkey, payload),
|
||||||
|
_ => {
|
||||||
|
let validator = MnemonicSetValidator {
|
||||||
|
word_lengths: [24, 48],
|
||||||
|
};
|
||||||
|
|
||||||
|
let [pubkey_mnemonic, payload_mnemonic] =
|
||||||
|
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
||||||
|
let pubkey = pubkey_mnemonic
|
||||||
|
.entropy()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| InvalidData)?;
|
||||||
|
let payload = payload_mnemonic.entropy();
|
||||||
|
(pubkey, payload)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let [pubkey_mnemonic, payload_mnemonic] =
|
let shared_secret = our_key.diffie_hellman(&PublicKey::from(pubkey)).to_bytes();
|
||||||
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
|
||||||
|
|
||||||
let their_key: [u8; 32] = pubkey_mnemonic
|
|
||||||
.entropy()
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| InvalidMnemonicData)?;
|
|
||||||
|
|
||||||
let shared_secret = our_key
|
|
||||||
.diffie_hellman(&PublicKey::from(their_key))
|
|
||||||
.to_bytes();
|
|
||||||
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
|
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
|
||||||
let mut hkdf_output = [0u8; 256 / 8];
|
let mut hkdf_output = [0u8; 256 / 8];
|
||||||
hkdf.expand(&[], &mut hkdf_output)?;
|
hkdf.expand(&[], &mut hkdf_output)?;
|
||||||
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
||||||
|
|
||||||
let payload = payload_mnemonic.entropy();
|
|
||||||
let payload =
|
let payload =
|
||||||
shared_key.decrypt(&nonce, &payload[..payload[payload.len() - 1] as usize])?;
|
shared_key.decrypt(&nonce, &payload[..payload[payload.len() - 1] as usize])?;
|
||||||
assert_eq!(HUNK_VERSION, payload[0], "Incompatible hunk version");
|
assert_eq!(HUNK_VERSION, payload[0], "Incompatible hunk version");
|
||||||
|
|
|
@ -17,9 +17,8 @@ use keyfork_derive_openpgp::derive_util::{
|
||||||
};
|
};
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
qrencode,
|
|
||||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||||
Error as PromptError, Message as PromptMessage, Terminal, PromptHandler,
|
Error as PromptError, Message as PromptMessage, PromptHandler, Terminal,
|
||||||
};
|
};
|
||||||
use openpgp::{
|
use openpgp::{
|
||||||
armor::{Kind, Writer},
|
armor::{Kind, Writer},
|
||||||
|
@ -55,7 +54,7 @@ use smartcard::SmartcardManager;
|
||||||
const SHARD_METADATA_VERSION: u8 = 1;
|
const SHARD_METADATA_VERSION: u8 = 1;
|
||||||
const SHARD_METADATA_OFFSET: usize = 2;
|
const SHARD_METADATA_OFFSET: usize = 2;
|
||||||
|
|
||||||
use super::{InvalidMnemonicData, SharksError, HUNK_VERSION};
|
use super::{InvalidData, SharksError, HUNK_VERSION};
|
||||||
|
|
||||||
// 256 bit share is 49 bytes + some amount of hunk bytes, gives us reasonable padding
|
// 256 bit share is 49 bytes + some amount of hunk bytes, gives us reasonable padding
|
||||||
const ENC_LEN: u8 = 4 * 16;
|
const ENC_LEN: u8 = 4 * 16;
|
||||||
|
@ -99,7 +98,7 @@ pub enum Error {
|
||||||
MnemonicFromStr(#[from] MnemonicFromStrError),
|
MnemonicFromStr(#[from] MnemonicFromStrError),
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidMnemonicData(#[from] InvalidMnemonicData),
|
InvalidMnemonicData(#[from] InvalidData),
|
||||||
|
|
||||||
#[error("IO error: {0}")]
|
#[error("IO error: {0}")]
|
||||||
Io(#[source] std::io::Error),
|
Io(#[source] std::io::Error),
|
||||||
|
@ -110,6 +109,9 @@ pub enum Error {
|
||||||
#[error("Derivation request: {0}")]
|
#[error("Derivation request: {0}")]
|
||||||
DerivationRequest(#[from] keyfork_derive_openpgp::derive_util::request::DerivationError),
|
DerivationRequest(#[from] keyfork_derive_openpgp::derive_util::request::DerivationError),
|
||||||
|
|
||||||
|
#[error("Unable to decode hex: {0}")]
|
||||||
|
HexDecode(#[from] smex::DecodeError),
|
||||||
|
|
||||||
#[error("Keyfork OpenPGP: {0}")]
|
#[error("Keyfork OpenPGP: {0}")]
|
||||||
KeyforkOpenPGP(#[from] keyfork_derive_openpgp::Error),
|
KeyforkOpenPGP(#[from] keyfork_derive_openpgp::Error),
|
||||||
}
|
}
|
||||||
|
@ -411,26 +413,54 @@ pub fn decrypt(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut pm = Terminal::new(stdin(), stdout())?;
|
let mut pm = Terminal::new(stdin(), stdout())?;
|
||||||
let wordlist = Wordlist::default();
|
let wordlist = Wordlist::default();
|
||||||
let validator = MnemonicSetValidator {
|
|
||||||
word_lengths: [9, 24],
|
|
||||||
};
|
|
||||||
let [nonce_mnemonic, pubkey_mnemonic] =
|
|
||||||
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
|
||||||
|
|
||||||
let their_key: [u8; 32] = pubkey_mnemonic
|
let mut nonce_data: Option<[u8; 12]> = None;
|
||||||
.entropy()
|
let mut pubkey_data: Option<[u8; 32]> = None;
|
||||||
.try_into()
|
|
||||||
.map_err(|_| InvalidMnemonicData)?;
|
#[cfg(feature = "qrcode")]
|
||||||
let their_nonce = nonce_mnemonic.entropy();
|
{
|
||||||
let their_nonce = Nonce::<U12>::from_slice(&their_nonce);
|
pm.prompt_message(PromptMessage::Text(
|
||||||
|
"Press enter, then present QR code to camera".to_string(),
|
||||||
|
))?;
|
||||||
|
if let Ok(Some(hex)) = keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0) {
|
||||||
|
let decoded_data = smex::decode(&hex)?;
|
||||||
|
let _ = nonce_data.insert(decoded_data[..12].try_into().map_err(|_| InvalidData)?);
|
||||||
|
let _ = pubkey_data.insert(decoded_data[12..].try_into().map_err(|_| InvalidData)?);
|
||||||
|
} else {
|
||||||
|
pm.prompt_message(PromptMessage::Text(
|
||||||
|
"Unable to detect QR code, falling back to text".to_string(),
|
||||||
|
))?;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let (nonce, pubkey) = match (nonce_data, pubkey_data) {
|
||||||
|
(Some(nonce), Some(pubkey)) => (nonce, pubkey),
|
||||||
|
_ => {
|
||||||
|
let validator = MnemonicSetValidator {
|
||||||
|
word_lengths: [9, 24],
|
||||||
|
};
|
||||||
|
let [nonce_mnemonic, pubkey_mnemonic] =
|
||||||
|
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
||||||
|
|
||||||
|
let nonce = nonce_mnemonic
|
||||||
|
.entropy()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| InvalidData)?;
|
||||||
|
let pubkey = pubkey_mnemonic
|
||||||
|
.entropy()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| InvalidData)?;
|
||||||
|
(nonce, pubkey)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce = Nonce::<U12>::from_slice(&nonce);
|
||||||
|
|
||||||
let our_key = EphemeralSecret::random();
|
let our_key = EphemeralSecret::random();
|
||||||
let our_mnemonic =
|
let our_pubkey_mnemonic =
|
||||||
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
|
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
|
||||||
|
|
||||||
let shared_secret = our_key
|
let shared_secret = our_key.diffie_hellman(&PublicKey::from(pubkey)).to_bytes();
|
||||||
.diffie_hellman(&PublicKey::from(their_key))
|
|
||||||
.to_bytes();
|
|
||||||
|
|
||||||
let (mut share, threshold, ..) = decrypt_one(encrypted_messages.to_vec(), certs, metadata)?;
|
let (mut share, threshold, ..) = decrypt_one(encrypted_messages.to_vec(), certs, metadata)?;
|
||||||
share.insert(0, HUNK_VERSION);
|
share.insert(0, HUNK_VERSION);
|
||||||
|
@ -445,8 +475,8 @@ pub fn decrypt(
|
||||||
hkdf.expand(&[], &mut hkdf_output)?;
|
hkdf.expand(&[], &mut hkdf_output)?;
|
||||||
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
||||||
|
|
||||||
let bytes = shared_key.encrypt(their_nonce, share.as_slice())?;
|
let bytes = shared_key.encrypt(nonce, share.as_slice())?;
|
||||||
shared_key.decrypt(their_nonce, &bytes[..])?;
|
shared_key.decrypt(nonce, &bytes[..])?;
|
||||||
|
|
||||||
// NOTE: Padding length is less than u8::MAX because ENC_LEN < u8::MAX
|
// NOTE: Padding length is less than u8::MAX because ENC_LEN < u8::MAX
|
||||||
// NOTE: This previously used a single value as the padding byte, but resulted in
|
// NOTE: This previously used a single value as the padding byte, but resulted in
|
||||||
|
@ -473,17 +503,22 @@ pub fn decrypt(
|
||||||
}
|
}
|
||||||
|
|
||||||
// safety: size of out_bytes is constant and always % 4 == 0
|
// safety: size of out_bytes is constant and always % 4 == 0
|
||||||
let mnemonic = unsafe { Mnemonic::from_raw_entropy(&out_bytes, Default::default()) };
|
let payload_mnemonic = unsafe { Mnemonic::from_raw_entropy(&out_bytes, Default::default()) };
|
||||||
let combined_mnemonic = format!("{our_mnemonic} {mnemonic}");
|
|
||||||
|
#[cfg(feature = "qrcode")]
|
||||||
|
{
|
||||||
|
use keyfork_qrcode::{qrencode, ErrorCorrection};
|
||||||
|
let mut qrcode_data = our_pubkey_mnemonic.entropy();
|
||||||
|
qrcode_data.extend(payload_mnemonic.entropy());
|
||||||
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Lowest) {
|
||||||
|
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pm.prompt_message(PromptMessage::Text(format!(
|
pm.prompt_message(PromptMessage::Text(format!(
|
||||||
"Our words: {combined_mnemonic}"
|
"Our words: {our_pubkey_mnemonic} {payload_mnemonic}"
|
||||||
)))?;
|
)))?;
|
||||||
|
|
||||||
if let Ok(qrcode) = qrencode::qrencode(&combined_mnemonic) {
|
|
||||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue