GaloisCounterMode.java 20.7 KB
Newer Older
V
valeriep 已提交
1
/*
2
 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
V
valeriep 已提交
3 4 5 6 7 8
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
9
 * by Oracle in the LICENSE file that accompanied this code.
V
valeriep 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.crypto.provider;

import java.util.Arrays;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;

/**
 * This class represents ciphers in GaloisCounter (GCM) mode.
 *
 * <p>This mode currently should only be used w/ AES cipher.
V
valeriep 已提交
38 39
 * Although no checking is done, caller should only pass AES
 * Cipher to the constructor.
V
valeriep 已提交
40
 *
V
valeriep 已提交
41 42 43
 * <p>NOTE: Unlike other modes, when used for decryption, this class
 * will buffer all processed outputs internally and won't return them
 * until the tag has been successfully verified.
V
valeriep 已提交
44 45 46 47 48 49 50 51
 *
 * @since 1.8
 */
final class GaloisCounterMode extends FeedbackCipher {

    static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
    static int DEFAULT_IV_LEN = 12; // in bytes

I
igerasim 已提交
52 53 54 55 56 57 58 59 60 61
    // In NIST SP 800-38D, GCM input size is limited to be no longer
    // than (2^36 - 32) bytes. Otherwise, the counter will wrap
    // around and lead to a leak of plaintext.
    // However, given the current GCM spec requirement that recovered
    // text can only be returned after successful tag verification,
    // we are bound by limiting the data size to the size limit of
    // java byte array, e.g. Integer.MAX_VALUE, since all data
    // can only be returned by the doFinal(...) call.
    private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;

V
valeriep 已提交
62 63 64 65
    // buffer for AAD data; if null, meaning update has been called
    private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
    private int sizeOfAAD = 0;

V
valeriep 已提交
66 67 68
    // buffer for storing input in decryption, not used for encryption
    private ByteArrayOutputStream ibuffer = null;

V
valeriep 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    // in bytes; need to convert to bits (default value 128) when needed
    private int tagLenBytes = DEFAULT_TAG_LEN;

    // these following 2 fields can only be initialized after init() is
    // called, e.g. after cipher key k is set, and STAY UNCHANGED
    private byte[] subkeyH = null;
    private byte[] preCounterBlock = null;

    private GCTR gctrPAndC = null;
    private GHASH ghashAllToS = null;

    // length of total data, i.e. len(C)
    private int processed = 0;

    // additional variables for save/restore calls
    private byte[] aadBufferSave = null;
    private int sizeOfAADSave = 0;
V
valeriep 已提交
86
    private byte[] ibufferSave = null;
V
valeriep 已提交
87 88 89 90 91
    private int processedSave = 0;

    // value must be 16-byte long; used by GCTR and GHASH as well
    static void increment32(byte[] value) {
        if (value.length != AES_BLOCK_SIZE) {
V
valeriep 已提交
92 93
            // should never happen
            throw new ProviderException("Illegal counter block length");
V
valeriep 已提交
94 95 96 97 98 99 100 101
        }
        // start from last byte and only go over 4 bytes, i.e. total 32 bits
        int n = value.length - 1;
        while ((n >= value.length - 4) && (++value[n] == 0)) {
            n--;
        }
    }

I
igerasim 已提交
102 103
    private static byte[] getLengthBlock(int ivLenInBytes) {
        long ivLen = ((long)ivLenInBytes) << 3;
V
valeriep 已提交
104
        byte[] out = new byte[AES_BLOCK_SIZE];
I
igerasim 已提交
105 106 107 108
        out[8] = (byte)(ivLen >>> 56);
        out[9] = (byte)(ivLen >>> 48);
        out[10] = (byte)(ivLen >>> 40);
        out[11] = (byte)(ivLen >>> 32);
V
valeriep 已提交
109 110 111 112 113 114 115
        out[12] = (byte)(ivLen >>> 24);
        out[13] = (byte)(ivLen >>> 16);
        out[14] = (byte)(ivLen >>> 8);
        out[15] = (byte)ivLen;
        return out;
    }

I
igerasim 已提交
116 117 118
    private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
        long aLen = ((long)aLenInBytes) << 3;
        long cLen = ((long)cLenInBytes) << 3;
V
valeriep 已提交
119
        byte[] out = new byte[AES_BLOCK_SIZE];
I
igerasim 已提交
120 121 122 123
        out[0] = (byte)(aLen >>> 56);
        out[1] = (byte)(aLen >>> 48);
        out[2] = (byte)(aLen >>> 40);
        out[3] = (byte)(aLen >>> 32);
V
valeriep 已提交
124 125 126 127
        out[4] = (byte)(aLen >>> 24);
        out[5] = (byte)(aLen >>> 16);
        out[6] = (byte)(aLen >>> 8);
        out[7] = (byte)aLen;
I
igerasim 已提交
128 129 130 131
        out[8] = (byte)(cLen >>> 56);
        out[9] = (byte)(cLen >>> 48);
        out[10] = (byte)(cLen >>> 40);
        out[11] = (byte)(cLen >>> 32);
V
valeriep 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
        out[12] = (byte)(cLen >>> 24);
        out[13] = (byte)(cLen >>> 16);
        out[14] = (byte)(cLen >>> 8);
        out[15] = (byte)cLen;
        return out;
    }

    private static byte[] expandToOneBlock(byte[] in, int inOfs, int len) {
        if (len > AES_BLOCK_SIZE) {
            throw new ProviderException("input " + len + " too long");
        }
        if (len == AES_BLOCK_SIZE && inOfs == 0) {
            return in;
        } else {
            byte[] paddedIn = new byte[AES_BLOCK_SIZE];
            System.arraycopy(in, inOfs, paddedIn, 0, len);
            return paddedIn;
        }
    }

    private static byte[] getJ0(byte[] iv, byte[] subkeyH) {
        byte[] j0;
        if (iv.length == 12) { // 96 bits
            j0 = expandToOneBlock(iv, 0, iv.length);
            j0[AES_BLOCK_SIZE - 1] = 1;
        } else {
            GHASH g = new GHASH(subkeyH);
            int lastLen = iv.length % AES_BLOCK_SIZE;
            if (lastLen != 0) {
                g.update(iv, 0, iv.length - lastLen);
                byte[] padded =
                    expandToOneBlock(iv, iv.length - lastLen, lastLen);
                g.update(padded);
            } else {
                g.update(iv);
            }
I
igerasim 已提交
168
            byte[] lengthBlock = getLengthBlock(iv.length);
V
valeriep 已提交
169 170 171 172 173 174
            g.update(lengthBlock);
            j0 = g.digest();
        }
        return j0;
    }

I
igerasim 已提交
175 176 177 178 179 180 181
    private static void checkDataLength(int processed, int len) {
        if (processed > MAX_BUF_SIZE - len) {
            throw new ProviderException("SunJCE provider only supports " +
                "input size up to " + MAX_BUF_SIZE + " bytes");
        }
    }

V
valeriep 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
    GaloisCounterMode(SymmetricCipher embeddedCipher) {
        super(embeddedCipher);
        aadBuffer = new ByteArrayOutputStream();
    }

    /**
     * Gets the name of the feedback mechanism
     *
     * @return the name of the feedback mechanism
     */
    String getFeedback() {
        return "GCM";
    }

    /**
     * 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;
V
valeriep 已提交
211 212 213
        if (ibuffer != null) {
            ibuffer.reset();
        }
V
valeriep 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226
    }

    /**
     * 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();
V
valeriep 已提交
227 228 229
        if (ibuffer != null) {
            ibufferSave = ibuffer.toByteArray();
        }
V
valeriep 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243
    }

    /**
     * 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);
            }
        }
V
valeriep 已提交
244 245 246 247 248 249
        if (gctrPAndC != null) gctrPAndC.restore();
        if (ghashAllToS != null) ghashAllToS.restore();
        if (ibuffer != null) {
            ibuffer.reset();
            ibuffer.write(ibufferSave, 0, ibufferSave.length);
        }
V
valeriep 已提交
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    }

    /**
     * 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
     */
265
    @Override
V
valeriep 已提交
266
    void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
267
            throws InvalidKeyException, InvalidAlgorithmParameterException {
V
valeriep 已提交
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
        init(decrypting, algorithm, key, iv, DEFAULT_TAG_LEN);
    }

    /**
     * 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)
286 287
              throws InvalidKeyException, InvalidAlgorithmParameterException {
        if (keyValue == null) {
V
valeriep 已提交
288 289
            throw new InvalidKeyException("Internal error");
        }
290 291 292 293 294 295
        if (ivValue == null) {
            throw new InvalidAlgorithmParameterException("Internal error");
        }
        if (ivValue.length == 0) {
            throw new InvalidAlgorithmParameterException("IV is empty");
        }
V
valeriep 已提交
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

        // 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;
V
valeriep 已提交
318 319 320
        if (decrypting) {
            ibuffer = new ByteArrayOutputStream();
        }
V
valeriep 已提交
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
    }

    /**
     * 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");
        }
    }

    // Feed the AAD data to GHASH, pad if necessary
    void processAAD() {
I
igerasim 已提交
359 360 361 362
        if (aadBuffer != null) {
            if (aadBuffer.size() > 0) {
                byte[] aad = aadBuffer.toByteArray();
                sizeOfAAD = aad.length;
V
valeriep 已提交
363

I
igerasim 已提交
364 365 366 367 368 369 370 371 372
                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);
                }
V
valeriep 已提交
373
            }
I
igerasim 已提交
374
            aadBuffer = null;
V
valeriep 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
        }
    }

    // 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);
        }
    }


    /**
     * Performs encryption operation.
     *
     * <p>The input plain text <code>in</code>, starting at <code>inOff</code>
     * and ending at <code>(inOff + len - 1)</code>, is encrypted. The result
     * is stored in <code>out</code>, starting at <code>outOfs</code>.
     *
     * @param in the buffer with the input 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 result
     * @param outOfs the offset in <code>out</code>
418 419 420
     * @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
V
valeriep 已提交
421
     */
V
valeriep 已提交
422
    int encrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
I
igerasim 已提交
423 424
        checkDataLength(processed, len);

425 426 427
        if ((len % blockSize) != 0) {
             throw new ProviderException("Internal error in input buffering");
        }
V
valeriep 已提交
428 429 430 431 432 433
        processAAD();
        if (len > 0) {
            gctrPAndC.update(in, inOfs, len, out, outOfs);
            processed += len;
            ghashAllToS.update(out, outOfs, len);
        }
V
valeriep 已提交
434
        return len;
V
valeriep 已提交
435 436 437 438 439 440 441 442 443 444 445 446
    }

    /**
     * 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
     */
V
valeriep 已提交
447 448
    int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
        throws IllegalBlockSizeException, ShortBufferException {
I
igerasim 已提交
449 450 451 452
        if (len > MAX_BUF_SIZE - tagLenBytes) {
            throw new ShortBufferException
                ("Can't fit both data and tag into one buffer");
        }
V
valeriep 已提交
453 454 455 456
        if (out.length - outOfs < (len + tagLenBytes)) {
            throw new ShortBufferException("Output buffer too small");
        }

I
igerasim 已提交
457 458
        checkDataLength(processed, len);

V
valeriep 已提交
459 460 461 462 463 464
        processAAD();
        if (len > 0) {
            doLastBlock(in, inOfs, len, out, outOfs, true);
        }

        byte[] lengthBlock =
I
igerasim 已提交
465
            getLengthBlock(sizeOfAAD, processed);
V
valeriep 已提交
466 467 468 469 470 471 472 473 474
        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);
    }
V
valeriep 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488

    /**
     * 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>
489 490 491
     * @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
V
valeriep 已提交
492
     */
V
valeriep 已提交
493
    int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
I
igerasim 已提交
494 495
        checkDataLength(ibuffer.size(), len);

496 497 498
        if ((len % blockSize) != 0) {
             throw new ProviderException("Internal error in input buffering");
        }
V
valeriep 已提交
499 500
        processAAD();

V
valeriep 已提交
501 502 503 504 505
        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);
V
valeriep 已提交
506
        }
V
valeriep 已提交
507
        return 0;
V
valeriep 已提交
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
    }

    /**
     * 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
     */
V
valeriep 已提交
526 527 528 529 530 531 532
    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");
        }
I
igerasim 已提交
533 534 535 536
        // do this check here can also catch the potential integer overflow
        // scenario for the subsequent output buffer capacity check.
        checkDataLength(ibuffer.size(), (len - tagLenBytes));

V
valeriep 已提交
537 538 539
        if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
            throw new ShortBufferException("Output buffer too small");
        }
I
igerasim 已提交
540

V
valeriep 已提交
541
        processAAD();
I
igerasim 已提交
542 543 544 545 546 547

        // get the trailing tag bytes from 'in'
        byte[] tag = new byte[tagLenBytes];
        System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
        len -= tagLenBytes;

V
valeriep 已提交
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
        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();

        if (len > 0) {
            doLastBlock(in, inOfs, len, out, outOfs, false);
        }

        byte[] lengthBlock =
I
igerasim 已提交
563
            getLengthBlock(sizeOfAAD, processed);
V
valeriep 已提交
564 565 566 567 568 569
        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);
A
ascarpino 已提交
570 571 572

        // check entire authentication tag for time-consistency
        int mismatch = 0;
V
valeriep 已提交
573
        for (int i = 0; i < tagLenBytes; i++) {
A
ascarpino 已提交
574
            mismatch |= tag[i] ^ sOut[i];
V
valeriep 已提交
575
        }
A
ascarpino 已提交
576 577 578 579 580

        if (mismatch != 0) {
            throw new AEADBadTagException("Tag mismatch!");
        }

V
valeriep 已提交
581 582
        return len;
    }
V
valeriep 已提交
583 584 585 586 587

    // return tag length in bytes
    int getTagLen() {
        return this.tagLenBytes;
    }
V
valeriep 已提交
588 589 590 591 592 593 594 595

    int getBufferedLength() {
        if (ibuffer == null) {
            return 0;
        } else {
            return ibuffer.size();
        }
    }
V
valeriep 已提交
596
}