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 }