From 30c45f2952ebe1618b3d6698d1d18e1f9952a72e Mon Sep 17 00:00:00 2001 From: James Sadler Date: Fri, 6 Nov 2026 22:01:30 +1100 Subject: [PATCH] refactor(stack-kms): make the vendor in backend errors a typed value KeyVendor::name was the one spelling of each vendor, but each vendor client wrote its name again as a string constant. Error::Backend and Error::ZeroKmsOnly took any &'static str, so the Go guest could report an invented vendor, "none". Error::Backend now holds a KeyBackend in the field `backend`, and Error::ZeroKmsOnly holds a KeyVendor. You cannot give these errors a vendor that does not exist. The payload field `vendor` and the message keep the same text, because both come from KeyBackend::name. KeyVendor::from_name reads a name back. The guest config uses it. The guest refused a call to its empty vendor with a backend error that named "none". That call is a routing bug and is not reachable. It is now Error::Unexpected, which has no vendor to name. The Go client kept the names as string literals next to the bytes. It now has one table from byte to name. A new test pins each name and byte to the guest: the guest refuses a name it does not know and a record of another vendor, so a drift on either side fails the test. --- .changeset/kms-vendor-name-type.md | 5 ++ languages/golang/encrypt/export_test.go | 13 ++++ languages/golang/encrypt/guest/src/config.rs | 5 +- languages/golang/encrypt/guest/src/status.rs | 4 +- languages/golang/encrypt/guest/src/vendor.rs | 9 +-- languages/golang/encrypt/guest/src/zerokms.rs | 4 +- languages/golang/encrypt/keyvendor.go | 67 +++++++++++++------ languages/golang/encrypt/keyvendor_test.go | 34 ++++++++++ .../typescript/packages/auth/src/keyset.rs | 6 +- packages/stack-encrypt/src/cipher.rs | 2 +- packages/stack-encrypt/src/codes.rs | 8 +-- packages/stack-kms-aws/src/client.rs | 8 +-- packages/stack-kms-aws/tests/fixtures.rs | 4 +- packages/stack-kms-azure/src/client.rs | 8 +-- packages/stack-kms-azure/tests/contract.rs | 4 +- packages/stack-kms-azure/tests/fixtures.rs | 4 +- packages/stack-kms-azure/tests/live.rs | 4 +- packages/stack-kms-gcp/src/client.rs | 8 +-- packages/stack-kms-gcp/tests/contract.rs | 4 +- packages/stack-kms-gcp/tests/fixtures.rs | 4 +- packages/stack-kms-gcp/tests/live.rs | 4 +- packages/stack-kms-vault/src/client.rs | 8 +-- packages/stack-kms-vault/tests/contract.rs | 4 +- packages/stack-kms-vault/tests/fixtures.rs | 4 +- packages/stack-kms-vendor/src/batch.rs | 4 +- packages/stack-kms-vendor/src/contract.rs | 2 +- packages/stack-kms-vendor/src/fake.rs | 4 +- packages/stack-kms-vendor/src/source.rs | 10 +-- packages/stack-kms-vendor/src/static_key.rs | 6 +- packages/stack-kms-vendor/src/vendor.rs | 10 +-- packages/stack-kms/src/backend.rs | 26 ++++++- packages/stack-kms/src/errors.rs | 40 +++++------ packages/stack-kms/src/payload.rs | 20 +++--- 33 files changed, 221 insertions(+), 126 deletions(-) create mode 100644 .changeset/kms-vendor-name-type.md diff --git a/.changeset/kms-vendor-name-type.md b/.changeset/kms-vendor-name-type.md new file mode 100644 index 000000000..50e323492 --- /dev/null +++ b/.changeset/kms-vendor-name-type.md @@ -0,0 +1,5 @@ +--- +'@cipherstash/auth': patch +--- + +Internal change to the key vendor clients that `keysetCreate` and `keysetRewrap` use. Each client now names its vendor with a typed value, not a string. The `vendor` field of a `VENDOR_ERROR` failure keeps the same values (`aws-kms`, `gcp-kms`, `azure-key-vault`, `vault-transit`, `static`). diff --git a/languages/golang/encrypt/export_test.go b/languages/golang/encrypt/export_test.go index 31df58017..0c93f5d91 100644 --- a/languages/golang/encrypt/export_test.go +++ b/languages/golang/encrypt/export_test.go @@ -136,6 +136,19 @@ func deterministicClient(ctx context.Context, wasm []byte, seed [32]byte) (*Clie return c, nil } +// KeyVendorWire is the KeyVendor byte and the key_vendor name the client +// sends to the guest for v. +func KeyVendorWire(v KeyVendor) (byte, string, error) { + spec, err := v.keyVendor() + if err != nil { + return 0, "", err + } + return byte(spec.vendor), spec.vendor.String(), nil +} + +// KeyVendorNameCount is the number of vendors in the Go table of names. +func KeyVendorNameCount() int { return len(keyVendorNames) } + // NewDeterministicVendorClient is NewDeterministicClient with a key vendor // backend beside the seeded ZeroKMS stand-in: the deterministic build's // object config, {"seed", key_vendor, keyset_records, ...}. Vendor requests diff --git a/languages/golang/encrypt/guest/src/config.rs b/languages/golang/encrypt/guest/src/config.rs index db43a3f83..28b711cef 100644 --- a/languages/golang/encrypt/guest/src/config.rs +++ b/languages/golang/encrypt/guest/src/config.rs @@ -307,10 +307,7 @@ impl VendorWire<'_> { let missing = |key: &str| format!("missing field `{key}`"); let invalid = |key: &str| format!("{key}: a value its type does not accept"); let name = self.key_vendor.ok_or_else(|| missing("key_vendor"))?; - let vendor = KeyVendor::ALL - .into_iter() - .find(|v| v.name() == name) - .ok_or_else(|| invalid("key_vendor"))?; + let vendor = KeyVendor::from_name(name).ok_or_else(|| invalid("key_vendor"))?; let records = self .keyset_records .ok_or_else(|| missing("keyset_records"))?; diff --git a/languages/golang/encrypt/guest/src/status.rs b/languages/golang/encrypt/guest/src/status.rs index a3210bff4..5cdff0480 100644 --- a/languages/golang/encrypt/guest/src/status.rs +++ b/languages/golang/encrypt/guest/src/status.rs @@ -383,7 +383,7 @@ mod tests { use stack_kms::BackendErrorKind as Kind; let status = |kind| { status_for_error(&stack_encrypt::Error::Kms(stack_kms::Error::Backend { - vendor: "test-vendor", + backend: stack_kms::KeyVendor::Aws.into(), kind, source: Box::new(std::io::Error::other("detail")), })) @@ -395,7 +395,7 @@ mod tests { assert_eq!(status(Kind::InvalidCiphertext), STATUS_AUTH); assert_eq!( status_for_error(&stack_encrypt::Error::Kms(stack_kms::Error::ZeroKmsOnly { - vendor: "test-vendor", + vendor: stack_kms::KeyVendor::Aws, feature: stack_kms::ZeroKmsFeature::DecryptionPolicy, })), STATUS_ENCODING diff --git a/languages/golang/encrypt/guest/src/vendor.rs b/languages/golang/encrypt/guest/src/vendor.rs index e057d7184..0de144584 100644 --- a/languages/golang/encrypt/guest/src/vendor.rs +++ b/languages/golang/encrypt/guest/src/vendor.rs @@ -425,13 +425,10 @@ impl std::fmt::Debug for GuestVendor { } /// Every method of [`GuestVendor::None`]: it holds no keyset, so a call is -/// a routing bug, refused rather than answered. +/// a routing bug, refused rather than answered. It is not a backend +/// failure, because there is no backend to name. fn no_vendor() -> Error { - Error::Backend { - vendor: "none", - kind: BackendErrorKind::NotFound, - source: "no key vendor backend is configured".into(), - } + Error::Unexpected("no key vendor backend is configured".into()) } /// Dispatch one `KeyVendorBackend` method to the configured client. diff --git a/languages/golang/encrypt/guest/src/zerokms.rs b/languages/golang/encrypt/guest/src/zerokms.rs index 1487bdad6..29dc187fd 100644 --- a/languages/golang/encrypt/guest/src/zerokms.rs +++ b/languages/golang/encrypt/guest/src/zerokms.rs @@ -45,7 +45,7 @@ impl std::error::Error for NoZeroKms {} /// The refusal of every request that reaches [`MaybeZeroKms::Absent`]. pub fn no_zerokms() -> Error { Error::Backend { - vendor: KeyBackend::ZeroKms.name(), + backend: KeyBackend::ZeroKms, kind: BackendErrorKind::NotFound, source: Box::new(NoZeroKms), } @@ -175,7 +175,7 @@ mod tests { assert_eq!(absent.key_mode(id), None); // Another backend error is not this one. assert!(!is_no_zerokms(&Error::Backend { - vendor: KeyBackend::ZeroKms.name(), + backend: KeyBackend::ZeroKms, kind: BackendErrorKind::NotFound, source: "other".into(), })); diff --git a/languages/golang/encrypt/keyvendor.go b/languages/golang/encrypt/keyvendor.go index 9e62ce642..571197ce5 100644 --- a/languages/golang/encrypt/keyvendor.go +++ b/languages/golang/encrypt/keyvendor.go @@ -38,10 +38,9 @@ type KeyVendor interface { // vendorSpec is a KeyVendor as the client uses it: the config keys the // guest reads, and where the per-request credential comes from. type vendorSpec struct { - // name is the guest's key_vendor value. - name string - // id is the KeyVendor byte the guest names in a credential request. - id uint32 + // vendor is the backend. Its byte is what the guest names in a + // credential request; its name is the guest's key_vendor value. + vendor keyVendorID // fields are the backend's settings in the guest config. fields vcvalue.Object // staticKey is StaticKey's key, consumed by NewClient. @@ -50,16 +49,41 @@ type vendorSpec struct { token AccessTokenSource } -// The KeyVendor bytes of the v2 leaf header and the keyset record. They -// cross in the guest's credential requests (step.go). +// keyVendorID is a KeyVendor byte of the v2 leaf header and the keyset +// record. It crosses in the guest's credential requests (step.go). +type keyVendorID uint32 + +// The KeyVendor bytes. They are a storage commitment, as in the Rust crate +// stack-kms (KeyVendor::byte). const ( - vendorAWS = 1 - vendorGCP = 2 - vendorAzure = 3 - vendorVault = 4 - vendorStatic = 5 + vendorAWS keyVendorID = 1 + vendorGCP keyVendorID = 2 + vendorAzure keyVendorID = 3 + vendorVault keyVendorID = 4 + vendorStatic keyVendorID = 5 ) +// keyVendorNames is the one table of the vendor names on the Go side: the +// guest's key_vendor value of each KeyVendor byte. The guest reads the name +// with KeyVendor::from_name of the Rust crate stack-kms, so these strings +// are a wire format, not a choice. TestKeyVendorNamesMatchTheGuest pins +// each one to the guest's name for the same byte. +var keyVendorNames = map[keyVendorID]string{ + vendorAWS: "aws-kms", + vendorGCP: "gcp-kms", + vendorAzure: "azure-key-vault", + vendorVault: "vault-transit", + vendorStatic: "static", +} + +// String is the vendor's name, as the guest spells it. +func (id keyVendorID) String() string { + if name, ok := keyVendorNames[id]; ok { + return name + } + return fmt.Sprintf("key vendor %d", uint32(id)) +} + // AWSKMS is AWS KMS as the key vendor backend. The key in each keyset // record is a KMS key ARN. Both key modes. type AWSKMS struct { @@ -85,7 +109,7 @@ func (v AWSKMS) keyVendor() (*vendorSpec, error) { if v.Endpoint != "" { fields = append(fields, vcvalue.Field{Key: "aws_endpoint", Value: v.Endpoint}) } - return &vendorSpec{name: "aws-kms", id: vendorAWS, fields: fields, aws: v.Credentials}, nil + return &vendorSpec{vendor: vendorAWS, fields: fields, aws: v.Credentials}, nil } // GoogleCloudKMS is Google Cloud KMS as the key vendor backend. The key in @@ -109,7 +133,7 @@ func (v GoogleCloudKMS) keyVendor() (*vendorSpec, error) { if v.Endpoint != "" { fields = append(fields, vcvalue.Field{Key: "gcp_endpoint", Value: v.Endpoint}) } - return &vendorSpec{name: "gcp-kms", id: vendorGCP, fields: fields, token: v.Token}, nil + return &vendorSpec{vendor: vendorGCP, fields: fields, token: v.Token}, nil } // AzureKeyVault is Azure Key Vault or Managed HSM as the key vendor @@ -128,7 +152,7 @@ func (v AzureKeyVault) keyVendor() (*vendorSpec, error) { if v.Token == nil { return nil, fmt.Errorf("%w: AzureKeyVault needs a Token source", ErrEncoding) } - return &vendorSpec{name: "azure-key-vault", id: vendorAzure, token: v.Token}, nil + return &vendorSpec{vendor: vendorAzure, token: v.Token}, nil } // VaultTransit is HashiCorp Vault's Transit secrets engine as the key @@ -162,7 +186,7 @@ func (v VaultTransit) keyVendor() (*vendorSpec, error) { if v.Mount != "" { fields = append(fields, vcvalue.Field{Key: "vault_mount", Value: v.Mount}) } - return &vendorSpec{name: "vault-transit", id: vendorVault, fields: fields, token: v.Token}, nil + return &vendorSpec{vendor: vendorVault, fields: fields, token: v.Token}, nil } // StaticKey is a 32-byte key the application holds, as the key vendor @@ -200,8 +224,7 @@ func (v StaticKey) keyVendor() (*vendorSpec, error) { return nil, fmt.Errorf("%w: StaticKey's Key is %d bytes, not 32", ErrEncoding, len(v.Key)) } return &vendorSpec{ - name: "static", - id: vendorStatic, + vendor: vendorStatic, fields: vcvalue.Object{{Key: "static_key_ref", Value: v.KeyRef}}, staticKey: v.Key, }, nil @@ -330,7 +353,7 @@ func keyVendorConfig(v KeyVendor, records []string) (*vendorSpec, error) { // The result holds a StaticKey's key: the caller wipes what it encodes. func (s *vendorSpec) configFields(records []string) vcvalue.Object { fields := vcvalue.Object{ - {Key: "key_vendor", Value: s.name}, + {Key: "key_vendor", Value: s.vendor.String()}, {Key: "keyset_records", Value: append([]string(nil), records...)}, } fields = append(fields, s.fields...) @@ -510,8 +533,8 @@ func StaticAccessToken(token string) AccessTokenSource { // asked for, and renders it as the guest reads it. The result holds a // secret; the caller wipes it. func (s *vendorSpec) vendorCredential(ctx context.Context, vendor uint32, scope string) ([]byte, error) { - if vendor != s.id { - return nil, fmt.Errorf("encrypt: the guest asked for a credential of key vendor %d, but the client's is %s", vendor, s.name) + if keyVendorID(vendor) != s.vendor { + return nil, fmt.Errorf("encrypt: the guest asked for a credential of key vendor %d, but the client's is %s", vendor, s.vendor) } var fields vcvalue.Object var expires time.Time @@ -538,12 +561,12 @@ func (s *vendorSpec) vendorCredential(ctx context.Context, vendor uint32, scope return nil, err } if t.Token == "" { - return nil, fmt.Errorf("%w: the %s token source returned an empty token", ErrNoKeyVendorCredentials, s.name) + return nil, fmt.Errorf("%w: the %s token source returned an empty token", ErrNoKeyVendorCredentials, s.vendor) } fields = vcvalue.Object{{Key: "token", Value: t.Token}} expires = t.Expires default: - return nil, fmt.Errorf("encrypt: the %s key backend takes no credentials", s.name) + return nil, fmt.Errorf("encrypt: the %s key backend takes no credentials", s.vendor) } if !expires.IsZero() { fields = append(fields, vcvalue.Field{Key: "expires_at", Value: expires.Unix()}) diff --git a/languages/golang/encrypt/keyvendor_test.go b/languages/golang/encrypt/keyvendor_test.go index 457a2cf40..90e1994d3 100644 --- a/languages/golang/encrypt/keyvendor_test.go +++ b/languages/golang/encrypt/keyvendor_test.go @@ -198,6 +198,40 @@ func (refusing) RoundTrip(*http.Request) (*http.Response, error) { return nil, errors.New("this test makes no HTTP request") } +// TestKeyVendorNamesMatchTheGuest pins the Go table of vendor names to the +// Rust names (KeyVendor::name in stack-kms). For each backend, the guest +// must accept the name the client sends as key_vendor, and a keyset record +// with the byte the client uses. The guest refuses a name it does not know, +// and a record of another vendor, so a drift on either side fails here. +func TestKeyVendorNamesMatchTheGuest(t *testing.T) { + token := encrypt.StaticAccessToken("t") + vendors := []encrypt.KeyVendor{ + encrypt.AWSKMS{Region: "us-east-1", Credentials: &countingCredentials{}}, + encrypt.GoogleCloudKMS{Token: token}, + encrypt.AzureKeyVault{Token: token}, + encrypt.VaultTransit{Address: "https://vault.example.com", Token: token}, + encrypt.StaticKey{KeyRef: "k", Key: bytes.Repeat([]byte{1}, 32)}, + } + seen := map[byte]string{} + for _, v := range vendors { + id, name, err := encrypt.KeyVendorWire(v) + if err != nil { + t.Fatal(err) + } + if other, ok := seen[id]; ok { + t.Fatalf("%s and %s have the same byte %d", other, name, id) + } + seen[id] = name + t.Run(name, func(t *testing.T) { + record := keysetRecord(randomID(t), id, modeDerived, "pin", "k", []byte{1}) + _ = vendorClient(t, refusing{}, v, record) + }) + } + if len(seen) != encrypt.KeyVendorNameCount() { + t.Errorf("the test covers %d vendors, the table has %d", len(seen), encrypt.KeyVendorNameCount()) + } +} + // fakeKMS answers the AWS KMS JSON API for the three operations the client // uses. A "ciphertext blob" is the key and the SHA-256 of the encryption // context's cipherstash:aad, so a decrypt under another context fails as diff --git a/languages/typescript/packages/auth/src/keyset.rs b/languages/typescript/packages/auth/src/keyset.rs index 156f41db1..3a19d426f 100644 --- a/languages/typescript/packages/auth/src/keyset.rs +++ b/languages/typescript/packages/auth/src/keyset.rs @@ -169,8 +169,8 @@ impl KeysetError { if let Some(help) = error.help() { let _ = fields.insert("help".into(), help.to_string().into()); } - if let RecordError::Kms(stack_kms::Error::Backend { vendor, kind, .. }) = error { - let _ = fields.insert("vendor".into(), (*vendor).into()); + if let RecordError::Kms(stack_kms::Error::Backend { backend, kind, .. }) = error { + let _ = fields.insert("vendor".into(), backend.name().into()); let _ = fields.insert("kind".into(), kind.as_str().into()); } } @@ -832,7 +832,7 @@ mod tests { to: "b".into(), }), KeysetError::Record(RecordError::Kms(stack_kms::Error::Backend { - vendor: "static", + backend: KeyVendor::Static.into(), kind: stack_kms::BackendErrorKind::NotFound, source: "none".into(), })), diff --git a/packages/stack-encrypt/src/cipher.rs b/packages/stack-encrypt/src/cipher.rs index 4ab70362e..57ed158b3 100644 --- a/packages/stack-encrypt/src/cipher.rs +++ b/packages/stack-encrypt/src/cipher.rs @@ -2213,7 +2213,7 @@ mod tests { let vitur = |kind| ViturRequestError::new(kind, "stubbed", std::io::Error::other("detail")); let backend = |kind| { Error::Kms(stack_kms::Error::Backend { - vendor: "test-vendor", + backend: stack_kms::KeyVendor::Static.into(), kind, source: Box::new(std::io::Error::other("detail")), }) diff --git a/packages/stack-encrypt/src/codes.rs b/packages/stack-encrypt/src/codes.rs index 0b6e21386..27ff18bb2 100644 --- a/packages/stack-encrypt/src/codes.rs +++ b/packages/stack-encrypt/src/codes.rs @@ -387,7 +387,7 @@ mod tests { use crate::ErrorPayload; use serde_json::json; let failure = Error::Kms(crate::kms::Error::Backend { - vendor: "test-vendor", + backend: crate::kms::KeyVendor::Aws.into(), kind: crate::kms::BackendErrorKind::AccessDenied, source: Box::new(std::io::Error::other("vendor detail")), }); @@ -397,10 +397,10 @@ mod tests { ); assert_eq!( serde_json::Value::Object(failure.payload()), - json!({ "vendor": "test-vendor", "kind": "AccessDenied" }) + json!({ "vendor": "aws-kms", "kind": "AccessDenied" }) ); let refusal = Error::Kms(crate::kms::Error::ZeroKmsOnly { - vendor: "test-vendor", + vendor: crate::kms::KeyVendor::Vault, feature: crate::kms::ZeroKmsFeature::UnverifiedContext, }); assert_eq!( @@ -409,7 +409,7 @@ mod tests { ); assert_eq!( serde_json::Value::Object(refusal.payload()), - json!({ "vendor": "test-vendor", "feature": "unverified_context" }) + json!({ "vendor": "vault-transit", "feature": "unverified_context" }) ); } diff --git a/packages/stack-kms-aws/src/client.rs b/packages/stack-kms-aws/src/client.rs index 9060e4178..b70973363 100644 --- a/packages/stack-kms-aws/src/client.rs +++ b/packages/stack-kms-aws/src/client.rs @@ -19,8 +19,8 @@ use crate::credentials::{CredentialsError, CredentialsProvider}; use crate::sigv4::{sign, SigningParams}; use crate::time::{system_clock, Clock}; -/// The vendor name in errors. -const VENDOR: &str = "aws-kms"; +/// This client's vendor: what [`KeyVendorBackend::vendor`] answers and what its errors name. +const VENDOR: KeyVendor = KeyVendor::Aws; /// The AWS KMS JSON protocol content type. const CONTENT_TYPE: &str = "application/x-amz-json-1.1"; /// The SigV4 signing name of AWS KMS. @@ -80,7 +80,7 @@ pub enum AwsKmsError { fn backend(kind: BackendErrorKind, error: AwsKmsError) -> Error { Error::Backend { - vendor: VENDOR, + backend: VENDOR.into(), kind, source: Box::new(error), } @@ -372,7 +372,7 @@ fn plaintext_key(operation: &'static str, response: KeyResponse) -> Result KeyVendorBackend for AwsKms { fn vendor(&self) -> KeyVendor { - KeyVendor::Aws + VENDOR } async fn wrap(&self, key_ref: &str, key: &KeyBytes, aad: &[u8]) -> Result, Error> { diff --git a/packages/stack-kms-aws/tests/fixtures.rs b/packages/stack-kms-aws/tests/fixtures.rs index 81fc1b0be..b52d965ec 100644 --- a/packages/stack-kms-aws/tests/fixtures.rs +++ b/packages/stack-kms-aws/tests/fixtures.rs @@ -27,8 +27,8 @@ fn record_aad() -> Vec { fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "aws-kms"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "aws-kms"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-azure/src/client.rs b/packages/stack-kms-azure/src/client.rs index e3a788722..66664fe49 100644 --- a/packages/stack-kms-azure/src/client.rs +++ b/packages/stack-kms-azure/src/client.rs @@ -18,8 +18,8 @@ use zeroize::Zeroizing; use crate::auth::{AuthError, TokenProvider}; use crate::config::{is_version, AzureKeyVaultConfig, KeyId, WrapAlgorithm, MAX_VERSION_LEN}; -/// The vendor name in errors ([`KeyVendor::name`] of [`KeyVendor::Azure`]). -const VENDOR: &str = "azure-key-vault"; +/// This client's vendor: what [`KeyVendorBackend::vendor`] answers and what its errors name. +const VENDOR: KeyVendor = KeyVendor::Azure; /// The first byte of a wrapped key this client stores: the format of the /// rest (`algorithm(1) ‖ version length(1) ‖ version ‖ Azure ciphertext`). @@ -79,7 +79,7 @@ pub enum AzureKeyVaultError { fn backend(kind: BackendErrorKind, error: AzureKeyVaultError) -> Error { Error::Backend { - vendor: VENDOR, + backend: VENDOR.into(), kind, source: Box::new(error), } @@ -595,7 +595,7 @@ impl AzureKeyVault { impl KeyVendorBackend for AzureKeyVault { fn vendor(&self) -> KeyVendor { - KeyVendor::Azure + VENDOR } /// The same key in the same vault or HSM, whatever version either key diff --git a/packages/stack-kms-azure/tests/contract.rs b/packages/stack-kms-azure/tests/contract.rs index 528dc2170..1632fcbaa 100644 --- a/packages/stack-kms-azure/tests/contract.rs +++ b/packages/stack-kms-azure/tests/contract.rs @@ -27,8 +27,8 @@ use vitaminc::protected::{Controlled, Protected}; fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "azure-key-vault"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "azure-key-vault"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-azure/tests/fixtures.rs b/packages/stack-kms-azure/tests/fixtures.rs index 610948a3d..e5c3065dc 100644 --- a/packages/stack-kms-azure/tests/fixtures.rs +++ b/packages/stack-kms-azure/tests/fixtures.rs @@ -50,8 +50,8 @@ fn data_aad(n: u8) -> Vec { fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "azure-key-vault"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "azure-key-vault"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-azure/tests/live.rs b/packages/stack-kms-azure/tests/live.rs index ba105cff0..fda368305 100644 --- a/packages/stack-kms-azure/tests/live.rs +++ b/packages/stack-kms-azure/tests/live.rs @@ -44,8 +44,8 @@ fn live_keys() -> Vec { fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "azure-key-vault"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "azure-key-vault"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-gcp/src/client.rs b/packages/stack-kms-gcp/src/client.rs index 20c7ea266..f74ab2d80 100644 --- a/packages/stack-kms-gcp/src/client.rs +++ b/packages/stack-kms-gcp/src/client.rs @@ -17,8 +17,8 @@ use crate::auth::{AuthError, TokenProvider}; use crate::config::{parse_key_name, GcpKmsConfig}; use crate::crc32c::crc32c; -/// The vendor name in errors ([`KeyVendor::name`] of [`KeyVendor::Gcp`]). -const VENDOR: &str = "gcp-kms"; +/// This client's vendor: what [`KeyVendorBackend::vendor`] answers and what its errors name. +const VENDOR: KeyVendor = KeyVendor::Gcp; /// The first byte of a wrapped key this client stores: the format of the /// rest (`key version(4, BE) ‖ Cloud KMS ciphertext`). @@ -80,7 +80,7 @@ pub enum GcpKmsError { fn backend(kind: BackendErrorKind, error: GcpKmsError) -> Error { Error::Backend { - vendor: VENDOR, + backend: VENDOR.into(), kind, source: Box::new(error), } @@ -406,7 +406,7 @@ fn to_json(operation: &'static str, value: &S) -> Result KeyVendorBackend for GcpKms { fn vendor(&self) -> KeyVendor { - KeyVendor::Gcp + VENDOR } /// The same crypto key, whatever `…/cryptoKeyVersions/{n}` either diff --git a/packages/stack-kms-gcp/tests/contract.rs b/packages/stack-kms-gcp/tests/contract.rs index 1ba29b0fb..bdf063548 100644 --- a/packages/stack-kms-gcp/tests/contract.rs +++ b/packages/stack-kms-gcp/tests/contract.rs @@ -20,8 +20,8 @@ use vitaminc::protected::{Controlled, Protected}; fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "gcp-kms"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "gcp-kms"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-gcp/tests/fixtures.rs b/packages/stack-kms-gcp/tests/fixtures.rs index e39a81887..3ef92df42 100644 --- a/packages/stack-kms-gcp/tests/fixtures.rs +++ b/packages/stack-kms-gcp/tests/fixtures.rs @@ -46,8 +46,8 @@ fn data_aad(n: u8) -> Vec { fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "gcp-kms"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "gcp-kms"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-gcp/tests/live.rs b/packages/stack-kms-gcp/tests/live.rs index 6241b10a9..1aa32abd5 100644 --- a/packages/stack-kms-gcp/tests/live.rs +++ b/packages/stack-kms-gcp/tests/live.rs @@ -37,8 +37,8 @@ fn live_key() -> Option { fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "gcp-kms"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "gcp-kms"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-vault/src/client.rs b/packages/stack-kms-vault/src/client.rs index 3e804adba..a2b8e55db 100644 --- a/packages/stack-kms-vault/src/client.rs +++ b/packages/stack-kms-vault/src/client.rs @@ -19,8 +19,8 @@ use zeroize::Zeroizing; use crate::auth::{AuthError, TokenProvider}; use crate::config::{is_segment, KeyDerivation, VaultConfig}; -/// The vendor name in errors ([`KeyVendor::name`] of [`KeyVendor::Vault`]). -const VENDOR: &str = "vault-transit"; +/// This client's vendor: what [`KeyVendorBackend::vendor`] answers and what its errors name. +const VENDOR: KeyVendor = KeyVendor::Vault; /// The first byte of a wrapped key this client stores: the format of the /// rest (`version(4, BE) ‖ Vault ciphertext bytes`). @@ -88,7 +88,7 @@ pub enum VaultError { fn backend(kind: BackendErrorKind, error: VaultError) -> Error { Error::Backend { - vendor: VENDOR, + backend: VENDOR.into(), kind, source: Box::new(error), } @@ -524,7 +524,7 @@ fn stored(operation: &'static str, result: BatchResult) -> Result, Error impl KeyVendorBackend for VaultTransit { fn vendor(&self) -> KeyVendor { - KeyVendor::Vault + VENDOR } async fn wrap(&self, key_ref: &str, key: &KeyBytes, aad: &[u8]) -> Result, Error> { diff --git a/packages/stack-kms-vault/tests/contract.rs b/packages/stack-kms-vault/tests/contract.rs index 8c7a28a4b..e43d9e4a3 100644 --- a/packages/stack-kms-vault/tests/contract.rs +++ b/packages/stack-kms-vault/tests/contract.rs @@ -35,8 +35,8 @@ fn client_with( fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "vault-transit"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "vault-transit"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-vault/tests/fixtures.rs b/packages/stack-kms-vault/tests/fixtures.rs index b89186a01..cae8da7b0 100644 --- a/packages/stack-kms-vault/tests/fixtures.rs +++ b/packages/stack-kms-vault/tests/fixtures.rs @@ -39,8 +39,8 @@ fn data_aads() -> Vec> { fn kind_of(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "vault-transit"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend.name(), "vault-transit"); kind } other => panic!("not a backend error: {other:?}"), diff --git a/packages/stack-kms-vendor/src/batch.rs b/packages/stack-kms-vendor/src/batch.rs index 324880ee8..25452903b 100644 --- a/packages/stack-kms-vendor/src/batch.rs +++ b/packages/stack-kms-vendor/src/batch.rs @@ -113,7 +113,7 @@ mod tests { fn throttled() -> Error { Error::Backend { - vendor: "test-vendor", + backend: stack_kms::KeyVendor::Static.into(), kind: BackendErrorKind::Throttled, source: "slow down".into(), } @@ -229,7 +229,7 @@ mod tests { async move { if index == 2 { Err(Error::Backend { - vendor: "test-vendor", + backend: stack_kms::KeyVendor::Static.into(), kind: BackendErrorKind::AccessDenied, source: "no".into(), }) diff --git a/packages/stack-kms-vendor/src/contract.rs b/packages/stack-kms-vendor/src/contract.rs index 558151351..2e7e84f66 100644 --- a/packages/stack-kms-vendor/src/contract.rs +++ b/packages/stack-kms-vendor/src/contract.rs @@ -148,7 +148,7 @@ impl Observed { .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |n| n.checked_sub(1)); match pending { Ok(_) => Err(KmsError::Backend { - vendor: self.inner.vendor().name(), + backend: self.inner.vendor().into(), kind: BackendErrorKind::Throttled, source: "throttled by the contract suite".into(), }), diff --git a/packages/stack-kms-vendor/src/fake.rs b/packages/stack-kms-vendor/src/fake.rs index c9c945dad..778326d4c 100644 --- a/packages/stack-kms-vendor/src/fake.rs +++ b/packages/stack-kms-vendor/src/fake.rs @@ -114,7 +114,7 @@ impl FakeVendor { if pending.is_ok() { let _ = self.throttled.fetch_add(1, Ordering::SeqCst); return Err(Error::Backend { - vendor: self.vendor.name(), + backend: self.vendor.into(), kind: BackendErrorKind::Throttled, source: "the fake vendor is throttling".into(), }); @@ -126,7 +126,7 @@ impl FakeVendor { fn rename(&self, error: Error) -> Error { match error { Error::Backend { kind, source, .. } => Error::Backend { - vendor: self.vendor.name(), + backend: self.vendor.into(), kind, source, }, diff --git a/packages/stack-kms-vendor/src/source.rs b/packages/stack-kms-vendor/src/source.rs index a400cbab4..1365749e4 100644 --- a/packages/stack-kms-vendor/src/source.rs +++ b/packages/stack-kms-vendor/src/source.rs @@ -107,7 +107,7 @@ pub(crate) fn vendor_of<'a>( fn not_found(vendor: KeyVendor) -> Error { Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: BackendErrorKind::NotFound, source: "no keyset record for this keyset".into(), } @@ -115,7 +115,7 @@ fn not_found(vendor: KeyVendor) -> Error { fn unavailable(vendor: KeyVendor, message: &'static str) -> Error { Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: BackendErrorKind::Unavailable, source: message.into(), } @@ -123,7 +123,7 @@ fn unavailable(vendor: KeyVendor, message: &'static str) -> Error { fn invalid(vendor: KeyVendor, message: &'static str) -> Error { Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: BackendErrorKind::InvalidCiphertext, source: message.into(), } @@ -445,7 +445,7 @@ impl DataKeySource for VendorKeySource { unverified_context: Option>, ) -> Result, Error> { let vendor = self.vendor(); - refuse_zerokms_extensions(vendor.name(), &payloads, unverified_context.as_deref())?; + refuse_zerokms_extensions(vendor, &payloads, unverified_context.as_deref())?; let record = self.resolve_id(keyset_id)?; match record.mode() { KeyMode::Derived => { @@ -510,7 +510,7 @@ impl DataKeySource for VendorKeySource { unverified_context: Option<&UnverifiedContext>, ) -> Result, Error> { let vendor = self.vendor(); - refuse_zerokms_extensions(vendor.name(), &payloads, unverified_context)?; + refuse_zerokms_extensions(vendor, &payloads, unverified_context)?; let record = self.resolve_id(keyset_id)?; if payloads .iter() diff --git a/packages/stack-kms-vendor/src/static_key.rs b/packages/stack-kms-vendor/src/static_key.rs index cd0d29286..d60d958de 100644 --- a/packages/stack-kms-vendor/src/static_key.rs +++ b/packages/stack-kms-vendor/src/static_key.rs @@ -78,7 +78,7 @@ impl StaticKeyVendor { fn failure(kind: BackendErrorKind, message: &'static str) -> Error { Error::Backend { - vendor: KeyVendor::Static.name(), + backend: KeyVendor::Static.into(), kind, source: message.into(), } @@ -150,8 +150,8 @@ mod tests { fn kind(error: Error) -> BackendErrorKind { match error { - Error::Backend { vendor, kind, .. } => { - assert_eq!(vendor, "static"); + Error::Backend { backend, kind, .. } => { + assert_eq!(backend, KeyVendor::Static.into()); kind } other => panic!("expected a backend error, got {other:?}"), diff --git a/packages/stack-kms-vendor/src/vendor.rs b/packages/stack-kms-vendor/src/vendor.rs index cb838db2f..6133ab6d7 100644 --- a/packages/stack-kms-vendor/src/vendor.rs +++ b/packages/stack-kms-vendor/src/vendor.rs @@ -33,7 +33,7 @@ pub type KeyBytes = Protected<[u8; 32]>; /// [`bound::bind_key`](crate::bound::bind_key) in place of the key, and /// open it with [`bound::open_bound_key`](crate::bound::open_bound_key). /// * **Report failures as [`Error::Backend`]**, with -/// [`KeyVendor::name`] as `vendor` and the nearest +/// this vendor ([`vendor`](Self::vendor)) as `backend` and the nearest /// [`BackendErrorKind`](stack_kms::BackendErrorKind). A wrong key, a wrong /// `aad` or damaged bytes is `InvalidCiphertext`. A rate limit is /// `Throttled`: the adapter backs off and retries it. Never put key @@ -207,12 +207,12 @@ pub struct UnwrapItem<'a> { /// A fresh random 256-bit key, from the CSPRNG. pub(crate) fn random_key(vendor: KeyVendor) -> Result { let mut rng = SafeRand::from_entropy().map_err(|error| Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: stack_kms::BackendErrorKind::Unavailable, source: Box::new(error), })?; KeyBytes::random(&mut rng).map_err(|error| Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: stack_kms::BackendErrorKind::Unavailable, source: Box::new(error), }) @@ -221,12 +221,12 @@ pub(crate) fn random_key(vendor: KeyVendor) -> Result { /// Fresh random bytes, from the CSPRNG. pub(crate) fn random_bytes(vendor: KeyVendor) -> Result<[u8; N], Error> { let mut rng = SafeRand::from_entropy().map_err(|error| Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: stack_kms::BackendErrorKind::Unavailable, source: Box::new(error), })?; <[u8; N]>::random(&mut rng).map_err(|error| Error::Backend { - vendor: vendor.name(), + backend: vendor.into(), kind: stack_kms::BackendErrorKind::Unavailable, source: Box::new(error), }) diff --git a/packages/stack-kms/src/backend.rs b/packages/stack-kms/src/backend.rs index 8efd91be3..6fa63a0cf 100644 --- a/packages/stack-kms/src/backend.rs +++ b/packages/stack-kms/src/backend.rs @@ -55,6 +55,12 @@ impl KeyBackend { } } +impl From for KeyBackend { + fn from(vendor: KeyVendor) -> Self { + Self::Vendor(vendor) + } +} + impl fmt::Display for KeyBackend { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.name()) @@ -103,9 +109,11 @@ impl KeyVendor { } } - /// A stable, lower-case name. A key vendor backend uses it as the - /// `vendor` of its [`Error::Backend`](crate::Error::Backend) and - /// [`Error::ZeroKmsOnly`](crate::Error::ZeroKmsOnly). + /// A stable, lower-case name. It is the one spelling of the vendor: the + /// `vendor` field of [`Error::Backend`](crate::Error::Backend) and + /// [`Error::ZeroKmsOnly`](crate::Error::ZeroKmsOnly) payloads, the + /// `key_vendor` value of the Go guest's config, and the `backend` that + /// `stash keyset` reports. pub const fn name(self) -> &'static str { match self { Self::Aws => "aws-kms", @@ -115,6 +123,12 @@ impl KeyVendor { Self::Static => "static", } } + + /// The vendor that [`name`](Self::name) spells `name`, or `None` for a + /// name that is not one of them. The match is exact. + pub fn from_name(name: &str) -> Option { + Self::ALL.into_iter().find(|vendor| vendor.name() == name) + } } impl fmt::Display for KeyVendor { @@ -194,6 +208,12 @@ mod tests { (5, "static"), ] ); + for vendor in KeyVendor::ALL { + assert_eq!(KeyVendor::from_name(vendor.name()), Some(vendor)); + } + for name in ["aws", "AWS-KMS", "zerokms", ""] { + assert_eq!(KeyVendor::from_name(name), None, "{name:?}"); + } let modes: Vec<(u8, &str)> = KeyMode::ALL .iter() .map(|mode| (mode.byte(), mode.name())) diff --git a/packages/stack-kms/src/errors.rs b/packages/stack-kms/src/errors.rs index 6f11bef6c..4c405b7cf 100644 --- a/packages/stack-kms/src/errors.rs +++ b/packages/stack-kms/src/errors.rs @@ -341,12 +341,12 @@ mod codes { Error::InvalidEndpoint(_) => Error::InvalidEndpoint(InvalidEndpoint::Userinfo), Error::Unexpected(_) => Error::Unexpected("unexpected".into()), Error::Backend { .. } => Error::Backend { - vendor: "test-vendor", + backend: crate::KeyVendor::Static.into(), kind: BackendErrorKind::NotFound, source: Box::new(std::io::Error::other("no key")), }, Error::ZeroKmsOnly { .. } => Error::ZeroKmsOnly { - vendor: "test-vendor", + vendor: crate::KeyVendor::Static, feature: crate::payload::ZeroKmsFeature::Context, }, ])); @@ -522,17 +522,17 @@ mod codes { BackendErrorKind::InvalidCiphertext, ] { let error = Error::Backend { - vendor: "test-vendor", + backend: crate::KeyVendor::Aws.into(), kind, source: Box::new(std::io::Error::other("marker-detail")), }; assert_eq!( serde_json::Value::Object(error.payload()), - json!({ "vendor": "test-vendor", "kind": format!("{kind:?}") }) + json!({ "vendor": "aws-kms", "kind": format!("{kind:?}") }) ); assert_eq!(kind.to_string(), format!("{kind:?}")); let shown = error.to_string(); - assert_eq!(shown, format!("Key backend test-vendor failed: {kind:?}")); + assert_eq!(shown, format!("Key backend aws-kms failed: {kind:?}")); assert!(!shown.contains("marker-detail"), "{shown}"); assert!(std::error::Error::source(&error) .is_some_and(|source| source.to_string() == "marker-detail")); @@ -543,16 +543,16 @@ mod codes { fn a_zerokms_only_refusal_names_the_vendor_and_the_feature() { use serde_json::json; let error = Error::ZeroKmsOnly { - vendor: "test-vendor", + vendor: crate::KeyVendor::Vault, feature: crate::payload::ZeroKmsFeature::DecryptionPolicy, }; assert_eq!( serde_json::Value::Object(error.payload()), - json!({ "vendor": "test-vendor", "feature": "decryption_policy" }) + json!({ "vendor": "vault-transit", "feature": "decryption_policy" }) ); assert_eq!( error.to_string(), - "Key backend test-vendor does not support the ZeroKMS feature decryption_policy" + "Key backend vault-transit does not support the ZeroKMS feature decryption_policy" ); } @@ -808,17 +808,18 @@ pub enum Error { #[diagnostic(code(stack_kms::unexpected))] Unexpected(String), - /// A key vendor backend failed. `vendor` names the backend (for example - /// `"aws-kms"`) and `kind` says what failed, in terms that hold for - /// every vendor. The vendor's own error is behind `source()`; the - /// message carries only the vendor and the kind. - #[error("Key backend {vendor} failed: {kind}")] + /// A key backend failed. `backend` names it (the payload's `vendor` + /// field is its [`KeyBackend::name`](crate::KeyBackend::name), for + /// example `"aws-kms"`) and `kind` says what failed, in terms that hold + /// for every vendor. The vendor's own error is behind `source()`; the + /// message carries only the backend and the kind. + #[error("Key backend {backend} failed: {kind}")] #[diagnostic( code(stack_kms::backend), help("Read the `kind` field. NotFound and AccessDenied are configuration; Throttled and Unavailable can be retried; InvalidCiphertext means the stored key or value is damaged, or belongs to another key.") )] Backend { - vendor: &'static str, + backend: crate::KeyBackend, kind: BackendErrorKind, #[source] source: Box, @@ -833,7 +834,7 @@ pub enum Error { help("Lock contexts, decryption policies and unverified contexts need ZeroKMS. Use a ZeroKMS keyset, or remove the feature from the request.") )] ZeroKmsOnly { - vendor: &'static str, + vendor: crate::KeyVendor, feature: crate::payload::ZeroKmsFeature, }, } @@ -885,11 +886,12 @@ impl ErrorPayload for Error { Self::RetrieveKey(error) => error.payload(), Self::LoadKeyset(error) => error.payload(), Self::Auth(error) => error.payload(), - Self::Backend { vendor, kind, .. } => { - payload([("vendor", (*vendor).into()), ("kind", kind.as_str().into())]) - } + Self::Backend { backend, kind, .. } => payload([ + ("vendor", backend.name().into()), + ("kind", kind.as_str().into()), + ]), Self::ZeroKmsOnly { vendor, feature } => payload([ - ("vendor", (*vendor).into()), + ("vendor", vendor.name().into()), ("feature", feature.as_str().into()), ]), Self::ConnectionInit(_) | Self::InvalidEndpoint(_) | Self::Unexpected(_) => { diff --git a/packages/stack-kms/src/payload.rs b/packages/stack-kms/src/payload.rs index 2ad32cd39..d334443fb 100644 --- a/packages/stack-kms/src/payload.rs +++ b/packages/stack-kms/src/payload.rs @@ -59,7 +59,7 @@ pub trait ZeroKmsExtensions { /// [`DataKeySource`](crate::DataKeySource) method, so that a lock context /// or a policy is refused, not silently dropped. pub fn refuse_zerokms_extensions( - vendor: &'static str, + vendor: crate::KeyVendor, payloads: &[P], unverified_context: Option<&UnverifiedContext>, ) -> Result<(), crate::Error> { @@ -252,7 +252,7 @@ mod tests { mod zerokms_extensions { use super::*; - use crate::Error; + use crate::{Error, KeyVendor}; use std::collections::HashMap; fn tag() -> Vec { @@ -299,7 +299,7 @@ mod tests { fn refused(result: Result<(), Error>) -> ZeroKmsFeature { match result { Err(Error::ZeroKmsOnly { vendor, feature }) => { - assert_eq!(vendor, "test-vendor"); + assert_eq!(vendor, KeyVendor::Static); feature } other => panic!("expected a ZeroKmsOnly refusal, got {other:?}"), @@ -310,7 +310,7 @@ mod tests { fn a_vendor_refuses_every_zerokms_feature() { let ctx = tag(); let plain = [GenerateKeyPayload::new("d", Cow::Owned(vec![]))]; - assert!(refuse_zerokms_extensions("test-vendor", &plain, None).is_ok()); + assert!(refuse_zerokms_extensions(KeyVendor::Static, &plain, None).is_ok()); let with_context = [ GenerateKeyPayload::new("d", Cow::Owned(vec![])), @@ -318,7 +318,7 @@ mod tests { ]; assert_eq!( refused(refuse_zerokms_extensions( - "test-vendor", + KeyVendor::Static, &with_context, None )), @@ -328,7 +328,11 @@ mod tests { let with_policy = [RetrieveKeyPayload::new([0u8; 16], "d", b"t").with_decryption_policy(policy())]; assert_eq!( - refused(refuse_zerokms_extensions("test-vendor", &with_policy, None)), + refused(refuse_zerokms_extensions( + KeyVendor::Static, + &with_policy, + None + )), ZeroKmsFeature::DecryptionPolicy ); @@ -337,7 +341,7 @@ mod tests { let unverified = UnverifiedContext(claims); assert_eq!( refused(refuse_zerokms_extensions( - "test-vendor", + KeyVendor::Static, &plain, Some(&unverified) )), @@ -350,7 +354,7 @@ mod tests { fn an_empty_unverified_context_is_not_refused() { let plain = [GenerateKeyPayload::new("d", Cow::Owned(vec![]))]; let empty = UnverifiedContext(HashMap::new()); - assert!(refuse_zerokms_extensions("test-vendor", &plain, Some(&empty)).is_ok()); + assert!(refuse_zerokms_extensions(KeyVendor::Static, &plain, Some(&empty)).is_ok()); } #[test]