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; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.crypto.tink.aead.AeadConfig; 23 import com.google.crypto.tink.aead.AesGcmParameters; 24 import com.google.crypto.tink.aead.PredefinedAeadParameters; 25 import com.google.crypto.tink.internal.LegacyProtoParameters; 26 import com.google.crypto.tink.internal.MutableSerializationRegistry; 27 import com.google.crypto.tink.internal.ParametersSerializer; 28 import com.google.crypto.tink.internal.ProtoParametersSerialization; 29 import com.google.crypto.tink.proto.AesGcmKeyFormat; 30 import com.google.crypto.tink.proto.OutputPrefixType; 31 import com.google.protobuf.ByteString; 32 import java.security.GeneralSecurityException; 33 import org.junit.BeforeClass; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 import org.junit.runners.JUnit4; 37 38 @RunWith(JUnit4.class) 39 public final class KeyTemplateTest { 40 private static final String AES_GCM_TYPE_URL = "type.googleapis.com/google.crypto.tink.AesGcmKey"; 41 42 @BeforeClass setUpClass()43 public static void setUpClass() throws Exception { 44 AeadConfig.register(); 45 } 46 47 @Test testToParameters_aesGcm_works()48 public void testToParameters_aesGcm_works() throws Exception { 49 AesGcmKeyFormat format = AesGcmKeyFormat.newBuilder().setKeySize(16).build(); 50 51 assertThat( 52 KeyTemplate.create( 53 AES_GCM_TYPE_URL, format.toByteArray(), KeyTemplate.OutputPrefixType.RAW) 54 .toParameters()) 55 .isEqualTo( 56 AesGcmParameters.builder() 57 .setKeySizeBytes(16) 58 .setIvSizeBytes(12) 59 .setTagSizeBytes(16) 60 .setVariant(AesGcmParameters.Variant.NO_PREFIX) 61 .build()); 62 63 assertThat( 64 KeyTemplate.create( 65 AES_GCM_TYPE_URL, format.toByteArray(), KeyTemplate.OutputPrefixType.TINK) 66 .toParameters()) 67 .isEqualTo( 68 AesGcmParameters.builder() 69 .setKeySizeBytes(16) 70 .setIvSizeBytes(12) 71 .setTagSizeBytes(16) 72 .setVariant(AesGcmParameters.Variant.TINK) 73 .build()); 74 } 75 76 @Test testToParameters_unParseableAesGcm_fails()77 public void testToParameters_unParseableAesGcm_fails() throws Exception { 78 // Invalid Key size 79 AesGcmKeyFormat format = AesGcmKeyFormat.newBuilder().setKeySize(17).build(); 80 KeyTemplate template = 81 KeyTemplate.create( 82 AES_GCM_TYPE_URL, format.toByteArray(), KeyTemplate.OutputPrefixType.RAW); 83 assertThrows(GeneralSecurityException.class, template::toParameters); 84 } 85 86 @Test testToParameters_notRegisteredTypeUrl_givesLegacy()87 public void testToParameters_notRegisteredTypeUrl_givesLegacy() throws Exception { 88 Parameters p = 89 KeyTemplate.create("nonexistenttypeurl", new byte[] {1}, KeyTemplate.OutputPrefixType.TINK) 90 .toParameters(); 91 assertThat(p).isInstanceOf(LegacyProtoParameters.class); 92 LegacyProtoParameters parameters = (LegacyProtoParameters) p; 93 assertThat(parameters.getSerialization().getKeyTemplate().getTypeUrl()) 94 .isEqualTo("nonexistenttypeurl"); 95 assertThat(parameters.getSerialization().getKeyTemplate().getValue()) 96 .isEqualTo(ByteString.copyFrom(new byte[] {1})); 97 assertThat(parameters.getSerialization().getKeyTemplate().getOutputPrefixType()) 98 .isEqualTo(OutputPrefixType.TINK); 99 } 100 101 @Test testCreateFromParameters_works()102 public void testCreateFromParameters_works() throws Exception { 103 assertThat(KeyTemplate.createFrom(PredefinedAeadParameters.AES128_GCM).toParameters()) 104 .isEqualTo(PredefinedAeadParameters.AES128_GCM); 105 } 106 107 @Test testCreateFromParameters_unparseable_throws()108 public void testCreateFromParameters_unparseable_throws() throws Exception { 109 Parameters p = 110 new Parameters() { 111 @Override 112 public boolean hasIdRequirement() { 113 return false; 114 } 115 }; 116 KeyTemplate t = KeyTemplate.createFrom(p); 117 assertThrows(RuntimeException.class, () -> t.getTypeUrl()); 118 } 119 120 private static class ParametersSubclass extends Parameters { ParametersSubclass()121 ParametersSubclass() {} 122 123 @Override hasIdRequirement()124 public boolean hasIdRequirement() { 125 return false; 126 } 127 } 128 129 private static ParametersSerializer<ParametersSubclass, ProtoParametersSerialization> 130 PARAMETERS_SUBCLASS_SERIALIZER = 131 ParametersSerializer.create( 132 (ParametersSubclass p) -> 133 ProtoParametersSerialization.create( 134 "sometypeurl", OutputPrefixType.RAW, AesGcmKeyFormat.getDefaultInstance()), 135 ParametersSubclass.class, 136 ProtoParametersSerialization.class); 137 138 @Test testCreateFromParameters_unserializableAtCreationButLaterYes_works()139 public void testCreateFromParameters_unserializableAtCreationButLaterYes_works() 140 throws Exception { 141 Parameters p = new ParametersSubclass(); 142 KeyTemplate t = KeyTemplate.createFrom(p); 143 // We only do this in this test, and never use it elsewhere -- hence this global state does not 144 // break anything. 145 MutableSerializationRegistry.globalInstance() 146 .registerParametersSerializer(PARAMETERS_SUBCLASS_SERIALIZER); 147 assertThat(t.getTypeUrl()).isEqualTo("sometypeurl"); 148 } 149 } 150