// Copyright 2023 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package com.google.crypto.tink.aead; import com.google.crypto.tink.AccessesPartialKey; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.RestrictedApi; import java.security.GeneralSecurityException; import java.util.Objects; import javax.annotation.Nullable; /** * Describes the parameters of an {@link LegacyKmsEnvelopeAeadKey}. * *
Usage of this key type is not recommended. Instead, we recommend to implement the idea of this * class manually: * *
Also, Tink does note compare the parameters of the parsed key with the parameters specified in * {@code dekParametersForNewKeys}. For example, if the {@code dekParametersForNewKeys} is specified * as AES_128_GCM in one binary, and AES_256_GCM in another binary, communication between the * binaries succeeds in both directions. * *
Some KMS have malleable ciphertexts. This means that the Aeads corresponding to these keys may * be malleable. See https://developers.google.com/tink/issues/envelope-aead-malleability */ public final class LegacyKmsEnvelopeAeadParameters extends AeadParameters { /** * Describes how the prefix is computed. There are two main possibilities: NO_PREFIX (empty * prefix) and TINK (prefix the ciphertext with 0x01 followed by a 4-byte key id in big endian. */ @Immutable public static final class Variant { public static final Variant TINK = new Variant("TINK"); public static final Variant NO_PREFIX = new Variant("NO_PREFIX"); private final String name; private Variant(String name) { this.name = name; } @Override public String toString() { return name; } } /** * Specifies how the DEK in received ciphertexts are parsed. * *
See section "Ciphertext format" above for a discussion of this. */ @Immutable public static final class DekParsingStrategy { /** When parsing, assume that the ciphertext was encrypted with AES GCM. */ public static final DekParsingStrategy ASSUME_AES_GCM = new DekParsingStrategy("ASSUME_AES_GCM"); /** When parsing, assume that the ciphertext was encrypted with XChaCha20-Poly1305. */ public static final DekParsingStrategy ASSUME_XCHACHA20POLY1305 = new DekParsingStrategy("ASSUME_XCHACHA20POLY1305"); /** When parsing, assume that the ciphertext was encrypted with ChaCha20-Poly1305. */ public static final DekParsingStrategy ASSUME_CHACHA20POLY1305 = new DekParsingStrategy("ASSUME_CHACHA20POLY1305"); /** When parsing, assume that the ciphertext was encrypted with AES CTR HMAC. */ public static final DekParsingStrategy ASSUME_AES_CTR_HMAC = new DekParsingStrategy("ASSUME_AES_CTR_HMAC"); /** When parsing, assume that the ciphertext was encrypted with AES EAX. */ public static final DekParsingStrategy ASSUME_AES_EAX = new DekParsingStrategy("ASSUME_AES_EAX"); /** When parsing, assume that the ciphertext was encrypted with AES GCM SIV. */ public static final DekParsingStrategy ASSUME_AES_GCM_SIV = new DekParsingStrategy("ASSUME_AES_GCM_SIV"); private final String name; private DekParsingStrategy(String name) { this.name = name; } @Override public String toString() { return name; } } private final Variant variant; private final String kekUri; private final DekParsingStrategy dekParsingStrategy; private final AeadParameters dekParametersForNewKeys; private LegacyKmsEnvelopeAeadParameters( Variant variant, String kekUri, DekParsingStrategy dekParsingStrategy, AeadParameters dekParametersForNewKeys) { this.variant = variant; this.kekUri = kekUri; this.dekParsingStrategy = dekParsingStrategy; this.dekParametersForNewKeys = dekParametersForNewKeys; } /** Builder for {@link LegacyKmsEnvelopeAeadParameters}. */ public static class Builder { @Nullable private Variant variant; @Nullable private String kekUri; @Nullable private DekParsingStrategy dekParsingStrategy; @Nullable private AeadParameters dekParametersForNewKeys; private Builder() {} @CanIgnoreReturnValue public Builder setVariant(Variant variant) { this.variant = variant; return this; } /** * Sets the URI of the KMS to be used. * *
The KMS will be used to encrypt the DEK key as an AEAD. */ @CanIgnoreReturnValue public Builder setKekUri(String kekUri) { this.kekUri = kekUri; return this; } @CanIgnoreReturnValue public Builder setDekParsingStrategy(DekParsingStrategy dekParsingStrategy) { this.dekParsingStrategy = dekParsingStrategy; return this; } @CanIgnoreReturnValue public Builder setDekParametersForNewKeys(AeadParameters aeadParameters) { this.dekParametersForNewKeys = aeadParameters; return this; } private static boolean parsingStrategyAllowed( DekParsingStrategy parsingStrategy, AeadParameters aeadParameters) { if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_GCM) && (aeadParameters instanceof AesGcmParameters)) { return true; } if (parsingStrategy.equals(DekParsingStrategy.ASSUME_CHACHA20POLY1305) && (aeadParameters instanceof ChaCha20Poly1305Parameters)) { return true; } if (parsingStrategy.equals(DekParsingStrategy.ASSUME_XCHACHA20POLY1305) && (aeadParameters instanceof XChaCha20Poly1305Parameters)) { return true; } if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_CTR_HMAC) && (aeadParameters instanceof AesCtrHmacAeadParameters)) { return true; } if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_EAX) && (aeadParameters instanceof AesEaxParameters)) { return true; } if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_GCM_SIV) && (aeadParameters instanceof AesGcmSivParameters)) { return true; } return false; } /** Builds the LegacyKmsEnvelopeAeadParameters. */ public LegacyKmsEnvelopeAeadParameters build() throws GeneralSecurityException { if (variant == null) { // Use NO_PREFIX as default prefix. variant = Variant.NO_PREFIX; } if (kekUri == null) { throw new GeneralSecurityException("kekUri must be set"); } if (dekParsingStrategy == null) { throw new GeneralSecurityException("dekParsingStrategy must be set"); } if (dekParametersForNewKeys == null) { throw new GeneralSecurityException("dekParametersForNewKeys must be set"); } if (dekParametersForNewKeys.hasIdRequirement()) { throw new GeneralSecurityException("dekParametersForNewKeys must not have ID Requirements"); } if (!parsingStrategyAllowed(dekParsingStrategy, dekParametersForNewKeys)) { throw new GeneralSecurityException( "Cannot use parsing strategy " + dekParsingStrategy.toString() + " when new keys are picked according to " + dekParametersForNewKeys + "."); } return new LegacyKmsEnvelopeAeadParameters( variant, kekUri, dekParsingStrategy, dekParametersForNewKeys); } } @RestrictedApi( explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", allowedOnPath = ".*Test\\.java", allowlistAnnotations = {AccessesPartialKey.class}) public static Builder builder() { return new Builder(); } /** Returns the URI with the key of the remote AEAD used. */ @RestrictedApi( explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", allowedOnPath = ".*Test\\.java", allowlistAnnotations = {AccessesPartialKey.class}) public String getKekUri() { return kekUri; } public Variant getVariant() { return variant; } @Override public boolean hasIdRequirement() { return variant != Variant.NO_PREFIX; } /** * Returns the type URL which is used when parsing encrypted keys. * *
See "Known Issues" section above. */ public DekParsingStrategy getDekParsingStrategy() { return dekParsingStrategy; } /** Parameters used when creating new keys. */ public AeadParameters getDekParametersForNewKeys() { return dekParametersForNewKeys; } @Override public boolean equals(Object o) { if (!(o instanceof LegacyKmsEnvelopeAeadParameters)) { return false; } LegacyKmsEnvelopeAeadParameters that = (LegacyKmsEnvelopeAeadParameters) o; return that.dekParsingStrategy.equals(dekParsingStrategy) && that.dekParametersForNewKeys.equals(dekParametersForNewKeys) && that.kekUri.equals(kekUri) && that.variant.equals(variant); } @Override public int hashCode() { return Objects.hash( LegacyKmsEnvelopeAeadParameters.class, kekUri, dekParsingStrategy, dekParametersForNewKeys, variant); } @Override public String toString() { return "LegacyKmsEnvelopeAead Parameters (kekUri: " + kekUri + ", " + "dekParsingStrategy: " + dekParsingStrategy + ", " + "dekParametersForNewKeys: " + dekParametersForNewKeys + ", " + "variant: " + variant + ")"; } }