• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.aead;
18 
19 import com.google.crypto.tink.AccessesPartialKey;
20 import com.google.errorprone.annotations.CanIgnoreReturnValue;
21 import com.google.errorprone.annotations.Immutable;
22 import com.google.errorprone.annotations.RestrictedApi;
23 import java.security.GeneralSecurityException;
24 import java.util.Objects;
25 import javax.annotation.Nullable;
26 
27 /**
28  * Describes the parameters of an {@link LegacyKmsEnvelopeAeadKey}.
29  *
30  * <p>Usage of this key type is not recommended. Instead, we recommend to implement the idea of this
31  * class manually:
32  *
33  * <ol>
34  *   <li>Create an remote {@link com.google.crypto.tink.Aead} object for your KMS with an
35  *       appropriate Tink extension (typically using a subclass of {@link
36  *       com.google.crypto.tink.KmsClient}).
37  *   <li>Create an envelope AEAD with {@link com.google.crypto.tink.aead.KmsEnvelopeAead#create}.
38  * </ol>
39  *
40  * <H1>Known Issues</h1>
41  *
42  * <H2>Global registration</h2>
43  *
44  * If a user uses a {@code LegacyKmsEnvelopeAeadKey}, when the corresponding {@code Aead} is
45  * created, Tink looks up the {@code KmsClient} in a global registry. This registry needs to store
46  * all the credentials and all the information. This is inappropriate in many situations.
47  *
48  * <h2>Ciphertext format</h2>
49  *
50  * The ciphertext format does not encode the key type of the key used. This can lead to unexpected
51  * results if a user changes the {@code dekParametersForNewKeys} or the {@code dekParsingStrategy}
52  * for the same remote key. In more details, the ciphertext contains a Tink key proto of newly
53  * generated key, but not the type URL. This means that if a user reuses the same remote Key with a
54  * different key type, it will be parsed with the wrong one.
55  *
56  * <p>Also, Tink does note compare the parameters of the parsed key with the parameters specified in
57  * {@code dekParametersForNewKeys}. For example, if the {@code dekParametersForNewKeys} is specified
58  * as AES_128_GCM in one binary, and AES_256_GCM in another binary, communication between the
59  * binaries succeeds in both directions.
60  *
61  * <h2>Ciphertext malleability</h2>
62  *
63  * <p>Some KMS have malleable ciphertexts. This means that the Aeads corresponding to these keys may
64  * be malleable. See https://developers.google.com/tink/issues/envelope-aead-malleability
65  */
66 public final class LegacyKmsEnvelopeAeadParameters extends AeadParameters {
67 
68   /**
69    * Describes how the prefix is computed. There are two main possibilities: NO_PREFIX (empty
70    * prefix) and TINK (prefix the ciphertext with 0x01 followed by a 4-byte key id in big endian.
71    */
72   @Immutable
73   public static final class Variant {
74     public static final Variant TINK = new Variant("TINK");
75     public static final Variant NO_PREFIX = new Variant("NO_PREFIX");
76 
77     private final String name;
78 
Variant(String name)79     private Variant(String name) {
80       this.name = name;
81     }
82 
83     @Override
toString()84     public String toString() {
85       return name;
86     }
87   }
88 
89   /**
90    * Specifies how the DEK in received ciphertexts are parsed.
91    *
92    * <p>See section "Ciphertext format" above for a discussion of this.
93    */
94   @Immutable
95   public static final class DekParsingStrategy {
96     /** When parsing, assume that the ciphertext was encrypted with AES GCM. */
97     public static final DekParsingStrategy ASSUME_AES_GCM =
98         new DekParsingStrategy("ASSUME_AES_GCM");
99 
100     /** When parsing, assume that the ciphertext was encrypted with XChaCha20-Poly1305. */
101     public static final DekParsingStrategy ASSUME_XCHACHA20POLY1305 =
102         new DekParsingStrategy("ASSUME_XCHACHA20POLY1305");
103 
104     /** When parsing, assume that the ciphertext was encrypted with ChaCha20-Poly1305. */
105     public static final DekParsingStrategy ASSUME_CHACHA20POLY1305 =
106         new DekParsingStrategy("ASSUME_CHACHA20POLY1305");
107 
108     /** When parsing, assume that the ciphertext was encrypted with AES CTR HMAC. */
109     public static final DekParsingStrategy ASSUME_AES_CTR_HMAC =
110         new DekParsingStrategy("ASSUME_AES_CTR_HMAC");
111 
112     /** When parsing, assume that the ciphertext was encrypted with AES EAX. */
113     public static final DekParsingStrategy ASSUME_AES_EAX =
114         new DekParsingStrategy("ASSUME_AES_EAX");
115 
116     /** When parsing, assume that the ciphertext was encrypted with AES GCM SIV. */
117     public static final DekParsingStrategy ASSUME_AES_GCM_SIV =
118         new DekParsingStrategy("ASSUME_AES_GCM_SIV");
119 
120     private final String name;
121 
DekParsingStrategy(String name)122     private DekParsingStrategy(String name) {
123       this.name = name;
124     }
125 
126     @Override
toString()127     public String toString() {
128       return name;
129     }
130   }
131 
132   private final Variant variant;
133   private final String kekUri;
134   private final DekParsingStrategy dekParsingStrategy;
135   private final AeadParameters dekParametersForNewKeys;
136 
LegacyKmsEnvelopeAeadParameters( Variant variant, String kekUri, DekParsingStrategy dekParsingStrategy, AeadParameters dekParametersForNewKeys)137   private LegacyKmsEnvelopeAeadParameters(
138       Variant variant,
139       String kekUri,
140       DekParsingStrategy dekParsingStrategy,
141       AeadParameters dekParametersForNewKeys) {
142     this.variant = variant;
143     this.kekUri = kekUri;
144     this.dekParsingStrategy = dekParsingStrategy;
145     this.dekParametersForNewKeys = dekParametersForNewKeys;
146   }
147 
148   /** Builder for {@link LegacyKmsEnvelopeAeadParameters}. */
149   public static class Builder {
150     @Nullable private Variant variant;
151     @Nullable private String kekUri;
152     @Nullable private DekParsingStrategy dekParsingStrategy;
153     @Nullable private AeadParameters dekParametersForNewKeys;
154 
Builder()155     private Builder() {}
156 
157     @CanIgnoreReturnValue
setVariant(Variant variant)158     public Builder setVariant(Variant variant) {
159       this.variant = variant;
160       return this;
161     }
162 
163     /**
164      * Sets the URI of the KMS to be used.
165      *
166      * <p>The KMS will be used to encrypt the DEK key as an AEAD.
167      */
168     @CanIgnoreReturnValue
setKekUri(String kekUri)169     public Builder setKekUri(String kekUri) {
170       this.kekUri = kekUri;
171       return this;
172     }
173 
174     @CanIgnoreReturnValue
setDekParsingStrategy(DekParsingStrategy dekParsingStrategy)175     public Builder setDekParsingStrategy(DekParsingStrategy dekParsingStrategy) {
176       this.dekParsingStrategy = dekParsingStrategy;
177       return this;
178     }
179 
180     @CanIgnoreReturnValue
setDekParametersForNewKeys(AeadParameters aeadParameters)181     public Builder setDekParametersForNewKeys(AeadParameters aeadParameters) {
182       this.dekParametersForNewKeys = aeadParameters;
183       return this;
184     }
185 
parsingStrategyAllowed( DekParsingStrategy parsingStrategy, AeadParameters aeadParameters)186     private static boolean parsingStrategyAllowed(
187         DekParsingStrategy parsingStrategy, AeadParameters aeadParameters) {
188       if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_GCM)
189           && (aeadParameters instanceof AesGcmParameters)) {
190         return true;
191       }
192       if (parsingStrategy.equals(DekParsingStrategy.ASSUME_CHACHA20POLY1305)
193           && (aeadParameters instanceof ChaCha20Poly1305Parameters)) {
194         return true;
195       }
196       if (parsingStrategy.equals(DekParsingStrategy.ASSUME_XCHACHA20POLY1305)
197           && (aeadParameters instanceof XChaCha20Poly1305Parameters)) {
198         return true;
199       }
200       if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_CTR_HMAC)
201           && (aeadParameters instanceof AesCtrHmacAeadParameters)) {
202         return true;
203       }
204       if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_EAX)
205           && (aeadParameters instanceof AesEaxParameters)) {
206         return true;
207       }
208       if (parsingStrategy.equals(DekParsingStrategy.ASSUME_AES_GCM_SIV)
209           && (aeadParameters instanceof AesGcmSivParameters)) {
210         return true;
211       }
212       return false;
213     }
214 
215     /** Builds the LegacyKmsEnvelopeAeadParameters. */
build()216     public LegacyKmsEnvelopeAeadParameters build() throws GeneralSecurityException {
217       if (variant == null) {
218         // Use NO_PREFIX as default prefix.
219         variant = Variant.NO_PREFIX;
220       }
221       if (kekUri == null) {
222         throw new GeneralSecurityException("kekUri must be set");
223       }
224       if (dekParsingStrategy == null) {
225         throw new GeneralSecurityException("dekParsingStrategy must be set");
226       }
227       if (dekParametersForNewKeys == null) {
228         throw new GeneralSecurityException("dekParametersForNewKeys must be set");
229       }
230       if (dekParametersForNewKeys.hasIdRequirement()) {
231         throw new GeneralSecurityException("dekParametersForNewKeys must not have ID Requirements");
232       }
233       if (!parsingStrategyAllowed(dekParsingStrategy, dekParametersForNewKeys)) {
234         throw new GeneralSecurityException(
235             "Cannot use parsing strategy "
236                 + dekParsingStrategy.toString()
237                 + " when new keys are picked according to "
238                 + dekParametersForNewKeys
239                 + ".");
240       }
241 
242       return new LegacyKmsEnvelopeAeadParameters(
243           variant, kekUri, dekParsingStrategy, dekParametersForNewKeys);
244     }
245   }
246 
247   @RestrictedApi(
248       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
249       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
250       allowedOnPath = ".*Test\\.java",
251       allowlistAnnotations = {AccessesPartialKey.class})
builder()252   public static Builder builder() {
253     return new Builder();
254   }
255 
256   /** Returns the URI with the key of the remote AEAD used. */
257   @RestrictedApi(
258       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
259       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
260       allowedOnPath = ".*Test\\.java",
261       allowlistAnnotations = {AccessesPartialKey.class})
getKekUri()262   public String getKekUri() {
263     return kekUri;
264   }
265 
getVariant()266   public Variant getVariant() {
267     return variant;
268   }
269 
270   @Override
hasIdRequirement()271   public boolean hasIdRequirement() {
272     return variant != Variant.NO_PREFIX;
273   }
274 
275   /**
276    * Returns the type URL which is used when parsing encrypted keys.
277    *
278    * <p>See "Known Issues" section above.
279    */
getDekParsingStrategy()280   public DekParsingStrategy getDekParsingStrategy() {
281     return dekParsingStrategy;
282   }
283 
284   /** Parameters used when creating new keys. */
getDekParametersForNewKeys()285   public AeadParameters getDekParametersForNewKeys() {
286     return dekParametersForNewKeys;
287   }
288 
289   @Override
equals(Object o)290   public boolean equals(Object o) {
291     if (!(o instanceof LegacyKmsEnvelopeAeadParameters)) {
292       return false;
293     }
294     LegacyKmsEnvelopeAeadParameters that = (LegacyKmsEnvelopeAeadParameters) o;
295     return that.dekParsingStrategy.equals(dekParsingStrategy)
296         && that.dekParametersForNewKeys.equals(dekParametersForNewKeys)
297         && that.kekUri.equals(kekUri)
298         && that.variant.equals(variant);
299   }
300 
301   @Override
hashCode()302   public int hashCode() {
303     return Objects.hash(
304         LegacyKmsEnvelopeAeadParameters.class,
305         kekUri,
306         dekParsingStrategy,
307         dekParametersForNewKeys,
308         variant);
309   }
310 
311   @Override
toString()312   public String toString() {
313     return "LegacyKmsEnvelopeAead Parameters (kekUri: "
314         + kekUri
315         + ", "
316         + "dekParsingStrategy: "
317         + dekParsingStrategy
318         + ", "
319         + "dekParametersForNewKeys: "
320         + dekParametersForNewKeys
321         + ", "
322         + "variant: "
323         + variant
324         + ")";
325   }
326 }
327