Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
38922 views
/*1* Copyright (c) 2013, 2019, 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 java.util.Arrays;28import java.io.*;29import java.security.*;30import javax.crypto.*;31import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;323334/**35* This class represents ciphers in GaloisCounter (GCM) mode.36*37* <p>This mode currently should only be used w/ AES cipher.38* Although no checking is done, caller should only pass AES39* Cipher to the constructor.40*41* <p>NOTE: Unlike other modes, when used for decryption, this class42* will buffer all processed outputs internally and won't return them43* until the tag has been successfully verified.44*45* @since 1.846*/47final class GaloisCounterMode extends FeedbackCipher {4849static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;50static int DEFAULT_IV_LEN = 12; // in bytes5152// In NIST SP 800-38D, GCM input size is limited to be no longer53// than (2^36 - 32) bytes. Otherwise, the counter will wrap54// around and lead to a leak of plaintext.55// However, given the current GCM spec requirement that recovered56// text can only be returned after successful tag verification,57// we are bound by limiting the data size to the size limit of58// java byte array, e.g. Integer.MAX_VALUE, since all data59// can only be returned by the doFinal(...) call.60private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;6162// data size when buffer is divided up to aid in intrinsics63private static final int TRIGGERLEN = 65536; // 64k6465// buffer for AAD data; if null, meaning update has been called66private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();67private int sizeOfAAD = 0;6869// buffer for storing input in decryption, not used for encryption70private ByteArrayOutputStream ibuffer = null;7172// in bytes; need to convert to bits (default value 128) when needed73private int tagLenBytes = DEFAULT_TAG_LEN;7475// these following 2 fields can only be initialized after init() is76// called, e.g. after cipher key k is set, and STAY UNCHANGED77private byte[] subkeyH = null;78private byte[] preCounterBlock = null;7980private GCTR gctrPAndC = null;81private GHASH ghashAllToS = null;8283// length of total data, i.e. len(C)84private int processed = 0;8586// additional variables for save/restore calls87private byte[] aadBufferSave = null;88private int sizeOfAADSave = 0;89private byte[] ibufferSave = null;90private int processedSave = 0;9192// value must be 16-byte long; used by GCTR and GHASH as well93static void increment32(byte[] value) {94if (value.length != AES_BLOCK_SIZE) {95// should never happen96throw new ProviderException("Illegal counter block length");97}98// start from last byte and only go over 4 bytes, i.e. total 32 bits99int n = value.length - 1;100while ((n >= value.length - 4) && (++value[n] == 0)) {101n--;102}103}104105private static byte[] getLengthBlock(int ivLenInBytes) {106long ivLen = ((long)ivLenInBytes) << 3;107byte[] out = new byte[AES_BLOCK_SIZE];108out[8] = (byte)(ivLen >>> 56);109out[9] = (byte)(ivLen >>> 48);110out[10] = (byte)(ivLen >>> 40);111out[11] = (byte)(ivLen >>> 32);112out[12] = (byte)(ivLen >>> 24);113out[13] = (byte)(ivLen >>> 16);114out[14] = (byte)(ivLen >>> 8);115out[15] = (byte)ivLen;116return out;117}118119private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {120long aLen = ((long)aLenInBytes) << 3;121long cLen = ((long)cLenInBytes) << 3;122byte[] out = new byte[AES_BLOCK_SIZE];123out[0] = (byte)(aLen >>> 56);124out[1] = (byte)(aLen >>> 48);125out[2] = (byte)(aLen >>> 40);126out[3] = (byte)(aLen >>> 32);127out[4] = (byte)(aLen >>> 24);128out[5] = (byte)(aLen >>> 16);129out[6] = (byte)(aLen >>> 8);130out[7] = (byte)aLen;131out[8] = (byte)(cLen >>> 56);132out[9] = (byte)(cLen >>> 48);133out[10] = (byte)(cLen >>> 40);134out[11] = (byte)(cLen >>> 32);135out[12] = (byte)(cLen >>> 24);136out[13] = (byte)(cLen >>> 16);137out[14] = (byte)(cLen >>> 8);138out[15] = (byte)cLen;139return out;140}141142private static byte[] expandToOneBlock(byte[] in, int inOfs, int len) {143if (len > AES_BLOCK_SIZE) {144throw new ProviderException("input " + len + " too long");145}146if (len == AES_BLOCK_SIZE && inOfs == 0) {147return in;148} else {149byte[] paddedIn = new byte[AES_BLOCK_SIZE];150System.arraycopy(in, inOfs, paddedIn, 0, len);151return paddedIn;152}153}154155private static byte[] getJ0(byte[] iv, byte[] subkeyH) {156byte[] j0;157if (iv.length == 12) { // 96 bits158j0 = expandToOneBlock(iv, 0, iv.length);159j0[AES_BLOCK_SIZE - 1] = 1;160} else {161GHASH g = new GHASH(subkeyH);162int lastLen = iv.length % AES_BLOCK_SIZE;163if (lastLen != 0) {164g.update(iv, 0, iv.length - lastLen);165byte[] padded =166expandToOneBlock(iv, iv.length - lastLen, lastLen);167g.update(padded);168} else {169g.update(iv);170}171byte[] lengthBlock = getLengthBlock(iv.length);172g.update(lengthBlock);173j0 = g.digest();174}175return j0;176}177178private static void checkDataLength(int processed, int len) {179if (processed > MAX_BUF_SIZE - len) {180throw new ProviderException("SunJCE provider only supports " +181"input size up to " + MAX_BUF_SIZE + " bytes");182}183}184185GaloisCounterMode(SymmetricCipher embeddedCipher) {186super(embeddedCipher);187aadBuffer = new ByteArrayOutputStream();188}189190/**191* Gets the name of the feedback mechanism192*193* @return the name of the feedback mechanism194*/195String getFeedback() {196return "GCM";197}198199/**200* Resets the cipher object to its original state.201* This is used when doFinal is called in the Cipher class, so that the202* cipher can be reused (with its original key and iv).203*/204void reset() {205if (aadBuffer == null) {206aadBuffer = new ByteArrayOutputStream();207} else {208aadBuffer.reset();209}210if (gctrPAndC != null) gctrPAndC.reset();211if (ghashAllToS != null) ghashAllToS.reset();212processed = 0;213sizeOfAAD = 0;214if (ibuffer != null) {215ibuffer.reset();216}217}218219/**220* Save the current content of this cipher.221*/222void save() {223processedSave = processed;224sizeOfAADSave = sizeOfAAD;225aadBufferSave =226((aadBuffer == null || aadBuffer.size() == 0)?227null : aadBuffer.toByteArray());228if (gctrPAndC != null) gctrPAndC.save();229if (ghashAllToS != null) ghashAllToS.save();230if (ibuffer != null) {231ibufferSave = ibuffer.toByteArray();232}233}234235/**236* Restores the content of this cipher to the previous saved one.237*/238void restore() {239processed = processedSave;240sizeOfAAD = sizeOfAADSave;241if (aadBuffer != null) {242aadBuffer.reset();243if (aadBufferSave != null) {244aadBuffer.write(aadBufferSave, 0, aadBufferSave.length);245}246}247if (gctrPAndC != null) gctrPAndC.restore();248if (ghashAllToS != null) ghashAllToS.restore();249if (ibuffer != null) {250ibuffer.reset();251ibuffer.write(ibufferSave, 0, ibufferSave.length);252}253}254255/**256* Initializes the cipher in the specified mode with the given key257* and iv.258*259* @param decrypting flag indicating encryption or decryption260* @param algorithm the algorithm name261* @param key the key262* @param iv the iv263* @param tagLenBytes the length of tag in bytes264*265* @exception InvalidKeyException if the given key is inappropriate for266* initializing this cipher267*/268@Override269void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)270throws InvalidKeyException, InvalidAlgorithmParameterException {271init(decrypting, algorithm, key, iv, DEFAULT_TAG_LEN);272}273274/**275* Initializes the cipher in the specified mode with the given key276* and iv.277*278* @param decrypting flag indicating encryption or decryption279* @param algorithm the algorithm name280* @param key the key281* @param iv the iv282* @param tagLenBytes the length of tag in bytes283*284* @exception InvalidKeyException if the given key is inappropriate for285* initializing this cipher286*/287void init(boolean decrypting, String algorithm, byte[] keyValue,288byte[] ivValue, int tagLenBytes)289throws InvalidKeyException, InvalidAlgorithmParameterException {290if (keyValue == null) {291throw new InvalidKeyException("Internal error");292}293if (ivValue == null) {294throw new InvalidAlgorithmParameterException("Internal error");295}296if (ivValue.length == 0) {297throw new InvalidAlgorithmParameterException("IV is empty");298}299300// always encrypt mode for embedded cipher301this.embeddedCipher.init(false, algorithm, keyValue);302this.subkeyH = new byte[AES_BLOCK_SIZE];303this.embeddedCipher.encryptBlock(new byte[AES_BLOCK_SIZE], 0,304this.subkeyH, 0);305306this.iv = ivValue.clone();307preCounterBlock = getJ0(iv, subkeyH);308byte[] j0Plus1 = preCounterBlock.clone();309increment32(j0Plus1);310gctrPAndC = new GCTR(embeddedCipher, j0Plus1);311ghashAllToS = new GHASH(subkeyH);312313this.tagLenBytes = tagLenBytes;314if (aadBuffer == null) {315aadBuffer = new ByteArrayOutputStream();316} else {317aadBuffer.reset();318}319processed = 0;320sizeOfAAD = 0;321if (decrypting) {322ibuffer = new ByteArrayOutputStream();323}324}325326/**327* Continues a multi-part update of the Additional Authentication328* Data (AAD), using a subset of the provided buffer. If this329* cipher is operating in either GCM or CCM mode, all AAD must be330* supplied before beginning operations on the ciphertext (via the331* {@code update} and {@code doFinal} methods).332* <p>333* NOTE: Given most modes do not accept AAD, default impl for this334* method throws IllegalStateException.335*336* @param src the buffer containing the AAD337* @param offset the offset in {@code src} where the AAD input starts338* @param len the number of AAD bytes339*340* @throws IllegalStateException if this cipher is in a wrong state341* (e.g., has not been initialized), does not accept AAD, or if342* operating in either GCM or CCM mode and one of the {@code update}343* methods has already been called for the active344* encryption/decryption operation345* @throws UnsupportedOperationException if this method346* has not been overridden by an implementation347*348* @since 1.8349*/350void updateAAD(byte[] src, int offset, int len) {351if (aadBuffer != null) {352aadBuffer.write(src, offset, len);353} else {354// update has already been called355throw new IllegalStateException356("Update has been called; no more AAD data");357}358}359360// Feed the AAD data to GHASH, pad if necessary361void processAAD() {362if (aadBuffer != null) {363if (aadBuffer.size() > 0) {364byte[] aad = aadBuffer.toByteArray();365sizeOfAAD = aad.length;366367int lastLen = aad.length % AES_BLOCK_SIZE;368if (lastLen != 0) {369ghashAllToS.update(aad, 0, aad.length - lastLen);370byte[] padded = expandToOneBlock(aad, aad.length - lastLen,371lastLen);372ghashAllToS.update(padded);373} else {374ghashAllToS.update(aad);375}376}377aadBuffer = null;378}379}380381// Utility to process the last block; used by encryptFinal and decryptFinal382void doLastBlock(byte[] in, int inOfs, int len, byte[] out, int outOfs,383boolean isEncrypt) throws IllegalBlockSizeException {384byte[] ct;385int ctOfs;386int ilen = len; // internal length387388if (isEncrypt) {389ct = out;390ctOfs = outOfs;391} else {392ct = in;393ctOfs = inOfs;394}395396// Divide up larger data sizes to trigger CTR & GHASH intrinsic quicker397if (len > TRIGGERLEN) {398int i = 0;399int tlen; // incremental lengths400final int plen = AES_BLOCK_SIZE * 6;401// arbitrary formula to aid intrinsic without reaching buffer end402final int count = len / 1024;403404while (count > i) {405tlen = gctrPAndC.update(in, inOfs, plen, out, outOfs);406ghashAllToS.update(ct, ctOfs, tlen);407inOfs += tlen;408outOfs += tlen;409ctOfs += tlen;410i++;411}412ilen -= count * plen;413processed += count * plen;414}415416gctrPAndC.doFinal(in, inOfs, ilen, out, outOfs);417processed += ilen;418419int lastLen = ilen % AES_BLOCK_SIZE;420if (lastLen != 0) {421ghashAllToS.update(ct, ctOfs, ilen - lastLen);422ghashAllToS.update(423expandToOneBlock(ct, (ctOfs + ilen - lastLen), lastLen));424} else {425ghashAllToS.update(ct, ctOfs, ilen);426}427}428429430/**431* Performs encryption operation.432*433* <p>The input plain text <code>in</code>, starting at <code>inOfs</code>434* and ending at <code>(inOfs + len - 1)</code>, is encrypted. The result435* is stored in <code>out</code>, starting at <code>outOfs</code>.436*437* @param in the buffer with the input data to be encrypted438* @param inOfs the offset in <code>in</code>439* @param len the length of the input data440* @param out the buffer for the result441* @param outOfs the offset in <code>out</code>442* @exception ProviderException if <code>len</code> is not443* a multiple of the block size444* @return the number of bytes placed into the <code>out</code> buffer445*/446int encrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {447checkDataLength(processed, len);448449RangeUtil.blockSizeCheck(len, blockSize);450processAAD();451452if (len > 0) {453RangeUtil.nullAndBoundsCheck(in, inOfs, len);454RangeUtil.nullAndBoundsCheck(out, outOfs, len);455456gctrPAndC.update(in, inOfs, len, out, outOfs);457processed += len;458ghashAllToS.update(out, outOfs, len);459}460461return len;462}463464/**465* Performs encryption operation for the last time.466*467* @param in the input buffer with the data to be encrypted468* @param inOfs the offset in <code>in</code>469* @param len the length of the input data470* @param out the buffer for the encryption result471* @param outOfs the offset in <code>out</code>472* @return the number of bytes placed into the <code>out</code> buffer473*/474int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)475throws IllegalBlockSizeException, ShortBufferException {476if (len > MAX_BUF_SIZE - tagLenBytes) {477throw new ShortBufferException478("Can't fit both data and tag into one buffer");479}480try {481RangeUtil.nullAndBoundsCheck(out, outOfs,482(len + tagLenBytes));483} catch (ArrayIndexOutOfBoundsException aiobe) {484throw new ShortBufferException("Output buffer too small");485}486487checkDataLength(processed, len);488489processAAD();490if (len > 0) {491RangeUtil.nullAndBoundsCheck(in, inOfs, len);492493doLastBlock(in, inOfs, len, out, outOfs, true);494}495496byte[] lengthBlock =497getLengthBlock(sizeOfAAD, processed);498ghashAllToS.update(lengthBlock);499byte[] s = ghashAllToS.digest();500byte[] sOut = new byte[s.length];501GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);502gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);503504System.arraycopy(sOut, 0, out, (outOfs + len), tagLenBytes);505return (len + tagLenBytes);506}507508/**509* Performs decryption operation.510*511* <p>The input cipher text <code>in</code>, starting at512* <code>inOfs</code> and ending at <code>(inOfs + len - 1)</code>,513* is decrypted. The result is stored in <code>out</code>, starting at514* <code>outOfs</code>.515*516* @param in the buffer with the input data to be decrypted517* @param inOfs the offset in <code>in</code>518* @param len the length of the input data519* @param out the buffer for the result520* @param outOfs the offset in <code>out</code>521* @exception ProviderException if <code>len</code> is not522* a multiple of the block size523* @return the number of bytes placed into the <code>out</code> buffer524*/525int decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {526checkDataLength(ibuffer.size(), len);527528RangeUtil.blockSizeCheck(len, blockSize);529processAAD();530531if (len > 0) {532// store internally until decryptFinal is called because533// spec mentioned that only return recovered data after tag534// is successfully verified535RangeUtil.nullAndBoundsCheck(in, inOfs, len);536ibuffer.write(in, inOfs, len);537}538return 0;539}540541/**542* Performs decryption operation for the last time.543*544* <p>NOTE: For cipher feedback modes which does not perform545* special handling for the last few blocks, this is essentially546* the same as <code>encrypt(...)</code>. Given most modes do547* not do special handling, the default impl for this method is548* to simply call <code>decrypt(...)</code>.549*550* @param in the input buffer with the data to be decrypted551* @param inOfs the offset in <code>cipher</code>552* @param len the length of the input data553* @param out the buffer for the decryption result554* @param outOfs the offset in <code>plain</code>555* @return the number of bytes placed into the <code>out</code> buffer556*/557int decryptFinal(byte[] in, int inOfs, int len,558byte[] out, int outOfs)559throws IllegalBlockSizeException, AEADBadTagException,560ShortBufferException {561if (len < tagLenBytes) {562throw new AEADBadTagException("Input too short - need tag");563}564565// do this check here can also catch the potential integer overflow566// scenario for the subsequent output buffer capacity check.567checkDataLength(ibuffer.size(), (len - tagLenBytes));568569try {570RangeUtil.nullAndBoundsCheck(out, outOfs,571(ibuffer.size() + len) - tagLenBytes);572} catch (ArrayIndexOutOfBoundsException aiobe) {573throw new ShortBufferException("Output buffer too small");574}575576processAAD();577578RangeUtil.nullAndBoundsCheck(in, inOfs, len);579580// get the trailing tag bytes from 'in'581byte[] tag = new byte[tagLenBytes];582System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);583len -= tagLenBytes;584585// If decryption is in-place or there is buffered "ibuffer" data, copy586// the "in" byte array into the ibuffer before proceeding.587if (in == out || ibuffer.size() > 0) {588if (len > 0) {589ibuffer.write(in, inOfs, len);590}591592// refresh 'in' to all buffered-up bytes593in = ibuffer.toByteArray();594inOfs = 0;595len = in.length;596ibuffer.reset();597}598599if (len > 0) {600doLastBlock(in, inOfs, len, out, outOfs, false);601}602603byte[] lengthBlock =604getLengthBlock(sizeOfAAD, processed);605ghashAllToS.update(lengthBlock);606607byte[] s = ghashAllToS.digest();608byte[] sOut = new byte[s.length];609GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);610gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);611612// check entire authentication tag for time-consistency613int mismatch = 0;614for (int i = 0; i < tagLenBytes; i++) {615mismatch |= tag[i] ^ sOut[i];616}617618if (mismatch != 0) {619throw new AEADBadTagException("Tag mismatch!");620}621622return len;623}624625// return tag length in bytes626int getTagLen() {627return this.tagLenBytes;628}629630int getBufferedLength() {631if (ibuffer == null) {632return 0;633} else {634return ibuffer.size();635}636}637}638639640