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