8248268: Support KWP in addition to KW

Reviewed-by: xuelei
This commit is contained in:
Valerie Peng 2021-06-02 21:31:57 +00:00
parent 3482cb87fd
commit 136badb1f7
18 changed files with 2223 additions and 675 deletions

View File

@ -0,0 +1,196 @@
/*
* 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 com.sun.crypto.provider;
import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import static com.sun.crypto.provider.KWUtil.*;
/**
* This class implement the AES KeyWrap mode of operation as defined in
* <a href=https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf>
* "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"</a>
* and represents AES cipher in KW mode.
*/
class AESKeyWrap extends FeedbackCipher {
// default integrity check value (icv) if iv is not supplied
private static final byte[] ICV1 = { // SEMI_BLKSIZE long
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
};
AESKeyWrap() {
super(new AESCrypt());
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>KW</code>
*/
@Override
String getFeedback() {
return "KW";
}
/**
* Save the current content of this cipher.
*/
@Override
void save() {
throw new UnsupportedOperationException("save not supported");
};
/**
* Restores the content of this cipher to the previous saved one.
*/
@Override
void restore() {
throw new UnsupportedOperationException("restore not supported");
};
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
* @param decrypting flag indicating encryption or decryption
* @param algorithm the algorithm name
* @param key the key
* @param iv the iv
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given iv is
* non-null and not the right length
*/
@Override
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("Invalid null key");
}
if (iv != null && iv.length != SEMI_BLKSIZE) {
throw new InvalidAlgorithmParameterException("Invalid IV");
}
embeddedCipher.init(decrypting, algorithm, key);
// iv is retrieved from IvParameterSpec.getIV() which is already cloned
this.iv = (iv == null? ICV1 : iv);
}
/**
* Resets the iv to its original value.
* This is used when doFinal is called in the Cipher class, so that the
* cipher can be reused (with its original iv).
*/
@Override
void reset() {
throw new UnsupportedOperationException("reset not supported");
};
// no support for multi-part encryption
@Override
int encrypt(byte[] pt, int ptOfs, int ptLen, byte[] ct, int ctOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
// no support for multi-part decryption
@Override
int decrypt(byte[] ct, int ctOfs, int ctLen, byte[] pt, int ptOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
/**
* Performs single-part encryption operation.
*
* <p>The input <code>pt</code>, starting at <code>0</code>
* and ending at <code>ptLen-1</code>, is encrypted.
* The result is stored in place into <code>pt</code>, starting at
* <code>0</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param pt the input buffer with the data to be encrypted
* @param dummy1 the offset in <code>pt</code> which is always 0
* @param ptLen the length of the input data
* @param dummy2 the output buffer for the encryption which is always pt
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>pt</code>
*/
@Override
int encryptFinal(byte[] pt, int dummy1, int ptLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
// adjust the min value since pt contains the first semi-block
if (ptLen < MIN_INPUTLEN || (ptLen % SEMI_BLKSIZE) != 0) {
throw new IllegalBlockSizeException("data should" +
" be at least 16 bytes and multiples of 8");
}
return W(iv, pt, ptLen, embeddedCipher);
}
/**
* Performs single-part decryption operation.
*
* <p>The input <code>ct</code>, starting at <code>0</code>
* and ending at <code>ctLen-1</code>, is decrypted.
* The result is stored in place into <code>ct</code>, starting at
* <code>0</code>.
*
* <p>NOTE: Purpose of this special impl is for minimizing array
* copying, those unused arguments are named as dummyN.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param ct the input buffer with the data to be decrypted
* @param dummy1 the offset in <code>ct</code> which is always 0
* @param ctLen the length of the input data
* @param dummy2 the output buffer for the decryption which is always ct
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>ct</code>
*/
@Override
int decryptFinal(byte[] ct, int dummy1, int ctLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
if (ctLen < MIN_INPUTLEN || (ctLen % SEMI_BLKSIZE) != 0) {
throw new IllegalBlockSizeException
("data should be at least 24 bytes and multiples of 8");
}
byte[] ivOut = new byte[SEMI_BLKSIZE];
ctLen = W_INV(ct, ctLen, ivOut, embeddedCipher);
// check against icv and fail if not match
if (!MessageDigest.isEqual(ivOut, this.iv)) {
throw new IllegalBlockSizeException("Integrity check failed");
}
return ctLen;
}
}

View File

@ -0,0 +1,260 @@
/*
* 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 com.sun.crypto.provider;
import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import static com.sun.crypto.provider.KWUtil.*;
/**
* This class implement the AES KeyWrap With Padding mode of operation as
* defined in
* <a href=https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf>
* "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"</a>
* and represents AES cipher in KWP mode.
*/
class AESKeyWrapPadded extends FeedbackCipher {
// default integrity check value (icv) if iv is not supplied
private static final byte[] ICV2 = { // SEMI_BLKSIZE/2 long
(byte) 0xA6, (byte) 0x59, (byte) 0x59, (byte) 0xA6,
};
private static final byte[] PAD_BLK = new byte[SEMI_BLKSIZE - 1];
// set the first semiblock of dest with iv and inLen
private static void setIvAndLen(byte[] dest, byte[] iv, int inLen) {
assert(dest.length >= SEMI_BLKSIZE) : "buffer needs at least 8 bytes";
System.arraycopy(iv, 0, dest, 0, iv.length);
dest[4] = (byte) ((inLen >>> 24) & 0xFF);
dest[5] = (byte) ((inLen >>> 16) & 0xFF);
dest[6] = (byte) ((inLen >>> 8) & 0xFF);
dest[7] = (byte) (inLen & 0xFF);
}
// validate the recovered internal ivAndLen semiblock against iv and
// return the recovered input length
private static int validateIV(byte[] ivAndLen, byte[] iv)
throws IllegalBlockSizeException {
// check against iv and fail if not match
int match = 0;
for (int i = 0; i < ICV2.length; i++) {
match |= (ivAndLen[i] ^ iv[i]);
}
if (match != 0) {
throw new IllegalBlockSizeException("Integrity check failed");
}
int outLen = ivAndLen[4];
for (int k = 5; k < SEMI_BLKSIZE; k++) {
if (outLen != 0) {
outLen <<= 8;
}
outLen |= ivAndLen[k] & 0xFF;
}
return outLen;
}
AESKeyWrapPadded() {
super(new AESCrypt());
}
/**
* Gets the name of this feedback mode.
*
* @return the string <code>KW</code>
*/
@Override
String getFeedback() {
return "KWP";
}
/**
* Save the current content of this cipher.
*/
@Override
void save() {
throw new UnsupportedOperationException("save not supported");
};
/**
* Restores the content of this cipher to the previous saved one.
*/
@Override
void restore() {
throw new UnsupportedOperationException("restore not supported");
};
/**
* Initializes the cipher in the specified mode with the given key
* and iv.
*
* @param decrypting flag indicating encryption or decryption
* @param algorithm the algorithm name
* @param key the key
* @param iv the iv
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given iv is
* non-null and not the right length
*/
@Override
void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("Invalid null key");
}
if (iv != null && iv.length != ICV2.length) {
throw new InvalidAlgorithmParameterException("Invalid IV length");
}
embeddedCipher.init(decrypting, algorithm, key);
// iv is retrieved from IvParameterSpec.getIV() which is already cloned
this.iv = (iv == null? ICV2 : iv);
}
/**
* Resets the iv to its original value.
* This is used when doFinal is called in the Cipher class, so that the
* cipher can be reused (with its original iv).
*/
@Override
void reset() {
throw new UnsupportedOperationException("reset not supported");
};
// no support for multi-part encryption
@Override
int encrypt(byte[] pt, int ptOfs, int ptLen, byte[] ct, int ctOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
// no support for multi-part decryption
@Override
int decrypt(byte[] ct, int ctOfs, int ctLen, byte[] pt, int ptOfs) {
throw new UnsupportedOperationException("multi-part not supported");
};
/**
* Performs single-part encryption operation.
*
* <p>The input <code>pt</code>, starting at <code>0</code>
* and ending at <code>ptLen-1</code>, is encrypted.
* The result is stored in place into <code>pt</code>, starting at
* <code>0</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param pt the input buffer with the data to be encrypted
* @param dummy1 the offset in <code>pt</code> which is always 0
* @param ptLen the length of the input data
* @param dummy2 the output buffer for the encryption which is always pt
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>pt</code>
*/
@Override
int encryptFinal(byte[] pt, int dummy1, int ptLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
int actualLen = ptLen - SEMI_BLKSIZE;
if (actualLen < 1) {
throw new IllegalBlockSizeException
("data should have at least 1 byte");
}
if (ptLen % SEMI_BLKSIZE != 0) {
int rem = SEMI_BLKSIZE - (ptLen % SEMI_BLKSIZE);
System.arraycopy(PAD_BLK, 0, pt, ptLen, rem);
ptLen += rem;
}
if (ptLen <= BLKSIZE) {
// overwrite the first semiblock with iv and input length
setIvAndLen(pt, iv, actualLen);
embeddedCipher.encryptBlock(pt, 0, pt, 0);
} else {
byte[] ivAndLen = new byte[SEMI_BLKSIZE];
setIvAndLen(ivAndLen, iv, actualLen);
ptLen = W(ivAndLen, pt, ptLen, embeddedCipher);
}
return ptLen;
}
/**
* Performs single-part decryption operation.
*
* <p>The input <code>ct</code>, starting at <code>0</code>
* and ending at <code>ctLen-1</code>, is decrypted.
* The result is stored in place into <code>ct</code>, starting at
* <code>0</code>.
*
* <p>The subclass that implements Cipher should ensure that
* <code>init</code> has been called before this method is called.
*
* @param ct the input buffer with the data to be decrypted
* @param dummy1 the offset in <code>ct</code> which is always 0
* @param ctLen the length of the input data
* @param dummy2 the output buffer for the decryption which is always ct
* @param dummy3 the offset in the output buffer which is always 0
* @return the number of bytes placed into <code>ct</code>
*/
@Override
int decryptFinal(byte[] ct, int dummy1, int ctLen, byte[] dummy2,
int dummy3) throws IllegalBlockSizeException {
if (ctLen < BLKSIZE || ctLen % SEMI_BLKSIZE != 0) {
throw new IllegalBlockSizeException
("data should be at least 16 bytes and multiples of 8");
}
byte[] ivAndLen = new byte[SEMI_BLKSIZE];
if (ctLen == BLKSIZE) {
embeddedCipher.decryptBlock(ct, 0, ct, 0);
System.arraycopy(ct, 0, ivAndLen, 0, SEMI_BLKSIZE);
System.arraycopy(ct, SEMI_BLKSIZE, ct, 0, SEMI_BLKSIZE);
ctLen -= SEMI_BLKSIZE;
} else {
ctLen = W_INV(ct, ctLen, ivAndLen, embeddedCipher);
}
int outLen = validateIV(ivAndLen, this.iv);
// check padding bytes
int padLen = ctLen - outLen;
if (padLen < 0 || padLen >= SEMI_BLKSIZE) {
throw new IllegalBlockSizeException("Invalid KWP pad length " +
padLen);
}
for (int k = padLen; k > 0; k--) {
if (ct[ctLen - k] != 0) {
throw new IllegalBlockSizeException("Invalid KWP pad value");
}
}
return outLen;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2002, 2021, Oracle and/or its affiliates. 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
@ -35,7 +35,8 @@ import java.security.spec.InvalidParameterSpecException;
* in feedback-mode. IV is defined in the standards as follows: * in feedback-mode. IV is defined in the standards as follows:
* *
* <pre> * <pre>
* IV ::= OCTET STRING -- 16 octets * IV ::= OCTET STRING -- 8 octets for KW, 4 octets for KWP, and 16 octets for
* other feedback modes
* </pre> * </pre>
* *
* @author Valerie Peng * @author Valerie Peng
@ -46,7 +47,7 @@ public final class AESParameters extends AlgorithmParametersSpi {
private BlockCipherParamsCore core; private BlockCipherParamsCore core;
public AESParameters() { public AESParameters() {
core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE); core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE, 4, 8);
} }
protected void engineInit(AlgorithmParameterSpec paramSpec) protected void engineInit(AlgorithmParameterSpec paramSpec)

View File

@ -1,526 +0,0 @@
/*
* Copyright (c) 2004, 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 com.sun.crypto.provider;
import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class implements the AES KeyWrap algorithm as defined
* in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
* "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".
* Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
* can be used for this algorithm.
*
* @author Valerie Peng
*
*
* @see AESCipher
*/
abstract class AESWrapCipher extends CipherSpi {
public static final class General extends AESWrapCipher {
public General() {
super(-1);
}
}
public static final class AES128 extends AESWrapCipher {
public AES128() {
super(16);
}
}
public static final class AES192 extends AESWrapCipher {
public AES192() {
super(24);
}
}
public static final class AES256 extends AESWrapCipher {
public AES256() {
super(32);
}
}
private static final byte[] IV = {
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
};
private static final int blksize = AESConstants.AES_BLOCK_SIZE;
/*
* internal cipher object which does the real work.
*/
private AESCrypt cipher;
/*
* are we encrypting or decrypting?
*/
private boolean decrypting = false;
/*
* needed to support AES oids which associates a fixed key size
* to the cipher object.
*/
private final int fixedKeySize; // in bytes, -1 if no restriction
/**
* Creates an instance of AES KeyWrap cipher with default
* mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
*/
public AESWrapCipher(int keySize) {
cipher = new AESCrypt();
fixedKeySize = keySize;
}
/**
* Sets the mode of this cipher. Only "ECB" mode is accepted for this
* cipher.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode
* is not "ECB".
*/
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
if (!mode.equalsIgnoreCase("ECB")) {
throw new NoSuchAlgorithmException(mode + " cannot be used");
}
}
/**
* Sets the padding mechanism of this cipher. Only "NoPadding" schmem
* is accepted for this cipher.
*
* @param padding the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* is not "NoPadding".
*/
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if (!padding.equalsIgnoreCase("NoPadding")) {
throw new NoSuchPaddingException(padding + " cannot be used");
}
}
/**
* Returns the block size (in bytes). i.e. 16 bytes.
*
* @return the block size (in bytes), i.e. 16 bytes.
*/
protected int engineGetBlockSize() {
return blksize;
}
/**
* Returns the length in bytes that an output buffer would need to be
* given the input length <code>inputLen</code> (in bytes).
*
* <p>The actual output length of the next <code>update</code> or
* <code>doFinal</code> call may be smaller than the length returned
* by this method.
*
* @param inputLen the input length (in bytes)
*
* @return the required output buffer size (in bytes)
*/
protected int engineGetOutputSize(int inputLen) {
// can only return an upper-limit if not initialized yet.
int result = 0;
if (decrypting) {
result = inputLen - 8;
} else {
result = Math.addExact(inputLen, 8);
}
return (result < 0? 0:result);
}
/**
* Returns the initialization vector (IV) which is null for this cipher.
*
* @return null for this cipher.
*/
protected byte[] engineGetIV() {
return null;
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:<b>
* Cipher.WRAP_MODE, and <b>
* Cipher.UNWRAP_MODE.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
*
* @param opmode the operation mode of this cipher. Only
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
* @param key the secret key.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher.
*/
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
if (opmode == Cipher.WRAP_MODE) {
decrypting = false;
} else if (opmode == Cipher.UNWRAP_MODE) {
decrypting = true;
} else {
throw new UnsupportedOperationException("This cipher can " +
"only be used for key wrapping and unwrapping");
}
AESCipher.checkKeySize(key, fixedKeySize);
byte[] encoded = key.getEncoded();
try {
cipher.init(decrypting, key.getAlgorithm(), encoded);
} finally {
Arrays.fill(encoded, (byte)0);
}
}
/**
* Initializes this cipher with a key, a set of algorithm parameters,
* and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:<b>
* Cipher.WRAP_MODE, and <b>
* Cipher.UNWRAP_MODE.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
*
* @param opmode the operation mode of this cipher. Only
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
* @param key the secret key.
* @param params the algorithm parameters; must be null for this cipher.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters is not null.
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("This cipher " +
"does not accept any parameters");
}
engineInit(opmode, key, random);
}
/**
* Initializes this cipher with a key, a set of algorithm parameters,
* and a source of randomness.
*
* <p>The cipher only supports the following two operation modes:<b>
* Cipher.WRAP_MODE, and <b>
* Cipher.UNWRAP_MODE.
* <p>For modes other than the above two, UnsupportedOperationException
* will be thrown.
*
* @param opmode the operation mode of this cipher. Only
* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
* @param key the secret key.
* @param params the algorithm parameters; must be null for this cipher.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters is not null.
*/
protected void engineInit(int opmode, Key key,
AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("This cipher " +
"does not accept any parameters");
}
engineInit(opmode, key, random);
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
* @param out the buffer for the result.
* @param outOffset the offset in <code>out</code> where the result
* is stored.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected int engineUpdate(byte[] in, int inOffset, int inLen,
byte[] out, int outOffset)
throws ShortBufferException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @param input the input buffer
* @param inputOffset the offset in <code>in</code> where the input
* starts
* @param inputLen the input length.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* This operation is not supported by this cipher.
* Since it's impossible to initialize this cipher given the
* current Cipher.engineInit(...) implementation,
* IllegalStateException will always be thrown upon invocation.
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
* @param out the buffer for the result.
* @param outOffset the ofset in <code>out</code> where the result
* is stored.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected int engineDoFinal(byte[] in, int inOffset, int inLen,
byte[] out, int outOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
throw new IllegalStateException("Cipher has not been initialized");
}
/**
* Returns the parameters used with this cipher which is always null
* for this cipher.
*
* @return null since this cipher does not use any parameters.
*/
protected AlgorithmParameters engineGetParameters() {
return null;
}
/**
* Returns the key size of the given key object in number of bits.
*
* @param key the key object.
*
* @return the "effective" key size of the given key object.
*
* @exception InvalidKeyException if <code>key</code> is invalid.
*/
protected int engineGetKeySize(Key key) throws InvalidKeyException {
byte[] encoded = key.getEncoded();
Arrays.fill(encoded, (byte)0);
if (!AESCrypt.isKeySizeValid(encoded.length)) {
throw new InvalidKeyException("Invalid key length: " +
encoded.length + " bytes");
}
return Math.multiplyExact(encoded.length, 8);
}
/**
* Wrap a key.
*
* @param key the key to be wrapped.
*
* @return the wrapped key.
*
* @exception IllegalBlockSizeException if this cipher is a block
* cipher, no padding has been requested, and the length of the
* encoding of the key to be wrapped is not a
* multiple of the block size.
*
* @exception InvalidKeyException if it is impossible or unsafe to
* wrap the key with this cipher (e.g., a hardware protected key is
* being passed to a software only cipher).
*/
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
byte[] keyVal = key.getEncoded();
if ((keyVal == null) || (keyVal.length == 0)) {
throw new InvalidKeyException("Cannot get an encoding of " +
"the key to be wrapped");
}
try {
byte[] out = new byte[Math.addExact(keyVal.length, 8)];
if (keyVal.length == 8) {
System.arraycopy(IV, 0, out, 0, IV.length);
System.arraycopy(keyVal, 0, out, IV.length, 8);
cipher.encryptBlock(out, 0, out, 0);
} else {
if (keyVal.length % 8 != 0) {
throw new IllegalBlockSizeException("length of the " +
"to be wrapped key should be multiples of 8 bytes");
}
System.arraycopy(IV, 0, out, 0, IV.length);
System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
int N = keyVal.length / 8;
byte[] buffer = new byte[blksize];
for (int j = 0; j < 6; j++) {
for (int i = 1; i <= N; i++) {
int T = i + j * N;
System.arraycopy(out, 0, buffer, 0, IV.length);
System.arraycopy(out, i * 8, buffer, IV.length, 8);
cipher.encryptBlock(buffer, 0, buffer, 0);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[IV.length - k] ^= v;
T >>>= 8;
}
System.arraycopy(buffer, 0, out, 0, IV.length);
System.arraycopy(buffer, 8, out, 8 * i, 8);
}
}
}
return out;
} finally {
Arrays.fill(keyVal, (byte)0);
}
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
protected Key engineUnwrap(byte[] wrappedKey,
String wrappedKeyAlgorithm,
int wrappedKeyType)
throws InvalidKeyException, NoSuchAlgorithmException {
int wrappedKeyLen = wrappedKey.length;
// ensure the wrappedKey length is multiples of 8 bytes and non-zero
if (wrappedKeyLen == 0) {
throw new InvalidKeyException("The wrapped key is empty");
}
if (wrappedKeyLen % 8 != 0) {
throw new InvalidKeyException
("The wrapped key has invalid key length");
}
byte[] out = new byte[wrappedKeyLen - 8];
byte[] buffer = new byte[blksize];
try {
if (wrappedKeyLen == 16) {
cipher.decryptBlock(wrappedKey, 0, buffer, 0);
for (int i = 0; i < IV.length; i++) {
if (IV[i] != buffer[i]) {
throw new InvalidKeyException("Integrity check failed");
}
}
System.arraycopy(buffer, IV.length, out, 0, out.length);
} else {
System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
int N = out.length / 8;
for (int j = 5; j >= 0; j--) {
for (int i = N; i > 0; i--) {
int T = i + j * N;
System.arraycopy(out, 8 * (i - 1), buffer, IV.length, 8);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[IV.length - k] ^= v;
T >>>= 8;
}
cipher.decryptBlock(buffer, 0, buffer, 0);
System.arraycopy(buffer, IV.length, out, 8 * (i - 1), 8);
}
}
for (int i = 0; i < IV.length; i++) {
if (IV[i] != buffer[i]) {
throw new InvalidKeyException("Integrity check failed");
}
}
}
return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
wrappedKeyType);
} finally {
Arrays.fill(out, (byte)0);
Arrays.fill(buffer, (byte)0);
}
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2002, 2021, Oracle and/or its affiliates. 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
@ -26,6 +26,7 @@
package com.sun.crypto.provider; package com.sun.crypto.provider;
import java.io.*; import java.io.*;
import java.util.Arrays;
import sun.security.util.*; import sun.security.util.*;
import sun.security.util.HexDumpEncoder; import sun.security.util.HexDumpEncoder;
import java.security.spec.AlgorithmParameterSpec; import java.security.spec.AlgorithmParameterSpec;
@ -48,8 +49,11 @@ final class BlockCipherParamsCore {
private int block_size = 0; private int block_size = 0;
private byte[] iv = null; private byte[] iv = null;
BlockCipherParamsCore(int blksize) { private int[] moreSizes = null;
BlockCipherParamsCore(int blksize, int... moreSizes) {
block_size = blksize; block_size = blksize;
this.moreSizes = moreSizes;
} }
void init(AlgorithmParameterSpec paramSpec) void init(AlgorithmParameterSpec paramSpec)
@ -59,9 +63,20 @@ final class BlockCipherParamsCore {
("Inappropriate parameter specification"); ("Inappropriate parameter specification");
} }
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV(); byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
if (tmpIv.length != block_size) { boolean check = (tmpIv.length == block_size);
throw new InvalidParameterSpecException("IV not " + if (!check && moreSizes != null) {
block_size + " bytes long"); for (int s : moreSizes) {
if (tmpIv.length == s) {
check = true;
break;
}
}
}
if (!check) {
String expectedLen = block_size + (moreSizes == null? "" :
Arrays.toString(moreSizes));
throw new InvalidParameterSpecException("IV length not " +
expectedLen + " bytes long");
} }
iv = tmpIv.clone(); iv = tmpIv.clone();
} }
@ -73,15 +88,17 @@ final class BlockCipherParamsCore {
if (der.available() != 0) { if (der.available() != 0) {
throw new IOException("IV parsing error: extra data"); throw new IOException("IV parsing error: extra data");
} }
if (tmpIv.length != block_size) { boolean check = (tmpIv.length == block_size);
throw new IOException("IV not " + block_size + if (!check) {
String expectedLen = block_size + (moreSizes == null? "" :
Arrays.toString(moreSizes));
throw new IOException("IV not " + expectedLen +
" bytes long"); " bytes long");
} }
iv = tmpIv; iv = tmpIv;
} }
void init(byte[] encoded, String decodingMethod) void init(byte[] encoded, String decodingMethod) throws IOException {
throws IOException {
if ((decodingMethod != null) && if ((decodingMethod != null) &&
(!decodingMethod.equalsIgnoreCase("ASN.1"))) { (!decodingMethod.equalsIgnoreCase("ASN.1"))) {
throw new IllegalArgumentException("Only support ASN.1 format"); throw new IllegalArgumentException("Only support ASN.1 format");
@ -90,8 +107,7 @@ final class BlockCipherParamsCore {
} }
<T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec) <T extends AlgorithmParameterSpec> T getParameterSpec(Class<T> paramSpec)
throws InvalidParameterSpecException throws InvalidParameterSpecException {
{
if (IvParameterSpec.class.isAssignableFrom(paramSpec)) { if (IvParameterSpec.class.isAssignableFrom(paramSpec)) {
return paramSpec.cast(new IvParameterSpec(this.iv)); return paramSpec.cast(new IvParameterSpec(this.iv));
} else { } else {

View File

@ -25,8 +25,6 @@
package com.sun.crypto.provider; package com.sun.crypto.provider;
import jdk.internal.access.SharedSecrets;
import java.security.Key; import java.security.Key;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -36,7 +34,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -60,11 +58,9 @@ final class ConstructKeys {
* @return a public key constructed from the encodedKey. * @return a public key constructed from the encodedKey.
*/ */
private static final PublicKey constructPublicKey(byte[] encodedKey, private static final PublicKey constructPublicKey(byte[] encodedKey,
String encodedKeyAlgorithm) String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException throws InvalidKeyException, NoSuchAlgorithmException {
{
PublicKey key = null; PublicKey key = null;
try { try {
KeyFactory keyFactory = KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm, KeyFactory.getInstance(encodedKeyAlgorithm,
@ -111,15 +107,15 @@ final class ConstructKeys {
* @return a private key constructed from the encodedKey. * @return a private key constructed from the encodedKey.
*/ */
private static final PrivateKey constructPrivateKey(byte[] encodedKey, private static final PrivateKey constructPrivateKey(byte[] encodedKey,
String encodedKeyAlgorithm) String encodedKeyAlgorithm)
throws InvalidKeyException, NoSuchAlgorithmException throws InvalidKeyException, NoSuchAlgorithmException {
{
PrivateKey key = null; PrivateKey key = null;
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
try { try {
KeyFactory keyFactory = KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm, KeyFactory.getInstance(encodedKeyAlgorithm,
SunJCE.getInstance()); SunJCE.getInstance());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
return keyFactory.generatePrivate(keySpec); return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae) { } catch (NoSuchAlgorithmException nsae) {
// Try to see whether there is another // Try to see whether there is another
@ -127,6 +123,8 @@ final class ConstructKeys {
try { try {
KeyFactory keyFactory = KeyFactory keyFactory =
KeyFactory.getInstance(encodedKeyAlgorithm); KeyFactory.getInstance(encodedKeyAlgorithm);
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(encodedKey);
key = keyFactory.generatePrivate(keySpec); key = keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException nsae2) { } catch (NoSuchAlgorithmException nsae2) {
throw new NoSuchAlgorithmException("No installed providers " + throw new NoSuchAlgorithmException("No installed providers " +
@ -144,8 +142,6 @@ final class ConstructKeys {
new InvalidKeyException("Cannot construct private key"); new InvalidKeyException("Cannot construct private key");
ike.initCause(ikse); ike.initCause(ikse);
throw ike; throw ike;
} finally {
SharedSecrets.getJavaSecuritySpecAccess().clearEncodedKeySpec(keySpec);
} }
return key; return key;
@ -161,29 +157,48 @@ final class ConstructKeys {
* @return a secret key constructed from the encodedKey. * @return a secret key constructed from the encodedKey.
*/ */
private static final SecretKey constructSecretKey(byte[] encodedKey, private static final SecretKey constructSecretKey(byte[] encodedKey,
String encodedKeyAlgorithm) int ofs, int len, String encodedKeyAlgorithm) {
{ return (new SecretKeySpec(encodedKey, ofs, len, encodedKeyAlgorithm));
return (new SecretKeySpec(encodedKey, encodedKeyAlgorithm));
} }
static final Key constructKey(byte[] encoding, String keyAlgorithm, static final Key constructKey(byte[] encoding, String keyAlgorithm,
int keyType) int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
throws InvalidKeyException, NoSuchAlgorithmException { return constructKey(encoding, 0, encoding.length, keyAlgorithm,
Key result = null; keyType);
}
static final Key constructKey(byte[] encoding, int ofs, int len,
String keyAlgorithm, int keyType)
throws InvalidKeyException, NoSuchAlgorithmException {
switch (keyType) { switch (keyType) {
case Cipher.SECRET_KEY: case Cipher.SECRET_KEY:
result = ConstructKeys.constructSecretKey(encoding, try {
keyAlgorithm); return ConstructKeys.constructSecretKey(encoding, ofs, len,
break; keyAlgorithm);
} finally {
Arrays.fill(encoding, ofs, len, (byte)0);
}
case Cipher.PRIVATE_KEY: case Cipher.PRIVATE_KEY:
result = ConstructKeys.constructPrivateKey(encoding, byte[] encoding2 = encoding;
keyAlgorithm); try {
break; if (ofs != 0 || len != encoding.length) {
encoding2 = Arrays.copyOfRange(encoding, ofs, ofs + len);
}
return ConstructKeys.constructPrivateKey(encoding2,
keyAlgorithm);
} finally {
Arrays.fill(encoding, ofs, len, (byte)0);
if (encoding2 != encoding) {
Arrays.fill(encoding2, (byte)0);
}
}
case Cipher.PUBLIC_KEY: case Cipher.PUBLIC_KEY:
result = ConstructKeys.constructPublicKey(encoding, if (ofs != 0 || len != encoding.length) {
keyAlgorithm); encoding = Arrays.copyOfRange(encoding, ofs, ofs + len);
break; }
return ConstructKeys.constructPublicKey(encoding, keyAlgorithm);
default:
throw new NoSuchAlgorithmException("Unsupported key type");
} }
return result;
} }
} }

View File

@ -0,0 +1,129 @@
/*
* 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 com.sun.crypto.provider;
import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
* This class acts as the base class for AES KeyWrap algorithms as defined
* in <a href=https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf>
* "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"
*/
class KWUtil {
static final int BLKSIZE = 16;
static final int SEMI_BLKSIZE = BLKSIZE >> 1;
static final int MIN_INPUTLEN = BLKSIZE + SEMI_BLKSIZE;
/*
* The wrapping function W as defined in section 6.1 of NIST SP 800-38F as
* well as sec 2.2.1 of RFC 3394.
* @param firstSemiblk the first semi block value to overwrite the input
* with
* @param in input bytes
* @param inLen length of the to-be-processed bytes
* @param cipher the initialized cipher object used
* @return the processed output length, i.e. same as {@code inLen}.
*/
static final int W(byte[] icvIn, byte[] in, int inLen,
SymmetricCipher cipher) {
assert((inLen >= MIN_INPUTLEN) && ((inLen % SEMI_BLKSIZE) == 0)) :
("Invalid data length for W: " + inLen);
assert(icvIn.length == SEMI_BLKSIZE) : "Invalid ICV buffer size";
// overwrite the first block of in with the icv semiblock
System.arraycopy(icvIn, 0, in, 0, SEMI_BLKSIZE);
int n = inLen / SEMI_BLKSIZE - 1;
byte[] buffer = new byte[BLKSIZE];
byte[] out = in; // in-place
for (int j = 0; j < 6; j++) {
for (int i = 1; i <= n; i++) {
int T = i + j*n;
System.arraycopy(out, 0, buffer, 0, SEMI_BLKSIZE);
System.arraycopy(out, i << 3, buffer, SEMI_BLKSIZE, 8);
cipher.encryptBlock(buffer, 0, buffer, 0);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[SEMI_BLKSIZE - k] ^= v;
T >>>= SEMI_BLKSIZE;
}
System.arraycopy(buffer, 0, out, 0, SEMI_BLKSIZE);
System.arraycopy(buffer, SEMI_BLKSIZE, out, i << 3,
SEMI_BLKSIZE);
}
}
// for W, output length is same as input length
return inLen;
}
/*
* The unwrapping function W^-1 as defined in section 6.1 of NIST SP
* 800-38F as well as sec 2.2.2 of RFC 3394.
* - separated out the initial value from the remaining recovered data
* - no output buffer argument since we cannot write out the recovered
* data until the initial value and padding bytes are verified.
* @param in input bytes, i.e. the to-be-processed data
* @param inLen length of the to-be-processed bytes
* @param ivOut buffer for holding the recovered ICV semiblock
* @param cipher the initialized cipher object used
* @return the recovered data length, i.e. {@code (inLen - icvOut.length)}
*/
static final int W_INV(byte[] in, int inLen, byte[] icvOut,
SymmetricCipher cipher) {
assert((inLen >= MIN_INPUTLEN) && ((inLen % SEMI_BLKSIZE) == 0)) :
("Invalid data length for W_INV: " + inLen);
assert(icvOut.length == SEMI_BLKSIZE) : "Invalid ICV buffer size";
byte[] buffer = new byte[BLKSIZE];
System.arraycopy(in, 0, buffer, 0, SEMI_BLKSIZE);
System.arraycopy(in, SEMI_BLKSIZE, in, 0, inLen - SEMI_BLKSIZE);
int n = (inLen - SEMI_BLKSIZE) / SEMI_BLKSIZE;
for (int j = 5; j >= 0; j--) {
for (int i = n; i > 0; i--) {
int T = i + n*j;
int idx = (i-1) << 3;
System.arraycopy(in, idx, buffer, SEMI_BLKSIZE, SEMI_BLKSIZE);
for (int k = 1; T != 0; k++) {
byte v = (byte) T;
buffer[SEMI_BLKSIZE - k] ^= v;
T >>>= SEMI_BLKSIZE;
}
cipher.decryptBlock(buffer, 0, buffer, 0);
System.arraycopy(buffer, SEMI_BLKSIZE, in, idx, SEMI_BLKSIZE);
}
}
System.arraycopy(buffer, 0, icvOut, 0, SEMI_BLKSIZE);
return inLen - SEMI_BLKSIZE;
}
}

View File

@ -0,0 +1,753 @@
/*
* Copyright (c) 2004, 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 com.sun.crypto.provider;
import java.util.Arrays;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import static com.sun.crypto.provider.KWUtil.*;
/**
* This class is the impl class for AES KeyWrap algorithms as defined in
* <a href=https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf>
* "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"
*/
abstract class KeyWrapCipher extends CipherSpi {
// for AESWrap + AES/KW/NoPadding
public static final class AES_KW_NoPadding extends KeyWrapCipher {
public AES_KW_NoPadding() {
super(new AESKeyWrap(), null, -1);
}
}
// for AESWrap_128 + AES_128/KW/NoPadding
public static final class AES128_KW_NoPadding extends KeyWrapCipher {
public AES128_KW_NoPadding() {
super(new AESKeyWrap(), null, 16);
}
}
// for AESWrap_192 + AES_192/KW/NoPadding
public static final class AES192_KW_NoPadding extends KeyWrapCipher {
public AES192_KW_NoPadding() {
super(new AESKeyWrap(), null, 24);
}
}
// for AESWrap_256 + AES_256/KW/NoPadding
public static final class AES256_KW_NoPadding extends KeyWrapCipher {
public AES256_KW_NoPadding() {
super(new AESKeyWrap(), null, 32);
}
}
// for AES/KW/NoPadding
public static final class AES_KW_PKCS5Padding extends KeyWrapCipher {
public AES_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), -1);
}
}
// for AES_128/KW/NoPadding
public static final class AES128_KW_PKCS5Padding extends KeyWrapCipher {
public AES128_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), 16);
}
}
// for AES_192/KW/NoPadding
public static final class AES192_KW_PKCS5Padding extends KeyWrapCipher {
public AES192_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), 24);
}
}
// for AES_256/KW/NoPadding
public static final class AES256_KW_PKCS5Padding extends KeyWrapCipher {
public AES256_KW_PKCS5Padding() {
super(new AESKeyWrap(), new PKCS5Padding(16), 32);
}
}
// for AES/KWP/NoPadding
public static final class AES_KWP_NoPadding extends KeyWrapCipher {
public AES_KWP_NoPadding() {
super(new AESKeyWrapPadded(), null, -1);
}
}
// for AES_128/KWP/NoPadding
public static final class AES128_KWP_NoPadding extends KeyWrapCipher {
public AES128_KWP_NoPadding() {
super(new AESKeyWrapPadded(), null, 16);
}
}
// for AES_192/KWP/NoPadding
public static final class AES192_KWP_NoPadding extends KeyWrapCipher {
public AES192_KWP_NoPadding() {
super(new AESKeyWrapPadded(), null, 24);
}
}
// for AES_256/KWP/NoPadding
public static final class AES256_KWP_NoPadding extends KeyWrapCipher {
public AES256_KWP_NoPadding() {
super(new AESKeyWrapPadded(), null, 32);
}
}
// store the specified bytes, e.g. in[inOfs...(inOfs+inLen-1)] into
// 'dataBuf' starting at 'dataIdx'.
// NOTE: if 'in' is null, this method will ensure that 'dataBuf' has enough
// capacity for 'inLen' bytes but will NOT copy bytes from 'in'.
private void store(byte[] in, int inOfs, int inLen) {
// In NIST SP 800-38F, KWP input size is limited to be no longer
// than 2^32 bytes. Otherwise, the length cannot be encoded in 32 bits
// However, given the current spec requirement that recovered text
// can only be returned after successful tag verification, we are
// bound by limiting the data size to the size limit of java byte array,
// e.g. Integer.MAX_VALUE, since all data are returned by doFinal().
int remain = Integer.MAX_VALUE - dataIdx;
if (inLen > remain) {
throw new ProviderException("SunJCE provider can only take " +
remain + " more bytes");
}
// resize 'dataBuf' to the smallest (n * BLKSIZE) + SEMI_BLKSIZE)
if (dataBuf == null || dataBuf.length - dataIdx < inLen) {
int newSize = Math.addExact(dataIdx, inLen);
int lastBlk = (dataIdx + inLen - SEMI_BLKSIZE) % BLKSIZE;
if (lastBlk != 0 || padding != null) {
newSize = Math.addExact(newSize, BLKSIZE - lastBlk);
}
byte[] temp = new byte[newSize];
if (dataBuf != null && dataIdx > 0) {
System.arraycopy(dataBuf, 0, temp, 0, dataIdx);
}
dataBuf = temp;
}
if (in != null) {
System.arraycopy(in, inOfs, dataBuf, dataIdx, inLen);
dataIdx += inLen;
}
}
// internal cipher object which does the real work.
private final FeedbackCipher cipher;
// internal padding object; null if NoPadding
private final Padding padding;
// encrypt/wrap or decrypt/unwrap?
private int opmode = -1; // must be set by init(..)
/*
* needed to support oids which associates a fixed key size
* to the cipher object.
*/
private final int fixedKeySize; // in bytes, -1 if no restriction
// internal data buffer for encrypt, decrypt calls
// must use store() to store data into 'dataBuf' as it will resize if needed
private byte[] dataBuf;
private int dataIdx;
/**
* Creates an instance of KeyWrap cipher using the specified
* symmetric cipher whose block size must be 128-bit, and
* the supported mode and padding scheme.
*/
public KeyWrapCipher(FeedbackCipher cipher, Padding padding, int keySize) {
this.cipher = cipher;
this.padding = padding;
this.fixedKeySize = keySize;
this.dataBuf = null;
this.dataIdx = 0;
}
/**
* Sets the mode of this cipher. Must match the mode specified in
* the constructor.
*
* @param mode the cipher mode
*
* @exception NoSuchAlgorithmException if the requested cipher mode
* does not match the supported mode
*/
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (mode != null && !cipher.getFeedback().equalsIgnoreCase(mode)) {
throw new NoSuchAlgorithmException(mode + " cannot be used");
}
}
/**
* Sets the padding mechanism of this cipher. The specified padding
* scheme should match what this cipher is configured with.
*
* @param padding the padding mechanism
*
* @exception NoSuchPaddingException if the requested padding mechanism
* does not match the supported padding scheme
*/
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if ((this.padding == null && !"NoPadding".equalsIgnoreCase(padding)) ||
this.padding instanceof PKCS5Padding &&
!"PKCS5Padding".equalsIgnoreCase(padding)) {
throw new NoSuchPaddingException("Unsupported padding " + padding);
}
}
/**
* Returns the block size (in bytes). i.e. 16 bytes.
*
* @return the block size (in bytes), i.e. 16 bytes.
*/
@Override
protected int engineGetBlockSize() {
return cipher.getBlockSize();
}
/**
* Returns the length in bytes that an output buffer would need to be
* given the input length <code>inLen</code> (in bytes).
*
* <p>The actual output length of the next <code>update</code> or
* <code>doFinal</code> call may be smaller than the length returned
* by this method.
*
* @param inLen the input length (in bytes)
*
* @return the required output buffer size (in bytes)
*/
protected int engineGetOutputSize(int inLen) {
int result;
if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
result = (dataIdx > 0?
Math.addExact(inLen, dataIdx - SEMI_BLKSIZE) : inLen);
// calculate padding length based on plaintext length
int padLen = 0;
if (padding != null) {
padLen = padding.padLength(result);
} else if (cipher instanceof AESKeyWrapPadded) {
int n = result % SEMI_BLKSIZE;
if (n != 0) {
padLen = SEMI_BLKSIZE - n;
}
}
// then add the first semiblock and padLen to result
result = Math.addExact(result, SEMI_BLKSIZE + padLen);
} else {
result = inLen - SEMI_BLKSIZE;
if (dataIdx > 0) {
result = Math.addExact(result, dataIdx);
}
}
return result;
}
/**
* Returns the initialization vector (IV).
*
* @return the user-specified iv or null if default iv is used.
*/
@Override
protected byte[] engineGetIV() {
return cipher.getIV().clone();
}
// actual impl for various engineInit(...) methods
private void implInit(int opmode, Key key, byte[] iv, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
byte[] keyBytes = key.getEncoded();
if (keyBytes == null) {
throw new InvalidKeyException("Null key");
}
this.opmode = opmode;
boolean decrypting = (opmode == Cipher.DECRYPT_MODE ||
opmode == Cipher.UNWRAP_MODE);
try {
cipher.init(decrypting, key.getAlgorithm(), keyBytes, iv);
dataBuf = null;
dataIdx = 0;
} finally {
Arrays.fill(keyBytes, (byte) 0);
}
}
/**
* Initializes this cipher with a key and a source of randomness.
*
* @param opmode the operation mode of this cipher.
* @param key the secret key.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher.
*/
@Override
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
try {
implInit(opmode, key, (byte[])null, random);
} catch (InvalidAlgorithmParameterException iae) {
// should never happen
throw new AssertionError(iae);
}
}
/**
* Initializes this cipher with a key, a set of algorithm parameters,
* and a source of randomness.
*
* @param opmode the operation mode of this cipher.
* @param key the secret key.
* @param params the algorithm parameters; if not null, must be of type
* IvParameterSpec
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate for
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters is invalid.
*/
@Override
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (params != null && !(params instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"Only IvParameterSpec is accepted");
}
byte[] iv = (params == null? null : ((IvParameterSpec)params).getIV());
implInit(opmode, key, iv, random);
}
/**
* Initializes this cipher with a key, a set of algorithm parameters,
* and a source of randomness.
*
* @param opmode the operation mode of this cipher.
* @param key the secret key.
* @param params the algorithm parameters; if not null, must be able to
* be converted to IvParameterSpec.
* @param random the source of randomness.
*
* @exception InvalidKeyException if the given key is inappropriate.
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters is invalid.
*/
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
byte[] iv = null;
if (params != null) {
try {
AlgorithmParameterSpec spec =
params.getParameterSpec(IvParameterSpec.class);
iv = ((IvParameterSpec)spec).getIV();
} catch (InvalidParameterSpecException ispe) {
throw new InvalidAlgorithmParameterException(
"Only IvParameterSpec is accepted");
}
}
try {
implInit(opmode, key, iv, random);
} catch (IllegalArgumentException iae) {
throw new InvalidAlgorithmParameterException(iae.getMessage());
}
}
/**
* See CipherSpi.engineUpdate(...) - buffers data internally as
* only single part operation is supported.
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
*
* @return null.
*/
@Override
protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) {
throw new IllegalStateException
("Cipher not initialized for update");
}
implUpdate(in, inOffset, inLen);
return null;
}
/**
* See CipherSpi.engineUpdate(...) - buffers data internally as
* only single part operation is supported.
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
* @param out the buffer for the result.
* @param outOffset the offset in <code>out</code> where the result
* is stored.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
@Override
protected int engineUpdate(byte[] in, int inOffset, int inLen,
byte[] out, int outOffset) throws ShortBufferException {
if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) {
throw new IllegalStateException
("Cipher not initialized for update");
}
implUpdate(in, inOffset, inLen);
return 0;
}
// actual impl for various engineUpdate(...) methods
private void implUpdate(byte[] in, int inOfs, int inLen) {
if (inLen <= 0) return;
if (opmode == Cipher.ENCRYPT_MODE && dataIdx == 0) {
// the first semiblock is for iv, store data after it
dataIdx = SEMI_BLKSIZE;
}
store(in, inOfs, inLen);
}
/**
* See CipherSpi.engineDoFinal(...)
*
* @param input the input buffer
* @param inputOffset the offset in <code>in</code> where the input
* starts
* @param inputLen the input length.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
@Override
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
int estOutLen = engineGetOutputSize(inLen);
byte[] out = new byte[estOutLen];
try {
int outLen = engineDoFinal(in, inOfs, inLen, out, 0);
if (outLen < estOutLen) {
return Arrays.copyOf(out, outLen);
} else {
return out;
}
} catch (ShortBufferException sbe) {
// should never happen
throw new AssertionError(sbe);
}
}
/**
* See CipherSpi.doFinal(...)
*
* @param in the input buffer.
* @param inOffset the offset in <code>in</code> where the input
* starts.
* @param inLen the input length.
* @param out the buffer for the result.
* @param outOffset the ofset in <code>out</code> where the result
* is stored.
*
* @return n/a.
*
* @exception IllegalStateException upon invocation of this method.
*/
protected int engineDoFinal(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs) throws IllegalBlockSizeException,
ShortBufferException, BadPaddingException {
if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE) {
throw new IllegalStateException
("Cipher not initialized for doFinal");
}
int estOutLen = engineGetOutputSize(inLen);
if (out.length - outOfs < estOutLen) {
throw new ShortBufferException("Need at least " + estOutLen);
}
try {
// cannot write out the result for decryption due to verification
// requirement
if (outOfs == 0 && opmode == Cipher.ENCRYPT_MODE) {
return implDoFinal(in, inOfs, inLen, out);
} else {
// use 'dataBuf' as output buffer and then copy into 'out'
// make sure 'dataBuf' is large enough
store(null, 0, inLen);
int outLen = implDoFinal(in, inOfs, inLen, dataBuf);
if (outLen > estOutLen) {
throw new AssertionError
("Actual output length exceeds estimated length");
}
System.arraycopy(dataBuf, 0, out, outOfs, outLen);
return outLen;
}
} finally {
dataBuf = null;
dataIdx = 0;
}
}
// actual impl for various engineDoFinal(...) methods.
// prepare 'out' buffer with the buffered bytes in 'dataBuf',
// and the to-be-processed bytes in 'in', then perform single-part
// encryption/decrytion over 'out' buffer
private int implDoFinal(byte[] in, int inOfs, int inLen, byte[] out)
throws IllegalBlockSizeException, BadPaddingException,
ShortBufferException {
int len = (out == dataBuf? dataIdx : 0);
// copy over the buffered bytes if out != dataBuf
if (out != dataBuf && dataIdx > 0) {
System.arraycopy(dataBuf, 0, out, 0, dataIdx);
len = dataIdx;
}
if (opmode == Cipher.ENCRYPT_MODE && len == 0) {
len = SEMI_BLKSIZE; // reserve space for the ICV if encryption
}
if (inLen > 0) {
System.arraycopy(in, inOfs, out, len, inLen);
len += inLen;
}
return (opmode == Cipher.ENCRYPT_MODE?
helperEncrypt(out, len) : helperDecrypt(out, len));
}
// helper routine for in-place encryption.
// 'inBuf' = semiblock | plain text | extra bytes if padding is used
// 'inLen' = semiblock length + plain text length
private int helperEncrypt(byte[] inBuf, int inLen)
throws IllegalBlockSizeException, ShortBufferException {
// pad data if padding is used
if (padding != null) {
int paddingLen = padding.padLength(inLen - SEMI_BLKSIZE);
if (inLen + paddingLen > inBuf.length) {
throw new AssertionError("encrypt buffer too small");
}
try {
padding.padWithLen(inBuf, inLen, paddingLen);
inLen += paddingLen;
} catch (ShortBufferException sbe) {
// should never happen
throw new AssertionError(sbe);
}
}
return cipher.encryptFinal(inBuf, 0, inLen, null, 0);
}
// helper routine for in-place decryption.
// 'inBuf' = cipher text
// 'inLen' = cipher text length
private int helperDecrypt(byte[] inBuf, int inLen)
throws IllegalBlockSizeException, BadPaddingException,
ShortBufferException {
int outLen = cipher.decryptFinal(inBuf, 0, inLen, null, 0);
// unpad data if padding is used
if (padding != null) {
int padIdx = padding.unpad(inBuf, 0, outLen);
if (padIdx <= 0) {
throw new BadPaddingException("Bad Padding: " + padIdx);
}
outLen = padIdx;
}
return outLen;
}
/**
* Returns the parameters used with this cipher.
*
* @return AlgorithmParameters object containing IV.
*/
@Override
protected AlgorithmParameters engineGetParameters() {
AlgorithmParameters params = null;
byte[] iv = cipher.getIV();
try {
params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(iv));
} catch (NoSuchAlgorithmException | InvalidParameterSpecException e) {
// should never happen
throw new AssertionError();
}
return params;
}
/**
* Returns the key size of the given key object in number of bits.
*
* @param key the key object.
*
* @return the "effective" key size of the given key object.
*
* @exception InvalidKeyException if <code>key</code> is invalid.
*/
protected int engineGetKeySize(Key key) throws InvalidKeyException {
byte[] encoded = key.getEncoded();
if (encoded == null) {
throw new InvalidKeyException("Cannot decide key length");
}
// only need length
Arrays.fill(encoded, (byte) 0);
int keyLen = encoded.length;
if (!key.getAlgorithm().equalsIgnoreCase("AES") ||
!AESCrypt.isKeySizeValid(keyLen) ||
(fixedKeySize != -1 && fixedKeySize != keyLen)) {
throw new InvalidKeyException("Invalid key length: " +
keyLen + " bytes");
}
return Math.multiplyExact(keyLen, 8);
}
/**
* Wrap a key.
*
* @param key the key to be wrapped.
*
* @return the wrapped key.
*
* @exception IllegalBlockSizeException if this cipher is a block
* cipher, no padding has been requested, and the length of the
* encoding of the key to be wrapped is not a
* multiple of the block size.
*
* @exception InvalidKeyException if it is impossible or unsafe to
* wrap the key with this cipher (e.g., a hardware protected key is
* being passed to a software only cipher).
*/
@Override
protected byte[] engineWrap(Key key)
throws IllegalBlockSizeException, InvalidKeyException {
if (opmode != Cipher.WRAP_MODE) {
throw new IllegalStateException("Cipher not initialized for wrap");
}
byte[] encoded = key.getEncoded();
if ((encoded == null) || (encoded.length == 0)) {
throw new InvalidKeyException("Cannot get an encoding of " +
"the key to be wrapped");
}
// output size is known, allocate output buffer
byte[] out = new byte[engineGetOutputSize(encoded.length)];
// reserve the first semiblock and do not write data
int len = SEMI_BLKSIZE;
System.arraycopy(encoded, 0, out, len, encoded.length);
len += encoded.length;
// discard key data
Arrays.fill(encoded, (byte) 0);
try {
int outLen = helperEncrypt(out, len);
if (outLen != out.length) {
throw new AssertionError("Wrong output buffer size");
}
return out;
} catch (ShortBufferException sbe) {
// should never happen
throw new AssertionError();
}
}
/**
* Unwrap a previously wrapped key.
*
* @param wrappedKey the key to be unwrapped.
*
* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
*
* @param wrappedKeyType the type of the wrapped key.
* This is one of <code>Cipher.SECRET_KEY</code>,
* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
*
* @return the unwrapped key.
*
* @exception NoSuchAlgorithmException if no installed providers
* can create keys of type <code>wrappedKeyType</code> for the
* <code>wrappedKeyAlgorithm</code>.
*
* @exception InvalidKeyException if <code>wrappedKey</code> does not
* represent a wrapped key of type <code>wrappedKeyType</code> for
* the <code>wrappedKeyAlgorithm</code>.
*/
@Override
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType) throws InvalidKeyException,
NoSuchAlgorithmException {
if (opmode != Cipher.UNWRAP_MODE) {
throw new IllegalStateException
("Cipher not initialized for unwrap");
}
byte[] buf = wrappedKey.clone();
try {
int outLen = helperDecrypt(buf, buf.length);
return ConstructKeys.constructKey(buf, 0, outLen,
wrappedKeyAlgorithm, wrappedKeyType);
} catch (ShortBufferException sbe) {
// should never happen
throw new AssertionError();
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new InvalidKeyException(e);
} finally {
Arrays.fill(buf, (byte) 0);
}
}
}

View File

@ -192,6 +192,16 @@ public final class SunJCE extends Provider {
attrs.clear(); attrs.clear();
attrs.put("SupportedKeyFormats", "RAW"); attrs.put("SupportedKeyFormats", "RAW");
psA("Cipher", "AES/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES_KW_NoPadding",
attrs);
ps("Cipher", "AES/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES_KWP_NoPadding",
attrs);
psA("Cipher", "AES_128/ECB/NoPadding", psA("Cipher", "AES_128/ECB/NoPadding",
"com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding", "com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding",
attrs); attrs);
@ -207,6 +217,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_128/GCM/NoPadding", psA("Cipher", "AES_128/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding", "com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
attrs); attrs);
psA("Cipher", "AES_128/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_NoPadding",
attrs);
ps("Cipher", "AES_128/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES_128/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES128_KWP_NoPadding",
attrs);
psA("Cipher", "AES_192/ECB/NoPadding", psA("Cipher", "AES_192/ECB/NoPadding",
"com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding", "com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding",
@ -223,6 +242,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_192/GCM/NoPadding", psA("Cipher", "AES_192/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding", "com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
attrs); attrs);
psA("Cipher", "AES_192/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_NoPadding",
attrs);
ps("Cipher", "AES_192/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES_192/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES192_KWP_NoPadding",
attrs);
psA("Cipher", "AES_256/ECB/NoPadding", psA("Cipher", "AES_256/ECB/NoPadding",
"com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding", "com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding",
@ -239,6 +267,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_256/GCM/NoPadding", psA("Cipher", "AES_256/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding", "com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
attrs); attrs);
psA("Cipher", "AES_256/KW/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_NoPadding",
attrs);
ps("Cipher", "AES_256/KW/PKCS5Padding",
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_PKCS5Padding",
null, attrs);
psA("Cipher", "AES_256/KWP/NoPadding",
"com.sun.crypto.provider.KeyWrapCipher$AES256_KWP_NoPadding",
attrs);
attrs.clear(); attrs.clear();
attrs.put("SupportedModes", "CBC"); attrs.put("SupportedModes", "CBC");
@ -253,17 +290,6 @@ public final class SunJCE extends Provider {
attrs.put("SupportedKeyFormats", "RAW"); attrs.put("SupportedKeyFormats", "RAW");
psA("Cipher", "ARCFOUR", psA("Cipher", "ARCFOUR",
"com.sun.crypto.provider.ARCFOURCipher", attrs); "com.sun.crypto.provider.ARCFOURCipher", attrs);
ps("Cipher", "AESWrap", "com.sun.crypto.provider.AESWrapCipher$General",
null, attrs);
psA("Cipher", "AESWrap_128",
"com.sun.crypto.provider.AESWrapCipher$AES128",
attrs);
psA("Cipher", "AESWrap_192",
"com.sun.crypto.provider.AESWrapCipher$AES192",
attrs);
psA("Cipher", "AESWrap_256",
"com.sun.crypto.provider.AESWrapCipher$AES256",
attrs);
attrs.clear(); attrs.clear();
attrs.put("SupportedKeyFormats", "RAW"); attrs.put("SupportedKeyFormats", "RAW");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Oracle and/or its affiliates. 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
@ -116,20 +116,31 @@ public enum KnownOIDs {
AES_128$CBC$NoPadding("2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"), AES_128$CBC$NoPadding("2.16.840.1.101.3.4.1.2", "AES_128/CBC/NoPadding"),
AES_128$OFB$NoPadding("2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"), AES_128$OFB$NoPadding("2.16.840.1.101.3.4.1.3", "AES_128/OFB/NoPadding"),
AES_128$CFB$NoPadding("2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"), AES_128$CFB$NoPadding("2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding"),
AESWRAP_128("2.16.840.1.101.3.4.1.5"), AES_128$KW$NoPadding("2.16.840.1.101.3.4.1.5", "AES_128/KW/NoPadding",
"AESWrap_128"),
AES_128$GCM$NoPadding("2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"), AES_128$GCM$NoPadding("2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding"),
AES_128$KWP$NoPadding("2.16.840.1.101.3.4.1.8", "AES_128/KWP/NoPadding",
"AESWrapPad_128"),
AES_192$ECB$NoPadding("2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"), AES_192$ECB$NoPadding("2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding"),
AES_192$CBC$NoPadding("2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"), AES_192$CBC$NoPadding("2.16.840.1.101.3.4.1.22", "AES_192/CBC/NoPadding"),
AES_192$OFB$NoPadding("2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"), AES_192$OFB$NoPadding("2.16.840.1.101.3.4.1.23", "AES_192/OFB/NoPadding"),
AES_192$CFB$NoPadding("2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"), AES_192$CFB$NoPadding("2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding"),
AESWRAP_192("2.16.840.1.101.3.4.1.25"), AES_192$KW$NoPadding("2.16.840.1.101.3.4.1.25", "AES_192/KW/NoPadding",
"AESWrap_192"),
AES_192$GCM$NoPadding("2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"), AES_192$GCM$NoPadding("2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding"),
AES_192$KWP$NoPadding("2.16.840.1.101.3.4.1.28", "AES_192/KWP/NoPadding",
"AESWrapPad_192"),
AES_256$ECB$NoPadding("2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"), AES_256$ECB$NoPadding("2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding"),
AES_256$CBC$NoPadding("2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"), AES_256$CBC$NoPadding("2.16.840.1.101.3.4.1.42", "AES_256/CBC/NoPadding"),
AES_256$OFB$NoPadding("2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"), AES_256$OFB$NoPadding("2.16.840.1.101.3.4.1.43", "AES_256/OFB/NoPadding"),
AES_256$CFB$NoPadding("2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"), AES_256$CFB$NoPadding("2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding"),
AESWRAP_256("2.16.840.1.101.3.4.1.45"), AES_256$KW$NoPadding("2.16.840.1.101.3.4.1.45", "AES_256/KW/NoPadding",
"AESWrap_256"),
AES_256$GCM$NoPadding("2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"), AES_256$GCM$NoPadding("2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding"),
AES_256$KWP$NoPadding("2.16.840.1.101.3.4.1.48", "AES_256/KWP/NoPadding",
"AESWrapPad_256"),
// hashAlgs 2.16.840.1.101.3.4.2.* // hashAlgs 2.16.840.1.101.3.4.2.*
SHA_256("2.16.840.1.101.3.4.2.1", "SHA-256", "SHA256"), SHA_256("2.16.840.1.101.3.4.2.1", "SHA-256", "SHA256"),

View File

@ -228,5 +228,8 @@ public final class SecurityProviderConstants {
// For backward compatility, refer to PKCS1 mapping for RSA // For backward compatility, refer to PKCS1 mapping for RSA
// KeyPairGenerator and KeyFactory // KeyPairGenerator and KeyFactory
store("PKCS1", KnownOIDs.PKCS1, KnownOIDs.RSA.value()); store("PKCS1", KnownOIDs.PKCS1, KnownOIDs.RSA.value());
store("AES/KW/NoPadding", null, "AESWrap");
store("AES/KWP/NoPadding", null, "AESWrapPad");
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2004, 2021, Oracle and/or its affiliates. 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
@ -23,10 +23,10 @@
/* /*
* @test * @test
* @bug 5008156 * @bug 5008156 8248268
* @run main NISTWrapKAT * @run testng NISTWrapKAT
* @summary Verify that the "AESWrap" key wrap cipher work as * @summary Verify that the AES-Key-Wrap and AES-Key-Wrap-Pad ciphers
* expected using NIST test vectors. * work as expected using NIST test vectors.
* @author Valerie Peng * @author Valerie Peng
*/ */
import java.security.Key; import java.security.Key;
@ -35,6 +35,9 @@ import javax.crypto.*;
import javax.crypto.spec.*; import javax.crypto.spec.*;
import java.util.Arrays; import java.util.Arrays;
import java.math.BigInteger; import java.math.BigInteger;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.Assert;
public class NISTWrapKAT { public class NISTWrapKAT {
@ -42,75 +45,328 @@ public class NISTWrapKAT {
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
private static final String DATA = private static final String DATA =
"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f"; "00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
// from RFC 3394 sec4
private static String AES128_128 = private static String KW_AES128_128 =
"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5"; "1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5";
private static String AES192_128 = private static String KW_AES192_128 =
"96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d"; "96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d";
private static String AES192_192 = private static String KW_AES192_192 =
"031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2"; "031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2";
private static String AES256_128 = private static String KW_AES256_128 =
"64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7"; "64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7";
private static String AES256_192 = private static String KW_AES256_192 =
"a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1"; "a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1";
private static String AES256_256 = private static String KW_AES256_256 =
"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21"; "28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21";
public static void testKeyWrap(int keyLen, int dataLen, private static String KWP_AES128_56 = "1B1D4BC2A90B1FA389412B3D40FECB20";
String expected) throws Exception { private static String KWP_AES128_112 =
System.out.println("Testing AESWrap Cipher with " + "EA0BFDE8AF063E8918E811A05D2A4C23A367B45315716B5B";
dataLen + "-byte data with " + 8*keyLen + "-bit key"); private static String KWP_AES192_56 = "87CE2C5C2D7196E09381056B319D91E9";
Cipher c = Cipher.getInstance("AESWrap", "SunJCE"); private static String KWP_AES192_112 =
byte[] keyVal = new byte[keyLen]; "900484950F84EB6ED74CE81DCDACA26E72BB29D4A6F7AC74";
byte[] dataVal = new byte[dataLen]; 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";
// setup the key encryption key and the to-be-wrapped key private static byte[] toBytes(String hex, int hexLen) {
BigInteger temp = new BigInteger(KEK.substring(0, keyLen*2), 16); 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(); byte[] val = temp.toByteArray();
System.arraycopy(val, 0, keyVal, keyVal.length-val.length, if (val.length == outLen) {
val.length); return val;
temp = new BigInteger(DATA.substring(0, dataLen*2), 16); } else {
val = temp.toByteArray(); byte[] out = new byte[outLen];
System.arraycopy(val, 0, dataVal, dataVal.length-val.length, if (val.length < outLen) {
val.length); // 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) 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 cipherKey = new SecretKeySpec(keyVal, "AES");
c.init(Cipher.WRAP_MODE, cipherKey);
SecretKey toBeWrappedKey = new SecretKeySpec(dataVal, "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 // first test WRAP with known values
byte[] wrapped = c.wrap(toBeWrappedKey); byte[] wrapped = c1.wrap(toBeWrappedKey);
byte[] expectedVal = new BigInteger(expected, 16).toByteArray(); byte[] wrapped2 = c2.wrap(toBeWrappedKey);
// need to add offset since BigInteger may pad "0x00" in the beginning byte[] wrapped3 = c3.wrap(toBeWrappedKey);
int offset = expectedVal.length - wrapped.length;
for (int i=0; i<wrapped.length; i++) { byte[] expectedVal = toBytes(expected, expected.length());
if (wrapped[i] != expectedVal[offset + i]) {
throw new Exception("Wrap failed; got different result"); 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 // then test UNWRAP and compare with the initial values
c.init(Cipher.UNWRAP_MODE, cipherKey); c1.init(Cipher.UNWRAP_MODE, cipherKey);
Key unwrapped = c.unwrap(wrapped, "AES", Cipher.SECRET_KEY); ivSpec = new IvParameterSpec(c1.getIV());
if (!Arrays.equals(unwrapped.getEncoded(), dataVal)) { 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"); throw new Exception("Unwrap failed; got different result");
} }
} }
public static void main(String[] argv) throws Exception { @Test(dataProvider = "testData")
testKeyWrap(16, 16, AES128_128); public void testEnc(String algo, String key, int keyLen, String data, int dataLen, String expected)
// only run the tests on longer key lengths if unlimited version throws Exception {
// of JCE jurisdiction policy files are installed System.out.println("Testing " + algo + " Cipher with enc " +
dataLen + "-byte data with " + 8*keyLen + "-bit KEK");
int allowed = Cipher.getMaxAllowedKeyLength("AES"); int allowed = Cipher.getMaxAllowedKeyLength("AES");
if (allowed >= 24*8) { if (keyLen > allowed) {
testKeyWrap(24, 16, AES192_128); System.out.println("=> skip, exceeds max allowed size " + allowed);
testKeyWrap(24, 24, AES192_192); return;
} }
if (allowed >= 32*8) { Cipher c1 = Cipher.getInstance(algo, "SunJCE");
testKeyWrap(32, 16, AES256_128); Cipher c2 = Cipher.getInstance(algo, "SunJCE");
testKeyWrap(32, 24, AES256_192); Cipher c3 = Cipher.getInstance(algo, "SunJCE");
testKeyWrap(32, 32, AES256_256);
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");
} }
System.out.println("All Tests Passed");
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2021, Oracle and/or its affiliates. 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
@ -53,7 +53,7 @@ import javax.crypto.spec.PBEParameterSpec;
/* /*
* @test * @test
* @bug 8048599 * @bug 8048599 8248268
* @summary Tests for key wrap and unwrap operations * @summary Tests for key wrap and unwrap operations
*/ */
@ -86,6 +86,10 @@ public class TestCipherKeyWrapperTest {
AESWrap_128("AES", "AESWrap_128", 128), AESWrap_128("AES", "AESWrap_128", 128),
AESWrap_192("AES", "AESWrap_192", 192), AESWrap_192("AES", "AESWrap_192", 192),
AESWrap_256("AES", "AESWrap_256", 256), 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),
DESedeWrap("desede", "DESedeWrap", -1), DESedeWrap("desede", "DESedeWrap", -1),
NegtiveWrap("AES", "DESedeWrap", -1); NegtiveWrap("AES", "DESedeWrap", -1);
@ -287,6 +291,8 @@ public class TestCipherKeyWrapperTest {
new Random().nextBytes(salt); new Random().nextBytes(salt);
pbeParams = new PBEParameterSpec(salt, iterCnt); pbeParams = new PBEParameterSpec(salt, iterCnt);
} }
out.println("Testing " + wrapAlgo + " cipher wrap/unwrap");
// Wrap & UnWrap operation // Wrap & UnWrap operation
Cipher wrapCI = Cipher.getInstance(wrapAlgo); Cipher wrapCI = Cipher.getInstance(wrapAlgo);
if (isPBE && !isAESBlowfish) { if (isPBE && !isAESBlowfish) {
@ -297,7 +303,6 @@ public class TestCipherKeyWrapperTest {
} else { } else {
wrapCI.init(Cipher.WRAP_MODE, initKey); wrapCI.init(Cipher.WRAP_MODE, initKey);
} }
out.println("keysize : " + wrapKey.getEncoded().length);
byte[] keyWrapper = wrapCI.wrap(wrapKey); byte[] keyWrapper = wrapCI.wrap(wrapKey);
if (isPBE && !isAESBlowfish) { if (isPBE && !isAESBlowfish) {
wrapCI.init(Cipher.UNWRAP_MODE, initKey, pbeParams); wrapCI.init(Cipher.UNWRAP_MODE, initKey, pbeParams);
@ -309,6 +314,7 @@ public class TestCipherKeyWrapperTest {
Key unwrappedKey = wrapCI.unwrap(keyWrapper, algo, keyType); Key unwrappedKey = wrapCI.unwrap(keyWrapper, algo, keyType);
// Comparison // Comparison
if (!Arrays.equals(wrapKey.getEncoded(), unwrappedKey.getEncoded())) { if (!Arrays.equals(wrapKey.getEncoded(), unwrappedKey.getEncoded())) {
out.println("keysize : " + wrapKey.getEncoded().length);
throw new RuntimeException("Comparation failed testing " throw new RuntimeException("Comparation failed testing "
+ transformation + ":" + wrapAlgo + ":" + keyType); + transformation + ":" + wrapAlgo + ":" + keyType);
} }

View File

@ -0,0 +1,187 @@
/*
* 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 8248268
* @summary Verify general properties of the AES/KW/NoPadding,
* AES/KW/PKCS5Padding, and AES/KWP/NoPadding.
* @run main TestGeneral
*/
import java.util.Arrays;
import java.util.Random;
import java.security.Key;
import java.security.InvalidAlgorithmParameterException;
import javax.crypto.*;
import javax.crypto.spec.*;
public class TestGeneral {
private static final SecretKey KEY = new SecretKeySpec(new byte[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 = 16; // 1-16
private static final int MAX_KWP_PAD_LEN = 7; // 0...7
public static void testEnc(Cipher c, byte[] in, int inLen, int ivLen,
int maxPadLen) throws Exception {
System.out.println("input len: " + inLen);
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
int estOutLen = c.getOutputSize(inLen);
byte[] out = c.doFinal(in, 0, inLen);
// for encryption output, the estimate should match the actual
if (estOutLen != out.length) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual enc out length: " + out.length);
throw new RuntimeException("Failed enc output len check");
}
// encryption outout should always be multiple of 8 and at least 8-byte
// longer than input
if ((out.length % 8 != 0) || (out.length - inLen < 8)) {
throw new RuntimeException("Invalid length of encrypted data: " +
out.length);
}
c.init(Cipher.DECRYPT_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
estOutLen = c.getOutputSize(out.length);
byte[] in2 = c.doFinal(out);
// for decryption output, the estimate should match the actual for
// AES/KW/NoPadding and slightly larger than the actual for the rest
if (estOutLen < in2.length || (estOutLen - in2.length) > maxPadLen) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual dec out length: " + in2.length);
throw new RuntimeException("Failed dec output len check");
}
if (!Arrays.equals(in, 0, inLen, in2, 0, inLen)) {
throw new RuntimeException("Failed decrypted data check");
}
}
public static void testWrap(Cipher c, byte[] in, int inLen, int ivLen,
int maxPadLen) throws Exception {
System.out.println("key len: " + inLen);
c.init(Cipher.WRAP_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
int estOutLen = c.getOutputSize(inLen);
byte[] out = c.wrap(new SecretKeySpec(in, 0, inLen, "Any"));
// for encryption output, the estimate should match the actual
if (estOutLen != out.length) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual wrap out length: " + out.length);
throw new RuntimeException("Failed wrap output len check");
}
// encryption outout should always be multiple of 8 and at least 8-byte
// longer than input
if ((out.length % 8 != 0) || (out.length - inLen < 8)) {
throw new RuntimeException("Invalid length of encrypted data: " +
out.length);
}
c.init(Cipher.UNWRAP_MODE, KEY, new IvParameterSpec(in, 0, ivLen));
estOutLen = c.getOutputSize(out.length);
Key key2 = c.unwrap(out, "Any", Cipher.SECRET_KEY);
if (!(key2 instanceof SecretKey)) {
throw new RuntimeException("Failed unwrap output type check");
}
byte[] in2 = key2.getEncoded();
// for decryption output, the estimate should match the actual for
// AES/KW/NoPadding and slightly larger than the actual for the rest
if (estOutLen < in2.length || (estOutLen - in2.length) > maxPadLen) {
System.out.println("=> estimated: " + estOutLen);
System.out.println("=> actual unwrap out length: " + in2.length);
throw new RuntimeException("Failed unwrap output len check");
}
if (inLen != in2.length ||
!Arrays.equals(in, 0, inLen, in2, 0, inLen)) {
throw new RuntimeException("Failed unwrap data check");
}
}
public static void testIv(Cipher c) throws Exception {
c.init(Cipher.ENCRYPT_MODE, KEY);
byte[] defIv = c.getIV();
// try init w/ an iv w/ different length
try {
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv, 0,
defIv.length/2));
} catch (InvalidAlgorithmParameterException iape) {
System.out.println("Invalid IV rejected as expected");
}
Arrays.fill(defIv, (byte) 0xFF);
c.init(Cipher.ENCRYPT_MODE, KEY, new IvParameterSpec(defIv));
byte[] newIv = c.getIV();
if (!Arrays.equals(newIv, defIv)) {
throw new RuntimeException("Failed iv check");
}
}
public static void main(String[] argv) throws Exception {
// test all possible pad lengths, i.e. 1 - 16
byte[] data = new byte[128];
new Random().nextBytes(data);
String ALGO = "AES/KW/PKCS5Padding";
System.out.println("Testing " + ALGO);
Cipher c = Cipher.getInstance(ALGO, "SunJCE");
for (int i = 0; i < MAX_KW_PKCS5PAD_LEN; i++) {
testEnc(c, data, data.length - i, KW_IV_LEN, MAX_KW_PKCS5PAD_LEN);
testWrap(c, data, data.length - i, KW_IV_LEN, MAX_KW_PKCS5PAD_LEN);
}
testIv(c);
ALGO = "AES/KW/NoPadding";
System.out.println("Testing " + ALGO);
c = Cipher.getInstance(ALGO, "SunJCE");
testEnc(c, data, data.length, KW_IV_LEN, 0);
testEnc(c, data, data.length >> 1, KW_IV_LEN, 0);
testWrap(c, data, data.length, KW_IV_LEN, 0);
testWrap(c, data, data.length >> 1, KW_IV_LEN, 0);
testIv(c);
ALGO = "AES/KWP/NoPadding";
System.out.println("Testing " + ALGO);
c = Cipher.getInstance(ALGO, "SunJCE");
for (int i = 0; i < MAX_KWP_PAD_LEN; i++) {
testEnc(c, data, data.length - i, KWP_IV_LEN, MAX_KWP_PAD_LEN);
testWrap(c, data, data.length - i, KWP_IV_LEN, MAX_KWP_PAD_LEN);
}
testIv(c);
System.out.println("All Tests Passed");
}
}

View File

@ -0,0 +1,104 @@
/*
* 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 8248268
* @summary Verify cipher key size restriction is enforced properly with IKE
* @run main TestKeySizeCheck
*/
import java.util.Arrays;
import java.util.Random;
import java.security.Key;
import java.security.InvalidKeyException;
import javax.crypto.*;
import javax.crypto.spec.*;
public class TestKeySizeCheck {
private static final byte[] BYTES_32 = new byte[32];
static {
for (int i = 0; i < BYTES_32.length; i++) {
BYTES_32[i] = (byte) i;
}
}
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(String algo, int[] invalidKeySizes)
throws Exception {
System.out.println("Testing " + algo);
Cipher c = Cipher.getInstance(algo, "SunJCE");
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[] argv) throws Exception {
test("AESWrap", new int[] { 120, 264 });
test("AESWrap_128", new int[] { 192, 256 });
test("AESWrap_192", new int[] { 128, 256 });
test("AESWrap_256", new int[] { 128, 192 });
test("AESWrapPad", new int[] { 120, 264 });
test("AESWrapPad_128", new int[] { 192, 256 });
test("AESWrapPad_192", new int[] { 128, 256 });
test("AESWrapPad_256", new int[] { 128, 192 });
test("AES/KW/NoPadding", new int[] { 120, 264 });
test("AES_128/KW/NoPadding", new int[] { 192, 256 });
test("AES_192/KW/NoPadding", new int[] { 128, 256 });
test("AES_256/KW/NoPadding", new int[] { 128, 192 });
test("AES/KWP/NoPadding", new int[] { 120, 264 });
test("AES_128/KWP/NoPadding", new int[] { 192, 256 });
test("AES_192/KWP/NoPadding", new int[] { 128, 256 });
test("AES_256/KWP/NoPadding", new int[] { 128, 192 });
System.out.println("All Tests Passed");
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2021, Oracle and/or its affiliates. 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
@ -38,8 +38,8 @@ import javax.crypto.SecretKey;
/* /*
* @test * @test
* @bug 8075286 * @bug 8075286 8248268
* @summary Test the AESWrap algorithm OIDs in JDK. * @summary Test the AES-Key-Wrap and AES-Key-Wrap-Pad algorithm OIDs in JDK.
* OID and Algorithm transformation string should match. * OID and Algorithm transformation string should match.
* Both could be able to be used to generate the algorithm instance. * Both could be able to be used to generate the algorithm instance.
* @run main TestAESWrapOids * @run main TestAESWrapOids
@ -49,9 +49,15 @@ public class TestAESWrapOids {
private static final String PROVIDER_NAME = "SunJCE"; private static final String PROVIDER_NAME = "SunJCE";
private static final List<DataTuple> DATA = Arrays.asList( private static final List<DataTuple> DATA = Arrays.asList(
new DataTuple("2.16.840.1.101.3.4.1.5", "AESWrap_128", 128), new DataTuple("2.16.840.1.101.3.4.1.5", "AESWrap_128"),
new DataTuple("2.16.840.1.101.3.4.1.25", "AESWrap_192", 192), new DataTuple("2.16.840.1.101.3.4.1.25", "AESWrap_192"),
new DataTuple("2.16.840.1.101.3.4.1.45", "AESWrap_256", 256)); new DataTuple("2.16.840.1.101.3.4.1.45", "AESWrap_256"),
new DataTuple("2.16.840.1.101.3.4.1.5", "AES_128/KW/NoPadding"),
new DataTuple("2.16.840.1.101.3.4.1.25", "AES_192/KW/NoPadding"),
new DataTuple("2.16.840.1.101.3.4.1.45", "AES_256/KW/NoPadding"),
new DataTuple("2.16.840.1.101.3.4.1.8", "AES_128/KWP/NoPadding"),
new DataTuple("2.16.840.1.101.3.4.1.28", "AES_192/KWP/NoPadding"),
new DataTuple("2.16.840.1.101.3.4.1.48", "AES_256/KWP/NoPadding"));
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
for (DataTuple dataTuple : DATA) { for (DataTuple dataTuple : DATA) {
@ -145,10 +151,15 @@ public class TestAESWrapOids {
private final String algorithm; private final String algorithm;
private final int keyLength; private final int keyLength;
private DataTuple(String oid, String algorithm, int keyLength) { private DataTuple(String oid, String algorithm) {
this.oid = oid; this.oid = oid;
this.algorithm = algorithm; this.algorithm = algorithm;
this.keyLength = keyLength; this.keyLength = switch (oid) {
case "2.16.840.1.101.3.4.1.5", "2.16.840.1.101.3.4.1.8"->128;
case "2.16.840.1.101.3.4.1.25", "2.16.840.1.101.3.4.1.28"->192;
case "2.16.840.1.101.3.4.1.45", "2.16.840.1.101.3.4.1.48"->256;
default->throw new RuntimeException("Unrecognized oid: " + oid);
};
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2004, 2021, Oracle and/or its affiliates. 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
@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 4953556 8210838 * @bug 4953556 8210838 8248268
* @summary ensure that IllegalStateException is thrown if the * @summary ensure that IllegalStateException is thrown if the
* Cipher object is initialized with a wrong mode, e.g. WRAP_MODE * Cipher object is initialized with a wrong mode, e.g. WRAP_MODE
* for update()/doFinal() calls. * for update()/doFinal() calls.
@ -39,44 +39,52 @@ import javax.crypto.spec.SecretKeySpec;
public class TestCipherMode { public class TestCipherMode {
private static final String ALGO = "DES"; private static final String[] TRANSFORMATIONS = {
"DES/ECB/PKCS5Padding",
"AES/KW/NoPadding",
"AES/KW/PKCS5Padding",
"AES/KWP/NoPadding",
};
private static final SecretKey DES_KEY =
new SecretKeySpec(new byte[8], "DES");
private static final SecretKey AES_KEY =
new SecretKeySpec(new byte[16], "AES");
public static void main(String[] argv) throws Exception { public static void main(String[] argv) throws Exception {
TestCipherMode test = new TestCipherMode(); for (String t : TRANSFORMATIONS) {
System.out.println("Testing ENCRYPT_MODE..."); System.out.println("Testing SunJCE provider, Cipher " + t );
test.checkMode(Cipher.ENCRYPT_MODE, "encryption");
System.out.println("Testing DECRYPT_MODE..."); TestCipherMode test = new TestCipherMode(t);
test.checkMode(Cipher.DECRYPT_MODE, "decryption"); System.out.println("Testing ENCRYPT_MODE...");
System.out.println("Testing WRAP_MODE..."); test.checkMode(Cipher.ENCRYPT_MODE, "encryption");
test.checkMode(Cipher.WRAP_MODE, "key wrapping"); System.out.println("Testing DECRYPT_MODE...");
System.out.println("Testing UNWRAP_MODE..."); test.checkMode(Cipher.DECRYPT_MODE, "decryption");
test.checkMode(Cipher.UNWRAP_MODE, "key unwrapping"); System.out.println("Testing WRAP_MODE...");
test.checkMode(Cipher.WRAP_MODE, "key wrapping");
System.out.println("Testing UNWRAP_MODE...");
test.checkMode(Cipher.UNWRAP_MODE, "key unwrapping");
}
System.out.println("All Tests Passed"); System.out.println("All Tests Passed");
} }
private Cipher c = null; private Cipher c = null;
private SecretKey key = null; private SecretKey key = null;
private TestCipherMode() throws NoSuchAlgorithmException, private TestCipherMode(String transformation)
NoSuchProviderException, NoSuchPaddingException { throws NoSuchAlgorithmException, NoSuchProviderException,
c = Cipher.getInstance(ALGO + "/ECB/PKCS5Padding", "SunJCE"); NoSuchPaddingException {
String output = c.toString(); c = Cipher.getInstance(transformation, "SunJCE");
if (!output.equals( this.key = switch (transformation.split("/")[0]) {
"Cipher.DES/ECB/PKCS5Padding, mode: not initialized, algorithm from: SunJCE")) { case "DES" -> DES_KEY;
throw new RuntimeException( case "AES" -> AES_KEY;
"Unexpected Cipher.toString() output:" + output); default -> throw new RuntimeException
} ("Error: Unsupported key algorithm");
key = new SecretKeySpec(new byte[8], ALGO); };
} }
private void checkMode(int mode, String opString) throws Exception { private void checkMode(int mode, String opString) throws Exception {
c.init(mode, key); c.init(mode, key);
String output = c.toString();
if (!output.contains("Cipher.DES/ECB/PKCS5Padding")
&& !output.contains(opString)
&& !output.contains("Algorithm from: SunJCE")) {
throw new Exception("Unexpected toString() output:" + output);
}
switch (mode) { switch (mode) {
case Cipher.ENCRYPT_MODE: case Cipher.ENCRYPT_MODE:
@ -89,7 +97,7 @@ public class TestCipherMode {
System.out.println("expected ISE is thrown for wrap()"); System.out.println("expected ISE is thrown for wrap()");
} }
try { try {
c.unwrap(new byte[16], ALGO, Cipher.SECRET_KEY); c.unwrap(new byte[16], key.getAlgorithm(), Cipher.SECRET_KEY);
throw new Exception("ERROR: should throw ISE for unwrap()"); throw new Exception("ERROR: should throw ISE for unwrap()");
} catch (IllegalStateException ise) { } catch (IllegalStateException ise) {
System.out.println("expected ISE is thrown for unwrap()"); System.out.println("expected ISE is thrown for unwrap()");

View File

@ -0,0 +1,92 @@
/*
* 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.
*/
package org.openjdk.bench.javax.crypto.full;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import java.util.concurrent.TimeUnit;
import java.security.Key;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidParameterSpecException;
public class AESKeyWrapBench extends CryptoBase {
@Param({"AES/KW/NoPadding" , "AES/KW/PKCS5Padding", "AES/KWP/NoPadding"})
private String algorithm;
@Param({"128"})
private int keyLength;
@Param({"16", "24"})
private int dataSize;
SecretKeySpec toBeWrappedKey;
byte[] wrappedKey;
private Cipher encryptCipher;
private Cipher decryptCipher;
SecretKeySpec ks;
@Setup
public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException {
setupProvider();
byte[] keystring = fillSecureRandom(new byte[keyLength / 8]);
ks = new SecretKeySpec(keystring, "AES");
encryptCipher = makeCipher(prov, algorithm);
encryptCipher.init(Cipher.WRAP_MODE, ks);
decryptCipher = makeCipher(prov, algorithm);
decryptCipher.init(Cipher.UNWRAP_MODE, ks);
byte[] data = fillRandom(new byte[dataSize]);
toBeWrappedKey = new SecretKeySpec(data, "Custom");
wrappedKey = encryptCipher.wrap(toBeWrappedKey);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public byte[] wrap() throws InvalidKeyException, IllegalBlockSizeException {
return encryptCipher.wrap(toBeWrappedKey);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public Key unwrap() throws InvalidKeyException, NoSuchAlgorithmException {
return decryptCipher.unwrap(wrappedKey, "Custom", Cipher.SECRET_KEY);
}
}