diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java
new file mode 100644
index 00000000000..53560273213
--- /dev/null
+++ b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrap.java
@@ -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
+ *
+ * "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"
+ * 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 KW
+ */
+ @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.
+ *
+ *
The input pt
, starting at 0
+ * and ending at ptLen-1
, is encrypted.
+ * The result is stored in place into pt
, starting at
+ * 0
.
+ *
+ *
The subclass that implements Cipher should ensure that
+ * init
has been called before this method is called.
+ *
+ * @param pt the input buffer with the data to be encrypted
+ * @param dummy1 the offset in pt
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 pt
+ */
+ @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.
+ *
+ *
The input ct
, starting at 0
+ * and ending at ctLen-1
, is decrypted.
+ * The result is stored in place into ct
, starting at
+ * 0
.
+ *
+ *
NOTE: Purpose of this special impl is for minimizing array + * copying, those unused arguments are named as dummyN. + * + *
The subclass that implements Cipher should ensure that
+ * init
has been called before this method is called.
+ *
+ * @param ct the input buffer with the data to be decrypted
+ * @param dummy1 the offset in ct
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 ct
+ */
+ @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;
+ }
+}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java
new file mode 100644
index 00000000000..c08c4389ce7
--- /dev/null
+++ b/src/java.base/share/classes/com/sun/crypto/provider/AESKeyWrapPadded.java
@@ -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
+ *
+ * "Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping"
+ * 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 KW
+ */
+ @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.
+ *
+ *
The input pt
, starting at 0
+ * and ending at ptLen-1
, is encrypted.
+ * The result is stored in place into pt
, starting at
+ * 0
.
+ *
+ *
The subclass that implements Cipher should ensure that
+ * init
has been called before this method is called.
+ *
+ * @param pt the input buffer with the data to be encrypted
+ * @param dummy1 the offset in pt
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 pt
+ */
+ @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.
+ *
+ *
The input ct
, starting at 0
+ * and ending at ctLen-1
, is decrypted.
+ * The result is stored in place into ct
, starting at
+ * 0
.
+ *
+ *
The subclass that implements Cipher should ensure that
+ * init
has been called before this method is called.
+ *
+ * @param ct the input buffer with the data to be decrypted
+ * @param dummy1 the offset in ct
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 ct
+ */
+ @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;
+ }
+}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java
index 6c2cdf24742..4c1c93ceb3c 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/AESParameters.java
@@ -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.
*
* 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:
*
*
- * IV ::= OCTET STRING -- 16 octets + * IV ::= OCTET STRING -- 8 octets for KW, 4 octets for KWP, and 16 octets for + * other feedback modes ** * @author Valerie Peng @@ -46,7 +47,7 @@ public final class AESParameters extends AlgorithmParametersSpi { private BlockCipherParamsCore core; public AESParameters() { - core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE); + core = new BlockCipherParamsCore(AESConstants.AES_BLOCK_SIZE, 4, 8); } protected void engineInit(AlgorithmParameterSpec paramSpec) diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AESWrapCipher.java b/src/java.base/share/classes/com/sun/crypto/provider/AESWrapCipher.java deleted file mode 100644 index 6a9985ee16e..00000000000 --- a/src/java.base/share/classes/com/sun/crypto/provider/AESWrapCipher.java +++ /dev/null @@ -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 - * "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap". - * Note: only
ECB
mode and NoPadding
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 inputLen
(in bytes).
- *
- * The actual output length of the next update
or
- * doFinal
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.
- *
- *
The cipher only supports the following two operation modes: - * Cipher.WRAP_MODE, and - * Cipher.UNWRAP_MODE. - *
For modes other than the above two, UnsupportedOperationException
- * will be thrown.
- *
- * @param opmode the operation mode of this cipher. Only
- * WRAP_MODE
or UNWRAP_MODE
) 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.
- *
- *
The cipher only supports the following two operation modes: - * Cipher.WRAP_MODE, and - * Cipher.UNWRAP_MODE. - *
For modes other than the above two, UnsupportedOperationException
- * will be thrown.
- *
- * @param opmode the operation mode of this cipher. Only
- * WRAP_MODE
or UNWRAP_MODE
) 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.
- *
- *
The cipher only supports the following two operation modes: - * Cipher.WRAP_MODE, and - * Cipher.UNWRAP_MODE. - *
For modes other than the above two, UnsupportedOperationException
- * will be thrown.
- *
- * @param opmode the operation mode of this cipher. Only
- * The actual output length of the next WRAP_MODE
or UNWRAP_MODE
) 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 in
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 in
where the input
- * starts.
- * @param inLen the input length.
- * @param out the buffer for the result.
- * @param outOffset the offset in out
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 in
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 in
where the input
- * starts.
- * @param inLen the input length.
- * @param out the buffer for the result.
- * @param outOffset the ofset in out
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 key
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 Cipher.SECRET_KEY
,
- * Cipher.PRIVATE_KEY
, or Cipher.PUBLIC_KEY
.
- *
- * @return the unwrapped key.
- *
- * @exception NoSuchAlgorithmException if no installed providers
- * can create keys of type wrappedKeyType
for the
- * wrappedKeyAlgorithm
.
- *
- * @exception InvalidKeyException if wrappedKey
does not
- * represent a wrapped key of type wrappedKeyType
for
- * the wrappedKeyAlgorithm
.
- */
- 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);
- }
- }
-}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java b/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java
index baddb84b625..20af0c78309 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/BlockCipherParamsCore.java
@@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
package com.sun.crypto.provider;
import java.io.*;
+import java.util.Arrays;
import sun.security.util.*;
import sun.security.util.HexDumpEncoder;
import java.security.spec.AlgorithmParameterSpec;
@@ -48,8 +49,11 @@ final class BlockCipherParamsCore {
private int block_size = 0;
private byte[] iv = null;
- BlockCipherParamsCore(int blksize) {
+ private int[] moreSizes = null;
+
+ BlockCipherParamsCore(int blksize, int... moreSizes) {
block_size = blksize;
+ this.moreSizes = moreSizes;
}
void init(AlgorithmParameterSpec paramSpec)
@@ -59,9 +63,20 @@ final class BlockCipherParamsCore {
("Inappropriate parameter specification");
}
byte[] tmpIv = ((IvParameterSpec)paramSpec).getIV();
- if (tmpIv.length != block_size) {
- throw new InvalidParameterSpecException("IV not " +
- block_size + " bytes long");
+ boolean check = (tmpIv.length == block_size);
+ if (!check && moreSizes != null) {
+ 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();
}
@@ -73,15 +88,17 @@ final class BlockCipherParamsCore {
if (der.available() != 0) {
throw new IOException("IV parsing error: extra data");
}
- if (tmpIv.length != block_size) {
- throw new IOException("IV not " + block_size +
+ boolean check = (tmpIv.length == block_size);
+ if (!check) {
+ String expectedLen = block_size + (moreSizes == null? "" :
+ Arrays.toString(moreSizes));
+ throw new IOException("IV not " + expectedLen +
" bytes long");
}
iv = tmpIv;
}
- void init(byte[] encoded, String decodingMethod)
- throws IOException {
+ void init(byte[] encoded, String decodingMethod) throws IOException {
if ((decodingMethod != null) &&
(!decodingMethod.equalsIgnoreCase("ASN.1"))) {
throw new IllegalArgumentException("Only support ASN.1 format");
@@ -90,8 +107,7 @@ final class BlockCipherParamsCore {
}
inLen
(in bytes).
+ *
+ * update
or
+ * doFinal
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 in
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 in
where the input
+ * starts.
+ * @param inLen the input length.
+ * @param out the buffer for the result.
+ * @param outOffset the offset in out
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 in
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 in
where the input
+ * starts.
+ * @param inLen the input length.
+ * @param out the buffer for the result.
+ * @param outOffset the ofset in out
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 key
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 Cipher.SECRET_KEY
,
+ * Cipher.PRIVATE_KEY
, or Cipher.PUBLIC_KEY
.
+ *
+ * @return the unwrapped key.
+ *
+ * @exception NoSuchAlgorithmException if no installed providers
+ * can create keys of type wrappedKeyType
for the
+ * wrappedKeyAlgorithm
.
+ *
+ * @exception InvalidKeyException if wrappedKey
does not
+ * represent a wrapped key of type wrappedKeyType
for
+ * the wrappedKeyAlgorithm
.
+ */
+ @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);
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
index 030c3c078ec..521afa4b696 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
@@ -192,6 +192,16 @@ public final class SunJCE extends Provider {
attrs.clear();
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",
"com.sun.crypto.provider.AESCipher$AES128_ECB_NoPadding",
attrs);
@@ -207,6 +217,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_128/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
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",
"com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding",
@@ -223,6 +242,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_192/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
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",
"com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding",
@@ -239,6 +267,15 @@ public final class SunJCE extends Provider {
psA("Cipher", "AES_256/GCM/NoPadding",
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
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.put("SupportedModes", "CBC");
@@ -253,17 +290,6 @@ public final class SunJCE extends Provider {
attrs.put("SupportedKeyFormats", "RAW");
psA("Cipher", "ARCFOUR",
"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.put("SupportedKeyFormats", "RAW");
diff --git a/src/java.base/share/classes/sun/security/util/KnownOIDs.java b/src/java.base/share/classes/sun/security/util/KnownOIDs.java
index cdb24121722..e1f3aeed46d 100644
--- a/src/java.base/share/classes/sun/security/util/KnownOIDs.java
+++ b/src/java.base/share/classes/sun/security/util/KnownOIDs.java
@@ -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.
*
* 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$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"),
- 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$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$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$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$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$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$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$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.*
SHA_256("2.16.840.1.101.3.4.2.1", "SHA-256", "SHA256"),
diff --git a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java
index 43122249032..6c65c19ba85 100644
--- a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java
+++ b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java
@@ -228,5 +228,8 @@ public final class SecurityProviderConstants {
// For backward compatility, refer to PKCS1 mapping for RSA
// KeyPairGenerator and KeyFactory
store("PKCS1", KnownOIDs.PKCS1, KnownOIDs.RSA.value());
+
+ store("AES/KW/NoPadding", null, "AESWrap");
+ store("AES/KWP/NoPadding", null, "AESWrapPad");
}
}
diff --git a/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java b/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java
index f9308417392..3da7c7072f0 100644
--- a/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java
+++ b/test/jdk/com/sun/crypto/provider/Cipher/KeyWrap/NISTWrapKAT.java
@@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,10 +23,10 @@
/*
* @test
- * @bug 5008156
- * @run main NISTWrapKAT
- * @summary Verify that the "AESWrap" key wrap cipher work as
- * expected using NIST test vectors.
+ * @bug 5008156 8248268
+ * @run testng NISTWrapKAT
+ * @summary Verify that the AES-Key-Wrap and AES-Key-Wrap-Pad ciphers
+ * work as expected using NIST test vectors.
* @author Valerie Peng
*/
import java.security.Key;
@@ -35,6 +35,9 @@ import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Arrays;
import java.math.BigInteger;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.testng.Assert;
public class NISTWrapKAT {
@@ -42,75 +45,328 @@ public class NISTWrapKAT {
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
private static final String DATA =
"00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f";
-
- private static String AES128_128 =
+ // from RFC 3394 sec4
+ private static String KW_AES128_128 =
"1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5";
- private static String AES192_128 =
+ private static String KW_AES192_128 =
"96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d";
- private static String AES192_192 =
+ private static String KW_AES192_192 =
"031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2";
- private static String AES256_128 =
+ private static String KW_AES256_128 =
"64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7";
- private static String AES256_192 =
+ private static String KW_AES256_192 =
"a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1";
- private static String AES256_256 =
+ private static String KW_AES256_256 =
"28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21";
- public static void testKeyWrap(int keyLen, int dataLen,
- String expected) throws Exception {
- System.out.println("Testing AESWrap Cipher with " +
- dataLen + "-byte data with " + 8*keyLen + "-bit key");
- Cipher c = Cipher.getInstance("AESWrap", "SunJCE");
- byte[] keyVal = new byte[keyLen];
- byte[] dataVal = new byte[dataLen];
+ private static String KWP_AES128_56 = "1B1D4BC2A90B1FA389412B3D40FECB20";
+ private static String KWP_AES128_112 =
+ "EA0BFDE8AF063E8918E811A05D2A4C23A367B45315716B5B";
+ private static String KWP_AES192_56 = "87CE2C5C2D7196E09381056B319D91E9";
+ private static String KWP_AES192_112 =
+ "900484950F84EB6ED74CE81DCDACA26E72BB29D4A6F7AC74";
+ private static String KWP_AES192_168 =
+ "A402348F1956DB968FDDFD8976420F9DDEB7183CF16B91B0AEB74CAB196C343E";
+ private static String KWP_AES256_56 = "809BB1864A18938529E97EFCD9544E9A";
+ private static String KWP_AES256_112 =
+ "C68168173F141E6D5767611574A941259090DA78D7DF9DF7";
+ private static String KWP_AES256_168 =
+ "308D49692B5F8CF638D54BB4B985633504237329964C76EBB3F669870A708DBC";
+ private static String KWP_AES256_224 =
+ "0942747DB07032A3F04CDB2E7DE1CBA038F92BC355393AE9A0E4AE8C901912AC3D3AF0F16D240607";
+ // from RFC 5649 sec6
+ private static String KEK2 = "5840DF6E29B02AF1AB493B705BF16EA1AE8338F4DCC176A8";
- // setup the key encryption key and the to-be-wrapped key
- BigInteger temp = new BigInteger(KEK.substring(0, keyLen*2), 16);
+ private static byte[] toBytes(String hex, int hexLen) {
+ if (hexLen < hex.length()) {
+ hex = hex.substring(0, hexLen);
+ } else {
+ hexLen = hex.length();
+ }
+ int outLen = hexLen >> 1;
+ BigInteger temp = new BigInteger(hex, 16);
byte[] val = temp.toByteArray();
- System.arraycopy(val, 0, keyVal, keyVal.length-val.length,
- val.length);
- temp = new BigInteger(DATA.substring(0, dataLen*2), 16);
- val = temp.toByteArray();
- System.arraycopy(val, 0, dataVal, dataVal.length-val.length,
- val.length);
+ if (val.length == outLen) {
+ return val;
+ } else {
+ byte[] out = new byte[outLen];
+ if (val.length < outLen) {
+ // enlarge
+ System.arraycopy(val, 0, out, outLen - val.length, val.length);
+ } else {
+ // truncate
+ System.arraycopy(val, val.length - outLen, out, 0, outLen);
+ }
+ return out;
+ }
+ }
+
+ @DataProvider
+ public Object[][] testData() {
+ return new Object[][] {
+ { "AESWrap", KEK, 16, DATA, 16, KW_AES128_128 },
+ { "AESWrap", KEK, 24, DATA, 16, KW_AES192_128 },
+ { "AESWrap", KEK, 24, DATA, 24, KW_AES192_192 },
+ { "AESWrap", KEK, 32, DATA, 16, KW_AES256_128 },
+ { "AESWrap", KEK, 32, DATA, 24, KW_AES256_192 },
+ { "AESWrap", KEK, 32, DATA, 32, KW_AES256_256 },
+ { "AESWrap_128", KEK, 16, DATA, 16, KW_AES128_128 },
+ { "AESWrap_192", KEK, 24, DATA, 16, KW_AES192_128 },
+ { "AESWrap_256", KEK, 32, DATA, 16, KW_AES256_128 },
+ { "AES/KW/NoPadding", KEK, 16, DATA, 16, KW_AES128_128 },
+ { "AES/KW/NoPadding", KEK, 24, DATA, 16, KW_AES192_128 },
+ { "AES/KW/NoPadding", KEK, 24, DATA, 24, KW_AES192_192 },
+ { "AES/KW/NoPadding", KEK, 32, DATA, 16, KW_AES256_128 },
+ { "AES/KW/NoPadding", KEK, 32, DATA, 24, KW_AES256_192 },
+ { "AES/KW/NoPadding", KEK, 32, DATA, 32, KW_AES256_256 },
+ { "AESWrapPad", KEK, 16, DATA, 7, KWP_AES128_56 },
+ { "AESWrapPad", KEK, 16, DATA, 14, KWP_AES128_112 },
+ { "AESWrapPad", KEK, 24, DATA, 7, KWP_AES192_56 },
+ { "AESWrapPad", KEK, 24, DATA, 14, KWP_AES192_112 },
+ { "AESWrapPad", KEK, 24, DATA, 21, KWP_AES192_168 },
+ { "AESWrapPad", KEK, 32, DATA, 7, KWP_AES256_56 },
+ { "AESWrapPad", KEK, 32, DATA, 14, KWP_AES256_112 },
+ { "AESWrapPad", KEK, 32, DATA, 21, KWP_AES256_168 },
+ { "AESWrapPad", KEK, 32, DATA, 28, KWP_AES256_224 },
+ { "AESWrapPad_128", KEK, 16, DATA, 7, KWP_AES128_56 },
+ { "AESWrapPad_192", KEK, 24, DATA, 7, KWP_AES192_56 },
+ { "AESWrapPad_256", KEK, 32, DATA, 7, KWP_AES256_56 },
+ { "AES/KWP/NoPadding", KEK, 16, DATA, 7, KWP_AES128_56 },
+ { "AES/KWP/NoPadding", KEK, 16, DATA, 14, KWP_AES128_112 },
+ { "AES/KWP/NoPadding", KEK, 24, DATA, 7, KWP_AES192_56 },
+ { "AES/KWP/NoPadding", KEK, 24, DATA, 14, KWP_AES192_112 },
+ { "AES/KWP/NoPadding", KEK, 24, DATA, 21, KWP_AES192_168 },
+ { "AES/KWP/NoPadding", KEK, 32, DATA, 7, KWP_AES256_56 },
+ { "AES/KWP/NoPadding", KEK, 32, DATA, 14, KWP_AES256_112 },
+ { "AES/KWP/NoPadding", KEK, 32, DATA, 21, KWP_AES256_168 },
+ { "AES/KWP/NoPadding", KEK, 32, DATA, 28, KWP_AES256_224 },
+ { "AES/KWP/NoPadding", KEK2, 24, "466F7250617369", 7,
+ "AFBEB0F07DFBF5419200F2CCB50BB24F" },
+ { "AES/KWP/NoPadding", KEK2, 24,
+ "C37B7E6492584340BED12207808941155068F738", 20,
+ "138BDEAA9B8FA7FC61F97742E72248EE5AE6AE5360D1AE6A5F54F373FA543B6A" },
+ // some more test vectors for KW and KWP
+ // from csrc.nist.gov/groups/STM/cavp/documents/mac/kwtestvectors.zip
+ { "AES/KW/NoPadding", "7575da3a93607cc2bfd8cec7aadfd9a6", 16,
+ "42136d3c384a3eeac95a066fd28fed3f", 16,
+ "031f6bd7e61e643df68594816f64caa3f56fabea2548f5fb" },
+ { "AES/KW/NoPadding", "e5d058e7f1c22c016c4e1cc9b26b9f8f", 16,
+ "7f604e9b8d39d3c91e193fe6f196c1e3da6211a7c9a33b8873b64b138d1803" +
+ "e4", 32,
+ "60b9f8ac797c56e01e9b5f84d65816a980777869f67991a0e6dc19b8cd75c9" +
+ "b54db4a38456bbd6f3" },
+ { "AES/KW/NoPadding", "67ae4270bcdd31e8326b7e7f94c80276", 16,
+ "57e748b62fbc37ba25e904ee973d01b136cf7c1d0c8c5c87", 24,
+ "96cec0e3272a21faa550a857957aa38ce3c1cf06f0dd9f5b5c5c422cef6c69" +
+ "a1" },
+ { "AES/KW/NoPadding", "d7aa53aefad65cd95b57c8eee7b0a906", 16,
+ "4a8daee6774751fc4489e837b8f7fba6896c70bb3d5e53053c92eb58046ee4" +
+ "a7002e542311253b97", 40,
+ "84ebd38cf06c674dcf186977de4a40c6dde3e7f49361a43420a887d2931b29" +
+ "c23e2db72e95e4107001da925181bb7097" },
+ { "AES/KW/NoPadding", "98311985c4661d7e811ee56070e6fecf", 16,
+ "18840c96813864ef3093b48cdde6ac5d78248b96d4a2cd1f15f0b56f98213d" +
+ "bf87e1ccad04e0d4f1954c233ea3e48fdad8f2b1156e54e19e3b5f4a66d2e9" +
+ "149032b876c51249165fe8c28e112a685b2d228a8ac308017574274af36a4e" +
+ "a3877bcc9850bafe8fc0e0a712faca0dea98396f9143bc5819fe4933a806e9" +
+ "b965133e3c695a45f0fbd6961798c400d7477287df64798b651e0d3009c13f" +
+ "7a2246c28f983509b9e5339919f2cdffcdfc550693cba9491c00334c4a62d8" +
+ "78c4d0ca57b1104bc0174968ea8e3730b9e68db49678b23cd508ebe3e12e94" +
+ "b0ad3791023a8ef95473f0b32f906738f34e94e45a4480ad768072e1853adb" +
+ "63996b9ac27a1dade70804b82290a2274c6dcc3ccd40a8b38a56a5eb03f590" +
+ "75de015e8f9096f53549f6374e02da947fb849287a447f757cc340b6bded71" +
+ "d480988b6d2fcd984fba841470519830304667fef0a577b4cf84f76aef9deb" +
+ "84dde36abfbd76673c17113dbea7a3e24bf9b57a8fd17173a1ef91497b732b" +
+ "3fa8889bed58a503a0d3e20bc27ec4dbf5d13a93cbad05495e3df15e1fe34a" +
+ "3a6d6f648ea4aa60b2f114f30944ae593675dac2db188f90a3c483fb82cec0" +
+ "f0d295544d798b62cdcb51c6c036af1a341d78babf87b92609c1d866e311a4" +
+ "6abccc8ba8c6a41420359bb061d7e752c0ed25990eef57c9f9e190572203f8" +
+ "c473edf8cfc8c26d34e37240f45ded97", 512,
+ "625aea9122b7b57b9f36446f9053acc42c6435a7f69d91b41547026f833291" +
+ "d488e477c7ccba698c143633a304f463d6af4a3e72c189234fcfc360013e65" +
+ "b07b7f7a36c529d3fdbbdbd6224bf100c14bc5354893b44790f54c739a2b1f" +
+ "5bda82d70fb600ed9b0606dbddea52e508b492b72d8779856274aaaaddc0a3" +
+ "edb6cfc788b603101bedfcc3f44baa62336bd950c2e349d5daf04f2e23ec26" +
+ "28893d214e277569c565e5e6aa8b72ffa14118a3b57f814b4deb179980b5ee" +
+ "efa4fd93f1751850466e929be537801babc2120f3ff1ffe5fea813ec7788ea" +
+ "f43f5ef657e5af48395c3ad11aaf741549090b58670695f7c95c68e00576ca" +
+ "18ef0313f2b4b757219fc8db3dc2db28721d6f912547ebfebcd96935c3100a" +
+ "a4e4df9955acae1b4e2c10df1166d46c4285ab631c6d2ce58ad3ae99c07c01" +
+ "9dcd15958694055281ccd6f803af290431f188cc4c429e84a4c30fd9c63968" +
+ "dfd0951c417efb71921c207de172a9546bdd3e2bb35b45e140892c649f88c3" +
+ "1a438f864e801a69f8010aa3d77a26601a7a89067c81b0f7e70d8e82f21f88" +
+ "c7d0bb0c8ca0db875d6c3f8c6f6d709bbb31c7da2e31f3571daa2c5ab13bfc" +
+ "16624cf35abd526e84269fb45bbd2fcd8c383d6fbb700bc4b5205b3ef8c432" +
+ "3dc0d9e0370e56a3d1e5e76aa4de082e4c2a0afd092845bd5dab52a4594318" +
+ "1461b76e3984b95f48bea80a94944241d04b5634c86274e7" },
+ { "AES/KWP/NoPadding", "6decf10a1caf8e3b80c7a4be8c9c84e8", 16,
+ "49", 1, "01a7d657fc4a5b216f261cca4d052c2b" },
+ { "AES/KWP/NoPadding", "a8e06da625a65b25cf5030826830b661", 16,
+ "43acff293120dd5d", 8, "b6f967616dd8d772e9fea295a456dba7" },
+ { "AES/KWP/NoPadding", "7865e20f3c21659ab4690b629cdf3cc4", 16,
+ "bd6843d420378dc896", 9,
+ "41eca956d4aa047eb5cf4efe659661e74db6f8c564e23500" },
+ { "AES/KWP/NoPadding", "be96dc195ec034d616486ed70e97fe83", 16,
+ "85b5437b6335ebba7635903a4493d12a77d9357a9e0dbc013456d85f1d3201",
+ 31,
+ "974769b3a7b4d5d32985f87fddf9990631e5610fbfb278387b58b1f48e05c7" +
+ "7d2fb7575c5169eb0e" },
+ { "AES/KWP/NoPadding", "0e54956a24c7d4a343f90269fb18a17f", 16,
+ "817ddabdc5d215eee233adff97e92193c6beec52a71340477f70243a794ce9" +
+ "54af51e356c9940e4ab198f0e68c543355f65ad179cb2d60dd369eaeb9ed14" +
+ "1fb18c9e4054ac7fdc83506896990a4d20833d2d6e9a34938796ee67c9d7d2" +
+ "3058544a4a35f2954103ce443a95a7e785602075ca0a73da37899e4568106b" +
+ "b2dbf1f901377d4d3380c70fa5175ebc550481ac6f15986a4407fde5c23ff3" +
+ "17e37544c0a25f87117506597db5bb79850c86247b73a5d0090417d63e4c25" +
+ "7ea0220c2c04db07a34f0ab7954e1dfa2007a1466795c4d0c2aa09ca3986c0" +
+ "28185b43a466526594afc9c891c263a7c608304bc1957c9873f544dc71e6f8" +
+ "47c48d32026ed03b2333825452ee7e12a50e1cd7d678319264c65f78001996" +
+ "d37fae7f9861fbd21cb506c2f8a3b0ee53c7debe17111b6e3f78a5c5677857" +
+ "b082c2c4943dfd1edf6337fea98a44fc25928361156ef38d865948b979cf6f" +
+ "4b46bd2119f12f0891cef7fc9d0638fd105fc05f9968d16948d1cb820751e8" +
+ "2e44cb68e99d4f072ffd1577da6c0631b5827bec7e1b9ec72d18b74cf5f233" +
+ "e85013c1668ceb5d7a1f5e0f016b0ff726a0a9d41e2cea8e14a2f56492b146" +
+ "06d3fafd8ac141335f39f90d56863735628e8f17be90e100ef0785f3cd57db" +
+ "8b9d89a6b2189dc2ea00c285d2657983f8bd7883c215477e67a55556401f1d" +
+ "8b27d4e0d541c7fb7ace370c2e428884", 512,
+ "876f3e53ba9cf4f6a521ac198bc813d0ede0f862ab6082e3e0a06ad82b4f27" +
+ "9582f7c43bb63574608446bc2a05f401a68f74086cf2776b4b3df6b3679c2e" +
+ "dfb91c024db54c6831e0752ae6f86c7596462de905ee0be908c1b9d043ecaf" +
+ "e2ad1cbddb904e18ebc9b7a107031be3a87059516a3d1257812d9c801b0b9f" +
+ "21539e70c47150c128d87c5e58fa6e4371aedde69c7b5cd16b73ac42267632" +
+ "8131f3ac48c602bb6e0741805aad9d23b33b3523b86cf0588cdf9dc6c4d5f9" +
+ "fa43d88ca17976eaf48fb37a41a598266da04144373df5631cc5126341c200" +
+ "a0c8499b29ae96e6e6e6c2bdf8d8903da62bf8ddae970569b695240e77f8ac" +
+ "5b191da5034008b6ef21936858e69bac372bbafd8794f6b03711503c187552" +
+ "8a9348681844edb199a0664d740f0f0b1f866c4248c80fe8b5700a3c4134cd" +
+ "ddb17676e0cd37d6d81831a0f4adfba071bb0935502480eccd48b28be5954e" +
+ "a6c7d873b51b8bd2b709c5b6132ed31296510915073c18f7012f0eff6a9aad" +
+ "5340a19fd5e372d35260b718d9e4807b1954c24e6a4fd48e4dbb8f395474e9" +
+ "9ab577367d2ab5ccaa18c947331047dc3986e213a878b41089aa221019dad4" +
+ "191a4feefd095f8606c2700a46d71cbb13efb6957df925ec26071c04d04d5a" +
+ "94e138e5fc5d1f059236aad76208077dcc607b1dd2086f9c04e33f955822b4" +
+ "57eecd68bd5f24836ecedbac675e6ed93d8a787cb57ad68e" },
+ };
+ }
+
+ @Test(dataProvider = "testData")
+ public void testKeyWrap(String algo, String key, int keyLen,
+ String data, int dataLen, String expected) 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");
- c.init(Cipher.WRAP_MODE, cipherKey);
SecretKey toBeWrappedKey = new SecretKeySpec(dataVal, "AES");
+ c1.init(Cipher.WRAP_MODE, cipherKey);
+ IvParameterSpec ivSpec = new IvParameterSpec(c1.getIV());
+ c2.init(Cipher.WRAP_MODE, cipherKey, ivSpec);
+ AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+ params.init(ivSpec);
+ c3.init(Cipher.WRAP_MODE, cipherKey, params);
+
// first test WRAP with known values
- byte[] wrapped = c.wrap(toBeWrappedKey);
- byte[] expectedVal = new BigInteger(expected, 16).toByteArray();
- // need to add offset since BigInteger may pad "0x00" in the beginning
- int offset = expectedVal.length - wrapped.length;
- for (int i=0; i