1 /*
   2  * Copyright (c) 2016, 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 8141039
  27  * @library /lib/testlibrary
  28  * @summary When random number is generated through the a SecureRandom instance
  29  *          as well from it's serialized instance in the same time then the
  30  *          generated random numbers should be different when one or both are
  31  *          reseeded.
  32  * @run main/othervm -Djava.security.egd=file:/dev/urandom SerializedSeedTest
  33  */
  34 import java.io.ByteArrayOutputStream;
  35 import java.io.IOException;
  36 import java.io.ObjectInputStream;
  37 import java.io.ObjectOutputStream;
  38 import java.io.ByteArrayInputStream;
  39 import java.security.NoSuchAlgorithmException;
  40 import java.security.SecureRandom;
  41 import java.security.Security;
  42 import jdk.testlibrary.Asserts;
  43 
  44 public class SerializedSeedTest {
  45 
  46     private static final byte[] SEED = "seed".getBytes();
  47     private static final String DRBG_CONFIG = "securerandom.drbg.config";
  48     private static final String DRBG_CONFIG_VALUE
  49             = Security.getProperty(DRBG_CONFIG);
  50 
  51     public static void main(String[] args) {
  52         boolean success = true;
  53 
  54         for (String mech : new String[]{
  55             "SHA1PRNG", "Hash_DRBG", "HMAC_DRBG", "CTR_DRBG"}) {
  56             System.out.printf(
  57                     "%nRunning test for SecureRandom mechanism: '%s'", mech);
  58             try {
  59                 // Serialize without seed and compare generated random numbers
  60                 // produced through original and serialized instances.
  61                 SecureRandom orig = getSRInstance(mech);
  62                 SecureRandom copy = deserializedCopy(orig);
  63                 System.out.printf("%nSerialize without seed. Generated random"
  64                         + " numbers should be different.");
  65                 check(orig, copy, false, mech);
  66 
  67                 // Serialize after default seed and compare generated random
  68                 // numbers produced through original and serialized instances.
  69                 orig = getSRInstance(mech);
  70                 orig.nextInt(); // Default seeded
  71                 copy = deserializedCopy(orig);
  72                 System.out.printf("%nSerialize after default seed. Generated"
  73                         + " random numbers should be same till 20-bytes.");
  74                 check(orig, copy, !isDRBG(mech), mech);
  75 
  76                 // Serialize after explicit seed and compare generated random
  77                 // numbers produced through original and serialized instances.
  78                 orig = getSRInstance(mech);
  79                 orig.setSeed(SEED); // Explicitly seeded
  80                 copy = deserializedCopy(orig);
  81                 System.out.printf("%nSerialize after explicit seed. Generated "
  82                         + "random numbers should be same till 20-bytes.");
  83                 check(orig, copy, !isDRBG(mech), mech);
  84 
  85                 // Serialize without seed but original is explicitly seeded
  86                 // before generating any random number. Then compare generated
  87                 // random numbers produced through original and serialized
  88                 // instances.
  89                 orig = getSRInstance(mech);
  90                 copy = deserializedCopy(orig);
  91                 orig.setSeed(SEED); // Explicitly seeded
  92                 System.out.printf("%nSerialize without seed. When original is "
  93                         + "explicitly seeded before generating random numbers,"
  94                         + " Generated random numbers should be different.");
  95                 check(orig, copy, false, mech);
  96 
  97                 // Serialize after default seed but original is explicitly
  98                 // seeded before generating any random number. Then compare
  99                 // generated random numbers produced through original and
 100                 // serialized instances.
 101                 orig = getSRInstance(mech);
 102                 orig.nextInt(); // Default seeded
 103                 copy = deserializedCopy(orig);
 104                 orig.setSeed(SEED); // Explicitly seeded
 105                 System.out.printf("%nSerialize after default seed but original "
 106                         + "is explicitly seeded before generating random number"
 107                         + ". Generated random numbers should be different.");
 108                 check(orig, copy, false, mech);
 109 
 110                 // Serialize after explicit seed but original is explicitly
 111                 // seeded again before generating random number. Then compare
 112                 // generated random numbers produced through original and
 113                 // serialized instances.
 114                 orig = getSRInstance(mech);
 115                 orig.setSeed(SEED); // Explicitly seeded
 116                 copy = deserializedCopy(orig);
 117                 orig.setSeed(SEED); // Explicitly seeded
 118                 System.out.printf("%nSerialize after explicit seed but "
 119                         + "original is explicitly seeded again before "
 120                         + "generating random number. Generated random "
 121                         + "numbers should be different.");
 122                 check(orig, copy, false, mech);
 123 
 124             } catch (Exception e) {
 125                 e.printStackTrace(System.out);
 126                 success = false;
 127             } finally {
 128                 Security.setProperty(DRBG_CONFIG, DRBG_CONFIG_VALUE);
 129             }
 130             System.out.printf("%n------Completed Test for %s------", mech);
 131         }
 132 
 133         if (!success) {
 134             throw new RuntimeException("At least one test failed.");
 135         }
 136     }
 137 
 138     /**
 139      * Find if the mechanism is a DRBG mechanism.
 140      * @param mech Mechanism name
 141      * @return True for DRBG mechanism else False
 142      */
 143     private static boolean isDRBG(String mech) {
 144         return mech.contains("_DRBG");
 145     }
 146 
 147     /**
 148      * Verify the similarity of random numbers generated though both original
 149      * as well as deserialized instance.
 150      */
 151     private static void check(SecureRandom orig, SecureRandom copy,
 152             boolean equal, String mech) {
 153         int o = orig.nextInt();
 154         int c = copy.nextInt();
 155         System.out.printf("%nRandom number generated for mechanism: '%s' "
 156                 + "from original instance as: '%s' and from serialized "
 157                 + "instance as: '%s'", mech, o, c);
 158         if (equal) {
 159             Asserts.assertEquals(o, c, mech);
 160         } else {
 161             Asserts.assertNotEquals(o, c, mech);
 162         }
 163     }
 164 
 165     /**
 166      * Get a copy of SecureRandom instance through deserialization.
 167      * @param orig Original SecureRandom instance
 168      * @return Deserialized SecureRandom instance
 169      * @throws IOException
 170      * @throws ClassNotFoundException
 171      */
 172     private static SecureRandom deserializedCopy(SecureRandom orig)
 173             throws IOException, ClassNotFoundException {
 174         return deserialize(serialize(orig));
 175     }
 176 
 177     /**
 178      * Deserialize the SecureRandom object.
 179      */
 180     private static SecureRandom deserialize(byte[] serialized)
 181             throws IOException, ClassNotFoundException {
 182         SecureRandom sr = null;
 183         try (ByteArrayInputStream bis = new ByteArrayInputStream(serialized);
 184                 ObjectInputStream ois = new ObjectInputStream(bis)) {
 185             sr = (SecureRandom) ois.readObject();
 186         }
 187         return sr;
 188     }
 189 
 190     /**
 191      * Serialize the given SecureRandom object.
 192      */
 193     private static byte[] serialize(SecureRandom sr) throws IOException {
 194         try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
 195                 ObjectOutputStream oos = new ObjectOutputStream(bos)) {
 196             oos.writeObject(sr);
 197             return bos.toByteArray();
 198         }
 199     }
 200 
 201     /**
 202      * Create a SecureRandom instance for a given mechanism.
 203      */
 204     private static SecureRandom getSRInstance(String mech)
 205             throws NoSuchAlgorithmException {
 206         if (!isDRBG(mech)) {
 207             return SecureRandom.getInstance(mech);
 208         } else {
 209             Security.setProperty(DRBG_CONFIG, mech);
 210             return SecureRandom.getInstance("DRBG");
 211         }
 212     }
 213 
 214 }