1 /*
  2  * Copyright (c) 2020, 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 import java.security.KeyPair;
 24 import java.security.KeyPairGenerator;
 25 import java.security.PrivateKey;
 26 import java.security.PublicKey;
 27 import java.security.SecureRandom;
 28 import java.security.Signature;
 29 import java.security.spec.EdDSAParameterSpec;
 30 import java.util.Arrays;
 31 import java.util.Hex;
 32 
 33 /*
 34  * @test
 35  * @bug 8209632
 36  * @summary Test EdDSAParameterSpec.
 37  * @library /test/lib
 38  * @build jdk.test.lib.Convert
 39  * @run main EdDSAParamSpec
 40  */
 41 public class EdDSAParamSpec {
 42 
 43     private static final String EDDSA = "EdDSA";
 44     private static final String ED25519 = "Ed25519";
 45     private static final String ED448 = "Ed448";
 46     private static final String PROVIDER = "SunEC";
 47     private static final byte[] MSG = "TEST".getBytes();
 48     private static final SecureRandom RND = new SecureRandom(new byte[]{0x1});
 49 
 50     public static void main(String[] args) throws Exception {
 51 
 52         testParam(PROVIDER, EDDSA);
 53         testParam(PROVIDER, ED25519);
 54         testParam(PROVIDER, ED448);
 55     }
 56 
 57     /**
 58      * Test Signature.
 59      */
 60     private static void testParam(String provider, String name)
 61             throws Exception {
 62 
 63         KeyPair kp = genKeyPair(provider, name);
 64         Signature sig = Signature.getInstance(name, provider);
 65         EdDSAParameterSpec initParam
 66                 = new EdDSAParameterSpec(true, "testContext".getBytes());
 67         sig.setParameter(initParam);
 68         byte[] origSign = sign(sig, kp.getPrivate(), MSG);
 69         for (boolean preHash : new boolean[]{true, false}) {
 70             System.out.printf("Testing signature for name: %s,"
 71                     + " algorithm spec: (prehash:%s)%n", name, preHash);
 72             verifyPublic(sig, kp.getPublic(), MSG,
 73                     new EdDSAParameterSpec(preHash), initParam, origSign);
 74             // Test Case with Context size combined.
 75             // As per rfc8032, value of context is maximum of 255 octet
 76             byte[] maxCtx = new byte[255];
 77             RND.nextBytes(maxCtx);
 78             for (byte[] context : new byte[][]{"others".getBytes(), maxCtx}) {
 79                 System.out.printf("Testing signature for name: %s,"
 80                         + " algorithm spec: (prehash:%s, context:%s)%n",
 81                         name, preHash, Hex.encoder().encode(context));
 82                 EdDSAParameterSpec params
 83                         = new EdDSAParameterSpec(preHash, context);
 84                 verifyPublic(sig, kp.getPublic(), MSG, params, initParam,
 85                         origSign);
 86             }
 87         }
 88         System.out.println("Passed.");
 89     }
 90 
 91     private static KeyPair genKeyPair(String provider, String name)
 92             throws Exception {
 93 
 94         KeyPairGenerator kpg = KeyPairGenerator.getInstance(name, provider);
 95         return kpg.generateKeyPair();
 96     }
 97 
 98     private static byte[] sign(Signature sig, PrivateKey priKey, byte[] msg)
 99             throws Exception {
100 
101         sig.initSign(priKey);
102         sig.update(msg);
103         return sig.sign();
104     }
105 
106     private static boolean verify(Signature sig, PublicKey pubKey, byte[] msg,
107             byte[] sign) throws Exception {
108 
109         sig.initVerify(pubKey);
110         sig.update(msg);
111         return sig.verify(sign);
112     }
113 
114     private static void verifyPublic(Signature sig, PublicKey pubKey,
115             byte[] msg, EdDSAParameterSpec params, EdDSAParameterSpec initParam,
116             byte[] origSign) throws Exception {
117 
118         sig.setParameter(params);
119         if (verify(sig, pubKey, msg, origSign)) {
120             byte[] context = params.getContext().isPresent()
121                     ? params.getContext().get() : null;
122             byte[] initContext = initParam.getContext().isPresent()
123                     ? initParam.getContext().get() : null;
124             boolean preHash = params.isPrehash();
125             boolean initPreHash = initParam.isPrehash();
126             // The signature should not get verified other than same parameter
127             // which is set through the signature instance.
128             if (!(equals(context, initContext) && equals(preHash, initPreHash))) {
129                 throw new RuntimeException(String.format("Signature verification"
130                         + " success with different param context(actual:%s, "
131                         + "expected:%s), Prehash(actual:%s, expected:%s)",
132                         Hex.encoder().encode(context),
133                         Hex.encoder().encode(initContext),
134                         preHash, initPreHash));
135             } else {
136                 System.out.println("Atleast a case matched");
137             }
138         }
139     }
140 
141     private static boolean equals(Object actual, Object expected) {
142 
143         if (actual == expected) {
144             return true;
145         }
146         if (actual == null || expected == null) {
147             return false;
148         }
149         boolean equals = actual.equals(expected);
150         if (!equals) {
151             throw new RuntimeException(String.format("Actual: %s, Expected: %s",
152                     actual, expected));
153         }
154         return equals;
155     }
156 
157     private static boolean equals(byte[] actual, byte[] expected) {
158 
159         if (actual == expected) {
160             return true;
161         }
162         if (actual == null || expected == null) {
163             return false;
164         }
165         boolean equals = Arrays.equals(actual, expected);
166         if (!equals) {
167             throw new RuntimeException(String.format("Actual array: %s, "
168                     + "Expected array:%s", Hex.encoder().encode(actual),
169                     Hex.encoder().encode(expected)));
170         }
171         return equals;
172     }
173 
174 }