1 // Copyright 2023 Google Inc. 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 static com.google.crypto.tink.internal.Util.toBytesFromPrintableAscii; 20 21 import com.google.crypto.tink.AccessesPartialKey; 22 import com.google.crypto.tink.Parameters; 23 import com.google.crypto.tink.SecretKeyAccess; 24 import com.google.crypto.tink.TinkProtoParametersFormat; 25 import com.google.crypto.tink.internal.KeyParser; 26 import com.google.crypto.tink.internal.KeySerializer; 27 import com.google.crypto.tink.internal.MutableSerializationRegistry; 28 import com.google.crypto.tink.internal.ParametersParser; 29 import com.google.crypto.tink.internal.ParametersSerializer; 30 import com.google.crypto.tink.internal.ProtoKeySerialization; 31 import com.google.crypto.tink.internal.ProtoParametersSerialization; 32 import com.google.crypto.tink.proto.KeyData.KeyMaterialType; 33 import com.google.crypto.tink.proto.KeyTemplate; 34 import com.google.crypto.tink.proto.KmsEnvelopeAeadKey; 35 import com.google.crypto.tink.proto.KmsEnvelopeAeadKeyFormat; 36 import com.google.crypto.tink.proto.OutputPrefixType; 37 import com.google.crypto.tink.util.Bytes; 38 import com.google.protobuf.ExtensionRegistryLite; 39 import com.google.protobuf.InvalidProtocolBufferException; 40 import java.security.GeneralSecurityException; 41 import javax.annotation.Nullable; 42 43 /** 44 * Serializers and Parsers for LegacyKmsEnvelopeAeadProtoKey and 45 * LegacyKmsEnvelopeAeadProtoParameters 46 */ 47 public final class LegacyKmsEnvelopeAeadProtoSerialization { 48 private static final String TYPE_URL = 49 "type.googleapis.com/google.crypto.tink.KmsEnvelopeAeadKey"; 50 private static final Bytes TYPE_URL_BYTES = toBytesFromPrintableAscii(TYPE_URL); 51 52 private static final ParametersSerializer< 53 LegacyKmsEnvelopeAeadParameters, ProtoParametersSerialization> 54 PARAMETERS_SERIALIZER = 55 ParametersSerializer.create( 56 LegacyKmsEnvelopeAeadProtoSerialization::serializeParameters, 57 LegacyKmsEnvelopeAeadParameters.class, 58 ProtoParametersSerialization.class); 59 60 private static final ParametersParser<ProtoParametersSerialization> PARAMETERS_PARSER = 61 ParametersParser.create( 62 LegacyKmsEnvelopeAeadProtoSerialization::parseParameters, 63 TYPE_URL_BYTES, 64 ProtoParametersSerialization.class); 65 66 private static final KeySerializer<LegacyKmsEnvelopeAeadKey, ProtoKeySerialization> 67 KEY_SERIALIZER = 68 KeySerializer.create( 69 LegacyKmsEnvelopeAeadProtoSerialization::serializeKey, 70 LegacyKmsEnvelopeAeadKey.class, 71 ProtoKeySerialization.class); 72 73 private static final KeyParser<ProtoKeySerialization> KEY_PARSER = 74 KeyParser.create( 75 LegacyKmsEnvelopeAeadProtoSerialization::parseKey, 76 TYPE_URL_BYTES, 77 ProtoKeySerialization.class); 78 toProtoOutputPrefixType( LegacyKmsEnvelopeAeadParameters.Variant variant)79 private static OutputPrefixType toProtoOutputPrefixType( 80 LegacyKmsEnvelopeAeadParameters.Variant variant) throws GeneralSecurityException { 81 if (LegacyKmsEnvelopeAeadParameters.Variant.TINK.equals(variant)) { 82 return OutputPrefixType.TINK; 83 } 84 if (LegacyKmsEnvelopeAeadParameters.Variant.NO_PREFIX.equals(variant)) { 85 return OutputPrefixType.RAW; 86 } 87 throw new GeneralSecurityException("Unable to serialize variant: " + variant); 88 } 89 toVariant( OutputPrefixType outputPrefixType)90 private static LegacyKmsEnvelopeAeadParameters.Variant toVariant( 91 OutputPrefixType outputPrefixType) throws GeneralSecurityException { 92 switch (outputPrefixType) { 93 case TINK: 94 return LegacyKmsEnvelopeAeadParameters.Variant.TINK; 95 case RAW: 96 return LegacyKmsEnvelopeAeadParameters.Variant.NO_PREFIX; 97 default: 98 throw new GeneralSecurityException( 99 "Unable to parse OutputPrefixType: " + outputPrefixType.getNumber()); 100 } 101 } 102 103 @AccessesPartialKey serializeParameters( LegacyKmsEnvelopeAeadParameters parameters)104 private static ProtoParametersSerialization serializeParameters( 105 LegacyKmsEnvelopeAeadParameters parameters) throws GeneralSecurityException { 106 return ProtoParametersSerialization.create( 107 KeyTemplate.newBuilder() 108 .setTypeUrl(TYPE_URL) 109 .setValue(serializeParametersToKmsEnvelopeAeadKeyFormat(parameters).toByteString()) 110 .setOutputPrefixType(toProtoOutputPrefixType(parameters.getVariant())) 111 .build()); 112 } 113 114 @AccessesPartialKey serializeParametersToKmsEnvelopeAeadKeyFormat( LegacyKmsEnvelopeAeadParameters parameters)115 private static KmsEnvelopeAeadKeyFormat serializeParametersToKmsEnvelopeAeadKeyFormat( 116 LegacyKmsEnvelopeAeadParameters parameters) throws GeneralSecurityException { 117 byte[] serializedDekParameters = 118 TinkProtoParametersFormat.serialize(parameters.getDekParametersForNewKeys()); 119 try { 120 KeyTemplate dekKeyTemplate = 121 KeyTemplate.parseFrom(serializedDekParameters, ExtensionRegistryLite.getEmptyRegistry()); 122 return KmsEnvelopeAeadKeyFormat.newBuilder() 123 .setKekUri(parameters.getKekUri()) 124 .setDekTemplate(dekKeyTemplate) 125 .build(); 126 } catch (InvalidProtocolBufferException e) { 127 throw new GeneralSecurityException("Parsing KmsEnvelopeAeadKeyFormat failed: ", e); 128 } 129 } 130 131 @AccessesPartialKey serializeKey( LegacyKmsEnvelopeAeadKey key, @Nullable SecretKeyAccess access)132 private static ProtoKeySerialization serializeKey( 133 LegacyKmsEnvelopeAeadKey key, @Nullable SecretKeyAccess access) 134 throws GeneralSecurityException { 135 return ProtoKeySerialization.create( 136 TYPE_URL, 137 KmsEnvelopeAeadKey.newBuilder() 138 .setParams(serializeParametersToKmsEnvelopeAeadKeyFormat(key.getParameters())) 139 .build() 140 .toByteString(), 141 KeyMaterialType.REMOTE, 142 toProtoOutputPrefixType(key.getParameters().getVariant()), 143 key.getIdRequirementOrNull()); 144 } 145 146 @AccessesPartialKey parseParameters( ProtoParametersSerialization serialization)147 private static LegacyKmsEnvelopeAeadParameters parseParameters( 148 ProtoParametersSerialization serialization) throws GeneralSecurityException { 149 if (!serialization.getKeyTemplate().getTypeUrl().equals(TYPE_URL)) { 150 throw new IllegalArgumentException( 151 "Wrong type URL in call to LegacyKmsEnvelopeAeadProtoSerialization.parseParameters: " 152 + serialization.getKeyTemplate().getTypeUrl()); 153 } 154 KmsEnvelopeAeadKeyFormat format; 155 try { 156 format = 157 KmsEnvelopeAeadKeyFormat.parseFrom( 158 serialization.getKeyTemplate().getValue(), ExtensionRegistryLite.getEmptyRegistry()); 159 } catch (InvalidProtocolBufferException e) { 160 throw new GeneralSecurityException("Parsing KmsEnvelopeAeadKeyFormat failed: ", e); 161 } 162 return parseParameters(format, serialization.getKeyTemplate().getOutputPrefixType()); 163 } 164 165 @AccessesPartialKey parseParameters( KmsEnvelopeAeadKeyFormat format, OutputPrefixType outputPrefixType)166 private static LegacyKmsEnvelopeAeadParameters parseParameters( 167 KmsEnvelopeAeadKeyFormat format, OutputPrefixType outputPrefixType) 168 throws GeneralSecurityException { 169 Parameters aeadParameters = 170 TinkProtoParametersFormat.parse( 171 KeyTemplate.newBuilder() 172 .setTypeUrl(format.getDekTemplate().getTypeUrl()) 173 .setValue(format.getDekTemplate().getValue()) 174 .setOutputPrefixType(OutputPrefixType.RAW) 175 .build() 176 .toByteArray()); 177 178 @Nullable LegacyKmsEnvelopeAeadParameters.DekParsingStrategy strategy; 179 180 if (aeadParameters instanceof AesGcmParameters) { 181 strategy = LegacyKmsEnvelopeAeadParameters.DekParsingStrategy.ASSUME_AES_GCM; 182 } else if (aeadParameters instanceof ChaCha20Poly1305Parameters) { 183 strategy = LegacyKmsEnvelopeAeadParameters.DekParsingStrategy.ASSUME_CHACHA20POLY1305; 184 } else if (aeadParameters instanceof XChaCha20Poly1305Parameters) { 185 strategy = LegacyKmsEnvelopeAeadParameters.DekParsingStrategy.ASSUME_XCHACHA20POLY1305; 186 } else if (aeadParameters instanceof AesCtrHmacAeadParameters) { 187 strategy = LegacyKmsEnvelopeAeadParameters.DekParsingStrategy.ASSUME_AES_CTR_HMAC; 188 } else if (aeadParameters instanceof AesEaxParameters) { 189 strategy = LegacyKmsEnvelopeAeadParameters.DekParsingStrategy.ASSUME_AES_EAX; 190 } else if (aeadParameters instanceof AesGcmSivParameters) { 191 strategy = LegacyKmsEnvelopeAeadParameters.DekParsingStrategy.ASSUME_AES_GCM_SIV; 192 } else { 193 throw new GeneralSecurityException( 194 "Unsupported DEK parameters when parsing " + aeadParameters); 195 } 196 return LegacyKmsEnvelopeAeadParameters.builder() 197 .setVariant(toVariant(outputPrefixType)) 198 .setKekUri(format.getKekUri()) 199 .setDekParametersForNewKeys((AeadParameters) aeadParameters) 200 .setDekParsingStrategy(strategy) 201 .build(); 202 } 203 204 @AccessesPartialKey parseKey( ProtoKeySerialization serialization, @Nullable SecretKeyAccess access)205 private static LegacyKmsEnvelopeAeadKey parseKey( 206 ProtoKeySerialization serialization, @Nullable SecretKeyAccess access) 207 throws GeneralSecurityException { 208 if (!serialization.getTypeUrl().equals(TYPE_URL)) { 209 throw new IllegalArgumentException( 210 "Wrong type URL in call to LegacyKmsEnvelopeAeadProtoSerialization.parseKey"); 211 } 212 try { 213 KmsEnvelopeAeadKey protoKey = 214 KmsEnvelopeAeadKey.parseFrom( 215 serialization.getValue(), ExtensionRegistryLite.getEmptyRegistry()); 216 if (protoKey.getVersion() != 0) { 217 throw new GeneralSecurityException( 218 "KmsEnvelopeAeadKeys are only accepted with version 0, got " + protoKey); 219 } 220 221 LegacyKmsEnvelopeAeadParameters parameters = 222 parseParameters(protoKey.getParams(), serialization.getOutputPrefixType()); 223 return LegacyKmsEnvelopeAeadKey.create(parameters, serialization.getIdRequirementOrNull()); 224 } catch (InvalidProtocolBufferException e) { 225 throw new GeneralSecurityException("Parsing KmsEnvelopeAeadKey failed: ", e); 226 } 227 } 228 register()229 public static void register() throws GeneralSecurityException { 230 register(MutableSerializationRegistry.globalInstance()); 231 } 232 register(MutableSerializationRegistry registry)233 public static void register(MutableSerializationRegistry registry) 234 throws GeneralSecurityException { 235 registry.registerParametersSerializer(PARAMETERS_SERIALIZER); 236 registry.registerParametersParser(PARAMETERS_PARSER); 237 registry.registerKeySerializer(KEY_SERIALIZER); 238 registry.registerKeyParser(KEY_PARSER); 239 } 240 LegacyKmsEnvelopeAeadProtoSerialization()241 private LegacyKmsEnvelopeAeadProtoSerialization() {} 242 } 243