/** Save the current content of this cipher. */ void save() { processedSave = processed; sizeOfAADSave = sizeOfAAD; aadBufferSave = ((aadBuffer == null || aadBuffer.size() == 0) ? null : aadBuffer.toByteArray()); if (gctrPAndC != null) gctrPAndC.save(); if (ghashAllToS != null) ghashAllToS.save(); if (ibuffer != null) { ibufferSave = ibuffer.toByteArray(); } }
/** * Resets the cipher object to its original state. This is used when doFinal is called in the * Cipher class, so that the cipher can be reused (with its original key and iv). */ void reset() { if (aadBuffer == null) { aadBuffer = new ByteArrayOutputStream(); } else { aadBuffer.reset(); } if (gctrPAndC != null) gctrPAndC.reset(); if (ghashAllToS != null) ghashAllToS.reset(); processed = 0; sizeOfAAD = 0; if (ibuffer != null) { ibuffer.reset(); } }
int getBufferedLength() { if (ibuffer == null) { return 0; } else { return ibuffer.size(); } }
// Feed the AAD data to GHASH, pad if necessary void processAAD() { if (aadBuffer != null && aadBuffer.size() > 0) { byte[] aad = aadBuffer.toByteArray(); sizeOfAAD = aad.length; aadBuffer = null; int lastLen = aad.length % AES_BLOCK_SIZE; if (lastLen != 0) { ghashAllToS.update(aad, 0, aad.length - lastLen); byte[] padded = expandToOneBlock(aad, aad.length - lastLen, lastLen); ghashAllToS.update(padded); } else { ghashAllToS.update(aad); } } }
/** * Initializes the cipher in the specified mode with the given key and iv. * * @param decrypting flag indicating encryption or decryption * @param algorithm the algorithm name * @param key the key * @param iv the iv * @param tagLenBytes the length of tag in bytes * @exception InvalidKeyException if the given key is inappropriate for initializing this cipher */ void init(boolean decrypting, String algorithm, byte[] keyValue, byte[] ivValue, int tagLenBytes) throws InvalidKeyException { if (keyValue == null || ivValue == null) { throw new InvalidKeyException("Internal error"); } // always encrypt mode for embedded cipher this.embeddedCipher.init(false, algorithm, keyValue); this.subkeyH = new byte[AES_BLOCK_SIZE]; this.embeddedCipher.encryptBlock(new byte[AES_BLOCK_SIZE], 0, this.subkeyH, 0); this.iv = ivValue.clone(); preCounterBlock = getJ0(iv, subkeyH); byte[] j0Plus1 = preCounterBlock.clone(); increment32(j0Plus1); gctrPAndC = new GCTR(embeddedCipher, j0Plus1); ghashAllToS = new GHASH(subkeyH); this.tagLenBytes = tagLenBytes; if (aadBuffer == null) { aadBuffer = new ByteArrayOutputStream(); } else { aadBuffer.reset(); } processed = 0; sizeOfAAD = 0; if (decrypting) { ibuffer = new ByteArrayOutputStream(); } }
/** Restores the content of this cipher to the previous saved one. */ void restore() { processed = processedSave; sizeOfAAD = sizeOfAADSave; if (aadBuffer != null) { aadBuffer.reset(); if (aadBufferSave != null) { aadBuffer.write(aadBufferSave, 0, aadBufferSave.length); } } if (gctrPAndC != null) gctrPAndC.restore(); if (ghashAllToS != null) ghashAllToS.restore(); if (ibuffer != null) { ibuffer.reset(); ibuffer.write(ibufferSave, 0, ibufferSave.length); } }
/** * Continues a multi-part update of the Additional Authentication Data (AAD), using a subset of * the provided buffer. If this cipher is operating in either GCM or CCM mode, all AAD must be * supplied before beginning operations on the ciphertext (via the {@code update} and {@code * doFinal} methods). * * <p>NOTE: Given most modes do not accept AAD, default impl for this method throws * IllegalStateException. * * @param src the buffer containing the AAD * @param offset the offset in {@code src} where the AAD input starts * @param len the number of AAD bytes * @throws IllegalStateException if this cipher is in a wrong state (e.g., has not been * initialized), does not accept AAD, or if operating in either GCM or CCM mode and one of the * {@code update} methods has already been called for the active encryption/decryption * operation * @throws UnsupportedOperationException if this method has not been overridden by an * implementation * @since 1.8 */ void updateAAD(byte[] src, int offset, int len) { if (aadBuffer != null) { aadBuffer.write(src, offset, len); } else { // update has already been called throw new IllegalStateException("Update has been called; no more AAD data"); } }
/** * 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 decryption operation. * * <p>The input cipher text <code>in</code>, starting at <code>inOfs</code> and ending at <code> * (inOfs + len - 1)</code>, is decrypted. The result is stored in <code>out</code>, starting at * <code>outOfs</code>. * * @param in the buffer with the input data to be decrypted * @param inOfs the offset in <code>in</code> * @param len the length of the input data * @param out the buffer for the result * @param outOfs the offset in <code>out</code> * @exception ProviderException if <code>len</code> is not a multiple of the block size * @return the number of bytes placed into the <code>out</code> buffer */ int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) { if ((len % blockSize) != 0) { throw new ProviderException("Internal error in input buffering"); } processAAD(); if (len > 0) { // store internally until decryptFinal is called because // spec mentioned that only return recovered data after tag // is successfully verified ibuffer.write(in, inOfs, len); } return 0; }