Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/crypto/provider/AESWrapCipher.java
38922 views
/*1* Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.crypto.provider;2627import java.util.Arrays;28import java.security.*;29import java.security.spec.*;30import javax.crypto.*;31import javax.crypto.spec.*;3233/**34* This class implements the AES KeyWrap algorithm as defined35* in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>36* "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".37* Note: only <code>ECB</code> mode and <code>NoPadding</code> padding38* can be used for this algorithm.39*40* @author Valerie Peng41*42*43* @see AESCipher44*/45abstract class AESWrapCipher extends CipherSpi {46public static final class General extends AESWrapCipher {47public General() {48super(-1);49}50}51public static final class AES128 extends AESWrapCipher {52public AES128() {53super(16);54}55}56public static final class AES192 extends AESWrapCipher {57public AES192() {58super(24);59}60}61public static final class AES256 extends AESWrapCipher {62public AES256() {63super(32);64}65}66private static final byte[] IV = {67(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,68(byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA669};7071private static final int blksize = AESConstants.AES_BLOCK_SIZE;7273/*74* internal cipher object which does the real work.75*/76private AESCrypt cipher;7778/*79* are we encrypting or decrypting?80*/81private boolean decrypting = false;8283/*84* needed to support AES oids which associates a fixed key size85* to the cipher object.86*/87private final int fixedKeySize; // in bytes, -1 if no restriction8889/**90* Creates an instance of AES KeyWrap cipher with default91* mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".92*/93public AESWrapCipher(int keySize) {94cipher = new AESCrypt();95fixedKeySize = keySize;9697}9899/**100* Sets the mode of this cipher. Only "ECB" mode is accepted for this101* cipher.102*103* @param mode the cipher mode104*105* @exception NoSuchAlgorithmException if the requested cipher mode106* is not "ECB".107*/108protected void engineSetMode(String mode)109throws NoSuchAlgorithmException {110if (!mode.equalsIgnoreCase("ECB")) {111throw new NoSuchAlgorithmException(mode + " cannot be used");112}113}114115/**116* Sets the padding mechanism of this cipher. Only "NoPadding" schmem117* is accepted for this cipher.118*119* @param padding the padding mechanism120*121* @exception NoSuchPaddingException if the requested padding mechanism122* is not "NoPadding".123*/124protected void engineSetPadding(String padding)125throws NoSuchPaddingException {126if (!padding.equalsIgnoreCase("NoPadding")) {127throw new NoSuchPaddingException(padding + " cannot be used");128}129}130131/**132* Returns the block size (in bytes). i.e. 16 bytes.133*134* @return the block size (in bytes), i.e. 16 bytes.135*/136protected int engineGetBlockSize() {137return blksize;138}139140/**141* Returns the length in bytes that an output buffer would need to be142* given the input length <code>inputLen</code> (in bytes).143*144* <p>The actual output length of the next <code>update</code> or145* <code>doFinal</code> call may be smaller than the length returned146* by this method.147*148* @param inputLen the input length (in bytes)149*150* @return the required output buffer size (in bytes)151*/152protected int engineGetOutputSize(int inputLen) {153// can only return an upper-limit if not initialized yet.154int result = 0;155if (decrypting) {156result = inputLen - 8;157} else {158result = Math.addExact(inputLen, 8);159}160return (result < 0? 0:result);161}162163/**164* Returns the initialization vector (IV) which is null for this cipher.165*166* @return null for this cipher.167*/168protected byte[] engineGetIV() {169return null;170}171172/**173* Initializes this cipher with a key and a source of randomness.174*175* <p>The cipher only supports the following two operation modes:<b>176* Cipher.WRAP_MODE, and <b>177* Cipher.UNWRAP_MODE.178* <p>For modes other than the above two, UnsupportedOperationException179* will be thrown.180*181* @param opmode the operation mode of this cipher. Only182* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.183* @param key the secret key.184* @param random the source of randomness.185*186* @exception InvalidKeyException if the given key is inappropriate for187* initializing this cipher.188*/189protected void engineInit(int opmode, Key key, SecureRandom random)190throws InvalidKeyException {191if (opmode == Cipher.WRAP_MODE) {192decrypting = false;193} else if (opmode == Cipher.UNWRAP_MODE) {194decrypting = true;195} else {196throw new UnsupportedOperationException("This cipher can " +197"only be used for key wrapping and unwrapping");198}199AESCipher.checkKeySize(key, fixedKeySize);200cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());201}202203/**204* Initializes this cipher with a key, a set of algorithm parameters,205* and a source of randomness.206*207* <p>The cipher only supports the following two operation modes:<b>208* Cipher.WRAP_MODE, and <b>209* Cipher.UNWRAP_MODE.210* <p>For modes other than the above two, UnsupportedOperationException211* will be thrown.212*213* @param opmode the operation mode of this cipher. Only214* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.215* @param key the secret key.216* @param params the algorithm parameters; must be null for this cipher.217* @param random the source of randomness.218*219* @exception InvalidKeyException if the given key is inappropriate for220* initializing this cipher221* @exception InvalidAlgorithmParameterException if the given algorithm222* parameters is not null.223*/224protected void engineInit(int opmode, Key key,225AlgorithmParameterSpec params,226SecureRandom random)227throws InvalidKeyException, InvalidAlgorithmParameterException {228if (params != null) {229throw new InvalidAlgorithmParameterException("This cipher " +230"does not accept any parameters");231}232engineInit(opmode, key, random);233}234235/**236* Initializes this cipher with a key, a set of algorithm parameters,237* and a source of randomness.238*239* <p>The cipher only supports the following two operation modes:<b>240* Cipher.WRAP_MODE, and <b>241* Cipher.UNWRAP_MODE.242* <p>For modes other than the above two, UnsupportedOperationException243* will be thrown.244*245* @param opmode the operation mode of this cipher. Only246* <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.247* @param key the secret key.248* @param params the algorithm parameters; must be null for this cipher.249* @param random the source of randomness.250*251* @exception InvalidKeyException if the given key is inappropriate.252* @exception InvalidAlgorithmParameterException if the given algorithm253* parameters is not null.254*/255protected void engineInit(int opmode, Key key,256AlgorithmParameters params,257SecureRandom random)258throws InvalidKeyException, InvalidAlgorithmParameterException {259if (params != null) {260throw new InvalidAlgorithmParameterException("This cipher " +261"does not accept any parameters");262}263engineInit(opmode, key, random);264}265266/**267* This operation is not supported by this cipher.268* Since it's impossible to initialize this cipher given the269* current Cipher.engineInit(...) implementation,270* IllegalStateException will always be thrown upon invocation.271*272* @param in the input buffer.273* @param inOffset the offset in <code>in</code> where the input274* starts.275* @param inLen the input length.276*277* @return n/a.278*279* @exception IllegalStateException upon invocation of this method.280*/281protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {282throw new IllegalStateException("Cipher has not been initialized");283}284285/**286* This operation is not supported by this cipher.287* Since it's impossible to initialize this cipher given the288* current Cipher.engineInit(...) implementation,289* IllegalStateException will always be thrown upon invocation.290*291* @param in the input buffer.292* @param inOffset the offset in <code>in</code> where the input293* starts.294* @param inLen the input length.295* @param out the buffer for the result.296* @param outOffset the offset in <code>out</code> where the result297* is stored.298*299* @return n/a.300*301* @exception IllegalStateException upon invocation of this method.302*/303protected int engineUpdate(byte[] in, int inOffset, int inLen,304byte[] out, int outOffset)305throws ShortBufferException {306throw new IllegalStateException("Cipher has not been initialized");307}308309/**310* This operation is not supported by this cipher.311* Since it's impossible to initialize this cipher given the312* current Cipher.engineInit(...) implementation,313* IllegalStateException will always be thrown upon invocation.314*315* @param in the input buffer316* @param inOffset the offset in <code>in</code> where the input317* starts318* @param inLen the input length.319*320* @return n/a.321*322* @exception IllegalStateException upon invocation of this method.323*/324protected byte[] engineDoFinal(byte[] input, int inputOffset,325int inputLen)326throws IllegalBlockSizeException, BadPaddingException {327throw new IllegalStateException("Cipher has not been initialized");328}329330/**331* This operation is not supported by this cipher.332* Since it's impossible to initialize this cipher given the333* current Cipher.engineInit(...) implementation,334* IllegalStateException will always be thrown upon invocation.335*336* @param in the input buffer.337* @param inOffset the offset in <code>in</code> where the input338* starts.339* @param inLen the input length.340* @param out the buffer for the result.341* @param outOffset the ofset in <code>out</code> where the result342* is stored.343*344* @return n/a.345*346* @exception IllegalStateException upon invocation of this method.347*/348protected int engineDoFinal(byte[] in, int inOffset, int inLen,349byte[] out, int outOffset)350throws IllegalBlockSizeException, ShortBufferException,351BadPaddingException {352throw new IllegalStateException("Cipher has not been initialized");353}354355/**356* Returns the parameters used with this cipher which is always null357* for this cipher.358*359* @return null since this cipher does not use any parameters.360*/361protected AlgorithmParameters engineGetParameters() {362return null;363}364365/**366* Returns the key size of the given key object in number of bits.367*368* @param key the key object.369*370* @return the "effective" key size of the given key object.371*372* @exception InvalidKeyException if <code>key</code> is invalid.373*/374protected int engineGetKeySize(Key key) throws InvalidKeyException {375byte[] encoded = key.getEncoded();376if (!AESCrypt.isKeySizeValid(encoded.length)) {377throw new InvalidKeyException("Invalid key length: " +378encoded.length + " bytes");379}380return Math.multiplyExact(encoded.length, 8);381}382383/**384* Wrap a key.385*386* @param key the key to be wrapped.387*388* @return the wrapped key.389*390* @exception IllegalBlockSizeException if this cipher is a block391* cipher, no padding has been requested, and the length of the392* encoding of the key to be wrapped is not a393* multiple of the block size.394*395* @exception InvalidKeyException if it is impossible or unsafe to396* wrap the key with this cipher (e.g., a hardware protected key is397* being passed to a software only cipher).398*/399protected byte[] engineWrap(Key key)400throws IllegalBlockSizeException, InvalidKeyException {401byte[] keyVal = key.getEncoded();402if ((keyVal == null) || (keyVal.length == 0)) {403throw new InvalidKeyException("Cannot get an encoding of " +404"the key to be wrapped");405}406byte[] out = new byte[Math.addExact(keyVal.length, 8)];407408if (keyVal.length == 8) {409System.arraycopy(IV, 0, out, 0, IV.length);410System.arraycopy(keyVal, 0, out, IV.length, 8);411cipher.encryptBlock(out, 0, out, 0);412} else {413if (keyVal.length % 8 != 0) {414throw new IllegalBlockSizeException("length of the " +415"to be wrapped key should be multiples of 8 bytes");416}417System.arraycopy(IV, 0, out, 0, IV.length);418System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);419int N = keyVal.length/8;420byte[] buffer = new byte[blksize];421for (int j = 0; j < 6; j++) {422for (int i = 1; i <= N; i++) {423int T = i + j*N;424System.arraycopy(out, 0, buffer, 0, IV.length);425System.arraycopy(out, i*8, buffer, IV.length, 8);426cipher.encryptBlock(buffer, 0, buffer, 0);427for (int k = 1; T != 0; k++) {428byte v = (byte) T;429buffer[IV.length - k] ^= v;430T >>>= 8;431}432System.arraycopy(buffer, 0, out, 0, IV.length);433System.arraycopy(buffer, 8, out, 8*i, 8);434}435}436}437return out;438}439440/**441* Unwrap a previously wrapped key.442*443* @param wrappedKey the key to be unwrapped.444*445* @param wrappedKeyAlgorithm the algorithm the wrapped key is for.446*447* @param wrappedKeyType the type of the wrapped key.448* This is one of <code>Cipher.SECRET_KEY</code>,449* <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.450*451* @return the unwrapped key.452*453* @exception NoSuchAlgorithmException if no installed providers454* can create keys of type <code>wrappedKeyType</code> for the455* <code>wrappedKeyAlgorithm</code>.456*457* @exception InvalidKeyException if <code>wrappedKey</code> does not458* represent a wrapped key of type <code>wrappedKeyType</code> for459* the <code>wrappedKeyAlgorithm</code>.460*/461protected Key engineUnwrap(byte[] wrappedKey,462String wrappedKeyAlgorithm,463int wrappedKeyType)464throws InvalidKeyException, NoSuchAlgorithmException {465int wrappedKeyLen = wrappedKey.length;466// ensure the wrappedKey length is multiples of 8 bytes and non-zero467if (wrappedKeyLen == 0) {468throw new InvalidKeyException("The wrapped key is empty");469}470if (wrappedKeyLen % 8 != 0) {471throw new InvalidKeyException472("The wrapped key has invalid key length");473}474byte[] out = new byte[wrappedKeyLen - 8];475byte[] buffer = new byte[blksize];476if (wrappedKeyLen == 16) {477cipher.decryptBlock(wrappedKey, 0, buffer, 0);478for (int i = 0; i < IV.length; i++) {479if (IV[i] != buffer[i]) {480throw new InvalidKeyException("Integrity check failed");481}482}483System.arraycopy(buffer, IV.length, out, 0, out.length);484} else {485System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);486System.arraycopy(wrappedKey, IV.length, out, 0, out.length);487int N = out.length/8;488for (int j = 5; j >= 0; j--) {489for (int i = N; i > 0; i--) {490int T = i + j*N;491System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);492for (int k = 1; T != 0; k++) {493byte v = (byte) T;494buffer[IV.length - k] ^= v;495T >>>= 8;496}497cipher.decryptBlock(buffer, 0, buffer, 0);498System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);499}500}501for (int i = 0; i < IV.length; i++) {502if (IV[i] != buffer[i]) {503throw new InvalidKeyException("Integrity check failed");504}505}506}507return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,508wrappedKeyType);509}510}511512513