--- a/jdk/src/share/classes/sun/security/pkcs11/P11Cipher.java Mon Mar 17 12:27:58 2008 -0700
+++ b/jdk/src/share/classes/sun/security/pkcs11/P11Cipher.java Thu Mar 20 16:02:23 2008 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2003-2008 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
@@ -22,10 +22,10 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
-
package sun.security.pkcs11;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
@@ -34,7 +34,6 @@
import javax.crypto.spec.*;
import sun.nio.ch.DirectBuffer;
-
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
@@ -43,8 +42,8 @@
* DES, DESede, AES, ARCFOUR, and Blowfish.
*
* This class is designed to support ECB and CBC with NoPadding and
- * PKCS5Padding for both. However, currently only CBC/NoPadding (and
- * ECB/NoPadding for stream ciphers) is functional.
+ * PKCS5Padding for both. It will use its own padding impl if the
+ * native mechanism does not support padding.
*
* Note that PKCS#11 current only supports ECB and CBC. There are no
* provisions for other modes such as CFB, OFB, PCBC, or CTR mode.
@@ -62,10 +61,56 @@
private final static int MODE_CBC = 4;
// padding constant for NoPadding
- private final static int PAD_NONE = 5;
+ private final static int PAD_NONE = 5;
// padding constant for PKCS5Padding
private final static int PAD_PKCS5 = 6;
+ private static interface Padding {
+ // ENC: format the specified buffer with padding bytes and return the
+ // actual padding length
+ int setPaddingBytes(byte[] paddingBuffer, int padLen);
+
+ // DEC: return the length of trailing padding bytes given the specified
+ // padded data
+ int unpad(byte[] paddedData, int ofs, int len)
+ throws BadPaddingException;
+ }
+
+ private static class PKCS5Padding implements Padding {
+
+ private final int blockSize;
+
+ PKCS5Padding(int blockSize)
+ throws NoSuchPaddingException {
+ if (blockSize == 0) {
+ throw new NoSuchPaddingException
+ ("PKCS#5 padding not supported with stream ciphers");
+ }
+ this.blockSize = blockSize;
+ }
+
+ public int setPaddingBytes(byte[] paddingBuffer, int padLen) {
+ Arrays.fill(paddingBuffer, 0, padLen, (byte) (padLen & 0x007f));
+ return padLen;
+ }
+
+ public int unpad(byte[] paddedData, int ofs, int len)
+ throws BadPaddingException {
+ byte padValue = paddedData[ofs + len - 1];
+ if (padValue < 1 || padValue > blockSize) {
+ throw new BadPaddingException("Invalid pad value!");
+ }
+ // sanity check padding bytes
+ int padStartIndex = ofs + len - padValue;
+ for (int i = padStartIndex; i < len; i++) {
+ if (paddedData[i] != padValue) {
+ throw new BadPaddingException("Invalid pad bytes!");
+ }
+ }
+ return padValue;
+ }
+ }
+
// token instance
private final Token token;
@@ -99,65 +144,93 @@
// padding type, on of PAD_* above (PAD_NONE for stream ciphers)
private int paddingType;
+ // when the padding is requested but unsupported by the native mechanism,
+ // we use the following to do padding and necessary data buffering.
+ // padding object which generate padding and unpad the decrypted data
+ private Padding paddingObj;
+ // buffer for holding back the block which contains padding bytes
+ private byte[] padBuffer;
+ private int padBufferLen;
+
// original IV, if in MODE_CBC
private byte[] iv;
- // total number of bytes processed
- private int bytesProcessed;
+ // number of bytes buffered internally by the native mechanism and padBuffer
+ // if we do the padding
+ private int bytesBuffered;
P11Cipher(Token token, String algorithm, long mechanism)
- throws PKCS11Exception {
+ throws PKCS11Exception, NoSuchAlgorithmException {
super();
this.token = token;
this.algorithm = algorithm;
this.mechanism = mechanism;
- keyAlgorithm = algorithm.split("/")[0];
+
+ String algoParts[] = algorithm.split("/");
+ keyAlgorithm = algoParts[0];
+
if (keyAlgorithm.equals("AES")) {
blockSize = 16;
- blockMode = MODE_CBC;
- // XXX change default to PKCS5Padding
- paddingType = PAD_NONE;
- } else if (keyAlgorithm.equals("RC4") || keyAlgorithm.equals("ARCFOUR")) {
+ } else if (keyAlgorithm.equals("RC4") ||
+ keyAlgorithm.equals("ARCFOUR")) {
blockSize = 0;
- blockMode = MODE_ECB;
- paddingType = PAD_NONE;
} else { // DES, DESede, Blowfish
blockSize = 8;
- blockMode = MODE_CBC;
- // XXX change default to PKCS5Padding
- paddingType = PAD_NONE;
+ }
+ this.blockMode =
+ (algoParts.length > 1 ? parseMode(algoParts[1]) : MODE_ECB);
+
+ String defPadding = (blockSize == 0 ? "NoPadding" : "PKCS5Padding");
+ String paddingStr =
+ (algoParts.length > 2 ? algoParts[2] : defPadding);
+ try {
+ engineSetPadding(paddingStr);
+ } catch (NoSuchPaddingException nspe) {
+ // should not happen
+ throw new ProviderException(nspe);
}
session = token.getOpSession();
}
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+ // Disallow change of mode for now since currently it's explicitly
+ // defined in transformation strings
+ throw new NoSuchAlgorithmException("Unsupported mode " + mode);
+ }
+
+ private int parseMode(String mode) throws NoSuchAlgorithmException {
mode = mode.toUpperCase();
+ int result;
if (mode.equals("ECB")) {
- this.blockMode = MODE_ECB;
+ result = MODE_ECB;
} else if (mode.equals("CBC")) {
if (blockSize == 0) {
throw new NoSuchAlgorithmException
("CBC mode not supported with stream ciphers");
}
- this.blockMode = MODE_CBC;
+ result = MODE_CBC;
} else {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
+ return result;
}
// see JCE spec
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
- if (padding.equalsIgnoreCase("NoPadding")) {
+ paddingObj = null;
+ padBuffer = null;
+ padding = padding.toUpperCase();
+ if (padding.equals("NOPADDING")) {
paddingType = PAD_NONE;
- } else if (padding.equalsIgnoreCase("PKCS5Padding")) {
- if (blockSize == 0) {
- throw new NoSuchPaddingException
- ("PKCS#5 padding not supported with stream ciphers");
+ } else if (padding.equals("PKCS5PADDING")) {
+ paddingType = PAD_PKCS5;
+ if (mechanism != CKM_DES_CBC_PAD && mechanism != CKM_DES3_CBC_PAD &&
+ mechanism != CKM_AES_CBC_PAD) {
+ // no native padding support; use our own padding impl
+ paddingObj = new PKCS5Padding(blockSize);
+ padBuffer = new byte[blockSize];
}
- paddingType = PAD_PKCS5;
- // XXX PKCS#5 not yet implemented
- throw new NoSuchPaddingException("pkcs5");
} else {
throw new NoSuchPaddingException("Unsupported padding " + padding);
}
@@ -175,7 +248,7 @@
// see JCE spec
protected byte[] engineGetIV() {
- return (iv == null) ? null : (byte[])iv.clone();
+ return (iv == null) ? null : (byte[]) iv.clone();
}
// see JCE spec
@@ -185,8 +258,9 @@
}
IvParameterSpec ivSpec = new IvParameterSpec(iv);
try {
- AlgorithmParameters params = AlgorithmParameters.getInstance
- (keyAlgorithm, P11Util.getSunJceProvider());
+ AlgorithmParameters params =
+ AlgorithmParameters.getInstance(keyAlgorithm,
+ P11Util.getSunJceProvider());
params.init(ivSpec);
return params;
} catch (GeneralSecurityException e) {
@@ -210,38 +284,38 @@
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
- byte[] iv;
+ byte[] ivValue;
if (params != null) {
if (params instanceof IvParameterSpec == false) {
throw new InvalidAlgorithmParameterException
("Only IvParameterSpec supported");
}
- IvParameterSpec ivSpec = (IvParameterSpec)params;
- iv = ivSpec.getIV();
+ IvParameterSpec ivSpec = (IvParameterSpec) params;
+ ivValue = ivSpec.getIV();
} else {
- iv = null;
+ ivValue = null;
}
- implInit(opmode, key, iv, random);
+ implInit(opmode, key, ivValue, random);
}
// see JCE spec
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
- byte[] iv;
+ byte[] ivValue;
if (params != null) {
try {
IvParameterSpec ivSpec = (IvParameterSpec)
params.getParameterSpec(IvParameterSpec.class);
- iv = ivSpec.getIV();
+ ivValue = ivSpec.getIV();
} catch (InvalidParameterSpecException e) {
throw new InvalidAlgorithmParameterException
("Could not decode IV", e);
}
} else {
- iv = null;
+ ivValue = null;
}
- implInit(opmode, key, iv, random);
+ implInit(opmode, key, ivValue, random);
}
// actual init() implementation
@@ -250,31 +324,31 @@
throws InvalidKeyException, InvalidAlgorithmParameterException {
cancelOperation();
switch (opmode) {
- case Cipher.ENCRYPT_MODE:
- encrypt = true;
- break;
- case Cipher.DECRYPT_MODE:
- encrypt = false;
- break;
- default:
- throw new InvalidAlgorithmParameterException
- ("Unsupported mode: " + opmode);
+ case Cipher.ENCRYPT_MODE:
+ encrypt = true;
+ break;
+ case Cipher.DECRYPT_MODE:
+ encrypt = false;
+ break;
+ default:
+ throw new InvalidAlgorithmParameterException
+ ("Unsupported mode: " + opmode);
}
if (blockMode == MODE_ECB) { // ECB or stream cipher
if (iv != null) {
if (blockSize == 0) {
throw new InvalidAlgorithmParameterException
- ("IV not used with stream ciphers");
+ ("IV not used with stream ciphers");
} else {
throw new InvalidAlgorithmParameterException
- ("IV not used in ECB mode");
+ ("IV not used in ECB mode");
}
}
} else { // MODE_CBC
if (iv == null) {
if (encrypt == false) {
throw new InvalidAlgorithmParameterException
- ("IV must be specified for decryption in CBC mode");
+ ("IV must be specified for decryption in CBC mode");
}
// generate random IV
if (random == null) {
@@ -285,7 +359,7 @@
} else {
if (iv.length != blockSize) {
throw new InvalidAlgorithmParameterException
- ("IV length must match block size");
+ ("IV length must match block size");
}
}
}
@@ -331,63 +405,43 @@
session = token.getOpSession();
}
if (encrypt) {
- token.p11.C_EncryptInit
- (session.id(), new CK_MECHANISM(mechanism, iv), p11Key.keyID);
+ token.p11.C_EncryptInit(session.id(),
+ new CK_MECHANISM(mechanism, iv), p11Key.keyID);
} else {
- token.p11.C_DecryptInit
- (session.id(), new CK_MECHANISM(mechanism, iv), p11Key.keyID);
+ token.p11.C_DecryptInit(session.id(),
+ new CK_MECHANISM(mechanism, iv), p11Key.keyID);
}
- bytesProcessed = 0;
+ bytesBuffered = 0;
+ padBufferLen = 0;
initialized = true;
}
- // XXX the calculations below assume the PKCS#11 implementation is smart.
- // conceivably, not all implementations are and we may need to estimate
- // more conservatively
-
- private int bytesBuffered(int totalLen) {
- if (paddingType == PAD_NONE) {
- // with NoPadding, buffer only the current unfinished block
- return totalLen & (blockSize - 1);
- } else { // PKCS5
- // with PKCS5Padding in decrypt mode, the buffer must never
- // be empty. Buffer a full block instead of nothing.
- int buffered = totalLen & (blockSize - 1);
- if ((buffered == 0) && (encrypt == false)) {
- buffered = blockSize;
- }
- return buffered;
- }
- }
-
// if update(inLen) is called, how big does the output buffer have to be?
private int updateLength(int inLen) {
if (inLen <= 0) {
return 0;
}
- if (blockSize == 0) {
- return inLen;
- } else {
- // bytes that need to be buffered now
- int buffered = bytesBuffered(bytesProcessed);
- // bytes that need to be buffered after this update
- int newBuffered = bytesBuffered(bytesProcessed + inLen);
- return inLen + buffered - newBuffered;
+
+ int result = inLen + bytesBuffered;
+ if (blockSize != 0) {
+ // minus the number of bytes in the last incomplete block.
+ result -= (result & (blockSize - 1));
}
+ return result;
}
// if doFinal(inLen) is called, how big does the output buffer have to be?
private int doFinalLength(int inLen) {
- if (paddingType == PAD_NONE) {
- return updateLength(inLen);
- }
if (inLen < 0) {
return 0;
}
- int buffered = bytesBuffered(bytesProcessed);
- int newProcessed = bytesProcessed + inLen;
- int paddedProcessed = (newProcessed + blockSize) & ~(blockSize - 1);
- return paddedProcessed - bytesProcessed + buffered;
+
+ int result = inLen + bytesBuffered;
+ if (blockSize != 0 && encrypt && paddingType != PAD_NONE) {
+ // add the number of bytes to make the last block complete.
+ result += (blockSize - (result & (blockSize - 1)));
+ }
+ return result;
}
// see JCE spec
@@ -397,6 +451,7 @@
int n = engineUpdate(in, inOfs, inLen, out, 0);
return P11Util.convert(out, 0, n);
} catch (ShortBufferException e) {
+ // convert since the output length is calculated by updateLength()
throw new ProviderException(e);
}
}
@@ -409,6 +464,7 @@
}
// see JCE spec
+ @Override
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException {
return implUpdate(inBuffer, outBuffer);
@@ -422,14 +478,15 @@
int n = engineDoFinal(in, inOfs, inLen, out, 0);
return P11Util.convert(out, 0, n);
} catch (ShortBufferException e) {
+ // convert since the output length is calculated by doFinalLength()
throw new ProviderException(e);
}
}
// see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
- int outOfs) throws ShortBufferException, IllegalBlockSizeException {
- // BadPaddingException {
+ int outOfs) throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
int n = 0;
if ((inLen != 0) && (in != null)) {
n = engineUpdate(in, inOfs, inLen, out, outOfs);
@@ -440,8 +497,10 @@
}
// see JCE spec
+ @Override
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
- throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
int n = engineUpdate(inBuffer, outBuffer);
n += implDoFinal(outBuffer);
return n;
@@ -454,18 +513,55 @@
}
try {
ensureInitialized();
- int k;
+ int k = 0;
if (encrypt) {
- k = token.p11.C_EncryptUpdate
- (session.id(), 0, in, inOfs, inLen, 0, out, outOfs, outLen);
+ k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen,
+ 0, out, outOfs, outLen);
} else {
- k = token.p11.C_DecryptUpdate
- (session.id(), 0, in, inOfs, inLen, 0, out, outOfs, outLen);
+ 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);
+ }
}
- bytesProcessed += inLen;
+ bytesBuffered += (inLen - k);
return k;
} catch (PKCS11Exception e) {
- // XXX throw correct exception
+ if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
+ throw (ShortBufferException)
+ (new ShortBufferException().initCause(e));
+ }
throw new ProviderException("update() failed", e);
}
}
@@ -481,101 +577,167 @@
if (outLen < updateLength(inLen)) {
throw new ShortBufferException();
}
- boolean inPosChanged = false;
+ int origPos = inBuffer.position();
try {
ensureInitialized();
long inAddr = 0;
- int inOfs = inBuffer.position();
+ int inOfs = 0;
byte[] inArray = null;
+
if (inBuffer instanceof DirectBuffer) {
- inAddr = ((DirectBuffer)inBuffer).address();
- } else {
- if (inBuffer.hasArray()) {
- inArray = inBuffer.array();
- inOfs += inBuffer.arrayOffset();
- } else {
- inArray = new byte[inLen];
- inBuffer.get(inArray);
- inOfs = 0;
- inPosChanged = true;
- }
+ inAddr = ((DirectBuffer) inBuffer).address();
+ inOfs = origPos;
+ } else if (inBuffer.hasArray()) {
+ inArray = inBuffer.array();
+ inOfs = (origPos + inBuffer.arrayOffset());
}
long outAddr = 0;
- int outOfs = outBuffer.position();
+ int outOfs = 0;
byte[] outArray = null;
if (outBuffer instanceof DirectBuffer) {
- outAddr = ((DirectBuffer)outBuffer).address();
+ outAddr = ((DirectBuffer) outBuffer).address();
+ outOfs = outBuffer.position();
} else {
if (outBuffer.hasArray()) {
outArray = outBuffer.array();
- outOfs += outBuffer.arrayOffset();
+ outOfs = (outBuffer.position() + outBuffer.arrayOffset());
} else {
outArray = new byte[outLen];
- outOfs = 0;
}
}
- int k;
+ int k = 0;
if (encrypt) {
- k = token.p11.C_EncryptUpdate
- (session.id(), inAddr, inArray, inOfs, inLen,
- outAddr, outArray, outOfs, outLen);
+ 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 {
- k = token.p11.C_DecryptUpdate
- (session.id(), inAddr, inArray, inOfs, inLen,
- outAddr, outArray, outOfs, outLen);
+ 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);
+ }
}
- bytesProcessed += inLen;
- if (!inPosChanged) {
- inBuffer.position(inBuffer.position() + inLen);
- }
+ bytesBuffered += (inLen - k);
if (!(outBuffer instanceof DirectBuffer) &&
- !outBuffer.hasArray()) {
+ !outBuffer.hasArray()) {
outBuffer.put(outArray, outOfs, k);
} else {
outBuffer.position(outBuffer.position() + k);
}
return k;
} catch (PKCS11Exception e) {
- // Un-read the bytes back to input buffer
- if (inPosChanged) {
- inBuffer.position(inBuffer.position() - inLen);
+ // Reset input buffer to its original position for
+ inBuffer.position(origPos);
+ if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
+ throw (ShortBufferException)
+ (new ShortBufferException().initCause(e));
}
- // XXX throw correct exception
throw new ProviderException("update() failed", e);
}
}
private int implDoFinal(byte[] out, int outOfs, int outLen)
- throws ShortBufferException, IllegalBlockSizeException {
- if (outLen < doFinalLength(0)) {
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ int requiredOutLen = doFinalLength(0);
+ if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
try {
ensureInitialized();
+ int k = 0;
if (encrypt) {
- return token.p11.C_EncryptFinal
- (session.id(), 0, out, outOfs, outLen);
+ 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);
+ }
+ k += token.p11.C_EncryptFinal(session.id(),
+ 0, out, (outOfs + k), (outLen - k));
} else {
- return token.p11.C_DecryptFinal
- (session.id(), 0, out, outOfs, outLen);
+ 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, 0, k);
+ k -= actualPadLen;
+ System.arraycopy(padBuffer, 0, out, outOfs, k);
+ } else {
+ k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs,
+ outLen);
+ }
}
+ return k;
} catch (PKCS11Exception e) {
handleException(e);
throw new ProviderException("doFinal() failed", e);
} finally {
initialized = false;
- bytesProcessed = 0;
+ bytesBuffered = 0;
+ padBufferLen = 0;
session = token.releaseSession(session);
}
}
private int implDoFinal(ByteBuffer outBuffer)
- throws ShortBufferException, IllegalBlockSizeException {
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
int outLen = outBuffer.remaining();
- if (outLen < doFinalLength(0)) {
+ int requiredOutLen = doFinalLength(0);
+ if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
@@ -583,30 +745,54 @@
ensureInitialized();
long outAddr = 0;
- int outOfs = outBuffer.position();
byte[] outArray = null;
+ int outOfs = 0;
if (outBuffer instanceof DirectBuffer) {
- outAddr = ((DirectBuffer)outBuffer).address();
+ outAddr = ((DirectBuffer) outBuffer).address();
+ outOfs = outBuffer.position();
} else {
if (outBuffer.hasArray()) {
outArray = outBuffer.array();
- outOfs += outBuffer.arrayOffset();
+ outOfs = outBuffer.position() + outBuffer.arrayOffset();
} else {
outArray = new byte[outLen];
- outOfs = 0;
}
}
- int k;
+ int k = 0;
+
if (encrypt) {
- k = token.p11.C_EncryptFinal
- (session.id(), outAddr, outArray, outOfs, outLen);
+ 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);
+ }
+ k += token.p11.C_EncryptFinal(session.id(),
+ outAddr, outArray, (outOfs + k), (outLen - k));
} else {
- k = token.p11.C_DecryptFinal
- (session.id(), outAddr, outArray, outOfs, outLen);
+ 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, 0, k);
+ k -= actualPadLen;
+ outArray = padBuffer;
+ outOfs = 0;
+ } else {
+ k = token.p11.C_DecryptFinal(session.id(),
+ outAddr, outArray, outOfs, outLen);
+ }
}
- if (!(outBuffer instanceof DirectBuffer) &&
- !outBuffer.hasArray()) {
+ if ((!encrypt && paddingObj != null) ||
+ (!(outBuffer instanceof DirectBuffer) &&
+ !outBuffer.hasArray())) {
outBuffer.put(outArray, outOfs, k);
} else {
outBuffer.position(outBuffer.position() + k);
@@ -617,20 +803,21 @@
throw new ProviderException("doFinal() failed", e);
} finally {
initialized = false;
- bytesProcessed = 0;
+ bytesBuffered = 0;
session = token.releaseSession(session);
}
}
private void handleException(PKCS11Exception e)
- throws IllegalBlockSizeException {
+ throws ShortBufferException, IllegalBlockSizeException {
long errorCode = e.getErrorCode();
- // XXX better check
- if (errorCode == CKR_DATA_LEN_RANGE) {
- throw (IllegalBlockSizeException)new
- IllegalBlockSizeException(e.toString()).initCause(e);
+ if (errorCode == CKR_BUFFER_TOO_SMALL) {
+ throw (ShortBufferException)
+ (new ShortBufferException().initCause(e));
+ } else if (errorCode == CKR_DATA_LEN_RANGE) {
+ throw (IllegalBlockSizeException)
+ (new IllegalBlockSizeException(e.toString()).initCause(e));
}
-
}
// see JCE spec
@@ -649,12 +836,14 @@
}
// see JCE spec
+ @Override
protected int engineGetKeySize(Key key) throws InvalidKeyException {
int n = P11SecretKeyFactory.convertKey
- (token, key, keyAlgorithm).keyLength();
+ (token, key, keyAlgorithm).keyLength();
return n;
}
+ @Override
protected void finalize() throws Throwable {
try {
if ((session != null) && token.isValid()) {
@@ -666,4 +855,15 @@
}
}
+ private final void bufferInputBytes(byte[] in, int inOfs, int len) {
+ System.arraycopy(in, inOfs, padBuffer, padBufferLen, len);
+ padBufferLen += len;
+ bytesBuffered += len;
+ }
+
+ private final void bufferInputBytes(ByteBuffer inBuffer, int len) {
+ inBuffer.get(padBuffer, padBufferLen, len);
+ padBufferLen += len;
+ bytesBuffered += len;
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java Thu Mar 20 16:02:23 2008 -0700
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2008 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 modi
+fy it
+ * under the terms of the GNU General Public License version 2 onl
+y, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, bu
+t WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABIL
+ITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public L
+icense
+ * version 2 for more details (a copy is included in the LICENSE f
+ile that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public Licen
+se version
+ * 2 along with this work; if not, write to the Free Software Foun
+dation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, San
+ta Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional inform
+ation or
+ * have any questions.
+ */
+
+/**
+ * @test %I% %E%
+ * @bug 4898461
+ * @summary basic test for symmetric ciphers with padding
+ * @author Valerie Peng
+ * @library ..
+ */
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+import java.security.*;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+
+public class TestSymmCiphers extends PKCS11Test {
+
+ private static class CI { // class for holding Cipher Information
+
+ String transformation;
+ String keyAlgo;
+ int dataSize;
+
+ CI(String transformation, String keyAlgo, int dataSize) {
+ this.transformation = transformation;
+ this.keyAlgo = keyAlgo;
+ this.dataSize = dataSize;
+ }
+ }
+ private static final CI[] TEST_LIST = {
+ new CI("ARCFOUR", "ARCFOUR", 400),
+ new CI("RC4", "RC4", 401),
+ new CI("DES/CBC/NoPadding", "DES", 400),
+ new CI("DESede/CBC/NoPadding", "DESede", 160),
+ new CI("AES/CBC/NoPadding", "AES", 4800),
+ new CI("Blowfish/CBC/NoPadding", "Blowfish", 24),
+ new CI("DES/cbc/PKCS5Padding", "DES", 6401),
+ new CI("DESede/CBC/PKCS5Padding", "DESede", 402),
+ new CI("AES/CBC/PKCS5Padding", "AES", 30),
+ new CI("Blowfish/CBC/PKCS5Padding", "Blowfish", 19),
+ new CI("DES/ECB/NoPadding", "DES", 400),
+ new CI("DESede/ECB/NoPadding", "DESede", 160),
+ new CI("AES/ECB/NoPadding", "AES", 4800),
+ new CI("DES/ECB/PKCS5Padding", "DES", 32),
+ new CI("DES/ECB/PKCS5Padding", "DES", 6400),
+ new CI("DESede/ECB/PKCS5Padding", "DESede", 400),
+ new CI("AES/ECB/PKCS5Padding", "AES", 64),
+ new CI("DES", "DES", 6400),
+ new CI("DESede", "DESede", 408),
+ new CI("AES", "AES", 128)
+ };
+ private static StringBuffer debugBuf = new StringBuffer();
+
+ public void main(Provider p) throws Exception {
+ // NSS reports CKR_DEVICE_ERROR when the data passed to
+ // its EncryptUpdate/DecryptUpdate is not multiple of blocks
+ int firstBlkSize = 16;
+ boolean status = true;
+ Random random = new Random();
+ try {
+ for (int i = 0; i < TEST_LIST.length; i++) {
+ CI currTest = TEST_LIST[i];
+ System.out.println("===" + currTest.transformation + "===");
+ try {
+ KeyGenerator kg =
+ KeyGenerator.getInstance(currTest.keyAlgo, p);
+ SecretKey key = kg.generateKey();
+ Cipher c1 = Cipher.getInstance(currTest.transformation, p);
+ Cipher c2 = Cipher.getInstance(currTest.transformation,
+ "SunJCE");
+
+ byte[] plainTxt = new byte[currTest.dataSize];
+ random.nextBytes(plainTxt);
+ System.out.println("Testing inLen = " + plainTxt.length);
+
+ c2.init(Cipher.ENCRYPT_MODE, key);
+ AlgorithmParameters params = c2.getParameters();
+ byte[] answer = c2.doFinal(plainTxt);
+ System.out.println("Encryption tests: START");
+ test(c1, Cipher.ENCRYPT_MODE, key, params, firstBlkSize,
+ plainTxt, answer);
+ System.out.println("Encryption tests: DONE");
+ c2.init(Cipher.DECRYPT_MODE, key, params);
+ byte[] answer2 = c2.doFinal(answer);
+ System.out.println("Decryption tests: START");
+ test(c1, Cipher.DECRYPT_MODE, key, params, firstBlkSize,
+ answer, answer2);
+ System.out.println("Decryption tests: DONE");
+ } catch (NoSuchAlgorithmException nsae) {
+ System.out.println("Skipping unsupported algorithm: " +
+ nsae);
+ }
+ }
+ } catch (Exception ex) {
+ // print out debug info when exception is encountered
+ if (debugBuf != null) {
+ System.out.println(debugBuf.toString());
+ debugBuf = new StringBuffer();
+ }
+ throw ex;
+ }
+ }
+
+ private static void test(Cipher cipher, int mode, SecretKey key,
+ AlgorithmParameters params, int firstBlkSize,
+ byte[] in, byte[] answer) throws Exception {
+ // test setup
+ long startTime, endTime;
+ cipher.init(mode, key, params);
+ int outLen = cipher.getOutputSize(in.length);
+ //debugOut("Estimated output size = " + outLen + "\n");
+
+ // test data preparation
+ ByteBuffer inBuf = ByteBuffer.allocate(in.length);
+ inBuf.put(in);
+ inBuf.position(0);
+ ByteBuffer inDirectBuf = ByteBuffer.allocateDirect(in.length);
+ inDirectBuf.put(in);
+ inDirectBuf.position(0);
+ ByteBuffer outBuf = ByteBuffer.allocate(outLen);
+ ByteBuffer outDirectBuf = ByteBuffer.allocateDirect(outLen);
+
+ // test#1: byte[] in + byte[] out
+ //debugOut("Test#1:\n");
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ startTime = System.nanoTime();
+ byte[] temp = cipher.update(in, 0, firstBlkSize);
+ if (temp != null && temp.length > 0) {
+ baos.write(temp, 0, temp.length);
+ }
+ temp = cipher.doFinal(in, firstBlkSize, in.length - firstBlkSize);
+ if (temp != null && temp.length > 0) {
+ baos.write(temp, 0, temp.length);
+ }
+ byte[] testOut1 = baos.toByteArray();
+ endTime = System.nanoTime();
+ perfOut("stream InBuf + stream OutBuf: " +
+ (endTime - startTime));
+ match(testOut1, answer);
+
+ // test#2: Non-direct Buffer in + non-direct Buffer out
+ //debugOut("Test#2:\n");
+ //debugOut("inputBuf: " + inBuf + "\n");
+ //debugOut("outputBuf: " + outBuf + "\n");
+
+ startTime = System.nanoTime();
+ cipher.update(inBuf, outBuf);
+ cipher.doFinal(inBuf, outBuf);
+ endTime = System.nanoTime();
+ perfOut("non-direct InBuf + non-direct OutBuf: " +
+ (endTime - startTime));
+ match(outBuf, answer);
+
+ // test#3: Direct Buffer in + direc Buffer out
+ //debugOut("Test#3:\n");
+ //debugOut("(pre) inputBuf: " + inDirectBuf + "\n");
+ //debugOut("(pre) outputBuf: " + outDirectBuf + "\n");
+
+ startTime = System.nanoTime();
+ cipher.update(inDirectBuf, outDirectBuf);
+ cipher.doFinal(inDirectBuf, outDirectBuf);
+ endTime = System.nanoTime();
+ perfOut("direct InBuf + direct OutBuf: " +
+ (endTime - startTime));
+
+ //debugOut("(post) inputBuf: " + inDirectBuf + "\n");
+ //debugOut("(post) outputBuf: " + outDirectBuf + "\n");
+ match(outDirectBuf, answer);
+
+ // test#4: Direct Buffer in + non-direct Buffer out
+ //debugOut("Test#4:\n");
+ inDirectBuf.position(0);
+ outBuf.position(0);
+ //debugOut("inputBuf: " + inDirectBuf + "\n");
+ //debugOut("outputBuf: " + outBuf + "\n");
+
+ startTime = System.nanoTime();
+ cipher.update(inDirectBuf, outBuf);
+ cipher.doFinal(inDirectBuf, outBuf);
+ endTime = System.nanoTime();
+ perfOut("direct InBuf + non-direct OutBuf: " +
+ (endTime - startTime));
+ match(outBuf, answer);
+
+ // test#5: Non-direct Buffer in + direct Buffer out
+ //debugOut("Test#5:\n");
+ inBuf.position(0);
+ outDirectBuf.position(0);
+
+ //debugOut("(pre) inputBuf: " + inBuf + "\n");
+ //debugOut("(pre) outputBuf: " + outDirectBuf + "\n");
+
+ startTime = System.nanoTime();
+ cipher.update(inBuf, outDirectBuf);
+ cipher.doFinal(inBuf, outDirectBuf);
+ endTime = System.nanoTime();
+ perfOut("non-direct InBuf + direct OutBuf: " +
+ (endTime - startTime));
+
+ //debugOut("(post) inputBuf: " + inBuf + "\n");
+ //debugOut("(post) outputBuf: " + outDirectBuf + "\n");
+ match(outDirectBuf, answer);
+
+ debugBuf = null;
+ }
+
+ private static void perfOut(String msg) {
+ if (debugBuf != null) {
+ debugBuf.append("PERF>" + msg);
+ }
+ }
+
+ private static void debugOut(String msg) {
+ if (debugBuf != null) {
+ debugBuf.append(msg);
+ }
+ }
+
+ private static void match(byte[] b1, byte[] b2) throws Exception {
+ if (b1.length != b2.length) {
+ debugOut("got len : " + b1.length + "\n");
+ debugOut("expect len: " + b2.length + "\n");
+ throw new Exception("mismatch - different length! got: " + b1.length + ", expect: " + b2.length + "\n");
+ } else {
+ for (int i = 0; i < b1.length; i++) {
+ if (b1[i] != b2[i]) {
+ debugOut("got : " + toString(b1) + "\n");
+ debugOut("expect: " + toString(b2) + "\n");
+ throw new Exception("mismatch");
+ }
+ }
+ }
+ }
+
+ private static void match(ByteBuffer bb, byte[] answer) throws Exception {
+ byte[] bbTemp = new byte[bb.position()];
+ bb.position(0);
+ bb.get(bbTemp, 0, bbTemp.length);
+ match(bbTemp, answer);
+ }
+
+ public static void main(String[] args) throws Exception {
+ main(new TestSymmCiphers());
+ }
+}