/* | 
|
 * 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 | 
|
    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;  | 
|
}  | 
|
}  |