changeset 7324:b4fc2a92ae21

PR1781: NSS PKCS11 provider fails to handle multipart AES encryption
author andrew
date Wed, 21 May 2014 15:25:53 +0100
parents c8c15663cef6
children a982f3aa40ed
files src/share/classes/sun/security/pkcs11/P11Cipher.java
diffstat 1 files changed, 235 insertions(+), 149 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/pkcs11/P11Cipher.java	Thu Apr 17 17:20:13 2014 +0100
+++ b/src/share/classes/sun/security/pkcs11/P11Cipher.java	Wed May 21 15:25:53 2014 +0100
@@ -69,7 +69,7 @@
     private static interface Padding {
         // ENC: format the specified buffer with padding bytes and return the
         // actual padding length
-        int setPaddingBytes(byte[] paddingBuffer, int padLen);
+        int setPaddingBytes(byte[] paddingBuffer, int offset, int padLen);
 
         // DEC: return the length of trailing padding bytes given the specified
         // padded data
@@ -90,8 +90,8 @@
             this.blockSize = blockSize;
         }
 
-        public int setPaddingBytes(byte[] paddingBuffer, int padLen) {
-            Arrays.fill(paddingBuffer, 0, padLen, (byte) (padLen & 0x007f));
+        public int setPaddingBytes(byte[] paddingBuffer, int offset, int padLen) {
+            Arrays.fill(paddingBuffer, offset, offset + padLen, (byte) (padLen & 0x007f));
             return padLen;
         }
 
@@ -160,10 +160,16 @@
     // original IV, if in MODE_CBC or MODE_CTR
     private byte[] iv;
 
-    // number of bytes buffered internally by the native mechanism and padBuffer
-    // if we do the padding
+    // number of bytes buffered by the blockBuffer
     private int bytesBuffered;
 
+    // number of bytes buffered internally
+    private int bytesBufferedInt;
+
+    // bytes buffered from an incomplete block
+    private byte[] blockBuffer;
+    private int blockBufferLen;
+
     P11Cipher(Token token, String algorithm, long mechanism)
             throws PKCS11Exception, NoSuchAlgorithmException {
         super();
@@ -194,6 +200,9 @@
             // should not happen
             throw new ProviderException(nspe);
         }
+
+	if (blockSize > 0)
+	    blockBuffer = new byte[blockSize];
     }
 
     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
@@ -403,7 +412,8 @@
                 token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
             }
         } catch (PKCS11Exception e) {
-            throw new ProviderException("Cancel failed", e);
+	    if (e.getErrorCode() != CKR_OPERATION_NOT_INITIALIZED)
+		throw new ProviderException("Cancel failed", e);
         } finally {
             reset();
         }
@@ -435,7 +445,9 @@
             throw ex;
         }
         bytesBuffered = 0;
+	bytesBufferedInt = 0;
         padBufferLen = 0;
+	blockBufferLen = 0;
         initialized = true;
     }
 
@@ -445,7 +457,7 @@
             return 0;
         }
 
-        int result = inLen + bytesBuffered;
+        int result = inLen + bytesBuffered + bytesBufferedInt;
         if (blockSize != 0) {
             // minus the number of bytes in the last incomplete block.
             result -= (result & (blockSize - 1));
@@ -459,7 +471,7 @@
             return 0;
         }
 
-        int result = inLen + bytesBuffered;
+        int result = inLen + bytesBuffered + bytesBufferedInt;
         if (blockSize != 0 && encrypt && paddingType != PAD_NONE) {
             // add the number of bytes to make the last block complete.
             result += (blockSize - (result & (blockSize - 1)));
@@ -471,7 +483,9 @@
     private void reset() {
         initialized = false;
         bytesBuffered = 0;
+	bytesBufferedInt = 0;
         padBufferLen = 0;
+	blockBufferLen = 0;
         if (session != null) {
             session = token.releaseSession(session);
         }
@@ -546,50 +560,65 @@
         }
         try {
             ensureInitialized();
-            int k = 0;
-            if (encrypt) {
-                k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen,
-                        0, out, outOfs, outLen);
-            } else {
-                int newPadBufferLen = 0;
-                if (paddingObj != null) {
-                    if (padBufferLen != 0) {
-                        // NSS throws up when called with data not in multiple
-                        // of blocks. Try to work around this by holding the
-                        // extra data in padBuffer.
-                        if (padBufferLen != padBuffer.length) {
-                            int bufCapacity = padBuffer.length - padBufferLen;
-                            if (inLen > bufCapacity) {
-                                bufferInputBytes(in, inOfs, bufCapacity);
-                                inOfs += bufCapacity;
-                                inLen -= bufCapacity;
-                            } else {
-                                bufferInputBytes(in, inOfs, inLen);
-                                return 0;
-                            }
-                        }
-                        k = token.p11.C_DecryptUpdate(session.id(),
-                                0, padBuffer, 0, padBufferLen,
-                                0, out, outOfs, outLen);
-                        padBufferLen = 0;
-                    }
-                    newPadBufferLen = inLen & (blockSize - 1);
-                    if (newPadBufferLen == 0) {
-                        newPadBufferLen = padBuffer.length;
-                    }
-                    inLen -= newPadBufferLen;
-                }
-                if (inLen > 0) {
-                    k += token.p11.C_DecryptUpdate(session.id(), 0, in, inOfs,
-                            inLen, 0, out, (outOfs + k), (outLen - k));
-                }
-                // update 'padBuffer' if using our own padding impl.
-                if (paddingObj != null) {
-                    bufferInputBytes(in, inOfs + inLen, newPadBufferLen);
-                }
-            }
-            bytesBuffered += (inLen - k);
-            return k;
+            int bufRes = 0;
+	    int inRes = 0;
+	    int newBlockBufferLen = 0;
+
+	    // NSS throws up when called with data not in multiple
+	    // of blocks. Try to work around this by holding the
+	    // extra data in blockBuffer.
+	    if (blockBufferLen != 0) {
+		if (blockBufferLen != blockBuffer.length) {
+		    int bufCapacity = blockBuffer.length - blockBufferLen;
+		    if (inLen >= bufCapacity) {
+			bufferInputBytes(in, inOfs, bufCapacity);
+			inOfs += bufCapacity;
+			inLen -= bufCapacity;
+		    } else {
+			bufferInputBytes(in, inOfs, inLen);
+			return 0;
+		    }
+		}
+		if (encrypt) {
+		    bufRes = token.p11.C_EncryptUpdate(session.id(), 0, blockBuffer, 0,
+						       blockBufferLen, 0, out, outOfs,
+						       outLen);
+		} else {
+		    bufRes = token.p11.C_DecryptUpdate(session.id(), 0, blockBuffer, 0,
+						       blockBufferLen, 0, out, outOfs,
+						       outLen);
+		}
+		bytesBufferedInt += (blockBufferLen - bufRes);
+		blockBufferLen = 0;
+		bytesBuffered = 0;
+	    }
+
+	    if (inLen == 0)
+		return bufRes;
+
+	    if (blockBuffer != null) {
+		newBlockBufferLen = inLen & (blockSize - 1);
+		if (!encrypt && paddingObj != null && newBlockBufferLen == 0)
+		    // Hold the last block in the buffer if we need to unpad
+		    newBlockBufferLen = blockBuffer.length;
+		inLen -= newBlockBufferLen;
+		bufferInputBytes(in, inOfs + inLen, newBlockBufferLen);
+	    }
+
+	    if (inLen > 0) {
+		if (encrypt) {
+                    inRes = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs,
+						      inLen, 0, out, (outOfs + bufRes),
+						      (outLen - bufRes));
+		} else {
+                    inRes = token.p11.C_DecryptUpdate(session.id(), 0, in, inOfs,
+						      inLen, 0, out, (outOfs + bufRes),
+						      (outLen - bufRes));
+		}
+		bytesBufferedInt += (inLen - inRes);
+	    }
+
+            return inRes + bufRes;
         } catch (PKCS11Exception e) {
             if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
                 throw (ShortBufferException)
@@ -642,70 +671,79 @@
                 }
             }
 
-            int k = 0;
-            if (encrypt) {
-                if (inAddr == 0 && inArray == null) {
-                    inArray = new byte[inLen];
-                    inBuffer.get(inArray);
-                } else {
-                    inBuffer.position(origPos + inLen);
-                }
-                k = token.p11.C_EncryptUpdate(session.id(),
-                        inAddr, inArray, inOfs, inLen,
-                        outAddr, outArray, outOfs, outLen);
-            } else {
-                int newPadBufferLen = 0;
-                if (paddingObj != null) {
-                    if (padBufferLen != 0) {
-                        // NSS throws up when called with data not in multiple
-                        // of blocks. Try to work around this by holding the
-                        // extra data in padBuffer.
-                        if (padBufferLen != padBuffer.length) {
-                            int bufCapacity = padBuffer.length - padBufferLen;
-                            if (inLen > bufCapacity) {
-                                bufferInputBytes(inBuffer, bufCapacity);
-                                inOfs += bufCapacity;
-                                inLen -= bufCapacity;
-                            } else {
-                                bufferInputBytes(inBuffer, inLen);
-                                return 0;
-                            }
-                        }
-                        k = token.p11.C_DecryptUpdate(session.id(), 0,
-                                padBuffer, 0, padBufferLen, outAddr, outArray,
-                                outOfs, outLen);
-                        padBufferLen = 0;
-                    }
-                    newPadBufferLen = inLen & (blockSize - 1);
-                    if (newPadBufferLen == 0) {
-                        newPadBufferLen = padBuffer.length;
-                    }
-                    inLen -= newPadBufferLen;
-                }
-                if (inLen > 0) {
-                    if (inAddr == 0 && inArray == null) {
-                        inArray = new byte[inLen];
-                        inBuffer.get(inArray);
-                    } else {
-                        inBuffer.position(inBuffer.position() + inLen);
-                    }
-                    k += token.p11.C_DecryptUpdate(session.id(), inAddr,
-                            inArray, inOfs, inLen, outAddr, outArray,
-                            (outOfs + k), (outLen - k));
-                }
-                // update 'padBuffer' if using our own padding impl.
-                if (paddingObj != null && newPadBufferLen != 0) {
-                    bufferInputBytes(inBuffer, newPadBufferLen);
-                }
-            }
-            bytesBuffered += (inLen - k);
+            int bufRes = 0;
+	    int inRes = 0;
+	    int newBlockBufferLen = 0;
+
+	    // NSS throws up when called with data not in multiple
+	    // of blocks. Try to work around this by holding the
+	    // extra data in blockBuffer.
+	    if (blockBufferLen != 0) {
+		if (blockBufferLen != blockBuffer.length) {
+		    int bufCapacity = blockBuffer.length - blockBufferLen;
+		    if (inLen >= bufCapacity) {
+			bufferInputBytes(inBuffer, bufCapacity);
+			inOfs += bufCapacity;
+			inLen -= bufCapacity;
+		    } else {
+			bufferInputBytes(inBuffer, inLen);
+			return 0;
+		    }
+		}
+		if (encrypt) {
+		    bufRes = token.p11.C_EncryptUpdate(session.id(), 0, blockBuffer, 0,
+						       blockBufferLen, outAddr, outArray, outOfs,
+						       outLen);
+		} else {
+		    bufRes = token.p11.C_DecryptUpdate(session.id(), 0, blockBuffer, 0,
+						       blockBufferLen, outAddr, outArray, outOfs,
+						       outLen);
+		}
+		bytesBufferedInt += (blockBufferLen - bufRes);
+		blockBufferLen = 0;
+		bytesBuffered = 0;
+	    }
+
+	    if (inLen == 0)
+		return bufRes;
+
+	    if (blockBuffer != null) {
+		newBlockBufferLen = inLen & (blockSize - 1);
+		if (!encrypt && paddingObj != null && newBlockBufferLen == 0)
+		    // Hold the last block in the buffer if we need to unpad
+		    newBlockBufferLen = blockBuffer.length;
+		inLen -= newBlockBufferLen;
+		bufferInputBytes(inBuffer, newBlockBufferLen);
+	    }
+
+	    if (inAddr == 0 && inArray == null) {
+		inArray = new byte[inLen];
+		inBuffer.get(inArray);
+	    } else {
+		inBuffer.position(inBuffer.position() + inLen);
+	    }
+
+	    if (inLen > 0) {
+		if (encrypt) {
+                    inRes = token.p11.C_EncryptUpdate(session.id(), inAddr, inArray, inOfs,
+						      inLen, outAddr, outArray, (outOfs + bufRes),
+						      (outLen - bufRes));
+		} else {
+                    inRes = token.p11.C_DecryptUpdate(session.id(), inAddr, inArray, inOfs,
+						      inLen, outAddr, outArray, (outOfs + bufRes),
+						      (outLen - bufRes));
+		}
+		bytesBufferedInt += (inLen - inRes);
+	    }
+
+	    int total = inRes + bufRes;
             if (!(outBuffer instanceof DirectBuffer) &&
                     !outBuffer.hasArray()) {
-                outBuffer.put(outArray, outOfs, k);
+                outBuffer.put(outArray, outOfs, total);
             } else {
-                outBuffer.position(outBuffer.position() + k);
+                outBuffer.position(outBuffer.position() + total);
             }
-            return k;
+	    return total;
         } catch (PKCS11Exception e) {
             // Reset input buffer to its original position for
             inBuffer.position(origPos);
@@ -722,45 +760,69 @@
             throws ShortBufferException, IllegalBlockSizeException,
             BadPaddingException {
         int requiredOutLen = doFinalLength(0);
+	boolean updating = false;
+	PKCS11Exception err = null;
+
         if (outLen < requiredOutLen) {
             throw new ShortBufferException();
         }
         try {
             ensureInitialized();
             int k = 0;
-            if (encrypt) {
-                if (paddingObj != null) {
-                    int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
-                            requiredOutLen - bytesBuffered);
-                    k = token.p11.C_EncryptUpdate(session.id(),
-                            0, padBuffer, 0, actualPadLen,
-                            0, out, outOfs, outLen);
-                }
+	    if (encrypt) {
+		// Do we need to pad?
+		if (paddingObj != null) {
+		    int actualPadLen = paddingObj.setPaddingBytes(blockBuffer,
+			blockBufferLen, blockSize - blockBufferLen);
+		    blockBufferLen = blockSize;
+		}
+		updating = true;
+		k = token.p11.C_EncryptUpdate(session.id(),
+					      0, blockBuffer, 0, blockBufferLen,
+					      0, out, outOfs, outLen);
+		updating = false;
                 k += token.p11.C_EncryptFinal(session.id(),
                         0, out, (outOfs + k), (outLen - k));
             } else {
+		if (blockBufferLen != 0) {
+		    if (paddingObj == null)
+			k = token.p11.C_DecryptUpdate(session.id(), 0,
+						      blockBuffer, 0, blockBufferLen, 0,
+						      out, outOfs, outLen);
+		    else
+			k = token.p11.C_DecryptUpdate(session.id(), 0,
+						      blockBuffer, 0, blockBufferLen, 0,
+						      padBuffer, 0, padBuffer.length);
+		}
                 if (paddingObj != null) {
-                    if (padBufferLen != 0) {
-                        k = token.p11.C_DecryptUpdate(session.id(), 0,
-                                padBuffer, 0, padBufferLen, 0, padBuffer, 0,
-                                padBuffer.length);
-                    }
                     k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k,
                             padBuffer.length - k);
                     int actualPadLen = paddingObj.unpad(padBuffer, k);
                     k -= actualPadLen;
                     System.arraycopy(padBuffer, 0, out, outOfs, k);
                 } else {
-                    k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs,
-                            outLen);
+                    k += token.p11.C_DecryptFinal(session.id(), 0, out, (outOfs + k),
+						  (outLen - k));
                 }
             }
             return k;
         } catch (PKCS11Exception e) {
+	    err = e;
             handleException(e);
             throw new ProviderException("doFinal() failed", e);
         } finally {
-            reset();
+            if (err != null) {
+		if (err.getErrorCode() != CKR_BUFFER_TOO_SMALL) {
+		    if (updating)
+			// Work around NSS not cancelling the
+			// operation on an error in update
+			cancelOperation();
+		    else
+			reset();
+		}
+	    } else {
+		reset();
+	    }
         }
     }
 
@@ -769,6 +831,9 @@
             BadPaddingException {
         int outLen = outBuffer.remaining();
         int requiredOutLen = doFinalLength(0);
+	boolean updating = false;
+	PKCS11Exception err = null;
+
         if (outLen < requiredOutLen) {
             throw new ShortBufferException();
         }
@@ -793,24 +858,32 @@
 
             int k = 0;
 
-            if (encrypt) {
-                if (paddingObj != null) {
-                    int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
-                            requiredOutLen - bytesBuffered);
-                    k = token.p11.C_EncryptUpdate(session.id(),
-                            0, padBuffer, 0, actualPadLen,
-                            outAddr, outArray, outOfs, outLen);
-                }
+	    if (encrypt) {
+		// Do we need to pad?
+		if (paddingObj != null) {
+		    int actualPadLen = paddingObj.setPaddingBytes(blockBuffer,
+			blockBufferLen, blockSize - blockBufferLen);
+		    blockBufferLen = blockSize;
+		}
+		updating = true;
+		k = token.p11.C_EncryptUpdate(session.id(),
+						  0, blockBuffer, 0, blockBufferLen,
+						  outAddr, outArray, outOfs, outLen);
+		updating = false;
                 k += token.p11.C_EncryptFinal(session.id(),
                         outAddr, outArray, (outOfs + k), (outLen - k));
-            } else {
+	    } else {
+		if (blockBufferLen != 0) {
+		    k = token.p11.C_DecryptUpdate(session.id(), 0,
+						  blockBuffer, 0, blockBufferLen, outAddr,
+						  outArray, outOfs, outLen);
+		} else {
+			k = token.p11.C_DecryptUpdate(session.id(), 0,
+						      blockBuffer, 0, blockBufferLen, 0,
+						      padBuffer, 0, padBuffer.length);
+		}
+
                 if (paddingObj != null) {
-                    if (padBufferLen != 0) {
-                        k = token.p11.C_DecryptUpdate(session.id(),
-                                0, padBuffer, 0, padBufferLen,
-                                0, padBuffer, 0, padBuffer.length);
-                        padBufferLen = 0;
-                    }
                     k += token.p11.C_DecryptFinal(session.id(),
                             0, padBuffer, k, padBuffer.length - k);
                     int actualPadLen = paddingObj.unpad(padBuffer, k);
@@ -819,7 +892,8 @@
                     outOfs = 0;
                 } else {
                     k = token.p11.C_DecryptFinal(session.id(),
-                            outAddr, outArray, outOfs, outLen);
+						 outAddr, outArray,
+						 (outOfs + k), (outLen - k));
                 }
             }
             if ((!encrypt && paddingObj != null) ||
@@ -831,10 +905,22 @@
             }
             return k;
         } catch (PKCS11Exception e) {
+	    err = e;
             handleException(e);
             throw new ProviderException("doFinal() failed", e);
         } finally {
-            reset();
+            if (err != null) {
+		if (err.getErrorCode() != CKR_BUFFER_TOO_SMALL) {
+		    if (updating)
+			// Work around NSS not cancelling the
+			// operation on an error in update
+			cancelOperation();
+		    else
+			reset();
+		}
+	    } else {
+		reset();
+	    }
         }
     }
 
@@ -875,14 +961,14 @@
     }
 
     private final void bufferInputBytes(byte[] in, int inOfs, int len) {
-        System.arraycopy(in, inOfs, padBuffer, padBufferLen, len);
-        padBufferLen += len;
+        System.arraycopy(in, inOfs, blockBuffer, blockBufferLen, len);
+        blockBufferLen += len;
         bytesBuffered += len;
     }
 
     private final void bufferInputBytes(ByteBuffer inBuffer, int len) {
-        inBuffer.get(padBuffer, padBufferLen, len);
-        padBufferLen += len;
+        inBuffer.get(blockBuffer, blockBufferLen, len);
+        blockBufferLen += len;
         bytesBuffered += len;
     }
 }