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