@Override
    void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
            SecureRandom random) throws InvalidKeyException,
        InvalidAlgorithmParameterException {
        byte[] iv;
        final int tagLenBits;
        if (params == null) {
            iv = null;
            tagLenBits = DEFAULT_TAG_SIZE_BITS;
        } else {
            GCMParameters gcmParams = Platform.fromGCMParameterSpec(params);
            if (gcmParams != null) {
                iv = gcmParams.getIV();
                tagLenBits = gcmParams.getTLen();
            } else if (params instanceof IvParameterSpec) {
                IvParameterSpec ivParams = (IvParameterSpec) params;
                iv = ivParams.getIV();
                tagLenBits = DEFAULT_TAG_SIZE_BITS;
            } else {
                iv = null;
                tagLenBits = DEFAULT_TAG_SIZE_BITS;
            }
        }

        checkSupportedTagLength(tagLenBits);

        tagLengthInBytes = tagLenBits / 8;

        final boolean encrypting = isEncrypting();

        evpAead = getEVP_AEAD(encodedKey.length);

        final int expectedIvLength = NativeCrypto.EVP_AEAD_nonce_length(evpAead);
        if (iv == null && expectedIvLength != 0) {
            if (!encrypting) {
                throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
                        + " mode");
            }

            iv = new byte[expectedIvLength];
            if (random != null) {
                random.nextBytes(iv);
            } else {
                NativeCrypto.RAND_bytes(iv);
            }
        } else if (expectedIvLength == 0 && iv != null) {
            throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
        } else if (iv != null && iv.length != expectedIvLength) {
            throw new InvalidAlgorithmParameterException("Expected IV length of "
                    + expectedIvLength + " but was " + iv.length);
        }

        if (isEncrypting() && iv != null && !allowsNonceReuse()) {
            if (previousKey != null && previousIv != null
                    && arraysAreEqual(previousKey, encodedKey)
                    && arraysAreEqual(previousIv, iv)) {
                mustInitialize = true;
                throw new InvalidAlgorithmParameterException(
                        "When using AEAD key and IV must not be re-used");
            }

            this.previousKey = encodedKey;
            this.previousIv = iv;
        }
        mustInitialize = false;
        this.iv = iv;
        aadBuf = null;
        if (buf != null) {
            buf.reset();
        }
        bufCount = 0;
    }
Constant-time array comparison.  Since we are using this to compare keys, we want to
ensure there's no opportunity for a timing attack.
common/src/main/java/org/conscrypt/OpenSSLAeadCipher.java