• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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.subtle;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertThrows;
22 
23 import com.google.crypto.tink.KeyWrap;
24 import com.google.crypto.tink.testing.TestUtil;
25 import com.google.crypto.tink.testing.WycheproofTestUtil;
26 import com.google.gson.JsonArray;
27 import com.google.gson.JsonObject;
28 import java.security.GeneralSecurityException;
29 import java.util.Set;
30 import java.util.TreeSet;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.JUnit4;
34 
35 /** Unit tests for {@link Kwp}. */
36 @RunWith(JUnit4.class)
37 public class KwpTest {
38 
39   @Test
testWrapUnwrapMsgSizes()40   public void testWrapUnwrapMsgSizes() throws Exception {
41     byte[] wrapKey = Random.randBytes(16);
42     KeyWrap wrapper = new Kwp(wrapKey);
43     for (int wrappedSize = 16; wrappedSize < 128; wrappedSize++) {
44       byte[] keyMaterialToWrap = Random.randBytes(wrappedSize);
45       byte[] wrapped = wrapper.wrap(keyMaterialToWrap);
46       byte[] unwrapped = wrapper.unwrap(wrapped);
47       assertArrayEquals(keyMaterialToWrap, unwrapped);
48     }
49   }
50 
51   @Test
testInvalidKeySizes()52   public void testInvalidKeySizes() throws Exception {
53     // Tests the wrapping key. Its key size is either 16 or 32.
54     for (int j = 0; j < 255; j++) {
55       final int i = j;
56       if (i == 16 || i == 32) {
57         continue;
58       }
59       assertThrows(
60           GeneralSecurityException.class,
61           () -> {
62             KeyWrap unused = new Kwp(new byte[i]);
63           });
64     }
65   }
66 
67   @Test
testInvalidWrappingSizes()68   public void testInvalidWrappingSizes() throws Exception {
69     byte[] wrapKey = Random.randBytes(16);
70     KeyWrap wrapper = new Kwp(wrapKey);
71     for (int i = 0; i < 16; i++) {
72       final int wrappedSize = i;
73       assertThrows(GeneralSecurityException.class, () -> wrapper.wrap(new byte[wrappedSize]));
74     }
75   }
76 
77   @Test
testWycheproof()78   public void testWycheproof() throws Exception {
79     final String expectedVersion = "0.6";
80     JsonObject json =
81         WycheproofTestUtil.readJson("../wycheproof/testvectors/kwp_test.json");
82     Set<String> exceptions = new TreeSet<String>();
83     String generatorVersion = json.get("generatorVersion").getAsString();
84     if (!generatorVersion.equals(expectedVersion)) {
85       System.out.printf("Expecting test vectors with version %s found version %s.\n",
86                         expectedVersion, generatorVersion);
87     }
88     int errors = 0;
89     JsonArray testGroups = json.getAsJsonArray("testGroups");
90     for (int i = 0; i < testGroups.size(); i++) {
91       JsonObject group = testGroups.get(i).getAsJsonObject();
92       JsonArray tests = group.getAsJsonArray("tests");
93       for (int j = 0; j < tests.size(); j++) {
94         JsonObject testcase = tests.get(j).getAsJsonObject();
95         int tcid = testcase.get("tcId").getAsInt();
96         String tc = "tcId: " + tcid + " " + testcase.get("comment").getAsString();
97         byte[] key = Hex.decode(testcase.get("key").getAsString());
98         byte[] data = Hex.decode(testcase.get("msg").getAsString());
99         byte[] expected = Hex.decode(testcase.get("ct").getAsString());
100         // Result is one of "valid", "invalid", "acceptable".
101         // "valid" are test vectors with matching plaintext, ciphertext and tag.
102         // "invalid" are test vectors with invalid parameters or invalid ciphertext and tag.
103         // "acceptable" are test vectors with weak parameters or legacy formats.
104         String result = testcase.get("result").getAsString();
105 
106         // Test wrapping
107         KeyWrap wrapper;
108         try {
109           wrapper = new Kwp(key);
110         } catch (GeneralSecurityException ex) {
111           // tink restrict the key sizes to 128 or 256 bits.
112           if (key.length == 16 || key.length == 32) {
113             System.out.printf("Rejected valid key:%s\n", tc);
114             System.out.println(ex.toString());
115             errors++;
116           }
117           continue;
118         }
119         try {
120           byte[] wrapped = wrapper.wrap(data);
121           boolean eq = TestUtil.arrayEquals(expected, wrapped);
122           if (result.equals("invalid")) {
123             if (eq) {
124               // Some test vectors use invalid parameters that should be rejected.
125               System.out.printf("Wrapped test case:%s\n", tc);
126               errors++;
127             }
128           } else {
129             if (!eq) {
130               System.out.printf("Incorrect wrapping for test case:%s wrapped bytes:%s\n",
131                                 tc, Hex.encode(wrapped));
132               errors++;
133             }
134           }
135         } catch (GeneralSecurityException ex) {
136           if (result.equals("valid")) {
137             System.out.printf("Failed to wrap test case:%s\n", tc);
138             errors++;
139           }
140         } catch (Exception ex) {
141           // Other exceptions are violating the interface.
142           System.out.printf("Test case %s throws %s.\n", tc, ex);
143           errors++;
144         }
145 
146         // Test unwrapping
147         // The algorithms tested in this class are typically malleable. Hence, it is in possible
148         // that modifying ciphertext randomly results in some other valid ciphertext.
149         // However, all the test vectors in Wycheproof are constructed such that they have
150         // invalid padding. If this changes then the test below is too strict.
151         try {
152           byte[] unwrapped = wrapper.unwrap(expected);
153           boolean eq = TestUtil.arrayEquals(data, unwrapped);
154           if (result.equals("invalid")) {
155             System.out.printf("Unwrapped invalid test case:%s unwrapped:%s\n", tc,
156                               Hex.encode(unwrapped));
157             errors++;
158           } else {
159             if (!eq) {
160               System.out.printf("Incorrect unwrap. Excepted:%s actual:%s\n",
161                                 Hex.encode(data), Hex.encode(unwrapped));
162               errors++;
163             }
164           }
165         } catch (GeneralSecurityException ex) {
166           // Trying to unwrap an invalid key should always result in a GeneralSecurityException
167           // or a subclass of it.
168           exceptions.add(ex.toString());
169           if (result.equals("valid")) {
170             System.out.printf("Failed to unwrap:%s\n", tc);
171             errors++;
172           }
173         } catch (Exception ex) {
174           // Other exceptions indicate a programming error.
175           System.out.printf("Test case:%s throws %s\n", tc, ex);
176           exceptions.add(ex.toString());
177           errors++;
178         }
179       }
180     }
181     // Even though strong pseudorandomness implies that information about incorrectly formatted
182     // ciphertexts is not helpful to an attacker, we still don't want to do this and expect
183     // exceptions that do not carry information about the unwrapped data.
184     System.out.printf("Number of distinct exceptions:%d\n", exceptions.size());
185     for (String ex : exceptions) {
186       System.out.println(ex);
187     }
188     assertEquals(0, errors);
189   }
190 }
191