org.bouncycastle.crypto.engines.ElephantEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcprov-ext-debug-jdk18on Show documentation
Show all versions of bcprov-ext-debug-jdk18on Show documentation
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.
The newest version!
package org.bouncycastle.crypto.engines;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
import org.bouncycastle.crypto.modes.AEADCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* Elephant AEAD v2, based on the current round 3 submission, https://www.esat.kuleuven.be/cosic/elephant/
* Reference C implementation: https://github.com/TimBeyne/Elephant
* Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/elephant-spec-final.pdf
*/
public class ElephantEngine
implements AEADCipher
{
public enum ElephantParameters
{
elephant160,
elephant176,
elephant200
}
private enum State
{
Uninitialized,
EncInit,
EncAad, // can process AAD
EncData, // cannot process AAD
EncFinal,
DecInit,
DecAad, // can process AAD
DecData, // cannot process AAD
DecFinal,
}
private boolean forEncryption;
private final String algorithmName;
private final ElephantParameters parameters;
private final int BLOCK_SIZE;
private int nBits;
private int nSBox;
private final int nRounds;
private byte lfsrIV;
private byte[] tag;
private byte[] npub;
private byte[] expanded_key;
private final byte CRYPTO_KEYBYTES = 16;
private final byte CRYPTO_NPUBBYTES = 12;
private final byte CRYPTO_ABYTES;
private boolean initialised;
private int nb_its;
private byte[] ad;
private int adOff;
private int adlen;
private final byte[] tag_buffer;
private byte[] previous_mask;
private byte[] current_mask;
private byte[] next_mask;
private final byte[] buffer;
private State m_state = State.Uninitialized;
private final ByteArrayOutputStream aadData = new ByteArrayOutputStream();
private int inputOff;
private byte[] inputMessage;
private final byte[] previous_outputMessage;
private final byte[] outputMessage;
private final byte[] sBoxLayer = {
(byte)0xee, (byte)0xed, (byte)0xeb, (byte)0xe0, (byte)0xe2, (byte)0xe1, (byte)0xe4, (byte)0xef, (byte)0xe7, (byte)0xea, (byte)0xe8, (byte)0xe5, (byte)0xe9, (byte)0xec, (byte)0xe3, (byte)0xe6,
(byte)0xde, (byte)0xdd, (byte)0xdb, (byte)0xd0, (byte)0xd2, (byte)0xd1, (byte)0xd4, (byte)0xdf, (byte)0xd7, (byte)0xda, (byte)0xd8, (byte)0xd5, (byte)0xd9, (byte)0xdc, (byte)0xd3, (byte)0xd6,
(byte)0xbe, (byte)0xbd, (byte)0xbb, (byte)0xb0, (byte)0xb2, (byte)0xb1, (byte)0xb4, (byte)0xbf, (byte)0xb7, (byte)0xba, (byte)0xb8, (byte)0xb5, (byte)0xb9, (byte)0xbc, (byte)0xb3, (byte)0xb6,
(byte)0x0e, (byte)0x0d, (byte)0x0b, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x04, (byte)0x0f, (byte)0x07, (byte)0x0a, (byte)0x08, (byte)0x05, (byte)0x09, (byte)0x0c, (byte)0x03, (byte)0x06,
(byte)0x2e, (byte)0x2d, (byte)0x2b, (byte)0x20, (byte)0x22, (byte)0x21, (byte)0x24, (byte)0x2f, (byte)0x27, (byte)0x2a, (byte)0x28, (byte)0x25, (byte)0x29, (byte)0x2c, (byte)0x23, (byte)0x26,
(byte)0x1e, (byte)0x1d, (byte)0x1b, (byte)0x10, (byte)0x12, (byte)0x11, (byte)0x14, (byte)0x1f, (byte)0x17, (byte)0x1a, (byte)0x18, (byte)0x15, (byte)0x19, (byte)0x1c, (byte)0x13, (byte)0x16,
(byte)0x4e, (byte)0x4d, (byte)0x4b, (byte)0x40, (byte)0x42, (byte)0x41, (byte)0x44, (byte)0x4f, (byte)0x47, (byte)0x4a, (byte)0x48, (byte)0x45, (byte)0x49, (byte)0x4c, (byte)0x43, (byte)0x46,
(byte)0xfe, (byte)0xfd, (byte)0xfb, (byte)0xf0, (byte)0xf2, (byte)0xf1, (byte)0xf4, (byte)0xff, (byte)0xf7, (byte)0xfa, (byte)0xf8, (byte)0xf5, (byte)0xf9, (byte)0xfc, (byte)0xf3, (byte)0xf6,
(byte)0x7e, (byte)0x7d, (byte)0x7b, (byte)0x70, (byte)0x72, (byte)0x71, (byte)0x74, (byte)0x7f, (byte)0x77, (byte)0x7a, (byte)0x78, (byte)0x75, (byte)0x79, (byte)0x7c, (byte)0x73, (byte)0x76,
(byte)0xae, (byte)0xad, (byte)0xab, (byte)0xa0, (byte)0xa2, (byte)0xa1, (byte)0xa4, (byte)0xaf, (byte)0xa7, (byte)0xaa, (byte)0xa8, (byte)0xa5, (byte)0xa9, (byte)0xac, (byte)0xa3, (byte)0xa6,
(byte)0x8e, (byte)0x8d, (byte)0x8b, (byte)0x80, (byte)0x82, (byte)0x81, (byte)0x84, (byte)0x8f, (byte)0x87, (byte)0x8a, (byte)0x88, (byte)0x85, (byte)0x89, (byte)0x8c, (byte)0x83, (byte)0x86,
(byte)0x5e, (byte)0x5d, (byte)0x5b, (byte)0x50, (byte)0x52, (byte)0x51, (byte)0x54, (byte)0x5f, (byte)0x57, (byte)0x5a, (byte)0x58, (byte)0x55, (byte)0x59, (byte)0x5c, (byte)0x53, (byte)0x56,
(byte)0x9e, (byte)0x9d, (byte)0x9b, (byte)0x90, (byte)0x92, (byte)0x91, (byte)0x94, (byte)0x9f, (byte)0x97, (byte)0x9a, (byte)0x98, (byte)0x95, (byte)0x99, (byte)0x9c, (byte)0x93, (byte)0x96,
(byte)0xce, (byte)0xcd, (byte)0xcb, (byte)0xc0, (byte)0xc2, (byte)0xc1, (byte)0xc4, (byte)0xcf, (byte)0xc7, (byte)0xca, (byte)0xc8, (byte)0xc5, (byte)0xc9, (byte)0xcc, (byte)0xc3, (byte)0xc6,
(byte)0x3e, (byte)0x3d, (byte)0x3b, (byte)0x30, (byte)0x32, (byte)0x31, (byte)0x34, (byte)0x3f, (byte)0x37, (byte)0x3a, (byte)0x38, (byte)0x35, (byte)0x39, (byte)0x3c, (byte)0x33, (byte)0x36,
(byte)0x6e, (byte)0x6d, (byte)0x6b, (byte)0x60, (byte)0x62, (byte)0x61, (byte)0x64, (byte)0x6f, (byte)0x67, (byte)0x6a, (byte)0x68, (byte)0x65, (byte)0x69, (byte)0x6c, (byte)0x63, (byte)0x66
};
private final byte[] KeccakRoundConstants = {
(byte)0x01, (byte)0x82, (byte)0x8a, (byte)0x00, (byte)0x8b, (byte)0x01, (byte)0x81, (byte)0x09, (byte)0x8a,
(byte)0x88, (byte)0x09, (byte)0x0a, (byte)0x8b, (byte)0x8b, (byte)0x89, (byte)0x03, (byte)0x02, (byte)0x80
};
private final int[] KeccakRhoOffsets = {0, 1, 6, 4, 3, 4, 4, 6, 7, 4, 3, 2, 3, 1, 7, 1, 5, 7, 5, 0, 2, 2, 5, 0, 6};
public ElephantEngine(ElephantParameters parameters)
{
switch (parameters)
{
case elephant160:
BLOCK_SIZE = 20;
nBits = 160;
nSBox = 20;
nRounds = 80;
lfsrIV = 0x75;
CRYPTO_ABYTES = 8;
algorithmName = "Elephant 160 AEAD";
break;
case elephant176:
BLOCK_SIZE = 22;
nBits = 176;
nSBox = 22;
nRounds = 90;
lfsrIV = 0x45;
CRYPTO_ABYTES = 8;
algorithmName = "Elephant 176 AEAD";
break;
case elephant200:
BLOCK_SIZE = 25;
nRounds = 18;
CRYPTO_ABYTES = 16;
algorithmName = "Elephant 200 AEAD";
break;
default:
throw new IllegalArgumentException("Invalid parameter settings for Elephant");
}
this.parameters = parameters;
tag_buffer = new byte[BLOCK_SIZE];
previous_mask = new byte[BLOCK_SIZE];
current_mask = new byte[BLOCK_SIZE];
next_mask = new byte[BLOCK_SIZE];
buffer = new byte[BLOCK_SIZE];
previous_outputMessage = new byte[BLOCK_SIZE];
outputMessage = new byte[BLOCK_SIZE];
initialised = false;
reset(false);
}
private void permutation(byte[] state)
{
switch (parameters)
{
case elephant160:
case elephant176:
byte IV = lfsrIV;
byte[] tmp = new byte[nSBox];
for (int i = 0; i < nRounds; i++)
{
/* Add counter values */
state[0] ^= IV;
state[nSBox - 1] ^= (byte)(((IV & 0x01) << 7) | ((IV & 0x02) << 5) | ((IV & 0x04) << 3) | ((IV & 0x08)
<< 1) | ((IV & 0x10) >>> 1) | ((IV & 0x20) >>> 3) | ((IV & 0x40) >>> 5) | ((IV & 0x80) >>> 7));
IV = (byte)(((IV << 1) | (((0x40 & IV) >>> 6) ^ ((0x20 & IV) >>> 5))) & 0x7f);
/* sBoxLayer layer */
for (int j = 0; j < nSBox; j++)
{
state[j] = sBoxLayer[(state[j] & 0xFF)];
}
/* pLayer */
int PermutedBitNo;
Arrays.fill(tmp, (byte)0);
for (int j = 0; j < nSBox; j++)
{
for (int k = 0; k < 8; k++)
{
PermutedBitNo = (j << 3) + k;
if (PermutedBitNo != nBits - 1)
{
PermutedBitNo = ((PermutedBitNo * nBits) >> 2) % (nBits - 1);
}
tmp[PermutedBitNo >>> 3] ^= (((state[j] & 0xFF) >>> k) & 0x1) << (PermutedBitNo & 7);
}
}
System.arraycopy(tmp, 0, state, 0, nSBox);
}
break;
case elephant200:
for (int i = 0; i < nRounds; i++)
{
KeccakP200Round(state, i);
}
break;
}
}
private byte rotl(byte b)
{
return (byte)(((b & 0xFF) << 1) | ((b & 0xFF) >>> 7));
}
private byte ROL8(byte a, int offset)
{
return (byte)((offset != 0) ? (((a & 0xFF) << offset) ^ ((a & 0xFF) >>> (8 - offset))) : a);
}
private int index(int x, int y)
{
return x + y * 5;
}
private void KeccakP200Round(byte[] state, int indexRound)
{
int x, y;
byte[] tempA = new byte[25];
//theta
for (x = 0; x < 5; x++)
{
for (y = 0; y < 5; y++)
{
tempA[x] ^= state[index(x, y)];
}
}
for (x = 0; x < 5; x++)
{
tempA[x + 5] = (byte)(ROL8(tempA[(x + 1) % 5], 1) ^ tempA[(x + 4) % 5]);
}
for (x = 0; x < 5; x++)
{
for (y = 0; y < 5; y++)
{
state[index(x, y)] ^= tempA[x + 5];
}
}
//rho
for (x = 0; x < 5; x++)
{
for (y = 0; y < 5; y++)
{
tempA[index(x, y)] = ROL8(state[index(x, y)], KeccakRhoOffsets[index(x, y)]);
}
}
//pi
for (x = 0; x < 5; x++)
{
for (y = 0; y < 5; y++)
{
state[index(y, (2 * x + 3 * y) % 5)] = tempA[index(x, y)];
}
}
//chi
for (y = 0; y < 5; y++)
{
for (x = 0; x < 5; x++)
{
tempA[x] = (byte)(state[index(x, y)] ^ ((~state[index((x + 1) % 5, y)]) & state[index((x + 2) % 5, y)]));
}
for (x = 0; x < 5; x++)
{
state[index(x, y)] = tempA[x];
}
}
//iota
state[0] ^= KeccakRoundConstants[indexRound];//index(0,0)
}
// State should be BLOCK_SIZE bytes long
// Note: input may be equal to output
private void lfsr_step(byte[] output, byte[] input)
{
switch (parameters)
{
case elephant160:
output[BLOCK_SIZE - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >>> 5)) ^
((input[3] & 0xFF) << 7) ^ ((input[13] & 0xFF) >>> 7));
break;
case elephant176:
output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >>> 7));
break;
case elephant200:
output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1));
break;
}
System.arraycopy(input, 1, output, 0, BLOCK_SIZE - 1);
}
private void xor_block(byte[] state, byte[] block, int bOff, int size)
{
for (int i = 0; i < size; ++i)
{
state[i] ^= block[i + bOff];
}
}
// Return the ith ciphertext block.
// clen is the length of the ciphertext in bytes
private void get_c_block(byte[] output, byte[] c, int cOff, int clen, int i)
{
int block_offset = i * BLOCK_SIZE;
// If clen is divisible by BLOCK_SIZE, add an additional padding block
if (block_offset == clen)
{
Arrays.fill(output, 0, BLOCK_SIZE, (byte)0);
output[0] = 0x01;
return;
}
int r_clen = clen - block_offset;
// Fill with ciphertext if available
if (BLOCK_SIZE <= r_clen)
{ // enough ciphertext
System.arraycopy(c, cOff, output, 0, BLOCK_SIZE);
}
else
{ // not enough ciphertext, need to pad
if (r_clen > 0) // c might be nullptr
{
System.arraycopy(c, cOff, output, 0, r_clen);
}
Arrays.fill(output, r_clen, BLOCK_SIZE, (byte)0);
output[r_clen] = 0x01;
}
}
@Override
public void init(boolean forEncryption, CipherParameters params)
throws IllegalArgumentException
{
this.forEncryption = forEncryption;
if (!(params instanceof ParametersWithIV))
{
throw new IllegalArgumentException(algorithmName + " init parameters must include an IV");
}
ParametersWithIV ivParams = (ParametersWithIV)params;
npub = ivParams.getIV();
if (npub == null || npub.length != CRYPTO_NPUBBYTES)
{
throw new IllegalArgumentException(algorithmName + " requires exactly 12 bytes of IV");
}
if (!(ivParams.getParameters() instanceof KeyParameter))
{
throw new IllegalArgumentException(algorithmName + " init parameters must include a key");
}
KeyParameter key = (KeyParameter)ivParams.getParameters();
byte[] k = key.getKey();
if (k.length != CRYPTO_KEYBYTES)
{
throw new IllegalArgumentException(algorithmName + " key must be 128 bits long");
}
// Storage for the expanded key L
expanded_key = new byte[BLOCK_SIZE];
System.arraycopy(k, 0, expanded_key, 0, CRYPTO_KEYBYTES);
permutation(expanded_key);
CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(
this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption)));
initialised = true;
m_state = forEncryption ? State.EncInit : State.DecInit;
inputMessage = new byte[BLOCK_SIZE + (forEncryption ? 0 : CRYPTO_ABYTES)];
reset(false);
}
@Override
public String getAlgorithmName()
{
return algorithmName;
}
@Override
public void processAADByte(byte input)
{
aadData.write(input);
}
@Override
public void processAADBytes(byte[] input, int inOff, int len)
{
if (inOff + len > input.length)
{
throw new DataLengthException("input buffer too short");
}
aadData.write(input, inOff, len);
}
@Override
public int processByte(byte input, byte[] output, int outOff)
throws DataLengthException
{
return processBytes(new byte[]{input}, 0, 1, output, outOff);
}
@Override
public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
throws DataLengthException
{
if (inOff + len > input.length)
{
throw new DataLengthException("input buffer too short");
}
byte[] ad = aadData.toByteArray();
if (inputOff + len - (forEncryption ? 0 : CRYPTO_ABYTES) >= BLOCK_SIZE)
{
switch (m_state)
{
case EncInit:
case DecInit:
processAADBytes(tag_buffer);
break;
}
int mlen = inputOff + len - (forEncryption ? 0 : CRYPTO_ABYTES);
int adlen = ad.length;
int nblocks_c = mlen / BLOCK_SIZE;
int nblocks_m = 1 + ((mlen % BLOCK_SIZE) != 0 ? nblocks_c : nblocks_c - 1);
int nblocks_ad = 1 + (CRYPTO_NPUBBYTES + adlen) / BLOCK_SIZE;
byte[] tempInput = new byte[Math.max(nblocks_c, 1) * BLOCK_SIZE];
System.arraycopy(inputMessage, 0, tempInput, 0, inputOff);
int templen = tempInput.length - inputOff;
System.arraycopy(input, inOff, tempInput, inputOff, tempInput.length - inputOff);
processBytes(tempInput, output, outOff, nblocks_c, nblocks_m, nblocks_c, mlen, nblocks_ad);
inputOff = len - templen;
System.arraycopy(input, inOff + templen, inputMessage, 0, inputOff);
nb_its += nblocks_c;
return nblocks_c * BLOCK_SIZE;
}
else
{
System.arraycopy(input, inOff, inputMessage, inputOff, len);
inputOff += len;
return 0;
}
}
@Override
public int doFinal(byte[] output, int outOff)
throws IllegalStateException, InvalidCipherTextException
{
if (!initialised)
{
throw new IllegalArgumentException(algorithmName + " needs call init function before doFinal");
}
int len = inputOff;
if ((forEncryption && len + outOff + CRYPTO_ABYTES > output.length) ||
(!forEncryption && len + outOff - CRYPTO_ABYTES > output.length))
{
throw new OutputLengthException("output buffer is too short");
}
byte[] ad = aadData.toByteArray();
switch (m_state)
{
case EncInit:
case DecInit:
processAADBytes(tag_buffer);
break;
}
int mlen = len + nb_its * BLOCK_SIZE - (forEncryption ? 0 : CRYPTO_ABYTES);
int adlen = ad.length;
int nblocks_c = 1 + mlen / BLOCK_SIZE;
int nblocks_m = (mlen % BLOCK_SIZE) != 0 ? nblocks_c : nblocks_c - 1;
int nblocks_ad = 1 + (CRYPTO_NPUBBYTES + adlen) / BLOCK_SIZE;
int nb_it = Math.max(nblocks_c + 1, nblocks_ad - 1);
outOff += processBytes(inputMessage, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad);
tag = new byte[CRYPTO_ABYTES];
xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE);
permutation(tag_buffer);
xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE);
if (forEncryption)
{
System.arraycopy(tag_buffer, 0, tag, 0, CRYPTO_ABYTES);
System.arraycopy(tag, 0, output, outOff, tag.length);
mlen += CRYPTO_ABYTES;
}
else
{
inputOff -= CRYPTO_ABYTES;
for (int i = 0; i < CRYPTO_ABYTES; ++i)
{
if (tag_buffer[i] != inputMessage[inputOff + i])
{
throw new IllegalArgumentException("Mac does not match");
}
}
}
reset(false);
return mlen;
}
@Override
public byte[] getMac()
{
return tag;
}
@Override
public int getUpdateOutputSize(int len)
{
switch (m_state)
{
case Uninitialized:
throw new IllegalArgumentException(algorithmName + " needs call init function before getUpdateOutputSize");
case DecFinal:
case EncFinal:
return 0;
case EncAad:
case EncData:
case EncInit:
return inputOff + len + CRYPTO_ABYTES;
}
return Math.max(0, len + inputOff - CRYPTO_ABYTES);
}
@Override
public int getOutputSize(int len)
{
switch (m_state)
{
case Uninitialized:
throw new IllegalArgumentException(algorithmName + " needs call init function before getUpdateOutputSize");
case DecFinal:
case EncFinal:
return 0;
case EncAad:
case EncData:
case EncInit:
return len + CRYPTO_ABYTES;
}
return Math.max(0, len - CRYPTO_ABYTES);
}
@Override
public void reset()
{
reset(true);
}
private void reset(boolean clearMac)
{
if (clearMac)
{
tag = null;
}
aadData.reset();
Arrays.fill(tag_buffer, (byte)0);
inputOff = 0;
nb_its = 0;
adOff = -1;
}
public int getKeyBytesSize()
{
return CRYPTO_KEYBYTES;
}
public int getIVBytesSize()
{
return CRYPTO_NPUBBYTES;
}
public int getBlockSize()
{
return CRYPTO_ABYTES;
}
private void checkAad()
{
switch (m_state)
{
case DecData:
throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the plaintext to be processed exceeds the a block size");
case EncData:
throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the ciphertext to be processed exceeds the a block size");
case EncFinal:
throw new IllegalArgumentException(algorithmName + " cannot be reused for encryption");
default:
break;
}
}
private void processAADBytes(byte[] output)
{
checkAad();
if (adOff == -1)
{
adlen = aadData.size();
ad = aadData.toByteArray();
adOff = 0;
}
int len = 0;
switch (m_state)
{
case DecInit:
System.arraycopy(expanded_key, 0, current_mask, 0, BLOCK_SIZE);
System.arraycopy(npub, 0, output, 0, CRYPTO_NPUBBYTES);
len += CRYPTO_NPUBBYTES;
m_state = State.DecAad;
break;
case EncInit:
System.arraycopy(expanded_key, 0, current_mask, 0, BLOCK_SIZE);
System.arraycopy(npub, 0, output, 0, CRYPTO_NPUBBYTES);
len += CRYPTO_NPUBBYTES;
m_state = State.EncAad;
break;
case DecAad:
case EncAad:
// If adlen is divisible by BLOCK_SIZE, add an additional padding block
if (adOff == adlen)
{
Arrays.fill(output, 0, BLOCK_SIZE, (byte)0);
output[0] = 0x01;
return;
}
break;
case DecData:
throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the plaintext to be processed exceeds the a block size");
case EncData:
throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the ciphertext to be processed exceeds the a block size");
case EncFinal:
throw new IllegalArgumentException(algorithmName + " cannot be reused for encryption");
}
int r_outlen = BLOCK_SIZE - len;
int r_adlen = adlen - adOff;
// Fill with associated data if available
if (r_outlen <= r_adlen)
{ // enough AD
System.arraycopy(ad, adOff, output, len, r_outlen);
adOff += r_outlen;
}
else
{ // not enough AD, need to pad
if (r_adlen > 0) // ad might be nullptr
{
System.arraycopy(ad, adOff, output, len, r_adlen);
adOff += r_adlen;
}
Arrays.fill(output, len + r_adlen, len + r_outlen, (byte)0);
output[len + r_adlen] = 0x01;
switch (m_state)
{
case DecAad:
m_state = State.DecData;
break;
case EncAad:
m_state = State.EncData;
break;
}
}
}
private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nblocks_m, int nblocks_c, int mlen,
int nblocks_ad)
{
int rv = 0;
for (int i = nb_its; i < nb_it; ++i)
{
// Compute mask for the next message
lfsr_step(next_mask, current_mask);
if (i < nblocks_m)
{
// Compute ciphertext block
System.arraycopy(npub, 0, buffer, 0, CRYPTO_NPUBBYTES);
Arrays.fill(buffer, CRYPTO_NPUBBYTES, BLOCK_SIZE, (byte)0);
xor_block(buffer, current_mask, 0, BLOCK_SIZE);
xor_block(buffer, next_mask, 0, BLOCK_SIZE);
permutation(buffer);
xor_block(buffer, current_mask, 0, BLOCK_SIZE);
xor_block(buffer, next_mask, 0, BLOCK_SIZE);
int r_size = (i == nblocks_m - 1) ? mlen - i * BLOCK_SIZE : BLOCK_SIZE;
xor_block(buffer, m, 0, r_size);
System.arraycopy(buffer, 0, output, outOff, r_size);
if (forEncryption)
{
System.arraycopy(buffer, 0, outputMessage, 0, r_size);
}
else
{
System.arraycopy(m, 0, outputMessage, 0, r_size);
}
rv += r_size;
}
if (i > 0 && i <= nblocks_c)
{
// Compute tag for ciphertext block
get_c_block(buffer, previous_outputMessage, 0, mlen, i - 1);
xor_block(buffer, previous_mask, 0, BLOCK_SIZE);
xor_block(buffer, next_mask, 0, BLOCK_SIZE);
permutation(buffer);
xor_block(buffer, previous_mask, 0, BLOCK_SIZE);
xor_block(buffer, next_mask, 0, BLOCK_SIZE);
xor_block(tag_buffer, buffer, 0, BLOCK_SIZE);
}
// If there is any AD left, compute tag for AD block
if (i + 1 < nblocks_ad)
{
processAADBytes(buffer);
xor_block(buffer, next_mask, 0, BLOCK_SIZE);
permutation(buffer);
xor_block(buffer, next_mask, 0, BLOCK_SIZE);
xor_block(tag_buffer, buffer, 0, BLOCK_SIZE);
}
// Cyclically shift the mask buffers
// Value of next_mask will be computed in the next iteration
byte[] temp = previous_mask;
previous_mask = current_mask;
current_mask = next_mask;
next_mask = temp;
System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BLOCK_SIZE);
}
return rv;
}
}