提交 c07dea0d 编写于 作者: V valeriep

4898461: Support for ECB and CBC/PKCS5Padding

Summary: Add support for ECB mode and PKCS5Padding
Reviewed-by: andreas
上级 fd4ec021
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
* CA 95054 USA or visit www.sun.com if you need additional information or * CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions. * have any questions.
*/ */
package sun.security.pkcs11; package sun.security.pkcs11;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays;
import java.security.*; import java.security.*;
import java.security.spec.*; import java.security.spec.*;
...@@ -34,7 +34,6 @@ import javax.crypto.*; ...@@ -34,7 +34,6 @@ import javax.crypto.*;
import javax.crypto.spec.*; import javax.crypto.spec.*;
import sun.nio.ch.DirectBuffer; import sun.nio.ch.DirectBuffer;
import sun.security.pkcs11.wrapper.*; import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
...@@ -43,8 +42,8 @@ import static sun.security.pkcs11.wrapper.PKCS11Constants.*; ...@@ -43,8 +42,8 @@ import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
* DES, DESede, AES, ARCFOUR, and Blowfish. * DES, DESede, AES, ARCFOUR, and Blowfish.
* *
* This class is designed to support ECB and CBC with NoPadding and * This class is designed to support ECB and CBC with NoPadding and
* PKCS5Padding for both. However, currently only CBC/NoPadding (and * PKCS5Padding for both. It will use its own padding impl if the
* ECB/NoPadding for stream ciphers) is functional. * native mechanism does not support padding.
* *
* Note that PKCS#11 current only supports ECB and CBC. There are no * 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. * provisions for other modes such as CFB, OFB, PCBC, or CTR mode.
...@@ -66,6 +65,52 @@ final class P11Cipher extends CipherSpi { ...@@ -66,6 +65,52 @@ final class P11Cipher extends CipherSpi {
// padding constant for PKCS5Padding // padding constant for PKCS5Padding
private final static int PAD_PKCS5 = 6; 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 // token instance
private final Token token; private final Token token;
...@@ -99,65 +144,93 @@ final class P11Cipher extends CipherSpi { ...@@ -99,65 +144,93 @@ final class P11Cipher extends CipherSpi {
// padding type, on of PAD_* above (PAD_NONE for stream ciphers) // padding type, on of PAD_* above (PAD_NONE for stream ciphers)
private int paddingType; 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 // original IV, if in MODE_CBC
private byte[] iv; private byte[] iv;
// total number of bytes processed // number of bytes buffered internally by the native mechanism and padBuffer
private int bytesProcessed; // if we do the padding
private int bytesBuffered;
P11Cipher(Token token, String algorithm, long mechanism) P11Cipher(Token token, String algorithm, long mechanism)
throws PKCS11Exception { throws PKCS11Exception, NoSuchAlgorithmException {
super(); super();
this.token = token; this.token = token;
this.algorithm = algorithm; this.algorithm = algorithm;
this.mechanism = mechanism; this.mechanism = mechanism;
keyAlgorithm = algorithm.split("/")[0];
String algoParts[] = algorithm.split("/");
keyAlgorithm = algoParts[0];
if (keyAlgorithm.equals("AES")) { if (keyAlgorithm.equals("AES")) {
blockSize = 16; blockSize = 16;
blockMode = MODE_CBC; } else if (keyAlgorithm.equals("RC4") ||
// XXX change default to PKCS5Padding keyAlgorithm.equals("ARCFOUR")) {
paddingType = PAD_NONE;
} else if (keyAlgorithm.equals("RC4") || keyAlgorithm.equals("ARCFOUR")) {
blockSize = 0; blockSize = 0;
blockMode = MODE_ECB;
paddingType = PAD_NONE;
} else { // DES, DESede, Blowfish } else { // DES, DESede, Blowfish
blockSize = 8; blockSize = 8;
blockMode = MODE_CBC; }
// XXX change default to PKCS5Padding this.blockMode =
paddingType = PAD_NONE; (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(); session = token.getOpSession();
} }
protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 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(); mode = mode.toUpperCase();
int result;
if (mode.equals("ECB")) { if (mode.equals("ECB")) {
this.blockMode = MODE_ECB; result = MODE_ECB;
} else if (mode.equals("CBC")) { } else if (mode.equals("CBC")) {
if (blockSize == 0) { if (blockSize == 0) {
throw new NoSuchAlgorithmException throw new NoSuchAlgorithmException
("CBC mode not supported with stream ciphers"); ("CBC mode not supported with stream ciphers");
} }
this.blockMode = MODE_CBC; result = MODE_CBC;
} else { } else {
throw new NoSuchAlgorithmException("Unsupported mode " + mode); throw new NoSuchAlgorithmException("Unsupported mode " + mode);
} }
return result;
} }
// see JCE spec // see JCE spec
protected void engineSetPadding(String padding) protected void engineSetPadding(String padding)
throws NoSuchPaddingException { throws NoSuchPaddingException {
if (padding.equalsIgnoreCase("NoPadding")) { paddingObj = null;
padBuffer = null;
padding = padding.toUpperCase();
if (padding.equals("NOPADDING")) {
paddingType = PAD_NONE; paddingType = PAD_NONE;
} else if (padding.equalsIgnoreCase("PKCS5Padding")) { } else if (padding.equals("PKCS5PADDING")) {
if (blockSize == 0) {
throw new NoSuchPaddingException
("PKCS#5 padding not supported with stream ciphers");
}
paddingType = PAD_PKCS5; paddingType = PAD_PKCS5;
// XXX PKCS#5 not yet implemented if (mechanism != CKM_DES_CBC_PAD && mechanism != CKM_DES3_CBC_PAD &&
throw new NoSuchPaddingException("pkcs5"); mechanism != CKM_AES_CBC_PAD) {
// no native padding support; use our own padding impl
paddingObj = new PKCS5Padding(blockSize);
padBuffer = new byte[blockSize];
}
} else { } else {
throw new NoSuchPaddingException("Unsupported padding " + padding); throw new NoSuchPaddingException("Unsupported padding " + padding);
} }
...@@ -175,7 +248,7 @@ final class P11Cipher extends CipherSpi { ...@@ -175,7 +248,7 @@ final class P11Cipher extends CipherSpi {
// see JCE spec // see JCE spec
protected byte[] engineGetIV() { protected byte[] engineGetIV() {
return (iv == null) ? null : (byte[])iv.clone(); return (iv == null) ? null : (byte[]) iv.clone();
} }
// see JCE spec // see JCE spec
...@@ -185,8 +258,9 @@ final class P11Cipher extends CipherSpi { ...@@ -185,8 +258,9 @@ final class P11Cipher extends CipherSpi {
} }
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);
try { try {
AlgorithmParameters params = AlgorithmParameters.getInstance AlgorithmParameters params =
(keyAlgorithm, P11Util.getSunJceProvider()); AlgorithmParameters.getInstance(keyAlgorithm,
P11Util.getSunJceProvider());
params.init(ivSpec); params.init(ivSpec);
return params; return params;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
...@@ -210,38 +284,38 @@ final class P11Cipher extends CipherSpi { ...@@ -210,38 +284,38 @@ final class P11Cipher extends CipherSpi {
protected void engineInit(int opmode, Key key, protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random) AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException { throws InvalidKeyException, InvalidAlgorithmParameterException {
byte[] iv; byte[] ivValue;
if (params != null) { if (params != null) {
if (params instanceof IvParameterSpec == false) { if (params instanceof IvParameterSpec == false) {
throw new InvalidAlgorithmParameterException throw new InvalidAlgorithmParameterException
("Only IvParameterSpec supported"); ("Only IvParameterSpec supported");
} }
IvParameterSpec ivSpec = (IvParameterSpec)params; IvParameterSpec ivSpec = (IvParameterSpec) params;
iv = ivSpec.getIV(); ivValue = ivSpec.getIV();
} else { } else {
iv = null; ivValue = null;
} }
implInit(opmode, key, iv, random); implInit(opmode, key, ivValue, random);
} }
// see JCE spec // see JCE spec
protected void engineInit(int opmode, Key key, AlgorithmParameters params, protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random) SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException { throws InvalidKeyException, InvalidAlgorithmParameterException {
byte[] iv; byte[] ivValue;
if (params != null) { if (params != null) {
try { try {
IvParameterSpec ivSpec = (IvParameterSpec) IvParameterSpec ivSpec = (IvParameterSpec)
params.getParameterSpec(IvParameterSpec.class); params.getParameterSpec(IvParameterSpec.class);
iv = ivSpec.getIV(); ivValue = ivSpec.getIV();
} catch (InvalidParameterSpecException e) { } catch (InvalidParameterSpecException e) {
throw new InvalidAlgorithmParameterException throw new InvalidAlgorithmParameterException
("Could not decode IV", e); ("Could not decode IV", e);
} }
} else { } else {
iv = null; ivValue = null;
} }
implInit(opmode, key, iv, random); implInit(opmode, key, ivValue, random);
} }
// actual init() implementation // actual init() implementation
...@@ -331,63 +405,43 @@ final class P11Cipher extends CipherSpi { ...@@ -331,63 +405,43 @@ final class P11Cipher extends CipherSpi {
session = token.getOpSession(); session = token.getOpSession();
} }
if (encrypt) { if (encrypt) {
token.p11.C_EncryptInit token.p11.C_EncryptInit(session.id(),
(session.id(), new CK_MECHANISM(mechanism, iv), p11Key.keyID); new CK_MECHANISM(mechanism, iv), p11Key.keyID);
} else { } else {
token.p11.C_DecryptInit token.p11.C_DecryptInit(session.id(),
(session.id(), new CK_MECHANISM(mechanism, iv), p11Key.keyID); new CK_MECHANISM(mechanism, iv), p11Key.keyID);
} }
bytesProcessed = 0; bytesBuffered = 0;
padBufferLen = 0;
initialized = true; 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? // if update(inLen) is called, how big does the output buffer have to be?
private int updateLength(int inLen) { private int updateLength(int inLen) {
if (inLen <= 0) { if (inLen <= 0) {
return 0; return 0;
} }
if (blockSize == 0) {
return inLen; int result = inLen + bytesBuffered;
} else { if (blockSize != 0) {
// bytes that need to be buffered now // minus the number of bytes in the last incomplete block.
int buffered = bytesBuffered(bytesProcessed); result -= (result & (blockSize - 1));
// bytes that need to be buffered after this update
int newBuffered = bytesBuffered(bytesProcessed + inLen);
return inLen + buffered - newBuffered;
} }
return result;
} }
// if doFinal(inLen) is called, how big does the output buffer have to be? // if doFinal(inLen) is called, how big does the output buffer have to be?
private int doFinalLength(int inLen) { private int doFinalLength(int inLen) {
if (paddingType == PAD_NONE) {
return updateLength(inLen);
}
if (inLen < 0) { if (inLen < 0) {
return 0; return 0;
} }
int buffered = bytesBuffered(bytesProcessed);
int newProcessed = bytesProcessed + inLen; int result = inLen + bytesBuffered;
int paddedProcessed = (newProcessed + blockSize) & ~(blockSize - 1); if (blockSize != 0 && encrypt && paddingType != PAD_NONE) {
return paddedProcessed - bytesProcessed + buffered; // add the number of bytes to make the last block complete.
result += (blockSize - (result & (blockSize - 1)));
}
return result;
} }
// see JCE spec // see JCE spec
...@@ -397,6 +451,7 @@ final class P11Cipher extends CipherSpi { ...@@ -397,6 +451,7 @@ final class P11Cipher extends CipherSpi {
int n = engineUpdate(in, inOfs, inLen, out, 0); int n = engineUpdate(in, inOfs, inLen, out, 0);
return P11Util.convert(out, 0, n); return P11Util.convert(out, 0, n);
} catch (ShortBufferException e) { } catch (ShortBufferException e) {
// convert since the output length is calculated by updateLength()
throw new ProviderException(e); throw new ProviderException(e);
} }
} }
...@@ -409,6 +464,7 @@ final class P11Cipher extends CipherSpi { ...@@ -409,6 +464,7 @@ final class P11Cipher extends CipherSpi {
} }
// see JCE spec // see JCE spec
@Override
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer) protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException { throws ShortBufferException {
return implUpdate(inBuffer, outBuffer); return implUpdate(inBuffer, outBuffer);
...@@ -422,14 +478,15 @@ final class P11Cipher extends CipherSpi { ...@@ -422,14 +478,15 @@ final class P11Cipher extends CipherSpi {
int n = engineDoFinal(in, inOfs, inLen, out, 0); int n = engineDoFinal(in, inOfs, inLen, out, 0);
return P11Util.convert(out, 0, n); return P11Util.convert(out, 0, n);
} catch (ShortBufferException e) { } catch (ShortBufferException e) {
// convert since the output length is calculated by doFinalLength()
throw new ProviderException(e); throw new ProviderException(e);
} }
} }
// see JCE spec // see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException, IllegalBlockSizeException { int outOfs) throws ShortBufferException, IllegalBlockSizeException,
// BadPaddingException { BadPaddingException {
int n = 0; int n = 0;
if ((inLen != 0) && (in != null)) { if ((inLen != 0) && (in != null)) {
n = engineUpdate(in, inOfs, inLen, out, outOfs); n = engineUpdate(in, inOfs, inLen, out, outOfs);
...@@ -440,8 +497,10 @@ final class P11Cipher extends CipherSpi { ...@@ -440,8 +497,10 @@ final class P11Cipher extends CipherSpi {
} }
// see JCE spec // see JCE spec
@Override
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer) protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int n = engineUpdate(inBuffer, outBuffer); int n = engineUpdate(inBuffer, outBuffer);
n += implDoFinal(outBuffer); n += implDoFinal(outBuffer);
return n; return n;
...@@ -454,18 +513,55 @@ final class P11Cipher extends CipherSpi { ...@@ -454,18 +513,55 @@ final class P11Cipher extends CipherSpi {
} }
try { try {
ensureInitialized(); ensureInitialized();
int k; int k = 0;
if (encrypt) { if (encrypt) {
k = token.p11.C_EncryptUpdate k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen,
(session.id(), 0, in, inOfs, inLen, 0, out, outOfs, outLen); 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 { } else {
k = token.p11.C_DecryptUpdate bufferInputBytes(in, inOfs, inLen);
(session.id(), 0, in, inOfs, inLen, 0, out, outOfs, outLen); return 0;
} }
bytesProcessed += inLen; }
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; return k;
} catch (PKCS11Exception e) { } 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); throw new ProviderException("update() failed", e);
} }
} }
...@@ -481,56 +577,94 @@ final class P11Cipher extends CipherSpi { ...@@ -481,56 +577,94 @@ final class P11Cipher extends CipherSpi {
if (outLen < updateLength(inLen)) { if (outLen < updateLength(inLen)) {
throw new ShortBufferException(); throw new ShortBufferException();
} }
boolean inPosChanged = false; int origPos = inBuffer.position();
try { try {
ensureInitialized(); ensureInitialized();
long inAddr = 0; long inAddr = 0;
int inOfs = inBuffer.position(); int inOfs = 0;
byte[] inArray = null; byte[] inArray = null;
if (inBuffer instanceof DirectBuffer) { if (inBuffer instanceof DirectBuffer) {
inAddr = ((DirectBuffer)inBuffer).address(); inAddr = ((DirectBuffer) inBuffer).address();
} else { inOfs = origPos;
if (inBuffer.hasArray()) { } else if (inBuffer.hasArray()) {
inArray = inBuffer.array(); inArray = inBuffer.array();
inOfs += inBuffer.arrayOffset(); inOfs = (origPos + inBuffer.arrayOffset());
} else {
inArray = new byte[inLen];
inBuffer.get(inArray);
inOfs = 0;
inPosChanged = true;
}
} }
long outAddr = 0; long outAddr = 0;
int outOfs = outBuffer.position(); int outOfs = 0;
byte[] outArray = null; byte[] outArray = null;
if (outBuffer instanceof DirectBuffer) { if (outBuffer instanceof DirectBuffer) {
outAddr = ((DirectBuffer)outBuffer).address(); outAddr = ((DirectBuffer) outBuffer).address();
outOfs = outBuffer.position();
} else { } else {
if (outBuffer.hasArray()) { if (outBuffer.hasArray()) {
outArray = outBuffer.array(); outArray = outBuffer.array();
outOfs += outBuffer.arrayOffset(); outOfs = (outBuffer.position() + outBuffer.arrayOffset());
} else { } else {
outArray = new byte[outLen]; outArray = new byte[outLen];
outOfs = 0;
} }
} }
int k; int k = 0;
if (encrypt) { if (encrypt) {
k = token.p11.C_EncryptUpdate if (inAddr == 0 && inArray == null) {
(session.id(), inAddr, inArray, inOfs, inLen, inArray = new byte[inLen];
outAddr, outArray, outOfs, outLen); inBuffer.get(inArray);
} else { } else {
k = token.p11.C_DecryptUpdate inBuffer.position(origPos + inLen);
(session.id(), inAddr, inArray, inOfs, inLen, }
k = token.p11.C_EncryptUpdate(session.id(),
inAddr, inArray, inOfs, inLen,
outAddr, outArray, outOfs, outLen); 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;
} }
bytesProcessed += inLen; if (inLen > 0) {
if (!inPosChanged) { if (inAddr == 0 && inArray == null) {
inArray = new byte[inLen];
inBuffer.get(inArray);
} else {
inBuffer.position(inBuffer.position() + inLen); 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);
if (!(outBuffer instanceof DirectBuffer) && if (!(outBuffer instanceof DirectBuffer) &&
!outBuffer.hasArray()) { !outBuffer.hasArray()) {
outBuffer.put(outArray, outOfs, k); outBuffer.put(outArray, outOfs, k);
...@@ -539,43 +673,71 @@ final class P11Cipher extends CipherSpi { ...@@ -539,43 +673,71 @@ final class P11Cipher extends CipherSpi {
} }
return k; return k;
} catch (PKCS11Exception e) { } catch (PKCS11Exception e) {
// Un-read the bytes back to input buffer // Reset input buffer to its original position for
if (inPosChanged) { inBuffer.position(origPos);
inBuffer.position(inBuffer.position() - inLen); if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
throw (ShortBufferException)
(new ShortBufferException().initCause(e));
} }
// XXX throw correct exception
throw new ProviderException("update() failed", e); throw new ProviderException("update() failed", e);
} }
} }
private int implDoFinal(byte[] out, int outOfs, int outLen) private int implDoFinal(byte[] out, int outOfs, int outLen)
throws ShortBufferException, IllegalBlockSizeException { throws ShortBufferException, IllegalBlockSizeException,
if (outLen < doFinalLength(0)) { BadPaddingException {
int requiredOutLen = doFinalLength(0);
if (outLen < requiredOutLen) {
throw new ShortBufferException(); throw new ShortBufferException();
} }
try { try {
ensureInitialized(); ensureInitialized();
int k = 0;
if (encrypt) { if (encrypt) {
return token.p11.C_EncryptFinal if (paddingObj != null) {
(session.id(), 0, out, outOfs, outLen); 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 { } else {
return token.p11.C_DecryptFinal if (paddingObj != null) {
(session.id(), 0, out, outOfs, outLen); 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) { } catch (PKCS11Exception e) {
handleException(e); handleException(e);
throw new ProviderException("doFinal() failed", e); throw new ProviderException("doFinal() failed", e);
} finally { } finally {
initialized = false; initialized = false;
bytesProcessed = 0; bytesBuffered = 0;
padBufferLen = 0;
session = token.releaseSession(session); session = token.releaseSession(session);
} }
} }
private int implDoFinal(ByteBuffer outBuffer) private int implDoFinal(ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException { throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int outLen = outBuffer.remaining(); int outLen = outBuffer.remaining();
if (outLen < doFinalLength(0)) { int requiredOutLen = doFinalLength(0);
if (outLen < requiredOutLen) {
throw new ShortBufferException(); throw new ShortBufferException();
} }
...@@ -583,30 +745,54 @@ final class P11Cipher extends CipherSpi { ...@@ -583,30 +745,54 @@ final class P11Cipher extends CipherSpi {
ensureInitialized(); ensureInitialized();
long outAddr = 0; long outAddr = 0;
int outOfs = outBuffer.position();
byte[] outArray = null; byte[] outArray = null;
int outOfs = 0;
if (outBuffer instanceof DirectBuffer) { if (outBuffer instanceof DirectBuffer) {
outAddr = ((DirectBuffer)outBuffer).address(); outAddr = ((DirectBuffer) outBuffer).address();
outOfs = outBuffer.position();
} else { } else {
if (outBuffer.hasArray()) { if (outBuffer.hasArray()) {
outArray = outBuffer.array(); outArray = outBuffer.array();
outOfs += outBuffer.arrayOffset(); outOfs = outBuffer.position() + outBuffer.arrayOffset();
} else { } else {
outArray = new byte[outLen]; outArray = new byte[outLen];
outOfs = 0;
} }
} }
int k; int k = 0;
if (encrypt) { if (encrypt) {
k = token.p11.C_EncryptFinal if (paddingObj != null) {
(session.id(), outAddr, outArray, outOfs, outLen); 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 {
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 { } else {
k = token.p11.C_DecryptFinal k = token.p11.C_DecryptFinal(session.id(),
(session.id(), outAddr, outArray, outOfs, outLen); outAddr, outArray, outOfs, outLen);
} }
if (!(outBuffer instanceof DirectBuffer) && }
!outBuffer.hasArray()) { if ((!encrypt && paddingObj != null) ||
(!(outBuffer instanceof DirectBuffer) &&
!outBuffer.hasArray())) {
outBuffer.put(outArray, outOfs, k); outBuffer.put(outArray, outOfs, k);
} else { } else {
outBuffer.position(outBuffer.position() + k); outBuffer.position(outBuffer.position() + k);
...@@ -617,20 +803,21 @@ final class P11Cipher extends CipherSpi { ...@@ -617,20 +803,21 @@ final class P11Cipher extends CipherSpi {
throw new ProviderException("doFinal() failed", e); throw new ProviderException("doFinal() failed", e);
} finally { } finally {
initialized = false; initialized = false;
bytesProcessed = 0; bytesBuffered = 0;
session = token.releaseSession(session); session = token.releaseSession(session);
} }
} }
private void handleException(PKCS11Exception e) private void handleException(PKCS11Exception e)
throws IllegalBlockSizeException { throws ShortBufferException, IllegalBlockSizeException {
long errorCode = e.getErrorCode(); long errorCode = e.getErrorCode();
// XXX better check if (errorCode == CKR_BUFFER_TOO_SMALL) {
if (errorCode == CKR_DATA_LEN_RANGE) { throw (ShortBufferException)
throw (IllegalBlockSizeException)new (new ShortBufferException().initCause(e));
IllegalBlockSizeException(e.toString()).initCause(e); } else if (errorCode == CKR_DATA_LEN_RANGE) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(e.toString()).initCause(e));
} }
} }
// see JCE spec // see JCE spec
...@@ -649,12 +836,14 @@ final class P11Cipher extends CipherSpi { ...@@ -649,12 +836,14 @@ final class P11Cipher extends CipherSpi {
} }
// see JCE spec // see JCE spec
@Override
protected int engineGetKeySize(Key key) throws InvalidKeyException { protected int engineGetKeySize(Key key) throws InvalidKeyException {
int n = P11SecretKeyFactory.convertKey int n = P11SecretKeyFactory.convertKey
(token, key, keyAlgorithm).keyLength(); (token, key, keyAlgorithm).keyLength();
return n; return n;
} }
@Override
protected void finalize() throws Throwable { protected void finalize() throws Throwable {
try { try {
if ((session != null) && token.isValid()) { if ((session != null) && token.isValid()) {
...@@ -666,4 +855,15 @@ final class P11Cipher extends CipherSpi { ...@@ -666,4 +855,15 @@ final class P11Cipher extends CipherSpi {
} }
} }
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;
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -601,14 +601,26 @@ public final class SunPKCS11 extends AuthProvider { ...@@ -601,14 +601,26 @@ public final class SunPKCS11 extends AuthProvider {
// XXX attributes for Ciphers (supported modes, padding) // XXX attributes for Ciphers (supported modes, padding)
d(CIP, "ARCFOUR", P11Cipher, s("RC4"), d(CIP, "ARCFOUR", P11Cipher, s("RC4"),
m(CKM_RC4)); m(CKM_RC4));
// XXX only CBC/NoPadding for block ciphers
d(CIP, "DES/CBC/NoPadding", P11Cipher, d(CIP, "DES/CBC/NoPadding", P11Cipher,
m(CKM_DES_CBC)); m(CKM_DES_CBC));
d(CIP, "DES/CBC/PKCS5Padding", P11Cipher,
m(CKM_DES_CBC_PAD, CKM_DES_CBC));
d(CIP, "DES/ECB", P11Cipher, s("DES"),
m(CKM_DES_ECB));
d(CIP, "DESede/CBC/NoPadding", P11Cipher, d(CIP, "DESede/CBC/NoPadding", P11Cipher,
m(CKM_DES3_CBC)); m(CKM_DES3_CBC));
d(CIP, "DESede/CBC/PKCS5Padding", P11Cipher,
m(CKM_DES3_CBC_PAD, CKM_DES3_CBC));
d(CIP, "DESede/ECB", P11Cipher, s("DESede"),
m(CKM_DES3_ECB));
d(CIP, "AES/CBC/NoPadding", P11Cipher, d(CIP, "AES/CBC/NoPadding", P11Cipher,
m(CKM_AES_CBC)); m(CKM_AES_CBC));
d(CIP, "Blowfish/CBC/NoPadding", P11Cipher, d(CIP, "AES/CBC/PKCS5Padding", P11Cipher,
m(CKM_AES_CBC_PAD, CKM_AES_CBC));
d(CIP, "AES/ECB", P11Cipher, s("AES"),
m(CKM_AES_ECB));
d(CIP, "Blowfish/CBC", P11Cipher,
m(CKM_BLOWFISH_CBC)); m(CKM_BLOWFISH_CBC));
// XXX RSA_X_509, RSA_OAEP not yet supported // XXX RSA_X_509, RSA_OAEP not yet supported
......
/*
* 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());
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册