1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import java.io.IOException;
20import java.lang.reflect.Constructor;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.security.AlgorithmParameters;
24import java.security.InvalidAlgorithmParameterException;
25import java.security.InvalidKeyException;
26import java.security.InvalidParameterException;
27import java.security.Key;
28import java.security.KeyFactory;
29import java.security.NoSuchAlgorithmException;
30import java.security.SecureRandom;
31import java.security.spec.AlgorithmParameterSpec;
32import java.security.spec.InvalidKeySpecException;
33import java.security.spec.InvalidParameterSpecException;
34import java.security.spec.PKCS8EncodedKeySpec;
35import java.security.spec.X509EncodedKeySpec;
36import java.util.Arrays;
37import java.util.Locale;
38import javax.crypto.BadPaddingException;
39import javax.crypto.Cipher;
40import javax.crypto.CipherSpi;
41import javax.crypto.IllegalBlockSizeException;
42import javax.crypto.NoSuchPaddingException;
43import javax.crypto.SecretKey;
44import javax.crypto.ShortBufferException;
45import javax.crypto.spec.IvParameterSpec;
46import javax.crypto.spec.SecretKeySpec;
47import org.conscrypt.util.ArrayUtils;
48import org.conscrypt.util.EmptyArray;
49import org.conscrypt.NativeConstants;
50import org.conscrypt.NativeRef.EVP_AEAD_CTX;
51import org.conscrypt.NativeRef.EVP_CIPHER_CTX;
52
53public abstract class OpenSSLCipher extends CipherSpi {
54
55    /**
56     * Modes that a block cipher may support.
57     */
58    protected static enum Mode {
59        CBC,
60        CTR,
61        ECB,
62        GCM,
63    }
64
65    /**
66     * Paddings that a block cipher may support.
67     */
68    protected static enum Padding {
69        NOPADDING,
70        PKCS5PADDING,
71        ISO10126PADDING,
72    }
73
74    /**
75     * The current cipher mode.
76     */
77    protected Mode mode = Mode.ECB;
78
79    /**
80     * The current cipher padding.
81     */
82    private Padding padding = Padding.PKCS5PADDING;
83
84    /**
85     * May be used when reseting the cipher instance after calling
86     * {@code doFinal}.
87     */
88    protected byte[] encodedKey;
89
90    /**
91     * The Initial Vector (IV) used for the current cipher.
92     */
93    protected byte[] iv;
94
95    /**
96     * Current cipher mode: encrypting or decrypting.
97     */
98    private boolean encrypting;
99
100    /**
101     * The block size of the current cipher.
102     */
103    private int blockSize;
104
105    protected OpenSSLCipher() {
106    }
107
108    protected OpenSSLCipher(Mode mode, Padding padding) {
109        this.mode = mode;
110        this.padding = padding;
111        blockSize = getCipherBlockSize();
112    }
113
114    /**
115     * API-specific implementation of initializing the cipher. The
116     * {@link #isEncrypting()} function will tell whether it should be
117     * initialized for encryption or decryption. The {@code encodedKey} will be
118     * the bytes of a supported key size.
119     */
120    protected abstract void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
121            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException;
122
123    /**
124     * API-specific implementation of updating the cipher. The
125     * {@code maximumLen} will be the maximum length of the output as returned
126     * by {@link #getOutputSizeForUpdate(int)}. The return value must be the
127     * number of bytes processed and placed into {@code output}. On error, an
128     * exception must be thrown.
129     */
130    protected abstract int updateInternal(byte[] input, int inputOffset, int inputLen,
131            byte[] output, int outputOffset, int maximumLen) throws ShortBufferException;
132
133    /**
134     * API-specific implementation of the final block. The {@code maximumLen}
135     * will be the maximum length of the possible output as returned by
136     * {@link #getOutputSizeForFinal(int)}. The return value must be the number
137     * of bytes processed and placed into {@code output}. On error, an exception
138     * must be thrown.
139     */
140    protected abstract int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
141            throws IllegalBlockSizeException, BadPaddingException, ShortBufferException;
142
143    /**
144     * Returns the standard name for the particular algorithm.
145     */
146    protected abstract String getBaseCipherName();
147
148    /**
149     * Checks whether the cipher supports this particular {@code keySize} (in
150     * bytes) and throws {@code InvalidKeyException} if it doesn't.
151     */
152    protected abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException;
153
154    /**
155     * Checks whether the cipher supports this particular cipher {@code mode}
156     * and throws {@code NoSuchAlgorithmException} if it doesn't.
157     */
158    protected abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException;
159
160    /**
161     * Checks whether the cipher supports this particular cipher {@code padding}
162     * and throws {@code NoSuchPaddingException} if it doesn't.
163     */
164    protected abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException;
165
166    protected abstract int getCipherBlockSize();
167
168    protected boolean supportsVariableSizeKey() {
169        return false;
170    }
171
172    protected boolean supportsVariableSizeIv() {
173        return false;
174    }
175
176    @Override
177    protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException {
178        final Mode mode;
179        try {
180            mode = Mode.valueOf(modeStr.toUpperCase(Locale.US));
181        } catch (IllegalArgumentException e) {
182            NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: " + modeStr);
183            newE.initCause(e);
184            throw newE;
185        }
186        checkSupportedMode(mode);
187        this.mode = mode;
188    }
189
190    @Override
191    protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException {
192        final String paddingStrUpper = paddingStr.toUpperCase(Locale.US);
193        final Padding padding;
194        try {
195            padding = Padding.valueOf(paddingStrUpper);
196        } catch (IllegalArgumentException e) {
197            NoSuchPaddingException newE = new NoSuchPaddingException("No such padding: "
198                    + paddingStr);
199            newE.initCause(e);
200            throw newE;
201        }
202        checkSupportedPadding(padding);
203        this.padding = padding;
204    }
205
206    /**
207     * Returns the padding type for which this cipher is initialized.
208     */
209    protected Padding getPadding() {
210        return padding;
211    }
212
213    @Override
214    protected int engineGetBlockSize() {
215        return blockSize;
216    }
217
218    /**
219     * The size of output if {@code doFinal()} is called with this
220     * {@code inputLen}. If padding is enabled and the size of the input puts it
221     * right at the block size, it will add another block for the padding.
222     */
223    protected abstract int getOutputSizeForFinal(int inputLen);
224
225    /**
226     * The size of output if {@code update()} is called with this
227     * {@code inputLen}. If padding is enabled and the size of the input puts it
228     * right at the block size, it will add another block for the padding.
229     */
230    protected abstract int getOutputSizeForUpdate(int inputLen);
231
232    @Override
233    protected int engineGetOutputSize(int inputLen) {
234        return getOutputSizeForFinal(inputLen);
235    }
236
237    @Override
238    protected byte[] engineGetIV() {
239        return iv;
240    }
241
242    @Override
243    protected AlgorithmParameters engineGetParameters() {
244        if (iv != null && iv.length > 0) {
245            try {
246                AlgorithmParameters params = AlgorithmParameters.getInstance(getBaseCipherName());
247                params.init(iv);
248                return params;
249            } catch (NoSuchAlgorithmException e) {
250                return null;
251            } catch (IOException e) {
252                return null;
253            }
254        }
255        return null;
256    }
257
258    @Override
259    protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
260        checkAndSetEncodedKey(opmode, key);
261        try {
262            engineInitInternal(this.encodedKey, null, random);
263        } catch (InvalidAlgorithmParameterException e) {
264            // This can't actually happen since we pass in null.
265            throw new RuntimeException(e);
266        }
267    }
268
269    @Override
270    protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
271            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
272        checkAndSetEncodedKey(opmode, key);
273        engineInitInternal(this.encodedKey, params, random);
274    }
275
276    @Override
277    protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
278            throws InvalidKeyException, InvalidAlgorithmParameterException {
279        AlgorithmParameterSpec spec;
280        if (params != null) {
281            try {
282                spec = params.getParameterSpec(IvParameterSpec.class);
283            } catch (InvalidParameterSpecException e) {
284                throw new InvalidAlgorithmParameterException(
285                        "Params must be convertible to IvParameterSpec", e);
286            }
287        } else {
288            spec = null;
289        }
290
291        engineInit(opmode, key, spec, random);
292    }
293
294    @Override
295    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
296        final int maximumLen = getOutputSizeForUpdate(inputLen);
297
298        /* See how large our output buffer would need to be. */
299        final byte[] output;
300        if (maximumLen > 0) {
301            output = new byte[maximumLen];
302        } else {
303            output = EmptyArray.BYTE;
304        }
305
306        final int bytesWritten;
307        try {
308            bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen);
309        } catch (ShortBufferException e) {
310            /* This shouldn't happen. */
311            throw new RuntimeException("calculated buffer size was wrong: " + maximumLen);
312        }
313
314        if (output.length == bytesWritten) {
315            return output;
316        } else if (bytesWritten == 0) {
317            return EmptyArray.BYTE;
318        } else {
319            return Arrays.copyOfRange(output, 0, bytesWritten);
320        }
321    }
322
323    @Override
324    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
325            int outputOffset) throws ShortBufferException {
326        final int maximumLen = getOutputSizeForUpdate(inputLen);
327        return updateInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen);
328    }
329
330    @Override
331    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
332            throws IllegalBlockSizeException, BadPaddingException {
333        final int maximumLen = getOutputSizeForFinal(inputLen);
334        /* Assume that we'll output exactly on a byte boundary. */
335        final byte[] output = new byte[maximumLen];
336
337        int bytesWritten;
338        if (inputLen > 0) {
339            try {
340                bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen);
341            } catch (ShortBufferException e) {
342                /* This should not happen since we sized our own buffer. */
343                throw new RuntimeException("our calculated buffer was too small", e);
344            }
345        } else {
346            bytesWritten = 0;
347        }
348
349        try {
350            bytesWritten += doFinalInternal(output, bytesWritten, maximumLen - bytesWritten);
351        } catch (ShortBufferException e) {
352            /* This should not happen since we sized our own buffer. */
353            throw new RuntimeException("our calculated buffer was too small", e);
354        }
355
356        if (bytesWritten == output.length) {
357            return output;
358        } else if (bytesWritten == 0) {
359            return EmptyArray.BYTE;
360        } else {
361            return Arrays.copyOfRange(output, 0, bytesWritten);
362        }
363    }
364
365    @Override
366    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
367            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
368            BadPaddingException {
369        if (output == null) {
370            throw new NullPointerException("output == null");
371        }
372
373        int maximumLen = getOutputSizeForFinal(inputLen);
374
375        final int bytesWritten;
376        if (inputLen > 0) {
377            bytesWritten = updateInternal(input, inputOffset, inputLen, output, outputOffset,
378                    maximumLen);
379            outputOffset += bytesWritten;
380            maximumLen -= bytesWritten;
381        } else {
382            bytesWritten = 0;
383        }
384
385        return bytesWritten + doFinalInternal(output, outputOffset, maximumLen);
386    }
387
388    @Override
389    protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
390        try {
391            byte[] encoded = key.getEncoded();
392            return engineDoFinal(encoded, 0, encoded.length);
393        } catch (BadPaddingException e) {
394            IllegalBlockSizeException newE = new IllegalBlockSizeException();
395            newE.initCause(e);
396            throw newE;
397        }
398    }
399
400    @Override
401    protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
402            throws InvalidKeyException, NoSuchAlgorithmException {
403        try {
404            byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
405            if (wrappedKeyType == Cipher.PUBLIC_KEY) {
406                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
407                return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
408            } else if (wrappedKeyType == Cipher.PRIVATE_KEY) {
409                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
410                return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
411            } else if (wrappedKeyType == Cipher.SECRET_KEY) {
412                return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
413            } else {
414                throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType);
415            }
416        } catch (IllegalBlockSizeException e) {
417            throw new InvalidKeyException(e);
418        } catch (BadPaddingException e) {
419            throw new InvalidKeyException(e);
420        } catch (InvalidKeySpecException e) {
421            throw new InvalidKeyException(e);
422        }
423    }
424
425    private byte[] checkAndSetEncodedKey(int opmode, Key key) throws InvalidKeyException {
426        if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
427            encrypting = true;
428        } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
429            encrypting = false;
430        } else {
431            throw new InvalidParameterException("Unsupported opmode " + opmode);
432        }
433
434        if (!(key instanceof SecretKey)) {
435            throw new InvalidKeyException("Only SecretKey is supported");
436        }
437
438        final byte[] encodedKey = key.getEncoded();
439        if (encodedKey == null) {
440            throw new InvalidKeyException("key.getEncoded() == null");
441        }
442        checkSupportedKeySize(encodedKey.length);
443        this.encodedKey = encodedKey;
444        return encodedKey;
445    }
446
447    protected boolean isEncrypting() {
448        return encrypting;
449    }
450
451    public static abstract class EVP_CIPHER extends OpenSSLCipher {
452        /**
453         * Native pointer for the OpenSSL EVP_CIPHER context.
454         */
455        private final EVP_CIPHER_CTX cipherCtx = new EVP_CIPHER_CTX(
456                NativeCrypto.EVP_CIPHER_CTX_new());
457
458        /**
459         * Whether the cipher has processed any data yet. EVP_CIPHER doesn't
460         * like calling "doFinal()" in decryption mode without processing any
461         * updates.
462         */
463        protected boolean calledUpdate;
464
465        /**
466         * The block size of the current mode.
467         */
468        private int modeBlockSize;
469
470        public EVP_CIPHER(Mode mode, Padding padding) {
471            super(mode, padding);
472        }
473
474        @Override
475        protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
476                SecureRandom random) throws InvalidKeyException,
477                InvalidAlgorithmParameterException {
478            byte[] iv;
479            if (params instanceof IvParameterSpec) {
480                IvParameterSpec ivParams = (IvParameterSpec) params;
481                iv = ivParams.getIV();
482            } else {
483                iv = null;
484            }
485
486            final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(
487                    encodedKey.length, mode));
488            if (cipherType == 0) {
489                throw new InvalidAlgorithmParameterException("Cannot find name for key length = "
490                        + (encodedKey.length * 8) + " and mode = " + mode);
491            }
492
493            final boolean encrypting = isEncrypting();
494
495            final int expectedIvLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
496            if (iv == null && expectedIvLength != 0) {
497                if (!encrypting) {
498                    throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
499                            + " mode");
500                }
501
502                iv = new byte[expectedIvLength];
503                if (random == null) {
504                    random = new SecureRandom();
505                }
506                random.nextBytes(iv);
507            } else if (expectedIvLength == 0 && iv != null) {
508                throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
509            } else if (iv != null && iv.length != expectedIvLength) {
510                throw new InvalidAlgorithmParameterException("expected IV length of "
511                        + expectedIvLength + " but was " + iv.length);
512            }
513
514            this.iv = iv;
515
516            if (supportsVariableSizeKey()) {
517                NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, null, null, encrypting);
518                NativeCrypto.EVP_CIPHER_CTX_set_key_length(cipherCtx, encodedKey.length);
519                NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting());
520            } else {
521                NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, encodedKey, iv, encrypting);
522            }
523
524            // OpenSSL only supports PKCS5 Padding.
525            NativeCrypto
526                    .EVP_CIPHER_CTX_set_padding(cipherCtx, getPadding() == Padding.PKCS5PADDING);
527            modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx);
528            calledUpdate = false;
529        }
530
531        @Override
532        protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
533                int outputOffset, int maximumLen) throws ShortBufferException {
534            final int intialOutputOffset = outputOffset;
535
536            final int bytesLeft = output.length - outputOffset;
537            if (bytesLeft < maximumLen) {
538                throw new ShortBufferException("output buffer too small during update: "
539                        + bytesLeft + " < " + maximumLen);
540            }
541
542            outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx, output, outputOffset, input,
543                    inputOffset, inputLen);
544
545            calledUpdate = true;
546
547            return outputOffset - intialOutputOffset;
548        }
549
550        @Override
551        protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
552                throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
553            /* Remember this so we can tell how many characters were written. */
554            final int initialOutputOffset = outputOffset;
555
556            /*
557             * If we're decrypting and haven't had any input, we should return
558             * null. Otherwise OpenSSL will complain if we call final.
559             */
560            if (!isEncrypting() && !calledUpdate) {
561                return 0;
562            }
563
564            /* Allow OpenSSL to pad if necessary and clean up state. */
565            final int bytesLeft = output.length - outputOffset;
566            final int writtenBytes;
567            if (bytesLeft >= maximumLen) {
568                writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, output, outputOffset);
569            } else {
570                final byte[] lastBlock = new byte[maximumLen];
571                writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, lastBlock, 0);
572                if (writtenBytes > bytesLeft) {
573                    throw new ShortBufferException("buffer is too short: " + writtenBytes + " > "
574                            + bytesLeft);
575                } else if (writtenBytes > 0) {
576                    System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes);
577                }
578            }
579            outputOffset += writtenBytes;
580
581            reset();
582
583            return outputOffset - initialOutputOffset;
584        }
585
586        @Override
587        protected int getOutputSizeForFinal(int inputLen) {
588            if (modeBlockSize == 1) {
589                return inputLen;
590            } else {
591                final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx);
592
593                if (getPadding() == Padding.NOPADDING) {
594                    return buffered + inputLen;
595                } else {
596                    final boolean finalUsed = NativeCrypto.get_EVP_CIPHER_CTX_final_used(cipherCtx);
597                    // There is an additional buffer containing the possible final block.
598                    int totalLen = inputLen + buffered + (finalUsed ? modeBlockSize : 0);
599                    // Extra block for remainder bytes plus padding.
600                    // In case it's encrypting and there are no remainder bytes, add an extra block
601                    // consisting only of padding.
602                    totalLen += ((totalLen % modeBlockSize != 0) || isEncrypting())
603                            ? modeBlockSize : 0;
604                    // The minimum multiple of {@code modeBlockSize} that can hold all the bytes.
605                    return totalLen - (totalLen % modeBlockSize);
606                }
607            }
608        }
609
610        @Override
611        protected int getOutputSizeForUpdate(int inputLen) {
612            return getOutputSizeForFinal(inputLen);
613        }
614
615        /**
616         * Returns the OpenSSL cipher name for the particular {@code keySize}
617         * and cipher {@code mode}.
618         */
619        protected abstract String getCipherName(int keySize, Mode mode);
620
621        /**
622         * Reset this Cipher instance state to process a new chunk of data.
623         */
624        private void reset() {
625            NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting());
626            calledUpdate = false;
627        }
628
629        public static class AES extends EVP_CIPHER {
630            private static final int AES_BLOCK_SIZE = 16;
631
632            protected AES(Mode mode, Padding padding) {
633                super(mode, padding);
634            }
635
636            public static class CBC extends AES {
637                public CBC(Padding padding) {
638                    super(Mode.CBC, padding);
639                }
640
641                public static class NoPadding extends CBC {
642                    public NoPadding() {
643                        super(Padding.NOPADDING);
644                    }
645                }
646
647                public static class PKCS5Padding extends CBC {
648                    public PKCS5Padding() {
649                        super(Padding.PKCS5PADDING);
650                    }
651                }
652            }
653
654            public static class CTR extends AES {
655                public CTR() {
656                    super(Mode.CTR, Padding.NOPADDING);
657                }
658            }
659
660            public static class ECB extends AES {
661                public ECB(Padding padding) {
662                    super(Mode.ECB, padding);
663                }
664
665                public static class NoPadding extends ECB {
666                    public NoPadding() {
667                        super(Padding.NOPADDING);
668                    }
669                }
670
671                public static class PKCS5Padding extends ECB {
672                    public PKCS5Padding() {
673                        super(Padding.PKCS5PADDING);
674                    }
675                }
676            }
677
678            @Override
679            protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
680                switch (keyLength) {
681                    case 16: // AES 128
682                    case 24: // AES 192
683                    case 32: // AES 256
684                        return;
685                    default:
686                        throw new InvalidKeyException("Unsupported key size: " + keyLength
687                                + " bytes");
688                }
689            }
690
691            @Override
692            protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
693                switch (mode) {
694                    case CBC:
695                    case CTR:
696                    case ECB:
697                        return;
698                    default:
699                        throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
700                }
701            }
702
703            @Override
704            protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
705                switch (padding) {
706                    case NOPADDING:
707                    case PKCS5PADDING:
708                        return;
709                    default:
710                        throw new NoSuchPaddingException("Unsupported padding "
711                                + padding.toString());
712                }
713            }
714
715            @Override
716            protected String getBaseCipherName() {
717                return "AES";
718            }
719
720            @Override
721            protected String getCipherName(int keyLength, Mode mode) {
722                return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
723            }
724
725            @Override
726            protected int getCipherBlockSize() {
727                return AES_BLOCK_SIZE;
728            }
729        }
730
731        public static class DESEDE extends EVP_CIPHER {
732            private static int DES_BLOCK_SIZE = 8;
733
734            public DESEDE(Mode mode, Padding padding) {
735                super(mode, padding);
736            }
737
738            public static class CBC extends DESEDE {
739                public CBC(Padding padding) {
740                    super(Mode.CBC, padding);
741                }
742
743                public static class NoPadding extends CBC {
744                    public NoPadding() {
745                        super(Padding.NOPADDING);
746                    }
747                }
748
749                public static class PKCS5Padding extends CBC {
750                    public PKCS5Padding() {
751                        super(Padding.PKCS5PADDING);
752                    }
753                }
754            }
755
756            @Override
757            protected String getBaseCipherName() {
758                return "DESede";
759            }
760
761            @Override
762            protected String getCipherName(int keySize, Mode mode) {
763                final String baseCipherName;
764                if (keySize == 16) {
765                    baseCipherName = "des-ede";
766                } else {
767                    baseCipherName = "des-ede3";
768                }
769
770                return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US);
771            }
772
773            @Override
774            protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
775                if (keySize != 16 && keySize != 24) {
776                    throw new InvalidKeyException("key size must be 128 or 192 bits");
777                }
778            }
779
780            @Override
781            protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
782                if (mode != Mode.CBC) {
783                    throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
784                }
785            }
786
787            @Override
788            protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
789                switch (padding) {
790                    case NOPADDING:
791                    case PKCS5PADDING:
792                        return;
793                    default:
794                        throw new NoSuchPaddingException("Unsupported padding "
795                                + padding.toString());
796                }
797            }
798
799            @Override
800            protected int getCipherBlockSize() {
801                return DES_BLOCK_SIZE;
802            }
803        }
804
805        public static class ARC4 extends EVP_CIPHER {
806            public ARC4() {
807                // Modes and padding don't make sense for ARC4.
808                super(Mode.ECB, Padding.NOPADDING);
809            }
810
811            @Override
812            protected String getBaseCipherName() {
813                return "ARCFOUR";
814            }
815
816            @Override
817            protected String getCipherName(int keySize, Mode mode) {
818                return "rc4";
819            }
820
821            @Override
822            protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
823            }
824
825            @Override
826            protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
827                throw new NoSuchAlgorithmException("ARC4 does not support modes");
828            }
829
830            @Override
831            protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
832                throw new NoSuchPaddingException("ARC4 does not support padding");
833            }
834
835            @Override
836            protected int getCipherBlockSize() {
837                return 0;
838            }
839
840            @Override
841            protected boolean supportsVariableSizeKey() {
842                return true;
843            }
844        }
845    }
846
847    public static abstract class EVP_AEAD extends OpenSSLCipher {
848        /**
849         * The default tag size when one is not specified. Default to
850         * full-length tags (128-bits or 16 octets).
851         */
852        private static final int DEFAULT_TAG_SIZE_BITS = 16 * 8;
853
854        /**
855         * Keeps track of the last used block size.
856         */
857        private static int lastGlobalMessageSize = 32;
858
859        /**
860         * The byte array containing the bytes written.
861         */
862        protected byte[] buf;
863
864        /**
865         * The number of bytes written.
866         */
867        protected int bufCount;
868
869        /**
870         * AEAD cipher reference.
871         */
872        protected long evpAead;
873
874        /**
875         * Additional authenticated data.
876         */
877        private byte[] aad;
878
879        /**
880         * The length of the AEAD cipher tag in bytes.
881         */
882        private int tagLengthInBytes;
883
884        public EVP_AEAD(Mode mode) {
885            super(mode, Padding.NOPADDING);
886        }
887
888        private void expand(int i) {
889            /* Can the buffer handle i more bytes, if not expand it */
890            if (bufCount + i <= buf.length) {
891                return;
892            }
893
894            byte[] newbuf = new byte[(bufCount + i) * 2];
895            System.arraycopy(buf, 0, newbuf, 0, bufCount);
896            buf = newbuf;
897        }
898
899        private void reset() {
900            aad = null;
901            final int lastBufSize = lastGlobalMessageSize;
902            if (buf == null) {
903                buf = new byte[lastBufSize];
904            } else if (bufCount > 0 && bufCount != lastBufSize) {
905                lastGlobalMessageSize = bufCount;
906                if (buf.length != bufCount) {
907                    buf = new byte[bufCount];
908                }
909            }
910            bufCount = 0;
911        }
912
913        @Override
914        protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
915                SecureRandom random) throws InvalidKeyException,
916                InvalidAlgorithmParameterException {
917            byte[] iv;
918            final int tagLenBits;
919            if (params == null) {
920                iv = null;
921                tagLenBits = DEFAULT_TAG_SIZE_BITS;
922            } else {
923                GCMParameters gcmParams = Platform.fromGCMParameterSpec(params);
924                if (gcmParams != null) {
925                    iv = gcmParams.getIV();
926                    tagLenBits = gcmParams.getTLen();
927                } else if (params instanceof IvParameterSpec) {
928                    IvParameterSpec ivParams = (IvParameterSpec) params;
929                    iv = ivParams.getIV();
930                    tagLenBits = DEFAULT_TAG_SIZE_BITS;
931                } else {
932                    iv = null;
933                    tagLenBits = DEFAULT_TAG_SIZE_BITS;
934                }
935            }
936
937            if (tagLenBits % 8 != 0) {
938                throw new InvalidAlgorithmParameterException(
939                        "Tag length must be a multiple of 8; was " + tagLengthInBytes);
940            }
941
942            tagLengthInBytes = tagLenBits / 8;
943
944            final boolean encrypting = isEncrypting();
945
946            evpAead = getEVP_AEAD(encodedKey.length);
947
948            final int expectedIvLength = NativeCrypto.EVP_AEAD_nonce_length(evpAead);
949            if (iv == null && expectedIvLength != 0) {
950                if (!encrypting) {
951                    throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
952                            + " mode");
953                }
954
955                iv = new byte[expectedIvLength];
956                if (random == null) {
957                    random = new SecureRandom();
958                }
959                random.nextBytes(iv);
960            } else if (expectedIvLength == 0 && iv != null) {
961                throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
962            } else if (iv != null && iv.length != expectedIvLength) {
963                throw new InvalidAlgorithmParameterException("Expected IV length of "
964                        + expectedIvLength + " but was " + iv.length);
965            }
966
967            this.iv = iv;
968            reset();
969        }
970
971        @Override
972        protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
973                int outputOffset, int maximumLen) throws ShortBufferException {
974            if (buf == null) {
975                throw new IllegalStateException("Cipher not initialized");
976            }
977
978            ArrayUtils.checkOffsetAndCount(input.length, inputOffset, inputLen);
979            if (inputLen > 0) {
980                expand(inputLen);
981                System.arraycopy(input, inputOffset, buf, this.bufCount, inputLen);
982                this.bufCount += inputLen;
983            }
984            return 0;
985        }
986
987        @Override
988        protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
989                throws IllegalBlockSizeException, BadPaddingException {
990            EVP_AEAD_CTX cipherCtx = new EVP_AEAD_CTX(NativeCrypto.EVP_AEAD_CTX_init(evpAead,
991                    encodedKey, tagLengthInBytes));
992            final int bytesWritten;
993            try {
994                if (isEncrypting()) {
995                    bytesWritten = NativeCrypto.EVP_AEAD_CTX_seal(cipherCtx, output, outputOffset,
996                            iv, buf, 0, bufCount, aad);
997                } else {
998                    bytesWritten = NativeCrypto.EVP_AEAD_CTX_open(cipherCtx, output, outputOffset,
999                            iv, buf, 0, bufCount, aad);
1000                }
1001            } catch (BadPaddingException e) {
1002                Constructor<?> aeadBadTagConstructor = null;
1003                try {
1004                    aeadBadTagConstructor = Class.forName("javax.crypto.AEADBadTagException")
1005                            .getConstructor(String.class);
1006                } catch (ClassNotFoundException | NoSuchMethodException e2) {
1007                }
1008
1009                if (aeadBadTagConstructor != null) {
1010                    BadPaddingException badTagException = null;
1011                    try {
1012                        badTagException = (BadPaddingException) aeadBadTagConstructor.newInstance(e
1013                                .getMessage());
1014                        badTagException.initCause(e.getCause());
1015                    } catch (IllegalAccessException | InstantiationException e2) {
1016                        // Fall through
1017                    } catch (InvocationTargetException e2) {
1018                        throw (BadPaddingException) new BadPaddingException().initCause(e2
1019                                .getTargetException());
1020                    }
1021                    if (badTagException != null) {
1022                        throw badTagException;
1023                    }
1024                }
1025                throw e;
1026            }
1027            reset();
1028            return bytesWritten;
1029        }
1030
1031        @Override
1032        protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
1033            if (padding != Padding.NOPADDING) {
1034                throw new NoSuchPaddingException("Must be NoPadding for AEAD ciphers");
1035            }
1036        }
1037
1038        @Override
1039        protected int getOutputSizeForFinal(int inputLen) {
1040            return bufCount + inputLen
1041                    + (isEncrypting() ? NativeCrypto.EVP_AEAD_max_overhead(evpAead) : 0);
1042        }
1043
1044        /* @Override */
1045        protected void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
1046            if (aad == null) {
1047                aad = Arrays.copyOfRange(input, inputOffset, inputOffset + inputLen);
1048            } else {
1049                int newSize = aad.length + inputLen;
1050                byte[] newaad = new byte[newSize];
1051                System.arraycopy(aad, 0, newaad, 0, aad.length);
1052                System.arraycopy(input, inputOffset, newaad, aad.length, inputLen);
1053                aad = newaad;
1054            }
1055        }
1056
1057        @Override
1058        protected AlgorithmParameters engineGetParameters() {
1059            // iv will be non-null after initialization.
1060            if (iv == null) {
1061                return null;
1062            }
1063
1064            AlgorithmParameterSpec spec = Platform.toGCMParameterSpec(tagLengthInBytes * 8, iv);
1065            if (spec == null) {
1066                // The platform doesn't support GCMParameterSpec. Fall back to
1067                // the generic AES parameters so at least the caller can get the
1068                // IV.
1069                return super.engineGetParameters();
1070            }
1071
1072            try {
1073                AlgorithmParameters params = AlgorithmParameters.getInstance("GCM");
1074                params.init(spec);
1075                return params;
1076            } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) {
1077                // This may happen since Conscrypt doesn't provide this itself.
1078                return null;
1079            }
1080        }
1081
1082        protected abstract long getEVP_AEAD(int keyLength) throws InvalidKeyException;
1083
1084        public abstract static class AES extends EVP_AEAD {
1085            private static final int AES_BLOCK_SIZE = 16;
1086
1087            protected AES(Mode mode) {
1088                super(mode);
1089            }
1090
1091            @Override
1092            protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
1093                switch (keyLength) {
1094                    case 16: // AES 128
1095                    case 32: // AES 256
1096                        return;
1097                    default:
1098                        throw new InvalidKeyException("Unsupported key size: " + keyLength
1099                                + " bytes (must be 16 or 32)");
1100                }
1101            }
1102
1103            @Override
1104            protected String getBaseCipherName() {
1105                return "AES";
1106            }
1107
1108            @Override
1109            protected int getCipherBlockSize() {
1110                return AES_BLOCK_SIZE;
1111            }
1112
1113            /**
1114             * AEAD buffers everything until a final output.
1115             */
1116            @Override
1117            protected int getOutputSizeForUpdate(int inputLen) {
1118                return 0;
1119            }
1120
1121            public static class GCM extends AES {
1122                public GCM() {
1123                    super(Mode.GCM);
1124                }
1125
1126                @Override
1127                protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
1128                    if (mode != Mode.GCM) {
1129                        throw new NoSuchAlgorithmException("Mode must be GCM");
1130                    }
1131                }
1132
1133                @Override
1134                protected long getEVP_AEAD(int keyLength) throws InvalidKeyException {
1135                    if (keyLength == 16) {
1136                        return NativeCrypto.EVP_aead_aes_128_gcm();
1137                    } else if (keyLength == 32) {
1138                        return NativeCrypto.EVP_aead_aes_256_gcm();
1139                    } else {
1140                        throw new RuntimeException("Unexpected key length: " + keyLength);
1141                    }
1142                }
1143            }
1144        }
1145    }
1146}
1147