1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.crypto.provider;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.IOException;
  30 import java.lang.invoke.MethodHandles;
  31 import java.lang.invoke.VarHandle;
  32 import java.nio.ByteBuffer;
  33 import java.nio.ByteOrder;
  34 import java.security.*;
  35 import java.security.spec.AlgorithmParameterSpec;
  36 import java.util.Arrays;
  37 import java.util.Objects;
  38 import javax.crypto.spec.ChaCha20ParameterSpec;
  39 import javax.crypto.spec.IvParameterSpec;
  40 import javax.crypto.spec.SecretKeySpec;
  41 import javax.crypto.*;
  42 import sun.security.util.DerValue;
  43 
  44 /**
  45  * Implementation of the ChaCha20 cipher, as described in RFC 7539.
  46  *
  47  * @since 11
  48  */
  49 abstract class ChaCha20Cipher extends CipherSpi {
  50     // Mode constants
  51     private static final int MODE_NONE = 0;
  52     private static final int MODE_AEAD = 1;
  53 
  54     // Constants used in setting up the initial state
  55     private static final int STATE_CONST_0 = 0x61707865;
  56     private static final int STATE_CONST_1 = 0x3320646e;
  57     private static final int STATE_CONST_2 = 0x79622d32;
  58     private static final int STATE_CONST_3 = 0x6b206574;
  59 
  60     // The keystream block size in bytes and as integers
  61     private static final int KEYSTREAM_SIZE = 64;
  62     private static final int KS_SIZE_INTS = KEYSTREAM_SIZE / Integer.BYTES;
  63     private static final int CIPHERBUF_BASE = 1024;
  64 
  65     // The mode of operation for this object
  66     protected int mode;
  67 
  68     // The direction (encrypt vs. decrypt) for the data flow
  69     private int direction;
  70 
  71     // Has all AAD data been provided (i.e. have we called our first update)
  72     private boolean aadDone = false;
  73 
  74     // The key and its encoding in bytes used for this object
  75     private Key key;
  76     private byte[] keyBytes;
  77 
  78     // The nonce used for this object
  79     private byte[] nonce;
  80 
  81     // The counter
  82     private static final long MAX_UINT32 = 0x00000000FFFFFFFFL;
  83     private long finalCounterValue;
  84     private long counter;
  85 
  86     // Two arrays, both implemented as 16-element integer arrays:
  87     // The base state, created at initialization time, and a working
  88     // state which is a clone of the start state, and is then modified
  89     // with the counter and the ChaCha20 block function.
  90     private final int[] startState = new int[KS_SIZE_INTS];
  91     private final byte[] keyStream = new byte[KEYSTREAM_SIZE];
  92 
  93     // The offset into the current keystream
  94     private int keyStrOffset;
  95 
  96     // AEAD-related fields and constants
  97     private static final int TAG_LENGTH = 16;
  98     private long aadLen;
  99     private long dataLen;
 100     //private byte[] tag;
 101 
 102     // Have a buffer of zero padding that can be read all or in part
 103     // by the authenticator.
 104     private static final byte[] padBuf = new byte[TAG_LENGTH];
 105 
 106     // Create a buffer for holding the AAD and Ciphertext lengths
 107     private final byte[] lenBuf = new byte[TAG_LENGTH];
 108 
 109     // The authenticator (Poly1305) when running in AEAD mode
 110     protected String authAlgName;
 111     private Poly1305 authenticator;
 112 
 113     // The underlying engine for doing the ChaCha20/Poly1305 work
 114     private ChaChaEngine engine;
 115 
 116     // Use this VarHandle for converting the state elements into little-endian
 117     // integer values for the ChaCha20 block function.
 118     private static final VarHandle asIntLittleEndian =
 119             MethodHandles.byteArrayViewVarHandle(int[].class,
 120                     ByteOrder.LITTLE_ENDIAN);
 121 
 122     // Use this VarHandle for converting the AAD and data lengths into
 123     // little-endian long values for AEAD tag computations.
 124     private static final VarHandle asLongLittleEndian =
 125             MethodHandles.byteArrayViewVarHandle(long[].class,
 126                     ByteOrder.LITTLE_ENDIAN);
 127 
 128     // Use this for pulling in 8 bytes at a time as longs for XOR operations
 129     private static final VarHandle asLongView =
 130             MethodHandles.byteArrayViewVarHandle(long[].class,
 131                     ByteOrder.nativeOrder());
 132 
 133     /**
 134      * Default constructor.
 135      */
 136     protected ChaCha20Cipher() {
 137     }
 138 
 139     /**
 140      * Set the mode of operation.  Since this is a stream cipher, there
 141      * is no mode of operation in the block-cipher sense of things.  The
 142      * protected {@code mode} field will only accept a value of {@code None}
 143      * (case-insensitive).
 144      *
 145      * @param mode The mode value
 146      *
 147      * @throws NoSuchAlgorithmException if a mode of operation besides
 148      *      {@code None} is provided.
 149      */
 150     @Override
 151     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
 152         if (mode.equalsIgnoreCase("None") == false) {
 153             throw new NoSuchAlgorithmException("Mode must be None");
 154         }
 155     }
 156 
 157     /**
 158      * Set the padding scheme.  Padding schemes do not make sense with stream
 159      * ciphers, but allow {@code NoPadding}.  See JCE spec.
 160      *
 161      * @param padding The padding type.  The only allowed value is
 162      *      {@code NoPadding} case insensitive).
 163      *
 164      * @throws NoSuchPaddingException if a padding scheme besides
 165      *      {@code NoPadding} is provided.
 166      */
 167     @Override
 168     protected void engineSetPadding(String padding)
 169             throws NoSuchPaddingException {
 170         if (padding.equalsIgnoreCase("NoPadding") == false) {
 171             throw new NoSuchPaddingException("Padding must be NoPadding");
 172         }
 173     }
 174 
 175     /**
 176      * Returns the block size.  For a stream cipher like ChaCha20, this
 177      * value will always be zero.
 178      *
 179      * @return This method always returns 0.  See the JCE Specification.
 180      */
 181     @Override
 182     protected int engineGetBlockSize() {
 183         return 0;
 184     }
 185 
 186     /**
 187      * Get the output size based on an input length.  In simple stream-cipher
 188      * mode, the output size will equal the input size.  For ChaCha20-Poly1305
 189      * for encryption the output size will be the sum of the input length
 190      * and tag length.  For decryption, the output size will be the input
 191      * length less the tag length or zero, whichever is larger.
 192      *
 193      * @param inputLen the length in bytes of the input
 194      *
 195      * @return the output length in bytes.
 196      */
 197     @Override
 198     protected int engineGetOutputSize(int inputLen) {
 199         int outLen = 0;
 200 
 201         if (mode == MODE_NONE) {
 202             outLen = inputLen;
 203         } else if (mode == MODE_AEAD) {
 204             outLen = (direction == Cipher.ENCRYPT_MODE) ?
 205                     Math.addExact(inputLen, TAG_LENGTH) :
 206                     Integer.max(inputLen - TAG_LENGTH, 0);
 207         }
 208 
 209         return outLen;
 210     }
 211 
 212     /**
 213      * Get the nonce value used.
 214      *
 215      * @return the nonce bytes.  For ChaCha20 this will be a 12-byte value.
 216      */
 217     @Override
 218     protected byte[] engineGetIV() {
 219         return nonce.clone();
 220     }
 221 
 222     /**
 223      * Get the algorithm parameters for this cipher.  Since this cipher
 224      * does not support initialization with {@code AlgorithmParameters} it
 225      * will always return null.
 226      *
 227      * @return a {@code null} value.
 228      */
 229     @Override
 230     protected AlgorithmParameters engineGetParameters() {
 231         return null;
 232     }
 233 
 234     /**
 235      * Initialize the engine using a key and secure random implementation.  If
 236      * a SecureRandom object is provided it will be used to create a random
 237      * nonce value.  Otherwise a nonce of all zeroes will be used.  The counter
 238      * value will be set to 1.
 239      *
 240      * @param opmode the type of operation to do.  This value must be either
 241      *      {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}
 242      * @param key a 256-bit key suitable for ChaCha20
 243      * @param random a {@code SecureRandom} implementation.  If {@code null}
 244      *      is used for the random object, then a nonce consisting of all
 245      *      zero bytes will be used.  Otherwise a random nonce will be
 246      *      used.
 247      *
 248      * @throws InvalidKeyException if the key is of the wrong type or is
 249      *      not 256-bits in length.  This will also be thrown if the opmode
 250      *      parameter is not {@code Cipher.ENCRYPT_MODE} or
 251      *      {@code Cipher.DECRYPT_MODE}.
 252      */
 253     @Override
 254     protected void engineInit(int opmode, Key key, SecureRandom random)
 255             throws InvalidKeyException {
 256         byte[] newNonce = new byte[12];
 257         if (random != null) {
 258             random.nextBytes(newNonce);
 259         }
 260 
 261         counter = 1;
 262         init(opmode, key, newNonce);
 263     }
 264 
 265     /**
 266      * Initialize the engine using a key and secure random implementation.
 267      *
 268      * @param opmode the type of operation to do.  This value must be either
 269      *      {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}
 270      * @param key a 256-bit key suitable for ChaCha20
 271      * @param params a {@code ChaCha20ParameterSpec} that will provide
 272      *      the nonce and initial block counter value.
 273      * @param random a {@code SecureRandom} implementation, this parameter
 274      *      is not used in this form of the initializer.
 275      *
 276      * @throws InvalidKeyException if the key is of the wrong type or is
 277      *      not 256-bits in length.  This will also be thrown if the opmode
 278      *      parameter is not {@code Cipher.ENCRYPT_MODE} or
 279      *      {@code Cipher.DECRYPT_MODE}.
 280      * @throws InvalidAlgorithmParameterException if {@code params} is
 281      *      not a {@code ChaCha20ParameterSpec}
 282      * @throws NullPointerException if {@code params} is {@code null}
 283      */
 284     @Override
 285     protected void engineInit(int opmode, Key key,
 286             AlgorithmParameterSpec params, SecureRandom random)
 287             throws InvalidKeyException, InvalidAlgorithmParameterException {
 288 
 289         // We will ignore the secure random implementation and use the nonce
 290         // from the AlgorithmParameterSpec instead.
 291         byte[] newNonce = null;
 292         switch (mode) {
 293             case MODE_NONE:
 294                 if (!(params instanceof ChaCha20ParameterSpec)) {
 295                     throw new InvalidAlgorithmParameterException(
 296                         "ChaCha20 algorithm requires ChaCha20ParameterSpec");
 297                 }
 298                 ChaCha20ParameterSpec chaParams = (ChaCha20ParameterSpec)params;
 299                 newNonce = chaParams.getNonce();
 300                 if (newNonce.length != 12) {
 301                     throw new InvalidAlgorithmParameterException(
 302                         "ChaCha20 nonce must be 96 bits in length");
 303                 }
 304                 counter = ((long)chaParams.getCounter()) & 0x00000000FFFFFFFFL;
 305                 break;
 306             case MODE_AEAD:
 307                 if (!(params instanceof IvParameterSpec)) {
 308                     throw new InvalidAlgorithmParameterException(
 309                         "ChaCha20-Poly1305 requires IvParameterSpec");
 310                 }
 311                 IvParameterSpec ivParams = (IvParameterSpec)params;
 312                 newNonce = ivParams.getIV();
 313                 if (newNonce.length != 12) {
 314                     throw new InvalidAlgorithmParameterException(
 315                         "ChaCha20-Poly1305 nonce must be 96 bits in length");
 316                 }
 317                 break;
 318             default:
 319                 // Should never happen
 320                 throw new RuntimeException("ChaCha20 in unsupported mode");
 321         }
 322         init(opmode, key, newNonce);
 323     }
 324 
 325     /**
 326      * Initialize the engine using the {@code AlgorithmParameter} initialization
 327      * format.  This cipher does supports initialization with
 328      * {@code AlgorithmParameter} objects for ChaCha20-Poly1305 but not for
 329      * ChaCha20 as a simple stream cipher.  In the latter case, it will throw
 330      * an {@code InvalidAlgorithmParameterException} if the value is non-null
 331      * If a null value is supplied for the {@code params} field
 332      * the cipher will be initialized with the counter value set to 1 and
 333      * either a random nonce if the {@code random} parameter is non-null, or
 334      * a nonce of all zeroes otherwise.
 335      *
 336      * @param opmode the type of operation to do.  This value must be either
 337      *      {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}
 338      * @param key a 256-bit key suitable for ChaCha20
 339      * @param params a {@code null} value if the algorithm is ChaCha20, or
 340      *      the appropriate {@code AlgorithmParameters} object containing the
 341      *      nonce information if the algorithm is ChaCha20-Poly1305.
 342      * @param random a {@code SecureRandom} implementation, may be {@code null}.
 343      *
 344      * @throws InvalidKeyException if the key is of the wrong type or is
 345      *      not 256-bits in length.  This will also be thrown if the opmode
 346      *      parameter is not {@code Cipher.ENCRYPT_MODE} or
 347      *      {@code Cipher.DECRYPT_MODE}.
 348      * @throws InvalidAlgorithmParameterException if {@code params} is
 349      *      non-null and the algorithm is ChaCha20.  This exception will be
 350      *      also thrown if the algorithm is ChaCha20-Poly1305 and an incorrect
 351      *      {@code AlgorithmParameters} object is supplied.
 352      */
 353     @Override
 354     protected void engineInit(int opmode, Key key,
 355             AlgorithmParameters params, SecureRandom random)
 356             throws InvalidKeyException, InvalidAlgorithmParameterException {
 357         byte[] newNonce = null;
 358         switch (mode) {
 359             case MODE_NONE:
 360                 if (params != null) {
 361                     throw new InvalidAlgorithmParameterException(
 362                             "Parameters not supported");
 363                 }
 364                 break;
 365             case MODE_AEAD:
 366                 if (params != null) {
 367                     String paramAlg = params.getAlgorithm();
 368                     if (!paramAlg.equalsIgnoreCase("ChaCha20-Poly1305")) {
 369                         throw new InvalidAlgorithmParameterException(
 370                                 "Invalid parameter type: " + paramAlg);
 371                     }
 372                     try {
 373                         DerValue dv = new DerValue(params.getEncoded());
 374                         if (dv.tag == DerValue.tag_OctetString) {
 375                             // Get the nonce value
 376                             newNonce = dv.getOctetString();
 377                             if (newNonce.length != 12) {
 378                                 throw new InvalidAlgorithmParameterException(
 379                                         "ChaCha20-Poly1305 nonce must be " +
 380                                         "96 bits in length");
 381                             }
 382                         }  else {
 383                             throw new InvalidAlgorithmParameterException(
 384                                     "ChaCha20-Poly1305 Parameter " +
 385                                     "ASN.1 encoding error");
 386                         }
 387                     } catch (IOException ioe) {
 388                         throw new InvalidAlgorithmParameterException(ioe);
 389                     }
 390 
 391                 }
 392                 break;
 393             default:
 394                 throw new UnsupportedOperationException("Invalid mode: " +
 395                         mode);
 396         }
 397 
 398         // If after all the above processing we still don't have a nonce value
 399         // then supply a random one provided a random source has been given.
 400         if (newNonce == null) {
 401             newNonce = new byte[12];
 402             if (random != null) {
 403                 random.nextBytes(newNonce);
 404             }
 405         }
 406 
 407         // Continue with initialization
 408         init(opmode, key, newNonce);
 409     }
 410 
 411     /**
 412      * Update additional authenticated data (AAD).
 413      *
 414      * @param src the byte array containing the authentication data.
 415      * @param offset the starting offset in the buffer to update.
 416      * @param len the amount of authentication data to update.
 417      *
 418      * @throws IllegalStateException if the cipher has not been initialized,
 419      *      {@code engineUpdate} has been called, or the cipher is running
 420      *      in a non-AEAD mode of operation.  It will also throw this
 421      *      exception if the submitted AAD would overflow a 64-bit length
 422      *      counter.
 423      */
 424     @Override
 425     protected void engineUpdateAAD(byte[] src, int offset, int len) {
 426         if (key == null) {
 427             // We know that the cipher has not been initialized if the key
 428             // is still null.
 429             throw new IllegalStateException(
 430                     "Attempted to update AAD on uninitialized Cipher");
 431         } else if (aadDone) {
 432             // No AAD updates allowed after the PT/CT update method  is called
 433             throw new IllegalStateException("Attempted to update AAD on " +
 434                     "Cipher after plaintext/ciphertext update");
 435         } else if (mode != MODE_AEAD) {
 436             throw new IllegalStateException(
 437                     "Cipher is running in non-AEAD mode");
 438         } else {
 439             try {
 440                 aadLen = Math.addExact(aadLen, len);
 441                 authUpdate(src, offset, len);
 442             } catch (ArithmeticException ae) {
 443                 throw new IllegalStateException("AAD overflow", ae);
 444             }
 445         }
 446     }
 447 
 448     /**
 449      * Update additional authenticated data (AAD).
 450      *
 451      * @param src the ByteBuffer containing the authentication data.
 452      *
 453      * @throws IllegalStateException if the cipher has not been initialized,
 454      *      {@code engineUpdate} has been called, or the cipher is running
 455      *      in a non-AEAD mode of operation.  It will also throw this
 456      *      exception if the submitted AAD would overflow a 64-bit length
 457      *      counter.
 458      */
 459     @Override
 460     protected void engineUpdateAAD(ByteBuffer src) {
 461         if (key == null) {
 462             // We know that the cipher has not been initialized if the key
 463             // is still null.
 464             throw new IllegalStateException(
 465                     "Attempted to update AAD on uninitialized Cipher");
 466         } else if (aadDone) {
 467             // No AAD updates allowed after the PT/CT update method  is called
 468             throw new IllegalStateException("Attempted to update AAD on " +
 469                     "Cipher after plaintext/ciphertext update");
 470         } else if (mode != MODE_AEAD) {
 471             throw new IllegalStateException(
 472                     "Cipher is running in non-AEAD mode");
 473         } else {
 474             try {
 475                 aadLen = Math.addExact(aadLen, (src.limit() - src.position()));
 476                 authenticator.engineUpdate(src);
 477             } catch (ArithmeticException ae) {
 478                 throw new IllegalStateException("AAD overflow", ae);
 479             }
 480         }
 481     }
 482 
 483     /**
 484      * Perform additional initialization actions based on the key and operation
 485      * type.
 486      *
 487      * @param opmode the type of operation to do.  This value must be either
 488      *      {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}
 489      * @param key a 256-bit key suitable for ChaCha20
 490      * @param newNonce the new nonce value for this initialization.
 491      *
 492      * @throws InvalidKeyException if the {@code opmode} parameter is not
 493      *      {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}, or
 494      *      if the key format is not {@code RAW}.
 495      */
 496     private void init(int opmode, Key key, byte[] newNonce)
 497             throws InvalidKeyException {
 498         if ((opmode != Cipher.ENCRYPT_MODE) &&
 499                 (opmode != Cipher.DECRYPT_MODE)) {
 500             throw new InvalidKeyException("Unknown opmode: " + opmode);
 501         }
 502 
 503         // Make sure that the provided key and nonce are unique before
 504         // assigning them to the object.
 505         byte[] newKeyBytes = getEncodedKey(key);
 506         checkKeyAndNonce(newKeyBytes, newNonce);
 507         this.key = key;
 508         this.keyBytes = newKeyBytes;
 509         nonce = newNonce;
 510 
 511         aadDone = false;
 512         direction = opmode;
 513 
 514         // Now that we have the key and nonce, we can build the initial state
 515         setInitialState();
 516 
 517         if (mode == MODE_NONE) {
 518             engine = new EngineStreamOnly();
 519         } else if (mode == MODE_AEAD) {
 520             if (direction == Cipher.ENCRYPT_MODE) {
 521                 engine = new EngineAEADEnc();
 522             } else if (direction == Cipher.DECRYPT_MODE) {
 523                 engine = new EngineAEADDec();
 524             } else {
 525                 throw new InvalidKeyException("Not encrypt or decrypt mode");
 526             }
 527         }
 528 
 529         // We can also get one block's worth of keystream created
 530         finalCounterValue = counter + MAX_UINT32;
 531         generateKeystream();
 532         this.keyStrOffset = 0;
 533     }
 534 
 535     /**
 536      * Check the key and nonce bytes to make sure that they do not repeat
 537      * across reinitialization.
 538      *
 539      * @param newKeyBytes the byte encoding for the newly provided key
 540      * @param newNonce the new nonce to be used with this initialization
 541      *
 542      * @throws InvalidKeyException if both the key and nonce match the
 543      *      previous initialization.
 544      *
 545      */
 546     private void checkKeyAndNonce(byte[] newKeyBytes, byte[] newNonce)
 547             throws InvalidKeyException {
 548         // A new initialization must have either a different key or nonce
 549         // so the starting state for each block is not the same as the
 550         // previous initialization.
 551         if (Arrays.equals(newKeyBytes, keyBytes) &&
 552                 Arrays.equals(newNonce, nonce)) {
 553             throw new InvalidKeyException(
 554                     "Matching key and nonce from previous initialization");
 555         }
 556     }
 557 
 558     /**
 559      * Return the encoded key as a byte array
 560      *
 561      * @param key the {@code Key} object used for this {@code Cipher}
 562      *
 563      * @return the key bytes
 564      *
 565      * @throws InvalidKeyException if the key is of the wrong type or length,
 566      *      or if the key encoding format is not {@code RAW}.
 567      */
 568     private static byte[] getEncodedKey(Key key) throws InvalidKeyException {
 569         String keyAlg = key.getAlgorithm();
 570 //        if (!keyAlg.equals("ChaCha20")) {
 571 //            throw new InvalidKeyException("Not a ChaCha20 key: " + keyAlg);
 572 //        }
 573         if ("RAW".equals(key.getFormat()) == false) {
 574             throw new InvalidKeyException("Key encoding format must be RAW");
 575         }
 576         byte[] encodedKey = key.getEncoded();
 577         if (encodedKey == null || encodedKey.length != 32) {
 578             throw new InvalidKeyException("Key length must be 256 bits");
 579         }
 580         return encodedKey;
 581     }
 582 
 583     /**
 584      * Update the currently running operation with additional data
 585      *
 586      * @param in the plaintext or ciphertext input bytes (depending on the
 587      *      operation type).
 588      * @param inOfs the offset into the input array
 589      * @param inLen the length of the data to use for the update operation.
 590      *
 591      * @return the resulting plaintext or ciphertext bytes (depending on
 592      *      the operation type)
 593      */
 594     @Override
 595     protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
 596         byte[] out = new byte[inLen];
 597         try {
 598             engine.doUpdate(in, inOfs, inLen, out, 0);
 599         } catch (ShortBufferException | KeyException exc) {
 600             throw new RuntimeException(exc);
 601         }
 602 
 603         return out;
 604     }
 605 
 606     /**
 607      * Update the currently running operation with additional data
 608      *
 609      * @param in the plaintext or ciphertext input bytes (depending on the
 610      *      operation type).
 611      * @param inOfs the offset into the input array
 612      * @param inLen the length of the data to use for the update operation.
 613      * @param out the byte array that will hold the resulting data.  The array
 614      *      must be large enough to hold the resulting data.
 615      * @param outOfs the offset for the {@code out} buffer to begin writing
 616      *      the resulting data.
 617      *
 618      * @return the length in bytes of the data written into the {@code out}
 619      *      buffer.
 620      *
 621      * @throws ShortBufferException if the buffer {@code out} does not have
 622      *      enough space to hold the resulting data.
 623      */
 624     @Override
 625     protected int engineUpdate(byte[] in, int inOfs, int inLen,
 626             byte[] out, int outOfs) throws ShortBufferException {
 627         int bytesUpdated = 0;
 628         try {
 629             bytesUpdated = engine.doUpdate(in, inOfs, inLen, out, outOfs);
 630         } catch (KeyException ke) {
 631             throw new RuntimeException(ke);
 632         }
 633         return bytesUpdated;
 634     }
 635 
 636     /**
 637      * Complete the currently running operation using any final
 638      * data provided by the caller.
 639      *
 640      * @param in the plaintext or ciphertext input bytes (depending on the
 641      *      operation type).
 642      * @param inOfs the offset into the input array
 643      * @param inLen the length of the data to use for the update operation.
 644      *
 645      * @return the resulting plaintext or ciphertext bytes (depending on
 646      *      the operation type)
 647      *
 648      * @throws AEADBadTagException if, during decryption, the provided tag
 649      *      does not match the calculated tag.
 650      */
 651     @Override
 652     protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
 653             throws AEADBadTagException {
 654         byte[] output = new byte[engineGetOutputSize(inLen)];
 655         try {
 656             engine.doFinal(in, inOfs, inLen, output, 0);
 657         } catch (ShortBufferException | KeyException exc) {
 658             throw new RuntimeException(exc);
 659         }
 660         return output;
 661     }
 662 
 663     /**
 664      * Complete the currently running operation using any final
 665      * data provided by the caller.
 666      *
 667      * @param in the plaintext or ciphertext input bytes (depending on the
 668      *      operation type).
 669      * @param inOfs the offset into the input array
 670      * @param inLen the length of the data to use for the update operation.
 671      * @param out the byte array that will hold the resulting data.  The array
 672      *      must be large enough to hold the resulting data.
 673      * @param outOfs the offset for the {@code out} buffer to begin writing
 674      *      the resulting data.
 675      *
 676      * @return the length in bytes of the data written into the {@code out}
 677      *      buffer.
 678      *
 679      * @throws ShortBufferException if the buffer {@code out} does not have
 680      *      enough space to hold the resulting data.
 681      * @throws AEADBadTagException if, during decryption, the provided tag
 682      *      does not match the calculated tag.
 683      */
 684     @Override
 685     protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
 686             int outOfs) throws ShortBufferException, AEADBadTagException {
 687         int bytesUpdated = 0;
 688         try {
 689             bytesUpdated = engine.doFinal(in, inOfs, inLen, out, outOfs);
 690         } catch (KeyException ke) {
 691             throw new RuntimeException(ke);
 692         }
 693         return bytesUpdated;
 694     }
 695 
 696     /**
 697      * Wrap a {@code Key} using this Cipher's current encryption parameters.
 698      *
 699      * @param key the key to wrap.  The data that will be encrypted will
 700      *      be the provided {@code Key} in its encoded form.
 701      *
 702      * @return a byte array consisting of the wrapped key.
 703      *
 704      * @throws IllegalBlockSizeException
 705      * @throws InvalidKeyException if the provided {@code Key} does not
 706      *      have an encoded form.  This exception will also be thrown
 707      *      if an AEAD cipher is used to perform the wrap and it fails
 708      *      during tag calculation.
 709      */
 710     @Override
 711     protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
 712             InvalidKeyException {
 713         byte[] encoded = key.getEncoded();
 714         if ((encoded == null) || (encoded.length == 0)) {
 715             throw new InvalidKeyException("Could not obtain encoded key");
 716         }
 717 
 718         try {
 719             return engineDoFinal(encoded, 0, encoded.length);
 720         } catch (AEADBadTagException abte) {
 721             throw new InvalidKeyException(abte);
 722         }
 723     }
 724 
 725     /**
 726      * Unwrap a {@code Key} using this Cipher's current encryption parameters.
 727      *
 728      * @param wrappedKey the key to unwrap.
 729      * @param algorithm the algorithm associated with the wrapped key
 730      * @param type the type of the wrapped key. This is one of
 731      *      {@code SECRET_KEY}, {@code PRIVATE_KEY}, or {@code PUBLIC_KEY}.
 732      *
 733      * @return the unwrapped key as a {@code Key} object.
 734      *
 735      * @throws NoSuchAlgorithmException if no installed providers can create
 736      *      keys of type {@code type} for the algorithm specified in
 737      *      {@code algorithm}.
 738      * @throws InvalidKeyException if {@code wrappedKey} does not represent a
 739      *      wrapped key of type {@code type} for the algorithm specified in
 740      *      {@code algorithm}.  This exception will also be thrown if the
 741      *      provided tag does not match the calculated tag during unwrap.
 742      */
 743     @Override
 744     protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
 745             int type) throws InvalidKeyException, NoSuchAlgorithmException {
 746         try {
 747             byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
 748             return ConstructKeys.constructKey(encoded, algorithm, type);
 749         } catch (AEADBadTagException abte) {
 750             throw new InvalidKeyException(abte);
 751         }
 752     }
 753 
 754     /**
 755      * Get the length of a provided key in bits.
 756      *
 757      * @param key the key to be evaluated
 758      *
 759      * @return the length of the key in bits
 760      *
 761      * @throws InvalidKeyException if the key is invalid or does not
 762      *      have an encoded form.
 763      */
 764     @Override
 765     protected int engineGetKeySize(Key key) throws InvalidKeyException {
 766         byte[] encodedKey = getEncodedKey(key);
 767         return encodedKey.length << 3;
 768     }
 769 
 770     /**
 771      * Set the initial state.  This will populate the state array and put the
 772      * key and nonce into their proper locations.  The counter field is not
 773      * set here.
 774      *
 775      * @throws IllegalArgumentException if the key or nonce are not in
 776      *      their proper lengths (32 bytes for the key, 12 bytes for the
 777      *      nonce).
 778      * @throws InvalidKeyException if the key does not support an encoded form.
 779      */
 780     private void setInitialState() throws InvalidKeyException {
 781         // Apply constants to first 4 words
 782         startState[0] = STATE_CONST_0;
 783         startState[1] = STATE_CONST_1;
 784         startState[2] = STATE_CONST_2;
 785         startState[3] = STATE_CONST_3;
 786 
 787         // Apply the key bytes as 8 32-bit little endian ints (4 through 11)
 788         for (int i = 0; i < 32; i += 4) {
 789             startState[(i / 4) + 4] = (keyBytes[i] & 0x000000FF) |
 790                 ((keyBytes[i + 1] << 8) & 0x0000FF00) |
 791                 ((keyBytes[i + 2] << 16) & 0x00FF0000) |
 792                 ((keyBytes[i + 3] << 24) & 0xFF000000);
 793         }
 794 
 795         startState[12] = 0;
 796 
 797         // The final integers for the state are from the nonce
 798         // interpreted as 3 little endian integers
 799         for (int i = 0; i < 12; i += 4) {
 800             startState[(i / 4) + 13] = (nonce[i] & 0x000000FF) |
 801                 ((nonce[i + 1] << 8) & 0x0000FF00) |
 802                 ((nonce[i + 2] << 16) & 0x00FF0000) |
 803                 ((nonce[i + 3] << 24) & 0xFF000000);
 804         }
 805     }
 806 
 807     /**
 808      * Using the current state and counter create the next set of keystream
 809      * bytes.  This method will generate the next 512 bits of keystream and
 810      * return it in the {@code keyStream} parameter.  Following the
 811      * block function the counter will be incremented.
 812      */
 813     private void generateKeystream() {
 814         chaCha20Block(startState, counter, keyStream);
 815         counter++;
 816     }
 817 
 818     /**
 819      * Perform a full 20-round ChaCha20 transform on the initial state.
 820      *
 821      * @param initState the starting state, not including the counter
 822      *      value.
 823      * @param counter the counter value to apply
 824      * @param result  the array that will hold the result of the ChaCha20
 825      *      block function.
 826      *
 827      * @note it is the caller's responsibility to ensure that the workState
 828      * is sized the same as the initState, no checking is performed internally.
 829      */
 830     private static void chaCha20Block(int[] initState, long counter,
 831                                       byte[] result) {
 832         // Create an initial state and clone a working copy
 833         int ws00 = STATE_CONST_0;
 834         int ws01 = STATE_CONST_1;
 835         int ws02 = STATE_CONST_2;
 836         int ws03 = STATE_CONST_3;
 837         int ws04 = initState[4];
 838         int ws05 = initState[5];
 839         int ws06 = initState[6];
 840         int ws07 = initState[7];
 841         int ws08 = initState[8];
 842         int ws09 = initState[9];
 843         int ws10 = initState[10];
 844         int ws11 = initState[11];
 845         int ws12 = (int)counter;
 846         int ws13 = initState[13];
 847         int ws14 = initState[14];
 848         int ws15 = initState[15];
 849 
 850         // Peform 10 iterations of the 8 quarter round set
 851         for (int round = 0; round < 10; round++) {
 852             ws00 += ws04;
 853             ws12 = Integer.rotateLeft(ws12 ^ ws00, 16);
 854 
 855             ws08 += ws12;
 856             ws04 = Integer.rotateLeft(ws04 ^ ws08, 12);
 857 
 858             ws00 += ws04;
 859             ws12 = Integer.rotateLeft(ws12 ^ ws00, 8);
 860 
 861             ws08 += ws12;
 862             ws04 = Integer.rotateLeft(ws04 ^ ws08, 7);
 863 
 864             ws01 += ws05;
 865             ws13 = Integer.rotateLeft(ws13 ^ ws01, 16);
 866 
 867             ws09 += ws13;
 868             ws05 = Integer.rotateLeft(ws05 ^ ws09, 12);
 869 
 870             ws01 += ws05;
 871             ws13 = Integer.rotateLeft(ws13 ^ ws01, 8);
 872 
 873             ws09 += ws13;
 874             ws05 = Integer.rotateLeft(ws05 ^ ws09, 7);
 875 
 876             ws02 += ws06;
 877             ws14 = Integer.rotateLeft(ws14 ^ ws02, 16);
 878 
 879             ws10 += ws14;
 880             ws06 = Integer.rotateLeft(ws06 ^ ws10, 12);
 881 
 882             ws02 += ws06;
 883             ws14 = Integer.rotateLeft(ws14 ^ ws02, 8);
 884 
 885             ws10 += ws14;
 886             ws06 = Integer.rotateLeft(ws06 ^ ws10, 7);
 887 
 888             ws03 += ws07;
 889             ws15 = Integer.rotateLeft(ws15 ^ ws03, 16);
 890 
 891             ws11 += ws15;
 892             ws07 = Integer.rotateLeft(ws07 ^ ws11, 12);
 893 
 894             ws03 += ws07;
 895             ws15 = Integer.rotateLeft(ws15 ^ ws03, 8);
 896 
 897             ws11 += ws15;
 898             ws07 = Integer.rotateLeft(ws07 ^ ws11, 7);
 899 
 900             ws00 += ws05;
 901             ws15 = Integer.rotateLeft(ws15 ^ ws00, 16);
 902 
 903             ws10 += ws15;
 904             ws05 = Integer.rotateLeft(ws05 ^ ws10, 12);
 905 
 906             ws00 += ws05;
 907             ws15 = Integer.rotateLeft(ws15 ^ ws00, 8);
 908 
 909             ws10 += ws15;
 910             ws05 = Integer.rotateLeft(ws05 ^ ws10, 7);
 911 
 912             ws01 += ws06;
 913             ws12 = Integer.rotateLeft(ws12 ^ ws01, 16);
 914 
 915             ws11 += ws12;
 916             ws06 = Integer.rotateLeft(ws06 ^ ws11, 12);
 917 
 918             ws01 += ws06;
 919             ws12 = Integer.rotateLeft(ws12 ^ ws01, 8);
 920 
 921             ws11 += ws12;
 922             ws06 = Integer.rotateLeft(ws06 ^ ws11, 7);
 923 
 924             ws02 += ws07;
 925             ws13 = Integer.rotateLeft(ws13 ^ ws02, 16);
 926 
 927             ws08 += ws13;
 928             ws07 = Integer.rotateLeft(ws07 ^ ws08, 12);
 929 
 930             ws02 += ws07;
 931             ws13 = Integer.rotateLeft(ws13 ^ ws02, 8);
 932 
 933             ws08 += ws13;
 934             ws07 = Integer.rotateLeft(ws07 ^ ws08, 7);
 935 
 936             ws03 += ws04;
 937             ws14 = Integer.rotateLeft(ws14 ^ ws03, 16);
 938 
 939             ws09 += ws14;
 940             ws04 = Integer.rotateLeft(ws04 ^ ws09, 12);
 941 
 942             ws03 += ws04;
 943             ws14 = Integer.rotateLeft(ws14 ^ ws03, 8);
 944 
 945             ws09 += ws14;
 946             ws04 = Integer.rotateLeft(ws04 ^ ws09, 7);
 947         }
 948 
 949         // Add the end working state back into the original state
 950         asIntLittleEndian.set(result, 0, ws00 + STATE_CONST_0);
 951         asIntLittleEndian.set(result, 4, ws01 + STATE_CONST_1);
 952         asIntLittleEndian.set(result, 8, ws02 + STATE_CONST_2);
 953         asIntLittleEndian.set(result, 12, ws03 + STATE_CONST_3);
 954         asIntLittleEndian.set(result, 16, ws04 + initState[4]);
 955         asIntLittleEndian.set(result, 20, ws05 + initState[5]);
 956         asIntLittleEndian.set(result, 24, ws06 + initState[6]);
 957         asIntLittleEndian.set(result, 28, ws07 + initState[7]);
 958         asIntLittleEndian.set(result, 32, ws08 + initState[8]);
 959         asIntLittleEndian.set(result, 36, ws09 + initState[9]);
 960         asIntLittleEndian.set(result, 40, ws10 + initState[10]);
 961         asIntLittleEndian.set(result, 44, ws11 + initState[11]);
 962         // Add the counter back into workState[12]
 963         asIntLittleEndian.set(result, 48, ws12 + (int)counter);
 964         asIntLittleEndian.set(result, 52, ws13 + initState[13]);
 965         asIntLittleEndian.set(result, 56, ws14 + initState[14]);
 966         asIntLittleEndian.set(result, 60, ws15 + initState[15]);
 967     }
 968 
 969     /**
 970      * Perform the ChaCha20 transform.
 971      *
 972      * @param in the array of bytes for the input
 973      * @param inOff the offset into the input array to start the transform
 974      * @param inLen the length of the data to perform the transform on.
 975      * @param out the output array.  It must be large enough to hold the
 976      *      resulting data
 977      * @param outOff the offset into the output array to place the resulting
 978      *      data.
 979      */
 980     private void chaCha20Transform(byte[] in, int inOff, int inLen,
 981             byte[] out, int outOff) throws KeyException {
 982         int remainingData = inLen;
 983 
 984         while (remainingData > 0) {
 985             int ksRemain = keyStream.length - keyStrOffset;
 986             if (ksRemain <= 0) {
 987                 if (counter <= finalCounterValue) {
 988                     generateKeystream();
 989                     keyStrOffset = 0;
 990                     ksRemain = keyStream.length;
 991                 } else {
 992                     throw new KeyException("Counter exhausted.  " +
 993                             "Reinitialize with new key and/or nonce");
 994                 }
 995             }
 996 
 997             // XOR each byte in the keystream against the input
 998             int xformLen = Math.min(remainingData, ksRemain);
 999             xor(keyStream, keyStrOffset, in, inOff, out, outOff, xformLen);
1000             outOff += xformLen;
1001             inOff += xformLen;
1002             keyStrOffset += xformLen;
1003             remainingData -= xformLen;
1004         }
1005     }
1006 
1007     private static void xor(byte[] in1, int off1, byte[] in2, int off2,
1008             byte[] out, int outOff, int len) {
1009         while (len >= 8) {
1010             long v1 = (long) asLongView.get(in1, off1);
1011             long v2 = (long) asLongView.get(in2, off2);
1012             asLongView.set(out, outOff, v1 ^ v2);
1013             off1 += 8;
1014             off2 += 8;
1015             outOff += 8;
1016             len -= 8;
1017         }
1018         while (len > 0) {
1019             out[outOff] = (byte) (in1[off1] ^ in2[off2]);
1020             off1++;
1021             off2++;
1022             outOff++;
1023             len--;
1024         }
1025     }
1026 
1027     /**
1028      * Perform initialization steps for the authenticator
1029      *
1030      * @throws InvalidKeyException if the key is unusable for some reason
1031      *      (invalid length, etc.)
1032      */
1033     private void initAuthenticator() throws InvalidKeyException {
1034         try {
1035             authenticator = new Poly1305();
1036 
1037             // Derive the Poly1305 key from the starting state
1038             byte[] serializedKey = new byte[KEYSTREAM_SIZE];
1039             chaCha20Block(startState, 0, serializedKey);
1040 
1041             authenticator.engineInit(new SecretKeySpec(serializedKey, 0, 32,
1042                     authAlgName), null);
1043             aadLen = 0;
1044             dataLen = 0;
1045         } catch (GeneralSecurityException gse) {
1046             throw new InvalidKeyException(gse);
1047         }
1048     }
1049 
1050     /**
1051      * Update the authenticator state with data.  This routine can be used
1052      * to add data to the authenticator, whether AAD or application data.
1053      *
1054      * @param data the data to stir into the authenticator.
1055      * @param offset the offset into the data.
1056      * @param length the length of data to add to the authenticator.
1057      *
1058      * @return the number of bytes processed by this method.
1059      */
1060     private int authUpdate(byte[] data, int offset, int length) {
1061         Objects.checkFromIndexSize(offset, length, data.length);
1062         authenticator.engineUpdate(data, offset, length);
1063         return length;
1064     }
1065 
1066     /**
1067      * Finalize the data and return the tag.
1068      *
1069      * @param data an array containing any remaining data to process.
1070      * @param dataOff the offset into the data.
1071      * @param length the length of the data to process.
1072      * @param out the array to write the resulting tag into
1073      * @param outOff the offset to begin writing the data.
1074      *
1075      * @throws ShortBufferException if there is insufficient room to
1076      *      write the tag.
1077      */
1078     private void authFinalizeData(byte[] data, int dataOff, int length,
1079             byte[] out, int outOff) throws ShortBufferException {
1080         // Update with the final chunk of ciphertext, then pad to a
1081         // multiple of 16.
1082         if (data != null) {
1083             dataLen += authUpdate(data, dataOff, length);
1084         }
1085         authPad16(dataLen);
1086 
1087         // Also write the AAD and ciphertext data lengths as little-endian
1088         // 64-bit values.
1089         authWriteLengths(aadLen, dataLen, lenBuf);
1090         authenticator.engineUpdate(lenBuf, 0, lenBuf.length);
1091         byte[] tag = authenticator.engineDoFinal();
1092         Objects.checkFromIndexSize(outOff, tag.length, out.length);
1093         System.arraycopy(tag, 0, out, outOff, tag.length);
1094     }
1095 
1096     /**
1097      * Based on a given length of data, make the authenticator process
1098      * zero bytes that will pad the length out to a multiple of 16.
1099      *
1100      * @param dataLen the starting length to be padded.
1101      */
1102     private void authPad16(long dataLen) {
1103         // Pad out the AAD or data to a multiple of 16 bytes
1104         authenticator.engineUpdate(padBuf, 0,
1105                 (TAG_LENGTH - ((int)dataLen & 15)) & 15);
1106     }
1107 
1108     /**
1109      * Write the two 64-bit little-endian length fields into an array
1110      * for processing by the poly1305 authenticator.
1111      *
1112      * @param aLen the length of the AAD.
1113      * @param dLen the length of the application data.
1114      * @param buf the buffer to write the two lengths into.
1115      *
1116      * @note it is the caller's responsibility to provide an array large
1117      *      enough to hold the two longs.
1118      */
1119     private void authWriteLengths(long aLen, long dLen, byte[] buf) {
1120         asLongLittleEndian.set(buf, 0, aLen);
1121         asLongLittleEndian.set(buf, Long.BYTES, dLen);
1122     }
1123 
1124     // TODO: eliminate when done
1125     private static String dumpHexBytes(byte[] data, int itemsPerLine,
1126             String lineDelim, String itemDelim) {
1127         return dumpHexBytes(data, 0, data.length, itemsPerLine, lineDelim,
1128                 itemDelim);
1129     }
1130 
1131     private static String dumpHexBytes(byte[] data, int offset, int length,
1132             int itemsPerLine, String lineDelim, String itemDelim) {
1133         StringBuilder sb = new StringBuilder();
1134         if (data != null) {
1135             for (int i = 0; i < length; i++) {
1136                 if (i % itemsPerLine == 0 && i != 0) {
1137                     sb.append(lineDelim);
1138                 }
1139                 sb.append(String.format("%02X",
1140                         data[i + offset])).append(itemDelim);
1141             }
1142         }
1143 
1144         return sb.toString();
1145     }
1146 
1147     private static String dumpState(int[] state) {
1148         StringBuilder sb = new StringBuilder();
1149         if (state != null) {
1150             for (int i = 0; i < state.length; i++) {
1151                 if (i % 4 == 0 && i != 0) {
1152                     sb.append("\n");
1153                 }
1154                 sb.append(String.format("%08x ", state[i]));
1155             }
1156         }
1157         return sb.toString();
1158     }
1159 
1160     /**
1161      * Interface for the underlying processing engines for ChaCha20
1162      */
1163     interface ChaChaEngine {
1164         /**
1165          * Perform a multi-part update for ChaCha20.
1166          *
1167          * @param in the input data.
1168          * @param inOff the offset into the input.
1169          * @param inLen the length of the data to process.
1170          * @param out the output buffer.
1171          * @param outOff the offset at which to write the output data.
1172          *
1173          * @return the number of output bytes written.
1174          *
1175          * @throws ShortBufferException if the output buffer does not
1176          *      provide enough space.
1177          * @throws KeyException if the counter value has been exhausted.
1178          */
1179         int doUpdate(byte[] in, int inOff, int inLen, byte[] out, int outOff)
1180                 throws ShortBufferException, KeyException;
1181 
1182         /**
1183          * Finalize a multi-part or single-part ChaCha20 operation.
1184          *
1185          * @param in the input data.
1186          * @param inOff the offset into the input.
1187          * @param inLen the length of the data to process.
1188          * @param out the output buffer.
1189          * @param outOff the offset at which to write the output data.
1190          *
1191          * @return the number of output bytes written.
1192          *
1193          * @throws ShortBufferException if the output buffer does not
1194          *      provide enough space.
1195          * @throws AEADBadTagException if in decryption mode the provided
1196          *      tag and calculated tag do not match.
1197          * @throws KeyException if the counter value has been exhausted.
1198          */
1199         int doFinal(byte[] in, int inOff, int inLen, byte[] out, int outOff)
1200                 throws ShortBufferException, AEADBadTagException, KeyException;
1201     }
1202 
1203     private final class EngineStreamOnly implements ChaChaEngine {
1204 
1205         private EngineStreamOnly () { }
1206 
1207         @Override
1208         public int doUpdate(byte[] in, int inOff, int inLen, byte[] out,
1209                 int outOff) throws ShortBufferException, KeyException {
1210             if (in != null) {
1211                 Objects.checkFromIndexSize(inOff, inLen, in.length);
1212                 Objects.checkFromIndexSize(outOff, inLen, out.length);
1213                 chaCha20Transform(in, inOff, inLen, out, outOff);
1214             }
1215             return inLen;
1216         }
1217 
1218         @Override
1219         public int doFinal(byte[] in, int inOff, int inLen, byte[] out,
1220                 int outOff) throws ShortBufferException, KeyException {
1221             return doUpdate(in, inOff, inLen, out, outOff);
1222         }
1223     }
1224 
1225     private final class EngineAEADEnc implements ChaChaEngine {
1226 
1227         private EngineAEADEnc() throws InvalidKeyException {
1228             initAuthenticator();
1229             counter = 1;
1230         }
1231 
1232         @Override
1233         public int doUpdate(byte[] in, int inOff, int inLen, byte[] out,
1234                 int outOff) throws ShortBufferException, KeyException {
1235             // If this is the first update since AAD updates, signal that
1236             // we're done processing AAD info and pad the AAD to a multiple
1237             // of 16 bytes.
1238             if (!aadDone) {
1239                 authPad16(aadLen);
1240                 aadDone = true;
1241             }
1242 
1243             if (in != null) {
1244                 Objects.checkFromIndexSize(inOff, inLen, in.length);
1245                 Objects.checkFromIndexSize(outOff, inLen, out.length);
1246                 chaCha20Transform(in, inOff, inLen, out, outOff);
1247                 dataLen += authUpdate(out, outOff, inLen);
1248             }
1249             return inLen;
1250         }
1251 
1252         @Override
1253         public int doFinal(byte[] in, int inOff, int inLen, byte[] out,
1254                 int outOff) throws ShortBufferException, KeyException {
1255             // Make sure we have enough room for the remaining data (if any)
1256             // and the tag.
1257             if ((inLen + TAG_LENGTH) > (out.length - outOff)) {
1258                 throw new ShortBufferException("Output buffer too small");
1259             }
1260 
1261             doUpdate(in, inOff, inLen, out, outOff);
1262             authFinalizeData(null, 0, 0, out, outOff + inLen);
1263             aadDone = false;
1264             return inLen + TAG_LENGTH;
1265         }
1266     }
1267 
1268     private final class EngineAEADDec implements ChaChaEngine {
1269 
1270         private final ByteArrayOutputStream cipherBuf;
1271         private final byte[] tag;
1272 
1273         private EngineAEADDec() throws InvalidKeyException {
1274             initAuthenticator();
1275             counter = 1;
1276             cipherBuf = new ByteArrayOutputStream(CIPHERBUF_BASE);
1277             tag = new byte[TAG_LENGTH];
1278         }
1279 
1280         @Override
1281         public int doUpdate(byte[] in, int inOff, int inLen, byte[] out,
1282                 int outOff) {
1283             // If this is the first update since AAD updates, signal that
1284             // we're done processing AAD info and pad the AAD to a multiple
1285             // of 16 bytes.
1286             if (!aadDone) {
1287                 authPad16(aadLen);
1288                 aadDone = true;
1289             }
1290 
1291             if (in != null) {
1292                 Objects.checkFromIndexSize(inOff, inLen, in.length);
1293                 cipherBuf.write(in, inOff, inLen);
1294             }
1295 
1296             return 0;
1297         }
1298 
1299         @Override
1300         public int doFinal(byte[] in, int inOff, int inLen, byte[] out,
1301                 int outOff) throws ShortBufferException, AEADBadTagException,
1302                 KeyException {
1303 
1304             doUpdate(in, inOff, inLen, out, outOff);
1305             byte[] ctPlusTag = cipherBuf.toByteArray();
1306 
1307             // There must at least be a tag length's worth of ciphertext
1308             // data in the buffered input.
1309             if (ctPlusTag.length < TAG_LENGTH) {
1310                 throw new AEADBadTagException("Input too short - need tag");
1311             }
1312             int ctLen = ctPlusTag.length - TAG_LENGTH;
1313 
1314             // Make sure we will have enough room for the output buffer
1315             try {
1316                 Objects.checkFromIndexSize(outOff, ctLen, out.length);
1317             } catch (IndexOutOfBoundsException ioobe) {
1318                 throw new ShortBufferException("Output buffer too small");
1319             }
1320 
1321             // Calculate and compare the tag.  Only do the decryption
1322             // if and only if the tag matches.
1323             authFinalizeData(ctPlusTag, 0, ctLen, tag, 0);
1324             if (Arrays.compare(ctPlusTag, ctLen, ctPlusTag.length,
1325                     tag, 0, tag.length) != 0) {
1326                 throw new AEADBadTagException("Tag mismatch");
1327             }
1328             chaCha20Transform(ctPlusTag, 0, ctLen, out, outOff);
1329             aadDone = false;
1330 
1331             return ctLen;
1332         }
1333     }
1334 
1335     public static final class ChaCha20Only extends ChaCha20Cipher {
1336         public ChaCha20Only() {
1337             mode = MODE_NONE;
1338         }
1339     }
1340 
1341     public static final class ChaCha20Poly1305 extends ChaCha20Cipher {
1342         public ChaCha20Poly1305() {
1343             mode = MODE_AEAD;
1344             authAlgName = "Poly1305";
1345         }
1346     }
1347 }