Compare commits
No commits in common. "b254ba7c56cf51b5f7d4f16cd459426c0cfd8e4f" and "ba64db8f009c873cb32beb4cff6ca3cd863c23c8" have entirely different histories.
b254ba7c56
...
ba64db8f00
|
@ -335,6 +335,12 @@ 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.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
|
@ -510,9 +516,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.16.3"
|
version = "1.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
|
checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
|
@ -835,15 +841,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "4.1.3"
|
version = "4.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"curve25519-dalek-derive",
|
"curve25519-dalek-derive",
|
||||||
"digest",
|
"digest",
|
||||||
"fiat-crypto",
|
"fiat-crypto",
|
||||||
|
"platforms",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
@ -952,17 +959,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "displaydoc"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dsa"
|
name = "dsa"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
@ -1528,124 +1524,6 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_collections"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"yoke",
|
|
||||||
"zerofrom",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_locid"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"litemap",
|
|
||||||
"tinystr",
|
|
||||||
"writeable",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_locid_transform"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"icu_locid",
|
|
||||||
"icu_locid_transform_data",
|
|
||||||
"icu_provider",
|
|
||||||
"tinystr",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_locid_transform_data"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_normalizer"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"icu_collections",
|
|
||||||
"icu_normalizer_data",
|
|
||||||
"icu_properties",
|
|
||||||
"icu_provider",
|
|
||||||
"smallvec",
|
|
||||||
"utf16_iter",
|
|
||||||
"utf8_iter",
|
|
||||||
"write16",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_normalizer_data"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_properties"
|
|
||||||
version = "1.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"icu_collections",
|
|
||||||
"icu_locid_transform",
|
|
||||||
"icu_properties_data",
|
|
||||||
"icu_provider",
|
|
||||||
"tinystr",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_properties_data"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_provider"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"icu_locid",
|
|
||||||
"icu_provider_macros",
|
|
||||||
"stable_deref_trait",
|
|
||||||
"tinystr",
|
|
||||||
"writeable",
|
|
||||||
"yoke",
|
|
||||||
"zerofrom",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_provider_macros"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idea"
|
name = "idea"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -1657,14 +1535,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.0.2"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd69211b9b519e98303c015e21a007e293db403b6c85b9b124e133d25e242cdd"
|
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"icu_normalizer",
|
"unicode-bidi",
|
||||||
"icu_properties",
|
"unicode-normalization",
|
||||||
"smallvec",
|
|
||||||
"utf8_iter",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1805,10 +1681,9 @@ dependencies = [
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"keyfork-bin",
|
"keyfork-bin",
|
||||||
"keyfork-derive-openpgp",
|
"keyfork-derive-openpgp",
|
||||||
"keyfork-derive-path-data",
|
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"keyfork-entropy",
|
"keyfork-entropy",
|
||||||
"keyfork-mnemonic",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-prompt",
|
"keyfork-prompt",
|
||||||
"keyfork-qrcode",
|
"keyfork-qrcode",
|
||||||
"keyfork-shard",
|
"keyfork-shard",
|
||||||
|
@ -1873,7 +1748,6 @@ version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"keyfork-derive-path-data",
|
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"keyforkd-client",
|
"keyforkd-client",
|
||||||
"sequoia-openpgp",
|
"sequoia-openpgp",
|
||||||
|
@ -1885,7 +1759,6 @@ name = "keyfork-derive-path-data"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"once_cell",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1898,7 +1771,7 @@ dependencies = [
|
||||||
"hmac",
|
"hmac",
|
||||||
"k256",
|
"k256",
|
||||||
"keyfork-bug",
|
"keyfork-bug",
|
||||||
"keyfork-mnemonic",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-slip10-test-data",
|
"keyfork-slip10-test-data",
|
||||||
"ripemd",
|
"ripemd",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1925,7 +1798,7 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-mnemonic"
|
name = "keyfork-mnemonic-util"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bip39",
|
"bip39",
|
||||||
|
@ -1944,7 +1817,7 @@ version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"keyfork-bug",
|
"keyfork-bug",
|
||||||
"keyfork-crossterm",
|
"keyfork-crossterm",
|
||||||
"keyfork-mnemonic",
|
"keyfork-mnemonic-util",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1966,13 +1839,13 @@ version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64 0.22.0",
|
||||||
"card-backend",
|
"card-backend",
|
||||||
"card-backend-pcsc",
|
"card-backend-pcsc",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"keyfork-bug",
|
"keyfork-bug",
|
||||||
"keyfork-derive-openpgp",
|
"keyfork-derive-openpgp",
|
||||||
"keyfork-mnemonic",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-prompt",
|
"keyfork-prompt",
|
||||||
"keyfork-qrcode",
|
"keyfork-qrcode",
|
||||||
"openpgp-card",
|
"openpgp-card",
|
||||||
|
@ -2020,7 +1893,7 @@ dependencies = [
|
||||||
"keyfork-derive-path-data",
|
"keyfork-derive-path-data",
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"keyfork-frame",
|
"keyfork-frame",
|
||||||
"keyfork-mnemonic",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-slip10-test-data",
|
"keyfork-slip10-test-data",
|
||||||
"keyforkd-models",
|
"keyforkd-models",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2160,12 +2033,6 @@ version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "litemap"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
|
@ -2470,32 +2337,6 @@ dependencies = [
|
||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "p384"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209"
|
|
||||||
dependencies = [
|
|
||||||
"ecdsa",
|
|
||||||
"elliptic-curve",
|
|
||||||
"primeorder",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "p521"
|
|
||||||
version = "0.13.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2"
|
|
||||||
dependencies = [
|
|
||||||
"base16ct",
|
|
||||||
"ecdsa",
|
|
||||||
"elliptic-curve",
|
|
||||||
"primeorder",
|
|
||||||
"rand_core",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -2689,6 +2530,12 @@ version = "0.3.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
|
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platforms"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polling"
|
name = "polling"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
|
@ -3038,14 +2885,14 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sequoia-openpgp"
|
name = "sequoia-openpgp"
|
||||||
version = "1.21.2"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13261ee216b44d932ef93b2d4a75d45199bef77864bcc5b77ecfc7bc0ecb02d6"
|
checksum = "2ea026cf8a70d331c742e3ad7e68fd405d0743ff86630fb4334a1bf8d0e194c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64 0.21.7",
|
||||||
"block-padding",
|
"block-padding",
|
||||||
"blowfish",
|
"blowfish",
|
||||||
"buffered-reader",
|
"buffered-reader",
|
||||||
|
@ -3080,8 +2927,6 @@ dependencies = [
|
||||||
"openssl",
|
"openssl",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
"p256",
|
"p256",
|
||||||
"p384",
|
|
||||||
"p521",
|
|
||||||
"rand",
|
"rand",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -3318,12 +3163,6 @@ dependencies = [
|
||||||
"der 0.7.8",
|
"der 0.7.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stable_deref_trait"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "string_cache"
|
name = "string_cache"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
|
@ -3371,17 +3210,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "synstructure"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.10.0"
|
version = "3.10.0"
|
||||||
|
@ -3454,16 +3282,6 @@ dependencies = [
|
||||||
"crunchy",
|
"crunchy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinystr"
|
|
||||||
version = "0.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
@ -3625,6 +3443,12 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
@ -3662,18 +3486,6 @@ version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf16_iter"
|
|
||||||
version = "1.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8_iter"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -3987,18 +3799,6 @@ version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "write16"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "writeable"
|
|
||||||
version = "0.5.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x25519-dalek"
|
name = "x25519-dalek"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -4017,30 +3817,6 @@ version = "0.8.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61"
|
checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "yoke"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"stable_deref_trait",
|
|
||||||
"yoke-derive",
|
|
||||||
"zerofrom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "yoke-derive"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
"synstructure",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.32"
|
version = "0.7.32"
|
||||||
|
@ -4061,27 +3837,6 @@ dependencies = [
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerofrom"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
|
||||||
dependencies = [
|
|
||||||
"zerofrom-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerofrom-derive"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
"synstructure",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -4101,25 +3856,3 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerovec"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
|
||||||
dependencies = [
|
|
||||||
"yoke",
|
|
||||||
"zerofrom",
|
|
||||||
"zerovec-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerovec-derive"
|
|
||||||
version = "0.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.48",
|
|
||||||
]
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ members = [
|
||||||
"crates/util/keyfork-crossterm",
|
"crates/util/keyfork-crossterm",
|
||||||
"crates/util/keyfork-entropy",
|
"crates/util/keyfork-entropy",
|
||||||
"crates/util/keyfork-frame",
|
"crates/util/keyfork-frame",
|
||||||
"crates/util/keyfork-mnemonic",
|
"crates/util/keyfork-mnemonic-util",
|
||||||
"crates/util/keyfork-prompt",
|
"crates/util/keyfork-prompt",
|
||||||
"crates/util/keyfork-slip10-test-data",
|
"crates/util/keyfork-slip10-test-data",
|
||||||
"crates/util/smex",
|
"crates/util/smex",
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
//! # The Keyforkd Client
|
//! # The Keyforkd Client
|
||||||
//!
|
//!
|
||||||
//! Keyfork allows securing the master key and highest-level derivation keys by having derivation
|
//! Keyfork allows securing the master key and highest-level derivation keys by having derivation
|
||||||
//! requests performed against a server, "Keyforkd" or the "Keyfork Server". This allows
|
//! requests performed against a server, "Keyforkd" or the "Keyfork Server". The server is operated
|
||||||
//! enforcement of policies, such as requiring at least two leves of a derivation path (for
|
//! on a UNIX socket with messages sent using the Keyfork Frame format.
|
||||||
//! instance, `m/0'` would not be allowed, but `m/0'/0'` would). The server is operated on a UNIX
|
|
||||||
//! socket with messages sent using the Keyfork Frame format.
|
|
||||||
//!
|
//!
|
||||||
//! Programs using the Keyfork Client should ensure they are built against a compatible version of
|
//! Programs using the Keyfork Client should ensure they are built against a compatible version of
|
||||||
//! the Keyfork Server. For versions prior to `1.0.0`, all versions within a "minor" version (i.e.,
|
//! the Keyfork Server. For versions prior to `1.0.0`, all versions within a "minor" version (i.e.,
|
||||||
|
@ -12,58 +10,9 @@
|
||||||
//! after `1.0.0`, all versions within a "major" version (i.e., `1.0.0`) will be compatible, but
|
//! after `1.0.0`, all versions within a "major" version (i.e., `1.0.0`) will be compatible, but
|
||||||
//! `1.x.y` will not be compatible with `2.0.0`.
|
//! `1.x.y` will not be compatible with `2.0.0`.
|
||||||
//!
|
//!
|
||||||
//! The Keyfork Client documentation makes extensive use of the `keyforkd::test_util` module.
|
//! Presently, the Keyfork server only supports the following requests:
|
||||||
//! This provides testing infrastructure to set up a temporary Keyfork Daemon. In
|
|
||||||
//! your code, you should assume the daemon has already been initialized, whether by another
|
|
||||||
//! process, on another terminal, or some other instance. At no point should a program deriving an
|
|
||||||
//! "endpoint" key have control over a mnemonic or a seed.
|
|
||||||
//!
|
//!
|
||||||
//! ## Server Requests
|
//! * Derive Key
|
||||||
//!
|
|
||||||
//! Keyfork is designed as a client-request/server-response model. The client sends a request, such
|
|
||||||
//! as a derivation request, and the server sends its response. Presently, the Keyfork server
|
|
||||||
//! supports the following requests:
|
|
||||||
//!
|
|
||||||
//! ### Request: Derive Key
|
|
||||||
//!
|
|
||||||
//! The client creates a derivation path of at least two indices and requests a derived XPrv
|
|
||||||
//! (Extended Private Key) from the server.
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use std::str::FromStr;
|
|
||||||
//!
|
|
||||||
//! use keyforkd_client::Client;
|
|
||||||
//! use keyfork_derive_util::DerivationPath;
|
|
||||||
//! # use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
|
||||||
//! // use k256::SecretKey as PrivateKey;
|
|
||||||
//! // use ed25519_dalek::SigningKey as PrivateKey;
|
|
||||||
//!
|
|
||||||
//! #[derive(Debug, thiserror::Error)]
|
|
||||||
//! enum Error {
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Path(#[from] keyfork_derive_util::PathError),
|
|
||||||
//!
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Keyforkd(#[from] keyforkd_client::Error),
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() -> Result<(), Error> {
|
|
||||||
//! # let seed = b"funky accordion noises";
|
|
||||||
//! # keyforkd::test_util::run_test(seed, |socket_path| {
|
|
||||||
//! let derivation_path = DerivationPath::from_str("m/44'/0'")?;
|
|
||||||
//! let mut client = Client::discover_socket()?;
|
|
||||||
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path)?;
|
|
||||||
//! # Ok::<_, Error>(())
|
|
||||||
//! # })?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ---
|
|
||||||
//!
|
|
||||||
//! Request objects are typically handled by the Keyfork Client library (such as with
|
|
||||||
//! [`Client::request_xprv`]). While unadvised, clients can also attempt to handle their own
|
|
||||||
//! requests, using [`Client::request`].
|
|
||||||
//!
|
//!
|
||||||
//! ## Extended Private Keys
|
//! ## Extended Private Keys
|
||||||
//!
|
//!
|
||||||
|
@ -72,101 +21,42 @@
|
||||||
//! The tests for this library ensure that all levels of Keyfork derivation beyond the required two
|
//! The tests for this library ensure that all levels of Keyfork derivation beyond the required two
|
||||||
//! will be derived similarly between the server and the client.
|
//! will be derived similarly between the server and the client.
|
||||||
//!
|
//!
|
||||||
|
//! # Examples
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use std::str::FromStr;
|
//! use std::str::FromStr;
|
||||||
//!
|
//!
|
||||||
//! use keyforkd_client::Client;
|
//! use keyforkd_client::Client;
|
||||||
//! use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
//! use keyfork_derive_util::DerivationPath;
|
||||||
//! # use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
//! # use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
||||||
//! // use k256::SecretKey as PrivateKey;
|
//! // use k256::SecretKey as PrivateKey;
|
||||||
//! // use ed25519_dalek::SigningKey as PrivateKey;
|
//! // use ed25519_dalek::SigningKey as PrivateKey;
|
||||||
//! # fn check_wallet<T>(_: T) {}
|
|
||||||
//!
|
//!
|
||||||
//! #[derive(Debug, thiserror::Error)]
|
|
||||||
//! enum Error {
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Index(#[from] keyfork_derive_util::IndexError),
|
|
||||||
//!
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Path(#[from] keyfork_derive_util::PathError),
|
|
||||||
//!
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! PrivateKey(#[from] keyfork_derive_util::PrivateKeyError),
|
|
||||||
//!
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Keyforkd(#[from] keyforkd_client::Error),
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() -> Result<(), Error> {
|
|
||||||
//! # let seed = b"funky accordion noises";
|
//! # let seed = b"funky accordion noises";
|
||||||
//! # keyforkd::test_util::run_test(seed, |socket_path| {
|
//! # keyforkd::test_util::run_test(seed, |socket_path| {
|
||||||
//! let derivation_path = DerivationPath::from_str("m/44'/0'/0'/0")?;
|
//! # std::env::set_var("KEYFORKD_SOCKET_PATH", socket_path);
|
||||||
//! let mut client = Client::discover_socket()?;
|
|
||||||
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path)?;
|
|
||||||
//! // scan first 20 wallets
|
|
||||||
//! for index in 0..20 {
|
|
||||||
//! // use non-hardened derivation
|
|
||||||
//! let new_xprv = xprv.derive_child(&DerivationIndex::new(index, false)?);
|
|
||||||
//! check_wallet(new_xprv)
|
|
||||||
//! }
|
|
||||||
//! # Ok::<_, Error>(())
|
|
||||||
//! # })?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Testing Infrastructure
|
|
||||||
//!
|
|
||||||
//! In tests, the `keyforkd::test_util` module and TestPrivateKeys can be used. These provide
|
|
||||||
//! useful utilities for writing tests that interact with the Keyfork Server without needing to
|
|
||||||
//! manually create the server for the purpose of the test. The `run_test` method can be used to
|
|
||||||
//! run a test, which can handle both returning errors and correctly translating panics (though,
|
|
||||||
//! the panics definitely won't look tidy).
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use std::str::FromStr;
|
|
||||||
//!
|
|
||||||
//! use keyforkd_client::Client;
|
|
||||||
//! use keyfork_derive_util::DerivationPath;
|
|
||||||
//! use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
|
||||||
//!
|
|
||||||
//! #[derive(Debug, thiserror::Error)]
|
|
||||||
//! enum Error {
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Path(#[from] keyfork_derive_util::PathError),
|
|
||||||
//!
|
|
||||||
//! #[error(transparent)]
|
|
||||||
//! Keyforkd(#[from] keyforkd_client::Error),
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn main() -> Result<(), Error> {
|
|
||||||
//! let seed = b"funky accordion noises";
|
|
||||||
//! keyforkd::test_util::run_test(seed, |socket_path| {
|
|
||||||
//! let derivation_path = DerivationPath::from_str("m/44'/0'")?;
|
|
||||||
//! let mut client = Client::discover_socket()?;
|
|
||||||
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path)?;
|
|
||||||
//! Ok::<_, Error>(())
|
|
||||||
//! })?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! If you would rather write tests to panic rather than error, or would rather not deal with error
|
|
||||||
//! types, the Panicable type should be used, which will handle the Error type for the closure.
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use std::str::FromStr;
|
|
||||||
//!
|
|
||||||
//! use keyforkd_client::Client;
|
|
||||||
//! use keyfork_derive_util::DerivationPath;
|
|
||||||
//! use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
|
||||||
//!
|
|
||||||
//! let seed = b"funky accordion noises";
|
|
||||||
//! keyforkd::test_util::run_test(seed, |socket_path| {
|
|
||||||
//! let derivation_path = DerivationPath::from_str("m/44'/0'").unwrap();
|
//! let derivation_path = DerivationPath::from_str("m/44'/0'").unwrap();
|
||||||
//! let mut client = Client::discover_socket().unwrap();
|
//! let mut client = Client::discover_socket().unwrap();
|
||||||
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path).unwrap();
|
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path).unwrap();
|
||||||
//! keyforkd::test_util::Panicable::Ok(())
|
//! # keyforkd::test_util::Infallible::Ok(())
|
||||||
|
//! # }).unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! In tests, the Keyforkd test_util module and TestPrivateKeys can be used.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::str::FromStr;
|
||||||
|
//!
|
||||||
|
//! use keyforkd_client::Client;
|
||||||
|
//! use keyfork_derive_util::DerivationPath;
|
||||||
|
//! use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
||||||
|
//!
|
||||||
|
//! let seed = b"funky accordion noises";
|
||||||
|
//! keyforkd::test_util::run_test(seed, |socket_path| {
|
||||||
|
//! std::env::set_var("KEYFORKD_SOCKET_PATH", socket_path);
|
||||||
|
//! let derivation_path = DerivationPath::from_str("m/44'/0'").unwrap();
|
||||||
|
//! let mut client = Client::discover_socket().unwrap();
|
||||||
|
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path).unwrap();
|
||||||
|
//! keyforkd::test_util::Infallible::Ok(())
|
||||||
//! }).unwrap();
|
//! }).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -275,9 +165,10 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # let seed = b"funky accordion noises";
|
/// # let seed = b"funky accordion noises";
|
||||||
/// # keyforkd::test_util::run_test(seed, |socket_path| {
|
/// # keyforkd::test_util::run_test(seed, |socket_path| {
|
||||||
/// let mut socket = get_socket()?;
|
/// # std::env::set_var("KEYFORKD_SOCKET_PATH", socket_path);
|
||||||
|
/// let mut socket = get_socket().unwrap();
|
||||||
/// let mut client = Client::new(socket);
|
/// let mut client = Client::new(socket);
|
||||||
/// # Ok::<_, keyforkd_client::Error>(())
|
/// # keyforkd::test_util::Infallible::Ok(())
|
||||||
/// # }).unwrap();
|
/// # }).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(socket: UnixStream) -> Self {
|
pub fn new(socket: UnixStream) -> Self {
|
||||||
|
@ -296,8 +187,9 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # let seed = b"funky accordion noises";
|
/// # let seed = b"funky accordion noises";
|
||||||
/// # keyforkd::test_util::run_test(seed, |socket_path| {
|
/// # keyforkd::test_util::run_test(seed, |socket_path| {
|
||||||
/// let mut client = Client::discover_socket()?;
|
/// # std::env::set_var("KEYFORKD_SOCKET_PATH", socket_path);
|
||||||
/// # Ok::<_, keyforkd_client::Error>(())
|
/// let mut client = Client::discover_socket().unwrap();
|
||||||
|
/// # keyforkd::test_util::Infallible::Ok(())
|
||||||
/// # }).unwrap();
|
/// # }).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn discover_socket() -> Result<Self> {
|
pub fn discover_socket() -> Result<Self> {
|
||||||
|
@ -325,10 +217,11 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # let seed = b"funky accordion noises";
|
/// # let seed = b"funky accordion noises";
|
||||||
/// # keyforkd::test_util::run_test(seed, |socket_path| {
|
/// # keyforkd::test_util::run_test(seed, |socket_path| {
|
||||||
|
/// # std::env::set_var("KEYFORKD_SOCKET_PATH", socket_path);
|
||||||
/// let derivation_path = DerivationPath::from_str("m/44'/0'").unwrap();
|
/// let derivation_path = DerivationPath::from_str("m/44'/0'").unwrap();
|
||||||
/// let mut client = Client::discover_socket().unwrap();
|
/// let mut client = Client::discover_socket().unwrap();
|
||||||
/// let xprv = client.request_xprv::<PrivateKey>(&derivation_path).unwrap();
|
/// let xprv = client.request_xprv::<PrivateKey>(&derivation_path).unwrap();
|
||||||
/// # keyforkd::test_util::Panicable::Ok(())
|
/// # keyforkd::test_util::Infallible::Ok(())
|
||||||
/// # }).unwrap();
|
/// # }).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn request_xprv<K>(&mut self, path: &DerivationPath) -> Result<ExtendedPrivateKey<K>>
|
pub fn request_xprv<K>(&mut self, path: &DerivationPath) -> Result<ExtendedPrivateKey<K>>
|
||||||
|
@ -351,20 +244,15 @@ impl Client {
|
||||||
_ => Err(Error::InvalidResponse),
|
_ => Err(Error::InvalidResponse),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
|
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
|
||||||
///
|
///
|
||||||
/// This function does not properly assert the association between a request type and a
|
|
||||||
/// response type, and does not perform any serialization of native objects into Request or
|
|
||||||
/// Response types, and should only be used when absolutely necessary.
|
|
||||||
///
|
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// An error may be returned if:
|
/// An error may be returned if:
|
||||||
/// * Reading or writing from or to the socket encountered an error.
|
/// * Reading or writing from or to the socket encountered an error.
|
||||||
/// * Bincode could not serialize the request or deserialize the response.
|
/// * Bincode could not serialize the request or deserialize the response.
|
||||||
/// * An error occurred in Keyforkd.
|
/// * An error occurred in Keyforkd.
|
||||||
|
#[doc(hidden)]
|
||||||
pub fn request(&mut self, req: &Request) -> Result<Response> {
|
pub fn request(&mut self, req: &Request) -> Result<Response> {
|
||||||
try_encode_to(&bincode::serialize(&req)?, &mut self.socket)?;
|
try_encode_to(&bincode::serialize(&req)?, &mut self.socket)?;
|
||||||
let resp = try_decode_from(&mut self.socket)?;
|
let resp = try_decode_from(&mut self.socket)?;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::Client;
|
use crate::Client;
|
||||||
use keyfork_derive_util::{request::*, DerivationPath};
|
use keyfork_derive_util::{request::*, DerivationPath};
|
||||||
use keyfork_slip10_test_data::test_data;
|
use keyfork_slip10_test_data::test_data;
|
||||||
use keyforkd::test_util::{run_test, Panicable};
|
use keyforkd::test_util::{run_test, Infallible};
|
||||||
use std::{os::unix::net::UnixStream, str::FromStr};
|
use std::{os::unix::net::UnixStream, str::FromStr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -109,7 +109,7 @@ fn ed25519_test_suite() {
|
||||||
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
|
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
|
||||||
assert_eq!(&response.data, test.private_key.as_slice());
|
assert_eq!(&response.data, test.private_key.as_slice());
|
||||||
}
|
}
|
||||||
Panicable::Ok(())
|
Infallible::Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ multithread = ["tokio/rt-multi-thread"]
|
||||||
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug", registry = "distrust" }
|
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug", registry = "distrust" }
|
||||||
keyfork-derive-util = { version = "0.2.0", path = "../../derive/keyfork-derive-util", registry = "distrust" }
|
keyfork-derive-util = { version = "0.2.0", path = "../../derive/keyfork-derive-util", registry = "distrust" }
|
||||||
keyfork-frame = { version = "0.1.0", path = "../../util/keyfork-frame", features = ["async"], registry = "distrust" }
|
keyfork-frame = { version = "0.1.0", path = "../../util/keyfork-frame", features = ["async"], registry = "distrust" }
|
||||||
keyfork-mnemonic = { version = "0.3.0", path = "../../util/keyfork-mnemonic", registry = "distrust" }
|
keyfork-mnemonic-util = { version = "0.3.0", path = "../../util/keyfork-mnemonic-util", registry = "distrust" }
|
||||||
keyfork-derive-path-data = { version = "0.1.0", path = "../../derive/keyfork-derive-path-data", registry = "distrust" }
|
keyfork-derive-path-data = { version = "0.1.0", path = "../../derive/keyfork-derive-path-data", registry = "distrust" }
|
||||||
keyforkd-models = { version = "0.2.0", path = "../keyforkd-models", registry = "distrust" }
|
keyforkd-models = { version = "0.2.0", path = "../keyforkd-models", registry = "distrust" }
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use keyfork_mnemonic::Mnemonic;
|
pub use keyfork_mnemonic_util::Mnemonic;
|
||||||
pub use tower::ServiceBuilder;
|
pub use tower::ServiceBuilder;
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
|
@ -57,7 +57,7 @@ pub async fn start_and_run_server_on(
|
||||||
let service = ServiceBuilder::new()
|
let service = ServiceBuilder::new()
|
||||||
.layer(middleware::BincodeLayer::new())
|
.layer(middleware::BincodeLayer::new())
|
||||||
// TODO: passphrase support and/or store passphrase with mnemonic
|
// TODO: passphrase support and/or store passphrase with mnemonic
|
||||||
.service(Keyforkd::new(mnemonic.generate_seed(None).to_vec()));
|
.service(Keyforkd::new(mnemonic.generate_seed(None)));
|
||||||
|
|
||||||
let mut server = match UnixServer::bind(socket_path) {
|
let mut server = match UnixServer::bind(socket_path) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use keyfork_mnemonic::Mnemonic;
|
use keyfork_mnemonic_util::Mnemonic;
|
||||||
|
|
||||||
use tokio::io::{self, AsyncBufReadExt, BufReader};
|
use tokio::io::{self, AsyncBufReadExt, BufReader};
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,20 @@ use keyfork_bug::bug;
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error("This error can never be instantiated")]
|
#[error("This error can never be instantiated")]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub enum UninstantiableError {}
|
pub struct InfallibleError {
|
||||||
|
protected: (),
|
||||||
|
}
|
||||||
|
|
||||||
/// A panicable result. This type can be used when a closure chooses to panic instead of
|
/// An infallible result. This type can be used to represent a function that should never error.
|
||||||
/// returning an error. This doesn't necessarily mean a closure _has_ to panic, and its absence
|
|
||||||
/// doesn't imply a closure _can't_ panic, but this is a useful utility function for writing tests,
|
|
||||||
/// to avoid the necessity of making custom error types.
|
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use keyforkd::test_util::Panicable;
|
/// use keyforkd::test_util::Infallible;
|
||||||
/// let closure = || {
|
/// let closure = || {
|
||||||
/// Panicable::Ok(())
|
/// Infallible::Ok(())
|
||||||
/// };
|
/// };
|
||||||
/// assert!(closure().is_ok());
|
/// assert!(closure().is_ok());
|
||||||
/// ```
|
/// ```
|
||||||
pub type Panicable<T> = std::result::Result<T, UninstantiableError>;
|
pub type Infallible<T> = std::result::Result<T, InfallibleError>;
|
||||||
|
|
||||||
/// Run a test making use of a Keyforkd server. The test may use a seed (the first argument) from a
|
/// Run a test making use of a Keyforkd server. The test may use a seed (the first argument) from a
|
||||||
/// test suite, or (as shown in the example below) a simple seed may be used solely to ensure
|
/// test suite, or (as shown in the example below) a simple seed may be used solely to ensure
|
||||||
|
@ -40,8 +39,6 @@ pub type Panicable<T> = std::result::Result<T, UninstantiableError>;
|
||||||
/// runtime.
|
/// runtime.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// The test utility provides a socket that can be connected to for deriving keys.
|
|
||||||
///
|
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::os::unix::net::UnixStream;
|
/// use std::os::unix::net::UnixStream;
|
||||||
/// let seed = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// let seed = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
@ -49,18 +46,6 @@ pub type Panicable<T> = std::result::Result<T, UninstantiableError>;
|
||||||
/// UnixStream::connect(&path).map(|_| ())
|
/// UnixStream::connect(&path).map(|_| ())
|
||||||
/// }).unwrap();
|
/// }).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// The `keyforkd-client` crate uses the `KEYFORKD_SOCKET_PATH` variable to determine the default
|
|
||||||
/// socket path. The test will export the environment variable so it may be used by default.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use std::os::unix::net::UnixStream;
|
|
||||||
/// let seed = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
||||||
/// keyforkd::test_util::run_test(seed.as_slice(), |path| {
|
|
||||||
/// assert_eq!(std::env::var_os("KEYFORKD_SOCKET_PATH").unwrap(), path.as_os_str());
|
|
||||||
/// UnixStream::connect(&path).map(|_| ())
|
|
||||||
/// }).unwrap();
|
|
||||||
/// ```
|
|
||||||
#[allow(clippy::missing_errors_doc)]
|
#[allow(clippy::missing_errors_doc)]
|
||||||
pub fn run_test<F, E>(seed: &[u8], closure: F) -> Result<(), E>
|
pub fn run_test<F, E>(seed: &[u8], closure: F) -> Result<(), E>
|
||||||
where
|
where
|
||||||
|
@ -97,7 +82,6 @@ where
|
||||||
rx.recv()
|
rx.recv()
|
||||||
.await
|
.await
|
||||||
.expect(bug!("can't receive server start signal from channel"));
|
.expect(bug!("can't receive server start signal from channel"));
|
||||||
std::env::set_var("KEYFORKD_SOCKET_PATH", &socket_path);
|
|
||||||
let test_handle = tokio::task::spawn_blocking(move || closure(&socket_path));
|
let test_handle = tokio::task::spawn_blocking(move || closure(&socket_path));
|
||||||
|
|
||||||
let result = test_handle.await;
|
let result = test_handle.await;
|
||||||
|
@ -105,8 +89,8 @@ where
|
||||||
result
|
result
|
||||||
});
|
});
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
if let Ok(reason) = e.try_into_panic() {
|
if e.is_panic() {
|
||||||
std::panic::resume_unwind(reason);
|
std::panic::resume_unwind(e.into_panic());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -119,6 +103,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_run_test() {
|
fn test_run_test() {
|
||||||
let seed = b"beefbeef";
|
let seed = b"beefbeef";
|
||||||
run_test(seed, |_path| Panicable::Ok(())).expect("infallible");
|
run_test(seed, |_path| Infallible::Ok(())).expect("infallible");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,4 +16,3 @@ ed25519-dalek = "2.0.0"
|
||||||
sequoia-openpgp = { version = "1.17.0", default-features = false }
|
sequoia-openpgp = { version = "1.17.0", default-features = false }
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
thiserror = "1.0.49"
|
thiserror = "1.0.49"
|
||||||
keyfork-derive-path-data = { version = "0.1.1", path = "../keyfork-derive-path-data" }
|
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
use std::{env, process::ExitCode, str::FromStr};
|
use std::{env, process::ExitCode, str::FromStr};
|
||||||
|
|
||||||
use keyfork_derive_util::DerivationPath;
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyfork_derive_path_data::paths;
|
|
||||||
use keyforkd_client::Client;
|
use keyforkd_client::Client;
|
||||||
|
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
|
@ -79,14 +78,16 @@ fn validate(
|
||||||
subkey_format: &str,
|
subkey_format: &str,
|
||||||
default_userid: &str,
|
default_userid: &str,
|
||||||
) -> Result<(DerivationPath, Vec<KeyType>, UserID), Box<dyn std::error::Error>> {
|
) -> Result<(DerivationPath, Vec<KeyType>, UserID), Box<dyn std::error::Error>> {
|
||||||
let index = paths::OPENPGP.inner().first().unwrap();
|
let mut pgp_u32 = [0u8; 4];
|
||||||
|
pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::<Vec<u8>>());
|
||||||
|
let index = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
|
||||||
|
|
||||||
let path = DerivationPath::from_str(path)?;
|
let path = DerivationPath::from_str(path)?;
|
||||||
assert!(path.len() >= 2, "Expected path of at least m/{index}/account_id'");
|
assert!(path.len() >= 2, "Expected path of at least m/{index}/account_id'");
|
||||||
|
|
||||||
let given_index = path.iter().next().expect("checked .len() above");
|
let given_index = path.iter().next().expect("checked .len() above");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
index, given_index,
|
&index, given_index,
|
||||||
"Expected derivation path starting with m/{index}, got: {given_index}",
|
"Expected derivation path starting with m/{index}, got: {given_index}",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
||||||
|
|
||||||
for packet in cert.into_packets2() {
|
for packet in cert.into_packets() {
|
||||||
packet.serialize(&mut w)?;
|
packet.serialize(&mut w)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,3 @@ license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyfork-derive-util = { version = "0.2.0", path = "../keyfork-derive-util", default-features = false, registry = "distrust" }
|
keyfork-derive-util = { version = "0.2.0", path = "../keyfork-derive-util", default-features = false, registry = "distrust" }
|
||||||
once_cell = "1.19.0"
|
|
||||||
|
|
|
@ -2,128 +2,32 @@
|
||||||
|
|
||||||
#![allow(clippy::unreadable_literal)]
|
#![allow(clippy::unreadable_literal)]
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
|
|
||||||
pub mod paths {
|
/// The default derivation path for OpenPGP.
|
||||||
use super::*;
|
pub static OPENPGP: DerivationIndex = DerivationIndex::new_unchecked(7366512, true);
|
||||||
|
|
||||||
/// The default derivation path for OpenPGP.
|
|
||||||
pub static OPENPGP: Lazy<DerivationPath> = Lazy::new(|| {
|
|
||||||
DerivationPath::default().chain_push(DerivationIndex::new_unchecked(
|
|
||||||
u32::from_be_bytes(*b"\x00pgp"),
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The derivation path for OpenPGP certificates used for sharding.
|
|
||||||
pub static OPENPGP_SHARD: Lazy<DerivationPath> = Lazy::new(|| {
|
|
||||||
DerivationPath::default()
|
|
||||||
.chain_push(DerivationIndex::new_unchecked(
|
|
||||||
u32::from_be_bytes(*b"\x00pgp"),
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
.chain_push(DerivationIndex::new_unchecked(
|
|
||||||
u32::from_be_bytes(*b"shrd"),
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The derivation path for OpenPGP certificates used for disaster recovery.
|
|
||||||
pub static OPENPGP_DISASTER_RECOVERY: Lazy<DerivationPath> = Lazy::new(|| {
|
|
||||||
DerivationPath::default()
|
|
||||||
.chain_push(DerivationIndex::new_unchecked(
|
|
||||||
u32::from_be_bytes(*b"\x00pgp"),
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
.chain_push(DerivationIndex::new_unchecked(
|
|
||||||
u32::from_be_bytes(*b"\x00\x00dr"),
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine if a prefix matches and whether the next index exists.
|
|
||||||
fn prefix_matches(given: &DerivationPath, target: &DerivationPath) -> Option<DerivationIndex> {
|
|
||||||
if given.len() <= target.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if target
|
|
||||||
.iter()
|
|
||||||
.zip(given.iter())
|
|
||||||
.all(|(left, right)| left == right)
|
|
||||||
{
|
|
||||||
given.iter().nth(target.len()).cloned()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A derivation target.
|
/// A derivation target.
|
||||||
#[derive(Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum Target {
|
pub enum Target {
|
||||||
/// An OpenPGP key, whose account is the given index.
|
/// An OpenPGP key, whose account is the given index.
|
||||||
OpenPGP(DerivationIndex),
|
OpenPGP(DerivationIndex),
|
||||||
|
|
||||||
/// An OpenPGP key used for sharding.
|
|
||||||
OpenPGPShard(DerivationIndex),
|
|
||||||
|
|
||||||
/// An OpenPGP key used for disaster recovery.
|
|
||||||
OpenPGPDisasterRecovery(DerivationIndex),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Target {
|
impl std::fmt::Display for Target {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Target::OpenPGP(account) => {
|
Self::OpenPGP(account) => {
|
||||||
write!(f, "OpenPGP key (account {account})")
|
write!(f, "OpenPGP key (account {account})")
|
||||||
}
|
}
|
||||||
Target::OpenPGPShard(shard_index) => {
|
|
||||||
write!(f, "OpenPGP Shard key (shard index {shard_index})")
|
|
||||||
}
|
|
||||||
Target::OpenPGPDisasterRecovery(account) => {
|
|
||||||
write!(f, "OpenPGP Disaster Recovery key (account {account})")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! test_match {
|
|
||||||
($var:ident, $shard:path, $target:path) => {
|
|
||||||
if let Some(index) = prefix_matches($var, &$shard) {
|
|
||||||
return Some($target(index));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the closest [`Target`] for the given path. This method is intended to be used by
|
/// Determine the closest [`Target`] for the given path. This method is intended to be used by
|
||||||
/// `keyforkd` to provide an optional textual prompt to what a client is attempting to derive.
|
/// `keyforkd` to provide an optional textual prompt to what a client is attempting to derive.
|
||||||
pub fn guess_target(path: &DerivationPath) -> Option<Target> {
|
pub fn guess_target(path: &DerivationPath) -> Option<Target> {
|
||||||
test_match!(path, paths::OPENPGP_SHARD, Target::OpenPGPShard);
|
Some(match path.iter().collect::<Vec<_>>()[..] {
|
||||||
test_match!(
|
[t, index] if t == &OPENPGP => Target::OpenPGP(index.clone()),
|
||||||
path,
|
_ => return None,
|
||||||
paths::OPENPGP_DISASTER_RECOVERY,
|
})
|
||||||
Target::OpenPGPDisasterRecovery
|
|
||||||
);
|
|
||||||
test_match!(path, paths::OPENPGP, Target::OpenPGP);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let index = DerivationIndex::new(5312, false).unwrap();
|
|
||||||
let dr_key = paths::OPENPGP_DISASTER_RECOVERY
|
|
||||||
.clone()
|
|
||||||
.chain_push(index.clone());
|
|
||||||
match guess_target(&dr_key) {
|
|
||||||
Some(Target::OpenPGPDisasterRecovery(idx)) if idx == index => (),
|
|
||||||
bad => panic!("invalid value: {bad:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ secp256k1 = ["k256"]
|
||||||
ed25519 = ["ed25519-dalek"]
|
ed25519 = ["ed25519-dalek"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyfork-mnemonic = { version = "0.3.0", path = "../../util/keyfork-mnemonic", registry = "distrust" }
|
keyfork-mnemonic-util = { version = "0.3.0", path = "../../util/keyfork-mnemonic-util", registry = "distrust" }
|
||||||
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug", registry = "distrust" }
|
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug", registry = "distrust" }
|
||||||
|
|
||||||
# Included in Rust
|
# Included in Rust
|
||||||
|
|
|
@ -23,7 +23,7 @@ performed directly on a master seed. This is how Keyforkd works internally.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use keyfork_mnemonic::Mnemonic;
|
use keyfork_mnemonic_util::Mnemonic;
|
||||||
use keyfork_derive_util::{*, request::*};
|
use keyfork_derive_util::{*, request::*};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use std::str::FromStr;
|
//! use std::str::FromStr;
|
||||||
//! use keyfork_mnemonic::Mnemonic;
|
//! use keyfork_mnemonic_util::Mnemonic;
|
||||||
//! use keyfork_derive_util::{*, request::*};
|
//! use keyfork_derive_util::{*, request::*};
|
||||||
//! use k256::SecretKey;
|
//! use k256::SecretKey;
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::{
|
||||||
DerivationPath, ExtendedPrivateKey,
|
DerivationPath, ExtendedPrivateKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
use keyfork_mnemonic::{Mnemonic, MnemonicGenerationError};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// An error encountered while deriving a key.
|
/// An error encountered while deriving a key.
|
||||||
|
@ -194,8 +194,8 @@ impl DerivationRequest {
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let mnemonic: keyfork_mnemonic::Mnemonic = //
|
/// let mnemonic: keyfork_mnemonic_util::Mnemonic = //
|
||||||
/// # keyfork_mnemonic::Mnemonic::from_entropy(
|
/// # keyfork_mnemonic_util::Mnemonic::from_entropy(
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
/// # )?;
|
/// # )?;
|
||||||
/// let algo: DerivationAlgorithm = //
|
/// let algo: DerivationAlgorithm = //
|
||||||
|
|
|
@ -23,7 +23,7 @@ sharks = "0.5.0"
|
||||||
thiserror = "1.0.50"
|
thiserror = "1.0.50"
|
||||||
|
|
||||||
# Remote operator mode
|
# Remote operator mode
|
||||||
keyfork-mnemonic = { version = "0.3.0", path = "../util/keyfork-mnemonic", registry = "distrust" }
|
keyfork-mnemonic-util = { version = "0.3.0", path = "../util/keyfork-mnemonic-util", registry = "distrust" }
|
||||||
x25519-dalek = { version = "2.0.0", features = ["getrandom"] }
|
x25519-dalek = { version = "2.0.0", features = ["getrandom"] }
|
||||||
aes-gcm = { version = "0.10.3", features = ["std"] }
|
aes-gcm = { version = "0.10.3", features = ["std"] }
|
||||||
hkdf = { version = "0.12.4", features = ["std"] }
|
hkdf = { version = "0.12.4", features = ["std"] }
|
||||||
|
|
|
@ -13,7 +13,7 @@ use aes_gcm::{
|
||||||
use base64::prelude::{Engine, BASE64_STANDARD};
|
use base64::prelude::{Engine, BASE64_STANDARD};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use keyfork_bug::{bug, POISONED_MUTEX};
|
use keyfork_bug::{bug, POISONED_MUTEX};
|
||||||
use keyfork_mnemonic::{English, Mnemonic};
|
use keyfork_mnemonic_util::{English, Mnemonic};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{
|
validators::{
|
||||||
mnemonic::{MnemonicSetValidator, MnemonicValidator, WordLength},
|
mnemonic::{MnemonicSetValidator, MnemonicValidator, WordLength},
|
||||||
|
@ -249,7 +249,7 @@ pub trait Format {
|
||||||
|
|
||||||
// create our shared key
|
// create our shared key
|
||||||
let our_key = EphemeralSecret::random();
|
let our_key = EphemeralSecret::random();
|
||||||
let our_pubkey_mnemonic = Mnemonic::try_from_slice(PublicKey::from(&our_key).as_bytes())?;
|
let our_pubkey_mnemonic = Mnemonic::from_bytes(PublicKey::from(&our_key).as_bytes())?;
|
||||||
let shared_secret = our_key.diffie_hellman(&PublicKey::from(their_pubkey));
|
let shared_secret = our_key.diffie_hellman(&PublicKey::from(their_pubkey));
|
||||||
assert!(
|
assert!(
|
||||||
shared_secret.was_contributory(),
|
shared_secret.was_contributory(),
|
||||||
|
@ -302,7 +302,7 @@ pub trait Format {
|
||||||
let mut mnemonic_bytes = [0u8; ENCRYPTED_LENGTH as usize];
|
let mut mnemonic_bytes = [0u8; ENCRYPTED_LENGTH as usize];
|
||||||
mnemonic_bytes.copy_from_slice(&encrypted_bytes);
|
mnemonic_bytes.copy_from_slice(&encrypted_bytes);
|
||||||
|
|
||||||
let payload_mnemonic = Mnemonic::from_array(mnemonic_bytes);
|
let payload_mnemonic = Mnemonic::from_nonstandard_bytes(mnemonic_bytes);
|
||||||
|
|
||||||
#[cfg(feature = "qrcode")]
|
#[cfg(feature = "qrcode")]
|
||||||
{
|
{
|
||||||
|
@ -439,7 +439,7 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
||||||
while iter_count.is_none() || iter_count.is_some_and(|i| i > 0) {
|
while iter_count.is_none() || iter_count.is_some_and(|i| i > 0) {
|
||||||
iter += 1;
|
iter += 1;
|
||||||
let our_key = EphemeralSecret::random();
|
let our_key = EphemeralSecret::random();
|
||||||
let key_mnemonic = Mnemonic::try_from_slice(PublicKey::from(&our_key).as_bytes())?;
|
let key_mnemonic = Mnemonic::from_bytes(PublicKey::from(&our_key).as_bytes())?;
|
||||||
|
|
||||||
#[cfg(feature = "qrcode")]
|
#[cfg(feature = "qrcode")]
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,7 +29,7 @@ keyforkd-client = { version = "0.2.0", path = "../daemon/keyforkd-client", defau
|
||||||
keyfork-derive-openpgp = { version = "0.1.1", path = "../derive/keyfork-derive-openpgp", registry = "distrust" }
|
keyfork-derive-openpgp = { version = "0.1.1", path = "../derive/keyfork-derive-openpgp", registry = "distrust" }
|
||||||
keyfork-derive-util = { version = "0.2.0", path = "../derive/keyfork-derive-util", default-features = false, features = ["ed25519"], registry = "distrust" }
|
keyfork-derive-util = { version = "0.2.0", path = "../derive/keyfork-derive-util", default-features = false, features = ["ed25519"], registry = "distrust" }
|
||||||
keyfork-entropy = { version = "0.1.0", path = "../util/keyfork-entropy", registry = "distrust" }
|
keyfork-entropy = { version = "0.1.0", path = "../util/keyfork-entropy", registry = "distrust" }
|
||||||
keyfork-mnemonic = { version = "0.3.0", path = "../util/keyfork-mnemonic", registry = "distrust" }
|
keyfork-mnemonic-util = { version = "0.3.0", path = "../util/keyfork-mnemonic-util", registry = "distrust" }
|
||||||
keyfork-prompt = { version = "0.1.0", path = "../util/keyfork-prompt", registry = "distrust" }
|
keyfork-prompt = { version = "0.1.0", path = "../util/keyfork-prompt", registry = "distrust" }
|
||||||
keyfork-qrcode = { version = "0.1.0", path = "../qrcode/keyfork-qrcode", default-features = false, registry = "distrust" }
|
keyfork-qrcode = { version = "0.1.0", path = "../qrcode/keyfork-qrcode", default-features = false, registry = "distrust" }
|
||||||
keyfork-shard = { version = "0.2.0", path = "../keyfork-shard", default-features = false, features = ["openpgp", "openpgp-card", "qrcode"], registry = "distrust" }
|
keyfork-shard = { version = "0.2.0", path = "../keyfork-shard", default-features = false, features = ["openpgp", "openpgp-card", "qrcode"], registry = "distrust" }
|
||||||
|
@ -44,4 +44,3 @@ openpgp-card-sequoia = { version = "0.2.0", default-features = false }
|
||||||
openpgp-card = "0.4.1"
|
openpgp-card = "0.4.1"
|
||||||
clap_complete = { version = "4.4.6", optional = true }
|
clap_complete = { version = "4.4.6", optional = true }
|
||||||
sequoia-openpgp = { version = "1.17.0", default-features = false, features = ["compression"] }
|
sequoia-openpgp = { version = "1.17.0", default-features = false, features = ["compression"] }
|
||||||
keyfork-derive-path-data = { version = "0.1.1", path = "../derive/keyfork-derive-path-data" }
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
openpgp::{
|
openpgp::{
|
||||||
|
@ -10,8 +10,7 @@ use keyfork_derive_openpgp::{
|
||||||
},
|
},
|
||||||
XPrvKey,
|
XPrvKey,
|
||||||
};
|
};
|
||||||
use keyfork_derive_util::DerivationIndex;
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyfork_derive_path_data::paths;
|
|
||||||
use keyforkd_client::Client;
|
use keyforkd_client::Client;
|
||||||
|
|
||||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
@ -28,26 +27,22 @@ pub enum DeriveSubcommands {
|
||||||
/// It is recommended to use the default expiration of one day and to change the expiration
|
/// It is recommended to use the default expiration of one day and to change the expiration
|
||||||
/// using an external utility, to ensure the Certify key is usable.
|
/// using an external utility, to ensure the Certify key is usable.
|
||||||
#[command(name = "openpgp")]
|
#[command(name = "openpgp")]
|
||||||
OpenPGP(OpenPGP)
|
OpenPGP {
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args, Clone, Debug)]
|
|
||||||
pub struct OpenPGP {
|
|
||||||
/// Default User ID for the certificate, using the OpenPGP User ID format.
|
/// Default User ID for the certificate, using the OpenPGP User ID format.
|
||||||
user_id: String,
|
user_id: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeriveSubcommands {
|
impl DeriveSubcommands {
|
||||||
fn handle(&self, account: DerivationIndex) -> Result<()> {
|
fn handle(&self, account: DerivationIndex) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
DeriveSubcommands::OpenPGP(opgp) => opgp.handle(account),
|
DeriveSubcommands::OpenPGP { user_id } => {
|
||||||
}
|
let mut pgp_u32 = [0u8; 4];
|
||||||
}
|
pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::<Vec<u8>>());
|
||||||
}
|
let chain = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
|
||||||
|
let path = DerivationPath::default()
|
||||||
impl OpenPGP {
|
.chain_push(chain)
|
||||||
pub fn handle(&self, account: DerivationIndex) -> Result<()> {
|
.chain_push(account);
|
||||||
let path = paths::OPENPGP.clone().chain_push(account);
|
|
||||||
// TODO: should this be customizable?
|
// TODO: should this be customizable?
|
||||||
let subkeys = vec![
|
let subkeys = vec![
|
||||||
KeyFlags::empty().set_certification(),
|
KeyFlags::empty().set_certification(),
|
||||||
|
@ -58,16 +53,19 @@ impl OpenPGP {
|
||||||
KeyFlags::empty().set_authentication(),
|
KeyFlags::empty().set_authentication(),
|
||||||
];
|
];
|
||||||
let xprv = Client::discover_socket()?.request_xprv::<XPrvKey>(&path)?;
|
let xprv = Client::discover_socket()?.request_xprv::<XPrvKey>(&path)?;
|
||||||
let default_userid = UserID::from(self.user_id.as_str());
|
let default_userid = UserID::from(user_id.as_str());
|
||||||
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &default_userid)?;
|
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &default_userid)?;
|
||||||
|
|
||||||
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
||||||
|
|
||||||
for packet in cert.into_packets2() {
|
for packet in cert.into_packets() {
|
||||||
packet.serialize(&mut w)?;
|
packet.serialize(&mut w)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
w.finalize()?;
|
w.finalize()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ impl MnemonicSeedSource {
|
||||||
MnemonicSeedSource::Tarot => todo!(),
|
MnemonicSeedSource::Tarot => todo!(),
|
||||||
MnemonicSeedSource::Dice => todo!(),
|
MnemonicSeedSource::Dice => todo!(),
|
||||||
};
|
};
|
||||||
let mnemonic = keyfork_mnemonic::Mnemonic::try_from_slice(&seed)?;
|
let mnemonic = keyfork_mnemonic_util::Mnemonic::from_bytes(&seed)?;
|
||||||
Ok(mnemonic.to_string())
|
Ok(mnemonic.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::Keyfork;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use keyfork_mnemonic::{English, Mnemonic};
|
use keyfork_mnemonic_util::{English, Mnemonic};
|
||||||
use keyfork_prompt::{default_terminal, DefaultTerminal};
|
use keyfork_prompt::{default_terminal, DefaultTerminal};
|
||||||
use keyfork_shard::{remote_decrypt, Format};
|
use keyfork_shard::{remote_decrypt, Format};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ pub struct Recover {
|
||||||
impl Recover {
|
impl Recover {
|
||||||
pub fn handle(&self, _k: &Keyfork) -> Result<()> {
|
pub fn handle(&self, _k: &Keyfork) -> Result<()> {
|
||||||
let seed = self.command.handle()?;
|
let seed = self.command.handle()?;
|
||||||
let mnemonic = Mnemonic::try_from_slice(&seed)?;
|
let mnemonic = Mnemonic::from_bytes(&seed)?;
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use std::{collections::HashSet, fs::File, io::IsTerminal, path::PathBuf};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fs::File,
|
||||||
|
io::IsTerminal,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
use card_backend_pcsc::PcscBackend;
|
use card_backend_pcsc::PcscBackend;
|
||||||
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
openpgp::{
|
openpgp::{self, packet::UserID, types::KeyFlags, Cert, serialize::Marshal, armor::{Writer, Kind}},
|
||||||
self,
|
|
||||||
armor::{Kind, Writer},
|
|
||||||
packet::UserID,
|
|
||||||
serialize::Marshal,
|
|
||||||
types::KeyFlags,
|
|
||||||
Cert,
|
|
||||||
},
|
|
||||||
XPrv,
|
XPrv,
|
||||||
};
|
};
|
||||||
use keyfork_derive_path_data::paths;
|
use keyfork_derive_util::{DerivationIndex, DerivationPath, VariableLengthSeed};
|
||||||
use keyfork_derive_util::DerivationIndex;
|
|
||||||
use keyfork_mnemonic::Mnemonic;
|
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
default_terminal,
|
default_terminal,
|
||||||
validators::{SecurePinValidator, Validator},
|
validators::{SecurePinValidator, Validator},
|
||||||
DefaultTerminal, Message, PromptHandler,
|
DefaultTerminal, Message, PromptHandler,
|
||||||
};
|
};
|
||||||
|
use keyfork_mnemonic_util::Mnemonic;
|
||||||
|
|
||||||
use keyfork_shard::{openpgp::OpenPGP, Format};
|
use keyfork_shard::{openpgp::OpenPGP, Format};
|
||||||
|
|
||||||
|
@ -45,8 +42,17 @@ fn derive_key(seed: [u8; 32], index: u8) -> Result<Cert> {
|
||||||
KeyFlags::empty().set_authentication(),
|
KeyFlags::empty().set_authentication(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let mut pgp_u32 = [0u8; 4];
|
||||||
|
pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::<Vec<u8>>());
|
||||||
|
let chain = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
|
||||||
|
let mut shrd_u32 = [0u8; 4];
|
||||||
|
shrd_u32[..].copy_from_slice(&"shrd".bytes().collect::<Vec<u8>>());
|
||||||
|
let account = DerivationIndex::new(u32::from_be_bytes(shrd_u32), true)?;
|
||||||
let subkey = DerivationIndex::new(u32::from(index), true)?;
|
let subkey = DerivationIndex::new(u32::from(index), true)?;
|
||||||
let path = paths::OPENPGP_SHARD.clone().chain_push(subkey);
|
let path = DerivationPath::default()
|
||||||
|
.chain_push(chain)
|
||||||
|
.chain_push(account)
|
||||||
|
.chain_push(subkey);
|
||||||
let xprv = XPrv::new(seed)
|
let xprv = XPrv::new(seed)
|
||||||
.expect("could not construct master key from seed")
|
.expect("could not construct master key from seed")
|
||||||
.derive_path(&path)?;
|
.derive_path(&path)?;
|
||||||
|
@ -103,82 +109,18 @@ fn factory_reset_current_card(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Clone, Debug)]
|
fn generate_shard_secret(
|
||||||
pub enum WizardSubcommands {
|
|
||||||
GenerateShardSecret(GenerateShardSecret),
|
|
||||||
BottomsUp(BottomsUp),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a 256 bit secret and shard the secret to smart cards.
|
|
||||||
///
|
|
||||||
/// Smart cards will need to be plugged in periodically during the wizard, where they will be
|
|
||||||
/// factory reset and provisioned to `m/pgp'/shrd'/<share index>`. The secret can then be recovered
|
|
||||||
/// with `keyfork recover shard` or `keyfork recover remote-shard`. The share file will be printed
|
|
||||||
/// to standard output.
|
|
||||||
#[derive(Args, Clone, Debug)]
|
|
||||||
pub struct GenerateShardSecret {
|
|
||||||
/// The minimum amount of keys required to decrypt the secret.
|
|
||||||
#[arg(long)]
|
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
|
|
||||||
/// The maximum amount of shards.
|
|
||||||
#[arg(long)]
|
|
||||||
max: u8,
|
max: u8,
|
||||||
|
|
||||||
/// The amount of smart cards to provision per-shard.
|
|
||||||
#[arg(long, default_value = "1")]
|
|
||||||
keys_per_shard: u8,
|
keys_per_shard: u8,
|
||||||
|
output_file: &Option<PathBuf>,
|
||||||
/// The file to write the generated shard file to.
|
) -> Result<()> {
|
||||||
#[arg(long)]
|
|
||||||
output: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates,
|
|
||||||
/// deriving the default OpenPGP certificate for the secret.
|
|
||||||
///
|
|
||||||
/// This command was purpose-built for DEFCON and is not intended to be used normally, as it
|
|
||||||
/// implies keys used for sharding have been generated by a custom source.
|
|
||||||
#[derive(Args, Clone, Debug)]
|
|
||||||
pub struct BottomsUp {
|
|
||||||
/// The location of OpenPGP certificates to use when sharding.
|
|
||||||
key_discovery: PathBuf,
|
|
||||||
|
|
||||||
/// The minimum amount of keys required to decrypt the secret.
|
|
||||||
#[arg(long)]
|
|
||||||
threshold: u8,
|
|
||||||
|
|
||||||
/// The file to write the generated shard file to.
|
|
||||||
#[arg(long)]
|
|
||||||
output_shardfile: PathBuf,
|
|
||||||
|
|
||||||
/// The file to write the generated OpenPGP certificate to.
|
|
||||||
#[arg(long)]
|
|
||||||
output_cert: PathBuf,
|
|
||||||
|
|
||||||
/// The User ID for the generated OpenPGP certificate.
|
|
||||||
#[arg(long, default_value = "Disaster Recovery")]
|
|
||||||
user_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WizardSubcommands {
|
|
||||||
// dispatch
|
|
||||||
fn handle(&self) -> Result<()> {
|
|
||||||
match self {
|
|
||||||
WizardSubcommands::GenerateShardSecret(gss) => gss.handle(),
|
|
||||||
WizardSubcommands::BottomsUp(bu) => bu.handle(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenerateShardSecret {
|
|
||||||
fn handle(&self) -> Result<()> {
|
|
||||||
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
||||||
let mut pm = default_terminal()?;
|
let mut pm = default_terminal()?;
|
||||||
let mut certs = vec![];
|
let mut certs = vec![];
|
||||||
let mut seen_cards: HashSet<String> = HashSet::new();
|
let mut seen_cards: HashSet<String> = HashSet::new();
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
if self.output.is_none() {
|
if output_file.is_none() {
|
||||||
assert!(
|
assert!(
|
||||||
!stdout.is_terminal(),
|
!stdout.is_terminal(),
|
||||||
"not printing shard to terminal, redirect output"
|
"not printing shard to terminal, redirect output"
|
||||||
|
@ -196,9 +138,9 @@ impl GenerateShardSecret {
|
||||||
}
|
}
|
||||||
.to_fn();
|
.to_fn();
|
||||||
|
|
||||||
for index in 0..self.max {
|
for index in 0..max {
|
||||||
let cert = derive_key(seed, index)?;
|
let cert = derive_key(seed, index)?;
|
||||||
for i in 0..self.keys_per_shard {
|
for i in 0..keys_per_shard {
|
||||||
pm.prompt_message(Message::Text(format!(
|
pm.prompt_message(Message::Text(format!(
|
||||||
"Please remove all keys and insert key #{} for user #{}",
|
"Please remove all keys and insert key #{} for user #{}",
|
||||||
(i as u16) + 1,
|
(i as u16) + 1,
|
||||||
|
@ -236,12 +178,12 @@ impl GenerateShardSecret {
|
||||||
|
|
||||||
let opgp = OpenPGP::<DefaultTerminal>::new();
|
let opgp = OpenPGP::<DefaultTerminal>::new();
|
||||||
|
|
||||||
if let Some(output_file) = self.output.as_ref() {
|
if let Some(output_file) = output_file {
|
||||||
let output = File::create(output_file)?;
|
let output = File::create(output_file)?;
|
||||||
opgp.shard_and_encrypt(self.threshold, certs.len() as u8, &seed, &certs[..], output)?;
|
opgp.shard_and_encrypt(threshold, certs.len() as u8, &seed, &certs[..], output)?;
|
||||||
} else {
|
} else {
|
||||||
opgp.shard_and_encrypt(
|
opgp.shard_and_encrypt(
|
||||||
self.threshold,
|
threshold,
|
||||||
certs.len() as u8,
|
certs.len() as u8,
|
||||||
&seed,
|
&seed,
|
||||||
&certs[..],
|
&certs[..],
|
||||||
|
@ -249,19 +191,19 @@ impl GenerateShardSecret {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BottomsUp {
|
fn bottoms_up(key_discovery: &Path, threshold: u8, output_shardfile: &Path, output_cert: &Path, user_id: &str,) -> Result<()> {
|
||||||
fn handle(&self) -> Result<()> {
|
|
||||||
let entropy = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
let entropy = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
||||||
let mnemonic = Mnemonic::from_array(entropy);
|
let mnemonic = Mnemonic::from_nonstandard_bytes(entropy);
|
||||||
|
// TODO: make this return const size, since is hash based
|
||||||
let seed = mnemonic.generate_seed(None);
|
let seed = mnemonic.generate_seed(None);
|
||||||
|
|
||||||
// TODO: should this allow for customizing the account index from 0? Potential for key reuse
|
// TODO: should this allow for customizing the account index from 0? Potential for key reuse
|
||||||
// errors.
|
// errors.
|
||||||
let path = paths::OPENPGP_DISASTER_RECOVERY
|
let path = DerivationPath::default()
|
||||||
.clone()
|
.chain_push(DerivationIndex::new(u32::from_be_bytes(*b"\x00pgp"), true)?)
|
||||||
|
.chain_push(DerivationIndex::new(u32::from_be_bytes(*b"\x00\x00dr"), true)?)
|
||||||
.chain_push(DerivationIndex::new(0, true)?);
|
.chain_push(DerivationIndex::new(0, true)?);
|
||||||
let subkeys = [
|
let subkeys = [
|
||||||
KeyFlags::empty().set_certification(),
|
KeyFlags::empty().set_certification(),
|
||||||
|
@ -271,29 +213,95 @@ impl BottomsUp {
|
||||||
.set_storage_encryption(),
|
.set_storage_encryption(),
|
||||||
KeyFlags::empty().set_authentication(),
|
KeyFlags::empty().set_authentication(),
|
||||||
];
|
];
|
||||||
let xprv = XPrv::new(seed)
|
let xprv = XPrv::new(VariableLengthSeed::new(&seed))
|
||||||
.expect("could not construct master key from seed")
|
.expect("could not construct master key from seed")
|
||||||
.derive_path(&path)?;
|
.derive_path(&path)?;
|
||||||
let userid = UserID::from(self.user_id.as_str());
|
let userid = UserID::from(user_id);
|
||||||
|
|
||||||
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
|
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
|
||||||
let certfile = File::create(&self.output_cert)?;
|
let certfile = File::create(output_cert)?;
|
||||||
let mut w = Writer::new(certfile, Kind::PublicKey)?;
|
let mut w = Writer::new(certfile, Kind::PublicKey)?;
|
||||||
cert.serialize(&mut w)?;
|
cert.serialize(&mut w)?;
|
||||||
w.finalize()?;
|
w.finalize()?;
|
||||||
|
|
||||||
let opgp = OpenPGP::<DefaultTerminal>::new();
|
let opgp = OpenPGP::<DefaultTerminal>::new();
|
||||||
let certs = OpenPGP::<DefaultTerminal>::discover_certs(&self.key_discovery)?;
|
let certs = OpenPGP::<DefaultTerminal>::discover_certs(key_discovery)?;
|
||||||
|
|
||||||
|
let shardfile = File::create(output_shardfile)?;
|
||||||
|
opgp.shard_and_encrypt(threshold, certs.len() as u8, &entropy, &certs[..], shardfile)?;
|
||||||
|
|
||||||
let shardfile = File::create(&self.output_shardfile)?;
|
|
||||||
opgp.shard_and_encrypt(
|
|
||||||
self.threshold,
|
|
||||||
certs.len() as u8,
|
|
||||||
&entropy,
|
|
||||||
&certs[..],
|
|
||||||
shardfile,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Clone, Debug)]
|
||||||
|
pub enum WizardSubcommands {
|
||||||
|
/// Create a 256 bit secret and shard the secret to smart cards.
|
||||||
|
///
|
||||||
|
/// Smart cards will need to be plugged in periodically during the wizard, where they will be factory reset and
|
||||||
|
/// provisioned to `m/pgp'/shrd'/<share index>`. The secret can then be recovered with `keyfork recover shard` or
|
||||||
|
/// `keyfork recover remote-shard`. The share file will be printed to standard output.
|
||||||
|
GenerateShardSecret {
|
||||||
|
/// The minimum amount of keys required to decrypt the secret.
|
||||||
|
#[arg(long)]
|
||||||
|
threshold: u8,
|
||||||
|
|
||||||
|
/// The maximum amount of shards.
|
||||||
|
#[arg(long)]
|
||||||
|
max: u8,
|
||||||
|
|
||||||
|
/// The amount of smart cards to provision per-shard.
|
||||||
|
#[arg(long, default_value = "1")]
|
||||||
|
keys_per_shard: u8,
|
||||||
|
|
||||||
|
/// The file to write the generated shard file to.
|
||||||
|
#[arg(long)]
|
||||||
|
output: Option<PathBuf>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates,
|
||||||
|
/// deriving the default OpenPGP certificate for the secret.
|
||||||
|
///
|
||||||
|
/// This command was purpose-built for DEFCON and is not intended to be used normally, as it
|
||||||
|
/// implies keys used for sharding have been generated by a custom source.
|
||||||
|
BottomsUp {
|
||||||
|
/// The location of OpenPGP certificates to use when sharding.
|
||||||
|
key_discovery: PathBuf,
|
||||||
|
|
||||||
|
/// The minimum amount of keys required to decrypt the secret.
|
||||||
|
#[arg(long)]
|
||||||
|
threshold: u8,
|
||||||
|
|
||||||
|
/// The file to write the generated shard file to.
|
||||||
|
#[arg(long)]
|
||||||
|
output_shardfile: PathBuf,
|
||||||
|
|
||||||
|
/// The file to write the generated OpenPGP certificate to.
|
||||||
|
#[arg(long)]
|
||||||
|
output_cert: PathBuf,
|
||||||
|
|
||||||
|
/// The User ID for the generated OpenPGP certificate.
|
||||||
|
#[arg(long, default_value = "Disaster Recovery")]
|
||||||
|
user_id: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WizardSubcommands {
|
||||||
|
fn handle(&self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
WizardSubcommands::GenerateShardSecret {
|
||||||
|
threshold,
|
||||||
|
max,
|
||||||
|
keys_per_shard,
|
||||||
|
output,
|
||||||
|
} => generate_shard_secret(*threshold, *max, *keys_per_shard, output),
|
||||||
|
WizardSubcommands::BottomsUp {
|
||||||
|
key_discovery,
|
||||||
|
threshold,
|
||||||
|
output_shardfile,
|
||||||
|
output_cert,
|
||||||
|
user_id,
|
||||||
|
} => bottoms_up(key_discovery, *threshold, output_shardfile, output_cert, user_id),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyfork-mnemonic"
|
name = "keyfork-mnemonic-util"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
description = "Utilities to generate and manage seeds based on BIP-0039 mnemonics."
|
description = "Utilities to generate and manage seeds based on BIP-0039 mnemonics."
|
||||||
repository = "https://git.distrust.co/public/keyfork"
|
repository = "https://git.distrust.co/public/keyfork"
|
|
@ -1,6 +1,6 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use keyfork_mnemonic::Mnemonic;
|
use keyfork_mnemonic_util::Mnemonic;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let input = std::io::stdin();
|
let input = std::io::stdin();
|
|
@ -3,17 +3,17 @@
|
||||||
//! Mnemonics can be used to safely encode data of 32, 48, and 64 bytes as a phrase:
|
//! Mnemonics can be used to safely encode data of 32, 48, and 64 bytes as a phrase:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use keyfork_mnemonic::Mnemonic;
|
//! use keyfork_mnemonic_util::Mnemonic;
|
||||||
//! let data = b"Hello, world! I am a mnemonic :)";
|
//! let data = b"Hello, world! I am a mnemonic :)";
|
||||||
//! assert_eq!(data.len(), 32);
|
//! assert_eq!(data.len(), 32);
|
||||||
//! let mnemonic = Mnemonic::try_from_slice(data).unwrap();
|
//! let mnemonic = Mnemonic::from_bytes(data).unwrap();
|
||||||
//! println!("Our mnemonic is: {mnemonic}");
|
//! println!("Our mnemonic is: {mnemonic}");
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! A mnemonic can also be parsed from a string:
|
//! A mnemonic can also be parsed from a string:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use keyfork_mnemonic::Mnemonic;
|
//! use keyfork_mnemonic_util::Mnemonic;
|
||||||
//! use std::str::FromStr;
|
//! use std::str::FromStr;
|
||||||
//!
|
//!
|
||||||
//! let data = b"Hello, world! I am a mnemonic :)";
|
//! let data = b"Hello, world! I am a mnemonic :)";
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
//! verified to be safe:
|
//! verified to be safe:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use keyfork_mnemonic::Mnemonic;
|
//! use keyfork_mnemonic_util::Mnemonic;
|
||||||
//! let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
//! let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
//! let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice()) };
|
//! let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice()) };
|
||||||
//! let mnemonic_text = mnemonic.to_string();
|
//! let mnemonic_text = mnemonic.to_string();
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
//! If given an invalid length, undefined behavior may follow, or code may panic.
|
//! If given an invalid length, undefined behavior may follow, or code may panic.
|
||||||
//!
|
//!
|
||||||
//! ```rust,should_panic
|
//! ```rust,should_panic
|
||||||
//! use keyfork_mnemonic::Mnemonic;
|
//! use keyfork_mnemonic_util::Mnemonic;
|
||||||
//! use std::str::FromStr;
|
//! use std::str::FromStr;
|
||||||
//!
|
//!
|
||||||
//! // NOTE: Data is of invalid length, 31
|
//! // NOTE: Data is of invalid length, 31
|
||||||
|
@ -268,11 +268,11 @@ where
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use keyfork_mnemonic::Mnemonic;
|
/// use keyfork_mnemonic_util::Mnemonic;
|
||||||
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let mnemonic = Mnemonic::try_from_slice(data.as_slice()).unwrap();
|
/// let mnemonic = Mnemonic::from_bytes(data.as_slice()).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_from_slice(bytes: &[u8]) -> Result<MnemonicBase<W>, MnemonicGenerationError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<MnemonicBase<W>, MnemonicGenerationError> {
|
||||||
let bit_count = bytes.len() * 8;
|
let bit_count = bytes.len() * 8;
|
||||||
|
|
||||||
if bit_count % 32 != 0 {
|
if bit_count % 32 != 0 {
|
||||||
|
@ -290,23 +290,23 @@ where
|
||||||
/// of a factor of 4, up to 1024 bytes.
|
/// of a factor of 4, up to 1024 bytes.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use keyfork_mnemonic::Mnemonic;
|
/// use keyfork_mnemonic_util::Mnemonic;
|
||||||
/// let data = b"hello world!";
|
/// let data = b"hello world!";
|
||||||
/// let mnemonic = Mnemonic::from_array(*data);
|
/// let mnemonic = Mnemonic::from_nonstandard_bytes(*data);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// If an invalid size is requested, the code will fail to compile:
|
/// If an invalid size is requested, the code will fail to compile:
|
||||||
///
|
///
|
||||||
/// ```rust,compile_fail
|
/// ```rust,compile_fail
|
||||||
/// use keyfork_mnemonic::Mnemonic;
|
/// use keyfork_mnemonic_util::Mnemonic;
|
||||||
/// let mnemonic = Mnemonic::from_array([0u8; 53]);
|
/// let mnemonic = Mnemonic::from_nonstandard_bytes([0u8; 53]);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ```rust,compile_fail
|
/// ```rust,compile_fail
|
||||||
/// use keyfork_mnemonic::Mnemonic;
|
/// use keyfork_mnemonic_util::Mnemonic;
|
||||||
/// let mnemonic = Mnemonic::from_array([0u8; 1024 + 4]);
|
/// let mnemonic = Mnemonic::from_nonstandard_bytes([0u8; 1024 + 4]);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_array<const N: usize>(bytes: [u8; N]) -> MnemonicBase<W> {
|
pub fn from_nonstandard_bytes<const N: usize>(bytes: [u8; N]) -> MnemonicBase<W> {
|
||||||
#[allow(clippy::let_unit_value)]
|
#[allow(clippy::let_unit_value)]
|
||||||
{
|
{
|
||||||
let () = AssertValidMnemonicSize::<N>::OK_CHUNKS;
|
let () = AssertValidMnemonicSize::<N>::OK_CHUNKS;
|
||||||
|
@ -315,6 +315,16 @@ where
|
||||||
Self::from_raw_bytes(&bytes)
|
Self::from_raw_bytes(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data is expected to be
|
||||||
|
/// of 128, 192, or 256 bits, as per BIP-0039.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// An error may be returned if the data is not within the expected lengths.
|
||||||
|
#[deprecated = "use Mnemonic::from_bytes"]
|
||||||
|
pub fn from_entropy(bytes: &[u8]) -> Result<MnemonicBase<W>, MnemonicGenerationError> {
|
||||||
|
MnemonicBase::from_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a Mnemonic using an arbitrary length of given data. The length does not need to
|
/// Create a Mnemonic using an arbitrary length of given data. The length does not need to
|
||||||
/// conform to BIP-0039 standards, but should be a multiple of 32 bits or 4 bytes.
|
/// conform to BIP-0039 standards, but should be a multiple of 32 bits or 4 bytes.
|
||||||
///
|
///
|
||||||
|
@ -322,12 +332,12 @@ where
|
||||||
/// This function can potentially produce mnemonics that are not BIP-0039 compliant or can't
|
/// This function can potentially produce mnemonics that are not BIP-0039 compliant or can't
|
||||||
/// properly be encoded as a mnemonic. It is assumed the caller asserts the byte count is `% 4
|
/// properly be encoded as a mnemonic. It is assumed the caller asserts the byte count is `% 4
|
||||||
/// == 0`. If the assumption is incorrect, code may panic. The
|
/// == 0`. If the assumption is incorrect, code may panic. The
|
||||||
/// [`MnemonicBase::from_array`] function may be used to generate entropy if the length of the
|
/// [`MnemonicBase::from_nonstandard_bytes`] function may be used to generate entropy if the
|
||||||
/// data is known at compile-time.
|
/// length of the data is known at compile-time.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use keyfork_mnemonic::Mnemonic;
|
/// use keyfork_mnemonic_util::Mnemonic;
|
||||||
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice()) };
|
/// let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice()) };
|
||||||
/// let mnemonic_text = mnemonic.to_string();
|
/// let mnemonic_text = mnemonic.to_string();
|
||||||
|
@ -336,7 +346,7 @@ where
|
||||||
/// If given an invalid length, undefined behavior may follow, or code may panic.
|
/// If given an invalid length, undefined behavior may follow, or code may panic.
|
||||||
///
|
///
|
||||||
/// ```rust,should_panic
|
/// ```rust,should_panic
|
||||||
/// use keyfork_mnemonic::Mnemonic;
|
/// use keyfork_mnemonic_util::Mnemonic;
|
||||||
/// use std::str::FromStr;
|
/// use std::str::FromStr;
|
||||||
///
|
///
|
||||||
/// // NOTE: Data is of invalid length, 31
|
/// // NOTE: Data is of invalid length, 31
|
||||||
|
@ -373,31 +383,16 @@ where
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A view to internal representation of the decoded data.
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A clone of the internal representation of the decoded data.
|
/// A clone of the internal representation of the decoded data.
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
self.data.to_vec()
|
self.data.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A clone of the internal representation of the decoded data.
|
|
||||||
pub fn to_vec(&self) -> Vec<u8> {
|
|
||||||
self.data.to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Conver the Mnemonic into the internal representation of the decoded data.
|
/// Conver the Mnemonic into the internal representation of the decoded data.
|
||||||
pub fn into_bytes(self) -> Vec<u8> {
|
pub fn into_bytes(self) -> Vec<u8> {
|
||||||
self.data
|
self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conver the Mnemonic into the internal representation of the decoded data.
|
|
||||||
pub fn into_vec(self) -> Vec<u8> {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clone the existing data.
|
/// Clone the existing data.
|
||||||
#[deprecated = "Use as_bytes(), to_bytes(), or into_bytes() instead"]
|
#[deprecated = "Use as_bytes(), to_bytes(), or into_bytes() instead"]
|
||||||
pub fn entropy(&self) -> Vec<u8> {
|
pub fn entropy(&self) -> Vec<u8> {
|
||||||
|
@ -413,7 +408,7 @@ where
|
||||||
&self,
|
&self,
|
||||||
passphrase: impl Into<Option<&'a str>>,
|
passphrase: impl Into<Option<&'a str>>,
|
||||||
) -> Result<Vec<u8>, MnemonicGenerationError> {
|
) -> Result<Vec<u8>, MnemonicGenerationError> {
|
||||||
Ok(self.generate_seed(passphrase).to_vec())
|
Ok(self.generate_seed(passphrase))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
||||||
|
@ -421,7 +416,8 @@ where
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// The function may panic if the HmacSha512 function returns an error. The only error the
|
/// The function may panic if the HmacSha512 function returns an error. The only error the
|
||||||
/// HmacSha512 function should return is an invalid length, which should not be possible.
|
/// HmacSha512 function should return is an invalid length, which should not be possible.
|
||||||
pub fn generate_seed<'a>(&self, passphrase: impl Into<Option<&'a str>>) -> [u8; 64] {
|
///
|
||||||
|
pub fn generate_seed<'a>(&self, passphrase: impl Into<Option<&'a str>>) -> Vec<u8> {
|
||||||
let passphrase = passphrase.into();
|
let passphrase = passphrase.into();
|
||||||
|
|
||||||
let mut seed = [0u8; 64];
|
let mut seed = [0u8; 64];
|
||||||
|
@ -429,7 +425,7 @@ where
|
||||||
let salt = ["mnemonic", passphrase.unwrap_or("")].join("");
|
let salt = ["mnemonic", passphrase.unwrap_or("")].join("");
|
||||||
pbkdf2::<Hmac<Sha512>>(mnemonic.as_bytes(), salt.as_bytes(), 2048, &mut seed)
|
pbkdf2::<Hmac<Sha512>>(mnemonic.as_bytes(), salt.as_bytes(), 2048, &mut seed)
|
||||||
.expect(bug!("HmacSha512 InvalidLength should be infallible"));
|
.expect(bug!("HmacSha512 InvalidLength should be infallible"));
|
||||||
seed
|
seed.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the mnemonic into a list of integers 11 bits in length, matching the length of a
|
/// Encode the mnemonic into a list of integers 11 bits in length, matching the length of a
|
||||||
|
@ -466,39 +462,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> MnemonicBase<W>
|
|
||||||
where
|
|
||||||
W: Wordlist,
|
|
||||||
{
|
|
||||||
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data is expected to be
|
|
||||||
/// of 128, 192, or 256 bits, as per BIP-0039.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// An error may be returned if the data is not within the expected lengths.
|
|
||||||
#[deprecated = "use Mnemonic::try_from_slice"]
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<MnemonicBase<W>, MnemonicGenerationError> {
|
|
||||||
MnemonicBase::try_from_slice(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data is expected to be
|
|
||||||
/// of 128, 192, or 256 bits, as per BIP-0039.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// An error may be returned if the data is not within the expected lengths.
|
|
||||||
#[deprecated = "use Mnemonic::try_from_slice"]
|
|
||||||
pub fn from_entropy(bytes: &[u8]) -> Result<MnemonicBase<W>, MnemonicGenerationError> {
|
|
||||||
MnemonicBase::try_from_slice(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data may be of a size
|
|
||||||
/// of a factor of 4, up to 1024 bytes.
|
|
||||||
///
|
|
||||||
#[deprecated = "Use Mnemonic::from_array"]
|
|
||||||
pub fn from_nonstandard_bytes<const N: usize>(bytes: [u8; N]) -> MnemonicBase<W> {
|
|
||||||
MnemonicBase::from_array(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{collections::HashSet, fs::File, io::Read};
|
use std::{collections::HashSet, fs::File, io::Read};
|
||||||
|
@ -515,7 +478,7 @@ mod tests {
|
||||||
let mut random_handle = File::open("/dev/random").unwrap();
|
let mut random_handle = File::open("/dev/random").unwrap();
|
||||||
let entropy = &mut [0u8; 256 / 8];
|
let entropy = &mut [0u8; 256 / 8];
|
||||||
random_handle.read_exact(&mut entropy[..]).unwrap();
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
||||||
let mnemonic = super::Mnemonic::try_from_slice(&entropy[..256 / 8]).unwrap();
|
let mnemonic = super::Mnemonic::from_bytes(&entropy[..256 / 8]).unwrap();
|
||||||
let new_entropy = mnemonic.as_bytes();
|
let new_entropy = mnemonic.as_bytes();
|
||||||
assert_eq!(new_entropy, entropy);
|
assert_eq!(new_entropy, entropy);
|
||||||
}
|
}
|
||||||
|
@ -531,7 +494,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
let hex = hex::decode(hex_.as_str().unwrap()).unwrap();
|
let hex = hex::decode(hex_.as_str().unwrap()).unwrap();
|
||||||
|
|
||||||
let mnemonic = Mnemonic::try_from_slice(&hex).unwrap();
|
let mnemonic = Mnemonic::from_bytes(&hex).unwrap();
|
||||||
|
|
||||||
assert_eq!(mnemonic.to_string(), seed.as_str().unwrap());
|
assert_eq!(mnemonic.to_string(), seed.as_str().unwrap());
|
||||||
}
|
}
|
||||||
|
@ -542,7 +505,7 @@ mod tests {
|
||||||
let mut random_handle = File::open("/dev/random").unwrap();
|
let mut random_handle = File::open("/dev/random").unwrap();
|
||||||
let entropy = &mut [0u8; 256 / 8];
|
let entropy = &mut [0u8; 256 / 8];
|
||||||
random_handle.read_exact(&mut entropy[..]).unwrap();
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
||||||
let my_mnemonic = Mnemonic::try_from_slice(&entropy[..256 / 8]).unwrap();
|
let my_mnemonic = Mnemonic::from_bytes(&entropy[..256 / 8]).unwrap();
|
||||||
let their_mnemonic = bip39::Mnemonic::from_entropy(&entropy[..256 / 8]).unwrap();
|
let their_mnemonic = bip39::Mnemonic::from_entropy(&entropy[..256 / 8]).unwrap();
|
||||||
assert_eq!(my_mnemonic.to_string(), their_mnemonic.to_string());
|
assert_eq!(my_mnemonic.to_string(), their_mnemonic.to_string());
|
||||||
assert_eq!(my_mnemonic.generate_seed(None), their_mnemonic.to_seed(""));
|
assert_eq!(my_mnemonic.generate_seed(None), their_mnemonic.to_seed(""));
|
||||||
|
@ -566,7 +529,7 @@ mod tests {
|
||||||
|
|
||||||
for _ in 0..tests {
|
for _ in 0..tests {
|
||||||
random.read_exact(&mut entropy[..]).unwrap();
|
random.read_exact(&mut entropy[..]).unwrap();
|
||||||
let mnemonic = Mnemonic::try_from_slice(&entropy[..256 / 8]).unwrap();
|
let mnemonic = Mnemonic::from_bytes(&entropy[..256 / 8]).unwrap();
|
||||||
let words = mnemonic.words();
|
let words = mnemonic.words();
|
||||||
hs.clear();
|
hs.clear();
|
||||||
hs.extend(words);
|
hs.extend(words);
|
||||||
|
@ -597,7 +560,7 @@ mod tests {
|
||||||
let mut entropy = [0u8; 1024];
|
let mut entropy = [0u8; 1024];
|
||||||
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
||||||
random.read_exact(&mut entropy[..]).unwrap();
|
random.read_exact(&mut entropy[..]).unwrap();
|
||||||
let mnemonic = Mnemonic::from_array(entropy);
|
let mnemonic = Mnemonic::from_nonstandard_bytes(entropy);
|
||||||
let words = mnemonic.words();
|
let words = mnemonic.words();
|
||||||
assert_eq!(words.len(), 768);
|
assert_eq!(words.len(), 768);
|
||||||
}
|
}
|
|
@ -10,10 +10,10 @@ license = "MIT"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["mnemonic"]
|
default = ["mnemonic"]
|
||||||
mnemonic = ["keyfork-mnemonic"]
|
mnemonic = ["keyfork-mnemonic-util"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyfork-bug = { version = "0.1.0", path = "../keyfork-bug", registry = "distrust" }
|
keyfork-bug = { version = "0.1.0", path = "../keyfork-bug", registry = "distrust" }
|
||||||
keyfork-crossterm = { version = "0.27.1", path = "../keyfork-crossterm", default-features = false, features = ["use-dev-tty", "events", "bracketed-paste"], registry = "distrust" }
|
keyfork-crossterm = { version = "0.27.1", path = "../keyfork-crossterm", default-features = false, features = ["use-dev-tty", "events", "bracketed-paste"], registry = "distrust" }
|
||||||
keyfork-mnemonic = { version = "0.3.0", path = "../keyfork-mnemonic", optional = true, registry = "distrust" }
|
keyfork-mnemonic-util = { version = "0.3.0", path = "../keyfork-mnemonic-util", optional = true, registry = "distrust" }
|
||||||
thiserror = "1.0.51"
|
thiserror = "1.0.51"
|
||||||
|
|
|
@ -7,7 +7,7 @@ use keyfork_prompt::{
|
||||||
Terminal, PromptHandler,
|
Terminal, PromptHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
use keyfork_mnemonic::English;
|
use keyfork_mnemonic_util::English;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut mgr = Terminal::new(stdin(), stdout())?;
|
let mut mgr = Terminal::new(stdin(), stdout())?;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
#[cfg(feature = "mnemonic")]
|
#[cfg(feature = "mnemonic")]
|
||||||
use keyfork_mnemonic::Wordlist;
|
use keyfork_mnemonic_util::Wordlist;
|
||||||
|
|
||||||
///
|
///
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
|
|
|
@ -158,7 +158,7 @@ pub mod mnemonic {
|
||||||
use super::Validator;
|
use super::Validator;
|
||||||
|
|
||||||
use keyfork_bug::bug;
|
use keyfork_bug::bug;
|
||||||
use keyfork_mnemonic::{Mnemonic, MnemonicFromStrError};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError};
|
||||||
|
|
||||||
/// A mnemonic could not be validated from the given input.
|
/// A mnemonic could not be validated from the given input.
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
|
|
@ -113,7 +113,6 @@ allow = [
|
||||||
"Unicode-DFS-2016",
|
"Unicode-DFS-2016",
|
||||||
"LGPL-2.0",
|
"LGPL-2.0",
|
||||||
"LGPL-3.0",
|
"LGPL-3.0",
|
||||||
"Unicode-3.0",
|
|
||||||
#"Apache-2.0 WITH LLVM-exception",
|
#"Apache-2.0 WITH LLVM-exception",
|
||||||
]
|
]
|
||||||
# List of explicitly disallowed licenses
|
# List of explicitly disallowed licenses
|
||||||
|
|
|
@ -45,7 +45,7 @@ A command line interface for generating, deriving from, and managing secrets.
|
||||||
* [`keyfork-derive-openpgp`]
|
* [`keyfork-derive-openpgp`]
|
||||||
* [`keyfork-derive-util`]
|
* [`keyfork-derive-util`]
|
||||||
* [`keyfork-entropy`]
|
* [`keyfork-entropy`]
|
||||||
* [`keyfork-mnemonic`]
|
* [`keyfork-mnemonic-util`]
|
||||||
* [`keyfork-prompt`]
|
* [`keyfork-prompt`]
|
||||||
* [`keyfork-qrcode`]
|
* [`keyfork-qrcode`]
|
||||||
* [`keyfork-shard`]
|
* [`keyfork-shard`]
|
||||||
|
@ -68,7 +68,7 @@ seed or close-to-root derivations.
|
||||||
* [`keyfork-derive-path-data`]
|
* [`keyfork-derive-path-data`]
|
||||||
* [`keyfork-derive-util`]
|
* [`keyfork-derive-util`]
|
||||||
* [`keyfork-frame`]
|
* [`keyfork-frame`]
|
||||||
* [`keyfork-mnemonic`]
|
* [`keyfork-mnemonic-util`]
|
||||||
* [`keyforkd-models`]
|
* [`keyforkd-models`]
|
||||||
* [`serde`]
|
* [`serde`]
|
||||||
* [`thiserror`]
|
* [`thiserror`]
|
||||||
|
@ -129,7 +129,7 @@ BIP-0032 derivation.
|
||||||
* [`ed25519-dalek`]: Ed25519 key parsing and arithmetic.
|
* [`ed25519-dalek`]: Ed25519 key parsing and arithmetic.
|
||||||
* [`hmac`]: Derivation of keys using HMAC.
|
* [`hmac`]: Derivation of keys using HMAC.
|
||||||
* [`k256`]: secp256k1 (K-256) key parsing and arithmetic.
|
* [`k256`]: secp256k1 (K-256) key parsing and arithmetic.
|
||||||
* [`keyfork-mnemonic`]
|
* [`keyfork-mnemonic-util`]
|
||||||
* [`ripemd`]: Generating hash for fingerprinting of BIP-0032 derived data.
|
* [`ripemd`]: Generating hash for fingerprinting of BIP-0032 derived data.
|
||||||
* [`serde`]
|
* [`serde`]
|
||||||
* [`sha2`]: Generating hashes for fingerprinting and derivation of data.
|
* [`sha2`]: Generating hashes for fingerprinting and derivation of data.
|
||||||
|
@ -145,7 +145,7 @@ M-of-N recombination of secret data using Shamir's Secret Sharing.
|
||||||
* [`card-backend-pcsc`]: PCSC support for OpenPGP-card.
|
* [`card-backend-pcsc`]: PCSC support for OpenPGP-card.
|
||||||
* [`hkdf`]: Key derivation for transport encryption keys.
|
* [`hkdf`]: Key derivation for transport encryption keys.
|
||||||
* [`keyfork-derive-openpgp`]
|
* [`keyfork-derive-openpgp`]
|
||||||
* [`keyfork-mnemonic`]: Encoding encrypted shards using mnemonics.
|
* [`keyfork-mnemonic-util`]: Encoding encrypted shards using mnemonics.
|
||||||
* [`keyfork-prompt`]
|
* [`keyfork-prompt`]
|
||||||
* [`keyfork-qrcode`]: Encoding and decoding of encrypted shards using QR codes.
|
* [`keyfork-qrcode`]: Encoding and decoding of encrypted shards using QR codes.
|
||||||
* [`openpgp-card`]: OpenPGP card support.
|
* [`openpgp-card`]: OpenPGP card support.
|
||||||
|
@ -193,7 +193,7 @@ Frame data in a length-storing checksum-verified format.
|
||||||
* [`thiserror`]
|
* [`thiserror`]
|
||||||
* [`tokio`]: Read and write from AsyncRead and AsyncWrite sources.
|
* [`tokio`]: Read and write from AsyncRead and AsyncWrite sources.
|
||||||
|
|
||||||
## `keyfork-mnemonic`
|
## `keyfork-mnemonic-util`
|
||||||
|
|
||||||
* [`hmac`]: Hash utilities.
|
* [`hmac`]: Hash utilities.
|
||||||
* [`sha2`]: Checksum of mnemonic data and hash for pbkdf2
|
* [`sha2`]: Checksum of mnemonic data and hash for pbkdf2
|
||||||
|
@ -202,7 +202,7 @@ Frame data in a length-storing checksum-verified format.
|
||||||
## `keyfork-prompt`
|
## `keyfork-prompt`
|
||||||
|
|
||||||
* [`keyfork-crossterm`]: Interacting with the terminal.
|
* [`keyfork-crossterm`]: Interacting with the terminal.
|
||||||
* [`keyfork-mnemonic`]
|
* [`keyfork-mnemonic-util`]
|
||||||
* [`thiserror`]
|
* [`thiserror`]
|
||||||
|
|
||||||
## `keyfork-plumbing`
|
## `keyfork-plumbing`
|
||||||
|
@ -210,7 +210,7 @@ Frame data in a length-storing checksum-verified format.
|
||||||
Binaries for `keyfork-entropy` and `keyfork-mnemonic-from-seed`.
|
Binaries for `keyfork-entropy` and `keyfork-mnemonic-from-seed`.
|
||||||
|
|
||||||
* [`keyfork-entropy`]
|
* [`keyfork-entropy`]
|
||||||
* [`keyfork-mnemonic`]
|
* [`keyfork-mnemonic-util`]
|
||||||
* [`smex`]
|
* [`smex`]
|
||||||
|
|
||||||
## `keyfork-slip10-test-data`
|
## `keyfork-slip10-test-data`
|
||||||
|
@ -229,7 +229,7 @@ Zero-dependency hex encoding and decoding.
|
||||||
[`keyfork-derive-util`]: #keyfork-derive-util
|
[`keyfork-derive-util`]: #keyfork-derive-util
|
||||||
[`keyfork-entropy`]: #keyfork-entropy
|
[`keyfork-entropy`]: #keyfork-entropy
|
||||||
[`keyfork-frame`]: #keyfork-frame
|
[`keyfork-frame`]: #keyfork-frame
|
||||||
[`keyfork-mnemonic`]: #keyfork-mnemonic
|
[`keyfork-mnemonic-util`]: #keyfork-mnemonic-util
|
||||||
[`keyfork-prompt`]: #keyfork-prompt
|
[`keyfork-prompt`]: #keyfork-prompt
|
||||||
[`keyfork-qrcode`]: #keyfork-qrcode
|
[`keyfork-qrcode`]: #keyfork-qrcode
|
||||||
[`keyfork-shard`]: #keyfork-shard
|
[`keyfork-shard`]: #keyfork-shard
|
||||||
|
|
Loading…
Reference in New Issue