1 /*
   2  * Copyright (c) 2003, 2013, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.pkcs11;
  27 
  28 import java.io.*;
  29 import java.security.*;
  30 import sun.security.pkcs11.wrapper.*;
  31 
  32 /**
  33  * SecureRandom implementation class. Some tokens support only
  34  * C_GenerateRandom() and not C_SeedRandom(). In order not to lose an
  35  * application specified seed, we create a SHA1PRNG that we mix with in that
  36  * case.
  37  *
  38  * Note that since SecureRandom is thread safe, we only need one
  39  * instance per PKCS#11 token instance. It is created on demand and cached
  40  * in the SunPKCS11 class.
  41  *
  42  * Also note that we obtain the PKCS#11 session on demand, no need to tie one
  43  * up.
  44  *
  45  * @author  Andreas Sterbenz
  46  * @since   1.5
  47  */
  48 final class P11SecureRandom extends SecureRandomSpi {
  49 
  50     private static final long serialVersionUID = -8939510236124553291L;
  51 
  52     // token instance
  53     private final Token token;
  54 
  55     // PRNG for mixing, non-null if active (i.e. setSeed() has been called)
  56     private volatile SecureRandom mixRandom;
  57 
  58     // buffer, if mixing is used
  59     private byte[] mixBuffer;
  60 
  61     // bytes remaining in mixBuffer, if mixing is used
  62     private int buffered;
  63 
  64     /*
  65      * we buffer data internally for efficiency but limit the lifetime
  66      * to avoid using stale bits.
  67      */
  68     // lifetime in ms, currently 100 ms (0.1 s)
  69     private static final long MAX_IBUFFER_TIME = 100;
  70 
  71     // size of the internal buffer
  72     private static final int IBUFFER_SIZE = 32;
  73 
  74     // internal buffer for the random bits
  75     private transient byte[] iBuffer = new byte[IBUFFER_SIZE];
  76 
  77     // number of bytes remain in iBuffer
  78     private transient int ibuffered = 0;
  79 
  80     // time that data was read into iBuffer
  81     private transient long lastRead = 0L;
  82 
  83     P11SecureRandom(Token token) {
  84         this.token = token;
  85     }
  86 
  87     // see JCA spec
  88     @Override
  89     protected synchronized void engineSetSeed(byte[] seed) {
  90         if (seed == null) {
  91             throw new NullPointerException("seed must not be null");
  92         }
  93         Session session = null;
  94         try {
  95             session = token.getOpSession();
  96             token.p11.C_SeedRandom(session.id(), seed);
  97         } catch (PKCS11Exception e) {
  98             // cannot set seed
  99             // let a SHA1PRNG use that seed instead
 100             SecureRandom random = mixRandom;
 101             if (random != null) {
 102                 random.setSeed(seed);
 103             } else {
 104                 try {
 105                     mixBuffer = new byte[20];
 106                     random = SecureRandom.getInstance("SHA1PRNG");
 107                     // initialize object before assigning to class field
 108                     random.setSeed(seed);
 109                     mixRandom = random;
 110                 } catch (NoSuchAlgorithmException ee) {
 111                     throw new ProviderException(ee);
 112                 }
 113             }
 114         } finally {
 115             token.releaseSession(session);
 116         }
 117     }
 118 
 119     // see JCA spec
 120     @Override
 121     protected void engineNextBytes(byte[] bytes) {
 122         if ((bytes == null) || (bytes.length == 0)) {
 123             return;
 124         }
 125         if (bytes.length <= IBUFFER_SIZE)  {
 126             int ofs = 0;
 127             synchronized (iBuffer) {
 128                 while (ofs < bytes.length) {
 129                     long time = System.currentTimeMillis();
 130                     // refill the internal buffer if empty or stale
 131                     if ((ibuffered == 0) ||
 132                             !(time - lastRead < MAX_IBUFFER_TIME)) {
 133                         lastRead = time;
 134                         implNextBytes(iBuffer);
 135                         ibuffered = IBUFFER_SIZE;
 136                     }
 137                     // copy the buffered bytes into 'bytes'
 138                     while ((ofs < bytes.length) && (ibuffered > 0)) {
 139                         bytes[ofs++] = iBuffer[IBUFFER_SIZE - ibuffered--];
 140                     }
 141                 }
 142             }
 143         } else {
 144             // avoid using the buffer - just fill bytes directly
 145             implNextBytes(bytes);
 146         }
 147 
 148     }
 149 
 150     // see JCA spec
 151     @Override
 152     protected byte[] engineGenerateSeed(int numBytes) {
 153         byte[] b = new byte[numBytes];
 154         engineNextBytes(b);
 155         return b;
 156     }
 157 
 158     private void mix(byte[] b) {
 159         SecureRandom random = mixRandom;
 160         if (random == null) {
 161             // avoid mixing if setSeed() has never been called
 162             return;
 163         }
 164         synchronized (this) {
 165             int ofs = 0;
 166             int len = b.length;
 167             while (len-- > 0) {
 168                 if (buffered == 0) {
 169                     random.nextBytes(mixBuffer);
 170                     buffered = mixBuffer.length;
 171                 }
 172                 b[ofs++] ^= mixBuffer[mixBuffer.length - buffered];
 173                 buffered--;
 174             }
 175         }
 176     }
 177 
 178     // fill up the specified buffer with random bytes, and mix them
 179     private void implNextBytes(byte[] bytes) {
 180         Session session = null;
 181         try {
 182             session = token.getOpSession();
 183             token.p11.C_GenerateRandom(session.id(), bytes);
 184             mix(bytes);
 185         } catch (PKCS11Exception e) {
 186             throw new ProviderException("nextBytes() failed", e);
 187         } finally {
 188             token.releaseSession(session);
 189         }
 190     }
 191 
 192     private void readObject(ObjectInputStream in)
 193             throws IOException, ClassNotFoundException {
 194         in.defaultReadObject();
 195         // assign default values to non-null transient fields
 196         iBuffer = new byte[IBUFFER_SIZE];
 197         ibuffered = 0;
 198         lastRead = 0L;
 199     }
 200 }