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.Immutable; 25 import com.google.errorprone.annotations.RestrictedApi; 26 import java.security.GeneralSecurityException; 27 import java.util.Objects; 28 import javax.annotation.Nullable; 29 30 /** 31 * Represents the Aead ChaCha20-Poly1305 specified in RFC 8439. 32 * 33 * <p>ChaCha20-Poly1305 allows no parameters; hence the main part here is really just the keys. 34 * However, Tink allows prefixing every ciphertext with an ID-dependent prefix, see {@link 35 * ChaCha20Poly1305Parameters.Variant}. 36 */ 37 @Immutable 38 public final class ChaCha20Poly1305Key extends AeadKey { 39 private final ChaCha20Poly1305Parameters parameters; 40 private final SecretBytes keyBytes; 41 private final Bytes outputPrefix; 42 @Nullable private final Integer idRequirement; 43 ChaCha20Poly1305Key( ChaCha20Poly1305Parameters parameters, SecretBytes keyBytes, Bytes outputPrefix, @Nullable Integer idRequirement)44 private ChaCha20Poly1305Key( 45 ChaCha20Poly1305Parameters parameters, 46 SecretBytes keyBytes, 47 Bytes outputPrefix, 48 @Nullable Integer idRequirement) { 49 this.parameters = parameters; 50 this.keyBytes = keyBytes; 51 this.outputPrefix = outputPrefix; 52 this.idRequirement = idRequirement; 53 } 54 getOutputPrefix( ChaCha20Poly1305Parameters parameters, @Nullable Integer idRequirement)55 private static Bytes getOutputPrefix( 56 ChaCha20Poly1305Parameters parameters, @Nullable Integer idRequirement) { 57 if (parameters.getVariant() == ChaCha20Poly1305Parameters.Variant.NO_PREFIX) { 58 return OutputPrefixUtil.EMPTY_PREFIX; 59 } 60 if (parameters.getVariant() == ChaCha20Poly1305Parameters.Variant.CRUNCHY) { 61 return OutputPrefixUtil.getLegacyOutputPrefix(idRequirement); 62 } 63 if (parameters.getVariant() == ChaCha20Poly1305Parameters.Variant.TINK) { 64 return OutputPrefixUtil.getTinkOutputPrefix(idRequirement); 65 } 66 throw new IllegalStateException("Unknown Variant: " + parameters.getVariant()); 67 } 68 69 @Override getOutputPrefix()70 public Bytes getOutputPrefix() { 71 return outputPrefix; 72 } 73 74 @RestrictedApi( 75 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 76 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 77 allowedOnPath = ".*Test\\.java", 78 allowlistAnnotations = {AccessesPartialKey.class}) 79 @AccessesPartialKey create(SecretBytes secretBytes)80 public static ChaCha20Poly1305Key create(SecretBytes secretBytes) 81 throws GeneralSecurityException { 82 return create(ChaCha20Poly1305Parameters.Variant.NO_PREFIX, secretBytes, null); 83 } 84 85 @RestrictedApi( 86 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 87 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 88 allowedOnPath = ".*Test\\.java", 89 allowlistAnnotations = {AccessesPartialKey.class}) create( ChaCha20Poly1305Parameters.Variant variant, SecretBytes secretBytes, @Nullable Integer idRequirement)90 public static ChaCha20Poly1305Key create( 91 ChaCha20Poly1305Parameters.Variant variant, 92 SecretBytes secretBytes, 93 @Nullable Integer idRequirement) 94 throws GeneralSecurityException { 95 if (variant != ChaCha20Poly1305Parameters.Variant.NO_PREFIX && idRequirement == null) { 96 throw new GeneralSecurityException( 97 "For given Variant " + variant + " the value of idRequirement must be non-null"); 98 } 99 if (variant == ChaCha20Poly1305Parameters.Variant.NO_PREFIX && idRequirement != null) { 100 throw new GeneralSecurityException( 101 "For given Variant NO_PREFIX the value of idRequirement must be null"); 102 } 103 if (secretBytes.size() != 32) { 104 throw new GeneralSecurityException( 105 "ChaCha20Poly1305 key must be constructed with key of length 32 bytes, not " 106 + secretBytes.size()); 107 } 108 ChaCha20Poly1305Parameters parameters = ChaCha20Poly1305Parameters.create(variant); 109 return new ChaCha20Poly1305Key( 110 parameters, secretBytes, getOutputPrefix(parameters, idRequirement), idRequirement); 111 } 112 113 @RestrictedApi( 114 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 115 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 116 allowedOnPath = ".*Test\\.java", 117 allowlistAnnotations = {AccessesPartialKey.class}) getKeyBytes()118 public SecretBytes getKeyBytes() { 119 return keyBytes; 120 } 121 122 @Override getParameters()123 public ChaCha20Poly1305Parameters getParameters() { 124 return parameters; 125 } 126 127 @Override 128 @Nullable getIdRequirementOrNull()129 public Integer getIdRequirementOrNull() { 130 return idRequirement; 131 } 132 133 @Override equalsKey(Key o)134 public boolean equalsKey(Key o) { 135 if (!(o instanceof ChaCha20Poly1305Key)) { 136 return false; 137 } 138 ChaCha20Poly1305Key that = (ChaCha20Poly1305Key) o; 139 // Since outputPrefix is a function of parameters, we can ignore it here. 140 return that.parameters.equals(parameters) 141 && that.keyBytes.equalsSecretBytes(keyBytes) 142 && Objects.equals(that.idRequirement, idRequirement); 143 } 144 } 145