1/*
2 * Copyright (c) 2013, 2017, 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/*
27 * (C) Copyright IBM Corp. 2013
28 */
29
30package com.sun.crypto.provider;
31
32import javax.crypto.IllegalBlockSizeException;
33import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
34
35/**
36 * This class represents the GCTR function defined in NIST 800-38D
37 * under section 6.5.  With a given cipher object and initial counter
38 * block, a counter mode operation is performed.  Blocksize is limited
39 * to 16 bytes.
40 *
41 * If any invariant is broken, failures can occur because the
42 * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM
43 * (see JDK-8067648 for details).
44 *
45 * The counter mode operations can be intrinsified and parallelized
46 * by using CounterMode.implCrypt() if HotSpot VM supports it on the
47 * architecture.
48 *
49 * <p>This function is used in the implementation of GCM mode.
50 *
51 * @since 1.8
52 */
53final class GCTR extends CounterMode {
54
55    GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
56        super(cipher);
57        if (initialCounterBlk.length != AES_BLOCK_SIZE) {
58            throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length +
59                                       ") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")");
60        }
61
62        iv = initialCounterBlk;
63        reset();
64    }
65
66    @Override
67    String getFeedback() {
68        return "GCTR";
69    }
70
71    // input must be multiples of 128-bit blocks when calling update
72    int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
73        if (inLen - inOfs > in.length) {
74            throw new RuntimeException("input length out of bound");
75        }
76        if (inLen < 0 || inLen % AES_BLOCK_SIZE != 0) {
77            throw new RuntimeException("input length unsupported");
78        }
79        if (out.length - outOfs < inLen) {
80            throw new RuntimeException("output buffer too small");
81        }
82
83        return encrypt(in, inOfs, inLen, out, outOfs);
84    }
85
86    // input can be arbitrary size when calling doFinal
87    int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
88                          int outOfs) throws IllegalBlockSizeException {
89        try {
90            if (inLen < 0) {
91                throw new IllegalBlockSizeException("Negative input size!");
92            } else if (inLen > 0) {
93                int lastBlockSize = inLen % AES_BLOCK_SIZE;
94                int completeBlkLen = inLen - lastBlockSize;
95                // process the complete blocks first
96                update(in, inOfs, completeBlkLen, out, outOfs);
97                if (lastBlockSize != 0) {
98                    // do the last partial block
99                    byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
100                    embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
101                    for (int n = 0; n < lastBlockSize; n++) {
102                        out[outOfs + completeBlkLen + n] =
103                            (byte) ((in[inOfs + completeBlkLen + n] ^
104                                     encryptedCntr[n]));
105                    }
106                }
107            }
108        } finally {
109            reset();
110        }
111        return inLen;
112    }
113}
114