8264849: Add KW and KWP support to PKCS11 provider

Reviewed-by: ascarpino
This commit is contained in:
Valerie Peng 2021-10-19 20:50:56 +00:00
parent bd2b41dd70
commit e63c1486dc
17 changed files with 2089 additions and 99 deletions

View File

@ -39,7 +39,7 @@ import sun.nio.ch.DirectBuffer;
import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* P11 AEAD Cipher implementation class. This class currently supports
@ -393,7 +393,7 @@ final class P11AEADCipher extends CipherSpi {
try {
initialize();
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_MECHANISM_PARAM_INVALID) {
if (e.match(CKR_MECHANISM_PARAM_INVALID)) {
throw new InvalidAlgorithmParameterException("Bad params", e);
}
throw new InvalidKeyException("Could not initialize cipher", e);
@ -416,7 +416,7 @@ final class P11AEADCipher extends CipherSpi {
0, buffer, 0, bufLen);
}
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already cancelled,
// do not fail here. This is part of a defensive mechanism for
@ -812,17 +812,16 @@ final class P11AEADCipher extends CipherSpi {
private void handleException(PKCS11Exception e)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
long errorCode = e.getErrorCode();
if (errorCode == CKR_BUFFER_TOO_SMALL) {
if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException().initCause(e));
} else if (errorCode == CKR_DATA_LEN_RANGE ||
errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {
} else if (e.match(CKR_DATA_LEN_RANGE) ||
e.match(CKR_ENCRYPTED_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(e.toString()).initCause(e));
} else if (errorCode == CKR_ENCRYPTED_DATA_INVALID ||
// Solaris-specific
errorCode == CKR_GENERAL_ERROR) {
} else if (e.match(CKR_ENCRYPTED_DATA_INVALID) ||
e.match(CKR_GENERAL_ERROR)) {
// CKR_GENERAL_ERROR is Solaris-specific workaround
throw (AEADBadTagException)
(new AEADBadTagException(e.toString()).initCause(e));
}

View File

@ -38,7 +38,7 @@ import sun.nio.ch.DirectBuffer;
import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* Cipher implementation class. This class currently supports
@ -456,7 +456,7 @@ final class P11Cipher extends CipherSpi {
token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
}
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already cancelled,
// do not fail here. This is part of a defensive mechanism for
@ -656,7 +656,7 @@ final class P11Cipher extends CipherSpi {
bytesBuffered += (inLen - k);
return k;
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException().initCause(e));
}
@ -780,7 +780,7 @@ final class P11Cipher extends CipherSpi {
} catch (PKCS11Exception e) {
// Reset input buffer to its original position for
inBuffer.position(origPos);
if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException().initCause(e));
}
@ -962,12 +962,11 @@ final class P11Cipher extends CipherSpi {
private void handleException(PKCS11Exception e)
throws ShortBufferException, IllegalBlockSizeException {
long errorCode = e.getErrorCode();
if (errorCode == CKR_BUFFER_TOO_SMALL) {
if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException().initCause(e));
} else if (errorCode == CKR_DATA_LEN_RANGE ||
errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {
} else if (e.match(CKR_DATA_LEN_RANGE) ||
e.match(CKR_ENCRYPTED_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(e.toString()).initCause(e));
}

View File

@ -27,8 +27,12 @@ package sun.security.pkcs11;
import java.security.*;
import java.security.spec.*;
import java.util.Map;
import java.util.HashMap;
import java.util.Locale;
import sun.security.pkcs11.wrapper.PKCS11Exception;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
/**
* KeyFactory base class. Provides common infrastructure for the RSA, DSA,
@ -55,6 +59,28 @@ abstract class P11KeyFactory extends KeyFactorySpi {
this.algorithm = algorithm;
}
private static final Map<String,Long> keyTypes;
static {
keyTypes = new HashMap<String,Long>();
addKeyType("RSA", CKK_RSA);
addKeyType("DSA", CKK_DSA);
addKeyType("DH", CKK_DH);
addKeyType("EC", CKK_EC);
}
private static void addKeyType(String name, long id) {
Long l = Long.valueOf(id);
keyTypes.put(name, l);
keyTypes.put(name.toUpperCase(Locale.ENGLISH), l);
}
// returns the PKCS11 key type of the specified algorithm
static long getPKCS11KeyType(String algorithm) {
Long kt = keyTypes.get(algorithm);
return (kt != null) ? kt.longValue() : -1;
}
/**
* Convert an arbitrary key of algorithm into a P11Key of token.
* Used by P11Signature.init() and RSACipher.init().

View File

@ -73,7 +73,7 @@ import static sun.security.pkcs11.P11Util.*;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
import sun.security.rsa.RSAKeyFactory;
@ -757,7 +757,7 @@ final class P11KeyStore extends KeyStoreSpi {
Throwable cause = e.getCause();
if (cause instanceof PKCS11Exception) {
PKCS11Exception pe = (PKCS11Exception) cause;
if (pe.getErrorCode() == CKR_PIN_INCORRECT) {
if (pe.match(CKR_PIN_INCORRECT)) {
// if password is wrong, the cause of the IOException
// should be an UnrecoverableKeyException
throw new IOException("load failed",
@ -2330,7 +2330,7 @@ final class P11KeyStore extends KeyStoreSpi {
cka_label = new String(attrs[0].getCharArray());
}
} catch (PKCS11Exception pe) {
if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) {
if (!pe.match(CKR_ATTRIBUTE_TYPE_INVALID)) {
throw pe;
}
@ -2371,7 +2371,7 @@ final class P11KeyStore extends KeyStoreSpi {
(session.id(), handle, trustedAttr);
cka_trusted = trustedAttr[0].getBoolean();
} catch (PKCS11Exception pe) {
if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) {
if (pe.match(CKR_ATTRIBUTE_TYPE_INVALID)) {
// XXX NSS, ibutton, sca1000
CKA_TRUSTED_SUPPORTED = false;
if (debug != null) {

View File

@ -0,0 +1,811 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs11;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Locale;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.HexFormat;
import sun.nio.ch.DirectBuffer;
import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
import static sun.security.pkcs11.TemplateManager.*;
import static sun.security.pkcs11.P11Cipher.*;
/**
* P11 KeyWrap Cipher implementation class for native impl which only support
* single part encryption/decryption through C_Encrypt/C_Decrypt() and
* key wrap/unwrap through C_WrapKey/C_UnwrapKey() calls.
* This class currently supports only AES cipher in KW and KWP modes.
*
* For multi-part encryption/decryption, this class has to buffer data until
* doFinal() is called.
*
* @since 18
*/
final class P11KeyWrapCipher extends CipherSpi {
private static final int BLK_SIZE = 8;
// supported mode and padding with AES cipher
private enum KeyWrapType {
KW_NOPADDING("KW", "NOPADDING"),
KW_PKCS5PADDING("KW", "PKCS5PADDING"),
KWP_NOPADDING("KWP", "NOPADDING");
private final String mode;
private final String padding;
private final byte[] defIv;
KeyWrapType(String mode, String padding) {
this.mode = mode;
this.padding = padding;
if (mode.equalsIgnoreCase("KW")) {
this.defIv = new byte[] {
(byte)0xA6, (byte)0xA6, (byte)0xA6, (byte)0xA6,
(byte)0xA6, (byte)0xA6, (byte)0xA6, (byte)0xA6
};
} else {
this.defIv = new byte[] {
(byte)0xA6, (byte)0x59, (byte)0x59, (byte)0xA6
};
}
}
}
// token instance
private final Token token;
// mechanism id
private final long mechanism;
// type of this KeyWrap cipher, one of Transformation enum above
private final KeyWrapType type;
// acceptable key size in bytes, -1 if more than 1 key sizes are accepted
private final int fixedKeySize;
// associated session, if any
private Session session = null;
// key, if init() was called
private P11Key p11Key = null;
// flag indicating whether an operation is initialized
private boolean initialized = false;
private int opmode = Cipher.ENCRYPT_MODE;
// parameters
private byte[] iv = null;
private SecureRandom random = JCAUtil.getSecureRandom();
// dataBuffer for storing enc/dec data; cleared upon doFinal calls
private ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
P11KeyWrapCipher(Token token, String algorithm, long mechanism)
throws PKCS11Exception, NoSuchAlgorithmException {
super();
this.token = token;
this.mechanism = mechanism;
// javax.crypto.Cipher ensures algoParts.length == 3
String[] algoParts = algorithm.split("/");
if (algoParts[0].startsWith("AES")) {
int index = algoParts[0].indexOf('_');
fixedKeySize = (index == -1? -1 :
// should be well-formed since we specify what we support
Integer.parseInt(algoParts[0].substring(index+1)) >> 3);
try {
this.type = KeyWrapType.valueOf(algoParts[1].toUpperCase() +
"_" + algoParts[2].toUpperCase());
} catch (IllegalArgumentException iae) {
throw new NoSuchAlgorithmException("Unsupported algorithm " +
algorithm);
}
} else {
throw new NoSuchAlgorithmException("Unsupported algorithm " +
algorithm);
}
}
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
// see JCE spec
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {
throw new NoSuchPaddingException("Unsupported padding " + padding);
}
}
// see JCE spec
@Override
protected int engineGetBlockSize() {
return BLK_SIZE;
}
// see JCE spec
@Override
protected int engineGetOutputSize(int inputLen) {
return doFinalLength(inputLen);
}
// see JCE spec
@Override
protected byte[] engineGetIV() {
return (iv == null) ? null : iv.clone();
}
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
// KW and KWP uses but not require parameters, return the default
// IV when no IV is supplied by caller
byte[] iv = (this.iv == null? type.defIv : this.iv);
AlgorithmParameterSpec spec = new IvParameterSpec(iv);
try {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(spec);
return params;
} catch (GeneralSecurityException e) {
// NoSuchAlgorithmException, NoSuchProviderException
// InvalidParameterSpecException
throw new ProviderException("Could not encode parameters", e);
}
}
// see JCE spec
protected void engineInit(int opmode, Key key, SecureRandom sr)
throws InvalidKeyException {
try {
implInit(opmode, key, null, sr);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException("init() failed", e);
}
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null && !(params instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only IvParameterSpec is supported");
}
byte[] ivValue = (params == null? null :
((IvParameterSpec)params).getIV());
implInit(opmode, key, ivValue, sr);
}
// see JCE spec
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
AlgorithmParameterSpec paramSpec = null;
if (params != null) {
try {
paramSpec = params.getParameterSpec(IvParameterSpec.class);
} catch (InvalidParameterSpecException ex) {
throw new InvalidAlgorithmParameterException(ex);
}
}
engineInit(opmode, key, paramSpec, sr);
}
// actual init() implementation
private void implInit(int opmode, Key key, byte[] iv, SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
reset(true);
if (fixedKeySize != -1) {
int keySize;
if (key instanceof P11Key) {
keySize = ((P11Key) key).length() >> 3;
} else {
byte[] encoding = key.getEncoded();
Arrays.fill(encoding, (byte) 0);
keySize = encoding.length;
}
if (keySize != fixedKeySize) {
throw new InvalidKeyException("Key size is invalid");
}
}
P11Key newKey = P11SecretKeyFactory.convertKey(token, key, "AES");
this.opmode = opmode;
if (iv == null) {
iv = type.defIv;
} else {
if (type == KeyWrapType.KWP_NOPADDING &&
!Arrays.equals(iv, type.defIv)) {
throw new InvalidAlgorithmParameterException
("For KWP mode, IV must has value 0x" +
HexFormat.of().withUpperCase().formatHex(type.defIv));
} else if (iv.length != type.defIv.length) {
throw new InvalidAlgorithmParameterException
("Wrong IV length, expected " + type.defIv.length +
" but got " + iv.length);
}
}
this.iv = iv;
this.p11Key = newKey;
if (sr != null) {
this.random = sr;
}
try {
initialize();
} catch (PKCS11Exception e) {
if (e.match(CKR_MECHANISM_PARAM_INVALID)) {
throw new InvalidAlgorithmParameterException("Bad params", e);
}
throw new InvalidKeyException("Could not initialize cipher", e);
}
}
private void cancelOperation() {
// cancel operation by finishing it; avoid killSession as some
// hardware vendors may require re-login
byte[] in = dataBuffer.toByteArray();
int inLen = in.length;
int bufLen = doFinalLength(0);
byte[] buffer = new byte[bufLen];
try {
if (opmode == Cipher.ENCRYPT_MODE) {
token.p11.C_Encrypt(session.id(), 0, in, 0, inLen,
0, buffer, 0, bufLen);
} else if (opmode == Cipher.DECRYPT_MODE) {
token.p11.C_Decrypt(session.id(), 0, in, 0, inLen,
0, buffer, 0, bufLen);
}
} catch (PKCS11Exception e) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already
// cancelled, do not fail here. This is part of a defensive
// mechanism for PKCS#11 libraries that do not strictly follow
// the standard.
return;
}
// ignore failure for en/decryption since it's likely to fail
// due to the minimum length requirement
}
}
private void ensureInitialized() throws PKCS11Exception {
if (!initialized) {
initialize();
}
}
private void initialize() throws PKCS11Exception {
if (p11Key == null) {
throw new ProviderException("Operation cannot be performed without"
+ " calling engineInit first");
}
token.ensureValid();
dataBuffer.reset();
if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.DECRYPT_MODE) {
long p11KeyID = p11Key.getKeyID();
try {
CK_MECHANISM mechWithParams = new CK_MECHANISM(mechanism, iv);
if (session == null) {
session = token.getOpSession();
}
switch (opmode) {
case Cipher.ENCRYPT_MODE:
token.p11.C_EncryptInit(session.id(), mechWithParams,
p11KeyID);
break;
case Cipher.DECRYPT_MODE:
token.p11.C_DecryptInit(session.id(), mechWithParams,
p11KeyID);
break;
}
} catch (PKCS11Exception e) {
session = token.releaseSession(session);
throw e;
} finally {
p11Key.releaseKeyID();
}
}
initialized = true;
}
// if doFinal(inLen) is called, how big does the output buffer have to be?
private int doFinalLength(int inLen) {
if (inLen < 0) {
throw new ProviderException("Invalid negative input length");
}
int result = inLen + dataBuffer.size();
boolean encrypt = (opmode == Cipher.ENCRYPT_MODE ||
opmode == Cipher.WRAP_MODE);
if (encrypt) {
if (type == KeyWrapType.KW_PKCS5PADDING) {
// add potential pad length, i.e. 1-8
result += (BLK_SIZE - (result & (BLK_SIZE - 1)));
} else if (type == KeyWrapType.KWP_NOPADDING &&
(result & (BLK_SIZE - 1)) != 0) {
// add potential pad length, i.e. 0-7
result += (BLK_SIZE - (result & (BLK_SIZE - 1)));
}
result += BLK_SIZE; // add the leading block including the ICV
} else {
result -= BLK_SIZE; // minus the leading block including the ICV
}
return (result > 0? result : 0);
}
// reset the states to the pre-initialized values
// set initialized to false, cancel operation, release session, and
// reset dataBuffer
private void reset(boolean doCancel) {
if (!initialized) {
return;
}
initialized = false;
try {
if (session == null) {
return;
}
if (doCancel && token.explicitCancel) {
cancelOperation();
}
} finally {
session = token.releaseSession(session);
dataBuffer.reset();
}
}
// see JCE spec
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
int n = implUpdate(in, inOfs, inLen);
return new byte[0];
}
// see JCE spec
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException {
implUpdate(in, inOfs, inLen);
return 0;
}
// see JCE spec
@Override
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException {
implUpdate(inBuffer);
return 0;
}
// see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
int maxOutLen = doFinalLength(inLen);
try {
byte[] out = new byte[maxOutLen];
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 {
return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs);
}
// see JCE spec
@Override
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
return implDoFinal(inBuffer, outBuffer);
}
private int implUpdate(byte[] in, int inOfs, int inLen) {
if (inLen > 0) {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("update() failed", e);
}
dataBuffer.write(in, inOfs, inLen);
}
// always 0 as NSS only supports single-part encryption/decryption
return 0;
}
private int implUpdate(ByteBuffer inBuf) {
int inLen = inBuf.remaining();
if (inLen > 0) {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("update() failed", e);
}
byte[] data = new byte[inLen];
inBuf.get(data);
dataBuffer.write(data, 0, data.length);
}
// always 0 as NSS only supports single-part encryption/decryption
return 0;
}
private int implDoFinal(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int requiredOutLen = doFinalLength(inLen);
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
int k = 0;
try {
ensureInitialized();
if (dataBuffer.size() > 0) {
if (in != null && inLen > 0) {
dataBuffer.write(in, inOfs, inLen);
}
in = dataBuffer.toByteArray();
inOfs = 0;
inLen = in.length;
}
if (opmode == Cipher.ENCRYPT_MODE) {
k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen,
0, out, outOfs, outLen);
doCancel = false;
} else {
// Special handling to match SunJCE provider behavior
if (inLen == 0) {
return 0;
}
k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen,
0, out, outOfs, outLen);
doCancel = false;
}
} catch (PKCS11Exception e) {
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
// successful calls to determine the output length. However,
// these cases are not expected here because the output length
// is checked in the OpenJDK side before making the PKCS#11 call.
// Thus, doCancel can safely be 'false'.
doCancel = false;
handleEncException("doFinal() failed", e);
} finally {
reset(doCancel);
}
return k;
}
private int implDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int outLen = outBuffer.remaining();
int inLen = inBuffer.remaining();
int requiredOutLen = doFinalLength(inLen);
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
int k = 0;
try {
ensureInitialized();
long inAddr = 0;
byte[] in = null;
int inOfs = 0;
if (dataBuffer.size() > 0) {
if (inBuffer != null && inLen > 0) {
byte[] temp = new byte[inLen];
inBuffer.get(temp);
dataBuffer.write(temp, 0, temp.length);
}
in = dataBuffer.toByteArray();
inOfs = 0;
inLen = in.length;
} else {
if (inBuffer instanceof DirectBuffer) {
inAddr = ((DirectBuffer) inBuffer).address();
inOfs = inBuffer.position();
} else {
if (inBuffer.hasArray()) {
in = inBuffer.array();
inOfs = inBuffer.position() + inBuffer.arrayOffset();
} else {
in = new byte[inLen];
inBuffer.get(in);
}
}
}
long outAddr = 0;
byte[] outArray = null;
int outOfs = 0;
if (outBuffer instanceof DirectBuffer) {
outAddr = ((DirectBuffer) outBuffer).address();
outOfs = outBuffer.position();
} else {
if (outBuffer.hasArray()) {
outArray = outBuffer.array();
outOfs = outBuffer.position() + outBuffer.arrayOffset();
} else {
outArray = new byte[outLen];
}
}
if (opmode == Cipher.ENCRYPT_MODE) {
k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen,
outAddr, outArray, outOfs, outLen);
doCancel = false;
} else {
// Special handling to match SunJCE provider behavior
if (inLen == 0) {
return 0;
}
k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen,
outAddr, outArray, outOfs, outLen);
doCancel = false;
}
inBuffer.position(inBuffer.limit());
outBuffer.position(outBuffer.position() + k);
} catch (PKCS11Exception e) {
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
// successful calls to determine the output length. However,
// these cases are not expected here because the output length
// is checked in the OpenJDK side before making the PKCS#11 call.
// Thus, doCancel can safely be 'false'.
doCancel = false;
handleEncException("doFinal() failed", e);
} finally {
reset(doCancel);
}
return k;
}
private void handleEncException(String msg, PKCS11Exception e)
throws IllegalBlockSizeException, ShortBufferException,
ProviderException {
if (e.match(CKR_DATA_LEN_RANGE) ||
e.match(CKR_ENCRYPTED_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(msg).initCause(e));
} else if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException(msg).initCause(e));
} else {
throw new ProviderException(msg, e);
}
}
// see JCE spec
protected byte[] engineWrap(Key tbwKey) throws IllegalBlockSizeException,
InvalidKeyException {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("wrap() failed", e);
}
// convert the specified key into P11Key handle
P11Key tbwP11Key = null;
if (!(tbwKey instanceof P11Key)) {
try {
tbwP11Key = (tbwKey instanceof SecretKey?
P11SecretKeyFactory.convertKey(token, tbwKey,
tbwKey.getAlgorithm()) :
P11KeyFactory.convertKey(token, tbwKey,
tbwKey.getAlgorithm()));
} catch (ProviderException pe) {
throw new InvalidKeyException("Cannot convert to PKCS11 key",
pe);
} catch (InvalidKeyException ike) {
// could be algorithms NOT supported by PKCS11 library
// try single part encryption instead
}
} else {
tbwP11Key = (P11Key) tbwKey;
}
long p11KeyID = 0;
try {
if (session == null) {
session = token.getOpSession();
}
p11KeyID = p11Key.getKeyID();
CK_MECHANISM mechWithParams = new CK_MECHANISM(mechanism, iv);
if (tbwP11Key != null) {
long tbwP11KeyID = tbwP11Key.getKeyID();
try {
return token.p11.C_WrapKey(session.id(), mechWithParams,
p11KeyID, tbwP11KeyID);
} finally {
tbwP11Key.releaseKeyID();
}
} else {
byte[] in = tbwKey.getEncoded();
try {
token.p11.C_EncryptInit(session.id(), mechWithParams,
p11KeyID);
int bufLen = doFinalLength(in.length);
byte[] buffer = new byte[bufLen];
token.p11.C_Encrypt(session.id(), 0, in, 0, in.length,
0, buffer, 0, bufLen);
return buffer;
} finally {
Arrays.fill(in, (byte)0);
}
}
} catch (PKCS11Exception e) {
String msg = "wrap() failed";
if (e.match(CKR_KEY_SIZE_RANGE) || e.match(CKR_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(msg).initCause(e));
} else if (e.match(CKR_KEY_NOT_WRAPPABLE) ||
e.match(CKR_KEY_UNEXTRACTABLE) ||
e.match(CKR_KEY_HANDLE_INVALID)) {
throw new InvalidKeyException(msg, e);
} else if (e.match(CKR_MECHANISM_INVALID)) {
throw new UnsupportedOperationException(msg, e);
} else {
throw new ProviderException(msg, e);
}
} finally {
if (p11KeyID != 0) p11Key.releaseKeyID();
reset(false);
}
}
// see JCE spec
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgo,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("unwrap() failed", e);
}
long keyClass;
long keyType;
switch (wrappedKeyType) {
case Cipher.PRIVATE_KEY:
keyClass = CKO_PRIVATE_KEY;
keyType = P11KeyFactory.getPKCS11KeyType(wrappedKeyAlgo);
break;
case Cipher.SECRET_KEY:
keyClass = CKO_SECRET_KEY;
keyType = P11SecretKeyFactory.getPKCS11KeyType(wrappedKeyAlgo);
break;
case Cipher.PUBLIC_KEY:
throw new UnsupportedOperationException
("cannot unwrap public keys");
default: // should never happen
throw new AssertionError();
};
CK_ATTRIBUTE[] attributes;
try {
attributes = new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA_CLASS, keyClass),
new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
};
attributes = token.getAttributes
(O_IMPORT, keyClass, keyType, attributes);
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("unwrap() failed", e);
}
CK_MECHANISM mechParams = new CK_MECHANISM(mechanism, iv);
long p11KeyID = 0;
try {
if (session == null) {
session = token.getOpSession();
}
p11KeyID = p11Key.getKeyID();
long unwrappedKeyID = token.p11.C_UnwrapKey(session.id(),
mechParams, p11KeyID, wrappedKey, attributes);
return (switch(wrappedKeyType) {
case Cipher.PRIVATE_KEY -> P11Key.privateKey
(session, unwrappedKeyID, wrappedKeyAlgo, -1, attributes);
case Cipher.SECRET_KEY -> P11Key.secretKey
(session, unwrappedKeyID, wrappedKeyAlgo, -1, attributes);
default -> null;
});
} catch (PKCS11Exception e) {
String msg = "unwrap() failed";
if (e.match(CKR_UNWRAPPING_KEY_SIZE_RANGE) ||
e.match(CKR_WRAPPED_KEY_INVALID) ||
e.match(CKR_WRAPPED_KEY_LEN_RANGE) ||
e.match(CKR_UNWRAPPING_KEY_HANDLE_INVALID) ||
e.match(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT)) {
throw new InvalidKeyException(msg, e);
} else if (e.match(CKR_MECHANISM_INVALID)) {
throw new UnsupportedOperationException(msg, e);
} else {
throw new ProviderException(msg, e);
}
} finally {
if (p11KeyID != 0) p11Key.releaseKeyID();
reset(false);
}
}
// see JCE spec
@Override
protected int engineGetKeySize(Key key) throws InvalidKeyException {
return P11SecretKeyFactory.convertKey(token, key, "AES").length();
}
}

View File

@ -36,7 +36,7 @@ import sun.nio.ch.DirectBuffer;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* MAC implementation class. This class currently supports HMAC using
@ -152,7 +152,7 @@ final class P11Mac extends MacSpi {
try {
token.p11.C_SignFinal(session.id(), 0);
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already cancelled,
// do not fail here. This is part of a defensive mechanism for

View File

@ -40,7 +40,7 @@ import java.security.interfaces.*;
import sun.security.pkcs11.wrapper.*;
import sun.security.util.KnownOIDs;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* RSASSA-PSS Signature implementation class. This class currently supports the
@ -298,7 +298,7 @@ final class P11PSSSignature extends SignatureSpi {
}
}
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already cancelled,
// do not fail here. This is part of a defensive mechanism for
@ -705,16 +705,9 @@ final class P11PSSSignature extends SignatureSpi {
return true;
} catch (PKCS11Exception pe) {
doCancel = false;
long errorCode = pe.getErrorCode();
if (errorCode == CKR_SIGNATURE_INVALID) {
return false;
}
if (errorCode == CKR_SIGNATURE_LEN_RANGE) {
// return false rather than throwing an exception
return false;
}
// ECF bug?
if (errorCode == CKR_DATA_LEN_RANGE) {
if (pe.match(CKR_SIGNATURE_INVALID) ||
pe.match(CKR_SIGNATURE_LEN_RANGE) ||
pe.match(CKR_DATA_LEN_RANGE)) {
return false;
}
throw new ProviderException(pe);

View File

@ -42,7 +42,7 @@ import sun.security.rsa.RSAPadding;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
import sun.security.util.KeyUtil;
/**
@ -315,7 +315,7 @@ final class P11Signature extends SignatureSpi {
}
}
} catch (PKCS11Exception e) {
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already cancelled,
// do not fail here. This is part of a defensive mechanism for
@ -323,9 +323,8 @@ final class P11Signature extends SignatureSpi {
return;
}
if (mode == M_VERIFY) {
long errorCode = e.getErrorCode();
if ((errorCode == CKR_SIGNATURE_INVALID) ||
(errorCode == CKR_SIGNATURE_LEN_RANGE)) {
if (e.match(CKR_SIGNATURE_INVALID) ||
e.match(CKR_SIGNATURE_LEN_RANGE)) {
// expected since signature is incorrect
return;
}
@ -727,16 +726,9 @@ final class P11Signature extends SignatureSpi {
return true;
} catch (PKCS11Exception pe) {
doCancel = false;
long errorCode = pe.getErrorCode();
if (errorCode == CKR_SIGNATURE_INVALID) {
return false;
}
if (errorCode == CKR_SIGNATURE_LEN_RANGE) {
// return false rather than throwing an exception
return false;
}
// ECF bug?
if (errorCode == CKR_DATA_LEN_RANGE) {
if (pe.match(CKR_SIGNATURE_INVALID) ||
pe.match(CKR_SIGNATURE_LEN_RANGE) ||
pe.match(CKR_DATA_LEN_RANGE)) { // ECF bug?
return false;
}
throw new ProviderException(pe);

View File

@ -52,7 +52,7 @@ import sun.security.pkcs11.Secmod.*;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* PKCS#11 provider main class.
@ -530,6 +530,7 @@ public final class SunPKCS11 extends AuthProvider {
String P11KeyAgreement = "sun.security.pkcs11.P11KeyAgreement";
String P11SecretKeyFactory = "sun.security.pkcs11.P11SecretKeyFactory";
String P11Cipher = "sun.security.pkcs11.P11Cipher";
String P11KeyWrapCipher = "sun.security.pkcs11.P11KeyWrapCipher";
String P11RSACipher = "sun.security.pkcs11.P11RSACipher";
String P11AEADCipher = "sun.security.pkcs11.P11AEADCipher";
String P11Signature = "sun.security.pkcs11.P11Signature";
@ -716,35 +717,59 @@ public final class SunPKCS11 extends AuthProvider {
m(CKM_DES3_ECB));
d(CIP, "AES/CBC/NoPadding", P11Cipher,
m(CKM_AES_CBC));
dA(CIP, "AES_128/CBC/NoPadding", P11Cipher,
dA(CIP, "AES_128/CBC/NoPadding", P11Cipher,
m(CKM_AES_CBC));
dA(CIP, "AES_192/CBC/NoPadding", P11Cipher,
dA(CIP, "AES_192/CBC/NoPadding", P11Cipher,
m(CKM_AES_CBC));
dA(CIP, "AES_256/CBC/NoPadding", P11Cipher,
dA(CIP, "AES_256/CBC/NoPadding", P11Cipher,
m(CKM_AES_CBC));
d(CIP, "AES/CBC/PKCS5Padding", P11Cipher,
m(CKM_AES_CBC_PAD, CKM_AES_CBC));
d(CIP, "AES/ECB/NoPadding", P11Cipher,
m(CKM_AES_ECB));
dA(CIP, "AES_128/ECB/NoPadding", P11Cipher,
dA(CIP, "AES_128/ECB/NoPadding", P11Cipher,
m(CKM_AES_ECB));
dA(CIP, "AES_192/ECB/NoPadding", P11Cipher,
dA(CIP, "AES_192/ECB/NoPadding", P11Cipher,
m(CKM_AES_ECB));
dA(CIP, "AES_256/ECB/NoPadding", P11Cipher,
dA(CIP, "AES_256/ECB/NoPadding", P11Cipher,
m(CKM_AES_ECB));
d(CIP, "AES/ECB/PKCS5Padding", P11Cipher,
List.of("AES"),
m(CKM_AES_ECB));
d(CIP, "AES/CTR/NoPadding", P11Cipher,
m(CKM_AES_CTR));
dA(CIP, "AES/KW/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP));
dA(CIP, "AES_128/KW/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP));
dA(CIP, "AES_192/KW/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP));
dA(CIP, "AES_256/KW/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP));
d(CIP, "AES/KW/PKCS5Padding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_PAD));
d(CIP, "AES_128/KW/PKCS5Padding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_PAD));
d(CIP, "AES_192/KW/PKCS5Padding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_PAD));
d(CIP, "AES_256/KW/PKCS5Padding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_PAD));
dA(CIP, "AES/KWP/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_KWP));
dA(CIP, "AES_128/KWP/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_KWP));
dA(CIP, "AES_192/KWP/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_KWP));
dA(CIP, "AES_256/KWP/NoPadding", P11KeyWrapCipher,
m(CKM_AES_KEY_WRAP_KWP));
d(CIP, "AES/GCM/NoPadding", P11AEADCipher,
m(CKM_AES_GCM));
dA(CIP, "AES_128/GCM/NoPadding", P11AEADCipher,
dA(CIP, "AES_128/GCM/NoPadding", P11AEADCipher,
m(CKM_AES_GCM));
dA(CIP, "AES_192/GCM/NoPadding", P11AEADCipher,
dA(CIP, "AES_192/GCM/NoPadding", P11AEADCipher,
m(CKM_AES_GCM));
dA(CIP, "AES_256/GCM/NoPadding", P11AEADCipher,
dA(CIP, "AES_256/GCM/NoPadding", P11AEADCipher,
m(CKM_AES_GCM));
d(CIP, "Blowfish/CBC/NoPadding", P11Cipher,
@ -1251,6 +1276,9 @@ public final class SunPKCS11 extends AuthProvider {
} else if (algorithm.endsWith("GCM/NoPadding") ||
algorithm.startsWith("ChaCha20-Poly1305")) {
return new P11AEADCipher(token, algorithm, mechanism);
} else if (algorithm.indexOf("/KW/") != -1 ||
algorithm.indexOf("/KWP/") != -1) {
return new P11KeyWrapCipher(token, algorithm, mechanism);
} else {
return new P11Cipher(token, algorithm, mechanism);
}
@ -1505,13 +1533,13 @@ public final class SunPKCS11 extends AuthProvider {
debug.println("login succeeded");
}
} catch (PKCS11Exception pe) {
if (pe.getErrorCode() == CKR_USER_ALREADY_LOGGED_IN) {
if (pe.match(CKR_USER_ALREADY_LOGGED_IN)) {
// let this one go
if (debug != null) {
debug.println("user already logged in");
}
return;
} else if (pe.getErrorCode() == CKR_PIN_INCORRECT) {
} else if (pe.match(CKR_PIN_INCORRECT)) {
FailedLoginException fle = new FailedLoginException();
fle.initCause(pe);
throw fle;
@ -1590,7 +1618,7 @@ public final class SunPKCS11 extends AuthProvider {
debug.println("logout succeeded");
}
} catch (PKCS11Exception pe) {
if (pe.getErrorCode() == CKR_USER_NOT_LOGGED_IN) {
if (pe.match(CKR_USER_NOT_LOGGED_IN)) {
// let this one go
if (debug != null) {
debug.println("user not logged in");

View File

@ -38,7 +38,7 @@ import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.TemplateManager.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* PKCS#11 token.
@ -400,7 +400,7 @@ class Token implements Serializable {
mechanism);
mechInfoMap.put(mechanism, result);
} catch (PKCS11Exception e) {
if (e.getErrorCode() != CKR_MECHANISM_INVALID) {
if (!e.match(CKR_MECHANISM_INVALID)) {
throw e;
} else {
mechInfoMap.put(mechanism, INVALID_MECH);

View File

@ -57,7 +57,7 @@ import java.security.PrivilegedAction;
import sun.security.util.Debug;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* This is the default implementation of the PKCS11 interface. IT connects to
@ -169,7 +169,7 @@ public class PKCS11 {
} catch (PKCS11Exception e) {
// ignore already-initialized error code
// rethrow all other errors
if (e.getErrorCode() != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
if (!e.match(CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
throw e;
}
}

View File

@ -72,37 +72,8 @@ public class PKCS11Exception extends Exception {
protected String errorMsg;
public static final long CKR_GENERAL_ERROR = RV.CKR_GENERAL_ERROR.value;
public static final long CKR_ATTRIBUTE_TYPE_INVALID =
RV.CKR_ATTRIBUTE_TYPE_INVALID.value;
public static final long CKR_DATA_LEN_RANGE = RV.CKR_DATA_LEN_RANGE.value;
public static final long CKR_ENCRYPTED_DATA_INVALID =
RV.CKR_ENCRYPTED_DATA_INVALID.value;
public static final long CKR_ENCRYPTED_DATA_LEN_RANGE =
RV.CKR_ENCRYPTED_DATA_LEN_RANGE.value;
public static final long CKR_MECHANISM_INVALID =
RV.CKR_MECHANISM_INVALID.value;
public static final long CKR_MECHANISM_PARAM_INVALID =
RV.CKR_MECHANISM_PARAM_INVALID.value;
public static final long CKR_OPERATION_NOT_INITIALIZED =
RV.CKR_OPERATION_NOT_INITIALIZED.value;
public static final long CKR_PIN_INCORRECT =
RV.CKR_PIN_INCORRECT.value;
public static final long CKR_SIGNATURE_INVALID =
RV.CKR_SIGNATURE_INVALID.value;
public static final long CKR_SIGNATURE_LEN_RANGE =
RV.CKR_SIGNATURE_LEN_RANGE.value;
public static final long CKR_USER_ALREADY_LOGGED_IN =
RV.CKR_USER_ALREADY_LOGGED_IN.value;
public static final long CKR_USER_NOT_LOGGED_IN =
RV.CKR_USER_NOT_LOGGED_IN.value;
public static final long CKR_BUFFER_TOO_SMALL =
RV.CKR_BUFFER_TOO_SMALL.value;
public static final long CKR_CRYPTOKI_ALREADY_INITIALIZED =
RV.CKR_CRYPTOKI_ALREADY_INITIALIZED.value;
// enum for all PKCS#11 return value
static enum RV {
public static enum RV {
CKR_OK(0x00000000L),
CKR_CANCEL(0x00000001L),
CKR_HOST_MEMORY(0x00000002L),
@ -255,4 +226,14 @@ public class PKCS11Exception extends Exception {
public long getErrorCode() {
return errorCode;
}
/**
* Returns true if the error code matches the the specified enum value.
* @return true if the error code matches the the specified enum value.
* @preconditions
* @postconditions
*/
public boolean match(RV errorEnum) {
return (errorCode == errorEnum.value);
}
}

View File

@ -0,0 +1,395 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8264849
* @library /test/lib ../..
* @run main/othervm NISTWrapKAT
* @summary Verify that the AES-Key-Wrap and AES-Key-Wrap-Pad ciphers
* work as expected using NIST test vectors.
*/
import java.security.Key;
import java.security.AlgorithmParameters;
import java.security.Provider;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Arrays;
import java.math.BigInteger;
// adapted from com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java
public class NISTWrapKAT extends PKCS11Test {
private static final String KEK =
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
private static final String DATA =
"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
// from RFC 3394 sec4
private static String KW_AES128_128 =
"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5";
private static String KW_AES192_128 =
"96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d";
private static String KW_AES192_192 =
"031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2";
private static String KW_AES256_128 =
"64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7";
private static String KW_AES256_192 =
"a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1";
private static String KW_AES256_256 =
"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21";
private static String KWP_AES128_56 = "1B1D4BC2A90B1FA389412B3D40FECB20";
private static String KWP_AES128_112 =
"EA0BFDE8AF063E8918E811A05D2A4C23A367B45315716B5B";
private static String KWP_AES192_56 = "87CE2C5C2D7196E09381056B319D91E9";
private static String KWP_AES192_112 =
"900484950F84EB6ED74CE81DCDACA26E72BB29D4A6F7AC74";
private static String KWP_AES192_168 =
"A402348F1956DB968FDDFD8976420F9DDEB7183CF16B91B0AEB74CAB196C343E";
private static String KWP_AES256_56 = "809BB1864A18938529E97EFCD9544E9A";
private static String KWP_AES256_112 =
"C68168173F141E6D5767611574A941259090DA78D7DF9DF7";
private static String KWP_AES256_168 =
"308D49692B5F8CF638D54BB4B985633504237329964C76EBB3F669870A708DBC";
private static String KWP_AES256_224 =
"0942747DB07032A3F04CDB2E7DE1CBA038F92BC355393AE9A0E4AE8C901912AC3D3AF0F16D240607";
// from RFC 5649 sec6
private static String KEK2 = "5840DF6E29B02AF1AB493B705BF16EA1AE8338F4DCC176A8";
private static byte[] toBytes(String hex, int hexLen) {
if (hexLen < hex.length()) {
hex = hex.substring(0, hexLen);
} else {
hexLen = hex.length();
}
int outLen = hexLen >> 1;
BigInteger temp = new BigInteger(hex, 16);
byte[] val = temp.toByteArray();
if (val.length == outLen) {
return val;
} else {
byte[] out = new byte[outLen];
if (val.length < outLen) {
// enlarge
System.arraycopy(val, 0, out, outLen - val.length, val.length);
} else {
// truncate
System.arraycopy(val, val.length - outLen, out, 0, outLen);
}
return out;
}
}
//@DataProvider
public Object[][] testData() {
return new Object[][] {
{ "AESWrap", KEK, 16, DATA, 16, KW_AES128_128 },
{ "AESWrap", KEK, 24, DATA, 16, KW_AES192_128 },
{ "AESWrap", KEK, 24, DATA, 24, KW_AES192_192 },
{ "AESWrap", KEK, 32, DATA, 16, KW_AES256_128 },
{ "AESWrap", KEK, 32, DATA, 24, KW_AES256_192 },
{ "AESWrap", KEK, 32, DATA, 32, KW_AES256_256 },
{ "AESWrap_128", KEK, 16, DATA, 16, KW_AES128_128 },
{ "AESWrap_192", KEK, 24, DATA, 16, KW_AES192_128 },
{ "AESWrap_256", KEK, 32, DATA, 16, KW_AES256_128 },
{ "AES/KW/NoPadding", KEK, 16, DATA, 16, KW_AES128_128 },
{ "AES/KW/NoPadding", KEK, 24, DATA, 16, KW_AES192_128 },
{ "AES/KW/NoPadding", KEK, 24, DATA, 24, KW_AES192_192 },
{ "AES/KW/NoPadding", KEK, 32, DATA, 16, KW_AES256_128 },
{ "AES/KW/NoPadding", KEK, 32, DATA, 24, KW_AES256_192 },
{ "AES/KW/NoPadding", KEK, 32, DATA, 32, KW_AES256_256 },
{ "AESWrapPad", KEK, 16, DATA, 7, KWP_AES128_56 },
{ "AESWrapPad", KEK, 16, DATA, 14, KWP_AES128_112 },
{ "AESWrapPad", KEK, 24, DATA, 7, KWP_AES192_56 },
{ "AESWrapPad", KEK, 24, DATA, 14, KWP_AES192_112 },
{ "AESWrapPad", KEK, 24, DATA, 21, KWP_AES192_168 },
{ "AESWrapPad", KEK, 32, DATA, 7, KWP_AES256_56 },
{ "AESWrapPad", KEK, 32, DATA, 14, KWP_AES256_112 },
{ "AESWrapPad", KEK, 32, DATA, 21, KWP_AES256_168 },
{ "AESWrapPad", KEK, 32, DATA, 28, KWP_AES256_224 },
{ "AESWrapPad_128", KEK, 16, DATA, 7, KWP_AES128_56 },
{ "AESWrapPad_192", KEK, 24, DATA, 7, KWP_AES192_56 },
{ "AESWrapPad_256", KEK, 32, DATA, 7, KWP_AES256_56 },
{ "AES/KWP/NoPadding", KEK, 16, DATA, 7, KWP_AES128_56 },
{ "AES/KWP/NoPadding", KEK, 16, DATA, 14, KWP_AES128_112 },
{ "AES/KWP/NoPadding", KEK, 24, DATA, 7, KWP_AES192_56 },
{ "AES/KWP/NoPadding", KEK, 24, DATA, 14, KWP_AES192_112 },
{ "AES/KWP/NoPadding", KEK, 24, DATA, 21, KWP_AES192_168 },
{ "AES/KWP/NoPadding", KEK, 32, DATA, 7, KWP_AES256_56 },
{ "AES/KWP/NoPadding", KEK, 32, DATA, 14, KWP_AES256_112 },
{ "AES/KWP/NoPadding", KEK, 32, DATA, 21, KWP_AES256_168 },
{ "AES/KWP/NoPadding", KEK, 32, DATA, 28, KWP_AES256_224 },
{ "AES/KWP/NoPadding", KEK2, 24, "466F7250617369", 7,
"AFBEB0F07DFBF5419200F2CCB50BB24F" },
{ "AES/KWP/NoPadding", KEK2, 24,
"C37B7E6492584340BED12207808941155068F738", 20,
"138BDEAA9B8FA7FC61F97742E72248EE5AE6AE5360D1AE6A5F54F373FA543B6A" },
// some more test vectors for KW and KWP
// from csrc.nist.gov/groups/STM/cavp/documents/mac/kwtestvectors.zip
{ "AES/KW/NoPadding", "7575da3a93607cc2bfd8cec7aadfd9a6", 16,
"42136d3c384a3eeac95a066fd28fed3f", 16,
"031f6bd7e61e643df68594816f64caa3f56fabea2548f5fb" },
{ "AES/KW/NoPadding", "e5d058e7f1c22c016c4e1cc9b26b9f8f", 16,
"7f604e9b8d39d3c91e193fe6f196c1e3da6211a7c9a33b8873b64b138d1803" +
"e4", 32,
"60b9f8ac797c56e01e9b5f84d65816a980777869f67991a0e6dc19b8cd75c9" +
"b54db4a38456bbd6f3" },
{ "AES/KW/NoPadding", "67ae4270bcdd31e8326b7e7f94c80276", 16,
"57e748b62fbc37ba25e904ee973d01b136cf7c1d0c8c5c87", 24,
"96cec0e3272a21faa550a857957aa38ce3c1cf06f0dd9f5b5c5c422cef6c69" +
"a1" },
{ "AES/KW/NoPadding", "d7aa53aefad65cd95b57c8eee7b0a906", 16,
"4a8daee6774751fc4489e837b8f7fba6896c70bb3d5e53053c92eb58046ee4" +
"a7002e542311253b97", 40,
"84ebd38cf06c674dcf186977de4a40c6dde3e7f49361a43420a887d2931b29" +
"c23e2db72e95e4107001da925181bb7097" },
{ "AES/KW/NoPadding", "98311985c4661d7e811ee56070e6fecf", 16,
"18840c96813864ef3093b48cdde6ac5d78248b96d4a2cd1f15f0b56f98213d" +
"bf87e1ccad04e0d4f1954c233ea3e48fdad8f2b1156e54e19e3b5f4a66d2e9" +
"149032b876c51249165fe8c28e112a685b2d228a8ac308017574274af36a4e" +
"a3877bcc9850bafe8fc0e0a712faca0dea98396f9143bc5819fe4933a806e9" +
"b965133e3c695a45f0fbd6961798c400d7477287df64798b651e0d3009c13f" +
"7a2246c28f983509b9e5339919f2cdffcdfc550693cba9491c00334c4a62d8" +
"78c4d0ca57b1104bc0174968ea8e3730b9e68db49678b23cd508ebe3e12e94" +
"b0ad3791023a8ef95473f0b32f906738f34e94e45a4480ad768072e1853adb" +
"63996b9ac27a1dade70804b82290a2274c6dcc3ccd40a8b38a56a5eb03f590" +
"75de015e8f9096f53549f6374e02da947fb849287a447f757cc340b6bded71" +
"d480988b6d2fcd984fba841470519830304667fef0a577b4cf84f76aef9deb" +
"84dde36abfbd76673c17113dbea7a3e24bf9b57a8fd17173a1ef91497b732b" +
"3fa8889bed58a503a0d3e20bc27ec4dbf5d13a93cbad05495e3df15e1fe34a" +
"3a6d6f648ea4aa60b2f114f30944ae593675dac2db188f90a3c483fb82cec0" +
"f0d295544d798b62cdcb51c6c036af1a341d78babf87b92609c1d866e311a4" +
"6abccc8ba8c6a41420359bb061d7e752c0ed25990eef57c9f9e190572203f8" +
"c473edf8cfc8c26d34e37240f45ded97", 512,
"625aea9122b7b57b9f36446f9053acc42c6435a7f69d91b41547026f833291" +
"d488e477c7ccba698c143633a304f463d6af4a3e72c189234fcfc360013e65" +
"b07b7f7a36c529d3fdbbdbd6224bf100c14bc5354893b44790f54c739a2b1f" +
"5bda82d70fb600ed9b0606dbddea52e508b492b72d8779856274aaaaddc0a3" +
"edb6cfc788b603101bedfcc3f44baa62336bd950c2e349d5daf04f2e23ec26" +
"28893d214e277569c565e5e6aa8b72ffa14118a3b57f814b4deb179980b5ee" +
"efa4fd93f1751850466e929be537801babc2120f3ff1ffe5fea813ec7788ea" +
"f43f5ef657e5af48395c3ad11aaf741549090b58670695f7c95c68e00576ca" +
"18ef0313f2b4b757219fc8db3dc2db28721d6f912547ebfebcd96935c3100a" +
"a4e4df9955acae1b4e2c10df1166d46c4285ab631c6d2ce58ad3ae99c07c01" +
"9dcd15958694055281ccd6f803af290431f188cc4c429e84a4c30fd9c63968" +
"dfd0951c417efb71921c207de172a9546bdd3e2bb35b45e140892c649f88c3" +
"1a438f864e801a69f8010aa3d77a26601a7a89067c81b0f7e70d8e82f21f88" +
"c7d0bb0c8ca0db875d6c3f8c6f6d709bbb31c7da2e31f3571daa2c5ab13bfc" +
"16624cf35abd526e84269fb45bbd2fcd8c383d6fbb700bc4b5205b3ef8c432" +
"3dc0d9e0370e56a3d1e5e76aa4de082e4c2a0afd092845bd5dab52a4594318" +
"1461b76e3984b95f48bea80a94944241d04b5634c86274e7" },
{ "AES/KWP/NoPadding", "6decf10a1caf8e3b80c7a4be8c9c84e8", 16,
"49", 1, "01a7d657fc4a5b216f261cca4d052c2b" },
{ "AES/KWP/NoPadding", "a8e06da625a65b25cf5030826830b661", 16,
"43acff293120dd5d", 8, "b6f967616dd8d772e9fea295a456dba7" },
{ "AES/KWP/NoPadding", "7865e20f3c21659ab4690b629cdf3cc4", 16,
"bd6843d420378dc896", 9,
"41eca956d4aa047eb5cf4efe659661e74db6f8c564e23500" },
{ "AES/KWP/NoPadding", "be96dc195ec034d616486ed70e97fe83", 16,
"85b5437b6335ebba7635903a4493d12a77d9357a9e0dbc013456d85f1d3201",
31,
"974769b3a7b4d5d32985f87fddf9990631e5610fbfb278387b58b1f48e05c7" +
"7d2fb7575c5169eb0e" },
{ "AES/KWP/NoPadding", "0e54956a24c7d4a343f90269fb18a17f", 16,
"817ddabdc5d215eee233adff97e92193c6beec52a71340477f70243a794ce9" +
"54af51e356c9940e4ab198f0e68c543355f65ad179cb2d60dd369eaeb9ed14" +
"1fb18c9e4054ac7fdc83506896990a4d20833d2d6e9a34938796ee67c9d7d2" +
"3058544a4a35f2954103ce443a95a7e785602075ca0a73da37899e4568106b" +
"b2dbf1f901377d4d3380c70fa5175ebc550481ac6f15986a4407fde5c23ff3" +
"17e37544c0a25f87117506597db5bb79850c86247b73a5d0090417d63e4c25" +
"7ea0220c2c04db07a34f0ab7954e1dfa2007a1466795c4d0c2aa09ca3986c0" +
"28185b43a466526594afc9c891c263a7c608304bc1957c9873f544dc71e6f8" +
"47c48d32026ed03b2333825452ee7e12a50e1cd7d678319264c65f78001996" +
"d37fae7f9861fbd21cb506c2f8a3b0ee53c7debe17111b6e3f78a5c5677857" +
"b082c2c4943dfd1edf6337fea98a44fc25928361156ef38d865948b979cf6f" +
"4b46bd2119f12f0891cef7fc9d0638fd105fc05f9968d16948d1cb820751e8" +
"2e44cb68e99d4f072ffd1577da6c0631b5827bec7e1b9ec72d18b74cf5f233" +
"e85013c1668ceb5d7a1f5e0f016b0ff726a0a9d41e2cea8e14a2f56492b146" +
"06d3fafd8ac141335f39f90d56863735628e8f17be90e100ef0785f3cd57db" +
"8b9d89a6b2189dc2ea00c285d2657983f8bd7883c215477e67a55556401f1d" +
"8b27d4e0d541c7fb7ace370c2e428884", 512,
"876f3e53ba9cf4f6a521ac198bc813d0ede0f862ab6082e3e0a06ad82b4f27" +
"9582f7c43bb63574608446bc2a05f401a68f74086cf2776b4b3df6b3679c2e" +
"dfb91c024db54c6831e0752ae6f86c7596462de905ee0be908c1b9d043ecaf" +
"e2ad1cbddb904e18ebc9b7a107031be3a87059516a3d1257812d9c801b0b9f" +
"21539e70c47150c128d87c5e58fa6e4371aedde69c7b5cd16b73ac42267632" +
"8131f3ac48c602bb6e0741805aad9d23b33b3523b86cf0588cdf9dc6c4d5f9" +
"fa43d88ca17976eaf48fb37a41a598266da04144373df5631cc5126341c200" +
"a0c8499b29ae96e6e6e6c2bdf8d8903da62bf8ddae970569b695240e77f8ac" +
"5b191da5034008b6ef21936858e69bac372bbafd8794f6b03711503c187552" +
"8a9348681844edb199a0664d740f0f0b1f866c4248c80fe8b5700a3c4134cd" +
"ddb17676e0cd37d6d81831a0f4adfba071bb0935502480eccd48b28be5954e" +
"a6c7d873b51b8bd2b709c5b6132ed31296510915073c18f7012f0eff6a9aad" +
"5340a19fd5e372d35260b718d9e4807b1954c24e6a4fd48e4dbb8f395474e9" +
"9ab577367d2ab5ccaa18c947331047dc3986e213a878b41089aa221019dad4" +
"191a4feefd095f8606c2700a46d71cbb13efb6957df925ec26071c04d04d5a" +
"94e138e5fc5d1f059236aad76208077dcc607b1dd2086f9c04e33f955822b4" +
"57eecd68bd5f24836ecedbac675e6ed93d8a787cb57ad68e" },
};
}
//@Test(dataProvider = "testData")
public void testKeyWrap(String algo, String key, int keyLen,
String data, int dataLen, String expected, Provider p)
throws Exception {
System.out.println("Testing " + algo + " Cipher with wrapping " +
dataLen + "-byte key with " + 8*keyLen + "-bit KEK");
int allowed = Cipher.getMaxAllowedKeyLength("AES");
if (keyLen > allowed) {
System.out.println("=> skip, exceeds max allowed size " + allowed);
return;
}
Cipher c1 = Cipher.getInstance(algo, "SunJCE");
Cipher c2 = Cipher.getInstance(algo, "SunJCE");
Cipher c3 = Cipher.getInstance(algo, "SunJCE");
byte[] keyVal = toBytes(key, keyLen << 1);
byte[] dataVal = toBytes(data, dataLen << 1);
SecretKey cipherKey = new SecretKeySpec(keyVal, "AES");
SecretKey toBeWrappedKey = new SecretKeySpec(dataVal, "AES");
c1.init(Cipher.WRAP_MODE, cipherKey);
IvParameterSpec ivSpec = new IvParameterSpec(c1.getIV());
c2.init(Cipher.WRAP_MODE, cipherKey, ivSpec);
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(ivSpec);
c3.init(Cipher.WRAP_MODE, cipherKey, params);
// first test WRAP with known values
byte[] wrapped = c1.wrap(toBeWrappedKey);
byte[] wrapped2 = c2.wrap(toBeWrappedKey);
byte[] wrapped3 = c3.wrap(toBeWrappedKey);
byte[] expectedVal = toBytes(expected, expected.length());
if (!Arrays.equals(wrapped, expectedVal) ||
!Arrays.equals(wrapped2, expectedVal) ||
!Arrays.equals(wrapped3, expectedVal)) {
throw new Exception("Wrap test failed; got different result");
}
// then test UNWRAP and compare with the initial values
c1.init(Cipher.UNWRAP_MODE, cipherKey);
ivSpec = new IvParameterSpec(c1.getIV());
c2.init(Cipher.UNWRAP_MODE, cipherKey, ivSpec);
params = AlgorithmParameters.getInstance("AES");
params.init(ivSpec);
c3.init(Cipher.UNWRAP_MODE, cipherKey, params);
Key unwrapped = c1.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
Key unwrapped2 = c2.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
Key unwrapped3 = c3.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
if (!Arrays.equals(unwrapped.getEncoded(), dataVal) ||
!Arrays.equals(unwrapped2.getEncoded(), dataVal) ||
!Arrays.equals(unwrapped3.getEncoded(), dataVal)) {
throw new Exception("Unwrap failed; got different result");
}
}
//@Test(dataProvider = "testData")
public void testEnc(String algo, String key, int keyLen, String data,
int dataLen, String expected, Provider p)
throws Exception {
System.out.println("Testing " + algo + " Cipher with enc " +
dataLen + "-byte data with " + 8*keyLen + "-bit KEK");
int allowed = Cipher.getMaxAllowedKeyLength("AES");
if (keyLen > allowed) {
System.out.println("=> skip, exceeds max allowed size " + allowed);
return;
}
Cipher c1 = Cipher.getInstance(algo, "SunJCE");
Cipher c2 = Cipher.getInstance(algo, "SunJCE");
Cipher c3 = Cipher.getInstance(algo, "SunJCE");
byte[] keyVal = toBytes(key, keyLen << 1);
byte[] dataVal = toBytes(data, dataLen << 1);
SecretKey cipherKey = new SecretKeySpec(keyVal, "AES");
c1.init(Cipher.ENCRYPT_MODE, cipherKey);
IvParameterSpec ivSpec = new IvParameterSpec(c1.getIV());
c2.init(Cipher.ENCRYPT_MODE, cipherKey, ivSpec);
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(ivSpec);
c3.init(Cipher.ENCRYPT_MODE, cipherKey, params);
// first test encryption with known values
byte[] ct11 = c1.update(dataVal);
byte[] ct12 = c1.doFinal();
byte[] ct2 = c1.doFinal(dataVal);
byte[] ct22 = c2.doFinal(dataVal);
byte[] ct32 = c3.doFinal(dataVal);
byte[] expectedVal = toBytes(expected, expected.length());
if (ct11 != null || !Arrays.equals(ct12, ct2) ||
!Arrays.equals(ct2, expectedVal) ||
!Arrays.equals(ct22, expectedVal) ||
!Arrays.equals(ct32, expectedVal)) {
throw new Exception("Encryption failed; got different result");
}
// then test decryption and compare with the initial values
c1.init(Cipher.DECRYPT_MODE, cipherKey);
ivSpec = new IvParameterSpec(c1.getIV());
c2.init(Cipher.DECRYPT_MODE, cipherKey, ivSpec);
params = AlgorithmParameters.getInstance("AES");
params.init(ivSpec);
c3.init(Cipher.DECRYPT_MODE, cipherKey, params);
byte[] pt11 = c1.update(ct12);
byte[] pt12 = c1.doFinal();
byte[] pt2 = c1.doFinal(ct2);
byte[] pt22 = c2.doFinal(ct2);
byte[] pt32 = c3.doFinal(ct2);
if (pt11 != null || !Arrays.equals(pt12, pt2) ||
!Arrays.equals(pt2, dataVal) || !Arrays.equals(pt22, dataVal) ||
!Arrays.equals(pt32, dataVal)) {
throw new Exception("Decryption failed; got different result");
}
}
public static void main(String[] args) throws Exception {
main(new NISTWrapKAT(), args);
}
@Override
public void main(Provider p) throws Exception {
Object[][] testDatum = testData();
for (int i = 0; i < testDatum.length; i++) {
Object[] td = testDatum[i];
String algo = (String) td[0];
if (p.getService("Cipher", algo) == null) {
System.out.println("Skip, due to no support: " + algo);
continue;
}
testKeyWrap(algo, (String)td[1], (int)td[2], (String)td[3],
(int)td[4], (String)td[5], p);
testEnc(algo, (String)td[1], (int)td[2], (String)td[3],
(int)td[4], (String)td[5], p);
}
System.out.println("Test Passed");
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import static java.lang.System.out;
import java.lang.Integer;
import java.lang.String;
import java.lang.System;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.KeyPairGenerator;
import java.security.Provider;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/*
* @test
* @bug 8264849
* @summary Tests for key wrap and unwrap operations
* @library /test/lib ../..
* @run main/othervm TestCipherKeyWrapperTest
*/
// adapted from
// com/sun/crypto/provider/Cipher/KeyWrap/TestCipherKeyWrapperTest.java
public class TestCipherKeyWrapperTest extends PKCS11Test {
private static final int LINIMITED_KEYSIZE = 128;
private static final byte[] DATA_32 =
Arrays.copyOf("1234567890123456789012345678901234".getBytes(), 32);
private enum TestVector {
AESWrap("AES", "AESWrap", -1),
AESWrap_128("AES", "AESWrap_128", 128),
AESWrap_192("AES", "AESWrap_192", 192),
AESWrap_256("AES", "AESWrap_256", 256),
AESWrapPad("AES", "AESWrapPad", -1),
AESWrapPad_128("AES", "AESWrapPad_128", 128),
AESWrapPad_192("AES", "AESWrapPad_192", 192),
AESWrapPad_256("AES", "AESWrapPad_256", 256);
final String algo;
final String wrapperAlgo;
final int keySize; // -1 means no restriction
final SecretKey wrapperKey;
private TestVector(String algo, String wrapperAlgo, int kSize) {
this.algo = algo;
this.wrapperAlgo = wrapperAlgo;
this.keySize = kSize;
if (kSize == -1) {
this.wrapperKey = new SecretKeySpec(DATA_32, "AES");
} else {
this.wrapperKey = new SecretKeySpec(DATA_32, 0, kSize >> 3,
"AES");
}
}
};
public static void main(String[] args) throws Exception {
main(new TestCipherKeyWrapperTest(), args);
}
@Override
public void main(Provider p) throws Exception {
for (TestVector tv : TestVector.values()) {
if (p.getService("Cipher", tv.wrapperAlgo) == null) {
System.out.println("Skip, due to no support: " +
tv.wrapperAlgo);
continue;
}
// only run the tests on longer key lengths if unlimited
// version of JCE jurisdiction policy files are installed
if (!(Cipher.getMaxAllowedKeyLength(tv.algo) == Integer.MAX_VALUE)
&& tv.keySize > LINIMITED_KEYSIZE) {
out.println(tv.algo + " will not run if unlimited version of"
+ " JCE jurisdiction policy files are installed");
continue;
}
wrapperSecretKeyTest(p, tv.wrapperAlgo, tv.wrapperKey, tv.algo);
wrapperPrivateKeyTest(p, tv.wrapperAlgo, tv.wrapperKey, "RSA");
}
}
private void wrapperSecretKeyTest(Provider p, String wrapAlgo,
SecretKey key, String algo)
throws Exception {
// Initialization
KeyGenerator kg = KeyGenerator.getInstance(algo, p);
SecretKey skey = kg.generateKey();
wrapTest(p, wrapAlgo, key, skey, Cipher.SECRET_KEY);
}
private void wrapperPrivateKeyTest(Provider p, String wrapAlgo,
SecretKey key, String algo)
throws Exception {
// Key pair generated
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algo, p);
kpg.initialize(2048);
System.out.println("Generate key pair (algorithm: " + algo
+ ", provider: " + kpg.getProvider().getName() + ")");
KeyPair kp = kpg.genKeyPair();
// key generated
wrapTest(p, wrapAlgo, key, kp.getPrivate(), Cipher.PRIVATE_KEY);
}
private void wrapTest(Provider p, String wrapAlgo, SecretKey initKey,
Key tbwKey, int keyType)
throws Exception {
AlgorithmParameters aps = null;
out.println("Testing " + wrapAlgo + " cipher wrap/unwrap");
Cipher wrapCI = Cipher.getInstance(wrapAlgo, p);
byte[] keyEncoding = tbwKey.getEncoded();
if (wrapAlgo.indexOf("Pad") == -1 &&
(keyEncoding.length % wrapCI.getBlockSize() != 0)) {
System.out.println("Skip due to key length: " +
keyEncoding.length);
return;
}
// Wrap & Unwrap operation
System.out.println("calling wrap()");
wrapCI.init(Cipher.WRAP_MODE, initKey);
aps = wrapCI.getParameters();
byte[] keyWrapped = wrapCI.wrap(tbwKey);
wrapCI.init(Cipher.UNWRAP_MODE, initKey, aps);
Key unwrappedKey = wrapCI.unwrap(keyWrapped, tbwKey.getAlgorithm(),
keyType);
// Comparison
if (!Arrays.equals(tbwKey.getEncoded(), unwrappedKey.getEncoded())) {
out.println("key encoding len : " + tbwKey.getEncoded().length);
throw new RuntimeException("Comparation failed testing "
+ wrapAlgo + ":" + keyType);
}
}
}

View File

@ -0,0 +1,333 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8264849
* @summary Verify general properties of the AES/KW/NoPadding,
* AES/KW/PKCS5Padding, and AES/KWP/NoPadding impls of SunPKCS11 provider.
* @library /test/lib ../..
* @run main/othervm TestGeneral
*/
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HexFormat;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
// adapted from com/sun/crypto/provider/Cipher/KeyWrap/TestGeneral.java
public class TestGeneral extends PKCS11Test {
private static final byte[] DATA_32 =
Arrays.copyOf("1234567890123456789012345678901234".getBytes(), 32);
private static final SecretKey KEY =
new SecretKeySpec(DATA_32, 0, 16, "AES");
private static final int KW_IV_LEN = 8;
private static final int KWP_IV_LEN = 4;
private static final int MAX_KW_PKCS5PAD_LEN = 8; // 1-8
private static final int MAX_KWP_PAD_LEN = 7; // 0-7
public static void testEnc(Cipher c, byte[] in, int startLen, int inc,
IvParameterSpec[] ivs, int maxPadLen) throws Exception {
System.out.println("testEnc, input len=" + startLen + " w/ inc=" +
inc);
for (IvParameterSpec iv : ivs) {
System.out.print("\t=> w/ iv=" + iv);
for (int inLen = startLen; inLen < in.length; inLen+=inc) {
c.init(Cipher.ENCRYPT_MODE, KEY, iv);
int estOutLen = c.getOutputSize(inLen);
System.out.println(", inLen=" + inLen);
byte[] out = c.doFinal(in, 0, inLen);
// check the length of encryption output
if (estOutLen != out.length || (out.length % 8 != 0) ||
(out.length - inLen < 8)) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual: " + out.length);
throw new RuntimeException("Failed enc output len check");
}
c.init(Cipher.DECRYPT_MODE, KEY, iv);
estOutLen = c.getOutputSize(out.length);
byte[] recovered = new byte[estOutLen];
// do decryption using ByteBuffer and multi-part
ByteBuffer outBB = ByteBuffer.wrap(out);
ByteBuffer recoveredBB = ByteBuffer.wrap(recovered);
int len = c.update(outBB, recoveredBB);
len += c.doFinal(outBB, recoveredBB);
// check the length of decryption output
if (estOutLen < len || (estOutLen - len) > maxPadLen) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual: " + len);
throw new RuntimeException("Failed dec output len check");
}
if (!Arrays.equals(in, 0, inLen, recovered, 0, len)) {
throw new RuntimeException("Failed decrypted data check");
}
}
}
}
public static void testKAT(Cipher c, String keyStr, String inStr,
String expectedStr) throws Exception {
System.out.println("testKAT, input len: " + inStr.length()/2);
byte[] keyVal = HexFormat.of().parseHex(keyStr);
byte[] in = HexFormat.of().parseHex(inStr);
byte[] expected = HexFormat.of().parseHex(expectedStr);
c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyVal, "AES"));
byte[] out = c.doFinal(in);
if (!Arrays.equals(out, expected)) {
System.out.println("=> expected: " +
HexFormat.of().withUpperCase().formatHex(expected));
System.out.println("=> actual: " +
HexFormat.of().withUpperCase().formatHex(out));
throw new RuntimeException("Failed KAT check");
}
}
public static void testWrap(Cipher c, Key[] inKeys, IvParameterSpec[] ivs,
int maxPadLen) throws Exception {
for (Key inKey : inKeys) {
System.out.println("testWrap, key: " + inKey);
for (IvParameterSpec iv : ivs) {
System.out.println("\t=> w/ iv " + iv);
c.init(Cipher.WRAP_MODE, KEY, iv);
byte[] out = c.wrap(inKey);
// output should always be multiple of cipher block size
if (out.length % c.getBlockSize() != 0) {
throw new RuntimeException("Invalid wrap len: " +
out.length);
}
c.init(Cipher.UNWRAP_MODE, KEY, iv);
// SecretKey or PrivateKey
int keyType = (inKey instanceof SecretKey? Cipher.SECRET_KEY :
Cipher.PRIVATE_KEY);
int estOutLen = c.getOutputSize(out.length);
Key key2 = c.unwrap(out, inKey.getAlgorithm(), keyType);
if ((keyType == Cipher.SECRET_KEY &&
!(key2 instanceof SecretKey)) ||
(keyType == Cipher.PRIVATE_KEY &&
!(key2 instanceof PrivateKey))) {
throw new RuntimeException("Failed unwrap type check");
}
byte[] in2 = key2.getEncoded();
// check decryption output length
if (estOutLen < in2.length ||
(estOutLen - in2.length) > maxPadLen) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual: " + in2.length);
throw new RuntimeException("Failed unwrap len check");
}
if (!Arrays.equals(inKey.getEncoded(), in2) ||
!(inKey.getAlgorithm().equalsIgnoreCase
(key2.getAlgorithm()))) {
throw new RuntimeException("Failed unwrap key check");
}
}
}
}
public static void testIv(Cipher c, int defIvLen, boolean allowCustomIv)
throws Exception {
System.out.println("testIv: defIvLen = " + defIvLen +
" allowCustomIv = " + allowCustomIv);
// get a fresh Cipher instance so we can test iv with pre-init state
c = Cipher.getInstance(c.getAlgorithm(), c.getProvider());
if (c.getIV() != null) {
throw new RuntimeException("Expects null iv");
}
AlgorithmParameters ivParams = c.getParameters();
if (ivParams == null) {
throw new RuntimeException("Expects non-null default parameters");
}
IvParameterSpec ivSpec =
ivParams.getParameterSpec(IvParameterSpec.class);
byte[] iv = ivSpec.getIV();
// try through all opmodes
c.init(Cipher.ENCRYPT_MODE, KEY);
c.init(Cipher.DECRYPT_MODE, KEY);
c.init(Cipher.WRAP_MODE, KEY);
c.init(Cipher.UNWRAP_MODE, KEY);
byte[] defIv = c.getIV();
// try again through all opmodes
c.init(Cipher.ENCRYPT_MODE, KEY);
c.init(Cipher.DECRYPT_MODE, KEY);
c.init(Cipher.WRAP_MODE, KEY);
c.init(Cipher.UNWRAP_MODE, KEY);
byte[] defIv2 = c.getIV();
if (iv.length != defIvLen || !Arrays.equals(iv, defIv) ||
!Arrays.equals(defIv, defIv2)) {
throw new RuntimeException("Failed default iv check");
}
if (defIv == defIv2) {
throw new RuntimeException("Failed getIV copy check");
}
// try init w/ an iv w/ invalid length
try {
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv, 0,
defIv.length/2));
throw new RuntimeException("Invalid iv accepted");
} catch (InvalidAlgorithmParameterException iape) {
System.out.println("Invalid IV rejected as expected");
}
if (allowCustomIv) {
Arrays.fill(defIv, (byte) 0xFF);
// try through all opmodes
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv));
c.init(Cipher.DECRYPT_MODE, KEY, new IvParameterSpec(defIv));
c.init(Cipher.WRAP_MODE, KEY, new IvParameterSpec(defIv));
c.init(Cipher.UNWRAP_MODE, KEY, new IvParameterSpec(defIv));
if (!Arrays.equals(defIv, c.getIV())) {
throw new RuntimeException("Failed set iv check");
}
}
}
public static void main(String[] args) throws Exception {
main(new TestGeneral(), args);
}
@Override
public void main(Provider p) throws Exception {
byte[] data = DATA_32;
SecretKey aes256 = new SecretKeySpec(DATA_32, "AES");
SecretKey any256 = new SecretKeySpec(DATA_32, "ANY");
PrivateKey priv = KeyPairGenerator.getInstance
("RSA", "SunRsaSign").generateKeyPair().getPrivate();
String[] algos = {
"AES/KW/PKCS5Padding", "AES/KW/NoPadding", "AES/KWP/NoPadding"
};
for (String a : algos) {
if (p.getService("Cipher", a) == null) {
System.out.println("Skip, due to no support: " + a);
continue;
}
System.out.println("Testing " + a);
Cipher c = Cipher.getInstance(a, p);
int blkSize = c.getBlockSize();
// set the default based on AES/KWP/NoPadding, the other two
// override as needed
int startLen = data.length - blkSize;
int inc = 1;
IvParameterSpec[] ivs = new IvParameterSpec[] { null };
int padLen = MAX_KWP_PAD_LEN;
Key[] keys = new Key[] { aes256, any256, priv };
int ivLen = KWP_IV_LEN;
boolean allowCustomIv = false;
switch (a) {
case "AES/KW/PKCS5Padding":
ivs = new IvParameterSpec[] {
null, new IvParameterSpec(DATA_32, 0, KW_IV_LEN) };
padLen = MAX_KW_PKCS5PAD_LEN;
ivLen = KW_IV_LEN;
allowCustomIv = true;
break;
case "AES/KW/NoPadding":
testKAT(c, "000102030405060708090A0B0C0D0E0F",
"00112233445566778899AABBCCDDEEFF",
"1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5");
testKAT(c, "000102030405060708090A0B0C0D0E0F1011121314151617",
"00112233445566778899AABBCCDDEEFF",
"96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D");
testKAT(c, "000102030405060708090A0B0C0D0E0F1011121314151617" +
"18191A1B1C1D1E1F",
"00112233445566778899AABBCCDDEEFF",
"64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7");
testKAT(c, "000102030405060708090A0B0C0D0E0F1011121314151617",
"00112233445566778899AABBCCDDEEFF0001020304050607",
"031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA" +
"814915C6762D2");
testKAT(c, "000102030405060708090A0B0C0D0E0F1011121314151617" +
"18191A1B1C1D1E1F",
"00112233445566778899AABBCCDDEEFF0001020304050607",
"A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD" +
"5D17D6B254DA1");
testKAT(c, "000102030405060708090A0B0C0D0E0F1011121314151617" +
"18191A1B1C1D1E1F",
"00112233445566778899AABBCCDDEEFF0001020304050607080" +
"90A0B0C0D0E0F",
"28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC" +
"7F0E71A99F43BFB988B9B7A02DD21");
startLen = data.length >> 1;
inc = blkSize;
ivs = new IvParameterSpec[] {
null, new IvParameterSpec(DATA_32, 0, KW_IV_LEN) };
padLen = 0;
keys = new Key[] { aes256, any256 };
ivLen = KW_IV_LEN;
allowCustomIv = true;
break;
case "AES/KWP/NoPadding":
testKAT(c, "5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8",
"c37b7e6492584340bed12207808941155068f738",
"138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a");
testKAT(c, "5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8",
"466f7250617369", "afbeb0f07dfbf5419200f2ccb50bb24f");
break;
}
// now test based on the configured arguments
testEnc(c, data, startLen, inc, ivs, padLen);
testWrap(c, keys, ivs, padLen);
testIv(c, ivLen, allowCustomIv);
}
System.out.println("All Tests Passed");
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8264849
* @summary Verify cipher key size restriction is enforced properly with IKE
* @library /test/lib ../..
* @run main/othervm TestKeySizeCheck
*/
import java.util.Arrays;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
// adapted from com/sun/crypto/provider/Cipher/KeyWrap/TestKeySizeCheck.java
public class TestKeySizeCheck extends PKCS11Test {
private static final byte[] BYTES_32 =
Arrays.copyOf("1234567890123456789012345678901234".getBytes(), 32);
private static SecretKey getKey(int sizeInBytes) {
if (sizeInBytes <= BYTES_32.length) {
return new SecretKeySpec(BYTES_32, 0, sizeInBytes, "AES");
} else {
return new SecretKeySpec(new byte[sizeInBytes], "AES");
}
}
private static String getModeStr(int mode) {
return (mode == Cipher.ENCRYPT_MODE? "ENC" : "WRAP");
}
public static void test(Provider p, String algo, int[] invalidKeySizes)
throws Exception {
Cipher c = Cipher.getInstance(algo, p);
System.out.println("Testing " + algo);
int[] modes = { Cipher.ENCRYPT_MODE, Cipher.WRAP_MODE };
for (int ks : invalidKeySizes) {
System.out.println("keysize: " + ks);
SecretKey key = getKey(ks);
for (int m : modes) {
try {
c.init(m, key);
throw new RuntimeException("Expected IKE not thrown for "
+ getModeStr(m));
} catch (InvalidKeyException ike) {
System.out.println(" => expected IKE thrown for "
+ getModeStr(m));
}
}
}
}
public static void main(String[] args) throws Exception {
main(new TestKeySizeCheck(), args);
}
@Override
public void main(Provider p) throws Exception {
String[] algos = {
"AESWrap", "AESWrapPad",
"AES/KW/PKCS5Padding", "AES/KW/NoPadding", "AES/KWP/NoPadding"
};
int[] keySizes = { 128, 192, 256 };
for (String a : algos) {
if (p.getService("Cipher", a) == null) {
System.out.println("Skip, due to no support: " + a);
continue;
}
test(p, a, new int[] { 120, 264 });
String from = (a == "AESWrap" || a == "AESWrapPad"? a : "AES");
test(p, a.replace(from, from + "_128"), new int[] {192, 256 });
test(p, a.replace(from, from + "_192"), new int[] {128, 256 });
test(p, a.replace(from, from + "_256"), new int[] {128, 192 });
}
System.out.println("All Tests Passed");
}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8264849
* @summary Verify that the key wrap cipher "AESWrap" work as expected.
* @library /test/lib ../..
* @run main/othervm XMLEncKAT
*/
import java.util.Base64;
import java.security.Key;
import java.security.AlgorithmParameters;
import java.security.Provider;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
// adapted from com/sun/crypto/provider/Cipher/KeyWrap/XMLEncKAT.java
public class XMLEncKAT extends PKCS11Test {
private static byte[] aes128Key_1;
private static byte[] aes192Key_1;
private static byte[] aes256Key_1;
private static byte[] aes128Key_2;
private static byte[] aes192Key_2;
private static byte[] aes256Key_2;
private static Base64.Decoder base64D = Base64.getDecoder();
private static Base64.Encoder base64E = Base64.getEncoder();
static {
try {
aes128Key_1 = "abcdefghijklmnop".getBytes("ASCII");
aes192Key_1 = "abcdefghijklmnopqrstuvwx".getBytes("ASCII");
aes256Key_1 = "abcdefghijklmnopqrstuvwxyz012345".getBytes("ASCII");
} catch (UnsupportedEncodingException uee) {
// should never happen
}
aes128Key_2 = base64D.decode("01+yuQ2huPS1+Qv0LH+zaQ==");
aes192Key_2 = base64D.decode("IlfuS40LvStVU0Mj8ePrrGHVhAb48y++");
aes256Key_2 = base64D.decode
("ZhZ4v3RlwTlCEOpIrHfLKVyJOBDtEJOOQDat/4xR1bA=");
}
private static String[] aes128WrappedKey_1 = {
"dV45TUpJbidb9iKa34xj1WVtTZ036cnqvym2TBJWR5c=",
"rPnY/XoSGCbuwy7vpslf29rs9dbvSCmGFOjEs3LT6g/qyZjfDA+2fQ=="
};
private static String[] aes128WrappedKey_2 = {
"GPl6bneL1jKl0/lGnf9gejlYHRI6XxFz"
};
private static String[] aes192WrappedKey_1 = {
"IbjZH7Mq564oMybpvCHWYM/5ER3eFsAV",
"19D633XVohP6UJvaVRAhJek+ahtM3gOiVs6nZyAasDEb+WCUQOcWZw=="
};
private static String[] aes192WrappedKey_2 = {
"5+GpVUQNTAT3uY8pPedEg/PpftiX+fJsTCun+fgmIz0=",
"iuZvvGBWScikHld9TtNIOz0Sm7Srg5AcxOBMA8qIvQY=",
"PeDwjnCsg6xWzs3SmzUtc2nyUz28nGu7"
};
private static String[] aes256WrappedKey_1 = {
"4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY=",
"tPCC89jQShB+WDINCdRfKgf8wTlAx8xRXD73RmEHPBfix8zS1N82KQ==",
"bsL63D0hPN6EOyzdgfEmKsAAvoJiGM+Wp9a9KZM92IKdl7s3YSntRg=="
};
private static String[] aes256WrappedKey_2 = {
"IbnoS1cvuIFIGB46jj1V1FGftc92irrCwcC7BoBvxwQ=",
"ic+Om6/3ZKcThVN3iv9lUEankNkDv3Et",
"jOvQe4SxDqEMvAHcmb3Z+/Uedj23pvL6BRQsl2sjJlQ=",
"IMwdsyg89IZ4Txf1SYYZNKUOKuYdDoIi/zEKXCjj4j9PM6BdkZligA=="
};
private void testKeyWrap(Provider p, String cAlg, byte[] cKeyVal,
String cKeyAlg, String[] base64Wrapped) throws Exception {
System.out.println("Testing " + cAlg + " Cipher with " +
8*cKeyVal.length + "-bit key");
Cipher c = Cipher.getInstance(cAlg, p);
SecretKey cKey = new SecretKeySpec(cKeyVal, cKeyAlg);
c.init(Cipher.UNWRAP_MODE, cKey);
Key[] key = new SecretKey[base64Wrapped.length];
IvParameterSpec[] params =
new IvParameterSpec[base64Wrapped.length];
// first test UNWRAP with known values
for (int i = 0; i < base64Wrapped.length; i++) {
byte[] wrappedKey = base64D.decode(base64Wrapped[i]);
key[i] = c.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
if (c.getIV() != null) {
params[i] = new IvParameterSpec(c.getIV());
}
}
// then test WRAP and compare with the known values
for (int i = 0; i < key.length; i++) {
c.init(Cipher.WRAP_MODE, cKey, params[i]);
byte[] wrapped2 = c.wrap(key[i]);
String out = base64E.encodeToString(wrapped2);
if (!out.equalsIgnoreCase(base64Wrapped[i])) {
throw new Exception("Wrap failed; got " + out + ", expect " +
base64Wrapped[i]);
}
}
}
public static void main(String[] argv) throws Exception {
main(new XMLEncKAT(), argv);
}
@Override
public void main(Provider p) throws Exception {
String wrapAlg = "AESWrap";
if (p.getService("Cipher", wrapAlg) == null) {
System.out.println("Skip, due to no support: " + wrapAlg);
return;
}
String keyAlg = "AES";
testKeyWrap(p, wrapAlg, aes128Key_1, keyAlg, aes128WrappedKey_1);
testKeyWrap(p, wrapAlg, aes128Key_2, keyAlg, aes128WrappedKey_2);
// only run the tests on longer key lengths if unlimited version
// of JCE jurisdiction policy files are installed
if (Cipher.getMaxAllowedKeyLength(keyAlg) == Integer.MAX_VALUE) {
testKeyWrap(p, wrapAlg, aes192Key_1, keyAlg, aes192WrappedKey_1);
testKeyWrap(p, wrapAlg, aes192Key_2, keyAlg, aes192WrappedKey_2);
testKeyWrap(p, wrapAlg, aes256Key_1, keyAlg, aes256WrappedKey_1);
testKeyWrap(p, wrapAlg, aes256Key_2, keyAlg, aes256WrappedKey_2);
}
System.out.println("All Tests Passed");
}
}