1 // Copyright 2022 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 AesGcmKey} */ 27 public final class AesGcmParameters extends AeadParameters { 28 /** 29 * Describes how the prefix is computed. For AEAD there are three main possibilities: NO_PREFIX 30 * (empty prefix), TINK (prefix the ciphertext with 0x01 followed by a 4-byte key id in big endian 31 * format) or CRUNCHY (prefix the ciphertext with 0x00 followed by a 4-byte key id in big endian 32 * format) 33 */ 34 @Immutable 35 public static final class Variant { 36 public static final Variant TINK = new Variant("TINK"); 37 public static final Variant CRUNCHY = new Variant("CRUNCHY"); 38 public static final Variant NO_PREFIX = new Variant("NO_PREFIX"); 39 40 private final String name; 41 Variant(String name)42 private Variant(String name) { 43 this.name = name; 44 } 45 46 @Override toString()47 public String toString() { 48 return name; 49 } 50 } 51 52 /** 53 * Builds a new AesGcmParameters instance. The class AesGcmParameters is not responsible for 54 * checking if all allowed values for the parameters are implemented and satisfy any potential 55 * security policies. Some implementation may not support the full set of parameters at the moment 56 * and may restrict them to certain lengths (i.e. key size may be restricted to 16 or 32 bytes). 57 */ 58 public static final class Builder { 59 @Nullable private Integer keySizeBytes = null; 60 @Nullable private Integer ivSizeBytes = null; 61 @Nullable private Integer tagSizeBytes = null; 62 private Variant variant = Variant.NO_PREFIX; 63 Builder()64 private Builder() {} 65 66 /** Accepts key sizes of 16, 24 or 32 bytes. */ 67 @CanIgnoreReturnValue setKeySizeBytes(int keySizeBytes)68 public Builder setKeySizeBytes(int keySizeBytes) throws GeneralSecurityException { 69 if (keySizeBytes != 16 && keySizeBytes != 24 && keySizeBytes != 32) { 70 throw new InvalidAlgorithmParameterException( 71 String.format( 72 "Invalid key size %d; only 16-byte, 24-byte and 32-byte AES keys are supported", 73 keySizeBytes)); 74 } 75 this.keySizeBytes = keySizeBytes; 76 return this; 77 } 78 79 /** IV size must greater than 0. */ 80 @CanIgnoreReturnValue setIvSizeBytes(int ivSizeBytes)81 public Builder setIvSizeBytes(int ivSizeBytes) throws GeneralSecurityException { 82 if (ivSizeBytes <= 0) { 83 throw new GeneralSecurityException( 84 String.format("Invalid IV size in bytes %d; IV size must be positive", ivSizeBytes)); 85 } 86 this.ivSizeBytes = ivSizeBytes; 87 return this; 88 } 89 /** Tag size must be between 12 and 16 bytes. */ 90 @CanIgnoreReturnValue setTagSizeBytes(int tagSizeBytes)91 public Builder setTagSizeBytes(int tagSizeBytes) throws GeneralSecurityException { 92 if (tagSizeBytes < 12 || tagSizeBytes > 16) { 93 throw new GeneralSecurityException( 94 String.format( 95 "Invalid tag size in bytes %d; value must be between 12 and 16 bytes", 96 tagSizeBytes)); 97 } 98 this.tagSizeBytes = tagSizeBytes; 99 return this; 100 } 101 102 @CanIgnoreReturnValue setVariant(Variant variant)103 public Builder setVariant(Variant variant) { 104 this.variant = variant; 105 return this; 106 } 107 build()108 public AesGcmParameters build() throws GeneralSecurityException { 109 if (keySizeBytes == null) { 110 throw new GeneralSecurityException("Key size is not set"); 111 } 112 if (variant == null) { 113 throw new GeneralSecurityException("Variant is not set"); 114 } 115 if (ivSizeBytes == null) { 116 throw new GeneralSecurityException("IV size is not set"); 117 } 118 119 if (tagSizeBytes == null) { 120 throw new GeneralSecurityException("Tag size is not set"); 121 } 122 123 return new AesGcmParameters(keySizeBytes, ivSizeBytes, tagSizeBytes, variant); 124 } 125 } 126 127 private final int keySizeBytes; 128 private final int ivSizeBytes; 129 private final int tagSizeBytes; 130 private final Variant variant; 131 AesGcmParameters(int keySizeBytes, int ivSizeBytes, int tagSizeBytes, Variant variant)132 private AesGcmParameters(int keySizeBytes, int ivSizeBytes, int tagSizeBytes, Variant variant) { 133 this.keySizeBytes = keySizeBytes; 134 this.ivSizeBytes = ivSizeBytes; 135 this.tagSizeBytes = tagSizeBytes; 136 this.variant = variant; 137 } 138 builder()139 public static Builder builder() { 140 return new Builder(); 141 } 142 getKeySizeBytes()143 public int getKeySizeBytes() { 144 return keySizeBytes; 145 } 146 getIvSizeBytes()147 public int getIvSizeBytes() { 148 return ivSizeBytes; 149 } 150 getTagSizeBytes()151 public int getTagSizeBytes() { 152 return tagSizeBytes; 153 } 154 155 /** Returns a variant object. */ getVariant()156 public Variant getVariant() { 157 return variant; 158 } 159 160 @Override equals(Object o)161 public boolean equals(Object o) { 162 if (!(o instanceof AesGcmParameters)) { 163 return false; 164 } 165 AesGcmParameters that = (AesGcmParameters) o; 166 return that.getKeySizeBytes() == getKeySizeBytes() 167 && that.getIvSizeBytes() == getIvSizeBytes() 168 && that.getTagSizeBytes() == getTagSizeBytes() 169 && that.getVariant() == getVariant(); 170 } 171 172 @Override hashCode()173 public int hashCode() { 174 return Objects.hash(AesGcmParameters.class, keySizeBytes, ivSizeBytes, tagSizeBytes, variant); 175 } 176 177 @Override hasIdRequirement()178 public boolean hasIdRequirement() { 179 return variant != Variant.NO_PREFIX; 180 } 181 182 @Override toString()183 public String toString() { 184 return "AesGcm Parameters (variant: " 185 + variant 186 + ", " 187 + ivSizeBytes 188 + "-byte IV, " 189 + tagSizeBytes 190 + "-byte tag, and " 191 + keySizeBytes 192 + "-byte key)"; 193 } 194 } 195