| /* | |
| * 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.security.MessageDigest; | |
| import javax.crypto.IllegalBlockSizeException; | |
| 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. | |
| * | |
| * @spec https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf | |
| * Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping | |
| */ | |
| 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 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 { | |
| // 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; | |
| } | |
| } |