diff --git a/Cargo.lock b/Cargo.lock index 792d55d..40e09d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arc-swap" @@ -174,7 +174,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -278,15 +278,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] @@ -299,9 +299,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -482,6 +482,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -542,12 +548,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -716,9 +722,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" @@ -738,12 +744,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -760,6 +760,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -849,12 +858,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", ] [[package]] @@ -880,9 +889,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jose-b64" @@ -922,10 +931,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -940,9 +950,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libm" @@ -997,7 +1007,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "local-mini-kms" -version = "1.0.5" +version = "1.0.6" dependencies = [ "aes-gcm-stream", "aes-kw", @@ -1010,9 +1020,9 @@ dependencies = [ "lazy_static", "log", "log4rs", + "pinentry-util", "procfs", "rand", - "rpassword", "rsa", "rusqlite", "rust_util", @@ -1098,6 +1108,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1109,16 +1125,25 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1248,6 +1273,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -1260,6 +1291,32 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pinentry" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ecb857a7b11a03e8872c704d0a1ae1efc20533b3be98338008527a1928ffa6" +dependencies = [ + "log", + "nom", + "percent-encoding", + "secrecy", + "which", + "zeroize", +] + +[[package]] +name = "pinentry-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4273c4b215a6b01462534af2a3254dea0031bf95e3731a05d5f1d17287b3ebcd" +dependencies = [ + "pinentry", + "rpassword", + "secrecy", + "zeroize", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -1403,9 +1460,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -1463,9 +1520,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest 0.10.7", @@ -1550,17 +1607,17 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", - "errno 0.3.9", + "errno 0.3.10", "itoa", "libc", "linux-raw-sys 0.4.14", "once_cell", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1612,16 +1669,25 @@ dependencies = [ "anyhow", "cfg-if", "libc", - "rustix 0.38.41", + "rustix 0.38.42", "thiserror", "windows", ] [[package]] -name = "serde" -version = "1.0.215" +name = "secrecy" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -1638,13 +1704,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1749,9 +1815,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1819,9 +1885,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote 1.0.37", @@ -1875,7 +1941,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1890,9 +1956,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -1914,14 +1980,14 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -1938,9 +2004,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", @@ -1948,9 +2014,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -2054,9 +2120,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -2065,24 +2131,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote 1.0.37", "wasm-bindgen-macro-support", @@ -2090,22 +2155,34 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.42", +] [[package]] name = "winapi" @@ -2169,7 +2246,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2180,7 +2257,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2465,7 +2542,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2486,5 +2563,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote 1.0.37", - "syn 2.0.89", + "syn 2.0.90", ] diff --git a/Cargo.toml b/Cargo.toml index 32081de..6d9fbfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "local-mini-kms" -version = "1.0.5" +version = "1.0.6" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -28,7 +28,6 @@ hyper = { version = "0.14", features = ["client", "server", "tcp", "http1", "htt # use bundled fix musl build core dump on CentOS 6.9 rusqlite = { version = "0.32", features = ["bundled"] } yubico_manager = { version = "0.9", optional = true } -rpassword = "7.3" rand = "0.8" rsa = "0.9" aes-kw = { version = "0.2", features = ["alloc"] } @@ -38,6 +37,7 @@ jose-jwk = "0.1" log = "0.4" env_logger = "0.11" log4rs = "1.3" +pinentry-util = "0.1.1" [target.'cfg(target_os = "linux")'.dependencies] procfs = { version = "0.13", optional = true } diff --git a/README.md b/README.md index 248b941..0a25e8a 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,10 @@ xh POST http://127.0.0.1:5567/datakey \ comment='this is a test key 01' ``` +```shell +xh POST http://127.0.0.1:5567/list type=value name=name limit:=10 +``` + | Key | Comment | |------------------|------------------------------------------------------| | type | `aes` | diff --git a/src/db.rs b/src/db.rs index 4db43c2..db7c52f 100644 --- a/src/db.rs +++ b/src/db.rs @@ -9,6 +9,11 @@ pub struct Key { pub comment: Option, } +pub struct Keys { + pub count: usize, + pub keys: Vec, +} + pub fn make_value_key_name(name: &str) -> String { format!("value:{}", name) } @@ -123,6 +128,48 @@ pub fn update_key(conn: &Connection, key: &Key) -> XResult<()> { Ok(()) } +pub fn list_keys(conn: &Connection, ty: &str, search: &str, limit: usize) -> XResult { + let name = format!("{}:%{}%", ty, search); + let mut count_stmt = conn.prepare("SELECT count(*) FROM keys WHERE name like ?1")?; + let mut count_iter = count_stmt.query_map(params![name], |row| { + let count: usize = row.get(0)?; + Ok(count) + })?; + let count = match count_iter.next() { + None => 0, + Some(Ok(count)) => count, + Some(Err(e)) => return simple_error!("List keys failed: {}", e), + }; + log::debug!("found {} keys via: {}, limit: {}", count, name, limit); + let mut keys = vec![]; + if count > 0 { + let mut list_stmt = + conn.prepare("SELECT id, name, value, comment FROM keys WHERE name like ?1 LIMIT ?2")?; + let mut list_iter = list_stmt.query_map(params![name, limit], |row| { + Ok(Key { + name: row.get(1)?, + encrypted_key: row.get(2)?, + comment: row.get(3)?, + }) + })?; + + loop { + match list_iter.next() { + None => { + break; + } + Some(Ok(r)) => { + log::debug!("found key name={}", r.name); + keys.push(r); + } + Some(Err(e)) => return simple_error!("List keys failed: {}", e), + } + } + } + + Ok(Keys { count, keys }) +} + pub fn find_key(conn: &Connection, name: &str) -> XResult> { let mut stmt = conn.prepare("SELECT id, name, value, comment FROM keys WHERE name = ?1")?; let mut key_iter = stmt.query_map(params![name], |row| { diff --git a/src/serve.rs b/src/serve.rs index f80b348..27d118c 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -28,16 +28,55 @@ lazy_static::lazy_static! { pub struct CommandImpl; impl Command for CommandImpl { - fn name(&self) -> &str { "serve" } + fn name(&self) -> &str { + "serve" + } fn subcommand<'a>(&self) -> App<'a, 'a> { - SubCommand::with_name(self.name()).about("Local mini KMS serve") - .arg(Arg::with_name("listen").long("listen").short("L").takes_value(true).default_value("127.0.0.1:5567").help("Listen")) - .arg(Arg::with_name("local-db").long("local-db").short("d").takes_value(true).default_value("local-mini-kms.db").help("Local db file")) - .arg(Arg::with_name("yubikey-challenge").long("yubikey-challenge").short("c").takes_value(true).help("Yubikey challenge")) - .arg(Arg::with_name("init-encrypted-master-key").long("init-encrypted-master-key").short("k").takes_value(true).help("Init encrypted mater key")) - .arg(Arg::with_name("log-level").long("log-level").takes_value(true).help("Log level: trace, debug, info, warn or error")) - .arg(Arg::with_name("log-file").long("log-file").takes_value(true).help("Log file #DEFAULT or config file")) + SubCommand::with_name(self.name()) + .about("Local mini KMS serve") + .arg( + Arg::with_name("listen") + .long("listen") + .short("L") + .takes_value(true) + .default_value("127.0.0.1:5567") + .help("Listen"), + ) + .arg( + Arg::with_name("local-db") + .long("local-db") + .short("d") + .takes_value(true) + .default_value("local-mini-kms.db") + .help("Local db file"), + ) + .arg( + Arg::with_name("yubikey-challenge") + .long("yubikey-challenge") + .short("c") + .takes_value(true) + .help("Yubikey challenge"), + ) + .arg( + Arg::with_name("init-encrypted-master-key") + .long("init-encrypted-master-key") + .short("k") + .takes_value(true) + .help("Init encrypted mater key"), + ) + .arg( + Arg::with_name("log-level") + .long("log-level") + .takes_value(true) + .help("Log level: trace, debug, info, warn or error"), + ) + .arg( + Arg::with_name("log-file") + .long("log-file") + .takes_value(true) + .help("Log file #DEFAULT or config file"), + ) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { @@ -48,7 +87,9 @@ impl Command for CommandImpl { println!("[ERROR] Init logger failed: {}", e); } - let local_mini_kms_db = sub_arg_matches.value_of("local-db").expect("Get local mini kms db error"); + let local_mini_kms_db = sub_arg_matches + .value_of("local-db") + .expect("Get local mini kms db error"); match init_instance(local_mini_kms_db) { Ok(true) => success!("Init server success"), Ok(false) => failure_and_exit!("SHOULD NOT HAPPEN, server already init"), @@ -59,9 +100,13 @@ impl Command for CommandImpl { #[cfg(feature = "yubikey")] init_with_yubikey_challenge(&rt, sub_arg_matches); - let listen = sub_arg_matches.value_of("listen").expect("Get argument listen error"); + let listen = sub_arg_matches + .value_of("listen") + .expect("Get argument listen error"); rt.block_on(async { - let addr = listen.parse().unwrap_or_else(|_| panic!("Parse listen error: {}", listen)); + let addr = listen + .parse() + .unwrap_or_else(|_| panic!("Parse listen error: {}", listen)); let client = Client::new(); let new_service = make_service_fn(move |conn: &AddrStream| { let remote_addr = conn.remote_addr(); @@ -94,7 +139,12 @@ async fn response_requests( let request_idx = GLOBAL_REQUEST_COUNT.fetch_add(1, Ordering::Relaxed); let process = proc::get_process(remote_addr.port()); match process { - None => log::info!("[{:06}] Receive request: {}, from: {}", request_idx, req.uri(), remote_addr ), + None => log::info!( + "[{:06}] Receive request: {}, from: {}", + request_idx, + req.uri(), + remote_addr + ), Some(process) => log::info!( "[{:06}] Receive request: {}, from: {}, process: {} {} {:?}", request_idx, @@ -110,17 +160,22 @@ async fn response_requests( (&Method::POST, "/update") => update().await, (&Method::POST, "/decrypt") => serve_encrypt_decrypt::decrypt(req).await, (&Method::POST, "/encrypt") => serve_encrypt_decrypt::encrypt(req).await, + (&Method::POST, "/list") => serve_read_write::list(req).await, (&Method::POST, "/read") => serve_read_write::read(req).await, (&Method::POST, "/write") => serve_read_write::write(req).await, (&Method::POST, "/datakey") => serve_datakey::generate(req).await, (&Method::GET, "/status") => serve_status::status().await, (&Method::GET, "/version") => get_version().await, (&Method::GET, "/") => get_root().await, - _ => Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .body(format!("{}\n", serde_json::to_string_pretty(&json!({ - "error": "not_found", - }))?).into())?), + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND).body( + format!( + "{}\n", + serde_json::to_string_pretty(&json!({ + "error": "not_found", + }))? + ) + .into(), + )?), } } @@ -130,7 +185,9 @@ fn init_instance(db: &str) -> XResult { let conn = db::open_db(db)?; db::init_db(&conn)?; - let mut startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().expect("Lock write startup rw lock error"); + let mut startup_rw_lock = serve_common::STATUP_RW_LOCK + .lock() + .expect("Lock write startup rw lock error"); match &*startup_rw_lock { Some(_) => Ok(false), None => { @@ -146,7 +203,9 @@ fn init_instance(db: &str) -> XResult { } fn update_instance_rsa_key_pair() -> XResult { - let mut startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().expect("Lock write startup rw lock error"); + let mut startup_rw_lock = serve_common::STATUP_RW_LOCK + .lock() + .expect("Lock write startup rw lock error"); match &mut *startup_rw_lock { Some(k) => { k.instance_rsa_key_pair = jose::generate_rsa_key(4096)?; @@ -162,21 +221,30 @@ async fn update() -> Result> { async fn inner_update() -> XResult<(StatusCode, Value)> { let update = update_instance_rsa_key_pair()?; - Ok((StatusCode::OK, json!({ - "update": update, - }))) + Ok(( + StatusCode::OK, + json!({ + "update": update, + }), + )) } async fn get_version() -> Result> { - Ok(Response::builder().body(format!( - "{} - {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") - ).into())?) + Ok(Response::builder().body( + format!( + "{} - {}\n", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ) + .into(), + )?) } async fn get_root() -> Result> { if std::env::var("LOCAL_MINI_KMS_HELP").is_ok() { - Ok(Response::builder().body(format!( - r##"{} - {} + Ok(Response::builder().body( + format!( + r##"{} - {} Supports commands: - GET /version - GET /status @@ -186,8 +254,12 @@ Supports commands: - POST /decrypt - POST /read - POST /write -"##, env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") - ).into())?) +"##, + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ) + .into(), + )?) } else { Ok(Response::builder().body("Root Not Found\n".into())?) } @@ -195,29 +267,37 @@ Supports commands: #[cfg(feature = "yubikey")] fn init_with_yubikey_challenge(rt: &Runtime, sub_arg_matches: &ArgMatches) { - let mut yubikey_challenge = sub_arg_matches.value_of("yubikey-challenge").map(ToString::to_string); + let mut yubikey_challenge = sub_arg_matches + .value_of("yubikey-challenge") + .map(ToString::to_string); let init_encrypted_master_key = sub_arg_matches.value_of("init-encrypted-master-key"); if init_encrypted_master_key.is_some() && yubikey_challenge.is_none() { - yubikey_challenge = rpassword::prompt_password("Yubikey challenge: ").ok(); + yubikey_challenge = + pinentry_util::read_pin(Some("Input yubikey challenge"), Some("Challenge: ")) + .ok() + .map(|p| p.get_pin().to_string()); } - let (challenge_key, init_encrypted_master_key) = match (yubikey_challenge, init_encrypted_master_key) { - (Some(yubikey_challenge), Some(init_encrypted_master_key)) => { - match yubikey_hmac::yubikey_challenge_as_32_bytes(yubikey_challenge.as_bytes()) { - Err(e) => { - warning!("Yubikey challenge failed: {}", e); - return; + let (challenge_key, init_encrypted_master_key) = + match (yubikey_challenge, init_encrypted_master_key) { + (Some(yubikey_challenge), Some(init_encrypted_master_key)) => { + match yubikey_hmac::yubikey_challenge_as_32_bytes(yubikey_challenge.as_bytes()) { + Err(e) => { + warning!("Yubikey challenge failed: {}", e); + return; + } + Ok(challenge_key) => (challenge_key, init_encrypted_master_key), } - Ok(challenge_key) => (challenge_key, init_encrypted_master_key), } - } - (Some(_), None) | (None, Some(_)) => { - warning!("Arguments yubikey-challenge and init-encrypted-master-key should both assigned."); - return; - } - _ => return, - }; + (Some(_), None) | (None, Some(_)) => { + warning!( + "Arguments yubikey-challenge and init-encrypted-master-key should both assigned." + ); + return; + } + _ => return, + }; match jose::deserialize_jwe_aes(init_encrypted_master_key, &challenge_key) { Err(e) => warning!("Yubikey seal master key failed: {}", e), @@ -229,7 +309,8 @@ fn init_with_yubikey_challenge(rt: &Runtime, sub_arg_matches: &ArgMatches) { clear_master_key_hex: Some(hex::encode(&key)), clear_master_key_base64: None, encrypted_master_key: None, - }).await + }) + .await }); match init_master_key_result { Err(e) => warning!("Init master key failed: {}", e), diff --git a/src/serve_read_write.rs b/src/serve_read_write.rs index e4f11f4..b09c5b7 100644 --- a/src/serve_read_write.rs +++ b/src/serve_read_write.rs @@ -6,7 +6,14 @@ use hyper::body::Buf; use hyper::{Body, Request, Response, StatusCode}; use rust_util::XResult; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::{json, Map, Value}; + +#[derive(Serialize, Deserialize)] +struct KeysQuery { + r#type: Option, + name: Option, + limit: Option, +} #[derive(Serialize, Deserialize)] struct Named { @@ -21,6 +28,34 @@ struct NamedValue { comment: Option, } +pub async fn list(req: Request) -> Result> { + do_response!(inner_list(req).await) +} + +async fn inner_list(req: Request) -> XResult<(StatusCode, Value)> { + let whole_body = hyper::body::aggregate(req).await?; + let keys_query: KeysQuery = serde_json::from_reader(whole_body.reader())?; + + let conn = open_local_db()?; + let keys = db::list_keys( + &conn, + keys_query.r#type.as_deref().unwrap_or("%"), + keys_query.name.as_deref().unwrap_or(""), + keys_query.limit.unwrap_or(10), + )?; + + let mut map = Map::new(); + map.insert("count".to_string(), keys.count.into()); + let keys = Value::Array( + keys.keys + .iter() + .map(|k| k.name.to_string().into()) + .collect(), + ); + map.insert("keys".to_string(), keys); + serve_common::ok(Value::Object(map)) +} + pub async fn read(req: Request) -> Result> { do_response!(inner_read(req).await) } diff --git a/src/yubikey_init_master_key.rs b/src/yubikey_init_master_key.rs index acd715e..09df73e 100644 --- a/src/yubikey_init_master_key.rs +++ b/src/yubikey_init_master_key.rs @@ -8,18 +8,46 @@ use rust_util::{failure_and_exit, opt_result, success}; pub struct CommandImpl; impl Command for CommandImpl { - fn name(&self) -> &str { "yubikey-init-master-key" } + fn name(&self) -> &str { + "yubikey-init-master-key" + } fn subcommand<'a>(&self) -> App<'a, 'a> { - SubCommand::with_name(self.name()).about("Local mini KMS init by Yubikey(HMAC)") - .arg(Arg::with_name("yubikey-challenge").long("yubikey-challenge").short("c").takes_value(true).help("Yubikey challenge")) - .arg(Arg::with_name("key-hex").long("key-hex").short("x").takes_value(true).help("Key(hex), for encrypt")) - .arg(Arg::with_name("key-base64").long("key-base64").short("b").takes_value(true).help("Key(base64), for encrypt")) - .arg(Arg::with_name("generate-key").long("generate-key").short("K").help("Generate key")) + SubCommand::with_name(self.name()) + .about("Local mini KMS init by Yubikey(HMAC)") + .arg( + Arg::with_name("yubikey-challenge") + .long("yubikey-challenge") + .short("c") + .takes_value(true) + .help("Yubikey challenge"), + ) + .arg( + Arg::with_name("key-hex") + .long("key-hex") + .short("x") + .takes_value(true) + .help("Key(hex), for encrypt"), + ) + .arg( + Arg::with_name("key-base64") + .long("key-base64") + .short("b") + .takes_value(true) + .help("Key(base64), for encrypt"), + ) + .arg( + Arg::with_name("generate-key") + .long("generate-key") + .short("K") + .help("Generate key"), + ) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let yubikey_challenge_opt = sub_arg_matches.value_of("yubikey-challenge").map(ToString::to_string); + let yubikey_challenge_opt = sub_arg_matches + .value_of("yubikey-challenge") + .map(ToString::to_string); let hex_value_opt = sub_arg_matches.value_of("key-hex"); let base64_value_opt = sub_arg_matches.value_of("key-base64"); @@ -29,12 +57,18 @@ impl Command for CommandImpl { failure_and_exit!("--key-hex, --key-base64 or --generate-key must assign one"); } let clear_master_key = if let Some(hex_value) = hex_value_opt { - opt_result!( hex::decode(hex_value), "Decode key-hex failed: {}") + opt_result!(hex::decode(hex_value), "Decode key-hex failed: {}") } else if let Some(base64_value) = base64_value_opt { - opt_result!(STANDARD.decode(base64_value), "Decode key-base64 failed: {}") + opt_result!( + STANDARD.decode(base64_value), + "Decode key-base64 failed: {}" + ) } else { let clear_master_key: [u8; 32] = random(); - success!("Clear master key generated: {}", hex::encode(clear_master_key)); + success!( + "Clear master key generated: {}", + hex::encode(clear_master_key) + ); clear_master_key.to_vec() }; @@ -42,19 +76,24 @@ impl Command for CommandImpl { failure_and_exit!("Master key must be 32 bytes"); } - let yubikey_challenge = yubikey_challenge_opt.unwrap_or_else( - || match rpassword::prompt_password("Yubikey challenge: ") { - Ok(yubikey_challenge) => yubikey_challenge, + let yubikey_challenge = yubikey_challenge_opt.unwrap_or_else(|| { + match pinentry_util::read_pin(Some("Input yubikey challenge"), Some("Challenge: ")) { + Ok(yubikey_challenge) => yubikey_challenge.get_pin().to_string(), Err(e) => failure_and_exit!("Read yubikey challenge failed: {}", e), } - ); + }); let challenge_key = opt_result!( - yubikey_hmac::yubikey_challenge_as_32_bytes(yubikey_challenge.as_bytes()), "Yubikey challenge failed: {}"); + yubikey_hmac::yubikey_challenge_as_32_bytes(yubikey_challenge.as_bytes()), + "Yubikey challenge failed: {}" + ); - let encrypted_master_key = opt_result!(jose::serialize_jwe_aes(&clear_master_key, &challenge_key), "Encrypt master key failed: {}"); + let encrypted_master_key = opt_result!( + jose::serialize_jwe_aes(&clear_master_key, &challenge_key), + "Encrypt master key failed: {}" + ); success!("Encrypted master key: {}", encrypted_master_key); Ok(Some(0)) } -} \ No newline at end of file +}