Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
b2506fd950
|
@@ -12,7 +12,7 @@ steps:
|
|||||||
environment:
|
environment:
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
commands:
|
commands:
|
||||||
- apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev libpam-dev
|
- apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev
|
||||||
- cargo test --locked
|
- cargo test --locked
|
||||||
- name: publish
|
- name: publish
|
||||||
image: ubuntu:focal
|
image: ubuntu:focal
|
||||||
@@ -22,7 +22,7 @@ steps:
|
|||||||
from_secret: cargo_tkn
|
from_secret: cargo_tkn
|
||||||
commands:
|
commands:
|
||||||
- grep -E 'version ?= ?"${DRONE_TAG}"' -i Cargo.toml || (printf "incorrect crate/tag version" && exit 1)
|
- grep -E 'version ?= ?"${DRONE_TAG}"' -i Cargo.toml || (printf "incorrect crate/tag version" && exit 1)
|
||||||
- apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev libpam-dev
|
- apt update && apt install -y cargo libkeyutils-dev libclang-dev clang pkg-config libcryptsetup-dev
|
||||||
- cargo package --all-features
|
- cargo package --all-features
|
||||||
- cargo publish --all-features
|
- cargo publish --all-features
|
||||||
when:
|
when:
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,7 +2,3 @@
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
fido2luks.bash
|
|
||||||
fido2luks.elv
|
|
||||||
fido2luks.fish
|
|
||||||
fido2luks.zsh
|
|
131
Cargo.lock
generated
131
Cargo.lock
generated
@@ -52,9 +52,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
@@ -72,9 +72,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.54.0"
|
version = "0.54.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66c0bb6167449588ff70803f4127f0684f9063097eca5016f37eb52b92c2cf36"
|
checksum = "f4d49b80beb70d76cdac92f5681e666f9a697c737c4f4117a67229a0386dc736"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
@@ -86,7 +86,7 @@ dependencies = [
|
|||||||
"lazycell",
|
"lazycell",
|
||||||
"log",
|
"log",
|
||||||
"peeking_take_while",
|
"peeking_take_while",
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
@@ -118,9 +118,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.59"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
|
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cexpr"
|
name = "cexpr"
|
||||||
@@ -150,9 +150,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "2.33.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"atty",
|
"atty",
|
||||||
@@ -188,12 +188,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.4.4"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"maybe-uninit",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -213,7 +213,7 @@ version = "0.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@@ -239,7 +239,7 @@ version = "0.7.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
@@ -292,10 +292,10 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"strsim 0.9.3",
|
"strsim 0.9.3",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -306,7 +306,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -317,9 +317,9 @@ checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"derive_builder_core",
|
"derive_builder_core",
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -329,9 +329,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -369,28 +369,26 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fido2luks"
|
name = "fido2luks"
|
||||||
version = "0.2.15"
|
version = "0.2.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctap_hmac",
|
"ctap_hmac",
|
||||||
"failure",
|
"failure",
|
||||||
"hex",
|
"hex",
|
||||||
"libcryptsetup-rs",
|
"libcryptsetup-rs",
|
||||||
"pamsm",
|
|
||||||
"ring",
|
"ring",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"structopt",
|
"structopt",
|
||||||
"sudo",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -476,15 +474,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazycell"
|
name = "lazycell"
|
||||||
version = "1.3.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.76"
|
version = "0.2.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
|
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcryptsetup-rs"
|
name = "libcryptsetup-rs"
|
||||||
@@ -550,14 +548,14 @@ version = "0.5.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
|
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.4.1"
|
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 = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722"
|
checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
@@ -589,7 +587,7 @@ version = "0.2.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.0.1",
|
"autocfg 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -598,12 +596,6 @@ version = "0.20.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pamsm"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3580ed2ebe075c74db583233318abf4b07bc8d9a40c7691d0ae9c186e19e43dd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -623,9 +615,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-error-attr",
|
"proc-macro-error-attr",
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -635,7 +627,7 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@@ -651,9 +643,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.20"
|
version = "1.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29"
|
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
@@ -679,7 +671,7 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -930,9 +922,9 @@ version = "1.0.115"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
|
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -966,9 +958,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "structopt"
|
name = "structopt"
|
||||||
version = "0.3.17"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5"
|
checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@@ -977,25 +969,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "structopt-derive"
|
name = "structopt-derive"
|
||||||
version = "0.4.10"
|
version = "0.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f"
|
checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sudo"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2a88e74edf206f281aff2820aa2066c781331044c770626dcafe19491f214e05"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1011,11 +993,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.40"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
|
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
@@ -1026,9 +1008,9 @@ version = "0.12.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.20",
|
"proc-macro2 1.0.19",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.40",
|
"syn 1.0.38",
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1061,12 +1043,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.44"
|
version = "0.1.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1121,12 +1102,6 @@ version = "0.9.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "3.1.1"
|
version = "3.1.1"
|
||||||
|
27
Cargo.toml
27
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fido2luks"
|
name = "fido2luks"
|
||||||
version = "0.2.15"
|
version = "0.2.12"
|
||||||
authors = ["shimunn <shimun@shimun.net>"]
|
authors = ["shimunn <shimun@shimun.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@@ -24,18 +24,6 @@ libcryptsetup-rs = "0.4.1"
|
|||||||
serde_json = "1.0.51"
|
serde_json = "1.0.51"
|
||||||
serde_derive = "1.0.106"
|
serde_derive = "1.0.106"
|
||||||
serde = "1.0.106"
|
serde = "1.0.106"
|
||||||
pamsm = { version = "0.4.1", features = ["libpam"] }
|
|
||||||
sudo = "0.5.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
ctap_hmac = { version="0.4.2", features = ["request_multiple"] }
|
|
||||||
hex = "0.3.2"
|
|
||||||
ring = "0.13.5"
|
|
||||||
failure = "0.1.5"
|
|
||||||
rpassword = "4.0.1"
|
|
||||||
libcryptsetup-rs = "0.4.1"
|
|
||||||
pamsm = { version = "0.4.1", features = ["libpam"] }
|
|
||||||
structopt = "0.3.2"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@@ -44,23 +32,12 @@ panic = 'abort'
|
|||||||
incremental = false
|
incremental = false
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "fido2luks"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "fido2luks_pam"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
depends = "$auto, cryptsetup"
|
depends = "$auto, cryptsetup"
|
||||||
build-depends = "libclang-dev, libcryptsetup-dev, libpam-dev"
|
build-depends = "libclang-dev, libcryptsetup-dev"
|
||||||
extended-description = "Decrypt your LUKS partition using a FIDO2 compatible authenticator"
|
extended-description = "Decrypt your LUKS partition using a FIDO2 compatible authenticator"
|
||||||
assets = [
|
assets = [
|
||||||
["target/release/fido2luks", "usr/bin/", "755"],
|
["target/release/fido2luks", "usr/bin/", "755"],
|
||||||
["target/release/libfido2luks_pam.so", "usr/lib/x86_64-linux-gnu/security/pam_fido2luks.so", "755"],
|
|
||||||
["fido2luks.bash", "usr/share/bash-completion/completions/fido2luks", "644"],
|
|
||||||
["initramfs-tools/keyscript.sh", "/lib/cryptsetup/scripts/fido2luks", "755" ],
|
["initramfs-tools/keyscript.sh", "/lib/cryptsetup/scripts/fido2luks", "755" ],
|
||||||
["initramfs-tools/hook/fido2luks.sh", "etc/initramfs-tools/hooks/", "755" ],
|
["initramfs-tools/hook/fido2luks.sh", "etc/initramfs-tools/hooks/", "755" ],
|
||||||
["initramfs-tools/fido2luks.conf", "etc/", "644"],
|
["initramfs-tools/fido2luks.conf", "etc/", "644"],
|
||||||
|
3
PKGBUILD
3
PKGBUILD
@@ -12,7 +12,7 @@ license=('MPL-2.0')
|
|||||||
pkgver() {
|
pkgver() {
|
||||||
# Use tag version if possible otherwise concat project version and git ref
|
# Use tag version if possible otherwise concat project version and git ref
|
||||||
git describe --exact-match --tags HEAD 2> /dev/null || \
|
git describe --exact-match --tags HEAD 2> /dev/null || \
|
||||||
echo "$(cargo pkgid | cut -d'#' -f2).$(git describe --always)"
|
echo "$(cargo pkgid | cut -d':' -f3).$(git describe --always)"
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
@@ -21,5 +21,4 @@ build() {
|
|||||||
|
|
||||||
package() {
|
package() {
|
||||||
install -Dm 755 target/release/${pkgname} -t "${pkgdir}/usr/bin"
|
install -Dm 755 target/release/${pkgname} -t "${pkgdir}/usr/bin"
|
||||||
install -Dm 644 ../fido2luks.bash "${pkgdir}/usr/share/bash-completion/completions/fido2luks"
|
|
||||||
}
|
}
|
||||||
|
24
build.rs
24
build.rs
@@ -1,24 +0,0 @@
|
|||||||
#![allow(warnings)]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate failure;
|
|
||||||
extern crate ctap_hmac as ctap;
|
|
||||||
|
|
||||||
#[path = "src/cli_args/mod.rs"]
|
|
||||||
mod cli_args;
|
|
||||||
#[path = "src/error.rs"]
|
|
||||||
mod error;
|
|
||||||
#[path = "src/util.rs"]
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
use cli_args::Args;
|
|
||||||
use std::env;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use structopt::clap::Shell;
|
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// generate completion scripts, zsh does panic for some reason
|
|
||||||
for shell in Shell::variants().iter().filter(|shell| **shell != "zsh") {
|
|
||||||
Args::clap().gen_completions(env!("CARGO_PKG_NAME"), Shell::from_str(shell).unwrap(), ".");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -7,8 +7,4 @@ if [ -z "$FIDO2LUKS_PASSWORD_HELPER" ]; then
|
|||||||
export FIDO2LUKS_PASSWORD_HELPER="plymouth ask-for-password --prompt '$MSG'"
|
export FIDO2LUKS_PASSWORD_HELPER="plymouth ask-for-password --prompt '$MSG'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$FIDO2LUKS_USE_TOKEN" -eq 1 ]; then
|
|
||||||
export FIDO2LUKS_CREDENTIAL_ID="$FIDO2LUKS_CREDENTIAL_ID,$(fido2luks token list --csv $CRYPTTAB_SOURCE)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fido2luks print-secret --bin
|
fido2luks print-secret --bin
|
||||||
|
322
src/cli.rs
322
src/cli.rs
@@ -1,28 +1,100 @@
|
|||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::luks::{Fido2LuksToken, LuksDevice};
|
|
||||||
use crate::util::sha256;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use cli_args::*;
|
|
||||||
|
|
||||||
use structopt::clap::Shell;
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use ctap::{FidoCredential, FidoErrorKind};
|
use ctap::{FidoCredential, FidoErrorKind};
|
||||||
|
use failure::_core::fmt::{Display, Error, Formatter};
|
||||||
|
use failure::_core::str::FromStr;
|
||||||
|
use failure::_core::time::Duration;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::str::FromStr;
|
use std::process::exit;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
|
use crate::luks::{Fido2LuksToken, LuksDevice};
|
||||||
|
use crate::util::sha256;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
pub use cli_args::Args;
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
pub struct HexEncoded(pub Vec<u8>);
|
||||||
|
|
||||||
fn read_pin(ap: &AuthenticatorParameters) -> Fido2LuksResult<String> {
|
impl Display for HexEncoded {
|
||||||
if let Some(src) = ap.pin_source.as_ref() {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
|
f.write_str(&hex::encode(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for HexEncoded {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for HexEncoded {
|
||||||
|
type Err = hex::FromHexError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(HexEncoded(hex::decode(s)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
pub struct CommaSeparated<T: FromStr + Display>(pub Vec<T>);
|
||||||
|
|
||||||
|
impl<T: Display + FromStr> Display for CommaSeparated<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
|
for i in &self.0 {
|
||||||
|
f.write_str(&i.to_string())?;
|
||||||
|
f.write_str(",")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display + FromStr> FromStr for CommaSeparated<T> {
|
||||||
|
type Err = <T as FromStr>::Err;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(CommaSeparated(
|
||||||
|
s.split(',')
|
||||||
|
.map(|part| <T as FromStr>::from_str(part))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct Credentials {
|
||||||
|
/// FIDO credential ids, separated by ',' generate using fido2luks credential
|
||||||
|
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
|
||||||
|
pub ids: CommaSeparated<HexEncoded>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct AuthenticatorParameters {
|
||||||
|
/// Request a PIN to unlock the authenticator
|
||||||
|
#[structopt(short = "P", long = "pin")]
|
||||||
|
pub pin: bool,
|
||||||
|
|
||||||
|
/// Location to read PIN from
|
||||||
|
#[structopt(long = "pin-source", env = "FIDO2LUKS_PIN_SOURCE")]
|
||||||
|
pub pin_source: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Await for an authenticator to be connected, timeout after n seconds
|
||||||
|
#[structopt(
|
||||||
|
long = "await-dev",
|
||||||
|
name = "await-dev",
|
||||||
|
env = "FIDO2LUKS_DEVICE_AWAIT",
|
||||||
|
default_value = "15"
|
||||||
|
)]
|
||||||
|
pub await_time: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthenticatorParameters {
|
||||||
|
fn read_pin(&self) -> Fido2LuksResult<String> {
|
||||||
|
if let Some(src) = self.pin_source.as_ref() {
|
||||||
let mut pin = String::new();
|
let mut pin = String::new();
|
||||||
File::open(src)?.read_to_string(&mut pin)?;
|
File::open(src)?.read_to_string(&mut pin)?;
|
||||||
Ok(pin)
|
Ok(pin)
|
||||||
@@ -30,6 +102,49 @@ fn read_pin(ap: &AuthenticatorParameters) -> Fido2LuksResult<String> {
|
|||||||
util::read_password("Authenticator PIN", false)
|
util::read_password("Authenticator PIN", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct LuksParameters {
|
||||||
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
|
device: PathBuf,
|
||||||
|
|
||||||
|
/// Try to unlock the device using a specifc keyslot, ignore all other slots
|
||||||
|
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
||||||
|
slot: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt, Clone)]
|
||||||
|
pub struct LuksModParameters {
|
||||||
|
/// Number of milliseconds required to derive the volume decryption key
|
||||||
|
/// Defaults to 10ms when using an authenticator or the default by cryptsetup when using a password
|
||||||
|
#[structopt(long = "kdf-time", name = "kdf-time")]
|
||||||
|
kdf_time: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct SecretParameters {
|
||||||
|
/// Salt for secret generation, defaults to 'ask'
|
||||||
|
///
|
||||||
|
/// Options:{n}
|
||||||
|
/// - ask : Prompt user using password helper{n}
|
||||||
|
/// - file:<PATH> : Will read <FILE>{n}
|
||||||
|
/// - string:<STRING> : Will use <STRING>, which will be handled like a password provided to the 'ask' option{n}
|
||||||
|
#[structopt(
|
||||||
|
name = "salt",
|
||||||
|
long = "salt",
|
||||||
|
env = "FIDO2LUKS_SALT",
|
||||||
|
default_value = "ask"
|
||||||
|
)]
|
||||||
|
pub salt: InputSalt,
|
||||||
|
/// Script used to obtain passwords, overridden by --interactive flag
|
||||||
|
#[structopt(
|
||||||
|
name = "password-helper",
|
||||||
|
env = "FIDO2LUKS_PASSWORD_HELPER",
|
||||||
|
default_value = "/usr/bin/env systemd-ask-password 'Please enter second factor for LUKS disk encryption!'"
|
||||||
|
)]
|
||||||
|
pub password_helper: PasswordHelper,
|
||||||
|
}
|
||||||
|
|
||||||
fn derive_secret(
|
fn derive_secret(
|
||||||
credentials: &[HexEncoded],
|
credentials: &[HexEncoded],
|
||||||
@@ -67,6 +182,167 @@ fn derive_secret(
|
|||||||
Ok((sha256(&[salt, &unsalted[..]]), cred.clone()))
|
Ok((sha256(&[salt, &unsalted[..]]), cred.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct Args {
|
||||||
|
/// Request passwords via Stdin instead of using the password helper
|
||||||
|
#[structopt(short = "i", long = "interactive")]
|
||||||
|
pub interactive: bool,
|
||||||
|
#[structopt(subcommand)]
|
||||||
|
pub command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt, Clone)]
|
||||||
|
pub struct OtherSecret {
|
||||||
|
/// Use a keyfile instead of a password
|
||||||
|
#[structopt(short = "d", long = "keyfile", conflicts_with = "fido_device")]
|
||||||
|
keyfile: Option<PathBuf>,
|
||||||
|
/// Use another fido device instead of a password
|
||||||
|
/// Note: this requires for the credential fot the other device to be passed as argument as well
|
||||||
|
#[structopt(short = "f", long = "fido-device", conflicts_with = "keyfile")]
|
||||||
|
fido_device: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub enum Command {
|
||||||
|
#[structopt(name = "print-secret")]
|
||||||
|
PrintSecret {
|
||||||
|
/// Prints the secret as binary instead of hex encoded
|
||||||
|
#[structopt(short = "b", long = "bin")]
|
||||||
|
binary: bool,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
credentials: Credentials,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
authenticator: AuthenticatorParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
secret: SecretParameters,
|
||||||
|
},
|
||||||
|
/// Adds a generated key to the specified LUKS device
|
||||||
|
#[structopt(name = "add-key")]
|
||||||
|
AddKey {
|
||||||
|
#[structopt(flatten)]
|
||||||
|
luks: LuksParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
credentials: Credentials,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
authenticator: AuthenticatorParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
secret: SecretParameters,
|
||||||
|
/// Will wipe all other keys
|
||||||
|
#[structopt(short = "e", long = "exclusive")]
|
||||||
|
exclusive: bool,
|
||||||
|
/// Will add an token to your LUKS 2 header, including the credential id
|
||||||
|
#[structopt(short = "t", long = "token")]
|
||||||
|
token: bool,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
existing_secret: OtherSecret,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
luks_mod: LuksModParameters,
|
||||||
|
},
|
||||||
|
/// Replace a previously added key with a password
|
||||||
|
#[structopt(name = "replace-key")]
|
||||||
|
ReplaceKey {
|
||||||
|
#[structopt(flatten)]
|
||||||
|
luks: LuksParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
credentials: Credentials,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
authenticator: AuthenticatorParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
secret: SecretParameters,
|
||||||
|
/// Add the password and keep the key
|
||||||
|
#[structopt(short = "a", long = "add-password")]
|
||||||
|
add_password: bool,
|
||||||
|
/// Will add an token to your LUKS 2 header, including the credential id
|
||||||
|
#[structopt(short = "t", long = "token")]
|
||||||
|
token: bool,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
replacement: OtherSecret,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
luks_mod: LuksModParameters,
|
||||||
|
},
|
||||||
|
/// Open the LUKS device
|
||||||
|
#[structopt(name = "open")]
|
||||||
|
Open {
|
||||||
|
#[structopt(flatten)]
|
||||||
|
luks: LuksParameters,
|
||||||
|
#[structopt(env = "FIDO2LUKS_MAPPER_NAME")]
|
||||||
|
name: String,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
credentials: Credentials,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
authenticator: AuthenticatorParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
secret: SecretParameters,
|
||||||
|
#[structopt(short = "r", long = "max-retries", default_value = "0")]
|
||||||
|
retries: i32,
|
||||||
|
},
|
||||||
|
/// Open the LUKS device using credentials embedded in the LUKS 2 header
|
||||||
|
#[structopt(name = "open-token")]
|
||||||
|
OpenToken {
|
||||||
|
#[structopt(flatten)]
|
||||||
|
luks: LuksParameters,
|
||||||
|
#[structopt(env = "FIDO2LUKS_MAPPER_NAME")]
|
||||||
|
name: String,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
authenticator: AuthenticatorParameters,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
secret: SecretParameters,
|
||||||
|
#[structopt(short = "r", long = "max-retries", default_value = "0")]
|
||||||
|
retries: i32,
|
||||||
|
},
|
||||||
|
/// Generate a new FIDO credential
|
||||||
|
#[structopt(name = "credential")]
|
||||||
|
Credential {
|
||||||
|
#[structopt(flatten)]
|
||||||
|
authenticator: AuthenticatorParameters,
|
||||||
|
/// Name to be displayed on the authenticator if it has a display
|
||||||
|
#[structopt(env = "FIDO2LUKS_CREDENTIAL_NAME")]
|
||||||
|
name: Option<String>,
|
||||||
|
},
|
||||||
|
/// Check if an authenticator is connected
|
||||||
|
#[structopt(name = "connected")]
|
||||||
|
Connected,
|
||||||
|
Token(TokenCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
///LUKS2 token related operations
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub enum TokenCommand {
|
||||||
|
/// List all tokens associated with the specified device
|
||||||
|
List {
|
||||||
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
|
device: PathBuf,
|
||||||
|
/// Dump all credentials as CSV
|
||||||
|
#[structopt(long = "csv")]
|
||||||
|
csv: bool,
|
||||||
|
},
|
||||||
|
/// Add credential to a keyslot
|
||||||
|
Add {
|
||||||
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
|
device: PathBuf,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
credentials: Credentials,
|
||||||
|
/// Slot to which the credentials will be added
|
||||||
|
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
||||||
|
slot: u32,
|
||||||
|
},
|
||||||
|
/// Remove credentials from token(s)
|
||||||
|
Remove {
|
||||||
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
|
device: PathBuf,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
credentials: Credentials,
|
||||||
|
/// Token from which the credentials will be removed
|
||||||
|
#[structopt(long = "token")]
|
||||||
|
token_id: Option<u32>,
|
||||||
|
},
|
||||||
|
/// Remove all unassigned tokens
|
||||||
|
GC {
|
||||||
|
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
||||||
|
device: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_cmdline() -> Args {
|
pub fn parse_cmdline() -> Args {
|
||||||
Args::from_args()
|
Args::from_args()
|
||||||
}
|
}
|
||||||
@@ -82,7 +358,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
} => {
|
} => {
|
||||||
let pin_string;
|
let pin_string;
|
||||||
let pin = if authenticator.pin {
|
let pin = if authenticator.pin {
|
||||||
pin_string = read_pin(authenticator)?;
|
pin_string = authenticator.read_pin()?;
|
||||||
Some(pin_string.as_ref())
|
Some(pin_string.as_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -99,7 +375,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
} => {
|
} => {
|
||||||
let pin_string;
|
let pin_string;
|
||||||
let pin = if authenticator.pin {
|
let pin = if authenticator.pin {
|
||||||
pin_string = read_pin(authenticator)?;
|
pin_string = authenticator.read_pin()?;
|
||||||
Some(pin_string.as_ref())
|
Some(pin_string.as_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -107,7 +383,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
let salt = if interactive || secret.password_helper == PasswordHelper::Stdin {
|
let salt = if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||||
util::read_password_hashed("Password", false)
|
util::read_password_hashed("Password", false)
|
||||||
} else {
|
} else {
|
||||||
secret.salt.obtain_sha256(&secret.password_helper)
|
secret.salt.obtain(&secret.password_helper)
|
||||||
}?;
|
}?;
|
||||||
let (secret, _cred) = derive_secret(
|
let (secret, _cred) = derive_secret(
|
||||||
credentials.ids.0.as_slice(),
|
credentials.ids.0.as_slice(),
|
||||||
@@ -143,7 +419,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let pin = if authenticator.pin {
|
let pin = if authenticator.pin {
|
||||||
Some(read_pin(authenticator)?)
|
Some(authenticator.read_pin()?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -151,7 +427,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||||
util::read_password_hashed(q, verify)
|
util::read_password_hashed(q, verify)
|
||||||
} else {
|
} else {
|
||||||
secret.salt.obtain_sha256(&secret.password_helper)
|
secret.salt.obtain(&secret.password_helper)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let other_secret = |salt_q: &str,
|
let other_secret = |salt_q: &str,
|
||||||
@@ -259,7 +535,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
} => {
|
} => {
|
||||||
let pin_string;
|
let pin_string;
|
||||||
let pin = if authenticator.pin {
|
let pin = if authenticator.pin {
|
||||||
pin_string = read_pin(authenticator)?;
|
pin_string = authenticator.read_pin()?;
|
||||||
Some(pin_string.as_ref())
|
Some(pin_string.as_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -268,7 +544,7 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
if interactive || secret.password_helper == PasswordHelper::Stdin {
|
||||||
util::read_password_hashed(q, verify)
|
util::read_password_hashed(q, verify)
|
||||||
} else {
|
} else {
|
||||||
secret.salt.obtain_sha256(&secret.password_helper)
|
secret.salt.obtain(&secret.password_helper)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -449,17 +725,5 @@ pub fn run_cli() -> Fido2LuksResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Command::GenerateCompletions { shell, out_dir } => {
|
|
||||||
Args::clap().gen_completions(
|
|
||||||
env!("CARGO_PKG_NAME"),
|
|
||||||
match shell.as_ref() {
|
|
||||||
"bash" => Shell::Bash,
|
|
||||||
"fish" => Shell::Fish,
|
|
||||||
_ => unreachable!("structopt shouldn't allow us to reach this point"),
|
|
||||||
},
|
|
||||||
&out_dir,
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,293 +0,0 @@
|
|||||||
use std::fmt::{Display, Error, Formatter};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use structopt::clap::AppSettings;
|
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
mod config;
|
|
||||||
|
|
||||||
pub use config::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
|
||||||
pub struct HexEncoded(pub Vec<u8>);
|
|
||||||
|
|
||||||
impl Display for HexEncoded {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
|
||||||
f.write_str(&hex::encode(&self.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for HexEncoded {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&self.0[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for HexEncoded {
|
|
||||||
type Err = hex::FromHexError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(HexEncoded(hex::decode(s)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
|
||||||
pub struct CommaSeparated<T: FromStr + Display>(pub Vec<T>);
|
|
||||||
|
|
||||||
impl<T: Display + FromStr> Display for CommaSeparated<T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
|
||||||
for i in &self.0 {
|
|
||||||
f.write_str(&i.to_string())?;
|
|
||||||
f.write_str(",")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Display + FromStr> FromStr for CommaSeparated<T> {
|
|
||||||
type Err = <T as FromStr>::Err;
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(CommaSeparated(
|
|
||||||
s.split(',')
|
|
||||||
.map(|part| <T as FromStr>::from_str(part))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct Credentials {
|
|
||||||
/// FIDO credential ids, separated by ',' generate using fido2luks credential
|
|
||||||
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
|
|
||||||
pub ids: CommaSeparated<HexEncoded>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct AuthenticatorParameters {
|
|
||||||
/// Request a PIN to unlock the authenticator
|
|
||||||
#[structopt(short = "P", long = "pin")]
|
|
||||||
pub pin: bool,
|
|
||||||
|
|
||||||
/// Location to read PIN from
|
|
||||||
#[structopt(long = "pin-source", env = "FIDO2LUKS_PIN_SOURCE")]
|
|
||||||
pub pin_source: Option<PathBuf>,
|
|
||||||
|
|
||||||
/// Await for an authenticator to be connected, timeout after n seconds
|
|
||||||
#[structopt(
|
|
||||||
long = "await-dev",
|
|
||||||
name = "await-dev",
|
|
||||||
env = "FIDO2LUKS_DEVICE_AWAIT",
|
|
||||||
default_value = "15"
|
|
||||||
)]
|
|
||||||
pub await_time: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct LuksParameters {
|
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
|
||||||
pub device: PathBuf,
|
|
||||||
|
|
||||||
/// Try to unlock the device using a specifc keyslot, ignore all other slots
|
|
||||||
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
|
||||||
pub slot: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt, Clone)]
|
|
||||||
pub struct LuksModParameters {
|
|
||||||
/// Number of milliseconds required to derive the volume decryption key
|
|
||||||
/// Defaults to 10ms when using an authenticator or the default by cryptsetup when using a password
|
|
||||||
#[structopt(long = "kdf-time", name = "kdf-time")]
|
|
||||||
pub kdf_time: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct SecretParameters {
|
|
||||||
/// Salt for secret generation, defaults to 'ask'
|
|
||||||
///
|
|
||||||
/// Options:{n}
|
|
||||||
/// - ask : Prompt user using password helper{n}
|
|
||||||
/// - file:<PATH> : Will read <FILE>{n}
|
|
||||||
/// - string:<STRING> : Will use <STRING>, which will be handled like a password provided to the 'ask' option{n}
|
|
||||||
#[structopt(
|
|
||||||
name = "salt",
|
|
||||||
long = "salt",
|
|
||||||
env = "FIDO2LUKS_SALT",
|
|
||||||
default_value = "ask"
|
|
||||||
)]
|
|
||||||
pub salt: SecretInput,
|
|
||||||
/// Script used to obtain passwords, overridden by --interactive flag
|
|
||||||
#[structopt(
|
|
||||||
name = "password-helper",
|
|
||||||
env = "FIDO2LUKS_PASSWORD_HELPER",
|
|
||||||
default_value = "/usr/bin/env systemd-ask-password 'Please enter second factor for LUKS disk encryption!'"
|
|
||||||
)]
|
|
||||||
pub password_helper: PasswordHelper,
|
|
||||||
}
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct Args {
|
|
||||||
/// Request passwords via Stdin instead of using the password helper
|
|
||||||
#[structopt(short = "i", long = "interactive")]
|
|
||||||
pub interactive: bool,
|
|
||||||
#[structopt(subcommand)]
|
|
||||||
pub command: Command,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt, Clone)]
|
|
||||||
pub struct OtherSecret {
|
|
||||||
/// Use a keyfile instead of a password
|
|
||||||
#[structopt(short = "d", long = "keyfile", conflicts_with = "fido_device")]
|
|
||||||
pub keyfile: Option<PathBuf>,
|
|
||||||
/// Use another fido device instead of a password
|
|
||||||
/// Note: this requires for the credential fot the other device to be passed as argument as well
|
|
||||||
#[structopt(short = "f", long = "fido-device", conflicts_with = "keyfile")]
|
|
||||||
pub fido_device: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub enum Command {
|
|
||||||
#[structopt(name = "print-secret")]
|
|
||||||
PrintSecret {
|
|
||||||
/// Prints the secret as binary instead of hex encoded
|
|
||||||
#[structopt(short = "b", long = "bin")]
|
|
||||||
binary: bool,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
credentials: Credentials,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
authenticator: AuthenticatorParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
secret: SecretParameters,
|
|
||||||
},
|
|
||||||
/// Adds a generated key to the specified LUKS device
|
|
||||||
#[structopt(name = "add-key")]
|
|
||||||
AddKey {
|
|
||||||
#[structopt(flatten)]
|
|
||||||
luks: LuksParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
credentials: Credentials,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
authenticator: AuthenticatorParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
secret: SecretParameters,
|
|
||||||
/// Will wipe all other keys
|
|
||||||
#[structopt(short = "e", long = "exclusive")]
|
|
||||||
exclusive: bool,
|
|
||||||
/// Will add an token to your LUKS 2 header, including the credential id
|
|
||||||
#[structopt(short = "t", long = "token")]
|
|
||||||
token: bool,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
existing_secret: OtherSecret,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
luks_mod: LuksModParameters,
|
|
||||||
},
|
|
||||||
/// Replace a previously added key with a password
|
|
||||||
#[structopt(name = "replace-key")]
|
|
||||||
ReplaceKey {
|
|
||||||
#[structopt(flatten)]
|
|
||||||
luks: LuksParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
credentials: Credentials,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
authenticator: AuthenticatorParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
secret: SecretParameters,
|
|
||||||
/// Add the password and keep the key
|
|
||||||
#[structopt(short = "a", long = "add-password")]
|
|
||||||
add_password: bool,
|
|
||||||
/// Will add an token to your LUKS 2 header, including the credential id
|
|
||||||
#[structopt(short = "t", long = "token")]
|
|
||||||
token: bool,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
replacement: OtherSecret,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
luks_mod: LuksModParameters,
|
|
||||||
},
|
|
||||||
/// Open the LUKS device
|
|
||||||
#[structopt(name = "open")]
|
|
||||||
Open {
|
|
||||||
#[structopt(flatten)]
|
|
||||||
luks: LuksParameters,
|
|
||||||
#[structopt(env = "FIDO2LUKS_MAPPER_NAME")]
|
|
||||||
name: String,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
credentials: Credentials,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
authenticator: AuthenticatorParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
secret: SecretParameters,
|
|
||||||
#[structopt(short = "r", long = "max-retries", default_value = "0")]
|
|
||||||
retries: i32,
|
|
||||||
},
|
|
||||||
/// Open the LUKS device using credentials embedded in the LUKS 2 header
|
|
||||||
#[structopt(name = "open-token")]
|
|
||||||
OpenToken {
|
|
||||||
#[structopt(flatten)]
|
|
||||||
luks: LuksParameters,
|
|
||||||
#[structopt(env = "FIDO2LUKS_MAPPER_NAME")]
|
|
||||||
name: String,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
authenticator: AuthenticatorParameters,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
secret: SecretParameters,
|
|
||||||
#[structopt(short = "r", long = "max-retries", default_value = "0")]
|
|
||||||
retries: i32,
|
|
||||||
},
|
|
||||||
/// Generate a new FIDO credential
|
|
||||||
#[structopt(name = "credential")]
|
|
||||||
Credential {
|
|
||||||
#[structopt(flatten)]
|
|
||||||
authenticator: AuthenticatorParameters,
|
|
||||||
/// Name to be displayed on the authenticator if it has a display
|
|
||||||
#[structopt(env = "FIDO2LUKS_CREDENTIAL_NAME")]
|
|
||||||
name: Option<String>,
|
|
||||||
},
|
|
||||||
/// Check if an authenticator is connected
|
|
||||||
#[structopt(name = "connected")]
|
|
||||||
Connected,
|
|
||||||
Token(TokenCommand),
|
|
||||||
/// Generate bash completion scripts
|
|
||||||
#[structopt(name = "completions", setting = AppSettings::Hidden)]
|
|
||||||
GenerateCompletions {
|
|
||||||
/// Shell to generate completions for: bash, fish
|
|
||||||
#[structopt(possible_values = &["bash", "fish"])]
|
|
||||||
shell: String,
|
|
||||||
out_dir: PathBuf,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
///LUKS2 token related operations
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub enum TokenCommand {
|
|
||||||
/// List all tokens associated with the specified device
|
|
||||||
List {
|
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
|
||||||
device: PathBuf,
|
|
||||||
/// Dump all credentials as CSV
|
|
||||||
#[structopt(long = "csv")]
|
|
||||||
csv: bool,
|
|
||||||
},
|
|
||||||
/// Add credential to a keyslot
|
|
||||||
Add {
|
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
|
||||||
device: PathBuf,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
credentials: Credentials,
|
|
||||||
/// Slot to which the credentials will be added
|
|
||||||
#[structopt(long = "slot", env = "FIDO2LUKS_DEVICE_SLOT")]
|
|
||||||
slot: u32,
|
|
||||||
},
|
|
||||||
/// Remove credentials from token(s)
|
|
||||||
Remove {
|
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
|
||||||
device: PathBuf,
|
|
||||||
#[structopt(flatten)]
|
|
||||||
credentials: Credentials,
|
|
||||||
/// Token from which the credentials will be removed
|
|
||||||
#[structopt(long = "token")]
|
|
||||||
token_id: Option<u32>,
|
|
||||||
},
|
|
||||||
/// Remove all unassigned tokens
|
|
||||||
GC {
|
|
||||||
#[structopt(env = "FIDO2LUKS_DEVICE")]
|
|
||||||
device: PathBuf,
|
|
||||||
},
|
|
||||||
}
|
|
@@ -10,33 +10,33 @@ use std::process::Command;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum SecretInput {
|
pub enum InputSalt {
|
||||||
AskPassword,
|
AskPassword,
|
||||||
String(String),
|
String(String),
|
||||||
File { path: PathBuf },
|
File { path: PathBuf },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SecretInput {
|
impl Default for InputSalt {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SecretInput::AskPassword
|
InputSalt::AskPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for SecretInput {
|
impl From<&str> for InputSalt {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
let mut parts = s.split(':');
|
let mut parts = s.split(':');
|
||||||
match parts.next() {
|
match parts.next() {
|
||||||
Some("ask") | Some("Ask") => SecretInput::AskPassword,
|
Some("ask") | Some("Ask") => InputSalt::AskPassword,
|
||||||
Some("file") => SecretInput::File {
|
Some("file") => InputSalt::File {
|
||||||
path: parts.collect::<Vec<_>>().join(":").into(),
|
path: parts.collect::<Vec<_>>().join(":").into(),
|
||||||
},
|
},
|
||||||
Some("string") => SecretInput::String(parts.collect::<Vec<_>>().join(":")),
|
Some("string") => InputSalt::String(parts.collect::<Vec<_>>().join(":")),
|
||||||
_ => Self::default(),
|
_ => Self::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for SecretInput {
|
impl FromStr for InputSalt {
|
||||||
type Err = Fido2LuksError;
|
type Err = Fido2LuksError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
@@ -44,42 +44,21 @@ impl FromStr for SecretInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SecretInput {
|
impl fmt::Display for InputSalt {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
f.write_str(&match self {
|
f.write_str(&match self {
|
||||||
SecretInput::AskPassword => "ask".to_string(),
|
InputSalt::AskPassword => "ask".to_string(),
|
||||||
SecretInput::String(s) => ["string", s].join(":"),
|
InputSalt::String(s) => ["string", s].join(":"),
|
||||||
SecretInput::File { path } => ["file", path.display().to_string().as_str()].join(":"),
|
InputSalt::File { path } => ["file", path.display().to_string().as_str()].join(":"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretInput {
|
impl InputSalt {
|
||||||
pub fn obtain_string(&self, password_helper: &PasswordHelper) -> Fido2LuksResult<String> {
|
pub fn obtain(&self, password_helper: &PasswordHelper) -> Fido2LuksResult<[u8; 32]> {
|
||||||
Ok(String::from_utf8(self.obtain(password_helper)?)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn obtain(&self, password_helper: &PasswordHelper) -> Fido2LuksResult<Vec<u8>> {
|
|
||||||
let mut secret = Vec::new();
|
|
||||||
match self {
|
|
||||||
SecretInput::File { path } => {
|
|
||||||
//TODO: replace with try_blocks
|
|
||||||
let mut do_io = || File::open(path)?.read_to_end(&mut secret);
|
|
||||||
do_io().map_err(|cause| Fido2LuksError::KeyfileError { cause })?;
|
|
||||||
}
|
|
||||||
SecretInput::AskPassword => {
|
|
||||||
secret.extend_from_slice(password_helper.obtain()?.as_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
SecretInput::String(s) => secret.extend_from_slice(s.as_bytes()),
|
|
||||||
}
|
|
||||||
Ok(secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn obtain_sha256(&self, password_helper: &PasswordHelper) -> Fido2LuksResult<[u8; 32]> {
|
|
||||||
let mut digest = digest::Context::new(&digest::SHA256);
|
let mut digest = digest::Context::new(&digest::SHA256);
|
||||||
match self {
|
match self {
|
||||||
SecretInput::File { path } => {
|
InputSalt::File { path } => {
|
||||||
let mut do_io = || {
|
let mut do_io = || {
|
||||||
let mut reader = File::open(path)?;
|
let mut reader = File::open(path)?;
|
||||||
let mut buf = [0u8; 512];
|
let mut buf = [0u8; 512];
|
||||||
@@ -94,7 +73,10 @@ impl SecretInput {
|
|||||||
};
|
};
|
||||||
do_io().map_err(|cause| Fido2LuksError::KeyfileError { cause })?;
|
do_io().map_err(|cause| Fido2LuksError::KeyfileError { cause })?;
|
||||||
}
|
}
|
||||||
_ => digest.update(self.obtain(password_helper)?.as_slice()),
|
InputSalt::AskPassword => {
|
||||||
|
digest.update(password_helper.obtain()?.as_bytes());
|
||||||
|
}
|
||||||
|
InputSalt::String(s) => digest.update(s.as_bytes()),
|
||||||
}
|
}
|
||||||
let mut salt = [0u8; 32];
|
let mut salt = [0u8; 32];
|
||||||
salt.as_mut().copy_from_slice(digest.finish().as_ref());
|
salt.as_mut().copy_from_slice(digest.finish().as_ref());
|
||||||
@@ -175,30 +157,24 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn input_salt_from_str() {
|
fn input_salt_from_str() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"file:/tmp/abc".parse::<SecretInput>().unwrap(),
|
"file:/tmp/abc".parse::<InputSalt>().unwrap(),
|
||||||
SecretInput::File {
|
InputSalt::File {
|
||||||
path: "/tmp/abc".into()
|
path: "/tmp/abc".into()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"string:abc".parse::<SecretInput>().unwrap(),
|
"string:abc".parse::<InputSalt>().unwrap(),
|
||||||
SecretInput::String("abc".into())
|
InputSalt::String("abc".into())
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
"ask".parse::<SecretInput>().unwrap(),
|
|
||||||
SecretInput::AskPassword
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
"lol".parse::<SecretInput>().unwrap(),
|
|
||||||
SecretInput::default()
|
|
||||||
);
|
);
|
||||||
|
assert_eq!("ask".parse::<InputSalt>().unwrap(), InputSalt::AskPassword);
|
||||||
|
assert_eq!("lol".parse::<InputSalt>().unwrap(), InputSalt::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn input_salt_obtain() {
|
fn input_salt_obtain() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
SecretInput::String("abc".into())
|
InputSalt::String("abc".into())
|
||||||
.obtain_sha256(&PasswordHelper::Stdin)
|
.obtain(&PasswordHelper::Stdin)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
[
|
[
|
||||||
186, 120, 22, 191, 143, 1, 207, 234, 65, 65, 64, 222, 93, 174, 34, 35, 176, 3, 97,
|
186, 120, 22, 191, 143, 1, 207, 234, 65, 65, 64, 222, 93, 174, 34, 35, 176, 3, 97,
|
48
src/error.rs
48
src/error.rs
@@ -1,10 +1,6 @@
|
|||||||
use ctap::FidoError;
|
use ctap::FidoError;
|
||||||
use libcryptsetup_rs::LibcryptErr;
|
|
||||||
use pamsm::PamError;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::ErrorKind;
|
|
||||||
use std::string::FromUtf8Error;
|
|
||||||
use Fido2LuksError::*;
|
|
||||||
pub type Fido2LuksResult<T> = Result<T, Fido2LuksError>;
|
pub type Fido2LuksResult<T> = Result<T, Fido2LuksError>;
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
@@ -29,10 +25,6 @@ pub enum Fido2LuksError {
|
|||||||
WrongSecret,
|
WrongSecret,
|
||||||
#[fail(display = "not an utf8 string")]
|
#[fail(display = "not an utf8 string")]
|
||||||
StringEncodingError { cause: FromUtf8Error },
|
StringEncodingError { cause: FromUtf8Error },
|
||||||
#[fail(display = "elevated privileges required")]
|
|
||||||
MissingPrivileges,
|
|
||||||
#[fail(display = "{}", cause)]
|
|
||||||
Configuration { cause: ConfigurationError },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fido2LuksError {
|
impl Fido2LuksError {
|
||||||
@@ -54,26 +46,6 @@ pub enum AskPassError {
|
|||||||
IO(io::Error),
|
IO(io::Error),
|
||||||
#[fail(display = "provided passwords don't match")]
|
#[fail(display = "provided passwords don't match")]
|
||||||
Mismatch,
|
Mismatch,
|
||||||
#[fail(display = "unable to retrieve password: {}", _0)]
|
|
||||||
Pam(PamError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PamError> for AskPassError {
|
|
||||||
fn from(e: PamError) -> Self {
|
|
||||||
AskPassError::Pam(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for AskPassError {
|
|
||||||
fn from(e: io::Error) -> Self {
|
|
||||||
AskPassError::IO(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AskPassError> for Fido2LuksError {
|
|
||||||
fn from(cause: AskPassError) -> Self {
|
|
||||||
Fido2LuksError::AskPassError { cause }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
@@ -109,6 +81,11 @@ impl From<LuksError> for Fido2LuksError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use libcryptsetup_rs::LibcryptErr;
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
use Fido2LuksError::*;
|
||||||
|
|
||||||
impl From<FidoError> for Fido2LuksError {
|
impl From<FidoError> for Fido2LuksError {
|
||||||
fn from(e: FidoError) -> Self {
|
fn from(e: FidoError) -> Self {
|
||||||
AuthenticatorError { cause: e }
|
AuthenticatorError { cause: e }
|
||||||
@@ -136,16 +113,3 @@ impl From<FromUtf8Error> for Fido2LuksError {
|
|||||||
StringEncodingError { cause: e }
|
StringEncodingError { cause: e }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Fail)]
|
|
||||||
pub enum ConfigurationError {
|
|
||||||
#[fail(display = "config is missing some values: {:?}", _0)]
|
|
||||||
Missing(Vec<String>),
|
|
||||||
#[fail(display = "config attribute {} contains an invalid value: {}", _1, _0)]
|
|
||||||
InvalidValue(String, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigurationError> for Fido2LuksError {
|
|
||||||
fn from(cause: ConfigurationError) -> Fido2LuksError {
|
|
||||||
Fido2LuksError::Configuration { cause }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
183
src/lib.rs
183
src/lib.rs
@@ -1,183 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate failure;
|
|
||||||
extern crate ctap_hmac as ctap;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
use crate::cli_args::{CommaSeparated, HexEncoded};
|
|
||||||
use crate::device::*;
|
|
||||||
use crate::error::*;
|
|
||||||
use crate::luks::*;
|
|
||||||
use ctap::FidoCredential;
|
|
||||||
use failure::_core::time::Duration;
|
|
||||||
use pamsm::PamLibExt;
|
|
||||||
use pamsm::*;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use sudo::{self, RunningAs};
|
|
||||||
|
|
||||||
pub mod cli_args;
|
|
||||||
pub mod device;
|
|
||||||
pub mod error;
|
|
||||||
pub mod luks;
|
|
||||||
pub mod util;
|
|
||||||
|
|
||||||
struct PamFido2Luks;
|
|
||||||
|
|
||||||
impl PamFido2Luks {
|
|
||||||
fn open(
|
|
||||||
&self,
|
|
||||||
user: String,
|
|
||||||
mut password: impl FnMut(&str) -> PamResult<String>,
|
|
||||||
args: Vec<String>,
|
|
||||||
) -> Fido2LuksResult<()> {
|
|
||||||
let args: HashMap<String, String> = args
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|arg| {
|
|
||||||
let mut parts = arg.split("=");
|
|
||||||
parts
|
|
||||||
.by_ref()
|
|
||||||
.next()
|
|
||||||
.map(|key| (key.to_string(), parts.collect::<Vec<_>>().join("=")))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let credentials = match args.get("credentials").map(|creds| {
|
|
||||||
<CommaSeparated<String>>::from_str(creds)
|
|
||||||
.map(|cs| cs.0)
|
|
||||||
.map_err(|_| ConfigurationError::InvalidValue("credentials".into(), creds.into()))
|
|
||||||
}) {
|
|
||||||
Some(creds) => creds?,
|
|
||||||
_ => Vec::new(),
|
|
||||||
};
|
|
||||||
let pin = args.get("pin");
|
|
||||||
let pin_prefix = args
|
|
||||||
.get("pin-prefix")
|
|
||||||
.map(|p| p.parse::<bool>().unwrap_or_default())
|
|
||||||
.unwrap_or_default();
|
|
||||||
let device = args
|
|
||||||
.get("device")
|
|
||||||
.map(|device| device.replace("%user%", user.as_str()));
|
|
||||||
let name = args
|
|
||||||
.get("name")
|
|
||||||
.map(|name| name.replace("%user%", user.as_str()));
|
|
||||||
|
|
||||||
let mut attempts = args
|
|
||||||
.get("attempts")
|
|
||||||
.and_then(|a| a.parse::<usize>().ok())
|
|
||||||
.unwrap_or(3);
|
|
||||||
|
|
||||||
if let (Some(device), Some(name)) = (device, name) {
|
|
||||||
if !Path::new(&device).exists() || Path::new(&format!("/dev/mapper/{}", name)).exists()
|
|
||||||
{
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
// root required to mount luks
|
|
||||||
match sudo::check() {
|
|
||||||
RunningAs::User => return Err(Fido2LuksError::MissingPrivileges),
|
|
||||||
_ => {
|
|
||||||
sudo::escalate_if_needed().map_err(|_| Fido2LuksError::MissingPrivileges)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut device = LuksDevice::load(device)?;
|
|
||||||
let mut additional_credentials: HashSet<String> = HashSet::new();
|
|
||||||
if device.is_luks2()? {
|
|
||||||
for token in device.tokens()? {
|
|
||||||
let (_, token) = token?;
|
|
||||||
additional_credentials.extend(token.credential.into_iter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let credentials: Vec<FidoCredential> = credentials
|
|
||||||
.into_iter()
|
|
||||||
.chain(additional_credentials.into_iter())
|
|
||||||
.map(|cred| HexEncoded::from_str(cred.as_str()))
|
|
||||||
.map(|cred| FidoCredential {
|
|
||||||
id: cred.unwrap().0,
|
|
||||||
public_key: None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let credentials: Vec<&FidoCredential> = credentials.iter().collect();
|
|
||||||
if !credentials.is_empty() {
|
|
||||||
loop {
|
|
||||||
let (pin, pass) = if pin_prefix {
|
|
||||||
let password = password("PIN + FIDO2 salt (pin:password):")
|
|
||||||
.map_err(|e| Fido2LuksError::AskPassError { cause: e.into() })?;
|
|
||||||
let mut parts = password.split(":");
|
|
||||||
(
|
|
||||||
parts.next().map(|p| p.to_string()).or(pin.cloned()),
|
|
||||||
parts.collect::<Vec<_>>().join(":"),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
pin.cloned(),
|
|
||||||
password("FIDO2 salt: ")
|
|
||||||
.map_err(|e| Fido2LuksError::AskPassError { cause: e.into() })?,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let salt = util::sha256(&[pass.as_bytes()]);
|
|
||||||
let secret = util::sha256(&[
|
|
||||||
&salt,
|
|
||||||
&perform_challenge(
|
|
||||||
&credentials[..],
|
|
||||||
&salt,
|
|
||||||
Duration::from_secs(15),
|
|
||||||
pin.as_ref().map(String::as_str),
|
|
||||||
)?
|
|
||||||
.0[..],
|
|
||||||
]);
|
|
||||||
match device.activate(name.as_str(), &secret[..], None) {
|
|
||||||
Ok(_) => return Ok(()),
|
|
||||||
_ if attempts > 0 => {
|
|
||||||
attempts -= 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Err(e) => break Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ConfigurationError::Missing(vec!["credentials".into()]).into())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PamServiceModule for PamFido2Luks {
|
|
||||||
fn authenticate(pamh: Pam, _flag: PamFlag, args: Vec<String>) -> PamError {
|
|
||||||
let perfrom_authenticate = move || -> Fido2LuksResult<()> {
|
|
||||||
let user = match pamh.get_cached_user() {
|
|
||||||
Err(e) => Err(AskPassError::Pam(e))?,
|
|
||||||
Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()),
|
|
||||||
};
|
|
||||||
let mut password = match pamh.get_authtok(None) {
|
|
||||||
Err(e) => Err(AskPassError::Pam(e))?,
|
|
||||||
Ok(p) => p.map(|s| s.to_str().map(str::to_string).unwrap()),
|
|
||||||
};
|
|
||||||
if let Some(user) = user {
|
|
||||||
PamFido2Luks.open(
|
|
||||||
user,
|
|
||||||
move |q: &str| match password.take() {
|
|
||||||
Some(pass) => Ok(pass),
|
|
||||||
None => pamh
|
|
||||||
.conv(Some(q), PamMsgStyle::PROMPT_ECHO_OFF)
|
|
||||||
.map(|s| s.map(|s| s.to_str().unwrap()).unwrap_or("").to_string()),
|
|
||||||
},
|
|
||||||
args,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Err(AskPassError::Pam(PamError::AUTH_ERR))?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match perfrom_authenticate() {
|
|
||||||
Ok(_) => PamError::SUCCESS,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{}", e);
|
|
||||||
PamError::AUTH_ERR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pam_module!(PamFido2Luks);
|
|
@@ -4,13 +4,15 @@ extern crate ctap_hmac as ctap;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
use crate::cli::*;
|
use crate::cli::*;
|
||||||
|
use crate::config::*;
|
||||||
use crate::device::*;
|
use crate::device::*;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
pub mod cli_args;
|
mod config;
|
||||||
mod device;
|
mod device;
|
||||||
mod error;
|
mod error;
|
||||||
mod luks;
|
mod luks;
|
||||||
|
Reference in New Issue
Block a user