1 // Copyright 2021 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.hybrid.internal; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.crypto.tink.HybridDecrypt; 23 import com.google.crypto.tink.HybridEncrypt; 24 import com.google.crypto.tink.proto.HpkeAead; 25 import com.google.crypto.tink.proto.HpkeKdf; 26 import com.google.crypto.tink.proto.HpkeKem; 27 import com.google.crypto.tink.proto.HpkeParams; 28 import com.google.crypto.tink.proto.HpkePrivateKey; 29 import com.google.crypto.tink.proto.HpkePublicKey; 30 import com.google.crypto.tink.proto.KeyData.KeyMaterialType; 31 import com.google.crypto.tink.subtle.Random; 32 import com.google.crypto.tink.subtle.X25519; 33 import com.google.protobuf.ByteString; 34 import java.security.GeneralSecurityException; 35 import org.junit.Before; 36 import org.junit.BeforeClass; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 import org.junit.runners.JUnit4; 40 41 /** Unit tests for {@link HpkePublicKeyManager}. */ 42 @RunWith(JUnit4.class) 43 public final class HpkePublicKeyManagerTest { 44 private static byte[] privateKeyBytes; 45 private static byte[] publicKeyBytes; 46 47 private HpkePublicKeyManager keyManager; 48 49 @BeforeClass generateKeyMaterial()50 public static void generateKeyMaterial() throws GeneralSecurityException { 51 privateKeyBytes = X25519.generatePrivateKey(); 52 publicKeyBytes = X25519.publicFromPrivate(privateKeyBytes); 53 } 54 55 @Before setUp()56 public void setUp() { 57 keyManager = new HpkePublicKeyManager(); 58 } 59 createValidHpkeParams()60 private HpkeParams createValidHpkeParams() { 61 return HpkeParams.newBuilder() 62 .setKem(HpkeKem.DHKEM_X25519_HKDF_SHA256) 63 .setKdf(HpkeKdf.HKDF_SHA256) 64 .setAead(HpkeAead.AES_256_GCM) 65 .build(); 66 } 67 createHpkePublicKey(HpkeParams params)68 private HpkePublicKey createHpkePublicKey(HpkeParams params) { 69 return HpkePublicKey.newBuilder() 70 .setVersion(0) 71 .setPublicKey(ByteString.copyFrom(publicKeyBytes)) 72 .setParams(params) 73 .build(); 74 } 75 createValidHpkePublicKey()76 private HpkePublicKey createValidHpkePublicKey() { 77 return createHpkePublicKey(createValidHpkeParams()); 78 } 79 80 @Test basics()81 public void basics() throws Exception { 82 assertThat(keyManager.getKeyType()) 83 .isEqualTo("type.googleapis.com/google.crypto.tink.HpkePublicKey"); 84 assertThat(keyManager.getVersion()).isEqualTo(0); 85 assertThat(keyManager.keyMaterialType()).isEqualTo(KeyMaterialType.ASYMMETRIC_PUBLIC); 86 } 87 88 @Test validateKey_succeedsWithValidKey()89 public void validateKey_succeedsWithValidKey() throws Exception { 90 keyManager.validateKey(createValidHpkePublicKey()); 91 } 92 93 @Test validateKey_failsWithEmptyKey()94 public void validateKey_failsWithEmptyKey() throws Exception { 95 assertThrows( 96 GeneralSecurityException.class, 97 () -> keyManager.validateKey(HpkePublicKey.getDefaultInstance())); 98 } 99 100 @Test validateKey_failsWithWrongVersion()101 public void validateKey_failsWithWrongVersion() throws Exception { 102 HpkePublicKey wrongVersionKey = 103 HpkePublicKey.newBuilder(createValidHpkePublicKey()).setVersion(1).build(); 104 assertThrows(GeneralSecurityException.class, () -> keyManager.validateKey(wrongVersionKey)); 105 } 106 107 @Test validateKey_failsWithMissingParams()108 public void validateKey_failsWithMissingParams() throws Exception { 109 HpkePublicKey missingParamsKey = 110 HpkePublicKey.newBuilder(createValidHpkePublicKey()).clearParams().build(); 111 assertThrows(GeneralSecurityException.class, () -> keyManager.validateKey(missingParamsKey)); 112 } 113 114 @Test validateKey_failsWithInvalidKem()115 public void validateKey_failsWithInvalidKem() throws Exception { 116 HpkeParams invalidKemParams = 117 HpkeParams.newBuilder() 118 .setKem(HpkeKem.KEM_UNKNOWN) 119 .setKdf(HpkeKdf.HKDF_SHA256) 120 .setAead(HpkeAead.AES_256_GCM) 121 .build(); 122 assertThrows( 123 GeneralSecurityException.class, 124 () -> keyManager.validateKey(createHpkePublicKey(invalidKemParams))); 125 } 126 127 @Test validateKey_failsWithInvalidKdf()128 public void validateKey_failsWithInvalidKdf() throws Exception { 129 HpkeParams invalidKdfParams = 130 HpkeParams.newBuilder() 131 .setKem(HpkeKem.DHKEM_X25519_HKDF_SHA256) 132 .setKdf(HpkeKdf.KDF_UNKNOWN) 133 .setAead(HpkeAead.AES_256_GCM) 134 .build(); 135 assertThrows( 136 GeneralSecurityException.class, 137 () -> keyManager.validateKey(createHpkePublicKey(invalidKdfParams))); 138 } 139 140 @Test validateKey_failsWithInvalidAead()141 public void validateKey_failsWithInvalidAead() throws Exception { 142 HpkeParams invalidAeadParams = 143 HpkeParams.newBuilder() 144 .setKem(HpkeKem.DHKEM_X25519_HKDF_SHA256) 145 .setKdf(HpkeKdf.HKDF_SHA256) 146 .setAead(HpkeAead.AEAD_UNKNOWN) 147 .build(); 148 assertThrows( 149 GeneralSecurityException.class, 150 () -> keyManager.validateKey(createHpkePublicKey(invalidAeadParams))); 151 } 152 153 @Test createPrimitive()154 public void createPrimitive() throws Exception { 155 HpkePublicKey publicKey = createValidHpkePublicKey(); 156 HpkePrivateKey privateKey = 157 HpkePrivateKey.newBuilder() 158 .setVersion(keyManager.getVersion()) 159 .setPublicKey(publicKey) 160 .setPrivateKey(ByteString.copyFrom(privateKeyBytes)) 161 .build(); 162 HybridEncrypt hybridEncrypt = keyManager.getPrimitive(publicKey, HybridEncrypt.class); 163 HybridDecrypt hybridDecrypt = HpkeDecrypt.createHpkeDecrypt(privateKey); 164 165 byte[] input = Random.randBytes(200); 166 byte[] contextInfo = Random.randBytes(100); 167 byte[] ciphertext = hybridEncrypt.encrypt(input, contextInfo); 168 byte[] plaintext = hybridDecrypt.decrypt(ciphertext, contextInfo); 169 170 assertThat(plaintext).isEqualTo(input); 171 } 172 } 173