Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java
41154 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.pkcs11;2627import java.io.*;28import java.security.*;29import sun.security.pkcs11.wrapper.*;3031/**32* SecureRandom implementation class. Some tokens support only33* C_GenerateRandom() and not C_SeedRandom(). In order not to lose an34* application specified seed, we create a SHA1PRNG that we mix with in that35* case.36*37* Note that since SecureRandom is thread safe, we only need one38* instance per PKCS#11 token instance. It is created on demand and cached39* in the SunPKCS11 class.40*41* Also note that we obtain the PKCS#11 session on demand, no need to tie one42* up.43*44* @author Andreas Sterbenz45* @since 1.546*/47final class P11SecureRandom extends SecureRandomSpi {4849private static final long serialVersionUID = -8939510236124553291L;5051// token instance52private final Token token;5354// PRNG for mixing, non-null if active (i.e. setSeed() has been called)55private volatile SecureRandom mixRandom;5657// buffer, if mixing is used58private byte[] mixBuffer;5960// bytes remaining in mixBuffer, if mixing is used61private int buffered;6263/*64* we buffer data internally for efficiency but limit the lifetime65* to avoid using stale bits.66*/67// lifetime in ms, currently 100 ms (0.1 s)68private static final long MAX_IBUFFER_TIME = 100;6970// size of the internal buffer71private static final int IBUFFER_SIZE = 32;7273// internal buffer for the random bits74private transient byte[] iBuffer = new byte[IBUFFER_SIZE];7576// number of bytes remain in iBuffer77private transient int ibuffered = 0;7879// time that data was read into iBuffer80private transient long lastRead = 0L;8182P11SecureRandom(Token token) {83this.token = token;84}8586// see JCA spec87@Override88protected synchronized void engineSetSeed(byte[] seed) {89if (seed == null) {90throw new NullPointerException("seed must not be null");91}92Session session = null;93try {94session = token.getOpSession();95token.p11.C_SeedRandom(session.id(), seed);96} catch (PKCS11Exception e) {97// cannot set seed98// let a SHA1PRNG use that seed instead99SecureRandom random = mixRandom;100if (random != null) {101random.setSeed(seed);102} else {103try {104mixBuffer = new byte[20];105random = SecureRandom.getInstance("SHA1PRNG");106// initialize object before assigning to class field107random.setSeed(seed);108mixRandom = random;109} catch (NoSuchAlgorithmException ee) {110throw new ProviderException(ee);111}112}113} finally {114token.releaseSession(session);115}116}117118// see JCA spec119@Override120protected void engineNextBytes(byte[] bytes) {121if ((bytes == null) || (bytes.length == 0)) {122return;123}124if (bytes.length <= IBUFFER_SIZE) {125int ofs = 0;126synchronized (iBuffer) {127while (ofs < bytes.length) {128long time = System.currentTimeMillis();129// refill the internal buffer if empty or stale130if ((ibuffered == 0) ||131!(time - lastRead < MAX_IBUFFER_TIME)) {132lastRead = time;133implNextBytes(iBuffer);134ibuffered = IBUFFER_SIZE;135}136// copy the buffered bytes into 'bytes'137while ((ofs < bytes.length) && (ibuffered > 0)) {138bytes[ofs++] = iBuffer[IBUFFER_SIZE - ibuffered--];139}140}141}142} else {143// avoid using the buffer - just fill bytes directly144implNextBytes(bytes);145}146147}148149// see JCA spec150@Override151protected byte[] engineGenerateSeed(int numBytes) {152byte[] b = new byte[numBytes];153engineNextBytes(b);154return b;155}156157private void mix(byte[] b) {158SecureRandom random = mixRandom;159if (random == null) {160// avoid mixing if setSeed() has never been called161return;162}163synchronized (this) {164int ofs = 0;165int len = b.length;166while (len-- > 0) {167if (buffered == 0) {168random.nextBytes(mixBuffer);169buffered = mixBuffer.length;170}171b[ofs++] ^= mixBuffer[mixBuffer.length - buffered];172buffered--;173}174}175}176177// fill up the specified buffer with random bytes, and mix them178private void implNextBytes(byte[] bytes) {179Session session = null;180try {181session = token.getOpSession();182token.p11.C_GenerateRandom(session.id(), bytes);183mix(bytes);184} catch (PKCS11Exception e) {185throw new ProviderException("nextBytes() failed", e);186} finally {187token.releaseSession(session);188}189}190191private void readObject(ObjectInputStream in)192throws IOException, ClassNotFoundException {193in.defaultReadObject();194// assign default values to non-null transient fields195iBuffer = new byte[IBUFFER_SIZE];196ibuffered = 0;197lastRead = 0L;198}199}200201202