/** * Performs decryption operation for the last time. * * <p>NOTE: For cipher feedback modes which does not perform special handling for the last few * blocks, this is essentially the same as <code>encrypt(...)</code>. Given most modes do not do * special handling, the default impl for this method is to simply call <code>decrypt(...)</code>. * * @param in the input buffer with the data to be decrypted * @param inOfs the offset in <code>cipher</code> * @param len the length of the input data * @param out the buffer for the decryption result * @param outOfs the offset in <code>plain</code> * @return the number of bytes placed into the <code>out</code> buffer */ int decryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs) throws IllegalBlockSizeException, AEADBadTagException, ShortBufferException { if (len < tagLenBytes) { throw new AEADBadTagException("Input too short - need tag"); } if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) { throw new ShortBufferException("Output buffer too small"); } processAAD(); if (len != 0) { ibuffer.write(in, inOfs, len); } // refresh 'in' to all buffered-up bytes in = ibuffer.toByteArray(); inOfs = 0; len = in.length; ibuffer.reset(); byte[] tag = new byte[tagLenBytes]; // get the trailing tag bytes from 'in' System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes); len -= tagLenBytes; if (len > 0) { doLastBlock(in, inOfs, len, out, outOfs, false); } byte[] lengthBlock = getLengthBlock(sizeOfAAD * 8, processed * 8); ghashAllToS.update(lengthBlock); byte[] s = ghashAllToS.digest(); byte[] sOut = new byte[s.length]; GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock); gctrForSToTag.doFinal(s, 0, s.length, sOut, 0); for (int i = 0; i < tagLenBytes; i++) { if (tag[i] != sOut[i]) { throw new AEADBadTagException("Tag mismatch!"); } } return len; }
/** * Performs encryption operation for the last time. * * @param in the input buffer with the data to be encrypted * @param inOfs the offset in <code>in</code> * @param len the length of the input data * @param out the buffer for the encryption result * @param outOfs the offset in <code>out</code> * @return the number of bytes placed into the <code>out</code> buffer */ int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs) throws IllegalBlockSizeException, ShortBufferException { if (out.length - outOfs < (len + tagLenBytes)) { throw new ShortBufferException("Output buffer too small"); } processAAD(); if (len > 0) { doLastBlock(in, inOfs, len, out, outOfs, true); } byte[] lengthBlock = getLengthBlock(sizeOfAAD * 8, processed * 8); ghashAllToS.update(lengthBlock); byte[] s = ghashAllToS.digest(); byte[] sOut = new byte[s.length]; GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock); gctrForSToTag.doFinal(s, 0, s.length, sOut, 0); System.arraycopy(sOut, 0, out, (outOfs + len), tagLenBytes); return (len + tagLenBytes); }
// Utility to process the last block; used by encryptFinal and decryptFinal void doLastBlock(byte[] in, int inOfs, int len, byte[] out, int outOfs, boolean isEncrypt) throws IllegalBlockSizeException { // process data in 'in' gctrPAndC.doFinal(in, inOfs, len, out, outOfs); processed += len; byte[] ct; int ctOfs; if (isEncrypt) { ct = out; ctOfs = outOfs; } else { ct = in; ctOfs = inOfs; } int lastLen = len % AES_BLOCK_SIZE; if (lastLen != 0) { ghashAllToS.update(ct, ctOfs, len - lastLen); byte[] padded = expandToOneBlock(ct, (ctOfs + len - lastLen), lastLen); ghashAllToS.update(padded); } else { ghashAllToS.update(ct, ctOfs, len); } }