@voxgig/sdkgen 4.20.0 → 4.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/helpers/applicability.js +11 -6
- package/dist/helpers/applicability.js.map +1 -1
- package/dist/helpers/collectDeps.js +31 -6
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/naming.js +2 -2
- package/dist/helpers/naming.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/model/sdkgen.aon +49 -3
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +128 -0
- package/project/.sdk/model/feature/validate.aon +3 -2
- package/project/.sdk/model/target/c.aon +1 -1
- package/project/.sdk/model/target/clojure.aon +1 -1
- package/project/.sdk/model/target/cpp.aon +1 -1
- package/project/.sdk/model/target/csharp.aon +1 -1
- package/project/.sdk/model/target/elixir.aon +1 -1
- package/project/.sdk/model/target/go.aon +1 -1
- package/project/.sdk/model/target/java.aon +1 -1
- package/project/.sdk/model/target/kotlin.aon +1 -1
- package/project/.sdk/model/target/lua.aon +1 -1
- package/project/.sdk/model/target/ocaml.aon +1 -1
- package/project/.sdk/model/target/perl.aon +1 -1
- package/project/.sdk/model/target/php.aon +1 -1
- package/project/.sdk/model/target/py.aon +1 -1
- package/project/.sdk/model/target/rb.aon +1 -1
- package/project/.sdk/model/target/rust.aon +1 -1
- package/project/.sdk/model/target/scala.aon +1 -1
- package/project/.sdk/model/target/swift.aon +1 -1
- package/project/.sdk/model/target/zig.aon +1 -1
- package/project/.sdk/src/cmp/c/Main_c.ts +3 -0
- package/project/.sdk/src/cmp/c/Schema_c.ts +106 -0
- package/project/.sdk/src/cmp/clojure/Main_clojure.ts +2 -0
- package/project/.sdk/src/cmp/clojure/ReadmeExplanation_clojure.ts +1 -0
- package/project/.sdk/src/cmp/clojure/Schema_clojure.ts +73 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +3 -0
- package/project/.sdk/src/cmp/cpp/Schema_cpp.ts +105 -0
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +3 -0
- package/project/.sdk/src/cmp/csharp/Schema_csharp.ts +85 -0
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +2 -0
- package/project/.sdk/src/cmp/elixir/ReadmeExplanation_elixir.ts +1 -0
- package/project/.sdk/src/cmp/elixir/Schema_elixir.ts +88 -0
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -0
- package/project/.sdk/src/cmp/go/Schema_go.ts +93 -0
- package/project/.sdk/src/cmp/java/Main_java.ts +2 -0
- package/project/.sdk/src/cmp/java/Schema_java.ts +125 -0
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -0
- package/project/.sdk/src/cmp/kotlin/Schema_kotlin.ts +111 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +46 -9
- package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -0
- package/project/.sdk/src/cmp/lua/ReadmeExplanation_lua.ts +1 -0
- package/project/.sdk/src/cmp/lua/Schema_lua.ts +72 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +24 -1
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +34 -13
- package/project/.sdk/src/cmp/ocaml/ReadmeExplanation_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/ocaml/Schema_ocaml.ts +99 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +2 -0
- package/project/.sdk/src/cmp/perl/ReadmeExplanation_perl.ts +1 -0
- package/project/.sdk/src/cmp/perl/Schema_perl.ts +107 -0
- package/project/.sdk/src/cmp/php/Main_php.ts +2 -0
- package/project/.sdk/src/cmp/php/Package_php.ts +1 -1
- package/project/.sdk/src/cmp/php/ReadmeExplanation_php.ts +1 -0
- package/project/.sdk/src/cmp/php/Schema_php.ts +129 -0
- package/project/.sdk/src/cmp/py/Main_py.ts +2 -0
- package/project/.sdk/src/cmp/py/ReadmeExplanation_py.ts +1 -0
- package/project/.sdk/src/cmp/py/Schema_py.ts +72 -0
- package/project/.sdk/src/cmp/rb/Main_rb.ts +2 -0
- package/project/.sdk/src/cmp/rb/ReadmeExplanation_rb.ts +1 -0
- package/project/.sdk/src/cmp/rb/Schema_rb.ts +70 -0
- package/project/.sdk/src/cmp/rust/Main_rust.ts +3 -0
- package/project/.sdk/src/cmp/rust/Schema_rust.ts +107 -0
- package/project/.sdk/src/cmp/scala/Main_scala.ts +3 -0
- package/project/.sdk/src/cmp/scala/Schema_scala.ts +108 -0
- package/project/.sdk/src/cmp/swift/Main_swift.ts +3 -0
- package/project/.sdk/src/cmp/swift/Schema_swift.ts +92 -0
- package/project/.sdk/src/cmp/zig/Main_zig.ts +3 -0
- package/project/.sdk/src/cmp/zig/Schema_zig.ts +114 -0
- package/project/.sdk/tm/c/core/context.c +3 -0
- package/project/.sdk/tm/c/core/sdk.h +14 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/config.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/config.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/env.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/env.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/export.c +3 -3
- package/project/.sdk/tm/c/feature/secrets/plugin/export.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/host.c +25 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/host.h +3 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/order.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/order.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/point.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/point.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/types.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/types.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/value.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/value.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/version.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugin/version.h +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/minivault.c +2072 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +151 -10
- package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +16 -10
- package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/plugins/support.h +12 -8
- package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +17 -2
- package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +5 -5
- package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +37 -9
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +2 -2
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +17 -2
- package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +16 -2
- package/project/.sdk/tm/c/feature/validate.c +364 -0
- package/project/.sdk/tm/c/feature/validate.h +29 -0
- package/project/.sdk/tm/c/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/c/tests/vendor/omni/json.c +2 -2
- package/project/.sdk/tm/c/tests/vendor/omni/omni.h +2 -2
- package/project/.sdk/tm/c/tests/vendor/omni/runner.c +2 -2
- package/project/.sdk/tm/c/tests/vendor/omni/util.c +2 -2
- package/project/.sdk/tm/c/utility/make_options.c +12 -24
- package/project/.sdk/tm/c/utility/make_spec.c +8 -0
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/capability.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/catalog.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/config.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/depend.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/env.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/export.clj +3 -3
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/graph.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/host.clj +17 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/json.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/order.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/point.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/ref.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/resolve.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/types.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/version.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/addr.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/chain.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/core.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/json.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/aws.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/azuresecrets.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/boru.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/doppler.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/gcpsecrets.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/hashicorp.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/httpjson.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/infisical.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/minivault.clj +837 -0
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/onepassword.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/proc.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/secretspec.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/sigv4.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/provider.clj +2 -2
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/providers.clj +6 -4
- package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto.clj +2 -2
- package/project/.sdk/tm/clojure/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +31 -41
- package/project/.sdk/tm/clojure/src/sdk/features.clj +202 -0
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/json.clj +2 -2
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/runner.clj +2 -2
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/util.clj +2 -2
- package/project/.sdk/tm/cpp/core/types.hpp +4 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +3 -3
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +21 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +3 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +9 -7
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Minivault.cpp +1345 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Minivault.hpp +219 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +22 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +12 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +3 -3
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +2 -2
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +2 -2
- package/project/.sdk/tm/cpp/feature/validate.hpp +301 -0
- package/project/.sdk/tm/cpp/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/cpp/test/vendor/omni/json.hpp +2 -2
- package/project/.sdk/tm/cpp/test/vendor/omni/omni.hpp +2 -2
- package/project/.sdk/tm/cpp/test/vendor/omni/util.hpp +2 -2
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +24 -41
- package/project/.sdk/tm/csharp/feature/ValidateFeature.cs +343 -0
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Capability.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Catalog.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Config.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Definition.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Depend.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Entry.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Env.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Export.cs +3 -3
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Graph.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Host.cs +9 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Inst.cs +21 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Json.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Order.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Plugin.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Point.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Refs.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Resolve.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Types.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugin/Version.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Aws.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/AzureSecrets.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Boru.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Child.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Doppler.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/GcpSecrets.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Hashicorp.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/HttpJson.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Infisical.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/MiniVault.cs +1572 -0
- package/project/.sdk/tm/csharp/feature/secrets/plugins/OnePassword.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/SecretSpec.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/plugins/Sigv4.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/sekreto/Json.cs +2 -2
- package/project/.sdk/tm/csharp/feature/secrets/sekreto/Providers.cs +3 -2
- package/project/.sdk/tm/csharp/feature/secrets/sekreto/Sekreto.cs +2 -2
- package/project/.sdk/tm/csharp/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/csharp/test/vendor/omni/Runner.cs +2 -2
- package/project/.sdk/tm/csharp/test/vendor/omni/Util.cs +2 -2
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +12 -85
- package/project/.sdk/tm/csharp/utility/struct/Struct.cs +24 -3
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/capability.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/catalog.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/config.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/depend.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/env.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/error.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/export.ex +7 -4
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/graph.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/host.ex +13 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/inst.ex +15 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/json.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/order.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/point.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/ref.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/resolve.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/types.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/version.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/voxgig_plugin.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/json.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/aws.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/azuresecrets.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/boru.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/doppler.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/gcpsecrets.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/hashicorp.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/http.ex +3 -3
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/httpjson.ex +3 -3
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/infisical.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/minivault.ex +1033 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/onepassword.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/proc.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/secretspec.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/sigv4.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/provider.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/providers.ex +17 -5
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/sekreto.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/validate.ex +320 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +10 -46
- package/project/.sdk/tm/elixir/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/elixir/test/vendor/omni/json.ex +2 -2
- package/project/.sdk/tm/elixir/test/vendor/omni/runner.ex +2 -2
- package/project/.sdk/tm/elixir/test/vendor/omni/util.ex +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugin/capability.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/catalog.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/config.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/depend.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/env.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/export.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugin/graph.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/host.go +25 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/order.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/point.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/ref.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/resolve.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/types.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/util.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugin/version.go +1 -1
- package/project/.sdk/tm/go/feature/secrets/plugins/aws/aws.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/aws/sigv4.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/azuresecrets/azuresecrets.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/boru/boru.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/doppler/doppler.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/gcpsecrets/gcpsecrets.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/hashicorp/hashicorp.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/httpjson/httpjson.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/infisical/infisical.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/minivault/format.go +493 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/minivault/minivault.go +1131 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/onepassword/onepassword.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/plugins/secretspec/secretspec.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/sekreto/addr.go +2 -2
- package/project/.sdk/tm/go/feature/secrets/sekreto/providers.go +14 -2
- package/project/.sdk/tm/go/feature/secrets/sekreto/sekreto.go +2 -2
- package/project/.sdk/tm/go/feature/validate_feature.go +346 -0
- package/project/.sdk/tm/go/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/go/test/omni/omni.go +2 -2
- package/project/.sdk/tm/go/test/omni/util.go +2 -2
- package/project/.sdk/tm/go/utility/make_options.go +13 -54
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +24 -8
- package/project/.sdk/tm/java/feature/ValidateFeature.java +330 -0
- package/project/.sdk/tm/java/feature/secrets/plugin/Capability.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Catalog.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Config.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Definition.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Depend.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Entry.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Env.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Export.java +3 -3
- package/project/.sdk/tm/java/feature/secrets/plugin/Graph.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Host.java +3 -3
- package/project/.sdk/tm/java/feature/secrets/plugin/Inst.java +21 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Json.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Order.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Plugin.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/PluginException.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Point.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Refs.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Resolve.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Types.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/plugin/Version.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/Addr.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/Builtins.java +4 -3
- package/project/.sdk/tm/java/feature/secrets/sekreto/Json.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/Provider.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/Sekreto.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/Support.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Aws.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Azuresecrets.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Boru.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Doppler.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Gcpsecrets.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Hashicorp.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Httpjson.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Infisical.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Minivault.java +1448 -0
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Onepassword.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Proc.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Secretspec.java +2 -2
- package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Sigv4.java +2 -2
- package/project/.sdk/tm/java/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/java/test/vendor/omni/Json.java +2 -2
- package/project/.sdk/tm/java/test/vendor/omni/Runner.java +2 -2
- package/project/.sdk/tm/java/test/vendor/omni/Util.java +2 -2
- package/project/.sdk/tm/java/utility/MakeOptions.java +13 -45
- package/project/.sdk/tm/java/utility/struct/Struct.java +1 -1
- package/project/.sdk/tm/js/src/feature/secrets/plugin/capability.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/catalog.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/config.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/depend.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/env.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/export.js +3 -3
- package/project/.sdk/tm/js/src/feature/secrets/plugin/graph.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/host.js +17 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/index.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/order.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/point.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/ref.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/resolve.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/types.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/plugin/version.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/Sekreto.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/index.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/aws.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/azuresecrets.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/boru.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/doppler.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/gcpsecrets.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/hashicorp.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/httpjson.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/infisical.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/minivault.js +866 -0
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/onepassword.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/secretspec.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/sigv4.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/addr.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/builtin.js +3 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/dotenv.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/env.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/file.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/memory.js +2 -2
- package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/support.js +2 -2
- package/project/.sdk/tm/js/src/utility/StructUtility.js +1 -1
- package/project/.sdk/tm/js/test/vendor/omni/index.js +1 -1
- package/project/.sdk/tm/js/test/vendor/omni/runner.js +1 -1
- package/project/.sdk/tm/js/test/vendor/omni/util.js +1 -1
- package/project/.sdk/tm/kotlin/feature/ValidateFeature.kt +297 -0
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Capability.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Catalog.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Config.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Depend.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Env.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Export.kt +3 -3
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Graph.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Host.kt +21 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Json.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Order.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Plugin.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Point.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Refs.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Resolve.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Types.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/plugin/Version.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Json.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Provider.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Providers.kt +3 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Sekreto.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Spec.kt +14 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Support.kt +10 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Aws.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Azuresecrets.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Boru.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Doppler.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Gcpsecrets.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Hashicorp.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Httpjson.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Infisical.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Minivault.kt +1095 -0
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Onepassword.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Secretspec.kt +2 -2
- package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Sigv4.kt +2 -2
- package/project/.sdk/tm/kotlin/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/kotlin/test/vendor/omni/Json.kt +2 -2
- package/project/.sdk/tm/kotlin/test/vendor/omni/Runner.kt +2 -2
- package/project/.sdk/tm/kotlin/test/vendor/omni/Util.kt +2 -2
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +13 -35
- package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +6 -0
- package/project/.sdk/tm/kotlin/utility/struct/Struct.kt +1 -1
- package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +2 -2
- package/project/.sdk/tm/lua/feature/secrets/native/sekretovault.c +240 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +12 -3
- package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +18 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/plugin.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/minivault.lua +1227 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +2 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +3 -2
- package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +2 -2
- package/project/.sdk/tm/lua/feature/validate_feature.lua +333 -0
- package/project/.sdk/tm/lua/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/vendor/omni/json.lua +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/regex.lua +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/runner.lua +2 -2
- package/project/.sdk/tm/lua/test/vendor/omni/util.lua +2 -2
- package/project/.sdk/tm/lua/utility/make_options.lua +74 -55
- package/project/.sdk/tm/lua/utility/make_spec.lua +8 -1
- package/project/.sdk/tm/lua/utility/struct/regex.lua +1 -1
- package/project/.sdk/tm/lua/utility/struct/struct.lua +1 -1
- package/project/.sdk/tm/ocaml/Makefile +12 -7
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +5 -3
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +18 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/minivault.ml +1086 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/minivault_stubs.c +222 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +33 -3
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +2 -2
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +4 -3
- package/project/.sdk/tm/ocaml/sdk_features.ml +174 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +12 -37
- package/project/.sdk/tm/ocaml/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/ocaml/test/vendor/omni/omni.ml +2 -2
- package/project/.sdk/tm/perl/core/helpers.pm +64 -16
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Capability.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Catalog.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Config.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Depend.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Env.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Export.pm +3 -3
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Graph.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Host.pm +19 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Order.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Point.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Ref.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Resolve.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Types.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Version.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Aws.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Azuresecrets.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Boru.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Doppler.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Gcpsecrets.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Hashicorp.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Httpjson.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Infisical.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Minivault.pm +1380 -0
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Onepassword.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Proc.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Secretspec.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Sigv4.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/sekreto/Voxgig/Sekreto/Addr.pm +2 -2
- package/project/.sdk/tm/perl/feature/secrets/sekreto/Voxgig/Sekreto/Providers.pm +4 -3
- package/project/.sdk/tm/perl/feature/secrets/sekreto/Voxgig/Sekreto.pm +2 -2
- package/project/.sdk/tm/perl/feature/validate_feature.pm +319 -0
- package/project/.sdk/tm/perl/lib/Voxgig/Struct.pm +1 -1
- package/project/.sdk/tm/perl/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Runner.pm +1 -1
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Util.pm +1 -1
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni.pm +1 -1
- package/project/.sdk/tm/perl/utility/make_options.pm +25 -43
- package/project/.sdk/tm/php/feature/ValidateFeature.php +361 -0
- package/project/.sdk/tm/php/feature/secrets/plugin/Capability.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Catalog.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Config.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Depend.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Env.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Export.php +3 -3
- package/project/.sdk/tm/php/feature/secrets/plugin/Graph.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Host.php +26 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Order.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Point.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Ref.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Resolve.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Types.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/Version.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/plugin/plugin.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/aws.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/azuresecrets.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/boru.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/doppler.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/gcpsecrets.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/hashicorp.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/httpjson.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/infisical.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/minivault.php +1250 -0
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/onepassword.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/runcmd.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/secretspec.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/sigv4.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/src/Addr.php +2 -2
- package/project/.sdk/tm/php/feature/secrets/sekreto/src/Providers.php +5 -3
- package/project/.sdk/tm/php/feature/secrets/sekreto/src/Sekreto.php +2 -2
- package/project/.sdk/tm/php/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/php/test/vendor/omni/Runner.php +2 -2
- package/project/.sdk/tm/php/test/vendor/omni/Util.php +2 -2
- package/project/.sdk/tm/php/utility/MakeOptions.php +15 -31
- package/project/.sdk/tm/php/utility/struct/Struct.php +56 -7
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/__init__.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/capability.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/catalog.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/config.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/depend.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/env.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/export.py +2 -2
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/graph.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/host.py +17 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/order.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/point.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/ref.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/resolve.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/types.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/version.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/__init__.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/addr.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/aws.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/azuresecrets.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/boru.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/doppler.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/hashicorp.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/httpjson.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/infisical.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/minivault.py +1202 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/onepassword.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/secretspec.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/sigv4.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/providers.py +2 -1
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/sekreto.py +1 -1
- package/project/.sdk/tm/py/pkg/feature/validate_feature.py +273 -0
- package/project/.sdk/tm/py/pkg/utility/make_options.py +14 -53
- package/project/.sdk/tm/py/pkg/utility/voxgig_struct/voxgig_struct.py +1 -1
- package/project/.sdk/tm/py/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/py/test/voxgig_omni/__init__.py +1 -1
- package/project/.sdk/tm/py/test/voxgig_omni/runner.py +1 -1
- package/project/.sdk/tm/py/test/voxgig_omni/util.py +1 -1
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/capability.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/catalog.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/config.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/depend.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/env.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/export.rb +3 -3
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/graph.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/host.rb +16 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/order.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/point.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/ref.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/resolve.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/types.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/version.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/addr.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/aws.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/azuresecrets.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/boru.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/doppler.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/hashicorp.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/httpjson.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/infisical.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/minivault.rb +979 -0
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/onepassword.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/secretspec.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/sigv4.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/providers.rb +6 -5
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/sekreto.rb +2 -2
- package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto.rb +2 -2
- package/project/.sdk/tm/rb/feature/validate_feature.rb +265 -0
- package/project/.sdk/tm/rb/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/rb/test/vendor/omni/runner.rb +2 -2
- package/project/.sdk/tm/rb/test/vendor/omni/util.rb +2 -2
- package/project/.sdk/tm/rb/test/vendor/omni/voxgig_omni.rb +2 -2
- package/project/.sdk/tm/rb/utility/make_options.rb +15 -32
- package/project/.sdk/tm/rb/utility/struct/voxgig_struct.rb +24 -1
- package/project/.sdk/tm/rust/core/mod.rs +3 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/capability.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/catalog.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/config.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/depend.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/env.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/export.rs +3 -3
- package/project/.sdk/tm/rust/feature/secrets/plugin/graph.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/host.rs +24 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/mod.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/order.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/point.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/refs.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/resolve.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/types.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/value.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugin/version.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/aws/crypto.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/aws/sigv4.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/aws.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/azuresecrets.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/boru.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/doppler.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/gcpsecrets.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/hashicorp.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/httpjson/http.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/httpjson/json.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/httpjson.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/infisical.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/minivault/format.rs +425 -0
- package/project/.sdk/tm/rust/feature/secrets/plugins/minivault.rs +1076 -0
- package/project/.sdk/tm/rust/feature/secrets/plugins/onepassword.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/plugins/secretspec.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/sekreto/addr.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/sekreto/mod.rs +2 -2
- package/project/.sdk/tm/rust/feature/secrets/sekreto/providers.rs +30 -4
- package/project/.sdk/tm/rust/feature/secrets/sekreto/sekreto.rs +2 -2
- package/project/.sdk/tm/rust/feature/validate.rs +387 -0
- package/project/.sdk/tm/rust/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/rust/tests/vendor/omni/json.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/mod.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/regex.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/runner.rs +2 -2
- package/project/.sdk/tm/rust/tests/vendor/omni/util.rs +2 -2
- package/project/.sdk/tm/rust/utility/make_options.rs +12 -63
- package/project/.sdk/tm/rust/utility/make_spec.rs +10 -3
- package/project/.sdk/tm/scala/feature/ValidateFeature.scala +286 -0
- package/project/.sdk/tm/scala/feature/secrets/plugin/Capability.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Config.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Depend.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Env.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Export.scala +3 -3
- package/project/.sdk/tm/scala/feature/secrets/plugin/Graph.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Host.scala +19 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Json.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Order.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Plugin.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Point.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Refs.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Resolve.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Types.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Value.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/plugin/Version.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/Json.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/Provider.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/Providers.scala +3 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/Sekreto.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/Spec.scala +13 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/Support.scala +11 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Aws.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Azuresecrets.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Boru.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Doppler.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Gcpsecrets.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Hashicorp.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Httpjson.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Infisical.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Minivault.scala +979 -0
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Onepassword.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Secretspec.scala +2 -2
- package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Sigv4.scala +2 -2
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Json.scala +2 -2
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Runner.scala +2 -2
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Util.scala +2 -2
- package/project/.sdk/tm/scala/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/scala/utility/Make.scala +12 -41
- package/project/.sdk/tm/scala/utility/MakeSpec.scala +8 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/ValidateFeature.swift +273 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Capability.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Catalog.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Config.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Depend.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Env.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Export.swift +3 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Graph.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Host.swift +17 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Json.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Order.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Point.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Refs.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Resolve.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Types.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Value.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Version.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Aws.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Azuresecrets.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Boru.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Crypto.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Doppler.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Gcpsecrets.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Hashicorp.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Httpjson.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Infisical.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Onepassword.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Proc.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Secretspec.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Sigv4.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Addr.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Json.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Provider.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Providers.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Sekreto.swift +2 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +9 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +12 -87
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +3 -3
- package/project/.sdk/tm/swift/Tests/vendor/omni/Json.swift +2 -2
- package/project/.sdk/tm/swift/Tests/vendor/omni/Runner.swift +2 -2
- package/project/.sdk/tm/swift/Tests/vendor/omni/Util.swift +2 -2
- package/project/.sdk/tm/swift/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Capability.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Catalog.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Config.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Depend.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Env.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Export.ts +13 -2
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/FeatureHost.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Graph.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Host.ts +27 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Order.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Point.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Ref.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Resolve.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Types.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Version.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/index.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/aws.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/azuresecrets.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/boru.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/doppler.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/gcpsecrets.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/hashicorp.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/httpjson.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/infisical.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/minivault.ts +1034 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/onepassword.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/secretspec.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/sigv4.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/builtin.ts +2 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +11 -1
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1 -1
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +1 -1
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +1 -1
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
- package/project/.sdk/tm/zig/core/utility.zig +20 -44
- package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +3 -3
- package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +18 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/minivault.zig +1930 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +2 -2
- package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +3 -2
- package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +19 -2
- package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +2 -2
- package/project/.sdk/tm/zig/feature/validate.zig +293 -0
- package/project/.sdk/tm/zig/src/feature/validate/.gitkeep +0 -0
- package/project/.sdk/tm/zig/test/vendor/omni/omni.zig +2 -2
- package/project/.sdk/tm/zig/test/vendor/omni/regex.zig +2 -2
- package/project/.sdk/tm/zig/utility/voxgigstruct/regex.zig +2 -2
- package/project/.sdk/tm/zig/utility/voxgigstruct/struct.zig +2 -2
- package/project/sdkgen-package.json +1 -1
- package/src/helpers/applicability.ts +11 -6
- package/src/helpers/collectDeps.ts +30 -5
- package/src/helpers/naming.ts +2 -2
|
@@ -0,0 +1,1930 @@
|
|
|
1
|
+
// VENDORED: @voxgig/sekreto sdk-20260917-1242-0 (zig/plugins/minivault.zig)
|
|
2
|
+
// Source: https://github.com/voxgig/sekreto @ 108c4a914bee7b6534c30d1c68c25cd1b9377696 [tag: sdk-20260917-1242-0]
|
|
3
|
+
// License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
//! A mini vault: every secret a project owns, encrypted, in ONE FILE.
|
|
5
|
+
//!
|
|
6
|
+
//! The store to reach for before there is a vault server. There is
|
|
7
|
+
//! nothing to run and nothing to reach over a socket - the whole store is
|
|
8
|
+
//! a single binary file - and the same chain that reads it in development
|
|
9
|
+
//! reads HashiCorp or AWS in production by changing config, which is the
|
|
10
|
+
//! reason sekreto exists.
|
|
11
|
+
//!
|
|
12
|
+
//! It is a plugin rather than a built-in kind because it needs crypto,
|
|
13
|
+
//! which is the line the four built-ins stay behind. `std.crypto` carries
|
|
14
|
+
//! every primitive the format names, so nothing here is hand-rolled.
|
|
15
|
+
//!
|
|
16
|
+
//! THE KEY DECIDES WHAT THE VAULT HOLDS. A master key reads and writes
|
|
17
|
+
//! every name and mints restricted keys. A restricted key reads the names
|
|
18
|
+
//! it was granted and CANNOT DERIVE ANY OTHER - the restriction is the
|
|
19
|
+
//! cryptography rather than a check this code performs, so a copy of the
|
|
20
|
+
//! file plus a restricted passphrase yields exactly what was granted and
|
|
21
|
+
//! nothing else. What that does and does not protect is set out in
|
|
22
|
+
//! DOCS.md under "What the mini vault protects".
|
|
23
|
+
//!
|
|
24
|
+
//! THE FILE FORMAT, which is the contract between the ports:
|
|
25
|
+
//!
|
|
26
|
+
//! magic 4 'SKMV'
|
|
27
|
+
//! version 1 FORMAT
|
|
28
|
+
//! kdf 1 1 = PBKDF2-HMAC-SHA256
|
|
29
|
+
//! cipher 1 1 = AES-256-GCM
|
|
30
|
+
//! reserved 1 0
|
|
31
|
+
//! keycount 4 u32
|
|
32
|
+
//! per key:
|
|
33
|
+
//! id 1 + bytes the key id, PLAINTEXT
|
|
34
|
+
//! salt 1 + bytes
|
|
35
|
+
//! iters 4 PBKDF2 rounds for this key
|
|
36
|
+
//! ring 1 + iv, 4 + bytes sealed under the passphrase
|
|
37
|
+
//! meta 1 + iv, 4 + bytes sealed under the vault's meta key
|
|
38
|
+
//! entrycount 4 u32
|
|
39
|
+
//! per entry:
|
|
40
|
+
//! id 1 + bytes the blinded lookup id
|
|
41
|
+
//! name 1 + iv, 4 + bytes sealed under the vault's name key
|
|
42
|
+
//! value 1 + iv, 4 + bytes sealed under that secret's own key
|
|
43
|
+
//!
|
|
44
|
+
//! Integers are big-endian and every length precedes its bytes, so the
|
|
45
|
+
//! file is written with the same two primitives it is read with.
|
|
46
|
+
//!
|
|
47
|
+
//! NOTHING OUTSIDE A KEY RECORD IS PLAINTEXT. Secret names are sealed,
|
|
48
|
+
//! and an entry is addressed by a blinded id derived from its own key, so
|
|
49
|
+
//! a restricted key finds what it was granted without the file ever
|
|
50
|
+
//! naming the rest. What the file does show anyone is the key ids and how
|
|
51
|
+
//! many secrets there are.
|
|
52
|
+
//!
|
|
53
|
+
//! A port of typescript/plugins/minivault.ts, which is canonical. The
|
|
54
|
+
//! bytes are pinned by ../../test/fixture/*.skmv rather than left to
|
|
55
|
+
//! agreement between implementations.
|
|
56
|
+
|
|
57
|
+
const std = @import("std");
|
|
58
|
+
|
|
59
|
+
const sekreto = @import("sekreto");
|
|
60
|
+
|
|
61
|
+
const Allocator = std.mem.Allocator;
|
|
62
|
+
const Answer = sekreto.Answer;
|
|
63
|
+
const Config = sekreto.Config;
|
|
64
|
+
const Found = sekreto.Found;
|
|
65
|
+
const Provider = sekreto.Provider;
|
|
66
|
+
const ProviderSpec = sekreto.ProviderSpec;
|
|
67
|
+
|
|
68
|
+
const plugin = sekreto.plugin;
|
|
69
|
+
const pv = plugin.value;
|
|
70
|
+
const pt = plugin.types;
|
|
71
|
+
|
|
72
|
+
const Aes256Gcm = std.crypto.aead.aes_gcm.Aes256Gcm;
|
|
73
|
+
const HmacSha256 = std.crypto.auth.hmac.sha2.HmacSha256;
|
|
74
|
+
const b64encoder = std.base64.standard.Encoder;
|
|
75
|
+
const b64decoder = std.base64.standard.Decoder;
|
|
76
|
+
|
|
77
|
+
// --- the format -------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
const MAGIC = "SKMV";
|
|
80
|
+
const FORMAT = 1;
|
|
81
|
+
|
|
82
|
+
const KDF_PBKDF2 = 1;
|
|
83
|
+
const CIPHER_AESGCM = 1;
|
|
84
|
+
|
|
85
|
+
/// AES-256-GCM: 32-byte keys, 12-byte nonces, 16-byte tags.
|
|
86
|
+
const KEYLEN = 32;
|
|
87
|
+
const IVLEN = 12;
|
|
88
|
+
const TAGLEN = 16;
|
|
89
|
+
const SALTLEN = 16;
|
|
90
|
+
|
|
91
|
+
/// The PBKDF2-HMAC-SHA256 round count when a caller names none.
|
|
92
|
+
pub const ITERATIONS: u32 = 210000;
|
|
93
|
+
|
|
94
|
+
/// The key id a vault gets when a caller names none.
|
|
95
|
+
pub const MASTERKEY = "master";
|
|
96
|
+
|
|
97
|
+
// Additional authenticated data. Every blob is bound to its PLACE in the
|
|
98
|
+
// file, so no ciphertext can be moved: a restricted key's ring cannot be
|
|
99
|
+
// relabelled as the master's, and one secret's value cannot be served
|
|
100
|
+
// under a name it was never written for.
|
|
101
|
+
const AAD_RING = "skmv1:ring:";
|
|
102
|
+
const AAD_META = "skmv1:meta:";
|
|
103
|
+
const AAD_NAME = "skmv1:name";
|
|
104
|
+
const AAD_SECRET = "skmv1:secret:";
|
|
105
|
+
|
|
106
|
+
// Everything a master reaches is derived from the root key, so rotating
|
|
107
|
+
// is one new random value rather than a re-wrap of each part.
|
|
108
|
+
const LABEL_NAMES = "skmv1:names";
|
|
109
|
+
const LABEL_META = "skmv1:meta";
|
|
110
|
+
const LABEL_ID = "skmv1:id";
|
|
111
|
+
|
|
112
|
+
/// The largest key id the format can record.
|
|
113
|
+
///
|
|
114
|
+
/// `small` writes a length in ONE byte. A longer id wrapped that byte and
|
|
115
|
+
/// the writer then appended the whole thing, so every field after it
|
|
116
|
+
/// shifted: a grant with a 300-character id replaced a working vault with
|
|
117
|
+
/// an unreadable one, and said nothing. Checked where an id is ACCEPTED,
|
|
118
|
+
/// so the refusal names the id rather than the file.
|
|
119
|
+
const IDMAX = 255;
|
|
120
|
+
|
|
121
|
+
/// Owner-only, because a vault file is the whole store. POSIX only: on
|
|
122
|
+
/// Windows `Permissions` is a file-attribute set with no mode bits, and
|
|
123
|
+
/// `.default_file` is what every other writer there uses.
|
|
124
|
+
const OWNERONLY: std.Io.File.Permissions = if (.windows == @import("builtin").os.tag)
|
|
125
|
+
.default_file
|
|
126
|
+
else
|
|
127
|
+
@enumFromInt(0o600);
|
|
128
|
+
|
|
129
|
+
fn fail(alloc: Allocator, comptime format: []const u8, args: anytype) Allocator.Error![]const u8 {
|
|
130
|
+
return sekreto.fail(alloc, "sekreto: minivault: " ++ format, args);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fn checkid(alloc: Allocator, id: []const u8, what: []const u8) Allocator.Error!Answer([]const u8) {
|
|
134
|
+
if (0 == id.len) {
|
|
135
|
+
return .{ .err = try fail(alloc, "{s}", .{what}) };
|
|
136
|
+
}
|
|
137
|
+
if (IDMAX < id.len) {
|
|
138
|
+
const cut = id[0..@min(32, id.len)];
|
|
139
|
+
return .{ .err = try fail(alloc, "key id is longer than {d} bytes: {s}...", .{ IDMAX, cut }) };
|
|
140
|
+
}
|
|
141
|
+
return .{ .ok = id };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// --- keys -------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
fn mac(alloc: Allocator, key: []const u8, text: []const u8) Allocator.Error![]const u8 {
|
|
147
|
+
const out = try alloc.alloc(u8, HmacSha256.mac_length);
|
|
148
|
+
HmacSha256.create(out[0..HmacSha256.mac_length], text, key);
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// The key-encryption key a passphrase unwraps a ring with.
|
|
153
|
+
///
|
|
154
|
+
/// PBKDF2 refuses a zero round count, which is what a damaged or hostile
|
|
155
|
+
/// file records to make the derivation free; that comes back as a
|
|
156
|
+
/// refusal rather than a panic.
|
|
157
|
+
fn kek(
|
|
158
|
+
alloc: Allocator,
|
|
159
|
+
passphrase: []const u8,
|
|
160
|
+
salt: []const u8,
|
|
161
|
+
iters: u32,
|
|
162
|
+
) Allocator.Error!Answer([]const u8) {
|
|
163
|
+
const out = try alloc.alloc(u8, KEYLEN);
|
|
164
|
+
std.crypto.pwhash.pbkdf2(out, passphrase, salt, iters, HmacSha256) catch {
|
|
165
|
+
return .{ .err = try fail(alloc, "unusable round count: {d}", .{iters}) };
|
|
166
|
+
};
|
|
167
|
+
return .{ .ok = out };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// The key one named secret's value is encrypted with.
|
|
171
|
+
///
|
|
172
|
+
/// DERIVED, never stored, for a master: it holds the root key and so
|
|
173
|
+
/// reaches every name, including ones written after it was made. A
|
|
174
|
+
/// restricted key holds the derived keys it was granted and nothing that
|
|
175
|
+
/// produces another, so every other name is ciphertext to it in exactly
|
|
176
|
+
/// the way it is to a stranger.
|
|
177
|
+
fn secretkey(alloc: Allocator, root: []const u8, name: []const u8) Allocator.Error![]const u8 {
|
|
178
|
+
const label = try std.fmt.allocPrint(alloc, AAD_SECRET ++ "{s}", .{name});
|
|
179
|
+
defer alloc.free(label);
|
|
180
|
+
return mac(alloc, root, label);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Where a secret lives in the file, derived from its own key so that
|
|
184
|
+
/// finding it needs no plaintext name. One-way: an id yields nothing
|
|
185
|
+
/// about the key that produced it.
|
|
186
|
+
fn entryid(alloc: Allocator, key: []const u8) Allocator.Error![]const u8 {
|
|
187
|
+
return mac(alloc, key, LABEL_ID);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Fresh entropy, from outside the process.
|
|
191
|
+
///
|
|
192
|
+
/// `randomSecure` rather than `random`: it always makes the syscall and
|
|
193
|
+
/// keeps no RNG state in process memory, and it reports failure instead
|
|
194
|
+
/// of falling back to a weaker source. A nonce repeated under one
|
|
195
|
+
/// AES-GCM key loses the confidentiality of both messages, so a silent
|
|
196
|
+
/// downgrade is not something a vault can accept.
|
|
197
|
+
fn random(alloc: Allocator, io: std.Io, length: usize) Allocator.Error!Answer([]u8) {
|
|
198
|
+
const out = try alloc.alloc(u8, length);
|
|
199
|
+
io.randomSecure(out) catch {
|
|
200
|
+
return .{ .err = try fail(alloc, "no randomness available", .{}) };
|
|
201
|
+
};
|
|
202
|
+
return .{ .ok = out };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// --- sealing ----------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
const Sealed = struct {
|
|
208
|
+
iv: []const u8,
|
|
209
|
+
blob: []const u8,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
fn sameseal(left: Sealed, right: Sealed) bool {
|
|
213
|
+
return std.mem.eql(u8, left.iv, right.iv) and std.mem.eql(u8, left.blob, right.blob);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
fn copyseal(alloc: Allocator, box: Sealed) Allocator.Error!Sealed {
|
|
217
|
+
return .{ .iv = try alloc.dupe(u8, box.iv), .blob = try alloc.dupe(u8, box.blob) };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fn seal(
|
|
221
|
+
alloc: Allocator,
|
|
222
|
+
io: std.Io,
|
|
223
|
+
key: []const u8,
|
|
224
|
+
plain: []const u8,
|
|
225
|
+
aad: []const u8,
|
|
226
|
+
) Allocator.Error!Answer(Sealed) {
|
|
227
|
+
if (KEYLEN != key.len) {
|
|
228
|
+
return .{ .err = try fail(alloc, "bad key", .{}) };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const iv = switch (try random(alloc, io, IVLEN)) {
|
|
232
|
+
.err => |message| return .{ .err = message },
|
|
233
|
+
.ok => |made| made,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// The tag rides at the end of the blob, which is where every other
|
|
237
|
+
// port's AEAD leaves it and therefore what the format records.
|
|
238
|
+
const blob = try alloc.alloc(u8, plain.len + TAGLEN);
|
|
239
|
+
Aes256Gcm.encrypt(
|
|
240
|
+
blob[0..plain.len],
|
|
241
|
+
blob[plain.len..][0..TAGLEN],
|
|
242
|
+
plain,
|
|
243
|
+
aad,
|
|
244
|
+
iv[0..IVLEN].*,
|
|
245
|
+
key[0..KEYLEN].*,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return .{ .ok = .{ .iv = iv, .blob = blob } };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/// The plaintext, or a refusal. A GCM tag that fails to verify is the
|
|
252
|
+
/// only evidence there is, and it cannot tell a wrong passphrase from a
|
|
253
|
+
/// damaged file, so `what` names the attempt and the message admits both.
|
|
254
|
+
fn unseal(
|
|
255
|
+
alloc: Allocator,
|
|
256
|
+
key: []const u8,
|
|
257
|
+
box: Sealed,
|
|
258
|
+
aad: []const u8,
|
|
259
|
+
what: []const u8,
|
|
260
|
+
) Allocator.Error!Answer([]const u8) {
|
|
261
|
+
if (box.blob.len < TAGLEN or IVLEN != box.iv.len) {
|
|
262
|
+
return .{ .err = try fail(alloc, "{s}: truncated", .{what}) };
|
|
263
|
+
}
|
|
264
|
+
if (KEYLEN != key.len) {
|
|
265
|
+
return .{ .err = try fail(alloc, "bad key", .{}) };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const cut = box.blob.len - TAGLEN;
|
|
269
|
+
const plain = try alloc.alloc(u8, cut);
|
|
270
|
+
|
|
271
|
+
Aes256Gcm.decrypt(
|
|
272
|
+
plain,
|
|
273
|
+
box.blob[0..cut],
|
|
274
|
+
box.blob[cut..][0..TAGLEN].*,
|
|
275
|
+
aad,
|
|
276
|
+
box.iv[0..IVLEN].*,
|
|
277
|
+
key[0..KEYLEN].*,
|
|
278
|
+
) catch {
|
|
279
|
+
return .{ .err = try fail(alloc, "{s}", .{what}) };
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
return .{ .ok = plain };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// --- base64 and json --------------------------------------------------
|
|
286
|
+
|
|
287
|
+
fn b64(alloc: Allocator, raw: []const u8) Allocator.Error![]const u8 {
|
|
288
|
+
const out = try alloc.alloc(u8, b64encoder.calcSize(raw.len));
|
|
289
|
+
return b64encoder.encode(out, raw);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
fn unb64(alloc: Allocator, text: []const u8, what: []const u8) Allocator.Error!Answer([]const u8) {
|
|
293
|
+
const size = b64decoder.calcSizeForSlice(text) catch {
|
|
294
|
+
return .{ .err = try fail(alloc, "missing {s}", .{what}) };
|
|
295
|
+
};
|
|
296
|
+
const out = try alloc.alloc(u8, size);
|
|
297
|
+
b64decoder.decode(out, text) catch {
|
|
298
|
+
return .{ .err = try fail(alloc, "missing {s}", .{what}) };
|
|
299
|
+
};
|
|
300
|
+
return .{ .ok = out };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/// A JSON string literal.
|
|
304
|
+
///
|
|
305
|
+
/// Every string this writes is either base64 or a secret name, and
|
|
306
|
+
/// `sekreto.checkname` has already confined a name to `[a-z0-9_.]` - but
|
|
307
|
+
/// "this cannot contain a quote" is exactly the assumption that stops
|
|
308
|
+
/// being true when a rule moves, so the escaping is done rather than
|
|
309
|
+
/// assumed away.
|
|
310
|
+
fn jsonstring(out: *std.ArrayList(u8), alloc: Allocator, text: []const u8) Allocator.Error!void {
|
|
311
|
+
try out.append(alloc, '"');
|
|
312
|
+
for (text) |ch| {
|
|
313
|
+
switch (ch) {
|
|
314
|
+
'"' => try out.appendSlice(alloc, "\\\""),
|
|
315
|
+
'\\' => try out.appendSlice(alloc, "\\\\"),
|
|
316
|
+
'\n' => try out.appendSlice(alloc, "\\n"),
|
|
317
|
+
'\r' => try out.appendSlice(alloc, "\\r"),
|
|
318
|
+
'\t' => try out.appendSlice(alloc, "\\t"),
|
|
319
|
+
else => if (0x20 > ch) {
|
|
320
|
+
try out.print(alloc, "\\u{x:0>4}", .{ch});
|
|
321
|
+
} else {
|
|
322
|
+
try out.append(alloc, ch);
|
|
323
|
+
},
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
try out.append(alloc, '"');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
fn jsonof(
|
|
330
|
+
alloc: Allocator,
|
|
331
|
+
plain: []const u8,
|
|
332
|
+
what: []const u8,
|
|
333
|
+
) Allocator.Error!Answer(std.json.Parsed(std.json.Value)) {
|
|
334
|
+
const parsed = std.json.parseFromSlice(std.json.Value, alloc, plain, .{}) catch {
|
|
335
|
+
return .{ .err = try fail(alloc, "unreadable {s}", .{what}) };
|
|
336
|
+
};
|
|
337
|
+
return .{ .ok = parsed };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
fn jget(value: std.json.Value, key: []const u8) ?std.json.Value {
|
|
341
|
+
if (.object != value) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
return value.object.get(key);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
fn jbool(value: ?std.json.Value) bool {
|
|
348
|
+
const given = value orelse return false;
|
|
349
|
+
return .bool == given and given.bool;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
fn jstr(value: ?std.json.Value) ?[]const u8 {
|
|
353
|
+
const given = value orelse return null;
|
|
354
|
+
return if (.string == given) given.string else null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// --- the file ---------------------------------------------------------
|
|
358
|
+
|
|
359
|
+
const KeyRecord = struct {
|
|
360
|
+
id: []const u8,
|
|
361
|
+
salt: []const u8,
|
|
362
|
+
iters: u32,
|
|
363
|
+
ring: Sealed,
|
|
364
|
+
meta: Sealed,
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const EntryRecord = struct {
|
|
368
|
+
id: []const u8,
|
|
369
|
+
name: Sealed,
|
|
370
|
+
value: Sealed,
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const VaultFile = struct {
|
|
374
|
+
keys: std.ArrayList(KeyRecord) = .empty,
|
|
375
|
+
entries: std.ArrayList(EntryRecord) = .empty,
|
|
376
|
+
|
|
377
|
+
fn key(self: *const VaultFile, id: []const u8) ?*KeyRecord {
|
|
378
|
+
for (self.keys.items) |*record| {
|
|
379
|
+
if (std.mem.eql(u8, id, record.id)) {
|
|
380
|
+
return record;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
fn entry(self: *const VaultFile, id: []const u8) ?*EntryRecord {
|
|
387
|
+
for (self.entries.items) |*record| {
|
|
388
|
+
if (std.mem.eql(u8, id, record.id)) {
|
|
389
|
+
return record;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/// A cursor, so that every length check is in one place: a truncated
|
|
397
|
+
/// vault is refused rather than read as a short one.
|
|
398
|
+
const Reader = struct {
|
|
399
|
+
bytes: []const u8,
|
|
400
|
+
at: usize = 0,
|
|
401
|
+
bad: bool = false,
|
|
402
|
+
|
|
403
|
+
/// Reads `length` bytes, or records the refusal.
|
|
404
|
+
///
|
|
405
|
+
/// The bound is checked as a u64 AGAINST WHAT IS LEFT, never by
|
|
406
|
+
/// adding it to `at`: a damaged vault can encode a length near
|
|
407
|
+
/// 0xffffffff, and `at + length` would wrap on a 32-bit target and
|
|
408
|
+
/// hand back a slice the caller had no business seeing.
|
|
409
|
+
fn take(self: *Reader, length: u64) []const u8 {
|
|
410
|
+
if (self.bad) {
|
|
411
|
+
return "";
|
|
412
|
+
}
|
|
413
|
+
if (@as(u64, self.bytes.len - self.at) < length) {
|
|
414
|
+
self.bad = true;
|
|
415
|
+
return "";
|
|
416
|
+
}
|
|
417
|
+
const out = self.bytes[self.at..][0..@intCast(length)];
|
|
418
|
+
self.at += @intCast(length);
|
|
419
|
+
return out;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
fn u8v(self: *Reader) u8 {
|
|
423
|
+
const out = self.take(1);
|
|
424
|
+
return if (0 == out.len) 0 else out[0];
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
fn u32v(self: *Reader) u32 {
|
|
428
|
+
const out = self.take(4);
|
|
429
|
+
return if (4 != out.len) 0 else std.mem.readInt(u32, out[0..4], .big);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
fn small(self: *Reader) []const u8 {
|
|
433
|
+
return self.take(self.u8v());
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
fn large(self: *Reader) []const u8 {
|
|
437
|
+
return self.take(self.u32v());
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
fn sealed(self: *Reader) Sealed {
|
|
441
|
+
// The iv is read before the blob, and zig evaluates struct
|
|
442
|
+
// fields in source order, so the two reads stay in file order.
|
|
443
|
+
const iv = self.small();
|
|
444
|
+
return .{ .iv = iv, .blob = self.large() };
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
fn readfile(alloc: Allocator, raw: []const u8) Allocator.Error!Answer(VaultFile) {
|
|
449
|
+
var read = Reader{ .bytes = raw };
|
|
450
|
+
|
|
451
|
+
if (!std.mem.eql(u8, MAGIC, read.take(4))) {
|
|
452
|
+
if (read.bad) {
|
|
453
|
+
return .{ .err = try fail(alloc, "the vault file is truncated", .{}) };
|
|
454
|
+
}
|
|
455
|
+
return .{ .err = try fail(alloc, "not a vault file", .{}) };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const version = read.u8v();
|
|
459
|
+
if (read.bad) {
|
|
460
|
+
return .{ .err = try fail(alloc, "the vault file is truncated", .{}) };
|
|
461
|
+
}
|
|
462
|
+
if (FORMAT != version) {
|
|
463
|
+
return .{ .err = try fail(alloc, "unsupported format version: {d}", .{version}) };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const kdf = read.u8v();
|
|
467
|
+
const cipher = read.u8v();
|
|
468
|
+
if (read.bad) {
|
|
469
|
+
return .{ .err = try fail(alloc, "the vault file is truncated", .{}) };
|
|
470
|
+
}
|
|
471
|
+
if (KDF_PBKDF2 != kdf or CIPHER_AESGCM != cipher) {
|
|
472
|
+
return .{ .err = try fail(alloc, "unsupported kdf or cipher: {d}/{d}", .{ kdf, cipher }) };
|
|
473
|
+
}
|
|
474
|
+
_ = read.u8v();
|
|
475
|
+
|
|
476
|
+
var file = VaultFile{};
|
|
477
|
+
|
|
478
|
+
// A COUNT IS BOUNDED BY WHAT IS LEFT. Each record carries at least a
|
|
479
|
+
// few bytes, so a file claiming four billion of them is damaged; the
|
|
480
|
+
// loop would find that out one truncation at a time, and a caller
|
|
481
|
+
// that preallocated would not.
|
|
482
|
+
const keycount = read.u32v();
|
|
483
|
+
if (raw.len - read.at < keycount) {
|
|
484
|
+
return .{ .err = try fail(alloc, "the vault file is truncated", .{}) };
|
|
485
|
+
}
|
|
486
|
+
for (0..keycount) |_| {
|
|
487
|
+
if (read.bad) {
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
const id = read.small();
|
|
491
|
+
const salt = read.small();
|
|
492
|
+
const iters = read.u32v();
|
|
493
|
+
const ring = read.sealed();
|
|
494
|
+
try file.keys.append(alloc, .{
|
|
495
|
+
.id = id,
|
|
496
|
+
.salt = salt,
|
|
497
|
+
.iters = iters,
|
|
498
|
+
.ring = ring,
|
|
499
|
+
.meta = read.sealed(),
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const entrycount = read.u32v();
|
|
504
|
+
if (raw.len - read.at < entrycount) {
|
|
505
|
+
return .{ .err = try fail(alloc, "the vault file is truncated", .{}) };
|
|
506
|
+
}
|
|
507
|
+
for (0..entrycount) |_| {
|
|
508
|
+
if (read.bad) {
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
const id = read.small();
|
|
512
|
+
const name = read.sealed();
|
|
513
|
+
try file.entries.append(alloc, .{ .id = id, .name = name, .value = read.sealed() });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (read.bad) {
|
|
517
|
+
return .{ .err = try fail(alloc, "the vault file is truncated", .{}) };
|
|
518
|
+
}
|
|
519
|
+
if (read.at != raw.len) {
|
|
520
|
+
return .{ .err = try fail(alloc, "the vault file has trailing bytes", .{}) };
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return .{ .ok = file };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const Writer = struct {
|
|
527
|
+
out: std.ArrayList(u8) = .empty,
|
|
528
|
+
alloc: Allocator,
|
|
529
|
+
|
|
530
|
+
fn u8v(self: *Writer, value: u8) Allocator.Error!void {
|
|
531
|
+
try self.out.append(self.alloc, value);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
fn u32v(self: *Writer, value: u32) Allocator.Error!void {
|
|
535
|
+
var four: [4]u8 = undefined;
|
|
536
|
+
std.mem.writeInt(u32, &four, value, .big);
|
|
537
|
+
try self.out.appendSlice(self.alloc, &four);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
fn small(self: *Writer, value: []const u8) Allocator.Error!void {
|
|
541
|
+
try self.u8v(@intCast(value.len));
|
|
542
|
+
try self.out.appendSlice(self.alloc, value);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
fn large(self: *Writer, value: []const u8) Allocator.Error!void {
|
|
546
|
+
try self.u32v(@intCast(value.len));
|
|
547
|
+
try self.out.appendSlice(self.alloc, value);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
fn sealed(self: *Writer, value: Sealed) Allocator.Error!void {
|
|
551
|
+
try self.small(value.iv);
|
|
552
|
+
try self.large(value.blob);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
fn byid(_: void, left: EntryRecord, right: EntryRecord) bool {
|
|
557
|
+
return .lt == std.mem.order(u8, left.id, right.id);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
fn writefile(alloc: Allocator, file: *const VaultFile) Allocator.Error![]const u8 {
|
|
561
|
+
var write = Writer{ .alloc = alloc };
|
|
562
|
+
|
|
563
|
+
try write.out.appendSlice(alloc, MAGIC);
|
|
564
|
+
try write.u8v(FORMAT);
|
|
565
|
+
try write.u8v(KDF_PBKDF2);
|
|
566
|
+
try write.u8v(CIPHER_AESGCM);
|
|
567
|
+
try write.u8v(0);
|
|
568
|
+
|
|
569
|
+
try write.u32v(@intCast(file.keys.items.len));
|
|
570
|
+
for (file.keys.items) |record| {
|
|
571
|
+
try write.small(record.id);
|
|
572
|
+
try write.small(record.salt);
|
|
573
|
+
try write.u32v(record.iters);
|
|
574
|
+
try write.sealed(record.ring);
|
|
575
|
+
try write.sealed(record.meta);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// SORTED BY ID, which is a blinded value: the file therefore records
|
|
579
|
+
// nothing about the order secrets were written in.
|
|
580
|
+
const entries = try alloc.dupe(EntryRecord, file.entries.items);
|
|
581
|
+
std.mem.sort(EntryRecord, entries, {}, byid);
|
|
582
|
+
|
|
583
|
+
try write.u32v(@intCast(entries.len));
|
|
584
|
+
for (entries) |record| {
|
|
585
|
+
try write.small(record.id);
|
|
586
|
+
try write.sealed(record.name);
|
|
587
|
+
try write.sealed(record.value);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return write.out.items;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// --- the vault --------------------------------------------------------
|
|
594
|
+
|
|
595
|
+
/// What a key may do. `grants` is empty for a master key, which reads and
|
|
596
|
+
/// writes every name there is.
|
|
597
|
+
pub const KeyInfo = struct {
|
|
598
|
+
key: []const u8,
|
|
599
|
+
master: bool = false,
|
|
600
|
+
write: bool = false,
|
|
601
|
+
/// Sorted, and `[]const` so that a caller cannot write through it.
|
|
602
|
+
/// The struct itself is a VALUE: `info.write = true` edits the
|
|
603
|
+
/// caller's copy and nothing the vault reads, which is how this port
|
|
604
|
+
/// answers the defect the review round found in the canonical.
|
|
605
|
+
grants: []const []const u8 = &.{},
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
/// What mints a restricted key.
|
|
609
|
+
pub const GrantSpec = struct {
|
|
610
|
+
/// The id the new key answers to.
|
|
611
|
+
key: []const u8,
|
|
612
|
+
/// What unwraps it. Nothing else does, and no master can recover it -
|
|
613
|
+
/// a lost restricted passphrase is re-granted, never read back.
|
|
614
|
+
passphrase: []const u8,
|
|
615
|
+
/// The names the key may read. A name that does not exist yet is
|
|
616
|
+
/// allowed and means what it says: the key reads it once a master
|
|
617
|
+
/// writes it.
|
|
618
|
+
names: []const []const u8 = &.{},
|
|
619
|
+
/// Whether it may overwrite the values it can read.
|
|
620
|
+
write: bool = false,
|
|
621
|
+
/// PBKDF2 rounds for this key, defaulting to the opening handle's.
|
|
622
|
+
iterations: u32 = 0,
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
/// How a vault file is opened as one key.
|
|
626
|
+
pub const Options = struct {
|
|
627
|
+
/// The vault file.
|
|
628
|
+
file: []const u8,
|
|
629
|
+
/// Which key to open with. Defaults to `MASTERKEY`.
|
|
630
|
+
key: []const u8 = "",
|
|
631
|
+
/// What unwraps that key.
|
|
632
|
+
passphrase: []const u8,
|
|
633
|
+
/// The PBKDF2 round count used when this handle CREATES a key.
|
|
634
|
+
/// Reading uses what the file records for the key being opened.
|
|
635
|
+
iterations: u32 = 0,
|
|
636
|
+
/// Make the file, with this key as its master, if it is not there.
|
|
637
|
+
///
|
|
638
|
+
/// Off by default. A missing vault is far more often a broken
|
|
639
|
+
/// deployment than a new one, and a store that invents itself where a
|
|
640
|
+
/// real vault was meant to be answers every read with a miss.
|
|
641
|
+
create: bool = false,
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const Grant = struct {
|
|
645
|
+
name: []const u8,
|
|
646
|
+
key: []const u8,
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
/// A handle on one vault file, opened as ONE key.
|
|
650
|
+
///
|
|
651
|
+
/// Every method answers as that key: `list` shows the names it may read,
|
|
652
|
+
/// `get` answers for those and misses on the rest, and the master-only
|
|
653
|
+
/// methods refuse for any other key. Nothing is read or derived until the
|
|
654
|
+
/// first call that needs the file, so putting a vault in a chain costs no
|
|
655
|
+
/// key derivation until a secret is actually wanted.
|
|
656
|
+
///
|
|
657
|
+
/// ONE THREAD. This port builds and reads a chain from one thread, as
|
|
658
|
+
/// voxgig/plugin's zig port does and as `provider.Building` says outright;
|
|
659
|
+
/// two handles on one file in one thread interleave only between calls,
|
|
660
|
+
/// and the format's answer to two PROCESSES is the exclusive create and
|
|
661
|
+
/// the atomic rename - a reader sees one whole vault or the other, never
|
|
662
|
+
/// half of one.
|
|
663
|
+
pub const Vault = struct {
|
|
664
|
+
alloc: Allocator,
|
|
665
|
+
io: std.Io,
|
|
666
|
+
file: []const u8,
|
|
667
|
+
key: []const u8,
|
|
668
|
+
passphrase: []const u8,
|
|
669
|
+
iterations: u32,
|
|
670
|
+
create: bool,
|
|
671
|
+
/// Where `VAULTS` holds this handle, so that a definition can export
|
|
672
|
+
/// an index; see `VAULTS`.
|
|
673
|
+
slot: usize,
|
|
674
|
+
|
|
675
|
+
/// The derived state, which outlives one call and is dropped
|
|
676
|
+
/// wholesale by `close`. Its own arena because a ring unwraps into a
|
|
677
|
+
/// dozen small allocations of three different lifetimes, and one
|
|
678
|
+
/// reset covers them all.
|
|
679
|
+
state: std.heap.ArenaAllocator,
|
|
680
|
+
info: ?KeyInfo = null,
|
|
681
|
+
root: ?[]const u8 = null,
|
|
682
|
+
grants: []const Grant = &.{},
|
|
683
|
+
/// THE SEALED RING THIS WAS DERIVED FROM, kept so that every later
|
|
684
|
+
/// call can check the file still says the same thing. A handle that
|
|
685
|
+
/// cached its keys and never looked again kept reading a vault after
|
|
686
|
+
/// its key was revoked, which is the one thing `revoke` promises.
|
|
687
|
+
ring: ?Sealed = null,
|
|
688
|
+
|
|
689
|
+
/// The vault file this handle reads.
|
|
690
|
+
pub fn vaultfile(self: *const Vault) []const u8 {
|
|
691
|
+
return self.file;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/// The key id this handle opens with.
|
|
695
|
+
pub fn vaultkey(self: *const Vault) []const u8 {
|
|
696
|
+
return self.key;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/// Derives the key and reads the file NOW rather than at first use.
|
|
700
|
+
///
|
|
701
|
+
/// A COPY, allocated from `alloc`. `set` asks this whether the key
|
|
702
|
+
/// may write, and handing back the value that answer lives in let a
|
|
703
|
+
/// caller flip its own permission.
|
|
704
|
+
pub fn open(self: *Vault, alloc: Allocator) Allocator.Error!Answer(KeyInfo) {
|
|
705
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
706
|
+
defer work.deinit();
|
|
707
|
+
|
|
708
|
+
const held = switch (try self.load(work.allocator(), alloc)) {
|
|
709
|
+
.err => |message| return .{ .err = message },
|
|
710
|
+
.ok => |loaded| loaded,
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
return .{ .ok = try copyinfo(alloc, held.info) };
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/// Forgets the derived keys. The next call opens again.
|
|
717
|
+
pub fn close(self: *Vault) void {
|
|
718
|
+
_ = self.state.reset(.free_all);
|
|
719
|
+
self.info = null;
|
|
720
|
+
self.root = null;
|
|
721
|
+
self.grants = &.{};
|
|
722
|
+
self.ring = null;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/// Releases the handle itself. The slot it held is reused.
|
|
726
|
+
pub fn deinit(self: *Vault) void {
|
|
727
|
+
self.state.deinit();
|
|
728
|
+
VAULTS.items[self.slot] = null;
|
|
729
|
+
self.alloc.free(self.file);
|
|
730
|
+
self.alloc.free(self.key);
|
|
731
|
+
self.alloc.free(self.passphrase);
|
|
732
|
+
self.alloc.destroy(self);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// --- reading the file ---------------------------------------------
|
|
736
|
+
|
|
737
|
+
fn bytes(self: *Vault, work: Allocator, alloc: Allocator) Allocator.Error!Answer([]const u8) {
|
|
738
|
+
const raw = std.Io.Dir.cwd().readFileAlloc(self.io, self.file, work, .unlimited) catch |err| switch (err) {
|
|
739
|
+
// A vault is configured deliberately, with a key. Its absence
|
|
740
|
+
// is a broken deployment and never "no secrets here":
|
|
741
|
+
// answering a miss would send the chain on to a weaker store,
|
|
742
|
+
// which is the failure mode this library most has to avoid.
|
|
743
|
+
// `create` is the caller saying the opposite, in writing.
|
|
744
|
+
error.FileNotFound, error.NotDir => {
|
|
745
|
+
if (!self.create) {
|
|
746
|
+
return .{ .err = try fail(alloc, "no vault file: {s}", .{self.file}) };
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
switch (try self.newvault(work, alloc, self.key, self.passphrase, self.iterations)) {
|
|
750
|
+
.err => |message| return .{ .err = message },
|
|
751
|
+
.ok => |fresh| switch (try self.putnew(work, alloc, &fresh)) {
|
|
752
|
+
.err => |message| return .{ .err = message },
|
|
753
|
+
.ok => {},
|
|
754
|
+
},
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const made = std.Io.Dir.cwd().readFileAlloc(self.io, self.file, work, .unlimited) catch |again| {
|
|
758
|
+
return .{ .err = try fail(alloc, "cannot read {s}: {s}", .{ self.file, @errorName(again) }) };
|
|
759
|
+
};
|
|
760
|
+
return .{ .ok = made };
|
|
761
|
+
},
|
|
762
|
+
else => return .{ .err = try fail(alloc, "cannot read {s}: {s}", .{ self.file, @errorName(err) }) },
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
return .{ .ok = raw };
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/// What one call works with: the parsed file and this key's rights.
|
|
769
|
+
///
|
|
770
|
+
/// The file is parsed into the CALL's arena - it is different bytes
|
|
771
|
+
/// every time - while the unwrapped ring lives in the handle's own,
|
|
772
|
+
/// because stretching a passphrase once per lookup is the cost this
|
|
773
|
+
/// caching exists to avoid.
|
|
774
|
+
const Loaded = struct {
|
|
775
|
+
file: VaultFile,
|
|
776
|
+
info: KeyInfo,
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
fn load(self: *Vault, work: Allocator, alloc: Allocator) Allocator.Error!Answer(Loaded) {
|
|
780
|
+
const raw = switch (try self.bytes(work, alloc)) {
|
|
781
|
+
.err => |message| return .{ .err = message },
|
|
782
|
+
.ok => |read| read,
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
const file = switch (try readfile(work, raw)) {
|
|
786
|
+
.err => |message| return .{ .err = message },
|
|
787
|
+
.ok => |parsed| parsed,
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
const record = file.key(self.key) orelse {
|
|
791
|
+
// REVOKED, or never there. Either way this handle is
|
|
792
|
+
// finished, and dropping what it derived is what stops the
|
|
793
|
+
// next call answering from memory.
|
|
794
|
+
self.close();
|
|
795
|
+
return .{ .err = try fail(alloc, "no such key: {s}", .{self.key}) };
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// The file still holds this key, and holds the SAME ring: a key
|
|
799
|
+
// revoked and re-granted under another passphrase is a different
|
|
800
|
+
// key wearing the id, and re-deriving is what refuses it.
|
|
801
|
+
if (self.info) |held| {
|
|
802
|
+
if (self.ring) |was| {
|
|
803
|
+
if (sameseal(was, record.ring)) {
|
|
804
|
+
return .{ .ok = .{ .file = file, .info = held } };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
self.close();
|
|
809
|
+
|
|
810
|
+
const keep = self.state.allocator();
|
|
811
|
+
|
|
812
|
+
const unwrapped = switch (try kek(work, self.passphrase, record.salt, record.iters)) {
|
|
813
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
814
|
+
.ok => |made| made,
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
const label = try std.fmt.allocPrint(work, AAD_RING ++ "{s}", .{self.key});
|
|
818
|
+
const what = try std.fmt.allocPrint(
|
|
819
|
+
work,
|
|
820
|
+
"wrong passphrase for key {s}, or a damaged vault",
|
|
821
|
+
.{self.key},
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
const plain = switch (try unseal(work, unwrapped, record.ring, label, what)) {
|
|
825
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
826
|
+
.ok => |made| made,
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
const parsed = switch (try jsonof(work, plain, "key ring")) {
|
|
830
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
831
|
+
.ok => |made| made,
|
|
832
|
+
};
|
|
833
|
+
const held = parsed.value;
|
|
834
|
+
|
|
835
|
+
var grants: std.ArrayList(Grant) = .empty;
|
|
836
|
+
var names: std.ArrayList([]const u8) = .empty;
|
|
837
|
+
|
|
838
|
+
if (jget(held, "grants")) |given| {
|
|
839
|
+
if (.object == given) {
|
|
840
|
+
var it = given.object.iterator();
|
|
841
|
+
while (it.next()) |pair| {
|
|
842
|
+
const text = jstr(pair.value_ptr.*) orelse {
|
|
843
|
+
return .{ .err = try fail(alloc, "missing a granted key", .{}) };
|
|
844
|
+
};
|
|
845
|
+
const raw_key = switch (try unb64(work, text, "a granted key")) {
|
|
846
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
847
|
+
.ok => |made| made,
|
|
848
|
+
};
|
|
849
|
+
const name = try keep.dupe(u8, pair.key_ptr.*);
|
|
850
|
+
try grants.append(keep, .{ .name = name, .key = try keep.dupe(u8, raw_key) });
|
|
851
|
+
try names.append(keep, name);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
std.mem.sort([]const u8, names.items, {}, bytext);
|
|
857
|
+
|
|
858
|
+
var root: ?[]const u8 = null;
|
|
859
|
+
if (jstr(jget(held, "root"))) |text| {
|
|
860
|
+
const raw_root = switch (try unb64(work, text, "the root key")) {
|
|
861
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
862
|
+
.ok => |made| made,
|
|
863
|
+
};
|
|
864
|
+
root = try keep.dupe(u8, raw_root);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
self.root = root;
|
|
868
|
+
self.grants = grants.items;
|
|
869
|
+
self.ring = try copyseal(keep, record.ring);
|
|
870
|
+
self.info = .{
|
|
871
|
+
.key = self.key,
|
|
872
|
+
.master = null != root,
|
|
873
|
+
.write = null != root or jbool(jget(held, "write")),
|
|
874
|
+
.grants = names.items,
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
return .{ .ok = .{ .file = file, .info = self.info.? } };
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/// The root key, or a refusal naming what needed it.
|
|
881
|
+
fn rootof(self: *Vault, alloc: Allocator, what: []const u8) Allocator.Error!Answer([]const u8) {
|
|
882
|
+
const root = self.root orelse {
|
|
883
|
+
return .{ .err = try fail(
|
|
884
|
+
alloc,
|
|
885
|
+
"{s} needs a master key, and {s} is restricted",
|
|
886
|
+
.{ what, self.key },
|
|
887
|
+
) };
|
|
888
|
+
};
|
|
889
|
+
return .{ .ok = root };
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/// The key for one name, or null when this key cannot reach it.
|
|
893
|
+
fn keyfor(self: *Vault, work: Allocator, name: []const u8) Allocator.Error!?[]const u8 {
|
|
894
|
+
if (self.root) |root| {
|
|
895
|
+
return try secretkey(work, root, name);
|
|
896
|
+
}
|
|
897
|
+
for (self.grants) |held| {
|
|
898
|
+
if (std.mem.eql(u8, name, held.name)) {
|
|
899
|
+
return held.key;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/// Replaces the file rather than editing it in place. The rename is
|
|
906
|
+
/// what makes a concurrent reader see either the old file or the new
|
|
907
|
+
/// one, so a write interrupted halfway leaves a vault rather than
|
|
908
|
+
/// wreckage.
|
|
909
|
+
///
|
|
910
|
+
/// THE TEMPORARY IS RANDOM AND EXCLUSIVE. `<vault>.<pid>.tmp` is a
|
|
911
|
+
/// name anyone can predict, and an ordinary create FOLLOWS a symlink,
|
|
912
|
+
/// so anyone who could write the vault's directory could point that
|
|
913
|
+
/// name at another file and have the next save truncate it. An
|
|
914
|
+
/// exclusive create refuses an existing path and will not follow a
|
|
915
|
+
/// symlink to make one, and the random suffix stops two writers
|
|
916
|
+
/// colliding on the name.
|
|
917
|
+
fn save(self: *Vault, work: Allocator, alloc: Allocator, file: *const VaultFile) Allocator.Error!Answer(void) {
|
|
918
|
+
const suffix = switch (try random(work, self.io, 8)) {
|
|
919
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
920
|
+
.ok => |made| made,
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
const temp = try std.fmt.allocPrint(work, "{s}.{x}.tmp", .{ self.file, suffix });
|
|
924
|
+
const raw = try writefile(work, file);
|
|
925
|
+
|
|
926
|
+
std.Io.Dir.cwd().writeFile(self.io, .{
|
|
927
|
+
.sub_path = temp,
|
|
928
|
+
.data = raw,
|
|
929
|
+
.flags = .{ .exclusive = true, .permissions = OWNERONLY },
|
|
930
|
+
}) catch |err| {
|
|
931
|
+
return .{ .err = try fail(alloc, "cannot write {s}: {s}", .{ self.file, @errorName(err) }) };
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
std.Io.Dir.cwd().rename(temp, .cwd(), self.file, self.io) catch |err| {
|
|
935
|
+
// The vault is unchanged either way, and the write error is
|
|
936
|
+
// what the caller needs to be told about.
|
|
937
|
+
std.Io.Dir.cwd().deleteFile(self.io, temp) catch {};
|
|
938
|
+
return .{ .err = try fail(alloc, "cannot write {s}: {s}", .{ self.file, @errorName(err) }) };
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
return .{ .ok = {} };
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/// Writes a vault file that is not there yet, and REFUSES one that is.
|
|
945
|
+
///
|
|
946
|
+
/// Straight to the target under an exclusive create rather than
|
|
947
|
+
/// through a temporary and a rename. A rename REPLACES its
|
|
948
|
+
/// destination, so two processes creating the same vault both
|
|
949
|
+
/// succeeded and the second discarded the first one's secrets; a stat
|
|
950
|
+
/// beforehand only narrows that window. There is nothing to lose by
|
|
951
|
+
/// writing the target directly here, because there is no file to
|
|
952
|
+
/// damage: either this call creates it or it fails.
|
|
953
|
+
fn putnew(self: *Vault, work: Allocator, alloc: Allocator, file: *const VaultFile) Allocator.Error!Answer(void) {
|
|
954
|
+
const raw = try writefile(work, file);
|
|
955
|
+
|
|
956
|
+
std.Io.Dir.cwd().writeFile(self.io, .{
|
|
957
|
+
.sub_path = self.file,
|
|
958
|
+
.data = raw,
|
|
959
|
+
.flags = .{ .exclusive = true, .permissions = OWNERONLY },
|
|
960
|
+
}) catch |err| switch (err) {
|
|
961
|
+
error.PathAlreadyExists => {
|
|
962
|
+
return .{ .err = try fail(alloc, "vault file already exists: {s}", .{self.file}) };
|
|
963
|
+
},
|
|
964
|
+
else => return .{ .err = try fail(alloc, "cannot write {s}: {s}", .{ self.file, @errorName(err) }) },
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
return .{ .ok = {} };
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// --- reading secrets ----------------------------------------------
|
|
971
|
+
|
|
972
|
+
/// The names this key can read, sorted.
|
|
973
|
+
pub fn list(self: *Vault, alloc: Allocator) Allocator.Error!Answer([]const []const u8) {
|
|
974
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
975
|
+
defer work.deinit();
|
|
976
|
+
const scratch = work.allocator();
|
|
977
|
+
|
|
978
|
+
const held = switch (try self.load(scratch, alloc)) {
|
|
979
|
+
.err => |message| return .{ .err = message },
|
|
980
|
+
.ok => |loaded| loaded,
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
var names: std.ArrayList([]const u8) = .empty;
|
|
984
|
+
|
|
985
|
+
if (self.root) |root| {
|
|
986
|
+
const namekey = try mac(scratch, root, LABEL_NAMES);
|
|
987
|
+
for (held.file.entries.items) |entry| {
|
|
988
|
+
const plain = switch (try unseal(
|
|
989
|
+
scratch,
|
|
990
|
+
namekey,
|
|
991
|
+
entry.name,
|
|
992
|
+
AAD_NAME,
|
|
993
|
+
"a secret name is damaged",
|
|
994
|
+
)) {
|
|
995
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
996
|
+
.ok => |made| made,
|
|
997
|
+
};
|
|
998
|
+
try names.append(alloc, try alloc.dupe(u8, plain));
|
|
999
|
+
}
|
|
1000
|
+
} else {
|
|
1001
|
+
// A restricted key has no name key, so it reports the grants
|
|
1002
|
+
// it can actually find: the vault never tells it what else is
|
|
1003
|
+
// there.
|
|
1004
|
+
for (held.info.grants) |name| {
|
|
1005
|
+
const key = try self.keyfor(scratch, name) orelse continue;
|
|
1006
|
+
if (null != held.file.entry(try entryid(scratch, key))) {
|
|
1007
|
+
try names.append(alloc, try alloc.dupe(u8, name));
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
std.mem.sort([]const u8, names.items, {}, bytext);
|
|
1013
|
+
|
|
1014
|
+
return .{ .ok = names.items };
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/// The value, or a miss. A name the vault does not hold and a name
|
|
1018
|
+
/// this key was not granted are both a miss.
|
|
1019
|
+
pub fn get(self: *Vault, alloc: Allocator, name: []const u8) Allocator.Error!Found {
|
|
1020
|
+
switch (try sekreto.checkname(alloc, name)) {
|
|
1021
|
+
.err => |message| return .{ .err = message },
|
|
1022
|
+
.ok => {},
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1026
|
+
defer work.deinit();
|
|
1027
|
+
const scratch = work.allocator();
|
|
1028
|
+
|
|
1029
|
+
const held = switch (try self.load(scratch, alloc)) {
|
|
1030
|
+
.err => |message| return .{ .err = message },
|
|
1031
|
+
.ok => |loaded| loaded,
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
// OUTSIDE THE GRANT IS A MISS, deliberately. The vault answers as
|
|
1035
|
+
// the key that opened it, so a name this key cannot read is a
|
|
1036
|
+
// name this store does not hold for this caller - the same answer
|
|
1037
|
+
// a stranger's vault gives, and the one that makes a restricted
|
|
1038
|
+
// key in front of a broader store a workable chain.
|
|
1039
|
+
const key = try self.keyfor(scratch, name) orelse return .{ .ok = null };
|
|
1040
|
+
|
|
1041
|
+
const entry = held.file.entry(try entryid(scratch, key)) orelse return .{ .ok = null };
|
|
1042
|
+
|
|
1043
|
+
const label = try std.fmt.allocPrint(scratch, AAD_SECRET ++ "{s}", .{name});
|
|
1044
|
+
const what = try std.fmt.allocPrint(scratch, "the value of {s} is damaged", .{name});
|
|
1045
|
+
|
|
1046
|
+
const plain = switch (try unseal(scratch, key, entry.value, label, what)) {
|
|
1047
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1048
|
+
.ok => |made| made,
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
return .{ .ok = try alloc.dupe(u8, plain) };
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/// Whether this key can read that name.
|
|
1055
|
+
pub fn has(self: *Vault, alloc: Allocator, name: []const u8) Allocator.Error!Answer(bool) {
|
|
1056
|
+
return switch (try self.get(alloc, name)) {
|
|
1057
|
+
.err => |message| .{ .err = message },
|
|
1058
|
+
.ok => |found| .{ .ok = null != found },
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// --- writing ------------------------------------------------------
|
|
1063
|
+
|
|
1064
|
+
/// Writes a value. A master writes any name; a restricted key holding
|
|
1065
|
+
/// `write` overwrites the names it was granted, and creates none.
|
|
1066
|
+
pub fn set(self: *Vault, alloc: Allocator, name: []const u8, value: []const u8) Allocator.Error!Answer(void) {
|
|
1067
|
+
// Every write on this file, from any handle in this process,
|
|
1068
|
+
// serializes here; see lockfor.
|
|
1069
|
+
const onefile = try lockfor(self.io, self.file);
|
|
1070
|
+
onefile.lockUncancelable(self.io);
|
|
1071
|
+
defer onefile.unlock(self.io);
|
|
1072
|
+
|
|
1073
|
+
switch (try sekreto.checkname(alloc, name)) {
|
|
1074
|
+
.err => |message| return .{ .err = message },
|
|
1075
|
+
.ok => {},
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1079
|
+
defer work.deinit();
|
|
1080
|
+
const scratch = work.allocator();
|
|
1081
|
+
|
|
1082
|
+
var held = switch (try self.load(scratch, alloc)) {
|
|
1083
|
+
.err => |message| return .{ .err = message },
|
|
1084
|
+
.ok => |loaded| loaded,
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
if (!held.info.write) {
|
|
1088
|
+
return .{ .err = try fail(alloc, "key {s} is read-only", .{held.info.key}) };
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
const key = try self.keyfor(scratch, name) orelse {
|
|
1092
|
+
return .{ .err = try fail(alloc, "key {s} was not granted {s}", .{ held.info.key, name }) };
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
const label = try std.fmt.allocPrint(scratch, AAD_SECRET ++ "{s}", .{name});
|
|
1096
|
+
|
|
1097
|
+
const box = switch (try seal(scratch, self.io, key, value, label)) {
|
|
1098
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1099
|
+
.ok => |made| made,
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
const id = try entryid(scratch, key);
|
|
1103
|
+
|
|
1104
|
+
if (held.file.entry(id)) |entry| {
|
|
1105
|
+
entry.value = box;
|
|
1106
|
+
} else {
|
|
1107
|
+
// A NEW NAME NEEDS THE NAME KEY, which only a master holds.
|
|
1108
|
+
// So a restricted key with `write` updates what it was
|
|
1109
|
+
// granted and cannot grow the vault, which is what
|
|
1110
|
+
// "restricted" has to mean for the grant list to stay the
|
|
1111
|
+
// whole story.
|
|
1112
|
+
const what = try std.fmt.allocPrint(scratch, "creating the secret {s}", .{name});
|
|
1113
|
+
const root = switch (try self.rootof(alloc, what)) {
|
|
1114
|
+
.err => |message| return .{ .err = message },
|
|
1115
|
+
.ok => |made| made,
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
const namekey = try mac(scratch, root, LABEL_NAMES);
|
|
1119
|
+
const sealedname = switch (try seal(scratch, self.io, namekey, name, AAD_NAME)) {
|
|
1120
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1121
|
+
.ok => |made| made,
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
try held.file.entries.append(scratch, .{ .id = id, .name = sealedname, .value = box });
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
return switch (try self.save(scratch, alloc, &held.file)) {
|
|
1128
|
+
.err => |message| .{ .err = message },
|
|
1129
|
+
.ok => .{ .ok = {} },
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/// Drops a name. Master only.
|
|
1134
|
+
pub fn remove(self: *Vault, alloc: Allocator, name: []const u8) Allocator.Error!Answer(void) {
|
|
1135
|
+
// Every write on this file, from any handle in this process,
|
|
1136
|
+
// serializes here; see lockfor.
|
|
1137
|
+
const onefile = try lockfor(self.io, self.file);
|
|
1138
|
+
onefile.lockUncancelable(self.io);
|
|
1139
|
+
defer onefile.unlock(self.io);
|
|
1140
|
+
|
|
1141
|
+
switch (try sekreto.checkname(alloc, name)) {
|
|
1142
|
+
.err => |message| return .{ .err = message },
|
|
1143
|
+
.ok => {},
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1147
|
+
defer work.deinit();
|
|
1148
|
+
const scratch = work.allocator();
|
|
1149
|
+
|
|
1150
|
+
var held = switch (try self.load(scratch, alloc)) {
|
|
1151
|
+
.err => |message| return .{ .err = message },
|
|
1152
|
+
.ok => |loaded| loaded,
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
const root = switch (try self.rootof(alloc, "removing a secret")) {
|
|
1156
|
+
.err => |message| return .{ .err = message },
|
|
1157
|
+
.ok => |made| made,
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
const want = try entryid(scratch, try secretkey(scratch, root, name));
|
|
1161
|
+
|
|
1162
|
+
var kept: std.ArrayList(EntryRecord) = .empty;
|
|
1163
|
+
var found = false;
|
|
1164
|
+
|
|
1165
|
+
for (held.file.entries.items) |entry| {
|
|
1166
|
+
if (!found and std.mem.eql(u8, want, entry.id)) {
|
|
1167
|
+
found = true;
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
try kept.append(scratch, entry);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (!found) {
|
|
1174
|
+
return .{ .err = try fail(alloc, "no such secret: {s}", .{name}) };
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
held.file.entries = kept;
|
|
1178
|
+
|
|
1179
|
+
return switch (try self.save(scratch, alloc, &held.file)) {
|
|
1180
|
+
.err => |message| .{ .err = message },
|
|
1181
|
+
.ok => .{ .ok = {} },
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// --- keys ---------------------------------------------------------
|
|
1186
|
+
|
|
1187
|
+
/// Every key in the file, with what it may do. Master only.
|
|
1188
|
+
pub fn keys(self: *Vault, alloc: Allocator) Allocator.Error!Answer([]const KeyInfo) {
|
|
1189
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1190
|
+
defer work.deinit();
|
|
1191
|
+
const scratch = work.allocator();
|
|
1192
|
+
|
|
1193
|
+
const held = switch (try self.load(scratch, alloc)) {
|
|
1194
|
+
.err => |message| return .{ .err = message },
|
|
1195
|
+
.ok => |loaded| loaded,
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
const root = switch (try self.rootof(alloc, "listing the keys")) {
|
|
1199
|
+
.err => |message| return .{ .err = message },
|
|
1200
|
+
.ok => |made| made,
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
const metakey = try mac(scratch, root, LABEL_META);
|
|
1204
|
+
var out: std.ArrayList(KeyInfo) = .empty;
|
|
1205
|
+
|
|
1206
|
+
for (held.file.keys.items) |record| {
|
|
1207
|
+
var info = KeyInfo{ .key = try alloc.dupe(u8, record.id) };
|
|
1208
|
+
|
|
1209
|
+
const label = try std.fmt.allocPrint(scratch, AAD_META ++ "{s}", .{record.id});
|
|
1210
|
+
|
|
1211
|
+
// A record written under a root key this one has replaced is
|
|
1212
|
+
// still in the file and still opens with its own passphrase,
|
|
1213
|
+
// so it is reported rather than hidden - with what it can do
|
|
1214
|
+
// unknown.
|
|
1215
|
+
switch (try unseal(scratch, metakey, record.meta, label, "metadata")) {
|
|
1216
|
+
.err => {},
|
|
1217
|
+
.ok => |plain| {
|
|
1218
|
+
const parsed = switch (try jsonof(scratch, plain, "metadata")) {
|
|
1219
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1220
|
+
.ok => |made| made,
|
|
1221
|
+
};
|
|
1222
|
+
const noted = parsed.value;
|
|
1223
|
+
|
|
1224
|
+
info.master = jbool(jget(noted, "master"));
|
|
1225
|
+
info.write = jbool(jget(noted, "write"));
|
|
1226
|
+
|
|
1227
|
+
var names: std.ArrayList([]const u8) = .empty;
|
|
1228
|
+
if (jget(noted, "grants")) |given| {
|
|
1229
|
+
if (.array == given) {
|
|
1230
|
+
for (given.array.items) |item| {
|
|
1231
|
+
if (jstr(item)) |text| {
|
|
1232
|
+
try names.append(alloc, try alloc.dupe(u8, text));
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
std.mem.sort([]const u8, names.items, {}, bytext);
|
|
1238
|
+
info.grants = names.items;
|
|
1239
|
+
},
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
try out.append(alloc, info);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
return .{ .ok = out.items };
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/// Mints a restricted key. Master only.
|
|
1249
|
+
pub fn grant(self: *Vault, alloc: Allocator, spec: GrantSpec) Allocator.Error!Answer(void) {
|
|
1250
|
+
// Every write on this file, from any handle in this process,
|
|
1251
|
+
// serializes here; see lockfor.
|
|
1252
|
+
const onefile = try lockfor(self.io, self.file);
|
|
1253
|
+
onefile.lockUncancelable(self.io);
|
|
1254
|
+
defer onefile.unlock(self.io);
|
|
1255
|
+
|
|
1256
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1257
|
+
defer work.deinit();
|
|
1258
|
+
const scratch = work.allocator();
|
|
1259
|
+
|
|
1260
|
+
var held = switch (try self.load(scratch, alloc)) {
|
|
1261
|
+
.err => |message| return .{ .err = message },
|
|
1262
|
+
.ok => |loaded| loaded,
|
|
1263
|
+
};
|
|
1264
|
+
|
|
1265
|
+
const root = switch (try self.rootof(alloc, "granting a key")) {
|
|
1266
|
+
.err => |message| return .{ .err = message },
|
|
1267
|
+
.ok => |made| made,
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
switch (try checkid(alloc, spec.key, "a grant needs a key id")) {
|
|
1271
|
+
.err => |message| return .{ .err = message },
|
|
1272
|
+
.ok => {},
|
|
1273
|
+
}
|
|
1274
|
+
if (0 == spec.passphrase.len) {
|
|
1275
|
+
return .{ .err = try fail(alloc, "a grant needs a passphrase", .{}) };
|
|
1276
|
+
}
|
|
1277
|
+
if (null != held.file.key(spec.key)) {
|
|
1278
|
+
return .{ .err = try fail(alloc, "key already exists: {s}", .{spec.key}) };
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const names = try scratch.dupe([]const u8, spec.names);
|
|
1282
|
+
std.mem.sort([]const u8, names, {}, bytext);
|
|
1283
|
+
|
|
1284
|
+
var ring: std.ArrayList(u8) = .empty;
|
|
1285
|
+
try ring.print(scratch, "{{\"v\":{d},\"write\":{s},\"grants\":{{", .{
|
|
1286
|
+
FORMAT,
|
|
1287
|
+
if (spec.write) "true" else "false",
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
for (names, 0..) |name, at| {
|
|
1291
|
+
switch (try sekreto.checkname(alloc, name)) {
|
|
1292
|
+
.err => |message| return .{ .err = message },
|
|
1293
|
+
.ok => {},
|
|
1294
|
+
}
|
|
1295
|
+
if (0 != at) {
|
|
1296
|
+
try ring.append(scratch, ',');
|
|
1297
|
+
}
|
|
1298
|
+
try jsonstring(&ring, scratch, name);
|
|
1299
|
+
try ring.append(scratch, ':');
|
|
1300
|
+
try jsonstring(&ring, scratch, try b64(scratch, try secretkey(scratch, root, name)));
|
|
1301
|
+
}
|
|
1302
|
+
try ring.appendSlice(scratch, "}}");
|
|
1303
|
+
|
|
1304
|
+
var meta: std.ArrayList(u8) = .empty;
|
|
1305
|
+
try meta.print(scratch, "{{\"v\":{d},\"master\":false,\"write\":{s},\"grants\":[", .{
|
|
1306
|
+
FORMAT,
|
|
1307
|
+
if (spec.write) "true" else "false",
|
|
1308
|
+
});
|
|
1309
|
+
for (names, 0..) |name, at| {
|
|
1310
|
+
if (0 != at) {
|
|
1311
|
+
try meta.append(scratch, ',');
|
|
1312
|
+
}
|
|
1313
|
+
try jsonstring(&meta, scratch, name);
|
|
1314
|
+
}
|
|
1315
|
+
try meta.appendSlice(scratch, "]}");
|
|
1316
|
+
|
|
1317
|
+
const iterations = if (0 < spec.iterations) spec.iterations else self.iterations;
|
|
1318
|
+
|
|
1319
|
+
const record = switch (try sealkey(
|
|
1320
|
+
scratch,
|
|
1321
|
+
self.io,
|
|
1322
|
+
root,
|
|
1323
|
+
spec.key,
|
|
1324
|
+
spec.passphrase,
|
|
1325
|
+
iterations,
|
|
1326
|
+
ring.items,
|
|
1327
|
+
meta.items,
|
|
1328
|
+
)) {
|
|
1329
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1330
|
+
.ok => |made| made,
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
try held.file.keys.append(scratch, record);
|
|
1334
|
+
|
|
1335
|
+
return switch (try self.save(scratch, alloc, &held.file)) {
|
|
1336
|
+
.err => |message| .{ .err = message },
|
|
1337
|
+
.ok => .{ .ok = {} },
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/// Drops a key. Master only.
|
|
1342
|
+
///
|
|
1343
|
+
/// Anyone who already copied the file keeps whatever that key could
|
|
1344
|
+
/// read, so revoking bars future reads of the LIVE file and `rotate`
|
|
1345
|
+
/// is what takes a secret back.
|
|
1346
|
+
pub fn revoke(self: *Vault, alloc: Allocator, key: []const u8) Allocator.Error!Answer(void) {
|
|
1347
|
+
// Every write on this file, from any handle in this process,
|
|
1348
|
+
// serializes here; see lockfor.
|
|
1349
|
+
const onefile = try lockfor(self.io, self.file);
|
|
1350
|
+
onefile.lockUncancelable(self.io);
|
|
1351
|
+
defer onefile.unlock(self.io);
|
|
1352
|
+
|
|
1353
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1354
|
+
defer work.deinit();
|
|
1355
|
+
const scratch = work.allocator();
|
|
1356
|
+
|
|
1357
|
+
var held = switch (try self.load(scratch, alloc)) {
|
|
1358
|
+
.err => |message| return .{ .err = message },
|
|
1359
|
+
.ok => |loaded| loaded,
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
switch (try self.rootof(alloc, "revoking a key")) {
|
|
1363
|
+
.err => |message| return .{ .err = message },
|
|
1364
|
+
.ok => {},
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (std.mem.eql(u8, key, held.info.key)) {
|
|
1368
|
+
return .{ .err = try fail(alloc, "a key cannot revoke itself: {s}", .{key}) };
|
|
1369
|
+
}
|
|
1370
|
+
if (null == held.file.key(key)) {
|
|
1371
|
+
return .{ .err = try fail(alloc, "no such key: {s}", .{key}) };
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
var kept: std.ArrayList(KeyRecord) = .empty;
|
|
1375
|
+
for (held.file.keys.items) |record| {
|
|
1376
|
+
if (!std.mem.eql(u8, key, record.id)) {
|
|
1377
|
+
try kept.append(scratch, record);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
held.file.keys = kept;
|
|
1381
|
+
|
|
1382
|
+
return switch (try self.save(scratch, alloc, &held.file)) {
|
|
1383
|
+
.err => |message| .{ .err = message },
|
|
1384
|
+
.ok => .{ .ok = {} },
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/// Takes a new root key, re-encrypts every value under it, and DROPS
|
|
1389
|
+
/// EVERY OTHER KEY. Master only.
|
|
1390
|
+
///
|
|
1391
|
+
/// The other keys go because they must: their rings are sealed under
|
|
1392
|
+
/// passphrases this process does not have, so there is no way to hand
|
|
1393
|
+
/// them keys they can unwrap. Re-grant afterwards.
|
|
1394
|
+
pub fn rotate(self: *Vault, alloc: Allocator) Allocator.Error!Answer(void) {
|
|
1395
|
+
// Every write on this file, from any handle in this process,
|
|
1396
|
+
// serializes here; see lockfor.
|
|
1397
|
+
const onefile = try lockfor(self.io, self.file);
|
|
1398
|
+
onefile.lockUncancelable(self.io);
|
|
1399
|
+
defer onefile.unlock(self.io);
|
|
1400
|
+
|
|
1401
|
+
var work = std.heap.ArenaAllocator.init(self.alloc);
|
|
1402
|
+
defer work.deinit();
|
|
1403
|
+
const scratch = work.allocator();
|
|
1404
|
+
|
|
1405
|
+
const held = switch (try self.load(scratch, alloc)) {
|
|
1406
|
+
.err => |message| return .{ .err = message },
|
|
1407
|
+
.ok => |loaded| loaded,
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
const oldroot = switch (try self.rootof(alloc, "rotating the vault")) {
|
|
1411
|
+
.err => |message| return .{ .err = message },
|
|
1412
|
+
.ok => |made| made,
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
const iters = held.file.key(self.key).?.iters;
|
|
1416
|
+
|
|
1417
|
+
// Read everything out under the old root before anything changes:
|
|
1418
|
+
// once the root is replaced the old derived keys are unreachable.
|
|
1419
|
+
const oldnamekey = try mac(scratch, oldroot, LABEL_NAMES);
|
|
1420
|
+
|
|
1421
|
+
var names: std.ArrayList([]const u8) = .empty;
|
|
1422
|
+
var values: std.ArrayList([]const u8) = .empty;
|
|
1423
|
+
|
|
1424
|
+
for (held.file.entries.items) |entry| {
|
|
1425
|
+
const name = switch (try unseal(
|
|
1426
|
+
scratch,
|
|
1427
|
+
oldnamekey,
|
|
1428
|
+
entry.name,
|
|
1429
|
+
AAD_NAME,
|
|
1430
|
+
"a secret name is damaged",
|
|
1431
|
+
)) {
|
|
1432
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1433
|
+
.ok => |made| made,
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
const label = try std.fmt.allocPrint(scratch, AAD_SECRET ++ "{s}", .{name});
|
|
1437
|
+
const what = try std.fmt.allocPrint(scratch, "the value of {s} is damaged", .{name});
|
|
1438
|
+
|
|
1439
|
+
const value = switch (try unseal(
|
|
1440
|
+
scratch,
|
|
1441
|
+
try secretkey(scratch, oldroot, name),
|
|
1442
|
+
entry.value,
|
|
1443
|
+
label,
|
|
1444
|
+
what,
|
|
1445
|
+
)) {
|
|
1446
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1447
|
+
.ok => |made| made,
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
try names.append(scratch, name);
|
|
1451
|
+
try values.append(scratch, value);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
const root = switch (try random(scratch, self.io, KEYLEN)) {
|
|
1455
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1456
|
+
.ok => |made| made,
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
const namekey = try mac(scratch, root, LABEL_NAMES);
|
|
1460
|
+
var fresh = VaultFile{};
|
|
1461
|
+
|
|
1462
|
+
for (names.items, values.items) |name, value| {
|
|
1463
|
+
const key = try secretkey(scratch, root, name);
|
|
1464
|
+
const label = try std.fmt.allocPrint(scratch, AAD_SECRET ++ "{s}", .{name});
|
|
1465
|
+
|
|
1466
|
+
const sealedname = switch (try seal(scratch, self.io, namekey, name, AAD_NAME)) {
|
|
1467
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1468
|
+
.ok => |made| made,
|
|
1469
|
+
};
|
|
1470
|
+
const box = switch (try seal(scratch, self.io, key, value, label)) {
|
|
1471
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1472
|
+
.ok => |made| made,
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
try fresh.entries.append(scratch, .{
|
|
1476
|
+
.id = try entryid(scratch, key),
|
|
1477
|
+
.name = sealedname,
|
|
1478
|
+
.value = box,
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const record = switch (try masterkey(scratch, self.io, root, self.key, self.passphrase, iters)) {
|
|
1483
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1484
|
+
.ok => |made| made,
|
|
1485
|
+
};
|
|
1486
|
+
try fresh.keys.append(scratch, record);
|
|
1487
|
+
|
|
1488
|
+
// SAVE FIRST, adopt second. A handle holding the new root over a
|
|
1489
|
+
// file that still holds the old one reads nothing and says the
|
|
1490
|
+
// vault is damaged, which is the wrong story about a failed write.
|
|
1491
|
+
switch (try self.save(scratch, alloc, &fresh)) {
|
|
1492
|
+
.err => |message| return .{ .err = message },
|
|
1493
|
+
.ok => {},
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// Dropped rather than replaced: the next call re-derives from the
|
|
1497
|
+
// file this one just wrote, which is the same rule every other
|
|
1498
|
+
// change follows.
|
|
1499
|
+
self.close();
|
|
1500
|
+
|
|
1501
|
+
return .{ .ok = {} };
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
fn newvault(
|
|
1505
|
+
self: *Vault,
|
|
1506
|
+
work: Allocator,
|
|
1507
|
+
alloc: Allocator,
|
|
1508
|
+
keyid: []const u8,
|
|
1509
|
+
passphrase: []const u8,
|
|
1510
|
+
iterations: u32,
|
|
1511
|
+
) Allocator.Error!Answer(VaultFile) {
|
|
1512
|
+
const root = switch (try random(work, self.io, KEYLEN)) {
|
|
1513
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1514
|
+
.ok => |made| made,
|
|
1515
|
+
};
|
|
1516
|
+
|
|
1517
|
+
const record = switch (try masterkey(work, self.io, root, keyid, passphrase, iterations)) {
|
|
1518
|
+
.err => |message| return .{ .err = try alloc.dupe(u8, message) },
|
|
1519
|
+
.ok => |made| made,
|
|
1520
|
+
};
|
|
1521
|
+
|
|
1522
|
+
var file = VaultFile{};
|
|
1523
|
+
try file.keys.append(work, record);
|
|
1524
|
+
|
|
1525
|
+
return .{ .ok = file };
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
fn bytext(_: void, left: []const u8, right: []const u8) bool {
|
|
1530
|
+
return .lt == std.mem.order(u8, left, right);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
fn copyinfo(alloc: Allocator, info: KeyInfo) Allocator.Error!KeyInfo {
|
|
1534
|
+
const grants = try alloc.alloc([]const u8, info.grants.len);
|
|
1535
|
+
for (info.grants, 0..) |name, at| {
|
|
1536
|
+
grants[at] = try alloc.dupe(u8, name);
|
|
1537
|
+
}
|
|
1538
|
+
return .{
|
|
1539
|
+
.key = try alloc.dupe(u8, info.key),
|
|
1540
|
+
.master = info.master,
|
|
1541
|
+
.write = info.write,
|
|
1542
|
+
.grants = grants,
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
fn sealkey(
|
|
1547
|
+
alloc: Allocator,
|
|
1548
|
+
io: std.Io,
|
|
1549
|
+
root: []const u8,
|
|
1550
|
+
id: []const u8,
|
|
1551
|
+
passphrase: []const u8,
|
|
1552
|
+
iters: u32,
|
|
1553
|
+
ring: []const u8,
|
|
1554
|
+
meta: []const u8,
|
|
1555
|
+
) Allocator.Error!Answer(KeyRecord) {
|
|
1556
|
+
const salt = switch (try random(alloc, io, SALTLEN)) {
|
|
1557
|
+
.err => |message| return .{ .err = message },
|
|
1558
|
+
.ok => |made| made,
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
const unwrapped = switch (try kek(alloc, passphrase, salt, iters)) {
|
|
1562
|
+
.err => |message| return .{ .err = message },
|
|
1563
|
+
.ok => |made| made,
|
|
1564
|
+
};
|
|
1565
|
+
|
|
1566
|
+
const ringlabel = try std.fmt.allocPrint(alloc, AAD_RING ++ "{s}", .{id});
|
|
1567
|
+
const sealedring = switch (try seal(alloc, io, unwrapped, ring, ringlabel)) {
|
|
1568
|
+
.err => |message| return .{ .err = message },
|
|
1569
|
+
.ok => |made| made,
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1572
|
+
const metalabel = try std.fmt.allocPrint(alloc, AAD_META ++ "{s}", .{id});
|
|
1573
|
+
const sealedmeta = switch (try seal(alloc, io, try mac(alloc, root, LABEL_META), meta, metalabel)) {
|
|
1574
|
+
.err => |message| return .{ .err = message },
|
|
1575
|
+
.ok => |made| made,
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
return .{ .ok = .{
|
|
1579
|
+
.id = id,
|
|
1580
|
+
.salt = salt,
|
|
1581
|
+
.iters = iters,
|
|
1582
|
+
.ring = sealedring,
|
|
1583
|
+
.meta = sealedmeta,
|
|
1584
|
+
} };
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/// The one key record a new or rotated vault starts with: a master
|
|
1588
|
+
/// holding the root, granted nothing because it needs nothing.
|
|
1589
|
+
fn masterkey(
|
|
1590
|
+
alloc: Allocator,
|
|
1591
|
+
io: std.Io,
|
|
1592
|
+
root: []const u8,
|
|
1593
|
+
id: []const u8,
|
|
1594
|
+
passphrase: []const u8,
|
|
1595
|
+
iters: u32,
|
|
1596
|
+
) Allocator.Error!Answer(KeyRecord) {
|
|
1597
|
+
var ring: std.ArrayList(u8) = .empty;
|
|
1598
|
+
try ring.print(alloc, "{{\"v\":{d},\"write\":true,\"root\":", .{FORMAT});
|
|
1599
|
+
try jsonstring(&ring, alloc, try b64(alloc, root));
|
|
1600
|
+
try ring.append(alloc, '}');
|
|
1601
|
+
|
|
1602
|
+
const meta = try std.fmt.allocPrint(
|
|
1603
|
+
alloc,
|
|
1604
|
+
"{{\"v\":{d},\"master\":true,\"write\":true,\"grants\":[]}}",
|
|
1605
|
+
.{FORMAT},
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
return sealkey(alloc, io, root, id, passphrase, iters, ring.items, meta);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// --- opening and creating ---------------------------------------------
|
|
1612
|
+
|
|
1613
|
+
/// THE LOCK EVERY HANDLE ON ONE FILE SHARES.
|
|
1614
|
+
///
|
|
1615
|
+
/// Each `Vault` is its own object, so two handles on one path did not
|
|
1616
|
+
/// coordinate: both could finish `load` before either saved, and the
|
|
1617
|
+
/// second rename then discarded the first one's change while reporting
|
|
1618
|
+
/// success.
|
|
1619
|
+
///
|
|
1620
|
+
/// Keyed by the path AS THE HANDLE HOLDS IT. Every other port keys by the
|
|
1621
|
+
/// absolute path so that two spellings of one file still meet; realpath
|
|
1622
|
+
/// at this level would want an allocator and a syscall per write, and
|
|
1623
|
+
/// this port says what it does rather than implying the stronger thing.
|
|
1624
|
+
///
|
|
1625
|
+
/// A guarantee WITHIN one process, which is what DOCS.md promises and
|
|
1626
|
+
/// what the go port arranges the same way. Two processes still race, and
|
|
1627
|
+
/// the format's answer to that is the exclusive create and the atomic
|
|
1628
|
+
/// rename: a reader sees one whole vault or the other, never half of one.
|
|
1629
|
+
var LOCKSMUTEX: std.Io.Mutex = .init;
|
|
1630
|
+
var LOCKS: std.ArrayList(Held) = .empty;
|
|
1631
|
+
|
|
1632
|
+
const Held = struct {
|
|
1633
|
+
file: []const u8,
|
|
1634
|
+
lock: *std.Io.Mutex,
|
|
1635
|
+
};
|
|
1636
|
+
|
|
1637
|
+
/// The lock for one path, made once and never dropped: a mutex is two
|
|
1638
|
+
/// words, a process opens few vaults, and freeing one a thread might
|
|
1639
|
+
/// still be waiting on is the bug this exists to avoid.
|
|
1640
|
+
///
|
|
1641
|
+
/// FROM THE PAGE ALLOCATOR, NOT THE CALLER'S. This table outlives every
|
|
1642
|
+
/// handle in it, and the callers are two different threads with two
|
|
1643
|
+
/// different arenas; taking the memory from whichever got here first
|
|
1644
|
+
/// would leave the other holding a pointer into a freed arena. The page
|
|
1645
|
+
/// allocator is process-lifetime and thread-safe, which is exactly what
|
|
1646
|
+
/// process-global state wants.
|
|
1647
|
+
fn lockfor(io: std.Io, file: []const u8) Allocator.Error!*std.Io.Mutex {
|
|
1648
|
+
const alloc = std.heap.page_allocator;
|
|
1649
|
+
|
|
1650
|
+
LOCKSMUTEX.lockUncancelable(io);
|
|
1651
|
+
defer LOCKSMUTEX.unlock(io);
|
|
1652
|
+
|
|
1653
|
+
|
|
1654
|
+
for (LOCKS.items) |held| {
|
|
1655
|
+
if (std.mem.eql(u8, held.file, file)) {
|
|
1656
|
+
return held.lock;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
const owned = try alloc.dupe(u8, file);
|
|
1661
|
+
const made = try alloc.create(std.Io.Mutex);
|
|
1662
|
+
made.* = .init;
|
|
1663
|
+
try LOCKS.append(alloc, .{ .file = owned, .lock = made });
|
|
1664
|
+
|
|
1665
|
+
return made;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/// The vaults this module has built.
|
|
1669
|
+
///
|
|
1670
|
+
/// voxgig/plugin's values are numbers and strings, not pointers, so a
|
|
1671
|
+
/// definition exports the INDEX of what it made and the reader looks it
|
|
1672
|
+
/// up - exactly as `providerplugin` exports an index into
|
|
1673
|
+
/// `provider.Building.made`. A closed vault nulls its slot, so `vaultof`
|
|
1674
|
+
/// on a torn-down chain refuses rather than following a dangling pointer,
|
|
1675
|
+
/// and the next vault reuses the slot rather than growing the list.
|
|
1676
|
+
///
|
|
1677
|
+
/// MODULE-GLOBAL, for the reason `provider.building` is: a lifecycle
|
|
1678
|
+
/// callback is a bare function pointer with no context. One thread, as
|
|
1679
|
+
/// that comment also says.
|
|
1680
|
+
var VAULTS: std.ArrayList(?*Vault) = .empty;
|
|
1681
|
+
|
|
1682
|
+
fn hold(alloc: Allocator, vault: *Vault) Allocator.Error!usize {
|
|
1683
|
+
for (VAULTS.items, 0..) |slot, at| {
|
|
1684
|
+
if (null == slot) {
|
|
1685
|
+
VAULTS.items[at] = vault;
|
|
1686
|
+
return at;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
try VAULTS.append(alloc, vault);
|
|
1690
|
+
return VAULTS.items.len - 1;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/// Opens a vault file as one key.
|
|
1694
|
+
///
|
|
1695
|
+
/// The handle is lazy. Nothing is read, and no passphrase is stretched,
|
|
1696
|
+
/// until a method needs the file - so a chain of ten providers costs ten
|
|
1697
|
+
/// objects rather than ten PBKDF2 runs.
|
|
1698
|
+
pub fn openvault(alloc: Allocator, io: std.Io, options: Options) Allocator.Error!Answer(*Vault) {
|
|
1699
|
+
if (0 == options.file.len) {
|
|
1700
|
+
return .{ .err = try fail(alloc, "a vault needs a file", .{}) };
|
|
1701
|
+
}
|
|
1702
|
+
if (0 == options.passphrase.len) {
|
|
1703
|
+
return .{ .err = try fail(alloc, "a vault needs a passphrase", .{}) };
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
const key = if (0 != options.key.len) options.key else MASTERKEY;
|
|
1707
|
+
|
|
1708
|
+
switch (try checkid(alloc, key, "a vault needs a key id")) {
|
|
1709
|
+
.err => |message| return .{ .err = message },
|
|
1710
|
+
.ok => {},
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
const vault = try alloc.create(Vault);
|
|
1714
|
+
errdefer alloc.destroy(vault);
|
|
1715
|
+
|
|
1716
|
+
vault.* = .{
|
|
1717
|
+
.alloc = alloc,
|
|
1718
|
+
.io = io,
|
|
1719
|
+
.file = try alloc.dupe(u8, options.file),
|
|
1720
|
+
.key = try alloc.dupe(u8, key),
|
|
1721
|
+
.passphrase = try alloc.dupe(u8, options.passphrase),
|
|
1722
|
+
.iterations = if (0 < options.iterations) options.iterations else ITERATIONS,
|
|
1723
|
+
.create = options.create,
|
|
1724
|
+
.slot = 0,
|
|
1725
|
+
.state = std.heap.ArenaAllocator.init(alloc),
|
|
1726
|
+
};
|
|
1727
|
+
vault.slot = try hold(alloc, vault);
|
|
1728
|
+
|
|
1729
|
+
return .{ .ok = vault };
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/// Makes a vault file and returns a handle on its master key.
|
|
1733
|
+
///
|
|
1734
|
+
/// Refuses a file that is already there: a vault is created once, and
|
|
1735
|
+
/// overwriting one discards every secret in it along with every key that
|
|
1736
|
+
/// could read them.
|
|
1737
|
+
pub fn createvault(alloc: Allocator, io: std.Io, options: Options) Allocator.Error!Answer(*Vault) {
|
|
1738
|
+
const vault = switch (try openvault(alloc, io, options)) {
|
|
1739
|
+
.err => |message| return .{ .err = message },
|
|
1740
|
+
.ok => |made| made,
|
|
1741
|
+
};
|
|
1742
|
+
|
|
1743
|
+
var work = std.heap.ArenaAllocator.init(alloc);
|
|
1744
|
+
defer work.deinit();
|
|
1745
|
+
const scratch = work.allocator();
|
|
1746
|
+
|
|
1747
|
+
// No stat first: the check and the write would be two steps, and
|
|
1748
|
+
// `putnew` refuses an existing file in ONE, which is what makes two
|
|
1749
|
+
// processes racing to create a vault leave one vault.
|
|
1750
|
+
const fresh = switch (try vault.newvault(scratch, alloc, vault.key, vault.passphrase, vault.iterations)) {
|
|
1751
|
+
.err => |message| {
|
|
1752
|
+
vault.deinit();
|
|
1753
|
+
return .{ .err = message };
|
|
1754
|
+
},
|
|
1755
|
+
.ok => |made| made,
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
switch (try vault.putnew(scratch, alloc, &fresh)) {
|
|
1759
|
+
.err => |message| {
|
|
1760
|
+
vault.deinit();
|
|
1761
|
+
return .{ .err = message };
|
|
1762
|
+
},
|
|
1763
|
+
.ok => {},
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
return .{ .ok = vault };
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
// --- the provider -----------------------------------------------------
|
|
1770
|
+
|
|
1771
|
+
/// Reads a vault as one store in a chain.
|
|
1772
|
+
///
|
|
1773
|
+
/// The provider is the READ half and nothing more: a chain resolves
|
|
1774
|
+
/// secrets, and writing one is a deliberate act with an API of its own.
|
|
1775
|
+
/// That API is the same handle, reached with `vaultof` off a chain or
|
|
1776
|
+
/// built directly with `openvault`.
|
|
1777
|
+
pub const MiniVaultProvider = struct {
|
|
1778
|
+
vault: *Vault,
|
|
1779
|
+
|
|
1780
|
+
pub fn lookup(self: *MiniVaultProvider, alloc: Allocator, name: []const u8) Allocator.Error!Found {
|
|
1781
|
+
return self.vault.get(alloc, name);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
pub fn describe(self: *MiniVaultProvider, alloc: Allocator) Allocator.Error![]const u8 {
|
|
1785
|
+
return std.fmt.allocPrint(alloc, "minivault:{s}", .{self.vault.vaultfile()});
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
pub fn deinit(self: *MiniVaultProvider, _: Allocator) void {
|
|
1789
|
+
self.vault.deinit();
|
|
1790
|
+
}
|
|
1791
|
+
};
|
|
1792
|
+
|
|
1793
|
+
/// The export key the vault API is published under, beside the `provider`
|
|
1794
|
+
/// key every kind publishes.
|
|
1795
|
+
pub const VAULT_EXPORT = "vault";
|
|
1796
|
+
|
|
1797
|
+
/// The `minivault` provider kind, as a voxgig/plugin definition.
|
|
1798
|
+
///
|
|
1799
|
+
/// Written out rather than built by `sekreto.providerplugin`, because
|
|
1800
|
+
/// this definition publishes TWO exports: `provider`, the read half every
|
|
1801
|
+
/// kind publishes, and `vault`, the programmatic API. voxgig/plugin's
|
|
1802
|
+
/// exports are how a definition offers an application more than the
|
|
1803
|
+
/// host's own vocabulary, and a store that can only be read is half a
|
|
1804
|
+
/// vault.
|
|
1805
|
+
///
|
|
1806
|
+
/// The `sekreto_error` wrapping is what `providerplugin` would have done:
|
|
1807
|
+
/// plugin wraps a code-less error raised in `define` as
|
|
1808
|
+
/// `plugin_define_failed`, and keeps one that already carries a code, so
|
|
1809
|
+
/// a refusal of this provider's own configuration travels under
|
|
1810
|
+
/// `sekreto_error` and comes back out of the host as itself.
|
|
1811
|
+
pub const minivault: sekreto.Definition = .{ .name = "minivault", .define = define };
|
|
1812
|
+
|
|
1813
|
+
fn define(inst: *sekreto.provider.Inst) pt.Err!void {
|
|
1814
|
+
const b = sekreto.provider.building orelse {
|
|
1815
|
+
return pt.fail("plugin_bad_state", "sekreto: a provider was built outside Sekreto.init", null);
|
|
1816
|
+
};
|
|
1817
|
+
|
|
1818
|
+
const spec = sekreto.provider.specof(inst.options);
|
|
1819
|
+
|
|
1820
|
+
// Configuration is refused HERE, so a mistyped chain fails at
|
|
1821
|
+
// construction. Reaching the file is not configuration: the handle is
|
|
1822
|
+
// lazy, and nothing is read or stretched until a lookup.
|
|
1823
|
+
const vault = switch (openvault(b.alloc, b.config.io, .{
|
|
1824
|
+
.file = spec.file,
|
|
1825
|
+
.key = spec.vaultkey,
|
|
1826
|
+
.passphrase = spec.passphrase,
|
|
1827
|
+
.iterations = if (0 < spec.iterations) @intCast(spec.iterations) else 0,
|
|
1828
|
+
.create = spec.create,
|
|
1829
|
+
}) catch {
|
|
1830
|
+
b.oom = true;
|
|
1831
|
+
return pt.fail(sekreto.ERROR_CODE, "sekreto: out of memory", null);
|
|
1832
|
+
}) {
|
|
1833
|
+
.err => |message| {
|
|
1834
|
+
defer b.alloc.free(message);
|
|
1835
|
+
const details = pv.vmap();
|
|
1836
|
+
pv.set(details, "ref", pv.vstr(inst.ref));
|
|
1837
|
+
pv.set(details, "cause", pv.vstr(pv.dupe(message)));
|
|
1838
|
+
return pt.fail(sekreto.ERROR_CODE, message, details);
|
|
1839
|
+
},
|
|
1840
|
+
.ok => |made| made,
|
|
1841
|
+
};
|
|
1842
|
+
|
|
1843
|
+
const made = sekreto.provide(b.alloc, MiniVaultProvider, .{ .vault = vault }) catch {
|
|
1844
|
+
vault.deinit();
|
|
1845
|
+
b.oom = true;
|
|
1846
|
+
return pt.fail(sekreto.ERROR_CODE, "sekreto: out of memory", null);
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
b.made.append(b.alloc, made) catch {
|
|
1850
|
+
made.deinit(b.alloc);
|
|
1851
|
+
b.oom = true;
|
|
1852
|
+
return pt.fail(sekreto.ERROR_CODE, "sekreto: out of memory", null);
|
|
1853
|
+
};
|
|
1854
|
+
|
|
1855
|
+
plugin.host.exportvalue(inst, sekreto.PROVIDER_EXPORT, pv.vnum(@floatFromInt(b.made.items.len - 1)));
|
|
1856
|
+
plugin.host.exportvalue(inst, VAULT_EXPORT, pv.vnum(@floatFromInt(vault.slot)));
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
/// The vault behind a store in a chain, as its programmatic API.
|
|
1860
|
+
///
|
|
1861
|
+
/// `secrets.host` is the voxgig/plugin host the chain is made of, and a
|
|
1862
|
+
/// definition's exports are readable off it by ref. This is the one call
|
|
1863
|
+
/// that turns a store into an API, and it lives here rather than on
|
|
1864
|
+
/// `Sekreto` because the core knows no plugin.
|
|
1865
|
+
///
|
|
1866
|
+
/// With no store named, the unqualified alias answers: one vault in the
|
|
1867
|
+
/// chain resolves whatever it is called, and two raise rather than
|
|
1868
|
+
/// picking one.
|
|
1869
|
+
pub fn vaultof(
|
|
1870
|
+
alloc: Allocator,
|
|
1871
|
+
secrets: *sekreto.Sekreto,
|
|
1872
|
+
store: []const u8,
|
|
1873
|
+
) Allocator.Error!Answer(*Vault) {
|
|
1874
|
+
if (0 == store.len) {
|
|
1875
|
+
const found = plugin.host.exports(secrets.host, "minivault/" ++ VAULT_EXPORT) catch {
|
|
1876
|
+
_ = pt.take();
|
|
1877
|
+
return .{ .err = try fail(alloc, "no minivault store in this chain", .{}) };
|
|
1878
|
+
};
|
|
1879
|
+
return slotof(alloc, found, "no minivault store in this chain");
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// A NAMED STORE MUST EXIST, and the alias must not stand in for it.
|
|
1883
|
+
// `host.exports` falls back to the alias when the exact ref misses, so
|
|
1884
|
+
// asking for `minivault` in a chain whose only vault is named `app`
|
|
1885
|
+
// used to hand back the `app` vault - and then write to it. Naming a
|
|
1886
|
+
// store that is not there raises, which is the rule the whole library
|
|
1887
|
+
// follows: `tryget` already means "may not have it", so it cannot also
|
|
1888
|
+
// mean "may not exist".
|
|
1889
|
+
const ref = if (std.mem.eql(u8, "minivault", store))
|
|
1890
|
+
"minivault"
|
|
1891
|
+
else
|
|
1892
|
+
try std.fmt.allocPrint(alloc, "minivault${s}", .{store});
|
|
1893
|
+
|
|
1894
|
+
const missing = try fail(alloc, "no minivault store named {s} in this chain", .{store});
|
|
1895
|
+
|
|
1896
|
+
const live = plugin.host.instance(secrets.host, ref) catch {
|
|
1897
|
+
_ = pt.take();
|
|
1898
|
+
return .{ .err = missing };
|
|
1899
|
+
};
|
|
1900
|
+
if (null == live) {
|
|
1901
|
+
return .{ .err = missing };
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
const key = try std.fmt.allocPrint(alloc, "{s}/{s}", .{ ref, VAULT_EXPORT });
|
|
1905
|
+
const found = plugin.host.exports(secrets.host, key) catch {
|
|
1906
|
+
_ = pt.take();
|
|
1907
|
+
return .{ .err = missing };
|
|
1908
|
+
};
|
|
1909
|
+
|
|
1910
|
+
return slotof(alloc, found, missing);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
fn slotof(alloc: Allocator, found: ?*pv.Value, missing: []const u8) Allocator.Error!Answer(*Vault) {
|
|
1914
|
+
if (!pv.isNum(found)) {
|
|
1915
|
+
return .{ .err = try alloc.dupe(u8, missing) };
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
const at: usize = @intFromFloat(pv.asNum(found));
|
|
1919
|
+
if (VAULTS.items.len <= at) {
|
|
1920
|
+
return .{ .err = try alloc.dupe(u8, missing) };
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
const vault = VAULTS.items[at] orelse {
|
|
1924
|
+
// The chain was torn down; the slot is free and may already hold
|
|
1925
|
+
// somebody else's vault. Refusing is the only safe answer.
|
|
1926
|
+
return .{ .err = try alloc.dupe(u8, missing) };
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
return .{ .ok = vault };
|
|
1930
|
+
}
|