• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
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.errorprone.annotations.CanIgnoreReturnValue;
20 import com.google.errorprone.annotations.Immutable;
21 import java.security.GeneralSecurityException;
22 import java.security.InvalidAlgorithmParameterException;
23 import java.util.Objects;
24 import javax.annotation.Nullable;
25 
26 /** Describes the parameters of an {@link AesCtrHmacAeadKey}. */
27 public final class AesCtrHmacAeadParameters extends AeadParameters {
28   private static final int PREFIX_SIZE_IN_BYTES = 5;
29   /**
30    * Describes how the prefix is computed. For AEAD there are three main possibilities: NO_PREFIX
31    * (empty prefix), TINK (prefix the ciphertext with 0x01 followed by a 4-byte key id in big endian
32    * format) or CRUNCHY (prefix the ciphertext with 0x00 followed by a 4-byte key id in big endian
33    * format)
34    */
35   @Immutable
36   public static final class Variant {
37     public static final Variant TINK = new Variant("TINK");
38     public static final Variant CRUNCHY = new Variant("CRUNCHY");
39     public static final Variant NO_PREFIX = new Variant("NO_PREFIX");
40 
41     private final String name;
42 
Variant(String name)43     private Variant(String name) {
44       this.name = name;
45     }
46 
47     @Override
toString()48     public String toString() {
49       return name;
50     }
51   }
52 
53   /** The Hash algorithm used for the HMAC. */
54   @Immutable
55   public static final class HashType {
56     public static final HashType SHA1 = new HashType("SHA1");
57     public static final HashType SHA224 = new HashType("SHA224");
58     public static final HashType SHA256 = new HashType("SHA256");
59     public static final HashType SHA384 = new HashType("SHA384");
60     public static final HashType SHA512 = new HashType("SHA512");
61 
62     private final String name;
63 
HashType(String name)64     private HashType(String name) {
65       this.name = name;
66     }
67 
68     @Override
toString()69     public String toString() {
70       return name;
71     }
72   }
73 
74   /** Builds a new AesCtrHmacAeadParameters instance. */
75   public static final class Builder {
76     @Nullable private Integer aesKeySizeBytes = null;
77     @Nullable private Integer hmacKeySizeBytes = null;
78     @Nullable private Integer ivSizeBytes = null;
79     @Nullable private Integer tagSizeBytes = null;
80     private HashType hashType = null;
81     private Variant variant = Variant.NO_PREFIX;
82 
Builder()83     private Builder() {}
84 
85     /** Accepts key sizes of 16, 24 or 32 bytes. */
86     @CanIgnoreReturnValue
setAesKeySizeBytes(int aesKeySizeBytes)87     public Builder setAesKeySizeBytes(int aesKeySizeBytes) throws GeneralSecurityException {
88       if (aesKeySizeBytes != 16 && aesKeySizeBytes != 24 && aesKeySizeBytes != 32) {
89         throw new InvalidAlgorithmParameterException(
90             String.format(
91                 "Invalid key size %d; only 16-byte, 24-byte and 32-byte AES keys are supported",
92                 aesKeySizeBytes));
93       }
94       this.aesKeySizeBytes = aesKeySizeBytes;
95       return this;
96     }
97 
98     /** Accepts key sizes of at least 16 bytes. */
99     @CanIgnoreReturnValue
setHmacKeySizeBytes(int hmacKeySizeBytes)100     public Builder setHmacKeySizeBytes(int hmacKeySizeBytes) throws GeneralSecurityException {
101       if (hmacKeySizeBytes < 16) {
102         throw new InvalidAlgorithmParameterException(
103             String.format(
104                 "Invalid key size in bytes %d; HMAC key must be at least 16 bytes",
105                 hmacKeySizeBytes));
106       }
107       this.hmacKeySizeBytes = hmacKeySizeBytes;
108       return this;
109     }
110 
111     /** IV size must be between 12 and 16 bytes. */
112     @CanIgnoreReturnValue
setIvSizeBytes(int ivSizeBytes)113     public Builder setIvSizeBytes(int ivSizeBytes) throws GeneralSecurityException {
114       if (ivSizeBytes < 12 || ivSizeBytes > 16) {
115         throw new GeneralSecurityException(
116             String.format(
117                 "Invalid IV size in bytes %d; IV size must be between 12 and 16 bytes",
118                 ivSizeBytes));
119       }
120       this.ivSizeBytes = ivSizeBytes;
121       return this;
122     }
123 
124     @CanIgnoreReturnValue
setTagSizeBytes(int tagSizeBytes)125     public Builder setTagSizeBytes(int tagSizeBytes) throws GeneralSecurityException {
126       if (tagSizeBytes < 10) {
127         throw new GeneralSecurityException(
128             String.format("Invalid tag size in bytes %d; must be at least 10 bytes", tagSizeBytes));
129       }
130       this.tagSizeBytes = tagSizeBytes;
131       return this;
132     }
133 
134     @CanIgnoreReturnValue
setVariant(Variant variant)135     public Builder setVariant(Variant variant) {
136       this.variant = variant;
137       return this;
138     }
139 
140     @CanIgnoreReturnValue
setHashType(HashType hashType)141     public Builder setHashType(HashType hashType) {
142       this.hashType = hashType;
143       return this;
144     }
145 
validateTagSizeBytes(int tagSizeBytes, HashType hashType)146     private static void validateTagSizeBytes(int tagSizeBytes, HashType hashType)
147         throws GeneralSecurityException {
148       if (hashType == HashType.SHA1) {
149         if (tagSizeBytes > 20) {
150           throw new GeneralSecurityException(
151               String.format(
152                   "Invalid tag size in bytes %d; can be at most 20 bytes for SHA1", tagSizeBytes));
153         }
154         return;
155       }
156       if (hashType == HashType.SHA224) {
157         if (tagSizeBytes > 28) {
158           throw new GeneralSecurityException(
159               String.format(
160                   "Invalid tag size in bytes %d; can be at most 28 bytes for SHA224",
161                   tagSizeBytes));
162         }
163         return;
164       }
165       if (hashType == HashType.SHA256) {
166         if (tagSizeBytes > 32) {
167           throw new GeneralSecurityException(
168               String.format(
169                   "Invalid tag size in bytes %d; can be at most 32 bytes for SHA256",
170                   tagSizeBytes));
171         }
172         return;
173       }
174       if (hashType == HashType.SHA384) {
175         if (tagSizeBytes > 48) {
176           throw new GeneralSecurityException(
177               String.format(
178                   "Invalid tag size in bytes %d; can be at most 48 bytes for SHA384",
179                   tagSizeBytes));
180         }
181         return;
182       }
183       if (hashType == HashType.SHA512) {
184         if (tagSizeBytes > 64) {
185           throw new GeneralSecurityException(
186               String.format(
187                   "Invalid tag size in bytes %d; can be at most 64 bytes for SHA512",
188                   tagSizeBytes));
189         }
190         return;
191       }
192       throw new GeneralSecurityException(
193           "unknown hash type; must be SHA1, SHA224, SHA256, SHA384 or SHA512");
194     }
195 
build()196     public AesCtrHmacAeadParameters build() throws GeneralSecurityException {
197       if (aesKeySizeBytes == null) {
198         throw new GeneralSecurityException("AES key size is not set");
199       }
200       if (hmacKeySizeBytes == null) {
201         throw new GeneralSecurityException("HMAC key size is not set");
202       }
203       if (ivSizeBytes == null) {
204         throw new GeneralSecurityException("iv size is not set");
205       }
206       if (tagSizeBytes == null) {
207         throw new GeneralSecurityException("tag size is not set");
208       }
209       if (hashType == null) {
210         throw new GeneralSecurityException("hash type is not set");
211       }
212       if (variant == null) {
213         throw new GeneralSecurityException("variant is not set");
214       }
215       validateTagSizeBytes(tagSizeBytes, hashType);
216       return new AesCtrHmacAeadParameters(
217           aesKeySizeBytes, hmacKeySizeBytes, ivSizeBytes, tagSizeBytes, variant, hashType);
218     }
219   }
220 
221   private final int aesKeySizeBytes;
222   private final int hmacKeySizeBytes;
223   private final int ivSizeBytes;
224   private final int tagSizeBytes;
225   private final Variant variant;
226   private final HashType hashType;
227 
AesCtrHmacAeadParameters( int aesKeySizeBytes, int hmacKeySizeBytes, int ivSizeBytes, int tagSizeBytes, Variant variant, HashType hashType)228   private AesCtrHmacAeadParameters(
229       int aesKeySizeBytes,
230       int hmacKeySizeBytes,
231       int ivSizeBytes,
232       int tagSizeBytes,
233       Variant variant,
234       HashType hashType) {
235     this.aesKeySizeBytes = aesKeySizeBytes;
236     this.hmacKeySizeBytes = hmacKeySizeBytes;
237     this.ivSizeBytes = ivSizeBytes;
238     this.tagSizeBytes = tagSizeBytes;
239     this.variant = variant;
240     this.hashType = hashType;
241   }
242 
builder()243   public static Builder builder() {
244     return new Builder();
245   }
246 
getAesKeySizeBytes()247   public int getAesKeySizeBytes() {
248     return aesKeySizeBytes;
249   }
250 
getHmacKeySizeBytes()251   public int getHmacKeySizeBytes() {
252     return hmacKeySizeBytes;
253   }
254 
getTagSizeBytes()255   public int getTagSizeBytes() {
256     return tagSizeBytes;
257   }
258 
getIvSizeBytes()259   public int getIvSizeBytes() {
260     return ivSizeBytes;
261   }
262 
263   /**
264    * Returns the size of the overhead added to the actual ciphertext (i.e. the size of the IV plus
265    * the size of the security relevant tag plus the size of the prefix with which this key prefixes
266    * the ciphertext.
267    */
getCiphertextOverheadSizeBytes()268   public int getCiphertextOverheadSizeBytes() {
269     if (variant == Variant.NO_PREFIX) {
270       return getTagSizeBytes() + getIvSizeBytes();
271     }
272     if (variant == Variant.TINK || variant == Variant.CRUNCHY) {
273       return getTagSizeBytes() + getIvSizeBytes() + PREFIX_SIZE_IN_BYTES;
274     }
275     throw new IllegalStateException("Unknown variant");
276   }
277 
278   /** Returns a variant object. */
getVariant()279   public Variant getVariant() {
280     return variant;
281   }
282 
283   /** Returns a hash type object. */
getHashType()284   public HashType getHashType() {
285     return hashType;
286   }
287 
288   @Override
equals(Object o)289   public boolean equals(Object o) {
290     if (!(o instanceof AesCtrHmacAeadParameters)) {
291       return false;
292     }
293     AesCtrHmacAeadParameters that = (AesCtrHmacAeadParameters) o;
294     return that.getAesKeySizeBytes() == getAesKeySizeBytes()
295         && that.getHmacKeySizeBytes() == getHmacKeySizeBytes()
296         && that.getIvSizeBytes() == getIvSizeBytes()
297         && that.getTagSizeBytes() == getTagSizeBytes()
298         && that.getVariant() == getVariant()
299         && that.getHashType() == getHashType();
300   }
301 
302   @Override
hashCode()303   public int hashCode() {
304     return Objects.hash(
305         AesCtrHmacAeadParameters.class,
306         aesKeySizeBytes,
307         hmacKeySizeBytes,
308         ivSizeBytes,
309         tagSizeBytes,
310         variant,
311         hashType);
312   }
313 
314   @Override
hasIdRequirement()315   public boolean hasIdRequirement() {
316     return variant != Variant.NO_PREFIX;
317   }
318 
319   @Override
toString()320   public String toString() {
321     return "AesCtrHmacAead Parameters (variant: "
322         + variant
323         + ", hashType: "
324         + hashType
325         + ", "
326         + ivSizeBytes
327         + "-byte IV, and "
328         + tagSizeBytes
329         + "-byte tags, and "
330         + aesKeySizeBytes
331         + "-byte AES key, and "
332         + hmacKeySizeBytes
333         + "-byte HMAC key)";
334   }
335 }
336