1 /*
  2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 8171277 8206915
 27  * @summary Test XDH key agreement
 28  * @library /test/lib
 29  * @build jdk.test.lib.Convert
 30  * @run main TestXDH
 31  */
 32 
 33 import java.security.*;
 34 import java.security.spec.*;
 35 import javax.crypto.*;
 36 import java.util.Arrays;
 37 import java.math.BigInteger;
 38 import jdk.test.lib.Convert;
 39 
 40 public class TestXDH {
 41 
 42     public static void main(String[] args) throws Exception {
 43 
 44         runBasicTests();
 45         runKAT();
 46         runSmallOrderTest();
 47         runNonCanonicalTest();
 48         runCurveMixTest();
 49     }
 50 
 51     private static void runBasicTests() throws Exception {
 52         runBasicTest("XDH", null);
 53         runBasicTest("XDH", 255);
 54         runBasicTest("XDH", 448);
 55         runBasicTest("XDH", "X25519");
 56         runBasicTest("XDH", "X448");
 57         runBasicTest("X25519", null);
 58         runBasicTest("X448", null);
 59         runBasicTest("1.3.101.110", null);
 60         runBasicTest("1.3.101.111", null);
 61         runBasicTest("OID.1.3.101.110", null);
 62         runBasicTest("OID.1.3.101.111", null);
 63     }
 64 
 65     private static void runBasicTest(String name, Object param)
 66         throws Exception {
 67 
 68         KeyPairGenerator kpg = KeyPairGenerator.getInstance(name);
 69         AlgorithmParameterSpec paramSpec = null;
 70         if (param instanceof Integer) {
 71             kpg.initialize((Integer) param);
 72         } else if (param instanceof String) {
 73             paramSpec = new NamedParameterSpec((String) param);
 74             kpg.initialize(paramSpec);
 75         }
 76         KeyPair kp = kpg.generateKeyPair();
 77 
 78         KeyAgreement ka = KeyAgreement.getInstance(name);
 79         ka.init(kp.getPrivate(), paramSpec);
 80         ka.doPhase(kp.getPublic(), true);
 81 
 82         byte[] secret = ka.generateSecret();
 83 
 84         KeyFactory kf = KeyFactory.getInstance(name);
 85         // Test with X509 and PKCS8 key specs
 86         X509EncodedKeySpec pubSpec =
 87             kf.getKeySpec(kp.getPublic(), X509EncodedKeySpec.class);
 88         PKCS8EncodedKeySpec priSpec =
 89             kf.getKeySpec(kp.getPrivate(), PKCS8EncodedKeySpec.class);
 90 
 91         PublicKey pubKey = kf.generatePublic(pubSpec);
 92         PrivateKey priKey = kf.generatePrivate(priSpec);
 93 
 94         ka.init(priKey);
 95         ka.doPhase(pubKey, true);
 96         byte[] secret2 = ka.generateSecret();
 97         if (!Arrays.equals(secret, secret2)) {
 98             throw new RuntimeException("Arrays not equal");
 99         }
100 
101         // make sure generateSecret() resets the state to after init()
102         try {
103             ka.generateSecret();
104             throw new RuntimeException("generateSecret does not reset state");
105         } catch (IllegalStateException ex) {
106             // do nothing---this is expected
107         }
108         ka.doPhase(pubKey, true);
109         ka.generateSecret();
110 
111         // test with XDH key specs
112         XECPublicKeySpec xdhPublic =
113             kf.getKeySpec(kp.getPublic(), XECPublicKeySpec.class);
114         XECPrivateKeySpec xdhPrivate =
115             kf.getKeySpec(kp.getPrivate(), XECPrivateKeySpec.class);
116         PublicKey pubKey2 = kf.generatePublic(xdhPublic);
117         PrivateKey priKey2 = kf.generatePrivate(xdhPrivate);
118         ka.init(priKey2);
119         ka.doPhase(pubKey2, true);
120         byte[] secret3 = ka.generateSecret();
121         if (!Arrays.equals(secret, secret3)) {
122             throw new RuntimeException("Arrays not equal");
123         }
124     }
125 
126     private static void runSmallOrderTest() throws Exception {
127         // Ensure that small-order points are rejected
128 
129         // X25519
130         // 0
131         testSmallOrder(
132             "X25519",
133             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
134             "0000000000000000000000000000000000000000000000000000000000000000",
135             "0000000000000000000000000000000000000000000000000000000000000000");
136         // 1 and -1
137         testSmallOrder(
138             "X25519",
139             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
140             "0100000000000000000000000000000000000000000000000000000000000000",
141             "0000000000000000000000000000000000000000000000000000000000000000");
142         testSmallOrder(
143             "X25519",
144             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
145             "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
146             "0000000000000000000000000000000000000000000000000000000000000000");
147 
148         // order 8 points
149         testSmallOrder(
150             "X25519",
151             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
152             "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157",
153             "0000000000000000000000000000000000000000000000000000000000000000");
154         testSmallOrder(
155             "X25519",
156             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
157             "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800",
158             "0000000000000000000000000000000000000000000000000000000000000000");
159 
160         // X448
161         // 0
162         testSmallOrder(
163             "X448",
164             "9A8F4925D1519F5775CF46B04B5800D4EE9EE8BAE8BC5565D498C28DD9C9BA" +
165             "F574A9419744897391006382A6F127AB1D9AC2D8C0A598726B",
166             "00000000000000000000000000000000000000000000000000000000000000" +
167             "00000000000000000000000000000000000000000000000000",
168             "00000000000000000000000000000000000000000000000000000000000000" +
169             "00000000000000000000000000000000000000000000000000");
170         // 1 and -1
171         testSmallOrder(
172             "X448",
173             "9A8F4925D1519F5775CF46B04B5800D4EE9EE8BAE8BC5565D498C28DD9C9BA" +
174             "F574A9419744897391006382A6F127AB1D9AC2D8C0A598726B",
175             "01000000000000000000000000000000000000000000000000000000000000" +
176             "00000000000000000000000000000000000000000000000000",
177             "00000000000000000000000000000000000000000000000000000000000000" +
178             "00000000000000000000000000000000000000000000000000");
179         testSmallOrder(
180             "X448",
181             "9A8F4925D1519F5775CF46B04B5800D4EE9EE8BAE8BC5565D498C28DD9C9BAF" +
182             "574A9419744897391006382A6F127AB1D9AC2D8C0A598726B",
183             "fefffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff" +
184             "fffffffffffffffffffffffffffffffffffffffffffffffff",
185             "000000000000000000000000000000000000000000000000000000000000000" +
186             "0000000000000000000000000000000000000000000000000");
187     }
188 
189     private static void testSmallOrder(String name, String a_pri,
190             String b_pub, String result) throws Exception {
191 
192         try {
193             runDiffieHellmanTest(name, a_pri, b_pub, result);
194         } catch (InvalidKeyException ex) {
195             return;
196         }
197 
198         throw new RuntimeException("No exception on small-order point");
199     }
200 
201     private static void runNonCanonicalTest() throws Exception {
202         // Test non-canonical values
203 
204         // high bit of public key set
205         // X25519
206         runDiffieHellmanTest(
207             "X25519",
208             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
209             "DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B8F",
210             "954e472439316f118ae158b65619eecff9e6bcf51ab29add66f3fd088681e233");
211 
212         runDiffieHellmanTest(
213             "3030020100300706032b656e05000422042077076d0a7318a57d3c16c1725" +
214             "1b26645df4c2f87ebc0992ab177fba51db92c2a",
215             "302c300706032b656e0500032100de9edb7d7b7dc1b4d35b61c2ece435373f" +
216             "8343c85b78674dadfc7e146f882b8f",
217             "954e472439316f118ae158b65619eecff9e6bcf51ab29add66f3fd088681e233");
218 
219         // large public key
220 
221         // X25519
222         // public key value is 2^255-2
223         runDiffieHellmanTest(
224             "X25519",
225             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
226             "FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F",
227             "81a02a45014594332261085128959869fc0540c6b12380f51db4b41380de2c2c");
228 
229         runDiffieHellmanTest(
230             "3030020100300706032b656e05000422042077076d0a7318a57d3c16c17251" +
231             "b26645df4c2f87ebc0992ab177fba51db92c2a",
232             "302c300706032b656e0500032100FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
233             "FFFFFFFFFFFFFFFFFFFFFFFFFFFF7F",
234             "81a02a45014594332261085128959869fc0540c6b12380f51db4b41380de2c2c");
235 
236         // X448
237         // public key value is 2^448-2
238         runDiffieHellmanTest(
239             "X448",
240             "9A8F4925D1519F5775CF46B04B5800D4EE9EE8BAE8BC5565D498C28DD9C9BA" +
241             "F574A9419744897391006382A6F127AB1D9AC2D8C0A598726B",
242             "FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
243             "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
244             "66e2e682b1f8e68c809f1bb3e406bd826921d9c1a5bfbfcbab7ae72feecee6" +
245             "3660eabd54934f3382061d17607f581a90bdac917a064959fb");
246 
247         runDiffieHellmanTest(
248             "3048020100300706032B656F0500043A04389A8F4925D1519F5775CF46B04B" +
249             "5800D4EE9EE8BAE8BC5565D498C28DD9C9BAF574A9419744897391006382A6" +
250             "F127AB1D9AC2D8C0A598726B",
251             "3044300706032B656F0500033900FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
252             "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
253             "FFFFFFFFFFFFFFFF",
254             "66e2e682b1f8e68c809f1bb3e406bd826921d9c1a5bfbfcbab7ae72feecee6" +
255             "3660eabd54934f3382061d17607f581a90bdac917a064959fb");
256 
257     }
258 
259     private static void runKAT() throws Exception {
260         // Test both sides of the key exchange using vectors in RFC 7748
261 
262         // X25519
263         // raw
264         runDiffieHellmanTest(
265             "X25519",
266             "77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
267             "DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B4F",
268             "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
269 
270         runDiffieHellmanTest(
271             "X25519",
272             "5DAB087E624A8A4B79E17F8B83800EE66F3BB1292618B6FD1C2F8B27FF88E0EB",
273             "8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A",
274             "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
275 
276         // encoded
277         runDiffieHellmanTest(
278             "3030020100300706032B656E05000422042077076D0A7318A57D3C16C17251" +
279             "B26645DF4C2F87EBC0992AB177FBA51DB92C2A",
280             "302C300706032B656E0500032100DE9EDB7D7B7DC1B4D35B61C2ECE435373F" +
281             "8343C85B78674DADFC7E146F882B4F",
282             "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
283 
284         runDiffieHellmanTest(
285             "3030020100300706032B656E0500042204205DAB087E624A8A4B79E17F8B83" +
286             "800EE66F3BB1292618B6FD1C2F8B27FF88E0EB",
287             "302C300706032B656E05000321008520F0098930A754748B7DDCB43EF75A0D" +
288             "BF3A0D26381AF4EBA4A98EAA9B4E6A",
289             "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
290 
291         // X448
292         //raw
293         runDiffieHellmanTest(
294             "X448",
295             "9A8F4925D1519F5775CF46B04B5800D4EE9EE8BAE8BC5565D498C28DD9C9BA" +
296             "F574A9419744897391006382A6F127AB1D9AC2D8C0A598726B",
297             "3EB7A829B0CD20F5BCFC0B599B6FECCF6DA4627107BDB0D4F345B43027D8B9" +
298             "72FC3E34FB4232A13CA706DCB57AEC3DAE07BDC1C67BF33609",
299             "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b" +
300             "56fd2464c335543936521c24403085d59a449a5037514a879d");
301 
302         runDiffieHellmanTest(
303             "X448",
304             "1C306A7AC2A0E2E0990B294470CBA339E6453772B075811D8FAD0D1D6927C1" +
305             "20BB5EE8972B0D3E21374C9C921B09D1B0366F10B65173992D",
306             "9B08F7CC31B7E3E67D22D5AEA121074A273BD2B83DE09C63FAA73D2C22C5D9" +
307             "BBC836647241D953D40C5B12DA88120D53177F80E532C41FA0",
308             "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b" +
309             "56fd2464c335543936521c24403085d59a449a5037514a879d");
310 
311         //encoded
312         runDiffieHellmanTest(
313             "3048020100300706032B656F0500043A04389A8F4925D1519F5775CF46B04B" +
314             "5800D4EE9EE8BAE8BC5565D498C28DD9C9BAF574A9419744897391006382A6" +
315             "F127AB1D9AC2D8C0A598726B",
316             "3044300706032B656F05000339003EB7A829B0CD20F5BCFC0B599B6FECCF6D" +
317             "A4627107BDB0D4F345B43027D8B972FC3E34FB4232A13CA706DCB57AEC3DAE" +
318             "07BDC1C67BF33609",
319             "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b" +
320             "56fd2464c335543936521c24403085d59a449a5037514a879d");
321 
322         runDiffieHellmanTest(
323             "3048020100300706032B656F0500043A04381C306A7AC2A0E2E0990B294470" +
324             "CBA339E6453772B075811D8FAD0D1D6927C120BB5EE8972B0D3E21374C9C92" +
325             "1B09D1B0366F10B65173992D",
326             "3044300706032B656F05000339009B08F7CC31B7E3E67D22D5AEA121074A27" +
327             "3BD2B83DE09C63FAA73D2C22C5D9BBC836647241D953D40C5B12DA88120D53" +
328             "177F80E532C41FA0",
329             "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b" +
330             "56fd2464c335543936521c24403085d59a449a5037514a879d");
331     }
332 
333     private static void runDiffieHellmanTest(String a_pri,
334         String b_pub, String result) throws Exception {
335 
336         KeyFactory kf = KeyFactory.getInstance("XDH");
337         byte[] a_pri_ba = Convert.hexStringToByteArray(a_pri);
338         KeySpec privateSpec = new PKCS8EncodedKeySpec(a_pri_ba);
339         PrivateKey privateKey = kf.generatePrivate(privateSpec);
340         byte[] b_pub_ba = Convert.hexStringToByteArray(b_pub);
341         KeySpec publicSpec = new X509EncodedKeySpec(b_pub_ba);
342         PublicKey publicKey = kf.generatePublic(publicSpec);
343 
344         KeyAgreement ka = KeyAgreement.getInstance("XDH");
345         ka.init(privateKey);
346         ka.doPhase(publicKey, true);
347 
348         byte[] sharedSecret = ka.generateSecret();
349         byte[] expectedResult = Convert.hexStringToByteArray(result);
350         if (!Arrays.equals(sharedSecret, expectedResult)) {
351             throw new RuntimeException("fail: expected=" + result + ", actual="
352                 + Convert.byteArrayToHexString(sharedSecret));
353         }
354 
355     }
356 
357     private static void runDiffieHellmanTest(String curveName, String a_pri,
358         String b_pub, String result) throws Exception {
359 
360         NamedParameterSpec paramSpec = new NamedParameterSpec(curveName);
361         KeyFactory kf = KeyFactory.getInstance("XDH");
362         KeySpec privateSpec = new XECPrivateKeySpec(paramSpec,
363             Convert.hexStringToByteArray(a_pri));
364         PrivateKey privateKey = kf.generatePrivate(privateSpec);
365         boolean clearHighBit = curveName.equals("X25519");
366         KeySpec publicSpec = new XECPublicKeySpec(paramSpec,
367             Convert.hexStringToBigInteger(clearHighBit, b_pub));
368         PublicKey publicKey = kf.generatePublic(publicSpec);
369 
370         byte[] encodedPrivateKey = privateKey.getEncoded();
371         System.out.println("Encoded private: " +
372             Convert.byteArrayToHexString(encodedPrivateKey));
373         byte[] encodedPublicKey = publicKey.getEncoded();
374         System.out.println("Encoded public: " +
375             Convert.byteArrayToHexString(encodedPublicKey));
376 
377         KeyAgreement ka = KeyAgreement.getInstance("XDH");
378         ka.init(privateKey);
379         ka.doPhase(publicKey, true);
380 
381         byte[] sharedSecret = ka.generateSecret();
382         byte[] expectedResult = Convert.hexStringToByteArray(result);
383         if (!Arrays.equals(sharedSecret, expectedResult)) {
384             throw new RuntimeException("fail: expected=" + result + ", actual="
385                 + Convert.byteArrayToHexString(sharedSecret));
386         }
387     }
388 
389     /*
390      * Ensure that SunEC rejects parameters/points for the wrong curve
391      * when the algorithm ID for a specific curve is specified.
392      */
393     private static void runCurveMixTest() throws Exception {
394         runCurveMixTest("SunEC", "X25519", 448);
395         runCurveMixTest("SunEC", "X25519", "X448");
396         runCurveMixTest("SunEC", "X448", 255);
397         runCurveMixTest("SunEC", "X448", "X25519");
398     }
399 
400     private static void runCurveMixTest(String providerName, String name,
401                                         Object param) throws Exception {
402 
403         KeyPairGenerator kpg = KeyPairGenerator.getInstance(name,
404             providerName);
405 
406         try {
407             if (param instanceof Integer) {
408                 kpg.initialize((Integer) param);
409             } else if (param instanceof String) {
410                 kpg.initialize(new NamedParameterSpec((String) param));
411             }
412             throw new RuntimeException(name + " KeyPairGenerator accepted "
413                 + param.toString() + " parameters");
414         } catch (InvalidParameterException ex) {
415             // expected
416         }
417 
418         // the rest of the test uses the parameter as an algorithm name to
419         // produce keys
420         if (param instanceof Integer) {
421             return;
422         }
423         String otherName = (String) param;
424         KeyPairGenerator otherKpg = KeyPairGenerator.getInstance(otherName,
425             providerName);
426         KeyPair otherKp = otherKpg.generateKeyPair();
427 
428         // ensure the KeyFactory rejects incorrect keys
429         KeyFactory kf = KeyFactory.getInstance(name, providerName);
430         try {
431             kf.getKeySpec(otherKp.getPublic(), XECPublicKeySpec.class);
432             throw new RuntimeException(name + " KeyFactory accepted "
433                 + param.toString() + " key");
434         } catch (InvalidKeySpecException ex) {
435             // expected
436         }
437         try {
438             kf.getKeySpec(otherKp.getPrivate(), XECPrivateKeySpec.class);
439             throw new RuntimeException(name + " KeyFactory accepted "
440                 + param.toString() + " key");
441         } catch (InvalidKeySpecException ex) {
442             // expected
443         }
444 
445         try {
446             kf.translateKey(otherKp.getPublic());
447             throw new RuntimeException(name + " KeyFactory accepted "
448                 + param.toString() + " key");
449         } catch (InvalidKeyException ex) {
450             // expected
451         }
452         try {
453             kf.translateKey(otherKp.getPrivate());
454             throw new RuntimeException(name + " KeyFactory accepted "
455                 + param.toString() + " key");
456         } catch (InvalidKeyException ex) {
457             // expected
458         }
459 
460         KeyFactory otherKf = KeyFactory.getInstance(otherName, providerName);
461         XECPublicKeySpec otherPubSpec = otherKf.getKeySpec(otherKp.getPublic(),
462             XECPublicKeySpec.class);
463         try {
464             kf.generatePublic(otherPubSpec);
465             throw new RuntimeException(name + " KeyFactory accepted "
466                 + param.toString() + " key");
467         } catch (InvalidKeySpecException ex) {
468             // expected
469         }
470         XECPrivateKeySpec otherPriSpec =
471             otherKf.getKeySpec(otherKp.getPrivate(), XECPrivateKeySpec.class);
472         try {
473             kf.generatePrivate(otherPriSpec);
474             throw new RuntimeException(name + " KeyFactory accepted "
475                 + param.toString() + " key");
476         } catch (InvalidKeySpecException ex) {
477             // expected
478         }
479 
480         // ensure the KeyAgreement rejects incorrect keys
481         KeyAgreement ka = KeyAgreement.getInstance(name, providerName);
482         try {
483             ka.init(otherKp.getPrivate());
484             throw new RuntimeException(name + " KeyAgreement accepted "
485                 + param.toString() + " key");
486         } catch (InvalidKeyException ex) {
487             // expected
488         }
489         KeyPair kp = kpg.generateKeyPair();
490         ka.init(kp.getPrivate());
491         try {
492             // This should always be rejected because it doesn't match the key
493             // passed to init, but it is tested here for good measure.
494             ka.doPhase(otherKp.getPublic(), true);
495             throw new RuntimeException(name + " KeyAgreement accepted "
496                 + param.toString() + " key");
497         } catch (InvalidKeyException ex) {
498             // expected
499         }
500     }
501 }
502