| // Copyright 2022 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package com.google.crypto.tink.internal; |
| |
| import static com.google.common.truth.Truth.assertWithMessage; |
| |
| import com.google.crypto.tink.Key; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Helps check correctness of implementations of "equalsKey". |
| * |
| * <p>This is similar to the Guava class {@code EqualsTester}, but uses {@code equalsKey} instead of |
| * {@code equals}, and tests additional properties which are useful for keys only. |
| * |
| * <p>Usage example: |
| * |
| * <pre> |
| * new KeyTester() |
| * .addEqualityGroup("128 bit AES Gcm Key", fixed128bitAesGcmKey(), fixed128bitAesGcmKey()) |
| * .addEqualityGroup("256 bit AES Gcm Key", fixed256bitAesGcmKey(), fixed256bitAesGcmKey()) |
| * .doTests(); |
| * </pre> |
| * |
| * <p>Compared to the Guava {@code EqualsTester}, the user has to specify a name for each group |
| * because keys cannot have a useful {@code toString} method. |
| * |
| * <p>This API is currently internal. If needed, we can consider adding it to the public API after |
| * we have some experience with it. |
| */ |
| public final class KeyTester { |
| private final Map<String, List<Key>> equivalenceGroups = new HashMap<>(); |
| |
| public KeyTester addEqualityGroup(String name, Key... keys) { |
| if (equivalenceGroups.containsKey(name)) { |
| throw new AssertionError("Group with name " + name + " already present"); |
| } |
| equivalenceGroups.put(name, Arrays.asList(keys)); |
| return this; |
| } |
| |
| private static void testSingleKey(String keyIdentifier, Key key) { |
| if (key.getParameters().hasIdRequirement()) { |
| assertWithMessage( |
| keyIdentifier |
| + " has parameters with IdRequirement, but getIdRequirementOrNull returns null") |
| .that(key.getIdRequirementOrNull()) |
| .isNotNull(); |
| } else { |
| assertWithMessage( |
| keyIdentifier |
| + " has parameters without IdRequirement, but getIdRequirementOrNull returns a" |
| + " non-null value") |
| .that(key.getIdRequirementOrNull()) |
| .isNull(); |
| } |
| } |
| |
| private static void testSameGroupKeys(String keysIdentifier, Key key1, Key key2) { |
| assertWithMessage(keysIdentifier + " are not equal: ").that(key1.equalsKey(key2)).isTrue(); |
| assertWithMessage(keysIdentifier + " have different ID requirements: ") |
| .that(key1.getIdRequirementOrNull()) |
| .isEqualTo(key2.getIdRequirementOrNull()); |
| assertWithMessage(keysIdentifier + " have different parameters: ") |
| .that(key1.getParameters()) |
| .isEqualTo(key2.getParameters()); |
| assertWithMessage(keysIdentifier + " have parameters with different hashCode values: ") |
| .that(key1.getParameters().hashCode()) |
| .isEqualTo(key2.getParameters().hashCode()); |
| } |
| |
| private static void testDifferentGroupKeys( |
| String keyIdentifier1, String keyIdentifier2, Key key1, Key key2) { |
| assertWithMessage( |
| keyIdentifier1 |
| + "and" |
| + keyIdentifier2 |
| + " are from different equality groups, but equalsKey returns true") |
| .that(key1.equalsKey(key2)) |
| .isFalse(); |
| } |
| |
| /** |
| * Runs tests on the given equality groups. |
| * |
| * <p>This tests the following properties on the previously configured equality groups: |
| * |
| * <ul> |
| * <li>For each individual key {@code k}, we check consistency of {@code getIdRequirementOrNull} |
| * and {@code k.getParameters().hasIdRequirement()}. |
| * <li>For each pair {@code k1, k2} of keys in the same group, we check that {@code |
| * k1.equalsKey(k2)}, that {@code k1.getParameters().equals(k2.getParameters())}, that |
| * {@code k1.getParameters().hashCode() == k2.getParameters().hashCode()}, and that {@code |
| * Object.equals(k1.getIdRequirementOrNull(), k2.getIdRequirementOrNull())}. |
| * <li>For each pair {@code k1, k2} of keys in different groups, we check that {@code |
| * k1.equalsKey(k2)} is false. |
| * </ul> |
| */ |
| public void doTests() { |
| for (Map.Entry<String, List<Key>> group : equivalenceGroups.entrySet()) { |
| for (int i = 0; i < group.getValue().size(); ++i) { |
| testSingleKey("Key #" + i + " from group " + group.getKey(), group.getValue().get(i)); |
| } |
| } |
| |
| for (Map.Entry<String, List<Key>> group1 : equivalenceGroups.entrySet()) { |
| for (Map.Entry<String, List<Key>> group2 : equivalenceGroups.entrySet()) { |
| for (int i1 = 0; i1 < group1.getValue().size(); ++i1) { |
| for (int i2 = 0; i2 < group2.getValue().size(); ++i2) { |
| String group1Name = group1.getKey(); |
| String group2Name = group2.getKey(); |
| Key key1 = group1.getValue().get(i1); |
| Key key2 = group2.getValue().get(i2); |
| if (group1Name.equals(group2Name)) { |
| String keysIdentifier = |
| "Keys #" + i1 + " and #" + i2 + " from group '" + group1Name + "'"; |
| testSameGroupKeys(keysIdentifier, key1, key2); |
| } else { |
| String keysIdentifier1 = "Key #" + i1 + " from group '" + group1.getKey() + "'"; |
| String keysIdentifier2 = "Key #" + i2 + " from group '" + group2.getKey() + "'"; |
| testDifferentGroupKeys(keysIdentifier1, keysIdentifier2, key1, key2); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |