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