in src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java [494:607]
public ProcessingSummary processBytes(
final byte[] in, final int off, final int len, final byte[] out, final int outOff)
throws BadCiphertextException, AwsCryptoException {
// We should arguably check if we are already complete_ here as other handlers
// like FrameDecryptionHandler and BlockDecryptionHandler do.
// However, adding that now could potentially break customers who have extra trailing
// bytes in their decryption streams.
// The handlers are also inconsistent in general with this check. Even those that
// do raise an exception here if already complete will not complain if
// a single call to processBytes() completes the message and provides extra trailing bytes:
// in that case they will just indicate that they didn't process the extra bytes instead.
if (len < 0 || off < 0) {
throw new AwsCryptoException(
String.format("Invalid values for input offset: %d and length: %d", off, len));
}
if (in.length == 0 || len == 0) {
return ProcessingSummary.ZERO;
}
final long totalBytesToParse = unparsedBytes_.length + (long) len;
// check for integer overflow
if (totalBytesToParse > Integer.MAX_VALUE) {
throw new AwsCryptoException(
"Size of the total bytes to parse and decrypt exceeded allowed maximum:"
+ Integer.MAX_VALUE);
}
checkSizeBound(len);
ciphertextBytesSupplied_ += len;
final byte[] bytesToParse = new byte[(int) totalBytesToParse];
final int leftoverBytes = unparsedBytes_.length;
// If there were previously unparsed bytes, add them as the first
// set of bytes to be parsed in this call.
System.arraycopy(unparsedBytes_, 0, bytesToParse, 0, unparsedBytes_.length);
System.arraycopy(in, off, bytesToParse, unparsedBytes_.length, len);
int totalParsedBytes = 0;
if (!ciphertextHeadersParsed_) {
totalParsedBytes += ciphertextHeaders_.deserialize(bytesToParse, 0, maxEncryptedDataKeys_);
// When ciphertext headers are complete, we have the data
// key and cipher mode to initialize the underlying cipher
if (ciphertextHeaders_.isComplete() == true) {
readHeaderFields(ciphertextHeaders_);
updateTrailingSignature(ciphertextHeaders_);
// reset unparsed bytes as parsing of ciphertext headers is
// complete.
unparsedBytes_ = new byte[0];
} else {
// If there aren't enough bytes to parse ciphertext
// headers, we don't have anymore bytes to continue parsing.
// But first copy the leftover bytes to unparsed bytes.
unparsedBytes_ = Arrays.copyOfRange(bytesToParse, totalParsedBytes, bytesToParse.length);
return new ProcessingSummary(0, len);
}
}
int actualOutLen = 0;
if (!contentCryptoHandler_.isComplete()) {
// if there are bytes to parse further, pass it off to underlying
// content cryptohandler.
if ((bytesToParse.length - totalParsedBytes) > 0) {
final ProcessingSummary contentResult =
contentCryptoHandler_.processBytes(
bytesToParse,
totalParsedBytes,
bytesToParse.length - totalParsedBytes,
out,
outOff);
updateTrailingSignature(bytesToParse, totalParsedBytes, contentResult.getBytesProcessed());
actualOutLen = contentResult.getBytesWritten();
totalParsedBytes += contentResult.getBytesProcessed();
}
if (contentCryptoHandler_.isComplete()) {
actualOutLen += contentCryptoHandler_.doFinal(out, outOff + actualOutLen);
}
}
if (contentCryptoHandler_.isComplete()) {
// If the crypto algorithm contains trailing signature, we will need to verify
// the footer of the message.
if (cryptoAlgo_.getTrailingSignatureLength() > 0) {
totalParsedBytes += ciphertextFooters_.deserialize(bytesToParse, totalParsedBytes);
if (ciphertextFooters_.isComplete()) {
// reset unparsed bytes as parsing of the ciphertext footer is
// complete.
// This isn't strictly necessary since processing any further data
// should be an error.
unparsedBytes_ = new byte[0];
try {
if (!trailingSig_.verify(ciphertextFooters_.getMAuth())) {
throw new BadCiphertextException("Bad trailing signature");
}
} catch (final SignatureException ex) {
throw new BadCiphertextException("Bad trailing signature", ex);
}
complete_ = true;
} else {
// If there aren't enough bytes to parse the ciphertext
// footer, we don't have any more bytes to continue parsing.
// But first copy the leftover bytes to unparsed bytes.
unparsedBytes_ = Arrays.copyOfRange(bytesToParse, totalParsedBytes, bytesToParse.length);
return new ProcessingSummary(actualOutLen, len);
}
} else {
complete_ = true;
}
}
return new ProcessingSummary(actualOutLen, totalParsedBytes - leftoverBytes);
}