diff --git a/Cargo.toml b/Cargo.toml index 4ad564ff7..98f88ebeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,10 @@ exclude = [ # `wasm:auth-guest:build`. "languages/golang/stackencrypt/guest", "languages/golang/stackauth/guest", + # The AWS KMS demo: detached so the AWS SDK, and the + # `serde_json/preserve_order` it turns on, stay out of this workspace's + # `--all-features` builds. See examples/aws-kms-demo/README.md. + "examples/aws-kms-demo", ] [workspace.package] diff --git a/examples/aws-kms-demo/.gitignore b/examples/aws-kms-demo/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/examples/aws-kms-demo/.gitignore @@ -0,0 +1 @@ +/target diff --git a/examples/aws-kms-demo/Cargo.lock b/examples/aws-kms-demo/Cargo.lock new file mode 100644 index 000000000..ba65fc26a --- /dev/null +++ b/examples/aws-kms-demo/Cargo.lock @@ -0,0 +1,2731 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common 0.1.7", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", + "zeroize", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-config" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d7b388a9fc3a6db15a5ec778c38b354eff1364882c94d08e0252f7a47dcaa4" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 1.5.0", + "sha1", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93964ffdaf57857f544be3666a5f57570bb699e934700f11b49708f61bb556e" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-kms-demo" +version = "0.0.0" +dependencies = [ + "aws-config", + "aws-sdk-kms", + "stack-encrypt", + "tokio", + "uuid", + "vitaminc-kms", +] + +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "aws-runtime" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8a9911551b4ea6ca13805ef52ed96f7d2bbb43cc3b4a14cb0776a71f33cfaa" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "bytes-utils", + "fastrand", + "http 1.5.0", + "http-body 1.1.0", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-kms" +version = "1.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bbe8833a7ad3b970f2a6a856939c9cb171942dcdfd2ab65f2db3a8baaf73cc1" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.114.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12be2f9c8eef7f5fc919c96d538e629698469a02b4cb75408b26b1bd984ebe79" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7645db8724ea3b82fdccfb67e1b0f637c9d8ab0e7ef29d84884d8d8d73f805d" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.119.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e490aa904849b38e770922faac779dd245f5cf8be81f19065acf18d276e4ad" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 1.5.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2312577f088c9fbf4206dfdb884cf1de9407b43e1a923cbed5237775116fc24b" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 1.5.0", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02e407fb3b54891734224b9ffac8a71fdd35f542500fa1af95754a6b2beb316" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.64.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639b4d8f8555f24a9be649811c3eb0b4d4616f4d61daf0c32e28873bc1ea9af1" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c89cc3f1f281d659a67a519a1b5c6d445b5ce09fa7e5aee40c2c2707e9509d" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.27", + "h2 0.4.19", + "http 0.2.12", + "http 1.5.0", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.11.1", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.10", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.45", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.6", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.63.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3385d469edbe8b60cc72002784652b5efca39178192aa9cc4b44c9875c6bdc18" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e86338c869539a581bf161247762a6e87f92c5c075060057b5ed6d06632ed0c" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-query" +version = "0.62.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d1d71f6562be974caa85442ecd90194c40fdb5df045f182a6c2e872ce95056" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-smithy-xml", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e302ac1d88b99652489df31abdec6ac42a2ab2ac3982ad0ac49f64dfaf28ba" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "bytes", + "fastrand", + "http 1.5.0", + "http-body 1.1.0", + "http-body-util", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0730c16f91124c6a2abb4932c77e299288b3dd9f967ea2e9ec48cc6731e87a4" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api-macros", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.5.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-runtime-api-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221eaa237ddf1ca79b60d1372aad77e47f9c0ea5b3ce5099da8c61d027dc77b3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "aws-smithy-schema" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8f395d93304280b64b7632fea798d177e74897fe7f063416ce627cd6fa24829" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 1.5.0", +] + +[[package]] +name = "aws-smithy-types" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69bb407740a197147da48238ecc94498493c9e85445732360cec180296ca45f1" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.5.0", + "http-body 0.4.6", + "http-body 1.1.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.62.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b932c8d6dc127fc980eecd78f8694ae9b9551b69a93a7def2a199c1c0033daf" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209f3a6d82a6e9e5f94abbed94c7a26e1c052341002bf57a5fb5481f625896fc" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "rustc_version", + "tracing", +] + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake3" +version = "1.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" +dependencies = [ + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.1", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout", +] + +[[package]] +name = "cllw-ore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476f300d37a5029d3d9dd57145d4db50a23f40ae9b4d1374c44543978b906191" +dependencies = [ + "blake3", + "hex", + "subtle", + "thiserror 1.0.69", + "unicode-normalization", + "zeroize", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", + "ctutils", + "zeroize", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.5.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http 1.5.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http 1.5.0", + "http-body 1.1.0", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.19", + "http 1.5.0", + "http-body 1.1.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http 1.5.0", + "hyper 1.11.1", + "hyper-util", + "rustls 0.23.45", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.26.6", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http 1.5.0", + "http-body 1.1.0", + "httparse", + "hyper 1.11.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.5", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mutants" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ac067452ff1aca8c5002111bd6b1c895baee6e45fcbc44e0193aea17be56" + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bee6c73da26345c729282832b60b0363cf3dd9f4bfd81d8551b7a1c889a113" + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.15", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted 0.9.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted 0.9.0", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stack-encrypt" +version = "0.3.0" +dependencies = [ + "base64ct", + "cllw-ore", + "serde", + "stack-encrypt-derive", + "thiserror 1.0.69", + "uuid", + "vitaminc-aead", + "vitaminc-encrypt", + "vitaminc-hmac", + "vitaminc-kms", + "vitaminc-prf", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "stack-encrypt-derive" +version = "0.3.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[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]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[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 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.5", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls 0.23.45", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "sha1_smol", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vitaminc-aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cacd4dae485e98cfe5407e919187a8468dd933ea4234be5115b7d13a30f3c6" +dependencies = [ + "bytes", + "serde", + "vitaminc-aead-derive", + "vitaminc-context", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-random 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-aead-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff34056b70a8417ad3bf9db73b2bd3be97f448794566c6de2080bd8a7cd9b6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-async-traits" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-traits", +] + +[[package]] +name = "vitaminc-context" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7df52501fe33cdb3ee9e155b5242fac184c037d147e58c96e5143ed011252a8" +dependencies = [ + "mutants", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vitaminc-encrypt" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbd0e1748055e381e02033e08af385de879c4dcf32e0f6739ac0c0cf0c6f13d" +dependencies = [ + "aes-gcm", + "aws-lc-rs", + "vitaminc-aead", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-random 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-hmac" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fe558a5e92d4f2b3f811c5b67d863d9255da5f66d08a71d4f0dd3d0011c563" +dependencies = [ + "hmac", + "sha2", + "vitaminc-prf", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-kms" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "aws-sdk-kms", + "futures", + "thiserror 2.0.21", + "vitaminc-async-traits", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-traits", + "zeroize", +] + +[[package]] +name = "vitaminc-prf" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712d31d46d76b38c6b62deb35f516fc5636a35f90f39c0213d0ca9a052b4400b" +dependencies = [ + "mutants", + "thiserror 2.0.21", + "vitaminc-context", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vitaminc-protected" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7b514f605a63f88874ba46ffea81775fad64994475ab239cb19da159e884b7b" +dependencies = [ + "bitvec", + "digest 0.11.3", + "libc", + "serde", + "serde_bytes", + "subtle", + "thiserror 2.0.21", + "vitaminc-protected-derive 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-protected" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "bitvec", + "digest 0.11.3", + "libc", + "serde", + "serde_bytes", + "subtle", + "thiserror 2.0.21", + "vitaminc-protected-derive 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "vitaminc-protected-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb0a3d11afbe2c909f0cfc9996fe37fd89b1196eb0b8d89a66e28716360ee09" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-protected-derive" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-random" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7b1bfab35ee322de37269c6fd94d8cd94047a66f97cf6722e53e3321482167" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand", + "thiserror 2.0.21", + "vitaminc-protected 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vitaminc-random-derives 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "vitaminc-random" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand", + "thiserror 2.0.21", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-random-derives 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "vitaminc-random-derives" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0915e2bfb417ce11e23123fb7ec122af5d27d8cfd22d6ad6a2cf7a1c2dbb4712" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-random-derives" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "vitaminc-traits" +version = "0.5.1" +source = "git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb#b3bc6eb484f3ed1b933b5706f18b5e2fedc0d823" +dependencies = [ + "anyhow", + "bytes", + "rmp-serde", + "serde", + "thiserror 2.0.21", + "vitaminc-protected 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "vitaminc-random 0.5.1 (git+https://github.com/cipherstash/vitaminc?rev=b3bc6eb)", + "zeroize", +] + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.6", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] diff --git a/examples/aws-kms-demo/Cargo.toml b/examples/aws-kms-demo/Cargo.toml new file mode 100644 index 000000000..1a5b4854b --- /dev/null +++ b/examples/aws-kms-demo/Cargo.toml @@ -0,0 +1,38 @@ +# `stack-encrypt` running end to end against AWS KMS, kept out of the root +# cargo workspace on purpose. +# +# A standalone workspace, like the WASI guests under languages/golang and the +# fuzz crates. CI builds the root workspace with `--all-features`, and no +# feature gate can hide a feature from that, so an AWS feature on a workspace +# member puts the AWS SDK in every workspace build. `aws-smithy-http-client` +# turns on `serde_json/preserve_order`, which under feature unification +# changes JSON map ordering for every other crate in the workspace. Detached, +# this crate resolves its own graph, in its own Cargo.lock. The root +# mise.toml's Rust pin applies here too. +# +# Run: docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +# cd examples/aws-kms-demo && mise x -- cargo run +[package] +name = "aws-kms-demo" +description = "stack-encrypt encrypting and decrypting with AWS KMS as the key provider" +version = "0.0.0" +edition = "2021" +publish = false + +[workspace] + +[dependencies] +# `default-features = false`: the demo builds its cipher over its own +# keyset registry, so it needs neither ZeroKMS nor the HTTP transport. +stack-encrypt = { path = "../../packages/stack-encrypt", default-features = false } +# `AwsDataKeySource`. The same git source and the same `rev` spelling as +# stack-encrypt's own dependency, so the two resolve to one package: a +# second copy would be a different `KeyProvider` trait, and its providers +# would not satisfy stack-encrypt's `KeysetRegistry`. Move all of them +# together. +vitaminc-kms = { git = "https://github.com/cipherstash/vitaminc", rev = "b3bc6eb", default-features = false, features = ["aws"] } + +aws-config = "1.8.18" +aws-sdk-kms = "1.109.0" +tokio = { version = "1", features = ["macros", "rt"] } +uuid = "1" diff --git a/examples/aws-kms-demo/README.md b/examples/aws-kms-demo/README.md new file mode 100644 index 000000000..fdad1f8db --- /dev/null +++ b/examples/aws-kms-demo/README.md @@ -0,0 +1,93 @@ +# aws-kms-demo + +`stack-encrypt` encrypting and decrypting a value with **AWS KMS** as the key +provider instead of ZeroKMS. + +The chain has three pieces. None of them is specific to this demo: + +| Piece | Role | +| --- | --- | +| `vitaminc_kms::AwsDataKeySource<32>` | A data key source bound to one AWS KMS key. It is a `vitaminc-kms` `KeyProvider<32>`. | +| `stack_encrypt::registry::FixedIndexKeySource` | Pairs that source with the persisted `KeyId` of the keyset's index key, so it is also an `IndexKeyProvider<32>`. | +| `stack_encrypt::registry::StaticKeysetRegistry` | stack-encrypt's registry over a fixed table of keysets. Here the table has one keyset, which is the default. | + +`StackCipher` is then built over that registry, in the same way as over +ZeroKMS. The same chain works for Azure Key Vault, Cloud KMS and Vault +Transit: turn on the vendor feature of `vitaminc-kms` and use that vendor's +data key source. + +## Running it + +LocalStack Community's `kms` service is sufficient. You do not need a Pro +tier or an AWS account: + +```sh +docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +cd examples/aws-kms-demo && mise x -- cargo run +docker compose -f examples/aws-kms-demo/docker-compose.yml down +``` + +LocalStack binds to `127.0.0.1` only, because it accepts any credentials. + +If nothing listens on `localhost:4566`, the demo prints a skip notice and +exits with success. Every other failure is an error and exits with a +failure. This includes an AWS KMS that refuses the first call (`ListKeys`), +for example because of bad credentials or an IAM policy. + +| Variable | Effect | +| --- | --- | +| `AWS_KMS_DEMO_ENDPOINT_URL` | The KMS endpoint. Unset: LocalStack at `http://localhost:4566`, with fake credentials. Empty: the AWS SDK's own endpoint, region and credentials. | +| `AWS_KMS_DEMO_KEY_ID` | The AWS KMS key that the keyset is bound to. Unset: the demo creates a new symmetric key. | +| `AWS_KMS_DEMO_INDEX_KEY_ID` | The persisted index `KeyId`, as hex. Unset: the demo provisions a new one. It needs `AWS_KMS_DEMO_KEY_ID`. | + +To run against a real AWS account, set `AWS_KMS_DEMO_ENDPOINT_URL` to an empty +string. Supply credentials with `kms:ListKeys`, `kms:GenerateDataKey` and +`kms:Decrypt`, and `kms:CreateKey` if you do not set `AWS_KMS_DEMO_KEY_ID`. + +## How the index key's `KeyId` is provisioned + +stack-encrypt derives each keyset's equality, match and order terms from one +**index key**. On ZeroKMS the service derives that key from the keyset. AWS +KMS has no deterministic derivation, so the index key is an ordinary data key +that is stored once and read back on every run: + +1. **Provision once.** Call `GenerateDataKey` one time on the keyset's AWS + KMS key. Keep only the result's `key_id`. That is the `CiphertextBlob`, + which is the data key that AWS KMS encrypted. Do not keep the plaintext. +2. **Persist the `KeyId`.** Store it with the keyset's configuration, for + example in a configuration file or a secrets store. It is encrypted, but + it is as important as the AWS KMS key itself: if you lose it, all of the + keyset's index terms become unfindable. +3. **Read it back on every run.** Give it to `FixedIndexKeySource::new`. + When the cipher loads the keyset, it decrypts that `KeyId` with AWS KMS, + and the result is the index key. + +Do not provision a new `KeyId` at startup. A new `KeyId` gives a new index +key, and then no index term written before matches a query. + +The demo does step 1 when `AWS_KMS_DEMO_INDEX_KEY_ID` is unset, and it prints +the two values to set for the next run. With both values set, it skips steps +1 and 2 and reads the index key back, as a deployment does. + +The keyset's id (`KEYSET_ID` in `src/main.rs`) is configuration too. Every +leaf sealed under the keyset carries it, so choose it once and keep it with +the AWS KMS key and the index `KeyId`. + +## Why this is a separate crate + +It is detached from the root cargo workspace. Its `Cargo.toml` has its own +`[workspace]` table, the same as `languages/golang/stackencrypt/guest`, and the +root `Cargo.toml` lists it in `exclude`. + +CI builds and tests the root workspace with `--all-features`, and no feature +gate can hide a feature from that. An AWS feature on a workspace member would +put the AWS SDK in *every* workspace build. `aws-smithy-http-client` turns on +`serde_json/preserve_order`, which, under cargo's feature unification, changes +the JSON map order for every other crate in the workspace and breaks snapshot +tests. + +Detached, this crate resolves its own dependency graph in its own +`Cargo.lock`. It builds with the Rust toolchain that the root `mise.toml` +pins. Its `vitaminc-kms` dependency must use the same git source and `rev` as +stack-encrypt's. Otherwise cargo builds two copies, and `AwsDataKeySource` +does not implement the `KeyProvider` trait that stack-encrypt uses. diff --git a/examples/aws-kms-demo/docker-compose.yml b/examples/aws-kms-demo/docker-compose.yml new file mode 100644 index 000000000..1c62ccb47 --- /dev/null +++ b/examples/aws-kms-demo/docker-compose.yml @@ -0,0 +1,27 @@ +# LocalStack for the AWS KMS demo in this folder. +# +# LocalStack Community's `kms` service covers CreateKey, GenerateDataKey, +# Decrypt and ListKeys. No Pro tier is necessary. +# +# Usage: +# docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +# (cd examples/aws-kms-demo && mise x -- cargo run) +# docker compose -f examples/aws-kms-demo/docker-compose.yml down +services: + localstack: + # Pinned to the last Community-only major. `:stable` and `:latest` now + # bundle LocalStack's "unified" image, which stops at startup unless it + # has a paid LOCALSTACK_AUTH_TOKEN, also for services that were free. + # `:4` still runs the plain Community image with no token. + image: localstack/localstack:4 + ports: + # Loopback only: LocalStack accepts any credentials, so it must not + # be reachable from other hosts on the network. + - "127.0.0.1:4566:4566" + environment: + - SERVICES=kms + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"] + interval: 2s + timeout: 5s + retries: 15 diff --git a/examples/aws-kms-demo/src/main.rs b/examples/aws-kms-demo/src/main.rs new file mode 100644 index 000000000..6f1c02438 --- /dev/null +++ b/examples/aws-kms-demo/src/main.rs @@ -0,0 +1,183 @@ +//! `stack-encrypt` running end to end with AWS KMS as the key provider +//! instead of ZeroKMS. +//! +//! The chain is three pieces, and none of them is specific to this demo: +//! +//! - `vitaminc_kms::AwsDataKeySource<32>`: a data key source bound to one +//! AWS KMS key. It is a `vitaminc-kms` `KeyProvider<32>`. +//! - `FixedIndexKeySource`: pairs that source with the persisted `KeyId` of +//! the keyset's index key, so it is also an `IndexKeyProvider<32>`. +//! - `StaticKeysetRegistry`: stack-encrypt's registry over a fixed table of +//! keysets. Here the table has one keyset, which is the default. +//! +//! `StackCipher` is then built over that registry exactly as it is over +//! ZeroKMS. +//! +//! Run it against LocalStack: +//! +//! ```sh +//! docker compose -f examples/aws-kms-demo/docker-compose.yml up -d +//! cd examples/aws-kms-demo && mise x -- cargo run +//! ``` +//! +//! See README.md for the environment variables, for how to run it against a +//! real AWS account, and for how the index key's `KeyId` is provisioned. + +use std::error::Error; + +use aws_sdk_kms::error::{DisplayErrorContext, SdkError}; +use stack_encrypt::registry::{FixedIndexKeySource, KeyId, StaticKeyset, StaticKeysetRegistry}; +use stack_encrypt::{nonempty, StackCipherBuilder}; +use uuid::Uuid; +use vitaminc_kms::{AwsDataKeySource, GenerateDataKey}; + +/// AES-256, the only data key size stack-encrypt uses. +const KEY_SIZE: usize = 32; + +/// The keyset's id. Every leaf sealed under the keyset carries it, so a real +/// deployment chooses it once and keeps it with the keyset's configuration. +const KEYSET_ID: Uuid = Uuid::from_u128(0x6177_732d_6b6d_732d_6465_6d6f_0000_0001); + +const LOCALSTACK: &str = "http://localhost:4566"; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<(), Box> { + let Some(client) = build_client().await? else { + println!("LocalStack is not running on {LOCALSTACK}; skipping. See README.md."); + return Ok(()); + }; + + // An index KeyId is a data key that one AWS KMS key encrypted. With no + // key named, the demo would create a new one, which cannot decrypt it. + if env("AWS_KMS_DEMO_INDEX_KEY_ID").is_some() && env("AWS_KMS_DEMO_KEY_ID").is_none() { + return Err( + "AWS_KMS_DEMO_INDEX_KEY_ID needs the AWS_KMS_DEMO_KEY_ID it was made under".into(), + ); + } + + // The AWS KMS key the keyset is bound to. + let kms_key_id = match env("AWS_KMS_DEMO_KEY_ID") { + Some(key_id) => key_id, + None => { + let key_id = create_symmetric_key(&client).await?; + println!("created AWS KMS key {key_id}"); + key_id + } + }; + let source = AwsDataKeySource::::new(client, kms_key_id.clone()); + + // The index key's `KeyId`. Provisioned once per keyset and then read + // back on every run: a new one makes every earlier index term + // unfindable. See README.md. + let index_key_id = match env("AWS_KMS_DEMO_INDEX_KEY_ID") { + Some(hex) => KeyId::new(unhex(&hex)?), + None => { + let key_id = source.generate_data_key().await?.key_id; + println!( + "provisioned an index key. To keep this keyset's index terms, run again with\n \ + AWS_KMS_DEMO_KEY_ID={kms_key_id}\n \ + AWS_KMS_DEMO_INDEX_KEY_ID={}", + hex(key_id.as_bytes()) + ); + key_id + } + }; + + let registry = StaticKeysetRegistry::new(StaticKeyset::new( + KEYSET_ID, + FixedIndexKeySource::new(source, index_key_id), + )); + let cipher = StackCipherBuilder::new().registry(registry).init().await?; + println!("built a StackCipher over AWS KMS"); + + let sealed = cipher + .default_keyset() + .encrypt("hello from AWS KMS".to_string(), nonempty!("demo/greeting")) + .await?; + let opened: String = cipher.decrypt(sealed, nonempty!("demo/greeting")).await?; + assert_eq!(opened, "hello from AWS KMS"); + println!("round-tripped a value through stack-encrypt and AWS KMS: {opened:?}"); + + Ok(()) +} + +/// An environment variable, or `None` when it is unset or empty. +fn env(name: &str) -> Option { + std::env::var(name).ok().filter(|value| !value.is_empty()) +} + +/// The AWS KMS client, or `None` when the demo targets LocalStack and +/// nothing is listening there. +/// +/// Only that case is a skip. Any answer from KMS that refuses the call (bad +/// credentials, an IAM policy without `kms:ListKeys`) is an error, and so is +/// an unreachable endpoint the caller chose: either means the demo cannot +/// run as configured, and exiting with success would hide it. +async fn build_client() -> Result, Box> { + use aws_config::{BehaviorVersion, Region}; + + // Unset: LocalStack. Empty: the SDK's own endpoint and credentials. + let endpoint_url = + std::env::var("AWS_KMS_DEMO_ENDPOINT_URL").unwrap_or_else(|_| LOCALSTACK.to_string()); + let localstack = endpoint_url == LOCALSTACK; + + let mut loader = aws_config::defaults(BehaviorVersion::v2026_01_12()); + if !endpoint_url.is_empty() { + // A local endpoint: fixed region, and fake static credentials, + // which LocalStack accepts. + let credentials = aws_sdk_kms::config::Credentials::new("fake", "fake", None, None, "demo"); + loader = loader + .region(Region::new("us-east-1")) + .credentials_provider(credentials) + .endpoint_url(endpoint_url); + } + let client = aws_sdk_kms::Client::new(&loader.load().await); + + // A cheap call that fails fast, before the demo creates anything. + match client.list_keys().limit(1).send().await { + Ok(_) => Ok(Some(client)), + Err(SdkError::DispatchFailure(_) | SdkError::TimeoutError(_)) if localstack => Ok(None), + Err(error) => Err(format!( + "AWS KMS refused or did not answer ListKeys: {}", + DisplayErrorContext(&error) + ) + .into()), + } +} + +async fn create_symmetric_key(client: &aws_sdk_kms::Client) -> Result> { + use aws_sdk_kms::types::{KeySpec, KeyUsageType}; + + // `EncryptDecrypt` usage and a symmetric spec are what GenerateDataKey + // and Decrypt, the two calls `AwsDataKeySource` makes, require. + let key = client + .create_key() + .key_usage(KeyUsageType::EncryptDecrypt) + .key_spec(KeySpec::SymmetricDefault) + .send() + .await + .map_err(|error| format!("CreateKey failed: {}", DisplayErrorContext(&error)))?; + + Ok(key + .key_metadata + .map(|metadata| metadata.key_id) + .ok_or("AWS KMS did not return key metadata")?) +} + +fn hex(bytes: &[u8]) -> String { + bytes.iter().map(|byte| format!("{byte:02x}")).collect() +} + +fn unhex(hex: &str) -> Result, Box> { + if !hex.len().is_multiple_of(2) { + return Err("AWS_KMS_DEMO_INDEX_KEY_ID must be hex, two digits a byte".into()); + } + (0..hex.len()) + .step_by(2) + .map(|i| { + hex.get(i..i + 2) + .and_then(|pair| u8::from_str_radix(pair, 16).ok()) + .ok_or_else(|| "AWS_KMS_DEMO_INDEX_KEY_ID must be hex".into()) + }) + .collect() +} diff --git a/packages/stack-encrypt/CHANGELOG.md b/packages/stack-encrypt/CHANGELOG.md index b838190ac..a4e2d2c72 100644 --- a/packages/stack-encrypt/CHANGELOG.md +++ b/packages/stack-encrypt/CHANGELOG.md @@ -250,6 +250,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `NoRegistry`, and the `vitaminc-kms` provider types an implementor names. - `StackCipher::registry`, and `FakeKeysetRegistry::resolves` (a counter of keyset lookups) for tests. +- **`registry::StaticKeysetRegistry`: keysets on AWS KMS, Azure Key Vault, + Cloud KMS or Vault Transit.** A fixed table of keysets, each an id, an + optional name and a key provider. The first keyset is the default; a + keyset resolves by id, or by name if it has one, and anything else is + `Error::UnknownKeyset`. It does not read format-1 leaves. Build each + provider as `registry::FixedIndexKeySource` (now re-exported) over a + `vitaminc-kms` data key source and the persisted `KeyId` of the keyset's + index key. `StaticKeyset`, `SharedProvider` and `StaticRegistryError` come + with it. `examples/aws-kms-demo` runs it on AWS KMS. ## [0.2.0] - 2026-10-04 diff --git a/packages/stack-encrypt/src/lib.rs b/packages/stack-encrypt/src/lib.rs index 9f7d9bd55..033b785b5 100644 --- a/packages/stack-encrypt/src/lib.rs +++ b/packages/stack-encrypt/src/lib.rs @@ -175,6 +175,19 @@ endpoint — are `StackKmsBuilder`'s, and the two keyset-cache knobs are "# )] //! +//! # Other key providers +//! +//! ZeroKMS is one backend. AWS KMS, Azure Key Vault, Cloud KMS and Vault +//! Transit reach a cipher as `vitaminc-kms` key providers: depend on +//! `vitaminc-kms` with the vendor's feature, wrap the vendor's data key +//! source in [`registry::FixedIndexKeySource`] with the persisted `KeyId` of +//! the keyset's index key, and put each keyset in a +//! [`registry::StaticKeysetRegistry`]. That `vitaminc-kms` must be the same +//! copy this crate depends on (the same git source and `rev` until it is on +//! crates.io), or its providers implement a different `KeyProvider` trait. +//! `examples/aws-kms-demo` at the repository root runs this end to end on +//! AWS KMS, and its README says how the index `KeyId` is provisioned. +//! //! # Testing without ZeroKMS //! //! `stack_encrypt::registry::fake::FakeKeysetRegistry` is an in-memory stub that needs no diff --git a/packages/stack-encrypt/src/registry.rs b/packages/stack-encrypt/src/registry.rs index 3151a1c06..4bb176f6e 100644 --- a/packages/stack-encrypt/src/registry.rs +++ b/packages/stack-encrypt/src/registry.rs @@ -3,7 +3,8 @@ //! //! A [`KeysetRegistry`] is the deployment's map. ZeroKMS's implementation //! asks the service and gets a keyset back; a vendor deployment's reads a -//! static table of backend keys it was configured with. Either way the +//! static table of backend keys it was configured with +//! ([`StaticKeysetRegistry`]). Either way the //! answer is one [`vitaminc_kms::provider::KeyProvider`] bound to one //! backend key, which is the only shape `vitaminc-kms` offers — selection //! is this crate's business, not that crate's. See cipherstash/vitaminc @@ -25,7 +26,8 @@ use vitaminc_kms::provider::MaybeSend; /// differently-sourced `vitaminc-kms` is a different `KeyProvider` trait at /// compile time, and an impl written against it would not satisfy this one. pub use vitaminc_kms::provider::{ - Binding, BindingSupport, IndexKeyProvider, KeyProvider, MaybeSend as ProviderMaybeSend, + Binding, BindingSupport, FixedIndexKeySource, IndexKeyProvider, KeyProvider, + MaybeSend as ProviderMaybeSend, }; pub use vitaminc_kms::{ GeneratedDataKey, IndexKeyMaterial, KeyId, KeyIsolation, KeyReconstruction, @@ -35,9 +37,12 @@ pub use vitaminc_protected_kms::Protected as ProviderProtected; #[cfg(any(test, feature = "test-support"))] pub mod fake; +mod static_keysets; #[cfg(feature = "zerokms")] mod zerokms; +pub use static_keysets::{SharedProvider, StaticKeyset, StaticKeysetRegistry, StaticRegistryError}; + /// A keyset's identity: globally unique, carried in every sealed leaf, and /// never re-checked once resolved. /// diff --git a/packages/stack-encrypt/src/registry/static_keysets.rs b/packages/stack-encrypt/src/registry/static_keysets.rs new file mode 100644 index 000000000..0d9c3b072 --- /dev/null +++ b/packages/stack-encrypt/src/registry/static_keysets.rs @@ -0,0 +1,378 @@ +//! A [`KeysetRegistry`] over a fixed table of keysets, for a deployment +//! whose backend has no keyset service of its own. +//! +//! On ZeroKMS a keyset is a service-side object. On AWS KMS, Azure Key +//! Vault, Cloud KMS or Vault Transit there is no such object, so a keyset is +//! a deployment configuration entry: an id, an optional name, and a +//! provider bound to one backend key together with the persisted `KeyId` of +//! that key's index key. `vitaminc-kms`'s +//! [`FixedIndexKeySource`](super::FixedIndexKeySource) is exactly that +//! provider. See ADR 0007. +//! +//! This module has no vendor dependency. It is generic over the provider, +//! so the vendor SDK is the caller's dependency, through `vitaminc-kms` and +//! the vendor feature the caller turns on. + +use std::sync::Arc; + +use super::{ + Binding, BindingSupport, GeneratedDataKey, IndexKeyMaterial, IndexKeyProvider, KeyId, + KeyIsolation, KeyProvider, KeyReconstruction, KeysetId, KeysetRef, KeysetRegistry, + ProviderProtected, Resolved, +}; + +/// One entry of a [`StaticKeysetRegistry`]: a keyset id, an optional name, +/// and the provider that serves the keyset. +pub struct StaticKeyset

{ + id: KeysetId, + name: Option, + provider: P, +} + +impl

StaticKeyset

{ + /// A keyset with this id and no name. It resolves by id only. + /// + /// The id is what every leaf sealed under the keyset carries. Choose it + /// once, and keep it with the provider's backend key and index `KeyId`: + /// a leaf whose keyset id the registry does not know fails with + /// [`Error::UnknownKeyset`](crate::Error::UnknownKeyset). + pub fn new(id: impl Into, provider: P) -> Self { + Self { + id: id.into(), + name: None, + provider, + } + } + + /// Give the keyset a name, so that it also resolves by that name. + pub fn named(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } +} + +/// Why a [`StaticKeysetRegistry`] refused a keyset. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum StaticRegistryError { + /// Two keysets have the same id. A leaf names its keyset by id, so the + /// id must select one keyset only. + #[error("two keysets have the id {0}")] + DuplicateId(KeysetId), + /// Two keysets have the same name. + #[error("two keysets have the name {0:?}")] + DuplicateName(String), +} + +/// A provider that a [`StaticKeysetRegistry`] holds once and hands out on +/// every resolution. +/// +/// A registry gives a provider by value each time it resolves a keyset, and +/// a provider such as `FixedIndexKeySource` is not `Clone`. This wrapper +/// shares one provider through an `Arc`, so each resolution is the same +/// provider, with the same client and the same connection pool. It +/// forwards every call, and the three capability constants, to that +/// provider. +pub struct SharedProvider

(Arc

); + +impl

Clone for SharedProvider

{ + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } +} + +impl

SharedProvider

{ + /// The provider this shares. + pub fn inner(&self) -> &P { + &self.0 + } +} + +impl KeyProvider for SharedProvider

+where + P: KeyProvider + Send + Sync, +{ + type Error = P::Error; + + const RECONSTRUCTION: KeyReconstruction = P::RECONSTRUCTION; + const ISOLATION: KeyIsolation = P::ISOLATION; + const BINDING: BindingSupport = P::BINDING; + + async fn generate_keys( + &self, + bindings: &[Binding<'_>], + ) -> Result>, Self::Error> { + self.0.generate_keys(bindings).await + } + + async fn retrieve_keys( + &self, + keys: &[(KeyId, Binding<'_>)], + ) -> Result>, Self::Error> { + self.0.retrieve_keys(keys).await + } +} + +impl IndexKeyProvider for SharedProvider

+where + P: IndexKeyProvider + Send + Sync, +{ + type Error = P::Error; + + async fn load_index_key(&self) -> Result, Self::Error> { + self.0.load_index_key().await + } +} + +struct Entry

{ + id: KeysetId, + name: Option, + provider: SharedProvider

, +} + +impl

From> for Entry

{ + fn from(keyset: StaticKeyset

) -> Self { + Self { + id: keyset.id, + name: keyset.name, + provider: SharedProvider(Arc::new(keyset.provider)), + } + } +} + +/// A [`KeysetRegistry`] over a fixed table of keysets, all served by one +/// provider type. +/// +/// The first keyset is the default: [`KeysetRef::Default`] resolves to it, +/// and [`StackCipher`](crate::StackCipher) loads it when the cipher is +/// built. A keyset resolves by its id, and by its name if it has one. Any +/// other reference is `Ok(None)`, which the cipher reports as +/// [`Error::UnknownKeyset`](crate::Error::UnknownKeyset). The registry +/// never fails to answer, so its error type is +/// [`Infallible`](std::convert::Infallible). +/// +/// It does not read format-1 leaves +/// ([`READS_V1_LEAVES`](KeysetRegistry::READS_V1_LEAVES) is `false`): only +/// ZeroKMS wrote them. +/// +/// ``` +/// # async fn example() -> Result<(), Box> { +/// use stack_encrypt::registry::{ +/// Binding, FixedIndexKeySource, KeyProvider, ProviderProtected, StaticKeyset, +/// StaticKeysetRegistry, +/// }; +/// use stack_encrypt::{nonempty, StackCipherBuilder}; +/// use uuid::Uuid; +/// +/// // A data key source bound to one backend key. In a deployment this is, +/// // for example, `vitaminc_kms::AwsDataKeySource<32>`. +/// let source = vitaminc_kms::provider::FakeKeyProvider::<32>::with_index_key( +/// ProviderProtected::new([7; 32]), +/// ); +/// +/// // Provision the index key once: generate one data key and store its +/// // `KeyId` with the deployment's configuration. Every later run reads +/// // that `KeyId` back. A new one makes all earlier index terms unfindable. +/// let index_key_id = source.generate_keys(&[Binding::EMPTY]).await?.remove(0).key_id; +/// +/// let registry = StaticKeysetRegistry::new( +/// StaticKeyset::new(Uuid::from_u128(1), FixedIndexKeySource::new(source, index_key_id)) +/// .named("main"), +/// ); +/// let cipher = StackCipherBuilder::new().registry(registry).init().await?; +/// let sealed = cipher +/// .keyset("main") +/// .await? +/// .encrypt("hello".to_string(), nonempty!("greeting")) +/// .await?; +/// let opened: String = cipher.decrypt(sealed, nonempty!("greeting")).await?; +/// assert_eq!(opened, "hello"); +/// # Ok(()) +/// # } +/// # tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(example()).unwrap(); +/// ``` +pub struct StaticKeysetRegistry

{ + /// The default keyset first, then the others in the order they were + /// added. A deployment has a few keysets, so a scan is enough. + keysets: Vec>, +} + +impl

StaticKeysetRegistry

{ + /// A registry whose default keyset is `default`. + pub fn new(default: StaticKeyset

) -> Self { + Self { + keysets: vec![default.into()], + } + } + + /// Add a keyset that a caller selects by id or by name. + /// + /// A keyset whose id or name is already in the registry is refused: an + /// id or a name must select one keyset only. + pub fn with_keyset(mut self, keyset: StaticKeyset

) -> Result { + if self.keysets.iter().any(|entry| entry.id == keyset.id) { + return Err(StaticRegistryError::DuplicateId(keyset.id)); + } + if let Some(name) = &keyset.name { + if self.find_name(name).is_some() { + return Err(StaticRegistryError::DuplicateName(name.clone())); + } + } + self.keysets.push(keyset.into()); + Ok(self) + } + + /// The id of the default keyset. + pub fn default_id(&self) -> KeysetId { + self.default_entry().id + } + + fn default_entry(&self) -> &Entry

{ + // `new` puts the default first, and nothing removes an entry. + &self.keysets[0] + } + + fn find_name(&self, name: &str) -> Option<&Entry

> { + self.keysets + .iter() + .find(|entry| entry.name.as_deref() == Some(name)) + } + + fn find(&self, keyset: &KeysetRef) -> Option<&Entry

> { + match keyset { + KeysetRef::Default => Some(self.default_entry()), + KeysetRef::Id(id) => self.keysets.iter().find(|entry| entry.id == *id), + KeysetRef::Name(name) => self.find_name(name), + } + } +} + +impl

KeysetRegistry for StaticKeysetRegistry

+where + P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, +{ + type Provider = SharedProvider

; + type Error = std::convert::Infallible; + + // Only ZeroKMS wrote format-1 leaves, and their key id is ZeroKMS's + // `iv ‖ tag`, which no other backend parses. + const READS_V1_LEAVES: bool = false; + + async fn resolve( + &self, + keyset: &KeysetRef, + ) -> Result>, Self::Error> { + Ok(self.find(keyset).map(|entry| Resolved { + id: entry.id, + // The name asked for, not a property of the keyset: a selection + // by id or by default looked nothing up and carries none. + name: keyset.name().map(str::to_owned), + provider: entry.provider.clone(), + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use uuid::Uuid; + use vitaminc_kms::provider::FakeKeyProvider; + use vitaminc_protected_kms::Protected; + + fn provider(seed: u8) -> FakeKeyProvider<32> { + FakeKeyProvider::with_index_key(Protected::new([seed; 32])) + } + + fn id(n: u128) -> KeysetId { + KeysetId::new(Uuid::from_u128(n)) + } + + async fn resolved( + registry: &StaticKeysetRegistry>, + keyset: impl Into, + ) -> Option<(KeysetId, Option)> { + let Ok(found) = registry.resolve(&keyset.into()).await; + found.map(|resolved| (resolved.id, resolved.name)) + } + + fn registry() -> StaticKeysetRegistry> { + StaticKeysetRegistry::new(StaticKeyset::new(id(1), provider(1)).named("main")) + .with_keyset(StaticKeyset::new(id(2), provider(2)).named("acme")) + .unwrap() + .with_keyset(StaticKeyset::new(id(3), provider(3))) + .unwrap() + } + + #[tokio::test] + async fn the_default_is_the_first_keyset() { + let registry = registry(); + assert_eq!(registry.default_id(), id(1)); + assert_eq!( + resolved(®istry, KeysetRef::Default).await, + Some((id(1), None)), + "a selection by default looked no name up" + ); + } + + #[tokio::test] + async fn a_keyset_resolves_by_its_id() { + let registry = registry(); + for n in 1..=3 { + assert_eq!(resolved(®istry, id(n)).await, Some((id(n), None))); + } + } + + #[tokio::test] + async fn a_named_keyset_resolves_by_its_name_and_carries_it() { + let registry = registry(); + assert_eq!( + resolved(®istry, "acme").await, + Some((id(2), Some("acme".to_owned()))) + ); + assert_eq!( + resolved(®istry, "main").await, + Some((id(1), Some("main".to_owned()))), + "the default keyset resolves by its name too" + ); + } + + #[tokio::test] + async fn anything_else_is_the_registrys_own_no() { + let registry = registry(); + assert_eq!(resolved(®istry, id(4)).await, None); + assert_eq!(resolved(®istry, "other").await, None); + assert_eq!( + resolved(®istry, "").await, + None, + "a keyset with no name does not answer to the empty name" + ); + } + + #[test] + fn an_id_or_a_name_selects_one_keyset_only() { + assert_eq!( + registry() + .with_keyset(StaticKeyset::new(id(3), provider(9))) + .err(), + Some(StaticRegistryError::DuplicateId(id(3))) + ); + assert_eq!( + registry() + .with_keyset(StaticKeyset::new(id(9), provider(9)).named("main")) + .err(), + Some(StaticRegistryError::DuplicateName("main".to_owned())) + ); + assert!( + registry() + .with_keyset(StaticKeyset::new(id(9), provider(9))) + .is_ok(), + "two keysets with no name do not collide" + ); + } + + #[test] + fn vendor_keysets_do_not_read_format_1_leaves() { + const { + assert!(!> as KeysetRegistry>::READS_V1_LEAVES) + }; + } +} diff --git a/packages/stack-encrypt/tests/registry_static.rs b/packages/stack-encrypt/tests/registry_static.rs new file mode 100644 index 000000000..26d812593 --- /dev/null +++ b/packages/stack-encrypt/tests/registry_static.rs @@ -0,0 +1,193 @@ +//! `StaticKeysetRegistry`: a vendor deployment's keysets, end to end through +//! `StackCipher`. +//! +//! Each keyset is served the way a vendor keyset is: a data key source bound +//! to one backend key, wrapped in `FixedIndexKeySource` with the `KeyId` of +//! an index key provisioned once. `FakeKeyProvider` stands in for the vendor +//! source. It binds each key to its descriptor and refuses a key id it did +//! not mint, so the index key here really is the provisioned key. + +use stack_encrypt::registry::{ + Binding, FixedIndexKeySource, IndexKeyProvider, KeyId, KeyProvider, ProviderProtected, + StaticKeyset, StaticKeysetRegistry, +}; +use stack_encrypt::{ + nonempty, CipherText, Error, KeysetId, KeysetRegistry, SealedValue, StackCipher, + StackCipherBuilder, StackCipherText, +}; +use uuid::Uuid; +use vitaminc_kms::provider::FakeKeyProvider; +use vitaminc_protected_kms::Controlled; + +type Source = FixedIndexKeySource>; + +const MAIN: Uuid = Uuid::from_u128(0x0a); +const ACME: Uuid = Uuid::from_u128(0x0b); + +/// A vendor keyset as a deployment configures it: the source, and the +/// `KeyId` of the index key it provisioned once. +async fn provisioned(seed: u8) -> (Source, KeyId) { + // The fake's own index key is never used: `FixedIndexKeySource` asks the + // source for the provisioned key instead. + let source = FakeKeyProvider::with_index_key(ProviderProtected::new([seed; 32])); + let index_key_id = source + .generate_keys(&[Binding::EMPTY]) + .await + .expect("mint the index key") + .remove(0) + .key_id; + ( + FixedIndexKeySource::new(source, index_key_id.clone()), + index_key_id, + ) +} + +async fn cipher() -> StackCipher> { + let (main, _) = provisioned(1).await; + let (acme, _) = provisioned(2).await; + let registry = StaticKeysetRegistry::new(StaticKeyset::new(MAIN, main).named("main")) + .with_keyset(StaticKeyset::new(ACME, acme).named("acme")) + .expect("distinct ids and names"); + StackCipherBuilder::new() + .registry(registry) + .init() + .await + .expect("build cipher") +} + +/// The index key a keyset serves is the key its source minted for the +/// provisioned `KeyId`, retrieved under the empty binding, and the shared +/// provider the registry hands out serves that same key. +#[tokio::test] +async fn the_index_key_is_the_provisioned_key() { + let (source, index_key_id) = provisioned(1).await; + let minted = source + .retrieve_keys(&[(index_key_id, Binding::EMPTY)]) + .await + .expect("the source minted it") + .remove(0); + + let registry = StaticKeysetRegistry::new(StaticKeyset::new(MAIN, source)); + let resolved = registry + .resolve(&MAIN.into()) + .await + .expect("infallible") + .expect("known"); + let served = resolved.provider.load_index_key().await.expect("index key"); + assert_eq!(served.0.risky_unwrap(), minted.risky_unwrap()); + let _: &Source = resolved.provider.inner(); +} + +/// A value sealed under a keyset selected by name opens again, and the leaf +/// names the keyset by its id. +#[tokio::test] +async fn a_value_round_trips_under_each_keyset() { + let cipher = cipher().await; + + for (selector, id) in [("main", MAIN), ("acme", ACME)] { + let keyset = cipher.keyset(selector).await.expect("known keyset"); + assert_eq!(keyset.keyset_id(), KeysetId::new(id)); + let sealed: StackCipherText = keyset + .encrypt("hello".to_string(), nonempty!("greeting")) + .await + .expect("seal"); + let CipherText::Single(leaf) = &sealed else { + panic!("a scalar seals to one leaf"); + }; + assert_eq!(leaf.keyset_id(), KeysetId::new(id)); + + let opened: String = cipher + .decrypt(sealed, nonempty!("greeting")) + .await + .expect("open"); + assert_eq!(opened, "hello"); + } + + let by_default: StackCipherText = cipher + .default_keyset() + .encrypt(7u32, nonempty!("count")) + .await + .expect("seal"); + let opened: u32 = cipher + .keyset(KeysetId::new(MAIN)) + .await + .expect("known by id") + .decrypt(by_default, nonempty!("count")) + .await + .expect("the default keyset is MAIN"); + assert_eq!(opened, 7); +} + +/// Two keysets have two index keys. One shared index key would make every +/// keyset's terms comparable with every other's. +#[tokio::test] +async fn each_keyset_serves_its_own_index_key() { + let cipher = cipher().await; + let registry = cipher.registry(); + let mut keys = Vec::new(); + for selector in ["main", "acme"] { + let resolved = registry + .resolve(&selector.into()) + .await + .expect("infallible") + .expect("known"); + let key = resolved.provider.load_index_key().await.expect("index key"); + keys.push(key.0.risky_unwrap()); + } + assert_ne!(keys[0], keys[1]); +} + +/// A keyset the table does not hold is the registry's own "no", which the +/// cipher reports as `UnknownKeyset`. +#[tokio::test] +async fn an_unknown_keyset_is_refused() { + let cipher = cipher().await; + for selector in [ + stack_encrypt::KeysetRef::from("other"), + stack_encrypt::KeysetRef::from(Uuid::from_u128(0x0c)), + ] { + let refused = cipher.keyset(selector).await.map(|_| ()); + assert!( + matches!(refused, Err(Error::UnknownKeyset { .. })), + "{refused:?}" + ); + } +} + +/// `"alice@example.com"`, sealed by stack-encrypt 0.2 in the format-1 layout +/// under keyset `5e7f0000-0000-4000-8000-000000000001` (the fixture in +/// `tests/format_v1.rs`). +const EMAIL_V1: &str = "015e7f000000004000800000000000000101010101010101010101010101010101100076312d666978747572652d7461672d310153767116f8f0c7450ebd6cf71a4afd93d9d7d037b51a48c3ab9d68480cf3ba4dc5d6a986ccbafb60d629baf402"; +const V1_KEYSET: Uuid = Uuid::from_u128(0x5e7f_0000_0000_4000_8000_0000_0000_0001); + +/// Only ZeroKMS wrote format-1 leaves, so a vendor registry refuses one with +/// the error that says why, even when it holds a keyset with that id. +#[tokio::test] +async fn a_format_1_leaf_is_refused() { + let (main, _) = provisioned(1).await; + let (v1, _) = provisioned(3).await; + let registry = StaticKeysetRegistry::new(StaticKeyset::new(MAIN, main)) + .with_keyset(StaticKeyset::new(V1_KEYSET, v1)) + .expect("distinct ids"); + let cipher = StackCipherBuilder::new() + .registry(registry) + .init() + .await + .expect("build cipher"); + + let bytes: Vec = (0..EMAIL_V1.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&EMAIL_V1[i..i + 2], 16).expect("hex")) + .collect(); + let leaf = SealedValue::from_bytes(&bytes).expect("a format-1 leaf parses"); + let opened: Result = cipher + .decrypt(CipherText::Single(leaf), "users/email") + .await; + assert!( + matches!( + opened, + Err(Error::V1LeafNeedsZeroKms { keyset_id }) if keyset_id == KeysetId::new(V1_KEYSET) + ), + "{opened:?}" + ); +} diff --git a/packages/stack-encrypt/tests/ui/execution_callback.stderr b/packages/stack-encrypt/tests/ui/execution_callback.stderr index 14048d4d6..1e8996202 100644 --- a/packages/stack-encrypt/tests/ui/execution_callback.stderr +++ b/packages/stack-encrypt/tests/ui/execution_callback.stderr @@ -5,24 +5,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `Encryption` --> src/target/operations.rs | @@ -39,24 +46,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | |__________^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `Encryption` --> src/target/operations.rs | @@ -70,24 +84,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | ^^^^^^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `KeysetCipher` --> src/keyset.rs | @@ -101,24 +122,31 @@ error[E0277]: the trait bound `(): KeysetRegistry` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `KeysetRegistry` is not implemented for `()` | help: the following other types implement trait `KeysetRegistry` - --> src/registry.rs + --> src/registry/zerokms.rs | - | impl KeysetRegistry for NoRegistry { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | / impl KeysetRegistry for Arc> + | | where + | | C: stack_auth::AuthStrategyBounds, + | | for<'a> &'a C: stack_auth::AuthStrategy, + | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, + | |_____________________________________________________^ `Arc>` | ::: src/registry/fake.rs | | impl KeysetRegistry for FakeKeysetRegistry { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `FakeKeysetRegistry` | - ::: src/registry/zerokms.rs + ::: src/registry.rs | - | / impl KeysetRegistry for Arc> + | impl KeysetRegistry for NoRegistry { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `NoRegistry` + | + ::: src/registry/static_keysets.rs + | + | / impl

KeysetRegistry for StaticKeysetRegistry

| | where - | | C: stack_auth::AuthStrategyBounds, - | | for<'a> &'a C: stack_auth::AuthStrategy, - | | Conn: stack_kms::ZeroKMSConnection + Send + Sync, - | |_____________________________________________________^ `Arc>` + | | P: KeyProvider<32> + IndexKeyProvider<32> + Send + Sync, + | |____________________________________________________________^ `StaticKeysetRegistry

` note: required by a bound in `stack_encrypt::Pending` --> src/target/pending.rs |