1 /*
  2  * Copyright (c) 2019, 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 
 24 import java.security.*;
 25 import java.security.spec.*;
 26 import java.math.*;
 27 import java.util.*;
 28 
 29 /*
 30  * @test
 31  * @bug 8147502
 32  * @summary Test that digests are properly truncated before the signature
 33  *     is applied. The digest should be truncated to the bit length of the
 34  *     group order.
 35  * @library /test/lib
 36  * @build jdk.test.lib.Convert
 37  * @run main/othervm -Djdk.sunec.disableNative=false SignatureDigestTruncate
 38  */
 39 public class SignatureDigestTruncate {
 40 
 41     /*
 42      * A SecureRandom that produces nextBytes in a way that causes the nonce
 43      * to be set to the value supplied to the constructor. This class
 44      * is specific to the way that the native ECDSA implementation in
 45      * SunEC produces nonces from random input. It may not work for all
 46      * test cases, and it will need to be updated when the behavior of
 47      * SunEC changes.
 48      */
 49     private static class FixedRandom extends SecureRandom {
 50 
 51         private final byte[] val;
 52 
 53         public FixedRandom(byte[] val) {
 54             // SunEC adds one to the value returned, so subtract one here in
 55             // order to get back to the correct value.
 56             BigInteger biVal = new BigInteger(1, val);
 57             biVal = biVal.subtract(BigInteger.ONE);
 58             byte[] temp = biVal.toByteArray();
 59             this.val = new byte[val.length];
 60             int inStartPos = Math.max(0, temp.length - val.length);
 61             int outStartPos = Math.max(0, val.length - temp.length);
 62             System.arraycopy(temp, inStartPos, this.val, outStartPos,
 63                 temp.length - inStartPos);
 64         }
 65 
 66         @Override
 67         public void nextBytes(byte[] bytes) {
 68             // SunEC samples (n + 1) * 2 bytes, but only n*2 bytes are used by
 69             // the native implementation. So the value must be offset slightly.
 70             Arrays.fill(bytes, (byte) 0);
 71             int copyLength = Math.min(val.length, bytes.length - 2);
 72             System.arraycopy(val, 0, bytes, bytes.length - copyLength - 2,
 73                 copyLength);
 74         }
 75     }
 76 
 77     private static void assertEquals(byte[] expected, byte[] actual,
 78             String name) {
 79         if (!Arrays.equals(actual, expected)) {
 80             System.out.println("expect: " + Hex.encoder().encode(expected));
 81             System.out.println("actual: " + Hex.encoder().encode(actual));
 82             throw new RuntimeException("Incorrect " + name + " value");
 83         }
 84     }
 85 
 86     private static void runTest(String alg, String curveName,
 87         String privateKeyStr, String msgStr, String kStr, String sigStr)
 88         throws Exception {
 89 
 90         System.out.println("Testing " + alg + " with " + curveName);
 91 
 92         Hex.Decoder decoder = Hex.decoder();
 93         byte[] privateKey = decoder.decode(privateKeyStr);
 94         byte[] msg = decoder.decode(msgStr);
 95         byte[] k = decoder.decode(kStr);
 96         byte[] expectedSig = decoder.decode(sigStr);
 97 
 98         AlgorithmParameters params =
 99             AlgorithmParameters.getInstance("EC", "SunEC");
100         params.init(new ECGenParameterSpec(curveName));
101         ECParameterSpec ecParams =
102             params.getParameterSpec(ECParameterSpec.class);
103 
104         KeyFactory kf = KeyFactory.getInstance("EC", "SunEC");
105         BigInteger s = new BigInteger(1, privateKey);
106         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(s, ecParams);
107         PrivateKey privKey = kf.generatePrivate(privKeySpec);
108 
109         Signature sig = Signature.getInstance(alg, "SunEC");
110         sig.initSign(privKey, new FixedRandom(k));
111         sig.update(msg);
112         byte[] computedSig = sig.sign();
113         assertEquals(expectedSig, computedSig, "signature");
114     }
115 
116     public static void main(String[] args) throws Exception {
117         runTest("SHA384withECDSAinP1363Format", "sect283r1",
118             "abcdef10234567", "010203040506070809",
119             "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d" +
120             "1e1f20212223",
121             "01d7544b5d3935216bd45e2f8042537e1e0296a11e0eb96666199281b409" +
122             "42abccd5358a035de8a314d3e6c2a97614daebf5fb1313540eec3f9a3272" +
123             "068aa10922ccae87d255c84c");
124     }
125 }