DecryptingStream cryptStream = CRYPT_CACHE.get(key);
if (cryptStream == null) {
cryptStream = (DecryptingStream) key.getStream();
}
VerifyingStream verifyStream = cryptStream.getVerifyingStream();
if (inputCopy.remaining() < verifyStream.digestSize()) {
throw new ShortCiphertextException(inputCopy.remaining());
}
// Slice off the signature into another buffer
inputCopy.position(inputCopy.limit() - verifyStream.digestSize());
ByteBuffer signature = inputCopy.slice();
// Reset the position of the input to start of the ciphertext
inputCopy.reset();
inputCopy.limit(inputCopy.limit() - verifyStream.digestSize());
// Initialize the crypt stream. This may read an IV if any.
cryptStream.initDecrypt(inputCopy);
// Verify the header and IV if any
ByteBuffer headerAndIvToVerify = input.asReadOnlyBuffer();
headerAndIvToVerify.limit(inputCopy.position());
verifyStream.initVerify();
verifyStream.updateVerify(headerAndIvToVerify);
output.mark();
// This will process large input in chunks, rather than all at once. This
// avoids making two passes through memory.
while (inputCopy.remaining() > DECRYPT_CHUNK_SIZE) {
ByteBuffer ciphertextChunk = inputCopy.slice();
ciphertextChunk.limit(DECRYPT_CHUNK_SIZE);
cryptStream.updateDecrypt(ciphertextChunk, output);
ciphertextChunk.rewind();
verifyStream.updateVerify(ciphertextChunk);
inputCopy.position(inputCopy.position() + DECRYPT_CHUNK_SIZE);
}
inputCopy.mark();
verifyStream.updateVerify(inputCopy);
if (!verifyStream.verify(signature)) {
throw new InvalidSignatureException();
}
inputCopy.reset();
cryptStream.doFinalDecrypt(inputCopy, output);
output.limit(output.position());