/* * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * 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 * by Oracle in the LICENSE file that accompanied this code. * * 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. */ /* * (C) Copyright IBM Corp. 2013 */ package com.sun.crypto.provider; import java.security.*; import javax.crypto.*; import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; /** * This class represents the GCTR function defined in NIST 800-38D * under section 6.5. It needs to be constructed w/ an initialized * cipher object, and initial counter block(ICB). Given an input X * of arbitrary length, it processes and returns an output which has * the same length as X. * * <p>This function is used in the implementation of GCM mode. * * @since 1.8 */ final class GCTR { // these fields should not change after the object has been constructed private final SymmetricCipher aes; private final byte[] icb; // the current counter value private byte[] counter; // needed for save/restore calls private byte[] counterSave; // NOTE: cipher should already be initialized GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) { this.aes = cipher; this.icb = initialCounterBlk; this.counter = icb.clone(); } // input must be multiples of 128-bit blocks when calling update int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { if (inLen - inOfs > in.length) { throw new RuntimeException("input length out of bound"); } if (inLen < 0 || inLen % AES_BLOCK_SIZE != 0) { throw new RuntimeException("input length unsupported"); } if (out.length - outOfs < inLen) { throw new RuntimeException("output buffer too small"); } byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; for (int i = 0; i < numOfCompleteBlocks; i++) { aes.encryptBlock(counter, 0, encryptedCntr, 0); for (int n = 0; n < AES_BLOCK_SIZE; n++) { int index = (i * AES_BLOCK_SIZE + n); out[outOfs + index] = (byte) ((in[inOfs + index] ^ encryptedCntr[n])); } GaloisCounterMode.increment32(counter); } return inLen; } // input can be arbitrary size when calling doFinal protected int doFinal(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) throws IllegalBlockSizeException { try { if (inLen < 0) { throw new IllegalBlockSizeException("Negative input size!"); } else if (inLen > 0) { int lastBlockSize = inLen % AES_BLOCK_SIZE; // process the complete blocks first update(in, inOfs, inLen - lastBlockSize, out, outOfs); if (lastBlockSize != 0) { // do the last partial block byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; aes.encryptBlock(counter, 0, encryptedCntr, 0); int processed = inLen - lastBlockSize; for (int n = 0; n < lastBlockSize; n++) { out[outOfs + processed + n] = (byte) ((in[inOfs + processed + n] ^ encryptedCntr[n])); } } } } finally { reset(); } return inLen; } /** * Resets the current counter to its initial value. * This is used after the doFinal() is called so this object can be * reused w/o explicit re-initialization. */ void reset() { System.arraycopy(icb, 0, counter, 0, icb.length); } /** * Save the current content of this object. */ void save() { this.counterSave = this.counter.clone(); } /** * Restores the content of this object to the previous saved one. */ void restore() { this.counter = this.counterSave; } }