@Override
  public byte[] decrypt(final byte[] ciphertext, final byte[] associatedData)
      throws GeneralSecurityException {
    if (ciphertext == null) {
      throw new NullPointerException("ciphertext is null");
    }
    if (ciphertext.length < outputPrefix.length + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES) {
      throw new GeneralSecurityException("ciphertext too short");
    }
    if (!isPrefix(outputPrefix, ciphertext)) {
      throw new GeneralSecurityException("Decryption failed (OutputPrefix mismatch).");
    }

    byte[] nonce = new byte[NONCE_SIZE_IN_BYTES];
    System.arraycopy(
        /* src= */ ciphertext,
        /* srcPos= */ outputPrefix.length,
        /* dest= */ nonce,
        /* destPos= */ 0,
        /* length= */ NONCE_SIZE_IN_BYTES);
    AlgorithmParameterSpec params = new IvParameterSpec(nonce);

    Cipher cipher = localCipher.get();
    cipher.init(Cipher.DECRYPT_MODE, keySpec, params);
    if (associatedData != null && associatedData.length != 0) {
      cipher.updateAAD(associatedData);
    }
    int offset = outputPrefix.length + NONCE_SIZE_IN_BYTES;
    int len = ciphertext.length - outputPrefix.length - NONCE_SIZE_IN_BYTES;
    return cipher.doFinal(ciphertext, offset, len);
  }
Returns a thread-local instance of the ChaCha20Poly1305, or null if ChaCha20Poly1305 is
not supported.
java_src/src/main/java/com/google/crypto/tink/aead/internal/ChaCha20Poly1305Jce.java