|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
* (C) Copyright IBM Corp. 2013 |
|
*/ |
|
|
|
package com.sun.crypto.provider; |
|
|
|
import java.nio.ByteBuffer; |
|
import java.nio.ByteOrder; |
|
import javax.crypto.IllegalBlockSizeException; |
|
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class GCTR extends CounterMode { |
|
|
|
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) { |
|
super(cipher); |
|
if (initialCounterBlk.length != AES_BLOCK_SIZE) { |
|
throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length + |
|
") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")"); |
|
} |
|
|
|
iv = initialCounterBlk; |
|
reset(); |
|
} |
|
|
|
@Override |
|
String getFeedback() { |
|
return "GCTR"; |
|
} |
|
|
|
|
|
private long blocksUntilRollover() { |
|
ByteBuffer buf = ByteBuffer.wrap(counter, counter.length - 4, 4); |
|
buf.order(ByteOrder.BIG_ENDIAN); |
|
long ctr32 = 0xFFFFFFFFL & buf.getInt(); |
|
long blocksLeft = (1L << 32) - ctr32; |
|
return blocksLeft; |
|
} |
|
|
|
|
|
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"); |
|
} |
|
|
|
long blocksLeft = blocksUntilRollover(); |
|
int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; |
|
if (numOfCompleteBlocks >= blocksLeft) { |
|
// Counter Mode encryption cannot be used because counter will |
|
|
|
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; |
|
for (int i = 0; i < numOfCompleteBlocks; i++) { |
|
embeddedCipher.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; |
|
} else { |
|
return encrypt(in, inOfs, inLen, out, outOfs); |
|
} |
|
} |
|
|
|
|
|
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; |
|
int completeBlkLen = inLen - lastBlockSize; |
|
|
|
update(in, inOfs, completeBlkLen, out, outOfs); |
|
if (lastBlockSize != 0) { |
|
|
|
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; |
|
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0); |
|
for (int n = 0; n < lastBlockSize; n++) { |
|
out[outOfs + completeBlkLen + n] = |
|
(byte) ((in[inOfs + completeBlkLen + n] ^ |
|
encryptedCntr[n])); |
|
} |
|
} |
|
} |
|
} finally { |
|
reset(); |
|
} |
|
return inLen; |
|
} |
|
} |