• 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.crypto.tink.AccessesPartialKey;
20 import com.google.crypto.tink.Key;
21 import com.google.crypto.tink.internal.OutputPrefixUtil;
22 import com.google.crypto.tink.util.Bytes;
23 import com.google.crypto.tink.util.SecretBytes;
24 import com.google.errorprone.annotations.CanIgnoreReturnValue;
25 import com.google.errorprone.annotations.RestrictedApi;
26 import java.security.GeneralSecurityException;
27 import java.util.Objects;
28 import javax.annotation.Nullable;
29 
30 /** Represents an AES-CTR-HMAC key used for computing AEAD. */
31 public final class AesCtrHmacAeadKey extends AeadKey {
32   private final AesCtrHmacAeadParameters parameters;
33   private final SecretBytes aesKeyBytes;
34   private final SecretBytes hmacKeyBytes;
35   private final Bytes outputPrefix;
36   @Nullable private final Integer idRequirement;
37 
38   /** Builder for AesCtrHmacAeadKey. */
39   public static class Builder {
40     @Nullable private AesCtrHmacAeadParameters parameters = null;
41     @Nullable private SecretBytes aesKeyBytes = null;
42     @Nullable private SecretBytes hmacKeyBytes = null;
43     @Nullable private Integer idRequirement = null;
44 
Builder()45     private Builder() {}
46 
47     @CanIgnoreReturnValue
setParameters(AesCtrHmacAeadParameters parameters)48     public Builder setParameters(AesCtrHmacAeadParameters parameters) {
49       this.parameters = parameters;
50       return this;
51     }
52 
53     @CanIgnoreReturnValue
setAesKeyBytes(SecretBytes aesKeyBytes)54     public Builder setAesKeyBytes(SecretBytes aesKeyBytes) {
55       this.aesKeyBytes = aesKeyBytes;
56       return this;
57     }
58 
59     @CanIgnoreReturnValue
setHmacKeyBytes(SecretBytes hmacKeyBytes)60     public Builder setHmacKeyBytes(SecretBytes hmacKeyBytes) {
61       this.hmacKeyBytes = hmacKeyBytes;
62       return this;
63     }
64 
65     @CanIgnoreReturnValue
setIdRequirement(@ullable Integer idRequirement)66     public Builder setIdRequirement(@Nullable Integer idRequirement) {
67       this.idRequirement = idRequirement;
68       return this;
69     }
70 
getOutputPrefix()71     private Bytes getOutputPrefix() {
72       if (parameters.getVariant() == AesCtrHmacAeadParameters.Variant.NO_PREFIX) {
73         return OutputPrefixUtil.EMPTY_PREFIX;
74       }
75       if (parameters.getVariant() == AesCtrHmacAeadParameters.Variant.CRUNCHY) {
76         return OutputPrefixUtil.getLegacyOutputPrefix(idRequirement);
77       }
78       if (parameters.getVariant() == AesCtrHmacAeadParameters.Variant.TINK) {
79         return OutputPrefixUtil.getTinkOutputPrefix(idRequirement);
80       }
81       throw new IllegalStateException(
82           "Unknown AesCtrHmacAeadParameters.Variant: " + parameters.getVariant());
83     }
84 
build()85     public AesCtrHmacAeadKey build() throws GeneralSecurityException {
86       if (parameters == null) {
87         throw new GeneralSecurityException("Cannot build without parameters");
88       }
89 
90       if (aesKeyBytes == null || hmacKeyBytes == null) {
91         throw new GeneralSecurityException("Cannot build without key material");
92       }
93 
94       if (parameters.getAesKeySizeBytes() != aesKeyBytes.size()) {
95         throw new GeneralSecurityException("AES key size mismatch");
96       }
97 
98       if (parameters.getHmacKeySizeBytes() != hmacKeyBytes.size()) {
99         throw new GeneralSecurityException("HMAC key size mismatch");
100       }
101 
102       if (parameters.hasIdRequirement() && idRequirement == null) {
103         throw new GeneralSecurityException(
104             "Cannot create key without ID requirement with parameters with ID requirement");
105       }
106 
107       if (!parameters.hasIdRequirement() && idRequirement != null) {
108         throw new GeneralSecurityException(
109             "Cannot create key with ID requirement with parameters without ID requirement");
110       }
111       Bytes outputPrefix = getOutputPrefix();
112       return new AesCtrHmacAeadKey(
113           parameters, aesKeyBytes, hmacKeyBytes, outputPrefix, idRequirement);
114     }
115   }
116 
AesCtrHmacAeadKey( AesCtrHmacAeadParameters parameters, SecretBytes aesKeyBytes, SecretBytes hmacKeyBytes, Bytes outputPrefix, @Nullable Integer idRequirement)117   private AesCtrHmacAeadKey(
118       AesCtrHmacAeadParameters parameters,
119       SecretBytes aesKeyBytes,
120       SecretBytes hmacKeyBytes,
121       Bytes outputPrefix,
122       @Nullable Integer idRequirement) {
123     this.parameters = parameters;
124     this.aesKeyBytes = aesKeyBytes;
125     this.hmacKeyBytes = hmacKeyBytes;
126     this.outputPrefix = outputPrefix;
127     this.idRequirement = idRequirement;
128   }
129 
130   @RestrictedApi(
131       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
132       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
133       allowedOnPath = ".*Test\\.java",
134       allowlistAnnotations = {AccessesPartialKey.class})
builder()135   public static Builder builder() {
136     return new Builder();
137   }
138 
139   /** Returns the underlying AES key bytes. */
140   @RestrictedApi(
141       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
142       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
143       allowedOnPath = ".*Test\\.java",
144       allowlistAnnotations = {AccessesPartialKey.class})
getAesKeyBytes()145   public SecretBytes getAesKeyBytes() {
146     return aesKeyBytes;
147   }
148 
149   /** Returns the underlying HMAC key bytes. */
150   @RestrictedApi(
151       explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey",
152       link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys",
153       allowedOnPath = ".*Test\\.java",
154       allowlistAnnotations = {AccessesPartialKey.class})
getHmacKeyBytes()155   public SecretBytes getHmacKeyBytes() {
156     return hmacKeyBytes;
157   }
158 
159   @Override
getOutputPrefix()160   public Bytes getOutputPrefix() {
161     return outputPrefix;
162   }
163 
164   @Override
getParameters()165   public AesCtrHmacAeadParameters getParameters() {
166     return parameters;
167   }
168 
169   @Override
170   @Nullable
getIdRequirementOrNull()171   public Integer getIdRequirementOrNull() {
172     return idRequirement;
173   }
174 
175   @Override
equalsKey(Key o)176   public boolean equalsKey(Key o) {
177     if (!(o instanceof AesCtrHmacAeadKey)) {
178       return false;
179     }
180     AesCtrHmacAeadKey that = (AesCtrHmacAeadKey) o;
181     // Since outputPrefix is a function of parameters, we can ignore it here.
182     return that.parameters.equals(parameters)
183         && that.aesKeyBytes.equalsSecretBytes(aesKeyBytes)
184         && that.hmacKeyBytes.equalsSecretBytes(hmacKeyBytes)
185         && Objects.equals(that.idRequirement, idRequirement);
186   }
187 }
188