Path: blob/master/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java
67773 views
/*1* Copyright (c) 2013, 2020, 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*/2425/*26* (C) Copyright IBM Corp. 201327*/2829package com.sun.crypto.provider;3031import java.nio.ByteBuffer;32import java.nio.ByteOrder;33import java.util.Arrays;3435/**36* This class represents the GCTR function defined in NIST 800-38D37* under section 6.5. With a given cipher object and initial counter38* block, a counter mode operation is performed. Blocksize is limited39* to 16 bytes.40*41* If any invariant is broken, failures can occur because the42* AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM43* (see JDK-8067648 for details).44*45* The counter mode operations can be intrinsified and parallelized46* by using CounterMode.implCrypt() if HotSpot VM supports it on the47* architecture.48*49* <p>This function is used in the implementation of GCM mode.50*51* @since 1.852*/53final class GCTR extends CounterMode implements GCM {5455// Maximum buffer size rotating ByteBuffer->byte[] intrinsic copy56private static final int MAX_LEN = 1024;57private byte[] block;5859GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {60super(cipher);61if (initialCounterBlk.length != blockSize) {62throw new RuntimeException("length of initial counter block (" +63initialCounterBlk.length + ") not equal to blockSize (" +64blockSize + ")");65}6667iv = initialCounterBlk;68reset();69}7071@Override72String getFeedback() {73return "GCTR";74}7576// return the number of blocks until the lower 32 bits roll over77private long blocksUntilRollover() {78ByteBuffer buf = ByteBuffer.wrap(counter, counter.length - 4, 4);79buf.order(ByteOrder.BIG_ENDIAN);80long ctr32 = 0xFFFFFFFFL & buf.getInt();81long blocksLeft = (1L << 32) - ctr32;82return blocksLeft;83}8485private void checkBlock() {86if (block == null) {87block = new byte[blockSize];88} else {89Arrays.fill(block, (byte)0);90}91}9293/**94* Using the given inLen, this operates only on blockSize data, leaving95* the remainder in 'in'.96* The return value will be (inLen - (inLen % blockSize))97*/98public int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {99if (inLen == 0) {100return 0;101}102103if (inLen - inOfs > in.length) {104throw new RuntimeException("input length out of bound");105}106if (inLen < 0) {107throw new RuntimeException("input length unsupported");108}109if (out.length - outOfs < (inLen - (inLen % blockSize))) {110throw new RuntimeException("output buffer too small");111}112113inLen -= inLen % blockSize;114long blocksLeft = blocksUntilRollover();115int numOfCompleteBlocks = inLen / blockSize;116if (numOfCompleteBlocks >= blocksLeft) {117// Counter Mode encryption cannot be used because counter will118// roll over incorrectly. Use GCM-specific code instead.119checkBlock();120for (int i = 0; i < numOfCompleteBlocks; i++) {121embeddedCipher.encryptBlock(counter, 0, block, 0);122for (int n = 0; n < blockSize; n++) {123int index = (i * blockSize + n);124out[outOfs + index] =125(byte) ((in[inOfs + index] ^ block[n]));126}127GaloisCounterMode.increment32(counter);128}129return inLen;130} else {131return encrypt(in, inOfs, inLen, out, outOfs);132}133}134135/**136* Operate on only blocksize data leaving the remainder in 'in' .137*/138public int update(byte[] in, int inOfs, int inLen, ByteBuffer dst) {139// If the bytebuffer is backed by arrays, use that instead of140// allocating and copying for direct bytebuffers141if (!dst.isDirect()) {142int len = update(in, inOfs, inLen, dst.array(),143dst.arrayOffset() + dst.position());144dst.position(dst.position() + len);145return len;146}147148// Direct ByteBuffer operation149if (inLen - inOfs > in.length) {150throw new RuntimeException("input length out of bound");151}152if (inLen < 0) {153throw new RuntimeException("input length unsupported");154}155// See GaloisCounterMode. decryptFinal(bytebuffer, bytebuffer) for156// details on the check for 'dst' having enough space for the result.157158long blocksLeft = blocksUntilRollover();159int numOfCompleteBlocks = inLen / blockSize;160if (numOfCompleteBlocks >= blocksLeft) {161// Counter Mode encryption cannot be used because counter will162// roll over incorrectly. Use GCM-specific code instead.163checkBlock();164for (int i = 0; i < numOfCompleteBlocks; i++) {165embeddedCipher.encryptBlock(counter, 0, block, 0);166for (int n = 0; n < blockSize; n++) {167int index = (i * blockSize + n);168dst.put((byte) ((in[inOfs + index] ^ block[n])));169}170GaloisCounterMode.increment32(counter);171}172return inLen;173} else {174int len = inLen - inLen % blockSize;175int processed = len;176byte[] out = new byte[Math.min(MAX_LEN, len)];177int offset = inOfs;178while (processed > MAX_LEN) {179encrypt(in, offset, MAX_LEN, out, 0);180dst.put(out, 0, MAX_LEN);181processed -= MAX_LEN;182offset += MAX_LEN;183}184encrypt(in, offset, processed, out, 0);185// If dst is less than blocksize, insert only what it can. Extra186// bytes would cause buffers with enough size to fail with a187// short buffer188dst.put(out, 0, Math.min(dst.remaining(), processed));189return len;190}191}192193/**194* Operate on only blocksize data leaving the remainder in the src buffer.195*/196public int update(ByteBuffer src, ByteBuffer dst) {197int len;198199// If the bytebuffer is backed by arrays, use that instead of200// allocating and copying for direct bytebuffers201if (src.hasArray() && dst.hasArray()) {202len = update(src.array(), src.arrayOffset() + src.position(),203src.remaining() - (src.remaining() % blockSize),204dst.array(), dst.arrayOffset() + dst.position());205src.position(src.position() + len);206dst.position(dst.position() + len);207return len;208}209210// Direct bytebuffer operation211long blocksLeft = blocksUntilRollover();212int numOfCompleteBlocks = src.remaining() / blockSize;213if (numOfCompleteBlocks >= blocksLeft) {214// Counter Mode encryption cannot be used because counter will215// roll over incorrectly. Use GCM-specific code instead.216checkBlock();217for (int i = 0; i < numOfCompleteBlocks; i++) {218embeddedCipher.encryptBlock(counter, 0, block, 0);219for (int n = 0; n < blockSize; n++) {220dst.put((byte) (src.get() ^ block[n]));221}222GaloisCounterMode.increment32(counter);223}224return numOfCompleteBlocks * blockSize;225}226227len = src.remaining() - (src.remaining() % blockSize);228int processed = len;229byte[] in = new byte[Math.min(MAX_LEN, len)];230while (processed > MAX_LEN) {231src.get(in, 0, MAX_LEN);232encrypt(in, 0, MAX_LEN, in, 0);233dst.put(in, 0, MAX_LEN);234processed -= MAX_LEN;235}236src.get(in, 0, processed);237encrypt(in, 0, processed, in, 0);238dst.put(in, 0, processed);239return len;240}241242/**243* doFinal operation by using update() for any full block operations needed,244* then operating on the final bytes in the input buffer.245*246* This method will not write any block padding to the output buffer247*/248public int doFinal(byte[] in, int inOfs, int inLen, byte[] out,249int outOfs) {250if (inLen == 0) {251return 0;252}253int lastBlockSize = inLen % blockSize;254int completeBlkLen = inLen - lastBlockSize;255// process the complete blocks first256update(in, inOfs, completeBlkLen, out, outOfs);257if (lastBlockSize != 0) {258// do the last partial block259checkBlock();260embeddedCipher.encryptBlock(counter, 0, block, 0);261for (int n = 0; n < lastBlockSize; n++) {262out[outOfs + completeBlkLen + n] =263(byte) ((in[inOfs + completeBlkLen + n] ^ block[n]));264}265}266return inLen;267}268269/**270* doFinal operation by using update() for any full block operations needed,271* then operating on the final bytes in the input buffer.272*273* If src and dst are array-backed bytebuffers, call doFinal(byte[]...) for274* less memory usage.275*/276public int doFinal(ByteBuffer src, ByteBuffer dst) {277// If the bytebuffer is backed by arrays, use that instead of278// allocating and copying for direct bytebuffers279if (src.hasArray() && dst.hasArray()) {280int len = doFinal(src.array(), src.arrayOffset() + src.position(),281src.remaining(), dst.array(),282dst.arrayOffset() + dst.position());283src.position(src.position() + len);284dst.position(dst.position() + len);285return len;286}287288int len = src.remaining();289int lastBlockSize = len % blockSize;290update(src, dst);291if (lastBlockSize != 0) {292checkBlock();293// do the last partial block294embeddedCipher.encryptBlock(counter, 0, block, 0);295for (int n = 0; n < lastBlockSize; n++) {296dst.put((byte) (src.get() ^ block[n]));297}298}299return len;300}301}302303304