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 }