6591117: Poor preformance of PKCS#11 security provider compared to Sun default provider
authorvaleriep
Thu, 18 Mar 2010 17:32:45 -0700
changeset 5156 bb63e4d4b1de
parent 5155 d70c73fdc68e
child 5157 eccef47ec254
6591117: Poor preformance of PKCS#11 security provider compared to Sun default provider Summary: Added internal buffering to PKCS11 SecureRandom impl Reviewed-by: wetmore
jdk/src/share/classes/sun/security/pkcs11/P11SecureRandom.java
--- a/jdk/src/share/classes/sun/security/pkcs11/P11SecureRandom.java	Thu Mar 18 17:05:42 2010 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11SecureRandom.java	Thu Mar 18 17:32:45 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2003-2010 Sun Microsystems, Inc.  All Rights Reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
 package sun.security.pkcs11;
 
 import java.util.*;
-
+import java.io.*;
 import java.security.*;
 
 import sun.security.pkcs11.wrapper.*;
@@ -61,9 +61,28 @@
     // buffer, if mixing is used
     private byte[] mixBuffer;
 
-    // bytes remaining in buffer, if mixing is used
+    // bytes remaining in mixBuffer, if mixing is used
     private int buffered;
 
+    /*
+     * we buffer data internally for efficiency but limit the lifetime
+     * to avoid using stale bits.
+     */
+    // lifetime in ms, currently 100 ms (0.1 s)
+    private static final long MAX_IBUFFER_TIME = 100;
+
+    // size of the internal buffer
+    private static final int IBUFFER_SIZE = 32;
+
+    // internal buffer for the random bits
+    private transient byte[] iBuffer = new byte[IBUFFER_SIZE];
+
+    // number of bytes remain in iBuffer
+    private transient int ibuffered = 0;
+
+    // time that data was read into iBuffer
+    private transient long lastRead = 0L;
+
     P11SecureRandom(Token token) {
         this.token = token;
     }
@@ -104,16 +123,29 @@
         if ((bytes == null) || (bytes.length == 0)) {
             return;
         }
-        Session session = null;
-        try {
-            session = token.getOpSession();
-            token.p11.C_GenerateRandom(session.id(), bytes);
-            mix(bytes);
-        } catch (PKCS11Exception e) {
-            throw new ProviderException("nextBytes() failed", e);
-        } finally {
-            token.releaseSession(session);
+        if (bytes.length <= IBUFFER_SIZE)  {
+            int ofs = 0;
+            synchronized (iBuffer) {
+                while (ofs < bytes.length) {
+                    long time = System.currentTimeMillis();
+                    // refill the internal buffer if empty or stale
+                    if ((ibuffered == 0) ||
+                            !(time - lastRead < MAX_IBUFFER_TIME)) {
+                        lastRead = time;
+                        implNextBytes(iBuffer);
+                        ibuffered = IBUFFER_SIZE;
+                    }
+                    // copy the buffered bytes into 'bytes'
+                    while ((ofs < bytes.length) && (ibuffered > 0)) {
+                        bytes[ofs++] = iBuffer[IBUFFER_SIZE - ibuffered--];
+                    }
+                }
+            }
+        } else {
+            // avoid using the buffer - just fill bytes directly
+            implNextBytes(bytes);
         }
+
     }
 
     // see JCA spec
@@ -143,4 +175,17 @@
         }
     }
 
+    // fill up the specified buffer with random bytes, and mix them
+    private void implNextBytes(byte[] bytes) {
+        Session session = null;
+        try {
+            session = token.getOpSession();
+            token.p11.C_GenerateRandom(session.id(), bytes);
+            mix(bytes);
+        } catch (PKCS11Exception e) {
+            throw new ProviderException("nextBytes() failed", e);
+        } finally {
+            token.releaseSession(session);
+        }
+    }
 }