mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
263 lines
9.5 KiB
Java
263 lines
9.5 KiB
Java
/*
|
|
* Copyright (c) 2021, 2024, 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.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.util.Arrays;
|
|
import java.util.HexFormat;
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
|
|
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
|
|
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 semi-block 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 semi-block 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");
|
|
}
|
|
// allow setting an iv but if non-null, must equal to ICV2
|
|
if (iv != null && !Arrays.equals(iv, ICV2)) {
|
|
HexFormat hf = HexFormat.of().withUpperCase();
|
|
throw new InvalidAlgorithmParameterException("Invalid IV, got 0x" +
|
|
hf.formatHex(iv) + " instead of 0x" + hf.formatHex(ICV2));
|
|
}
|
|
embeddedCipher.init(decrypting, algorithm, key);
|
|
this.iv = ICV2;
|
|
}
|
|
|
|
/**
|
|
* 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 multipart encryption
|
|
@Override
|
|
int encrypt(byte[] pt, int ptOfs, int ptLen, byte[] ct, int ctOfs) {
|
|
throw new UnsupportedOperationException("multipart not supported");
|
|
}
|
|
|
|
// no support for multipart decryption
|
|
@Override
|
|
int decrypt(byte[] ct, int ctOfs, int ctLen, byte[] pt, int ptOfs) {
|
|
throw new UnsupportedOperationException("multipart 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 semi-block 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;
|
|
}
|
|
}
|