| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
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.*;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
abstract class KeyWrapCipher extends CipherSpi { | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES_KW_NoPadding extends KeyWrapCipher { | 
 | 
        public AES_KW_NoPadding() { | 
 | 
            super(new AESKeyWrap(), null, -1);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES128_KW_NoPadding extends KeyWrapCipher { | 
 | 
        public AES128_KW_NoPadding() { | 
 | 
            super(new AESKeyWrap(), null, 16);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES192_KW_NoPadding extends KeyWrapCipher { | 
 | 
        public AES192_KW_NoPadding() { | 
 | 
            super(new AESKeyWrap(), null, 24);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES256_KW_NoPadding extends KeyWrapCipher { | 
 | 
        public AES256_KW_NoPadding() { | 
 | 
            super(new AESKeyWrap(), null, 32);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES_KW_PKCS5Padding extends KeyWrapCipher { | 
 | 
        public AES_KW_PKCS5Padding() { | 
 | 
            super(new AESKeyWrap(), new PKCS5Padding(16), -1);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES128_KW_PKCS5Padding extends KeyWrapCipher { | 
 | 
        public AES128_KW_PKCS5Padding() { | 
 | 
            super(new AESKeyWrap(), new PKCS5Padding(16), 16);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES192_KW_PKCS5Padding extends KeyWrapCipher { | 
 | 
        public AES192_KW_PKCS5Padding() { | 
 | 
            super(new AESKeyWrap(), new PKCS5Padding(16), 24);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES256_KW_PKCS5Padding extends KeyWrapCipher { | 
 | 
        public AES256_KW_PKCS5Padding() { | 
 | 
            super(new AESKeyWrap(), new PKCS5Padding(16), 32);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES_KWP_NoPadding extends KeyWrapCipher { | 
 | 
        public AES_KWP_NoPadding() { | 
 | 
            super(new AESKeyWrapPadded(), null, -1);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES128_KWP_NoPadding extends KeyWrapCipher { | 
 | 
        public AES128_KWP_NoPadding() { | 
 | 
            super(new AESKeyWrapPadded(), null, 16);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES192_KWP_NoPadding extends KeyWrapCipher { | 
 | 
        public AES192_KWP_NoPadding() { | 
 | 
            super(new AESKeyWrapPadded(), null, 24);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    public static final class AES256_KWP_NoPadding extends KeyWrapCipher { | 
 | 
        public AES256_KWP_NoPadding() { | 
 | 
            super(new AESKeyWrapPadded(), null, 32);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    // store the specified bytes, e.g. in[inOfs...(inOfs+inLen-1)] into  | 
 | 
    // 'dataBuf' starting at 'dataIdx'.  | 
 | 
    // NOTE: if 'in' is null, this method will ensure that 'dataBuf' has enough  | 
 | 
      | 
 | 
    private void store(byte[] in, int inOfs, int inLen) { | 
 | 
        // In NIST SP 800-38F, KWP input size is limited to be no longer  | 
 | 
        // than 2^32 bytes. Otherwise, the length cannot be encoded in 32 bits  | 
 | 
        // However, given the current spec requirement that recovered text  | 
 | 
        // can only be returned after successful tag verification, we are  | 
 | 
        // bound by limiting the data size to the size limit of java byte array,  | 
 | 
          | 
 | 
        int remain = Integer.MAX_VALUE - dataIdx;  | 
 | 
        if (inLen > remain) { | 
 | 
            throw new ProviderException("SunJCE provider can only take " + | 
 | 
                remain + " more bytes");  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (dataBuf == null || dataBuf.length - dataIdx < inLen) { | 
 | 
            int newSize = Math.addExact(dataIdx, inLen);  | 
 | 
            int lastBlk = (dataIdx + inLen - SEMI_BLKSIZE) % BLKSIZE;  | 
 | 
            if (lastBlk != 0 || padding != null) { | 
 | 
                newSize = Math.addExact(newSize, BLKSIZE - lastBlk);  | 
 | 
            }  | 
 | 
            byte[] temp = new byte[newSize];  | 
 | 
            if (dataBuf != null && dataIdx > 0) { | 
 | 
                System.arraycopy(dataBuf, 0, temp, 0, dataIdx);  | 
 | 
            }  | 
 | 
            dataBuf = temp;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (in != null) { | 
 | 
            System.arraycopy(in, inOfs, dataBuf, dataIdx, inLen);  | 
 | 
            dataIdx += inLen;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    // internal cipher object which does the real work.  | 
 | 
      | 
 | 
    private final FeedbackCipher cipher;  | 
 | 
 | 
 | 
      | 
 | 
    private final Padding padding;  | 
 | 
 | 
 | 
    // encrypt/wrap or decrypt/unwrap?  | 
 | 
    private int opmode = -1;   | 
 | 
 | 
 | 
    /*  | 
 | 
     * needed to support oids which associates a fixed key size  | 
 | 
     * to the cipher object.  | 
 | 
     */  | 
 | 
    private final int fixedKeySize;   | 
 | 
 | 
 | 
    // internal data buffer for encrypt, decrypt calls  | 
 | 
      | 
 | 
    private byte[] dataBuf;  | 
 | 
    private int dataIdx;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public KeyWrapCipher(FeedbackCipher cipher, Padding padding, int keySize) { | 
 | 
        this.cipher = cipher;  | 
 | 
        this.padding = padding;  | 
 | 
        this.fixedKeySize = keySize;  | 
 | 
        this.dataBuf = null;  | 
 | 
        this.dataIdx = 0;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected void engineSetMode(String mode) throws NoSuchAlgorithmException { | 
 | 
        if (mode != null && !cipher.getFeedback().equalsIgnoreCase(mode)) { | 
 | 
            throw new NoSuchAlgorithmException(mode + " cannot be used");  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected void engineSetPadding(String padding)  | 
 | 
            throws NoSuchPaddingException { | 
 | 
        if ((this.padding == null && !"NoPadding".equalsIgnoreCase(padding)) ||  | 
 | 
                 this.padding instanceof PKCS5Padding &&  | 
 | 
                 !"PKCS5Padding".equalsIgnoreCase(padding)) { | 
 | 
            throw new NoSuchPaddingException("Unsupported padding " + padding); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected int engineGetBlockSize() { | 
 | 
        return cipher.getBlockSize();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    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);  | 
 | 
              | 
 | 
            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;  | 
 | 
                }  | 
 | 
            }  | 
 | 
              | 
 | 
            result = Math.addExact(result, SEMI_BLKSIZE + padLen);  | 
 | 
        } else { | 
 | 
            result = inLen - SEMI_BLKSIZE;  | 
 | 
            if (dataIdx > 0) { | 
 | 
                result = Math.addExact(result, dataIdx);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return result;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected byte[] engineGetIV() { | 
 | 
        byte[] iv = cipher.getIV();  | 
 | 
        return (iv == null? null : iv.clone());  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    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);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected void engineInit(int opmode, Key key, SecureRandom random)  | 
 | 
        throws InvalidKeyException { | 
 | 
        try { | 
 | 
            implInit(opmode, key, (byte[])null, random);  | 
 | 
        } catch (InvalidAlgorithmParameterException iae) { | 
 | 
              | 
 | 
            throw new AssertionError(iae);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    private void implUpdate(byte[] in, int inOfs, int inLen) { | 
 | 
        if (inLen <= 0) return;  | 
 | 
 | 
 | 
        if (opmode == Cipher.ENCRYPT_MODE && dataIdx == 0) { | 
 | 
              | 
 | 
            dataIdx = SEMI_BLKSIZE;  | 
 | 
        }  | 
 | 
        store(in, inOfs, inLen);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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) { | 
 | 
                try { | 
 | 
                    return Arrays.copyOf(out, outLen);  | 
 | 
                } finally { | 
 | 
                    Arrays.fill(out, (byte)0);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                return out;  | 
 | 
            }  | 
 | 
        } catch (ShortBufferException sbe) { | 
 | 
              | 
 | 
            throw new AssertionError(sbe);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    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  | 
 | 
              | 
 | 
            if (outOfs == 0 && opmode == Cipher.ENCRYPT_MODE) { | 
 | 
                return implDoFinal(in, inOfs, inLen, out);  | 
 | 
            } else { | 
 | 
                // use 'dataBuf' as output buffer and then copy into 'out'  | 
 | 
                  | 
 | 
                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 { | 
 | 
            if (dataBuf != null) { | 
 | 
                Arrays.fill(dataBuf, (byte)0);  | 
 | 
            }  | 
 | 
            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  | 
 | 
      | 
 | 
    private int implDoFinal(byte[] in, int inOfs, int inLen, byte[] out)  | 
 | 
        throws IllegalBlockSizeException, BadPaddingException,  | 
 | 
            ShortBufferException { | 
 | 
 | 
 | 
        int len = (out == dataBuf? dataIdx : 0);  | 
 | 
 | 
 | 
          | 
 | 
        if (out != dataBuf && dataIdx > 0) { | 
 | 
            System.arraycopy(dataBuf, 0, out, 0, dataIdx);  | 
 | 
            len = dataIdx;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (opmode == Cipher.ENCRYPT_MODE && len == 0) { | 
 | 
            len = SEMI_BLKSIZE;   | 
 | 
        }  | 
 | 
 | 
 | 
        if (inLen > 0) { | 
 | 
            System.arraycopy(in, inOfs, out, len, inLen);  | 
 | 
            len += inLen;  | 
 | 
        }  | 
 | 
 | 
 | 
        try { | 
 | 
            return (opmode == Cipher.ENCRYPT_MODE ?  | 
 | 
                    helperEncrypt(out, len) : helperDecrypt(out, len));  | 
 | 
        } finally { | 
 | 
            if (dataBuf != null && dataBuf != out) { | 
 | 
                Arrays.fill(dataBuf, (byte)0);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    // helper routine for in-place encryption.  | 
 | 
    // 'inBuf' = semiblock | plain text | extra bytes if padding is used  | 
 | 
      | 
 | 
    private int helperEncrypt(byte[] inBuf, int inLen)  | 
 | 
            throws IllegalBlockSizeException, ShortBufferException { | 
 | 
 | 
 | 
          | 
 | 
        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) { | 
 | 
                  | 
 | 
                throw new AssertionError(sbe);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return cipher.encryptFinal(inBuf, 0, inLen, null, 0);  | 
 | 
    }  | 
 | 
 | 
 | 
    // helper routine for in-place decryption.  | 
 | 
    // 'inBuf' = cipher text  | 
 | 
      | 
 | 
    private int helperDecrypt(byte[] inBuf, int inLen)  | 
 | 
            throws IllegalBlockSizeException, BadPaddingException,  | 
 | 
            ShortBufferException { | 
 | 
 | 
 | 
        int outLen = cipher.decryptFinal(inBuf, 0, inLen, null, 0);  | 
 | 
          | 
 | 
        if (padding != null) { | 
 | 
            int padIdx = padding.unpad(inBuf, 0, outLen);  | 
 | 
            if (padIdx <= 0) { | 
 | 
                throw new BadPaddingException("Bad Padding: " + padIdx); | 
 | 
            }  | 
 | 
            outLen = padIdx;  | 
 | 
        }  | 
 | 
        return outLen;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected AlgorithmParameters engineGetParameters() { | 
 | 
        AlgorithmParameters params = null;  | 
 | 
 | 
 | 
        byte[] iv = cipher.getIV();  | 
 | 
        if (iv == null) { | 
 | 
            iv = (cipher instanceof AESKeyWrap?  | 
 | 
                    AESKeyWrap.ICV1 : AESKeyWrapPadded.ICV2);  | 
 | 
        }  | 
 | 
        try { | 
 | 
            params = AlgorithmParameters.getInstance("AES"); | 
 | 
            params.init(new IvParameterSpec(iv));  | 
 | 
        } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { | 
 | 
              | 
 | 
            throw new AssertionError();  | 
 | 
        }  | 
 | 
        return params;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected int engineGetKeySize(Key key) throws InvalidKeyException { | 
 | 
        byte[] encoded = key.getEncoded();  | 
 | 
        if (encoded == null)  { | 
 | 
            throw new InvalidKeyException("Cannot decide key 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);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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");  | 
 | 
        }  | 
 | 
          | 
 | 
        byte[] out = new byte[engineGetOutputSize(encoded.length)];  | 
 | 
 | 
 | 
          | 
 | 
        int len = SEMI_BLKSIZE;  | 
 | 
        System.arraycopy(encoded, 0, out, len, encoded.length);  | 
 | 
        len += encoded.length;  | 
 | 
 | 
 | 
          | 
 | 
        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) { | 
 | 
              | 
 | 
            throw new AssertionError();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @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) { | 
 | 
              | 
 | 
            throw new AssertionError();  | 
 | 
        } catch (IllegalBlockSizeException | BadPaddingException e) { | 
 | 
            throw new InvalidKeyException(e);  | 
 | 
        } finally { | 
 | 
            Arrays.fill(buf, (byte) 0);  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |