Path: blob/master/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
41161 views
/*1* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.crypto.provider;2627import sun.nio.ch.DirectBuffer;28import sun.security.util.ArrayUtil;2930import javax.crypto.AEADBadTagException;31import javax.crypto.IllegalBlockSizeException;32import javax.crypto.ShortBufferException;33import java.io.ByteArrayOutputStream;34import java.io.IOException;35import java.nio.ByteBuffer;36import java.security.InvalidAlgorithmParameterException;37import java.security.InvalidKeyException;38import java.security.ProviderException;3940import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;414243/**44* This class represents ciphers in GaloisCounter (GCM) mode.45*46* <p>This mode currently should only be used w/ AES cipher.47* Although no checking is done, caller should only pass AES48* Cipher to the constructor.49*50* <p>NOTE: Unlike other modes, when used for decryption, this class51* will buffer all processed outputs internally and won't return them52* until the tag has been successfully verified.53*54* @since 1.855*/56final class GaloisCounterMode extends FeedbackCipher {5758static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;59static int DEFAULT_IV_LEN = 12; // in bytes6061// In NIST SP 800-38D, GCM input size is limited to be no longer62// than (2^36 - 32) bytes. Otherwise, the counter will wrap63// around and lead to a leak of plaintext.64// However, given the current GCM spec requirement that recovered65// text can only be returned after successful tag verification,66// we are bound by limiting the data size to the size limit of67// java byte array, e.g. Integer.MAX_VALUE, since all data68// can only be returned by the doFinal(...) call.69private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;7071// data size when buffer is divided up to aid in intrinsics72private static final int TRIGGERLEN = 65536; // 64k7374// buffer for AAD data; if null, meaning update has been called75private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();76private int sizeOfAAD = 0;7778// buffer data for crypto operation79private ByteArrayOutputStream ibuffer = null;8081// Original dst buffer if there was an overlap situation82private ByteBuffer originalDst = null;8384// in bytes; need to convert to bits (default value 128) when needed85private int tagLenBytes = DEFAULT_TAG_LEN;8687// these following 2 fields can only be initialized after init() is88// called, e.g. after cipher key k is set, and STAY UNCHANGED89private byte[] subkeyH = null;90private byte[] preCounterBlock = null;9192private GCTR gctrPAndC = null;93private GHASH ghashAllToS = null;9495// length of total data, i.e. len(C)96private int processed = 0;9798// additional variables for save/restore calls99private byte[] aadBufferSave = null;100private int sizeOfAADSave = 0;101private byte[] ibufferSave = null;102private int processedSave = 0;103104// value must be 16-byte long; used by GCTR and GHASH as well105static void increment32(byte[] value) {106if (value.length != AES_BLOCK_SIZE) {107// should never happen108throw new ProviderException("Illegal counter block length");109}110// start from last byte and only go over 4 bytes, i.e. total 32 bits111int n = value.length - 1;112while ((n >= value.length - 4) && (++value[n] == 0)) {113n--;114}115}116117private static byte[] getLengthBlock(int ivLenInBytes) {118long ivLen = ((long)ivLenInBytes) << 3;119byte[] out = new byte[AES_BLOCK_SIZE];120out[8] = (byte)(ivLen >>> 56);121out[9] = (byte)(ivLen >>> 48);122out[10] = (byte)(ivLen >>> 40);123out[11] = (byte)(ivLen >>> 32);124out[12] = (byte)(ivLen >>> 24);125out[13] = (byte)(ivLen >>> 16);126out[14] = (byte)(ivLen >>> 8);127out[15] = (byte)ivLen;128return out;129}130131private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {132long aLen = ((long)aLenInBytes) << 3;133long cLen = ((long)cLenInBytes) << 3;134byte[] out = new byte[AES_BLOCK_SIZE];135out[0] = (byte)(aLen >>> 56);136out[1] = (byte)(aLen >>> 48);137out[2] = (byte)(aLen >>> 40);138out[3] = (byte)(aLen >>> 32);139out[4] = (byte)(aLen >>> 24);140out[5] = (byte)(aLen >>> 16);141out[6] = (byte)(aLen >>> 8);142out[7] = (byte)aLen;143out[8] = (byte)(cLen >>> 56);144out[9] = (byte)(cLen >>> 48);145out[10] = (byte)(cLen >>> 40);146out[11] = (byte)(cLen >>> 32);147out[12] = (byte)(cLen >>> 24);148out[13] = (byte)(cLen >>> 16);149out[14] = (byte)(cLen >>> 8);150out[15] = (byte)cLen;151return out;152}153154private static byte[] expandToOneBlock(byte[] in, int inOfs, int len) {155if (len > AES_BLOCK_SIZE) {156throw new ProviderException("input " + len + " too long");157}158if (len == AES_BLOCK_SIZE && inOfs == 0) {159return in;160} else {161byte[] paddedIn = new byte[AES_BLOCK_SIZE];162System.arraycopy(in, inOfs, paddedIn, 0, len);163return paddedIn;164}165}166167private static byte[] getJ0(byte[] iv, byte[] subkeyH) {168byte[] j0;169if (iv.length == 12) { // 96 bits170j0 = expandToOneBlock(iv, 0, iv.length);171j0[AES_BLOCK_SIZE - 1] = 1;172} else {173GHASH g = new GHASH(subkeyH);174int lastLen = iv.length % AES_BLOCK_SIZE;175if (lastLen != 0) {176g.update(iv, 0, iv.length - lastLen);177byte[] padded =178expandToOneBlock(iv, iv.length - lastLen, lastLen);179g.update(padded);180} else {181g.update(iv);182}183byte[] lengthBlock = getLengthBlock(iv.length);184g.update(lengthBlock);185j0 = g.digest();186}187return j0;188}189190/**191* Calculate if the given data lengths and the already processed data192* exceeds the maximum allowed processed data by GCM.193* @param lengths lengths of unprocessed data.194*/195private void checkDataLength(int ... lengths) {196int max = MAX_BUF_SIZE;197for (int len : lengths) {198max = Math.subtractExact(max, len);199}200if (processed > max) {201throw new ProviderException("SunJCE provider only supports " +202"input size up to " + MAX_BUF_SIZE + " bytes");203}204}205206GaloisCounterMode(SymmetricCipher embeddedCipher) {207super(embeddedCipher);208aadBuffer = new ByteArrayOutputStream();209}210211/**212* Gets the name of the feedback mechanism213*214* @return the name of the feedback mechanism215*/216String getFeedback() {217return "GCM";218}219220/**221* Resets the cipher object to its original state.222* This is used when doFinal is called in the Cipher class, so that the223* cipher can be reused (with its original key and iv).224*/225void reset() {226if (aadBuffer == null) {227aadBuffer = new ByteArrayOutputStream();228} else {229aadBuffer.reset();230}231if (gctrPAndC != null) gctrPAndC.reset();232if (ghashAllToS != null) ghashAllToS.reset();233processed = 0;234sizeOfAAD = 0;235if (ibuffer != null) {236ibuffer.reset();237}238}239240/**241* Save the current content of this cipher.242*/243void save() {244processedSave = processed;245sizeOfAADSave = sizeOfAAD;246aadBufferSave =247((aadBuffer == null || aadBuffer.size() == 0)?248null : aadBuffer.toByteArray());249if (gctrPAndC != null) gctrPAndC.save();250if (ghashAllToS != null) ghashAllToS.save();251if (ibuffer != null) {252ibufferSave = ibuffer.toByteArray();253}254}255256/**257* Restores the content of this cipher to the previous saved one.258*/259void restore() {260processed = processedSave;261sizeOfAAD = sizeOfAADSave;262if (aadBuffer != null) {263aadBuffer.reset();264if (aadBufferSave != null) {265aadBuffer.write(aadBufferSave, 0, aadBufferSave.length);266}267}268if (gctrPAndC != null) gctrPAndC.restore();269if (ghashAllToS != null) ghashAllToS.restore();270if (ibuffer != null) {271ibuffer.reset();272ibuffer.write(ibufferSave, 0, ibufferSave.length);273}274}275276/**277* Initializes the cipher in the specified mode with the given key278* and iv.279*280* @param decrypting flag indicating encryption or decryption281* @param algorithm the algorithm name282* @param key the key283* @param iv the iv284* @exception InvalidKeyException if the given key is inappropriate for285* initializing this cipher286*/287@Override288void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)289throws InvalidKeyException, InvalidAlgorithmParameterException {290init(decrypting, algorithm, key, iv, DEFAULT_TAG_LEN);291}292293/**294* Initializes the cipher in the specified mode with the given key295* and iv.296*297* @param decrypting flag indicating encryption or decryption298* @param algorithm the algorithm name299* @param keyValue the key300* @param ivValue the iv301* @param tagLenBytes the length of tag in bytes302*303* @exception InvalidKeyException if the given key is inappropriate for304* initializing this cipher305*/306void init(boolean decrypting, String algorithm, byte[] keyValue,307byte[] ivValue, int tagLenBytes)308throws InvalidKeyException, InvalidAlgorithmParameterException {309if (keyValue == null) {310throw new InvalidKeyException("Internal error");311}312if (ivValue == null) {313throw new InvalidAlgorithmParameterException("Internal error");314}315if (ivValue.length == 0) {316throw new InvalidAlgorithmParameterException("IV is empty");317}318319// always encrypt mode for embedded cipher320this.embeddedCipher.init(false, algorithm, keyValue);321this.subkeyH = new byte[AES_BLOCK_SIZE];322this.embeddedCipher.encryptBlock(new byte[AES_BLOCK_SIZE], 0,323this.subkeyH, 0);324325this.iv = ivValue.clone();326preCounterBlock = getJ0(iv, subkeyH);327byte[] j0Plus1 = preCounterBlock.clone();328increment32(j0Plus1);329gctrPAndC = new GCTR(embeddedCipher, j0Plus1);330ghashAllToS = new GHASH(subkeyH);331332this.tagLenBytes = tagLenBytes;333if (aadBuffer == null) {334aadBuffer = new ByteArrayOutputStream();335} else {336aadBuffer.reset();337}338processed = 0;339sizeOfAAD = 0;340if (decrypting) {341ibuffer = new ByteArrayOutputStream();342}343}344345/**346* Continues a multi-part update of the Additional Authentication347* Data (AAD), using a subset of the provided buffer. If this348* cipher is operating in either GCM or CCM mode, all AAD must be349* supplied before beginning operations on the ciphertext (via the350* {@code update} and {@code doFinal} methods).351* <p>352* NOTE: Given most modes do not accept AAD, default impl for this353* method throws IllegalStateException.354*355* @param src the buffer containing the AAD356* @param offset the offset in {@code src} where the AAD input starts357* @param len the number of AAD bytes358*359* @throws IllegalStateException if this cipher is in a wrong state360* (e.g., has not been initialized), does not accept AAD, or if361* operating in either GCM or CCM mode and one of the {@code update}362* methods has already been called for the active363* encryption/decryption operation364* @throws UnsupportedOperationException if this method365* has not been overridden by an implementation366*367* @since 1.8368*/369void updateAAD(byte[] src, int offset, int len) {370if (aadBuffer != null) {371aadBuffer.write(src, offset, len);372} else {373// update has already been called374throw new IllegalStateException375("Update has been called; no more AAD data");376}377}378379// Feed the AAD data to GHASH, pad if necessary380void processAAD() {381if (aadBuffer != null) {382if (aadBuffer.size() > 0) {383byte[] aad = aadBuffer.toByteArray();384sizeOfAAD = aad.length;385386int lastLen = aad.length % AES_BLOCK_SIZE;387if (lastLen != 0) {388ghashAllToS.update(aad, 0, aad.length - lastLen);389byte[] padded = expandToOneBlock(aad, aad.length - lastLen,390lastLen);391ghashAllToS.update(padded);392} else {393ghashAllToS.update(aad);394}395}396aadBuffer = null;397}398}399400// Utility to process the last block; used by encryptFinal and decryptFinal401void doLastBlock(byte[] in, int inOfs, int len, byte[] out, int outOfs,402boolean isEncrypt) throws IllegalBlockSizeException {403byte[] ct;404int ctOfs;405int ilen = len; // internal length406407if (isEncrypt) {408ct = out;409ctOfs = outOfs;410} else {411ct = in;412ctOfs = inOfs;413}414415// Divide up larger data sizes to trigger CTR & GHASH intrinsic quicker416if (len > TRIGGERLEN) {417int i = 0;418int tlen; // incremental lengths419final int plen = AES_BLOCK_SIZE * 6;420// arbitrary formula to aid intrinsic without reaching buffer end421final int count = len / 1024;422423while (count > i) {424tlen = gctrPAndC.update(in, inOfs, plen, out, outOfs);425ghashAllToS.update(ct, ctOfs, tlen);426inOfs += tlen;427outOfs += tlen;428ctOfs += tlen;429i++;430}431ilen -= count * plen;432processed += count * plen;433}434435gctrPAndC.doFinal(in, inOfs, ilen, out, outOfs);436processed += ilen;437438int lastLen = ilen % AES_BLOCK_SIZE;439if (lastLen != 0) {440ghashAllToS.update(ct, ctOfs, ilen - lastLen);441ghashAllToS.update(442expandToOneBlock(ct, (ctOfs + ilen - lastLen), lastLen));443} else {444ghashAllToS.update(ct, ctOfs, ilen);445}446}447448// Process en/decryption all the way to the last block. It takes both449// For input it takes the ibuffer which is wrapped in 'buffer' and 'src'450// from doFinal.451void doLastBlock(ByteBuffer buffer, ByteBuffer src, ByteBuffer dst)452throws IllegalBlockSizeException {453454if (buffer != null && buffer.remaining() > 0) {455// en/decrypt on how much buffer there is in AES_BLOCK_SIZE456processed += gctrPAndC.update(buffer, dst);457458// Process the remainder in the buffer459if (buffer.remaining() > 0) {460// Copy the remainder of the buffer into the extra block461byte[] block = new byte[AES_BLOCK_SIZE];462int over = buffer.remaining();463int len = over; // how much is processed by in the extra block464buffer.get(block, 0, over);465466// if src is empty, update the final block and wait for later467// to finalize operation468if (src.remaining() > 0) {469// Fill out block with what is in data470if (src.remaining() > AES_BLOCK_SIZE - over) {471src.get(block, over, AES_BLOCK_SIZE - over);472len += AES_BLOCK_SIZE - over;473} else {474// If the remaining in buffer + data does not fill a475// block, complete the ghash operation476int l = src.remaining();477src.get(block, over, l);478len += l;479}480}481gctrPAndC.update(block, 0, AES_BLOCK_SIZE, dst);482processed += len;483}484}485486// en/decrypt whatever remains in src.487// If src has been consumed, this will be a no-op488processed += gctrPAndC.doFinal(src, dst);489}490491/*492* This method is for CipherCore to insert the remainder of its buffer493* into the ibuffer before a doFinal(ByteBuffer, ByteBuffer) operation494*/495int encrypt(byte[] in, int inOfs, int len) {496if (len > 0) {497// store internally until encryptFinal498ArrayUtil.nullAndBoundsCheck(in, inOfs, len);499if (ibuffer == null) {500ibuffer = new ByteArrayOutputStream();501}502ibuffer.write(in, inOfs, len);503}504return len;505}506507/**508* Performs encryption operation.509*510* <p>The input plain text <code>in</code>, starting at <code>inOfs</code>511* and ending at <code>(inOfs + len - 1)</code>, is encrypted. The result512* is stored in <code>out</code>, starting at <code>outOfs</code>.513*514* @param in the buffer with the input data to be encrypted515* @param inOfs the offset in <code>in</code>516* @param inLen the length of the input data517* @param out the buffer for the result518* @param outOfs the offset in <code>out</code>519* @return the number of bytes placed into the <code>out</code> buffer520*/521int encrypt(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {522checkDataLength(inLen, getBufferedLength());523ArrayUtil.nullAndBoundsCheck(in, inOfs, inLen);524ArrayUtil.nullAndBoundsCheck(out, outOfs, inLen);525526processAAD();527// 'inLen' stores the length to use with buffer 'in'.528// 'len' stores the length returned by the method.529int len = inLen;530531// if there is enough data in the ibuffer and 'in', encrypt it.532if (ibuffer != null && ibuffer.size() > 0) {533byte[] buffer = ibuffer.toByteArray();534// number of bytes not filling a block535int remainder = ibuffer.size() % blockSize;536// number of bytes along block boundary537int blen = ibuffer.size() - remainder;538539// If there is enough bytes in ibuffer for a block or more,540// encrypt that first.541if (blen > 0) {542encryptBlocks(buffer, 0, blen, out, outOfs);543outOfs += blen;544}545546// blen is now the offset for 'buffer'547548// Construct and encrypt a block if there is enough 'buffer' and549// 'in' to make one550if ((inLen + remainder) >= blockSize) {551byte[] block = new byte[blockSize];552553System.arraycopy(buffer, blen, block, 0, remainder);554int inLenUsed = blockSize - remainder;555System.arraycopy(in, inOfs, block, remainder, inLenUsed);556557encryptBlocks(block, 0, blockSize, out, outOfs);558inOfs += inLenUsed;559inLen -= inLenUsed;560len += (blockSize - inLenUsed);561outOfs += blockSize;562ibuffer.reset();563// Code below will write the remainder from 'in' to ibuffer564} else if (remainder > 0) {565// If a block or more was encrypted from 'buffer' only, but the566// rest of 'buffer' with 'in' could not construct a block, then567// put the rest of 'buffer' back into ibuffer.568ibuffer.reset();569ibuffer.write(buffer, blen, remainder);570// Code below will write the remainder from 'in' to ibuffer571}572// If blen == 0 and there was not enough to construct a block573// from 'buffer' and 'in', then let the below code append 'in' to574// the ibuffer.575}576577// Write any remaining bytes outside the blockSize into ibuffer.578int remainder = inLen % blockSize;579if (remainder > 0) {580if (ibuffer == null) {581ibuffer = new ByteArrayOutputStream(inLen % blockSize);582}583len -= remainder;584inLen -= remainder;585// remainder offset is based on original buffer length586ibuffer.write(in, inOfs + inLen, remainder);587}588589// Encrypt the remaining blocks inside of 'in'590if (inLen > 0) {591encryptBlocks(in, inOfs, inLen, out, outOfs);592}593594return len;595}596597void encryptBlocks(byte[] in, int inOfs, int len, byte[] out, int outOfs) {598gctrPAndC.update(in, inOfs, len, out, outOfs);599processed += len;600ghashAllToS.update(out, outOfs, len);601}602603/**604* Performs encryption operation for the last time.605*606* @param in the input buffer with the data to be encrypted607* @param inOfs the offset in <code>in</code>608* @param len the length of the input data609* @param out the buffer for the encryption result610* @param outOfs the offset in <code>out</code>611* @return the number of bytes placed into the <code>out</code> buffer612*/613int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)614throws IllegalBlockSizeException, ShortBufferException {615checkDataLength(len, getBufferedLength(), tagLenBytes);616617try {618ArrayUtil.nullAndBoundsCheck(out, outOfs,619(len + tagLenBytes));620} catch (ArrayIndexOutOfBoundsException aiobe) {621throw new ShortBufferException("Output buffer too small");622}623624processAAD();625if (len > 0) {626ArrayUtil.nullAndBoundsCheck(in, inOfs, len);627628doLastBlock(in, inOfs, len, out, outOfs, true);629}630631byte[] block = getLengthBlock(sizeOfAAD, processed);632ghashAllToS.update(block);633block = ghashAllToS.digest();634GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);635gctrForSToTag.doFinal(block, 0, tagLenBytes, block, 0);636637System.arraycopy(block, 0, out, (outOfs + len), tagLenBytes);638return (len + tagLenBytes);639}640641int encryptFinal(ByteBuffer src, ByteBuffer dst)642throws IllegalBlockSizeException, ShortBufferException {643dst = overlapDetection(src, dst);644int len = src.remaining();645len += getBufferedLength();646647// 'len' includes ibuffer data648checkDataLength(len, tagLenBytes);649dst.mark();650if (dst.remaining() < len + tagLenBytes) {651throw new ShortBufferException("Output buffer too small");652}653654processAAD();655if (len > 0) {656doLastBlock((ibuffer == null || ibuffer.size() == 0) ?657null : ByteBuffer.wrap(ibuffer.toByteArray()), src, dst);658dst.reset();659ghashAllToS.doLastBlock(dst, len);660}661662byte[] block = getLengthBlock(sizeOfAAD, processed);663ghashAllToS.update(block);664block = ghashAllToS.digest();665GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);666gctrForSToTag.doFinal(block, 0, tagLenBytes, block, 0);667dst.put(block, 0, tagLenBytes);668restoreDst(dst);669670return (len + tagLenBytes);671}672673/**674* Performs decryption operation.675*676* <p>The input cipher text <code>in</code>, starting at677* <code>inOfs</code> and ending at <code>(inOfs + len - 1)</code>,678* is decrypted. The result is stored in <code>out</code>, starting at679* <code>outOfs</code>.680*681* @param in the buffer with the input data to be decrypted682* @param inOfs the offset in <code>in</code>683* @param len the length of the input data684* @param out the buffer for the result685* @param outOfs the offset in <code>out</code>686* @exception ProviderException if <code>len</code> is not687* a multiple of the block size688* @return the number of bytes placed into the <code>out</code> buffer689*/690int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {691processAAD();692693if (len > 0) {694// store internally until decryptFinal is called because695// spec mentioned that only return recovered data after tag696// is successfully verified697ArrayUtil.nullAndBoundsCheck(in, inOfs, len);698ibuffer.write(in, inOfs, len);699}700return 0;701}702703int decrypt(ByteBuffer src, ByteBuffer dst) {704if (src.remaining() > 0) {705byte[] b = new byte[src.remaining()];706src.get(b);707try {708ibuffer.write(b);709} catch (IOException e) {710throw new ProviderException("Unable to add remaining input to the buffer", e);711}712}713return 0;714}715716/**717* Performs decryption operation for the last time.718*719* <p>NOTE: For cipher feedback modes which does not perform720* special handling for the last few blocks, this is essentially721* the same as <code>encrypt(...)</code>. Given most modes do722* not do special handling, the default impl for this method is723* to simply call <code>decrypt(...)</code>.724*725* @param in the input buffer with the data to be decrypted726* @param inOfs the offset in <code>cipher</code>727* @param len the length of the input data728* @param out the buffer for the decryption result729* @param outOfs the offset in <code>plain</code>730* @return the number of bytes placed into the <code>out</code> buffer731*/732int decryptFinal(byte[] in, int inOfs, int len,733byte[] out, int outOfs)734throws IllegalBlockSizeException, AEADBadTagException,735ShortBufferException {736if (len < tagLenBytes) {737throw new AEADBadTagException("Input too short - need tag");738}739740// do this check here can also catch the potential integer overflow741// scenario for the subsequent output buffer capacity check.742checkDataLength(getBufferedLength(), (len - tagLenBytes));743744try {745ArrayUtil.nullAndBoundsCheck(out, outOfs,746(getBufferedLength() + len) - tagLenBytes);747} catch (ArrayIndexOutOfBoundsException aiobe) {748throw new ShortBufferException("Output buffer too small");749}750751processAAD();752753ArrayUtil.nullAndBoundsCheck(in, inOfs, len);754755// get the trailing tag bytes from 'in'756byte[] tag = new byte[tagLenBytes];757System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);758len -= tagLenBytes;759760// If decryption is in-place or there is buffered "ibuffer" data, copy761// the "in" byte array into the ibuffer before proceeding.762if (in == out || getBufferedLength() > 0) {763if (len > 0) {764ibuffer.write(in, inOfs, len);765}766767// refresh 'in' to all buffered-up bytes768in = ibuffer.toByteArray();769inOfs = 0;770len = in.length;771ibuffer.reset();772}773774if (len > 0) {775doLastBlock(in, inOfs, len, out, outOfs, false);776}777778byte[] block = getLengthBlock(sizeOfAAD, processed);779ghashAllToS.update(block);780block = ghashAllToS.digest();781GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);782gctrForSToTag.doFinal(block, 0, tagLenBytes, block, 0);783784// check entire authentication tag for time-consistency785int mismatch = 0;786for (int i = 0; i < tagLenBytes; i++) {787mismatch |= tag[i] ^ block[i];788}789790if (mismatch != 0) {791throw new AEADBadTagException("Tag mismatch!");792}793794return len;795}796797// Note: In-place operations do not need an intermediary copy because798// the GHASH check was performed before the decryption.799int decryptFinal(ByteBuffer src, ByteBuffer dst)800throws IllegalBlockSizeException, AEADBadTagException,801ShortBufferException {802803dst = overlapDetection(src, dst);804// Length of the input805ByteBuffer tag;806ByteBuffer ct = src.duplicate();807808ByteBuffer buffer = ((ibuffer == null || ibuffer.size() == 0) ? null :809ByteBuffer.wrap(ibuffer.toByteArray()));810int len;811812if (ct.remaining() >= tagLenBytes) {813tag = src.duplicate();814tag.position(ct.limit() - tagLenBytes);815ct.limit(ct.limit() - tagLenBytes);816len = ct.remaining();817if (buffer != null) {818len += buffer.remaining();819}820} else if (buffer != null && ct.remaining() < tagLenBytes) {821// It's unlikely the tag will be between the buffer and data822tag = ByteBuffer.allocate(tagLenBytes);823int limit = buffer.remaining() - (tagLenBytes - ct.remaining());824buffer.mark();825buffer.position(limit);826// Read from "new" limit to buffer's end827tag.put(buffer);828// reset buffer to data only829buffer.reset();830buffer.limit(limit);831tag.put(ct);832tag.flip();833// Limit is how much of the ibuffer has been chopped off.834len = buffer.remaining();835} else {836throw new AEADBadTagException("Input too short - need tag");837}838839// 'len' contains the length in ibuffer and src840checkDataLength(len);841842if (len > dst.remaining()) {843throw new ShortBufferException("Output buffer too small");844}845846processAAD();847// Set the mark for a later reset. Either it will be zero, or the tag848// buffer creation above will have consume some or all of it.849ct.mark();850851// If there is data stored in the buffer852if (buffer != null && buffer.remaining() > 0) {853ghashAllToS.update(buffer, buffer.remaining());854// Process the overage855if (buffer.remaining() > 0) {856// Fill out block between two buffers857if (ct.remaining() > 0) {858int over = buffer.remaining();859byte[] block = new byte[AES_BLOCK_SIZE];860// Copy the remainder of the buffer into the extra block861buffer.get(block, 0, over);862863// Fill out block with what is in data864if (ct.remaining() > AES_BLOCK_SIZE - over) {865ct.get(block, over, AES_BLOCK_SIZE - over);866ghashAllToS.update(block, 0, AES_BLOCK_SIZE);867} else {868// If the remaining in buffer + data does not fill a869// block, complete the ghash operation870int l = ct.remaining();871ct.get(block, over, l);872ghashAllToS.doLastBlock(ByteBuffer.wrap(block), over + l);873}874} else {875// data is empty, so complete the ghash op with the876// remaining buffer877ghashAllToS.doLastBlock(buffer, buffer.remaining());878}879}880// Prepare buffer for decryption881buffer.flip();882}883884if (ct.remaining() > 0) {885ghashAllToS.doLastBlock(ct, ct.remaining());886}887// Prepare buffer for decryption if available888ct.reset();889890byte[] block = getLengthBlock(sizeOfAAD, len);891ghashAllToS.update(block);892block = ghashAllToS.digest();893GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);894gctrForSToTag.doFinal(block, 0, tagLenBytes, block, 0);895896// check entire authentication tag for time-consistency897int mismatch = 0;898for (int i = 0; i < tagLenBytes; i++) {899mismatch |= tag.get() ^ block[i];900}901902if (mismatch != 0) {903throw new AEADBadTagException("Tag mismatch!");904}905906// Decrypt the all the input data and put it into dst907doLastBlock(buffer, ct, dst);908restoreDst(dst);909src.position(src.limit());910// 'processed' from the gctr decryption operation, not ghash911return processed;912}913914// return tag length in bytes915int getTagLen() {916return this.tagLenBytes;917}918919int getBufferedLength() {920if (ibuffer == null) {921return 0;922} else {923return ibuffer.size();924}925}926927/**928* Check for overlap. If the src and dst buffers are using shared data and929* if dst will overwrite src data before src can be processed. If so, make930* a copy to put the dst data in.931*/932ByteBuffer overlapDetection(ByteBuffer src, ByteBuffer dst) {933if (src.isDirect() && dst.isDirect()) {934DirectBuffer dsrc = (DirectBuffer) src;935DirectBuffer ddst = (DirectBuffer) dst;936937// Get the current memory address for the given ByteBuffers938long srcaddr = dsrc.address();939long dstaddr = ddst.address();940941// Find the lowest attachment that is the base memory address of the942// shared memory for the src object943while (dsrc.attachment() != null) {944srcaddr = ((DirectBuffer) dsrc.attachment()).address();945dsrc = (DirectBuffer) dsrc.attachment();946}947948// Find the lowest attachment that is the base memory address of the949// shared memory for the dst object950while (ddst.attachment() != null) {951dstaddr = ((DirectBuffer) ddst.attachment()).address();952ddst = (DirectBuffer) ddst.attachment();953}954955// If the base addresses are not the same, there is no overlap956if (srcaddr != dstaddr) {957return dst;958}959// At this point we know these objects share the same memory.960// This checks the starting position of the src and dst address for961// overlap.962// It uses the base address minus the passed object's address to get963// the offset from the base address, then add the position() from964// the passed object. That gives up the true offset from the base965// address. As long as the src side is >= the dst side, we are not966// in overlap.967if (((DirectBuffer) src).address() - srcaddr + src.position() >=968((DirectBuffer) dst).address() - dstaddr + dst.position()) {969return dst;970}971972} else if (!src.isDirect() && !dst.isDirect()) {973if (!src.isReadOnly()) {974// If using the heap, check underlying byte[] address.975if (src.array() != dst.array()) {976return dst;977}978979// Position plus arrayOffset() will give us the true offset from980// the underlying byte[] address.981if (src.position() + src.arrayOffset() >=982dst.position() + dst.arrayOffset()) {983return dst;984}985}986} else {987// buffer types aren't the same988return dst;989}990991// Create a copy992ByteBuffer tmp = dst.duplicate();993// We can use a heap buffer for internal use, save on alloc cost994ByteBuffer bb = ByteBuffer.allocate(dst.remaining());995tmp.limit(dst.limit());996tmp.position(dst.position());997bb.put(tmp);998bb.flip();999originalDst = dst;1000return bb;1001}10021003/**1004* If originalDst exists, dst is an internal dst buffer, then copy the data1005* into the original dst buffer1006*/1007void restoreDst(ByteBuffer dst) {1008if (originalDst == null) {1009return;1010}10111012dst.flip();1013originalDst.put(dst);1014originalDst = null;1015}1016}101710181019