Sealing: Encryption of Secrets #1
24 changed files with 1315 additions and 317 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -7,6 +7,5 @@
|
||||||
*.pdf
|
*.pdf
|
||||||
target/
|
target/
|
||||||
go_client/openapi.json
|
go_client/openapi.json
|
||||||
crates/storage-sled/sled_db
|
|
||||||
test.db
|
*.db*
|
||||||
src/storage/database.db
|
|
||||||
|
|
|
||||||
546
Cargo.lock
generated
546
Cargo.lock
generated
|
|
@ -17,6 +17,54 @@ version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aead"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm-siv"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"polyval",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
|
@ -99,9 +147,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.1"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
|
checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -133,12 +181,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.5.0"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
|
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-core",
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
|
|
@ -166,6 +214,12 @@ dependencies = [
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
|
|
@ -187,13 +241,25 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitvec"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||||
|
dependencies = [
|
||||||
|
"funty",
|
||||||
|
"radium",
|
||||||
|
"tap",
|
||||||
|
"wyz",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -223,6 +289,16 @@ 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 = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
@ -283,16 +359,39 @@ version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
"rand_core",
|
||||||
|
"serdect",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array 0.14.7",
|
||||||
|
"rand_core",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctr"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.9"
|
version = "0.7.9"
|
||||||
|
|
@ -343,6 +442,20 @@ version = "0.15.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.16.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"digest",
|
||||||
|
"elliptic-curve",
|
||||||
|
"rfc6979",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
|
|
@ -352,6 +465,41 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.13.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"digest",
|
||||||
|
"ff",
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
"group",
|
||||||
|
"hkdf",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core",
|
||||||
|
"sec1",
|
||||||
|
"subtle",
|
||||||
|
"tap",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve-tools"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e48843edfbd0a370b3dd14cdbb4e446e9a8855311e6b2b57bf9a1fd1367bc317"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
"heapless",
|
||||||
|
"hex",
|
||||||
|
"multiexp",
|
||||||
|
"serde",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_filter"
|
name = "env_filter"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
|
@ -419,6 +567,17 @@ version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
@ -451,6 +610,12 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "funty"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
|
|
@ -531,6 +696,16 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -562,6 +737,36 @@ version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.15.2"
|
||||||
|
|
@ -579,7 +784,17 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -743,9 +958,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_locid_transform_data"
|
name = "icu_locid_transform_data"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_normalizer"
|
name = "icu_normalizer"
|
||||||
|
|
@ -767,9 +982,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_normalizer_data"
|
name = "icu_normalizer_data"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties"
|
name = "icu_properties"
|
||||||
|
|
@ -788,9 +1003,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties_data"
|
name = "icu_properties_data"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_provider"
|
name = "icu_provider"
|
||||||
|
|
@ -848,7 +1063,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.14.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -888,25 +1112,12 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "json-patch"
|
name = "keccak"
|
||||||
version = "4.0.0"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "159294d661a039f7644cea7e4d844e6b25aaf71c1ffe9d73a96d768c24b0faf4"
|
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jsonptr",
|
"cpufeatures",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jsonptr"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1017,6 +1228,44 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "multiexp"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25a383da1ae933078ddb1e4141f1dd617b512b4183779d6977e6451b0e644806"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"group",
|
||||||
|
"rustversion",
|
||||||
|
"std-shims",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint-dig"
|
name = "num-bigint-dig"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
|
@ -1034,6 +1283,16 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -1060,6 +1319,17 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
|
|
@ -1081,9 +1351,27 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.1"
|
version = "1.21.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p256"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"primeorder",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
|
|
@ -1168,6 +1456,18 @@ version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polyval"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
|
|
@ -1195,7 +1495,16 @@ version = "0.2.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy 0.8.24",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "primeorder"
|
||||||
|
version = "0.13.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1222,6 +1531,12 @@ version = "5.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radium"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
@ -1290,6 +1605,16 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc6979"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||||
|
dependencies = [
|
||||||
|
"hmac",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
|
|
@ -1374,9 +1699,9 @@ checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.0"
|
version = "0.103.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f"
|
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
|
@ -1393,17 +1718,20 @@ checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
name = "rvault-server"
|
name = "rvault-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes-gcm-siv",
|
||||||
"axum",
|
"axum",
|
||||||
|
"base64",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"json-patch",
|
|
||||||
"log",
|
"log",
|
||||||
|
"p256",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
"vsss-rs",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1419,6 +1747,20 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"der",
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
"pkcs8",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
|
|
@ -1473,6 +1815,16 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serdect"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
|
@ -1495,6 +1847,16 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha3"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"keccak",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -1595,7 +1957,7 @@ dependencies = [
|
||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
|
|
@ -1608,7 +1970,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 2.0.12",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
|
@ -1675,7 +2037,7 @@ dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"generic-array",
|
"generic-array 0.14.7",
|
||||||
"hex",
|
"hex",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
|
@ -1693,7 +2055,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror 2.0.12",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
|
|
@ -1731,7 +2093,7 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror 2.0.12",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
|
|
@ -1767,6 +2129,16 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "std-shims"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90e49360f31b0b75a6a82a5205c6103ea07a79a60808d44f5cc879d303337926"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stringprep"
|
name = "stringprep"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
|
@ -1812,6 +2184,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tap"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.19.1"
|
version = "3.19.1"
|
||||||
|
|
@ -1825,33 +2203,13 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl 1.0.69",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.12"
|
version = "2.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.12",
|
"thiserror-impl",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2054,6 +2412,16 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
@ -2101,6 +2469,25 @@ version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vsss-rs"
|
||||||
|
version = "5.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fec4ebcc5594130c31b49594d55c0583fe80621f252f570b222ca4845cafd3cf"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-bigint",
|
||||||
|
"elliptic-curve",
|
||||||
|
"elliptic-curve-tools",
|
||||||
|
"generic-array 1.2.0",
|
||||||
|
"hex",
|
||||||
|
"num",
|
||||||
|
"rand_core",
|
||||||
|
"serde",
|
||||||
|
"sha3",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
|
@ -2310,6 +2697,15 @@ 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 = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wyz"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||||
|
dependencies = [
|
||||||
|
"tap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
|
@ -2334,13 +2730,33 @@ dependencies = [
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.24"
|
version = "0.8.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive 0.8.24",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
29
Cargo.toml
29
Cargo.toml
|
|
@ -3,19 +3,24 @@ name = "rvault-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["shamir"]
|
||||||
|
# default = ["insecure-dev-sealing"]
|
||||||
|
insecure-dev-sealing = []
|
||||||
|
shamir = ["vsss-rs", "p256"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.21"
|
log = "0.4.27"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.7"
|
||||||
zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
|
zeroize = { version = "1.8.1", features = ["zeroize_derive"] }
|
||||||
time = { version = "0.3.39", features = ["serde", "formatting"]}
|
time = { version = "0.3.41", features = ["serde", "formatting"]}
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.44.1", features = ["full"] }
|
||||||
tower = { version = "0.5.2", features = [] }
|
tower = { version = "0.5.2", features = [] }
|
||||||
axum = "0.8.1"
|
axum = "0.8.3"
|
||||||
serde = "1.0.201"
|
serde = "1.0.219"
|
||||||
serde_json = "1.0.117"
|
serde_json = "1.0.140"
|
||||||
json-patch = "4.0.0"
|
|
||||||
# serde_with = "3.8.1"
|
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
|
base64 = "0.22.1"
|
||||||
|
|
||||||
# utoipa = { version = "4.2.0", features = ["axum_extras"] }
|
# utoipa = { version = "4.2.0", features = ["axum_extras"] }
|
||||||
sqlx = { version = "0.8.3", features = [
|
sqlx = { version = "0.8.3", features = [
|
||||||
|
|
@ -28,6 +33,10 @@ sqlx = { version = "0.8.3", features = [
|
||||||
"time"
|
"time"
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
aes-gcm-siv = "0.11.1"
|
||||||
|
vsss-rs = { version = "5.1.0", optional = true, default-features = false, features = ["zeroize", "std"] }
|
||||||
|
p256 = { version = "0.13.2", optional = true, default-features = false, features = ["std", "ecdsa"] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// vault "github.com/openbao/openbao/api/v2"
|
// vault "github.com/openbao/openbao/api/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var client *vault.Client
|
var Client *vault.Client
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
|
@ -26,32 +26,46 @@ func TestMain(m *testing.M) {
|
||||||
config.Timeout = 30 * time.Second
|
config.Timeout = 30 * time.Second
|
||||||
|
|
||||||
// prepare a client with the given base address
|
// prepare a client with the given base address
|
||||||
client, err = vault.NewClient(config)
|
Client, err = vault.NewClient(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to initialize Vault client: %v", err)
|
log.Fatalf("unable to initialize Vault client: %v", err)
|
||||||
}
|
}
|
||||||
log.Println("client prepared")
|
log.Println("client prepared")
|
||||||
|
|
||||||
// authenticate with a root token (insecure)
|
// authenticate with a root token (insecure)
|
||||||
client.SetToken("my-token")
|
Client.SetToken("my-token")
|
||||||
|
|
||||||
exitCode := m.Run() // run all tests and get code
|
exitCode := m.Run() // run all tests and get code
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Requires in-code portions
|
||||||
|
// func TestUnseal(t *testing.T) {
|
||||||
|
// abc := []string{
|
||||||
|
// "eyJpIjpbMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwxXSwidiI6WzE4OCw2NiwxMTksMTQ0LDE1OSw3MCw4NiwxMTUsMTIwLDI1MywxMjQsOTYsMTM5LDk0LDQ1LDE2NiwyMTMsMzYsMTE1LDU4LDg5LDE0OCw2MCwyOCwxNTAsMTE2LDU3LDg5LDIwMCw5NywxNDYsMjEzXX0=",
|
||||||
|
// "eyJpIjpbMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwyXSwidiI6WzE1OCwyNDQsNzEsOTUsMTIyLDEzOCwyNDEsMjEzLDQ1LDE1NiwxMTgsNCwxNzYsNiwxNTcsMTkyLDE2MSwxNjEsNDMsMTc1LDE5NSw4NywxODAsMTAwLDE1NiwxNCwxNDgsMTUsMTc4LDkwLDY3LDExOF19",
|
||||||
|
// }
|
||||||
|
// for i := range abc {
|
||||||
|
// if _, err := Client.Sys().Unseal(abc[i]); err != nil {
|
||||||
|
// t.Fatal("Error unsealing", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
func kv2Write(t *testing.T, mount string, path string) {
|
func kv2Write(t *testing.T, mount string, path string) {
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
"password1": "123abc",
|
"password1": "123abc",
|
||||||
"password2": "horse horse horse battery staple correct",
|
"password2": "horse horse horse battery staple correct",
|
||||||
}
|
}
|
||||||
t.Logf("Attempting to write to KV2 %s path %s:\t", mount, path)
|
t.Logf("Attempting to write to KV2 %s path %s:\t", mount, path)
|
||||||
v, err := client.KVv2(mount).Put(ctx, path, data)
|
v, err := Client.KVv2(mount).Put(ctx, path, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("ERROR writing secret:\n\t", err)
|
t.Fatal("ERROR writing secret:\n\t", err)
|
||||||
}
|
}
|
||||||
t.Log("Success (unchecked)\n\t", v)
|
t.Log("Success (unchecked)\n\t", v)
|
||||||
|
|
||||||
res, err := client.KVv2(mount).Get(ctx, path)
|
res, err := Client.KVv2(mount).Get(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("ERROR checking/reading secret (request failed)\n\t", err)
|
t.Fatal("ERROR checking/reading secret (request failed)\n\t", err)
|
||||||
}
|
}
|
||||||
|
|
@ -65,11 +79,11 @@ func kv2Write(t *testing.T, mount string, path string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func kv2Delete(t *testing.T, mount string, path string) {
|
func kv2Delete(t *testing.T, mount string, path string) {
|
||||||
err := client.KVv2(mount).Delete(ctx, path) // currently disregarding modifier options
|
err := Client.KVv2(mount).Delete(ctx, path) // currently disregarding modifier options
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("ERROR deleting secret:\n\t", err)
|
log.Fatal("ERROR deleting secret:\n\t", err)
|
||||||
}
|
}
|
||||||
res, err := client.KVv2(mount).Get(ctx, path)
|
res, err := Client.KVv2(mount).Get(ctx, path)
|
||||||
if res != nil || err == nil {
|
if res != nil || err == nil {
|
||||||
t.Fatal("ERROR checking/reading secret (request failed)\n\t", res, err)
|
t.Fatal("ERROR checking/reading secret (request failed)\n\t", res, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,12 @@ CREATE TABLE kv2_secret_version (
|
||||||
secret_path TEXT NOT NULL,
|
secret_path TEXT NOT NULL,
|
||||||
|
|
||||||
version_number INTEGER NOT NULL CHECK ( version_number > 0 ),
|
version_number INTEGER NOT NULL CHECK ( version_number > 0 ),
|
||||||
secret_data TEXT NOT NULL,
|
|
||||||
created_time DATETIME NOT NULL,
|
created_time DATETIME NOT NULL,
|
||||||
deletion_time DATETIME,
|
deletion_time DATETIME,
|
||||||
|
|
||||||
|
encrypted_data BLOB NOT NULL,
|
||||||
|
nonce BLOB NOT NULL CHECK ( length(nonce) = 12 ),
|
||||||
|
|
||||||
PRIMARY KEY (engine_path, secret_path, version_number),
|
PRIMARY KEY (engine_path, secret_path, version_number),
|
||||||
FOREIGN KEY (engine_path, secret_path) REFERENCES kv2_metadata(engine_path, secret_path)
|
FOREIGN KEY (engine_path, secret_path) REFERENCES kv2_metadata(engine_path, secret_path)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
8
migrations/20250326160659_sealing.sql
Normal file
8
migrations/20250326160659_sealing.sql
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Sealing Key
|
||||||
|
|
||||||
|
CREATE TABLE root_key (
|
||||||
|
version INTEGER PRIMARY KEY CHECK ( version = 1 ),
|
||||||
|
encrypted_key BLOB NOT NULL,
|
||||||
|
nonce BLOB,
|
||||||
|
type TEXT NOT NULL CHECK ( type IN ('dev_only', 'simple', 'shamir') )
|
||||||
|
);
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
|
||||||
use crate::storage::DatabaseDriver;
|
use crate::storage::DbPool;
|
||||||
|
|
||||||
// route prefix: `/auth/token/`
|
// route prefix: `/auth/token/`
|
||||||
// mod token;
|
// mod token;
|
||||||
|
|
||||||
// use self::token::token_auth_router;
|
// use self::token::token_auth_router;
|
||||||
|
|
||||||
pub fn auth_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
pub fn auth_router(pool: DbPool) -> Router<DbPool> {
|
||||||
Router::new().with_state(pool)
|
Router::new().with_state(pool)
|
||||||
// .nest("/token", token_auth_router())
|
// .nest("/token", token_auth_router())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,14 @@ impl HttpError {
|
||||||
HttpError::new(status_code, vec![error.to_string(); 1])
|
HttpError::new(status_code, vec![error.to_string(); 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Custom serialization function for `secret_data`
|
||||||
|
pub fn serialize_reject_none<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
Some(data) => serializer.serialize_str(data),
|
||||||
|
None => Err(serde::ser::Error::custom("`secret_data` must not be None during serialization!")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ use axum::{
|
||||||
use log::*;
|
use log::*;
|
||||||
use tower::Service;
|
use tower::Service;
|
||||||
|
|
||||||
use crate::{common::HttpError, storage::DatabaseDriver};
|
use crate::{common::HttpError, storage::DbPool};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
/// State to be used to store the database pool
|
/// State to be used to store the database pool
|
||||||
/// and the routers for each engine
|
/// and the routers for each engine
|
||||||
struct EngineMapperState {
|
struct EngineMapperState {
|
||||||
pool: DatabaseDriver,
|
pool: DbPool,
|
||||||
kv_v2: Router,
|
kv_v2: Router,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ struct EngineMapperState {
|
||||||
struct EnginePath(String);
|
struct EnginePath(String);
|
||||||
|
|
||||||
/// Secret engine router
|
/// Secret engine router
|
||||||
pub fn secrets_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
pub fn secrets_router(pool: DbPool) -> Router<DbPool> {
|
||||||
// State containing the pool and engine routers
|
// State containing the pool and engine routers
|
||||||
let state = EngineMapperState {
|
let state = EngineMapperState {
|
||||||
pool: pool.clone(),
|
pool: pool.clone(),
|
||||||
|
|
@ -81,7 +81,7 @@ fn unknown_engine(engine_type: String) -> impl IntoResponse {
|
||||||
|
|
||||||
/// Returns the mount path and engine type for the request,
|
/// Returns the mount path and engine type for the request,
|
||||||
/// if the mount path is registed at the database
|
/// if the mount path is registed at the database
|
||||||
async fn map_mount_points(req: &Uri, pool: &DatabaseDriver) -> Option<(String, String)> {
|
async fn map_mount_points(req: &Uri, pool: &DbPool) -> Option<(String, String)> {
|
||||||
let mut mount_path_fragments: Vec<&str> = req.path().split('/').collect();
|
let mut mount_path_fragments: Vec<&str> = req.path().split('/').collect();
|
||||||
|
|
||||||
// Find longest matching existing mount path for the request
|
// Find longest matching existing mount path for the request
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,13 @@ mod meta;
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// mod tests;
|
// mod tests;
|
||||||
|
|
||||||
use crate::storage::DatabaseDriver;
|
use crate::storage::DbPool;
|
||||||
use axum::{
|
use axum::{
|
||||||
Router,
|
Router,
|
||||||
extract::{Path, State},
|
|
||||||
routing::*,
|
routing::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn kv_router(pool: DatabaseDriver) -> Router {
|
pub fn kv_router(pool: DbPool) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/config", get(get_config))
|
.route("/config", get(get_config))
|
||||||
.route("/config", post(post_config))
|
.route("/config", post(post_config))
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
use super::structs::KvV2WriteRequest;
|
use super::structs::KvV2WriteRequest;
|
||||||
use crate::{
|
use crate::{
|
||||||
DatabaseDriver,
|
common::HttpError, engines::{
|
||||||
common::HttpError,
|
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath
|
||||||
engines::{
|
}, storage::sealing::Secret, DbPool
|
||||||
EnginePath,
|
|
||||||
kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension, Json,
|
Extension, Json,
|
||||||
|
|
@ -13,9 +10,9 @@ use axum::{
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, NoContent, Response},
|
response::{IntoResponse, NoContent, Response},
|
||||||
};
|
};
|
||||||
use log::{error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use time::UtcDateTime;
|
use time::{OffsetDateTime, UtcDateTime};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetDataQuery {
|
pub struct GetDataQuery {
|
||||||
|
|
@ -25,25 +22,51 @@ pub struct GetDataQuery {
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unluckily needed as `sqlx::query_as!()` does not support FromRow derivations
|
||||||
|
struct SecretDataInternal {
|
||||||
|
pub created_time: OffsetDateTime,
|
||||||
|
pub deletion_time: Option<OffsetDateTime>,
|
||||||
|
pub version_number: i64,
|
||||||
|
pub secret_path: String,
|
||||||
|
|
||||||
|
pub nonce: Vec<u8>,
|
||||||
|
pub encrypted_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecretDataInternal {
|
||||||
|
pub async fn into_external(self) -> KvSecretData {
|
||||||
|
let secret = Secret::new(self.encrypted_data, self.nonce).decrypt().await;
|
||||||
|
KvSecretData {
|
||||||
|
created_time: self.created_time,
|
||||||
|
deletion_time: self.deletion_time,
|
||||||
|
version_number: self.version_number,
|
||||||
|
secret_path: self.secret_path,
|
||||||
|
secret_data: secret.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_data(
|
pub async fn get_data(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DbPool>,
|
||||||
Query(params): Query<GetDataQuery>,
|
Query(params): Query<GetDataQuery>,
|
||||||
Path(path): Path<String>,
|
Path(path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> Result<Response, ()> {
|
) -> Result<Response, ()> {
|
||||||
let res = if params.version == 0 {
|
debug!("Get request: Engine: {engine_path}, path: {path}",);
|
||||||
|
|
||||||
|
let res = if params.version != 0 {
|
||||||
// With specific version
|
// With specific version
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
KvSecretData,
|
SecretDataInternal,
|
||||||
r#"SELECT secret_data, created_time, deletion_time, version_number, secret_path
|
r#"SELECT nonce, encrypted_data, created_time, deletion_time, version_number, secret_path
|
||||||
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
||||||
AND version_number = $3"#,
|
AND version_number = $3"#,
|
||||||
engine_path, path, params.version).fetch_one(&pool).await
|
engine_path, path, params.version).fetch_one(&pool).await
|
||||||
} else {
|
} else {
|
||||||
// Without specific version
|
// Without specific version
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
KvSecretData,
|
SecretDataInternal,
|
||||||
r#"SELECT secret_data, created_time, deletion_time, version_number, secret_path
|
r#"SELECT nonce, encrypted_data, created_time, deletion_time, version_number, secret_path
|
||||||
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
FROM kv2_secret_version WHERE engine_path = $1 AND secret_path = $2 AND deletion_time IS NULL
|
||||||
ORDER BY version_number DESC LIMIT 1"#,
|
ORDER BY version_number DESC LIMIT 1"#,
|
||||||
engine_path, path).fetch_one(&pool).await
|
engine_path, path).fetch_one(&pool).await
|
||||||
|
|
@ -51,8 +74,10 @@ pub async fn get_data(
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(secret_content) => {
|
Ok(secret_content) => {
|
||||||
|
let secret_content = secret_content.into_external().await;
|
||||||
|
let inner = secret_content.secret_data;
|
||||||
let data = Wrapper {
|
let data = Wrapper {
|
||||||
data: serde_json::from_str(&secret_content.secret_data).unwrap(),
|
data: serde_json::from_str(&inner).unwrap(),
|
||||||
};
|
};
|
||||||
let return_secret = KvSecretRes {
|
let return_secret = KvSecretRes {
|
||||||
data,
|
data,
|
||||||
|
|
@ -60,34 +85,33 @@ pub async fn get_data(
|
||||||
version: Some(secret_content.version_number),
|
version: Some(secret_content.version_number),
|
||||||
};
|
};
|
||||||
let return_secret = Json(return_secret);
|
let return_secret = Json(return_secret);
|
||||||
info!("{:?}", return_secret);
|
info!("{return_secret:?}");
|
||||||
|
|
||||||
Ok(return_secret.into_response())
|
Ok(return_secret.into_response())
|
||||||
}
|
}
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
sqlx::Error::RowNotFound => {
|
sqlx::Error::RowNotFound => {
|
||||||
error!("Row not found {:?}", e);
|
warn!("Secret not found (could be correct behavior) {e:?}");
|
||||||
Ok(HttpError::simple(
|
Ok(HttpError::simple(
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
"Secret not found within kv2 engine",
|
"Secret not found within kv2 engine",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => panic!("{e:?}"),
|
_ => panic!("Unhandled error: {e:?}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_data(
|
pub async fn post_data(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DbPool>,
|
||||||
Path(kv_path): Path<String>,
|
Path(kv_path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
Json(secret): Json<KvV2WriteRequest>,
|
Json(secret): Json<KvV2WriteRequest>,
|
||||||
) -> Result<Response, ()> {
|
) -> Result<Response, ()> {
|
||||||
log::debug!(
|
debug!(
|
||||||
"Engine: {}, Secret: {}, Content: {}, Version: {:?}, path: {}",
|
"Engine: {}, Secret: {}, Version: {:?}, path: {}",
|
||||||
engine_path,
|
engine_path,
|
||||||
kv_path,
|
kv_path,
|
||||||
secret.data,
|
|
||||||
secret.version, //.unwrap_or(0),
|
secret.version, //.unwrap_or(0),
|
||||||
kv_path
|
kv_path
|
||||||
);
|
);
|
||||||
|
|
@ -95,9 +119,14 @@ pub async fn post_data(
|
||||||
let created_time = time::UtcDateTime::now();
|
let created_time = time::UtcDateTime::now();
|
||||||
let ts = created_time.unix_timestamp();
|
let ts = created_time.unix_timestamp();
|
||||||
|
|
||||||
|
let content = serde_json::to_string(&secret.data).unwrap();
|
||||||
|
|
||||||
|
let Secret { nonce, protected_data } = Secret::encrypt(&content).await.unwrap();
|
||||||
|
let nonce = nonce.as_slice();
|
||||||
|
|
||||||
let mut tx = pool.begin().await.unwrap();
|
let mut tx = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let res_m = sqlx::query!("
|
let _ = sqlx::query!("
|
||||||
INSERT INTO kv2_metadata (engine_path, secret_path, cas_required, created_time, max_versions, updated_time)
|
INSERT INTO kv2_metadata (engine_path, secret_path, cas_required, created_time, max_versions, updated_time)
|
||||||
VALUES ($1, $2, 0, $3, 100, $3)
|
VALUES ($1, $2, 0, $3, 100, $3)
|
||||||
ON CONFLICT(engine_path, secret_path) DO NOTHING;
|
ON CONFLICT(engine_path, secret_path) DO NOTHING;
|
||||||
|
|
@ -107,7 +136,8 @@ pub async fn post_data(
|
||||||
"src/engines/kv/post_secret.sql",
|
"src/engines/kv/post_secret.sql",
|
||||||
engine_path,
|
engine_path,
|
||||||
kv_path,
|
kv_path,
|
||||||
secret.data,
|
nonce,
|
||||||
|
protected_data,
|
||||||
ts,
|
ts,
|
||||||
secret.version,
|
secret.version,
|
||||||
)
|
)
|
||||||
|
|
@ -117,8 +147,6 @@ pub async fn post_data(
|
||||||
|
|
||||||
tx.commit().await.expect("FAILED TO WRITE TX!");
|
tx.commit().await.expect("FAILED TO WRITE TX!");
|
||||||
|
|
||||||
warn!("test: {res_m:?} {res_r:?} {}", res_r.version_number);
|
|
||||||
|
|
||||||
let res = KvV2WriteResponse {
|
let res = KvV2WriteResponse {
|
||||||
created_time: created_time.into(),
|
created_time: created_time.into(),
|
||||||
custom_metadata: None,
|
custom_metadata: None,
|
||||||
|
|
@ -134,11 +162,11 @@ pub async fn post_data(
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret
|
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions
|
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions
|
||||||
pub async fn delete_data(
|
pub async fn delete_data(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DbPool>,
|
||||||
Path(path): Path<String>,
|
Path(path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
) -> Result<Response, Response> {
|
) -> Result<Response, Response> {
|
||||||
log::debug!("Secret: {}, path: {}", path, path);
|
debug!("Secret: {path}, path: {path}");
|
||||||
|
|
||||||
let del_time = UtcDateTime::now().unix_timestamp();
|
let del_time = UtcDateTime::now().unix_timestamp();
|
||||||
|
|
||||||
|
|
@ -186,7 +214,7 @@ pub async fn delete_data(
|
||||||
error!(
|
error!(
|
||||||
"Strange - a version to be deleted has been found but could not be found to set deletion.\n\t{e:?}"
|
"Strange - a version to be deleted has been found but could not be found to set deletion.\n\t{e:?}"
|
||||||
);
|
);
|
||||||
// Not commited transactions will be aborted upon drop
|
// Not committed transactions will be aborted upon drop
|
||||||
// tx.rollback().await.unwrap();
|
// tx.rollback().await.unwrap();
|
||||||
return Err(HttpError::simple(
|
return Err(HttpError::simple(
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
|
@ -201,10 +229,8 @@ pub async fn delete_data(
|
||||||
Ok(NoContent.into_response())
|
Ok(NoContent.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Not
|
|
||||||
pub async fn patch_data(
|
pub async fn patch_data(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DbPool>,
|
||||||
Path(kv_path): Path<String>,
|
Path(kv_path): Path<String>,
|
||||||
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
Extension(EnginePath(engine_path)): Extension<EnginePath>,
|
||||||
Json(secret): Json<KvV2WriteRequest>,
|
Json(secret): Json<KvV2WriteRequest>,
|
||||||
|
|
@ -212,4 +238,3 @@ pub async fn patch_data(
|
||||||
// TODO: implement only application/merge-patch+json
|
// TODO: implement only application/merge-patch+json
|
||||||
todo!("Not implemented")
|
todo!("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::storage::DatabaseDriver;
|
use crate::storage::DbPool;
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
|
|
||||||
pub async fn delete_path() -> &'static str {
|
pub async fn delete_path() -> &'static str {
|
||||||
|
|
@ -14,7 +14,7 @@ pub async fn get_meta() -> &'static str {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_meta(
|
pub async fn post_meta(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DbPool>,
|
||||||
Path((mount_path, kv_path)): Path<(String, String)>,
|
Path((mount_path, kv_path)): Path<(String, String)>,
|
||||||
body: String,
|
body: String,
|
||||||
) -> &'static str {
|
) -> &'static str {
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@ WITH latest_version AS (
|
||||||
FROM kv2_secret_version
|
FROM kv2_secret_version
|
||||||
WHERE engine_path = $1 AND secret_path = $2 -- engine_path AND secret_path
|
WHERE engine_path = $1 AND secret_path = $2 -- engine_path AND secret_path
|
||||||
)
|
)
|
||||||
INSERT INTO kv2_secret_version (engine_path, secret_path, secret_data, created_time, version_number)
|
INSERT INTO kv2_secret_version (engine_path, secret_path, nonce, encrypted_data, created_time, version_number)
|
||||||
VALUES (
|
VALUES (
|
||||||
$1, -- engine_path
|
$1, -- engine_path
|
||||||
$2, -- secret_path
|
$2, -- secret_path
|
||||||
$3, -- secret_data
|
$3, -- nonce
|
||||||
$4, -- created_time
|
$4, -- encrypted_data
|
||||||
|
$5, -- created_time
|
||||||
CASE -- Use provided version if given
|
CASE -- Use provided version if given
|
||||||
WHEN $5 IS NOT NULL THEN $5 -- version_number (optional)
|
WHEN $6 IS NOT NULL THEN $6 -- version_number (optional)
|
||||||
ELSE COALESCE((SELECT max_version FROM latest_version) + 1, 1) -- otherwise 1
|
ELSE COALESCE((SELECT max_version FROM latest_version) + 1, 1) -- otherwise 1
|
||||||
END -- version_number logic
|
END -- version_number logic
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ use time::{OffsetDateTime, UtcDateTime, serde::rfc3339};
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
/// For SQLite support
|
|
||||||
pub struct KvSecretData {
|
pub struct KvSecretData {
|
||||||
pub secret_data: String,
|
pub secret_data: String,
|
||||||
#[serde(with = "rfc3339")]
|
#[serde(with = "rfc3339")]
|
||||||
|
|
@ -43,7 +42,6 @@ pub struct Wrapper<T> {
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct KvSecretRes {
|
pub struct KvSecretRes {
|
||||||
/// Map (required)
|
/// Map (required)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
|
||||||
use crate::storage::DatabaseDriver;
|
use crate::storage::DbPool;
|
||||||
|
|
||||||
pub fn identity_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
pub fn identity_router(pool: DbPool) -> Router<DbPool> {
|
||||||
Router::new().with_state(pool)
|
Router::new().with_state(pool)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -1,14 +1,16 @@
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Router,
|
||||||
extract::Request,
|
extract::Request,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
middleware::{self, Next},
|
middleware::{self, Next},
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use std::{env, net::SocketAddr, str::FromStr};
|
use std::{env, net::SocketAddr, str::FromStr};
|
||||||
use storage::DatabaseDriver;
|
use storage::DbPool;
|
||||||
use tokio::{net::TcpListener, signal};
|
use tokio::{net::TcpListener, signal};
|
||||||
|
|
||||||
use crate::common::HttpError;
|
use crate::common::HttpError;
|
||||||
|
|
@ -48,7 +50,11 @@ async fn main() {
|
||||||
.layer(middleware::from_fn(set_default_content_type_json))
|
.layer(middleware::from_fn(set_default_content_type_json))
|
||||||
.with_state(pool.clone());
|
.with_state(pool.clone());
|
||||||
|
|
||||||
warn!("Listening on {}", listen_addr.to_string());
|
if !storage::sealing::prepare_unseal(&pool).await {
|
||||||
|
storage::sealing::init_default(&pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
warn!("Listening on {listen_addr}");
|
||||||
// Start listening
|
// Start listening
|
||||||
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
|
|
@ -70,7 +76,7 @@ async fn set_default_content_type_json(
|
||||||
Ok(next.run(req).await)
|
Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn shutdown_signal(pool: DatabaseDriver) {
|
async fn shutdown_signal(pool: DbPool) {
|
||||||
let ctrl_c = async {
|
let ctrl_c = async {
|
||||||
signal::ctrl_c()
|
signal::ctrl_c()
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
|
pub mod sealing;
|
||||||
|
|
||||||
use std::{fs::File, path::Path};
|
use std::{fs::File, path::Path};
|
||||||
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use sqlx::{sqlite::SqlitePoolOptions, Pool, Sqlite};
|
use sqlx::{sqlite::SqlitePoolOptions, Pool, Sqlite};
|
||||||
|
|
||||||
pub(crate) type DatabaseDriver = Pool<Sqlite>;
|
pub(crate) type DbType = Sqlite;
|
||||||
|
pub(crate) type DbPool = Pool<DbType>;
|
||||||
|
|
||||||
pub async fn create_pool(db_url: String) -> DatabaseDriver {
|
pub async fn create_pool(db_url: String) -> DbPool {
|
||||||
// Create SQLite database file if it does not exist
|
// Create SQLite database file if it does not exist
|
||||||
if db_url.starts_with("sqlite:") && db_url != ("sqlite::memory:") {
|
if db_url.starts_with("sqlite:") && db_url != ("sqlite::memory:") {
|
||||||
let path = db_url.replace("sqlite:", "");
|
let path = db_url.replace("sqlite:", "");
|
||||||
if !Path::new(&path).exists() {
|
if !Path::new(&path).exists() {
|
||||||
warn!("Sqlite database does not exist, creating file {}", path);
|
warn!("Sqlite database does not exist, creating file {path}");
|
||||||
File::create(&path).expect("Failed to create database file");
|
File::create(&path).expect("Failed to create database file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
334
src/storage/sealing.rs
Normal file
334
src/storage/sealing.rs
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
#[cfg(feature = "shamir")]
|
||||||
|
pub mod shamir;
|
||||||
|
pub mod simple;
|
||||||
|
|
||||||
|
use aes_gcm_siv::{
|
||||||
|
AeadCore, Aes256GcmSiv, KeyInit,
|
||||||
|
aead::{Aead, OsRng},
|
||||||
|
};
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use simple::SimpleSealing;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use super::DbPool;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum KeyEnum {
|
||||||
|
/// Final key
|
||||||
|
MainKey(Vec<u8>),
|
||||||
|
/// Encrypted with single secret (protected_rk, nonce)
|
||||||
|
Simple(SimpleSealing),
|
||||||
|
#[cfg(feature = "shamir")]
|
||||||
|
// Shamir's Secret Sharing
|
||||||
|
Shamir(shamir::ShamirBucket),
|
||||||
|
/// Unknown or not initialized
|
||||||
|
Uninitialized,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Sealing {
|
||||||
|
fn new(protected_rk: Vec<u8>, nonce: Vec<u8>) -> Self;
|
||||||
|
|
||||||
|
async fn unseal(&mut self, key: String) -> UnsealResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProtectedRK {
|
||||||
|
pub protection_type: String,
|
||||||
|
pub encrypted_key: Vec<u8>,
|
||||||
|
pub nonce: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static ROOT_KEY_MAYBE: RwLock<KeyEnum> = RwLock::const_new(KeyEnum::Uninitialized);
|
||||||
|
|
||||||
|
/// Returns `true` if vault is initialized or unsealed.
|
||||||
|
/// Returns `false` if uninitialized (nothing in the database).
|
||||||
|
pub async fn prepare_unseal(pool: &DbPool) -> bool {
|
||||||
|
{
|
||||||
|
if !matches!(*ROOT_KEY_MAYBE.read().await, KeyEnum::Uninitialized) {
|
||||||
|
info!("Vault unseal is already prepared");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lock = ROOT_KEY_MAYBE.write(); // Not awaited just here
|
||||||
|
|
||||||
|
let rk = sqlx::query_as!(
|
||||||
|
ProtectedRK,
|
||||||
|
"SELECT encrypted_key, type as protection_type, nonce FROM root_key ORDER BY version LIMIT 1"
|
||||||
|
)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to optionally read root key from the database");
|
||||||
|
|
||||||
|
let v = match rk {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
warn!("No root key was found in the database!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
info!(
|
||||||
|
"Root key of type {} found in the database",
|
||||||
|
v.protection_type
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut lock = lock.await;
|
||||||
|
let nonce = v.nonce.expect("Simple encryption but the nonce is missing");
|
||||||
|
let res = match &*v.protection_type {
|
||||||
|
#[cfg(feature = "insecure-dev-sealing")]
|
||||||
|
"dev_only" => {
|
||||||
|
warn!(
|
||||||
|
"Root key is of type {}. This is INSECURE and must only be used for development purposes!",
|
||||||
|
v.protection_type
|
||||||
|
);
|
||||||
|
KeyEnum::MainKey(v.encrypted_key)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "insecure-dev-sealing"))]
|
||||||
|
"dev_only" => panic!(
|
||||||
|
r#"Database is insecure but "insecure-dev-sealing" is not enabled for this build!"#
|
||||||
|
),
|
||||||
|
"simple" => KeyEnum::Simple(SimpleSealing::new(v.encrypted_key, nonce)),
|
||||||
|
#[cfg(feature = "shamir")]
|
||||||
|
"shamir" => KeyEnum::Shamir(shamir::ShamirBucket::new(v.encrypted_key, nonce)),
|
||||||
|
#[cfg(not(feature = "shamir"))]
|
||||||
|
"shamir" => panic!(r#"Feature "shamir" is not enabled for this build!"#),
|
||||||
|
_ => panic!("Unknown root key type in database"),
|
||||||
|
};
|
||||||
|
*lock = res;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Must NOT be used in production.
|
||||||
|
/// Token is plainly stored in the database and will be unsealed directly by [prepare_unseal]!
|
||||||
|
/// Danger!
|
||||||
|
#[cfg(feature = "insecure-dev-sealing")]
|
||||||
|
pub async fn init_insecure_in_db(pool: &DbPool) {
|
||||||
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
|
||||||
|
warn!(
|
||||||
|
"Danger: INSECURE! Generated root key is stored plainly in the database. Must ONLY be used for development!"
|
||||||
|
);
|
||||||
|
write_new_root_key(pool, root_key, "dev_only", Some(b"")).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_new_root_key(
|
||||||
|
pool: &DbPool,
|
||||||
|
protected_key: Vec<u8>,
|
||||||
|
type_to_be: &str,
|
||||||
|
nonce: Option<&[u8]>,
|
||||||
|
) {
|
||||||
|
let _ = sqlx::query!(
|
||||||
|
"
|
||||||
|
INSERT INTO root_key (encrypted_key, type, version, nonce)
|
||||||
|
VALUES ($1, $2, 1, $3)
|
||||||
|
",
|
||||||
|
protected_key,
|
||||||
|
type_to_be,
|
||||||
|
nonce
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to write new root key to the database");
|
||||||
|
|
||||||
|
info!("Initialized new root key!");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reseal(pool: &DbPool) {
|
||||||
|
{
|
||||||
|
let mut lock = ROOT_KEY_MAYBE.write().await;
|
||||||
|
*lock = KeyEnum::Uninitialized;
|
||||||
|
}
|
||||||
|
prepare_unseal(pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub async fn sealing_status() {
|
||||||
|
// let lock = ROOT_KEY_MAYBE.read().await;
|
||||||
|
// match &*lock {
|
||||||
|
// KeyEnum::MainKey(_) => todo!(),
|
||||||
|
// KeyEnum::Simple(_, _) => todo!(),
|
||||||
|
// KeyEnum::Uninitialized => todo!(),
|
||||||
|
// KeyEnum::Shamir(_, _) => todo!(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub async fn provide_key(key: String) -> UnsealResult {
|
||||||
|
// First, check if we need to write-lock at all
|
||||||
|
{
|
||||||
|
let read_lock = ROOT_KEY_MAYBE.read().await;
|
||||||
|
if matches!(*read_lock, KeyEnum::MainKey(_)) {
|
||||||
|
info!("Providing keys is useless since vault is already unlocked");
|
||||||
|
return UnsealResult::AlreadyDone;
|
||||||
|
} else if matches!(*read_lock, KeyEnum::Uninitialized) {
|
||||||
|
error!("Cannot process provided key when the vault is uninitialized");
|
||||||
|
return UnsealResult::Uninitialized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A write lock is necessary.
|
||||||
|
let mut write_lock = ROOT_KEY_MAYBE.write().await;
|
||||||
|
let rk = match &mut *write_lock {
|
||||||
|
KeyEnum::MainKey(_) | KeyEnum::Uninitialized => {
|
||||||
|
unreachable!("Should have been checked above")
|
||||||
|
}
|
||||||
|
KeyEnum::Simple(simple) => simple.unseal(key).await,
|
||||||
|
#[cfg(feature = "shamir")]
|
||||||
|
KeyEnum::Shamir(shamir) => shamir.unseal(key).await,
|
||||||
|
};
|
||||||
|
let rk = match rk {
|
||||||
|
UnsealResult::DoneConfidential(rk) => rk,
|
||||||
|
UnsealResult::Done => unreachable!(),
|
||||||
|
reject_action => return reject_action,
|
||||||
|
};
|
||||||
|
*write_lock = KeyEnum::MainKey(rk);
|
||||||
|
|
||||||
|
info!("Unsealing done; Vault ready");
|
||||||
|
UnsealResult::Done
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Secret {
|
||||||
|
pub nonce: [u8; 12],
|
||||||
|
pub protected_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Secret {
|
||||||
|
pub fn new<D, N>(data: D, nonce: N) -> Self
|
||||||
|
where
|
||||||
|
D: Into<Vec<u8>>,
|
||||||
|
N: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
let nonce_slice = nonce.as_ref();
|
||||||
|
assert!(
|
||||||
|
nonce_slice.len() == 12,
|
||||||
|
"Nonce must be exactly 12 bytes long"
|
||||||
|
);
|
||||||
|
|
||||||
|
let nonce: &[u8; 12] = nonce_slice.try_into().expect("Nonce must be 12 bytes long");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
nonce: *nonce,
|
||||||
|
protected_data: data.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encrypt a secret
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if .
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error if the vault is uninitialized or an unknown error occurs.
|
||||||
|
pub async fn encrypt(data: &String) -> Result<Self, ()> {
|
||||||
|
let cipher = if let KeyEnum::MainKey(key) = &*ROOT_KEY_MAYBE.read().await {
|
||||||
|
match Aes256GcmSiv::new_from_slice(key) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to create new AesGcmSiv cipher from variable size key: {e}");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Cannot encrypt secret since the vault is not unsealed");
|
||||||
|
return Err(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce: aes_gcm_siv::aead::generic_array::GenericArray<
|
||||||
|
u8,
|
||||||
|
<Aes256GcmSiv as aes_gcm_siv::AeadCore>::NonceSize,
|
||||||
|
> = Aes256GcmSiv::generate_nonce(&mut OsRng); // 96-bits; unique per message
|
||||||
|
let enc = match cipher.encrypt(&nonce, data.as_bytes()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to encrypt secret with cipher: {e}");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug_assert!(nonce.len() == 12, "Nonce should be exactly 12 bytes");
|
||||||
|
let nonce = match nonce.as_slice().try_into() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Nonce should be exactly 12 bytes: {e}");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
nonce,
|
||||||
|
protected_data: enc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decrypt_bytes(self) -> Result<Vec<u8>, ()> {
|
||||||
|
assert!(self.nonce.len() == 12);
|
||||||
|
let cipher = match &*ROOT_KEY_MAYBE.read().await {
|
||||||
|
KeyEnum::MainKey(key) => Aes256GcmSiv::new_from_slice(key),
|
||||||
|
_ => panic!("Cannot seal secret since the vault is not unsealed"),
|
||||||
|
}
|
||||||
|
.expect("Failed to create new AesGcmSiv cipher from variable size key");
|
||||||
|
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(&self.nonce);
|
||||||
|
let enc = match cipher.decrypt(nonce, self.protected_data.as_ref()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to decrypt secret with given nonce and cipher: {e}");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decrypt(self) -> Result<String, ()> {
|
||||||
|
String::from_utf8(self.decrypt_bytes().await?).map_err(|e| {
|
||||||
|
error!("Failed to parse secret as UTF8: {e}");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum UnsealResult {
|
||||||
|
/// Unsealing finished, with root key hidden
|
||||||
|
Done,
|
||||||
|
/// Was already unsealed, no action taken
|
||||||
|
AlreadyDone,
|
||||||
|
/// Could not unseal as the vault is uninitialized
|
||||||
|
Uninitialized,
|
||||||
|
|
||||||
|
/// Unsealing finished, returns root key
|
||||||
|
DoneConfidential(Vec<u8>),
|
||||||
|
/// Unsealing attempt has been recorded but is not sufficient
|
||||||
|
Unfinished,
|
||||||
|
/// The provided or the set of previously provided portions are invalid.
|
||||||
|
/// Unsealing has been reset.
|
||||||
|
InvalidReset,
|
||||||
|
/// Duplicate share
|
||||||
|
Duplicate,
|
||||||
|
/// Error processing share, invalid
|
||||||
|
InvalidRejected,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_default(pool: &DbPool) {
|
||||||
|
#[cfg(feature = "insecure-dev-sealing")]
|
||||||
|
let user_key = {
|
||||||
|
storage::sealing::init_insecure_in_db(&pool).await;
|
||||||
|
"INSECURE automatic unlock - TESTING ONLY"
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "insecure-dev-sealing"))]
|
||||||
|
let user_key = {
|
||||||
|
#[cfg(not(feature = "shamir"))]
|
||||||
|
{
|
||||||
|
simple::init_simple(&pool).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "shamir")]
|
||||||
|
{
|
||||||
|
shamir::init_shamir(&pool, 2, 5).await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let success = prepare_unseal(&pool).await;
|
||||||
|
warn!("New sealing password generated: {user_key:?}");
|
||||||
|
assert!(
|
||||||
|
success,
|
||||||
|
"Vault ought to have been initialized just now but it is not."
|
||||||
|
);
|
||||||
|
}
|
||||||
223
src/storage/sealing/shamir.rs
Normal file
223
src/storage/sealing/shamir.rs
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
use aes_gcm_siv::{
|
||||||
|
AeadCore, Aes256GcmSiv, KeyInit,
|
||||||
|
aead::{Aead, OsRng, generic_array::GenericArray},
|
||||||
|
};
|
||||||
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use p256::{NonZeroScalar, Scalar, SecretKey};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
use vsss_rs::{
|
||||||
|
DefaultShare, Error as VsssErr, IdentifierPrimeField, ReadableShareSet, ShareElement,
|
||||||
|
ValuePrimeField,
|
||||||
|
};
|
||||||
|
use zeroize::ZeroizeOnDrop;
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
use super::{write_new_root_key, Sealing, UnsealResult};
|
||||||
|
|
||||||
|
type P256Share = DefaultShare<IdentifierPrimeField<Scalar>, IdentifierPrimeField<Scalar>>;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, ZeroizeOnDrop)]
|
||||||
|
/// Differs from [P256Share] by containing Strings
|
||||||
|
struct ShamirPortion {
|
||||||
|
#[serde(rename = "i")]
|
||||||
|
pub identifier: Vec<u8>,
|
||||||
|
#[serde(rename = "v")]
|
||||||
|
pub value: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct ShamirBucket {
|
||||||
|
portions: Vec<ShamirPortion>,
|
||||||
|
protected_rk: Vec<u8>,
|
||||||
|
nonce: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealing for ShamirBucket {
|
||||||
|
fn new(protected_rk: Vec<u8>, nonce: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
portions: Vec::with_capacity(2),
|
||||||
|
protected_rk,
|
||||||
|
nonce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unseal(&mut self, key: String) -> UnsealResult {
|
||||||
|
let key = match BASE64_STANDARD.decode(key) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Portion could not be decoded: {e}");
|
||||||
|
return UnsealResult::InvalidRejected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let key_portion: ShamirPortion = match serde_json::from_slice(&key) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
info!("Portion could not be parsed: {e}");
|
||||||
|
return UnsealResult::InvalidRejected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.portions.contains(&key_portion) {
|
||||||
|
warn!("The supplied Shamir portion is already known. Duplication ignored.");
|
||||||
|
return UnsealResult::Duplicate;
|
||||||
|
}
|
||||||
|
self.portions.push(key_portion);
|
||||||
|
|
||||||
|
let abc = match join_keys(&self.portions) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
return match e {
|
||||||
|
VsssErr::SharingMinThreshold => {
|
||||||
|
info!("Shamir portion provided. Sharing threshold not reached.");
|
||||||
|
UnsealResult::Unfinished
|
||||||
|
},
|
||||||
|
VsssErr::SharingDuplicateIdentifier => unreachable!("Addition of duplicate keys should have been prevented by not recording them"),
|
||||||
|
e => {
|
||||||
|
error!("Unknown error occurred upon joining keys {e:?}");
|
||||||
|
unreachable!()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_bytes();
|
||||||
|
|
||||||
|
let cipher = match Aes256GcmSiv::new_from_slice(&abc) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
info!("Cipher could not be created from slice: {e}");
|
||||||
|
return UnsealResult::InvalidRejected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug_assert_eq!(self.nonce.len(), 12);
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(&self.nonce);
|
||||||
|
let root_key = cipher.decrypt(nonce, self.protected_rk.as_ref());
|
||||||
|
match root_key {
|
||||||
|
Ok(v) => UnsealResult::DoneConfidential(v),
|
||||||
|
Err(_) => {
|
||||||
|
// Err is opaque on purpose
|
||||||
|
self.portions.clear();
|
||||||
|
warn!(
|
||||||
|
"Enough shares have been provided but the set of shares is invalid. The set of shares has been reset."
|
||||||
|
);
|
||||||
|
UnsealResult::InvalidReset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shamir Secret Sharing does not verify a portion for validity,
|
||||||
|
/// unlike Feldman Verified Secret Sharing, which is built on Shamir.
|
||||||
|
/// "Validation" happens by attempting to decrypt the root key.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// List of encoded key portions
|
||||||
|
pub async fn init_shamir(pool: &DbPool, threshold: usize, limit: usize) -> Vec<String> {
|
||||||
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let nonce: GenericArray<u8, <Aes256GcmSiv as AeadCore>::NonceSize> =
|
||||||
|
Aes256GcmSiv::generate_nonce(&mut OsRng); // 96-bits; unique per message
|
||||||
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
|
||||||
|
let (user_key, protected_rk) = {
|
||||||
|
let key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let cipher = Aes256GcmSiv::new(&key);
|
||||||
|
let nonce: &[u8] = nonce.as_slice();
|
||||||
|
debug_assert_eq!(nonce.len(), 12);
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||||
|
let enc = cipher.encrypt(nonce, root_key.as_slice()).unwrap();
|
||||||
|
(key, enc)
|
||||||
|
};
|
||||||
|
|
||||||
|
let portions = share_keys(&mut OsRng, threshold, limit, &user_key);
|
||||||
|
|
||||||
|
log::debug!("Shared Keys: {portions:?}");
|
||||||
|
|
||||||
|
write_new_root_key(pool, protected_rk, "shamir", Some(nonce.as_slice())).await;
|
||||||
|
portions
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a Vec of Base64 encoded JSON-wrapped identifier-value pairs
|
||||||
|
fn share_keys(
|
||||||
|
mut osrng: &mut OsRng,
|
||||||
|
threshold: usize,
|
||||||
|
limit: usize,
|
||||||
|
root_key: &[u8],
|
||||||
|
) -> Vec<String> {
|
||||||
|
log::debug!("RK: {root_key:?}");
|
||||||
|
assert!(
|
||||||
|
threshold <= limit,
|
||||||
|
"Threshold cannot be higher than the number of shares (limit)"
|
||||||
|
);
|
||||||
|
|
||||||
|
let rk_array = GenericArray::from_slice(root_key);
|
||||||
|
let rk_scalar = NonZeroScalar::from_repr(*rk_array).unwrap();
|
||||||
|
let shared_secret = IdentifierPrimeField(*rk_scalar.as_ref());
|
||||||
|
let res =
|
||||||
|
vsss_rs::shamir::split_secret::<P256Share>(threshold, limit, &shared_secret, &mut osrng);
|
||||||
|
|
||||||
|
res.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
BASE64_STANDARD.encode(
|
||||||
|
json!(ShamirPortion {
|
||||||
|
identifier: f.identifier.to_vec(),
|
||||||
|
value: f.value.to_vec(),
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_keys(shares: &[ShamirPortion]) -> Result<SecretKey, vsss_rs::Error> {
|
||||||
|
let shares: Vec<P256Share> = shares
|
||||||
|
.iter()
|
||||||
|
.map(|portion| {
|
||||||
|
let identifier = IdentifierPrimeField::<Scalar>::from_slice(&portion.identifier)
|
||||||
|
.map_err(|e| {
|
||||||
|
info!("Portion could not be converted to IdentifierPrimeField: {e}");
|
||||||
|
VsssErr::InvalidShare
|
||||||
|
})?;
|
||||||
|
let value = ValuePrimeField::<Scalar>::from_slice(&portion.value).map_err(|e| {
|
||||||
|
info!("Portion could not be converted to ValuePrimeField: {e}");
|
||||||
|
VsssErr::InvalidShare
|
||||||
|
})?;
|
||||||
|
Ok(P256Share { identifier, value })
|
||||||
|
})
|
||||||
|
.collect::<Result<_, VsssErr>>()?;
|
||||||
|
|
||||||
|
let scalar = shares.combine()?;
|
||||||
|
// A little suboptimal thanks to CtOption
|
||||||
|
let nzs = match NonZeroScalar::from_repr(scalar.0.into()).into_option() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return Err(VsssErr::InvalidShare),
|
||||||
|
};
|
||||||
|
let sk = SecretKey::from(nzs);
|
||||||
|
Ok(sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_and_join() {
|
||||||
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
let kps = share_keys(&mut OsRng, 2, 5, &root_key);
|
||||||
|
|
||||||
|
let kps: Vec<_> = kps
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let b = BASE64_STANDARD
|
||||||
|
.decode(f)
|
||||||
|
.expect("A portion could not be decoded from BASE64");
|
||||||
|
serde_json::from_slice(&b).expect("A portion could not be parsed as a key pair")
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let k = join_keys(&kps).expect("Error on joining key pairs");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
root_key,
|
||||||
|
k.to_bytes().as_slice(),
|
||||||
|
"Original key and re-combined key from shares are not equal"
|
||||||
|
);
|
||||||
|
}
|
||||||
47
src/storage/sealing/simple.rs
Normal file
47
src/storage/sealing/simple.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use aes_gcm_siv::{
|
||||||
|
AeadCore, Aes256GcmSiv, KeyInit,
|
||||||
|
aead::{Aead, OsRng, generic_array::GenericArray},
|
||||||
|
};
|
||||||
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
use super::{write_new_root_key, Sealing, UnsealResult};
|
||||||
|
|
||||||
|
/// Pair of protected root key and nonce
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct SimpleSealing(Vec<u8>, Vec<u8>);
|
||||||
|
|
||||||
|
impl Sealing for SimpleSealing {
|
||||||
|
fn new(protected_rk: Vec<u8>, nonce: Vec<u8>) -> Self {
|
||||||
|
Self(protected_rk, nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unseal(&mut self, key: String) -> UnsealResult {
|
||||||
|
let key = BASE64_STANDARD.decode(key).unwrap();
|
||||||
|
let cipher = Aes256GcmSiv::new_from_slice(&key).unwrap();
|
||||||
|
debug_assert_eq!(self.1.len(), 12);
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(self.1.as_slice());
|
||||||
|
UnsealResult::DoneConfidential(cipher.decrypt(nonce, self.0.as_ref()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub async fn init_simple(pool: &DbPool) -> String {
|
||||||
|
let root_key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let nonce: GenericArray<u8, <Aes256GcmSiv as AeadCore>::NonceSize> =
|
||||||
|
Aes256GcmSiv::generate_nonce(&mut OsRng); // 96-bits; unique per message
|
||||||
|
let root_key = root_key.as_slice().to_owned();
|
||||||
|
|
||||||
|
let (user_key, protected_rk) = {
|
||||||
|
let key = Aes256GcmSiv::generate_key(&mut OsRng);
|
||||||
|
let cipher = Aes256GcmSiv::new(&key);
|
||||||
|
let nonce: &[u8] = nonce.as_slice();
|
||||||
|
debug_assert_eq!(nonce.len(), 12);
|
||||||
|
let nonce = aes_gcm_siv::aead::generic_array::GenericArray::from_slice(nonce);
|
||||||
|
let enc = cipher.encrypt(nonce, root_key.as_slice()).unwrap();
|
||||||
|
(key, enc)
|
||||||
|
};
|
||||||
|
write_new_root_key(pool, protected_rk, "simple", Some(nonce.as_slice())).await;
|
||||||
|
BASE64_STANDARD.encode(user_key)
|
||||||
|
}
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use base::create_mock_meta;
|
|
||||||
#[test]
|
|
||||||
fn test_update_secret() {
|
|
||||||
let db: sled::Db = sled::open("sled_db").unwrap();
|
|
||||||
update_secret(&db, "foo", TempSecret{version: -99, content: "cool".to_string()});
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_get_secret() {
|
|
||||||
let db: sled::Db = sled::open("sled_db").unwrap();
|
|
||||||
get_secret(&db, "foo");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_delete_secret(){
|
|
||||||
let db: sled::Db = sled::open("sled_db").unwrap();
|
|
||||||
delete_secret(&db, "foo");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_meta(){
|
|
||||||
let db: sled::Db = sled::open("sled_db").unwrap();
|
|
||||||
println!("writing metadata:");
|
|
||||||
update_secret_meta(&db, "metatest", create_mock_meta());
|
|
||||||
println!("getting metadata:");
|
|
||||||
get_secretmeta(&db, "metatest");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use sled::Db;
|
|
||||||
use base::{deserialize_metadata_struct, deserialize_secret_struct, serialize_metadata_json, serialize_secret_json, SecretMeta, TempSecret};
|
|
||||||
|
|
||||||
/// [TODO] Currently no proper versioning
|
|
||||||
/// inserts a secret. If there was already a secret in the given path, the version is incremented
|
|
||||||
fn update_secret(db: &Db, path: &str, mut secret: TempSecret) {
|
|
||||||
match get_secret(db, path) {
|
|
||||||
Some(old_secret) => {
|
|
||||||
// case secret found. TODO save it somewhere for versioning
|
|
||||||
secret.version = old_secret.version + 1;
|
|
||||||
#[cfg(test)]
|
|
||||||
print!("something was found. new version {} \n", secret.version)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// case new secret
|
|
||||||
secret.version = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if let secret_json = serialize_secret_json(&secret) {
|
|
||||||
// let _res = db.insert(path, secret_json); // maybe this can be handled cleaner
|
|
||||||
match serialize_secret_json(&secret) {
|
|
||||||
Ok(secret_json) => {
|
|
||||||
#[cfg(test)]
|
|
||||||
println!("String: {:?}", secret_json.clone());
|
|
||||||
let as_ivec = sled::IVec::from(secret_json.into_bytes()); // maybe outsource this in a fn later
|
|
||||||
#[cfg(test)]
|
|
||||||
println!("ivec: {:?}", as_ivec);
|
|
||||||
match db.insert(path, as_ivec) {
|
|
||||||
Ok(_) => println!("Secret inserted"),
|
|
||||||
Err(e) => eprintln!("Failed to insert secret: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("Failed to serialize secret: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// !TODO eliminate redundancy: refactor get and update functions to accept generic types!
|
|
||||||
|
|
||||||
// read and return a secret from the DB
|
|
||||||
//if there is no secret, return None
|
|
||||||
fn get_secret(db: &Db, path: &str) -> Option<TempSecret>{
|
|
||||||
let raw_secret;
|
|
||||||
match db.get(path) {
|
|
||||||
Ok(Some(ivec)) => {
|
|
||||||
raw_secret = ivec;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error on retrieving secret: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// outsource this in a fn later. TODO maybe deal with unwrap
|
|
||||||
let as_str = String::from_utf8(raw_secret.to_vec()).unwrap();
|
|
||||||
match deserialize_secret_struct(&as_str) {
|
|
||||||
Ok(secret) => {
|
|
||||||
#[cfg(test)]
|
|
||||||
println!("got some secret: {:?}", secret);
|
|
||||||
return Some(secret);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("error on secret deserialization: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO write abstract get_something fn
|
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#read-secret-metadata
|
|
||||||
fn get_secretmeta(db: &Db, path: &str) -> Option<SecretMeta>{
|
|
||||||
let raw_metadata;
|
|
||||||
match db.get(path) {
|
|
||||||
Ok(Some(ivec)) => {
|
|
||||||
raw_metadata = ivec;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error on retrieving metadata: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let as_str = String::from_utf8(raw_metadata.to_vec()).unwrap();
|
|
||||||
match deserialize_metadata_struct(&as_str) {
|
|
||||||
Ok(meta) => {
|
|
||||||
#[cfg(test)]
|
|
||||||
println!("got some metadata: {:?}", meta);
|
|
||||||
return Some(meta);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("error on secret deserialization: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently early version (copied from update_secret)
|
|
||||||
fn update_secret_meta(db: &Db, path: &str, mut meta: SecretMeta) {
|
|
||||||
match get_secretmeta(db, path) {
|
|
||||||
Some(meta) => {
|
|
||||||
// case secret found. TODO save it somewhere for versioning
|
|
||||||
#[cfg(test)]
|
|
||||||
print!("something was found. new version {:?} \n", meta)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match serialize_metadata_json(&meta) {
|
|
||||||
Ok(meta_json) => {
|
|
||||||
#[cfg(test)]
|
|
||||||
println!("String: {:?}", meta_json.clone());
|
|
||||||
let as_ivec = sled::IVec::from(meta_json.into_bytes()); // maybe outsource this in a fn later
|
|
||||||
#[cfg(test)]
|
|
||||||
println!("ivec: {:?}", as_ivec);
|
|
||||||
match db.insert(path, as_ivec) {
|
|
||||||
Ok(_) => println!("Metadata inserted"),
|
|
||||||
Err(e) => eprintln!("Failed to insert meta: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("Failed to serialize meta: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// TODO soft delete the secret version at path. can be undone with undelete_secret
|
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret
|
|
||||||
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions
|
|
||||||
|
|
||||||
/// hard delete secret at path
|
|
||||||
fn delete_secret(db: &Db, path: &str) {
|
|
||||||
let rem = db.remove(path);
|
|
||||||
}
|
|
||||||
16
src/sys.rs
16
src/sys.rs
|
|
@ -1,8 +1,16 @@
|
||||||
use axum::Router;
|
mod root_generation;
|
||||||
|
mod sealing;
|
||||||
|
|
||||||
use crate::storage::DatabaseDriver;
|
use axum::Router;
|
||||||
|
use root_generation::root_generation;
|
||||||
|
use sealing::sealing_routes;
|
||||||
|
|
||||||
|
use crate::storage::DbPool;
|
||||||
|
|
||||||
/// System routes
|
/// System routes
|
||||||
pub fn sys_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
|
pub fn sys_router(pool: DbPool) -> Router<DbPool> {
|
||||||
Router::new().with_state(pool)
|
Router::new()
|
||||||
|
.merge(sealing_routes())
|
||||||
|
.merge(root_generation())
|
||||||
|
.with_state(pool)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/sys/root_generation.rs
Normal file
12
src/sys/root_generation.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use axum::{Router, routing::post};
|
||||||
|
|
||||||
|
use crate::DbPool;
|
||||||
|
|
||||||
|
pub fn root_generation() -> Router<DbPool> {
|
||||||
|
Router::new()
|
||||||
|
// .route("/generate-root", get(get_root_generation_attempt))
|
||||||
|
.route("/generate-root", post(generate_new_root))
|
||||||
|
// .route("/generate-root", delete(cancel_generate_root))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn generate_new_root() {}
|
||||||
50
src/sys/sealing.rs
Normal file
50
src/sys/sealing.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
use axum::{
|
||||||
|
extract::State, routing::{get, post, put}, Json, Router
|
||||||
|
};
|
||||||
|
use log::warn;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::storage::{DbPool, sealing};
|
||||||
|
|
||||||
|
pub fn sealing_routes() -> Router<DbPool> {
|
||||||
|
Router::new()
|
||||||
|
.route("/seal", post(seal_post))
|
||||||
|
.route("/seal-status", get(seal_status_get))
|
||||||
|
.route("/unseal", post(unseal_post))
|
||||||
|
// WTF? Again? Its supposed to be POST but actually a PUT
|
||||||
|
.route("/unseal", put(unseal_post))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn seal_post(State(pool): State<DbPool>) {
|
||||||
|
sealing::reseal(&pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct UnsealRequest {
|
||||||
|
/// Required, unless `reset` is true
|
||||||
|
pub key: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
/// Specifies if previously-provided unseal keys are discarded and the unseal process is reset.
|
||||||
|
pub reset: bool,
|
||||||
|
// #[serde(default)]
|
||||||
|
// /// Used to migrate the seal from shamir to autoseal or autoseal to shamir. Must be provided on all unseal key calls.
|
||||||
|
// pub migrate: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unseal_post(State(pool): State<DbPool>, Json(req): Json<UnsealRequest>) -> Result<(), ()> {
|
||||||
|
if req.reset {
|
||||||
|
warn!("Unsealing progress has been reset on unseal request");
|
||||||
|
sealing::reseal(&pool).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(key) = req.key {
|
||||||
|
sealing::provide_key(key).await;
|
||||||
|
} else if !req.reset {
|
||||||
|
// No request key nor reset = bad request
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn seal_status_get(State(pool): State<DbPool>) {}
|
||||||
Loading…
Reference in a new issue