1 // Copyright 2022 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 java.util.concurrent.TimeUnit.DAYS; 21 import static org.junit.Assert.assertNotNull; 22 23 import com.google.crypto.tink.proto.KeyData; 24 import com.google.protobuf.ByteString; 25 import java.security.GeneralSecurityException; 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.Executors; 30 import java.util.concurrent.Future; 31 import org.junit.Test; 32 import org.junit.runner.RunWith; 33 import org.junit.runners.JUnit4; 34 35 /** Thread safety tests for {@link Registry}. */ 36 @RunWith(JUnit4.class) 37 public final class RegistryMultithreadTest { 38 private static class TestKeyManager implements KeyManager<Aead> { TestKeyManager(String typeUrl)39 public TestKeyManager(String typeUrl) { 40 this.typeUrl = typeUrl; 41 } 42 43 private final String typeUrl; 44 45 @Override getPrimitive(ByteString serializedKey)46 public Aead getPrimitive(ByteString serializedKey) throws GeneralSecurityException { 47 throw new UnsupportedOperationException("Not needed for test"); 48 } 49 50 @Override newKeyData(ByteString serializedKeyFormat)51 public KeyData newKeyData(ByteString serializedKeyFormat) throws GeneralSecurityException { 52 throw new UnsupportedOperationException("Not needed for test"); 53 } 54 55 @Override getKeyType()56 public String getKeyType() { 57 return this.typeUrl; 58 } 59 60 @Override getPrimitiveClass()61 public Class<Aead> getPrimitiveClass() { 62 return Aead.class; 63 } 64 } 65 66 private static final int REPETITIONS = 200; 67 68 @Test registerAndGetKeyManager_works()69 public void registerAndGetKeyManager_works() throws Exception { 70 ExecutorService threadPool = Executors.newFixedThreadPool(4); 71 List<Future<?>> futures = new ArrayList<>(); 72 Registry.registerKeyManager(new TestKeyManager("KeyManagerStart"), false); 73 futures.add( 74 threadPool.submit( 75 () -> { 76 try { 77 for (int i = 0; i < REPETITIONS; ++i) { 78 Registry.registerKeyManager(new TestKeyManager("KeyManager" + i), false); 79 } 80 } catch (GeneralSecurityException e) { 81 throw new RuntimeException(e); 82 } 83 })); 84 futures.add( 85 threadPool.submit( 86 () -> { 87 try { 88 for (int i = 0; i < REPETITIONS; ++i) { 89 assertNotNull(Registry.getUntypedKeyManager("KeyManagerStart")); 90 } 91 } catch (GeneralSecurityException e) { 92 throw new RuntimeException(e); 93 } 94 })); 95 96 threadPool.shutdown(); 97 // Wait forever: if the test times out we will notice independently. 98 assertThat(threadPool.awaitTermination(1, DAYS)).isTrue(); 99 for (int i = 0; i < futures.size(); ++i) { 100 futures.get(i).get(); // This will throw an exception if the thread threw an exception. 101 } 102 } 103 104 // TODO(tholenst): Epxand the test coverage for primitive wrappers and catalogues. 105 } 106