summaryrefslogtreecommitdiffstats
path: root/bcprov/src/main/java/org/bouncycastle/crypto/modes
diff options
context:
space:
mode:
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/crypto/modes')
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java18
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java2
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java125
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java44
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java403
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java50
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java21
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java7
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java117
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java57
-rw-r--r--bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java59
11 files changed, 630 insertions, 273 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
index 3c3bf34..71b7595 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/AEADBlockCipher.java
@@ -36,6 +36,24 @@ public interface AEADBlockCipher
public BlockCipher getUnderlyingCipher();
/**
+ * Add a single byte to the associated data check.
+ * <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
+ *
+ * @param in the byte to be processed.
+ */
+ public void processAADByte(byte in);
+
+ /**
+ * Add a sequence of bytes to the associated data check.
+ * <br>If the implementation supports it, this will be an online operation and will not retain the associated data.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ */
+ public void processAADBytes(byte[] in, int inOff, int len);
+
+ /**
* encrypt/decrypt a single byte.
*
* @param in the byte to be processed.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
index 1219f6d..d4800e6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CBCBlockCipher.java
@@ -93,7 +93,7 @@ public class CBCBlockCipher
{
reset();
- // if it;s null key is to be reused.
+ // if it's null, key is to be reused.
if (params != null)
{
cipher.init(encrypting, params);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index bedc3d1..18a3425 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -25,10 +25,11 @@ public class CCMBlockCipher
private int blockSize;
private boolean forEncryption;
private byte[] nonce;
- private byte[] associatedText;
+ private byte[] initialAssociatedText;
private int macSize;
private CipherParameters keyParam;
private byte[] macBlock;
+ private ByteArrayOutputStream associatedText = new ByteArrayOutputStream();
private ByteArrayOutputStream data = new ByteArrayOutputStream();
/**
@@ -69,7 +70,7 @@ public class CCMBlockCipher
AEADParameters param = (AEADParameters)params;
nonce = param.getNonce();
- associatedText = param.getAssociatedText();
+ initialAssociatedText = param.getAssociatedText();
macSize = param.getMacSize() / 8;
keyParam = param.getKey();
}
@@ -78,7 +79,7 @@ public class CCMBlockCipher
ParametersWithIV param = (ParametersWithIV)params;
nonce = param.getIV();
- associatedText = null;
+ initialAssociatedText = null;
macSize = macBlock.length / 2;
keyParam = param.getParameters();
}
@@ -93,6 +94,17 @@ public class CCMBlockCipher
return cipher.getAlgorithmName() + "/CCM";
}
+ public void processAADByte(byte in)
+ {
+ associatedText.write(in);
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ // TODO: Process AAD online
+ associatedText.write(in, inOff, len);
+ }
+
public int processByte(byte in, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
@@ -125,6 +137,7 @@ public class CCMBlockCipher
public void reset()
{
cipher.reset();
+ associatedText.reset();
data.reset();
}
@@ -150,60 +163,62 @@ public class CCMBlockCipher
public int getOutputSize(int len)
{
+ int totalData = len + data.size();
+
if (forEncryption)
{
- return data.size() + len + macSize;
- }
- else
- {
- return data.size() + len - macSize;
+ return totalData + macSize;
}
+
+ return totalData < macSize ? 0 : totalData - macSize;
}
public byte[] processPacket(byte[] in, int inOff, int inLen)
throws IllegalStateException, InvalidCipherTextException
{
+ // TODO: handle null keyParam (e.g. via RepeatedKeySpec)
+ // Need to keep the CTR and CBC Mac parts around and reset
if (keyParam == null)
{
throw new IllegalStateException("CCM cipher unitialized.");
}
-
+
BlockCipher ctrCipher = new SICBlockCipher(cipher);
byte[] iv = new byte[blockSize];
byte[] out;
iv[0] = (byte)(((15 - nonce.length) - 1) & 0x7);
-
+
System.arraycopy(nonce, 0, iv, 1, nonce.length);
-
+
ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
-
+
if (forEncryption)
{
int index = inOff;
int outOff = 0;
-
+
out = new byte[inLen + macSize];
-
+
calculateMac(in, inOff, inLen, macBlock);
-
+
ctrCipher.processBlock(macBlock, 0, macBlock, 0); // S0
-
+
while (index < inLen - blockSize) // S1...
{
ctrCipher.processBlock(in, index, out, outOff);
outOff += blockSize;
index += blockSize;
}
-
+
byte[] block = new byte[blockSize];
-
+
System.arraycopy(in, index, block, 0, inLen - index);
-
+
ctrCipher.processBlock(block, 0, block, 0);
-
+
System.arraycopy(block, 0, out, outOff, inLen - index);
-
+
outOff += inLen - index;
System.arraycopy(macBlock, 0, out, outOff, out.length - outOff);
@@ -212,49 +227,49 @@ public class CCMBlockCipher
{
int index = inOff;
int outOff = 0;
-
+
out = new byte[inLen - macSize];
-
+
System.arraycopy(in, inOff + inLen - macSize, macBlock, 0, macSize);
-
+
ctrCipher.processBlock(macBlock, 0, macBlock, 0);
-
+
for (int i = macSize; i != macBlock.length; i++)
{
macBlock[i] = 0;
}
-
+
while (outOff < out.length - blockSize)
{
ctrCipher.processBlock(in, index, out, outOff);
outOff += blockSize;
index += blockSize;
}
-
+
byte[] block = new byte[blockSize];
-
+
System.arraycopy(in, index, block, 0, out.length - outOff);
-
+
ctrCipher.processBlock(block, 0, block, 0);
-
+
System.arraycopy(block, 0, out, outOff, out.length - outOff);
-
+
byte[] calculatedMacBlock = new byte[blockSize];
-
+
calculateMac(out, 0, out.length, calculatedMacBlock);
-
+
if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
{
throw new InvalidCipherTextException("mac check in CCM failed");
}
}
-
+
return out;
}
-
+
private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
{
- Mac cMac = new CBCBlockCipherMac(cipher, macSize * 8);
+ Mac cMac = new CBCBlockCipherMac(cipher, macSize * 8);
cMac.init(keyParam);
@@ -292,10 +307,11 @@ public class CCMBlockCipher
{
int extra;
- if (associatedText.length < ((1 << 16) - (1 << 8)))
+ int textLength = getAssociatedTextLength();
+ if (textLength < ((1 << 16) - (1 << 8)))
{
- cMac.update((byte)(associatedText.length >> 8));
- cMac.update((byte)associatedText.length);
+ cMac.update((byte)(textLength >> 8));
+ cMac.update((byte)textLength);
extra = 2;
}
@@ -303,17 +319,25 @@ public class CCMBlockCipher
{
cMac.update((byte)0xff);
cMac.update((byte)0xfe);
- cMac.update((byte)(associatedText.length >> 24));
- cMac.update((byte)(associatedText.length >> 16));
- cMac.update((byte)(associatedText.length >> 8));
- cMac.update((byte)associatedText.length);
+ cMac.update((byte)(textLength >> 24));
+ cMac.update((byte)(textLength >> 16));
+ cMac.update((byte)(textLength >> 8));
+ cMac.update((byte)textLength);
extra = 6;
}
-
- cMac.update(associatedText, 0, associatedText.length);
-
- extra = (extra + associatedText.length) % 16;
+
+ if (initialAssociatedText != null)
+ {
+ cMac.update(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ if (associatedText.size() > 0)
+ {
+ byte[] tmp = associatedText.toByteArray();
+ cMac.update(tmp, 0, tmp.length);
+ }
+
+ extra = (extra + textLength) % 16;
if (extra != 0)
{
for (int i = 0; i != 16 - extra; i++)
@@ -331,8 +355,13 @@ public class CCMBlockCipher
return cMac.doFinal(macBlock, 0);
}
+ private int getAssociatedTextLength()
+ {
+ return associatedText.size() + ((initialAssociatedText == null) ? 0 : initialAssociatedText.length);
+ }
+
private boolean hasAssociatedText()
{
- return associatedText != null && associatedText.length != 0;
+ return getAssociatedTextLength() > 0;
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index 0af49f4..d0fb9bb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -68,36 +68,40 @@ public class CFBBlockCipher
if (params instanceof ParametersWithIV)
{
- ParametersWithIV ivParam = (ParametersWithIV)params;
- byte[] iv = ivParam.getIV();
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
- if (iv.length < IV.length)
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
{
- // prepend the supplied IV with zeros (per FIPS PUB 81)
- System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
- for (int i = 0; i < IV.length - iv.length; i++)
- {
- IV[i] = 0;
- }
- }
- else
- {
- System.arraycopy(iv, 0, IV, 0, IV.length);
+ IV[i] = 0;
}
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
- reset();
+ reset();
- // if null it's an IV changed only.
- if (ivParam.getParameters() != null)
- {
- cipher.init(true, ivParam.getParameters());
- }
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
}
else
{
- reset();
+ reset();
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
cipher.init(true, params);
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
index 7c98efa..9e617ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java
@@ -4,7 +4,9 @@ import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
+import org.bouncycastle.crypto.modes.gcm.GCMExponentiator;
import org.bouncycastle.crypto.modes.gcm.GCMMultiplier;
+import org.bouncycastle.crypto.modes.gcm.Tables1kGCMExponentiator;
import org.bouncycastle.crypto.modes.gcm.Tables8kGCMMultiplier;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
@@ -20,28 +22,31 @@ public class GCMBlockCipher
implements AEADBlockCipher
{
private static final int BLOCK_SIZE = 16;
- private static final byte[] ZEROES = new byte[BLOCK_SIZE];
// not final due to a compiler bug
private BlockCipher cipher;
private GCMMultiplier multiplier;
+ private GCMExponentiator exp;
// These fields are set by init and not modified by processing
private boolean forEncryption;
private int macSize;
private byte[] nonce;
- private byte[] A;
+ private byte[] initialAssociatedText;
private byte[] H;
- private byte[] initS;
private byte[] J0;
// These fields are modified during processing
private byte[] bufBlock;
private byte[] macBlock;
- private byte[] S;
+ private byte[] S, S_at, S_atPre;
private byte[] counter;
private int bufOff;
private long totalLength;
+ private byte[] atBlock;
+ private int atBlockPos;
+ private long atLength;
+ private long atLengthPre;
public GCMBlockCipher(BlockCipher c)
{
@@ -82,14 +87,14 @@ public class GCMBlockCipher
this.forEncryption = forEncryption;
this.macBlock = null;
- KeyParameter keyParam;
+ KeyParameter keyParam;
if (params instanceof AEADParameters)
{
AEADParameters param = (AEADParameters)params;
nonce = param.getNonce();
- A = param.getAssociatedText();
+ initialAssociatedText = param.getAssociatedText();
int macSizeBits = param.getMacSize();
if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0)
@@ -105,7 +110,7 @@ public class GCMBlockCipher
ParametersWithIV param = (ParametersWithIV)params;
nonce = param.getIV();
- A = null;
+ initialAssociatedText = null;
macSize = 16;
keyParam = (KeyParameter)param.getParameters();
}
@@ -122,48 +127,54 @@ public class GCMBlockCipher
throw new IllegalArgumentException("IV must be at least 1 byte");
}
- if (A == null)
- {
- // Avoid lots of null checks
- A = new byte[0];
- }
+ // TODO This should be configurable by init parameters
+ // (but must be 16 if nonce length not 12) (BLOCK_SIZE?)
+// this.tagLength = 16;
// Cipher always used in forward mode
// if keyParam is null we're reusing the last key.
if (keyParam != null)
{
cipher.init(true, keyParam);
- }
- // TODO This should be configurable by init parameters
- // (but must be 16 if nonce length not 12) (BLOCK_SIZE?)
-// this.tagLength = 16;
+ this.H = new byte[BLOCK_SIZE];
+ cipher.processBlock(H, 0, H, 0);
- this.H = new byte[BLOCK_SIZE];
- cipher.processBlock(ZEROES, 0, H, 0);
- multiplier.init(H);
+ // GCMMultiplier tables don't change unless the key changes (and are expensive to init)
+ multiplier.init(H);
+ exp = null;
+ }
- this.initS = gHASH(A);
+ this.J0 = new byte[BLOCK_SIZE];
if (nonce.length == 12)
{
- this.J0 = new byte[16];
System.arraycopy(nonce, 0, J0, 0, nonce.length);
- this.J0[15] = 0x01;
+ this.J0[BLOCK_SIZE - 1] = 0x01;
}
else
{
- this.J0 = gHASH(nonce);
- byte[] X = new byte[16];
- packLength((long)nonce.length * 8, X, 8);
- xor(this.J0, X);
- multiplier.multiplyH(this.J0);
+ gHASH(J0, nonce, nonce.length);
+ byte[] X = new byte[BLOCK_SIZE];
+ Pack.longToBigEndian((long)nonce.length * 8, X, 8);
+ gHASHBlock(J0, X);
}
- this.S = Arrays.clone(initS);
+ this.S = new byte[BLOCK_SIZE];
+ this.S_at = new byte[BLOCK_SIZE];
+ this.S_atPre = new byte[BLOCK_SIZE];
+ this.atBlock = new byte[BLOCK_SIZE];
+ this.atBlockPos = 0;
+ this.atLength = 0;
+ this.atLengthPre = 0;
this.counter = Arrays.clone(J0);
this.bufOff = 0;
this.totalLength = 0;
+
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
}
public byte[] getMac()
@@ -173,23 +184,88 @@ public class GCMBlockCipher
public int getOutputSize(int len)
{
+ int totalData = len + bufOff;
+
if (forEncryption)
{
- return len + bufOff + macSize;
+ return totalData + macSize;
}
- return len + bufOff - macSize;
+ return totalData < macSize ? 0 : totalData - macSize;
}
public int getUpdateOutputSize(int len)
{
- return ((len + bufOff) / BLOCK_SIZE) * BLOCK_SIZE;
+ int totalData = len + bufOff;
+ if (!forEncryption)
+ {
+ if (totalData < macSize)
+ {
+ return 0;
+ }
+ totalData -= macSize;
+ }
+ return totalData - totalData % BLOCK_SIZE;
+ }
+
+ public void processAADByte(byte in)
+ {
+ atBlock[atBlockPos] = in;
+ if (++atBlockPos == BLOCK_SIZE)
+ {
+ // Hash each block as it fills
+ gHASHBlock(S_at, atBlock);
+ atBlockPos = 0;
+ atLength += BLOCK_SIZE;
+ }
+ }
+
+ public void processAADBytes(byte[] in, int inOff, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ atBlock[atBlockPos] = in[inOff + i];
+ if (++atBlockPos == BLOCK_SIZE)
+ {
+ // Hash each block as it fills
+ gHASHBlock(S_at, atBlock);
+ atBlockPos = 0;
+ atLength += BLOCK_SIZE;
+ }
+ }
+ }
+
+ private void initCipher()
+ {
+ if (atLength > 0)
+ {
+ System.arraycopy(S_at, 0, S_atPre, 0, BLOCK_SIZE);
+ atLengthPre = atLength;
+ }
+
+ // Finish hash for partial AAD block
+ if (atBlockPos > 0)
+ {
+ gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
+ atLengthPre += atBlockPos;
+ }
+
+ if (atLengthPre > 0)
+ {
+ System.arraycopy(S_atPre, 0, S, 0, BLOCK_SIZE);
+ }
}
public int processByte(byte in, byte[] out, int outOff)
throws DataLengthException
{
- return process(in, out, outOff);
+ bufBlock[bufOff] = in;
+ if (++bufOff == bufBlock.length)
+ {
+ outputBlock(out, outOff);
+ return BLOCK_SIZE;
+ }
+ return 0;
}
public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
@@ -197,21 +273,12 @@ public class GCMBlockCipher
{
int resultLen = 0;
- for (int i = 0; i != len; i++)
+ for (int i = 0; i < len; ++i)
{
-// resultLen += process(in[inOff + i], out, outOff + resultLen);
- bufBlock[bufOff++] = in[inOff + i];
-
- if (bufOff == bufBlock.length)
+ bufBlock[bufOff] = in[inOff + i];
+ if (++bufOff == bufBlock.length)
{
- gCTRBlock(bufBlock, BLOCK_SIZE, out, outOff + resultLen);
- if (!forEncryption)
- {
- System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
- }
-// bufOff = 0;
- bufOff = bufBlock.length - BLOCK_SIZE;
-// return bufBlock.Length;
+ outputBlock(out, outOff + resultLen);
resultLen += BLOCK_SIZE;
}
}
@@ -219,30 +286,32 @@ public class GCMBlockCipher
return resultLen;
}
- private int process(byte in, byte[] out, int outOff)
- throws DataLengthException
+ private void outputBlock(byte[] output, int offset)
{
- bufBlock[bufOff++] = in;
-
- if (bufOff == bufBlock.length)
+ if (totalLength == 0)
{
- gCTRBlock(bufBlock, BLOCK_SIZE, out, outOff);
- if (!forEncryption)
- {
- System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
- }
-// bufOff = 0;
- bufOff = bufBlock.length - BLOCK_SIZE;
-// return bufBlock.length;
- return BLOCK_SIZE;
+ initCipher();
+ }
+ gCTRBlock(bufBlock, output, offset);
+ if (forEncryption)
+ {
+ bufOff = 0;
+ }
+ else
+ {
+ System.arraycopy(bufBlock, BLOCK_SIZE, bufBlock, 0, macSize);
+ bufOff = macSize;
}
-
- return 0;
}
public int doFinal(byte[] out, int outOff)
throws IllegalStateException, InvalidCipherTextException
{
+ if (totalLength == 0)
+ {
+ initCipher();
+ }
+
int extra = bufOff;
if (!forEncryption)
{
@@ -255,18 +324,57 @@ public class GCMBlockCipher
if (extra > 0)
{
- byte[] tmp = new byte[BLOCK_SIZE];
- System.arraycopy(bufBlock, 0, tmp, 0, extra);
- gCTRBlock(tmp, extra, out, outOff);
+ gCTRPartial(bufBlock, 0, extra, out, outOff);
+ }
+
+ atLength += atBlockPos;
+
+ if (atLength > atLengthPre)
+ {
+ /*
+ * Some AAD was sent after the cipher started. We determine the difference b/w the hash value
+ * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
+ * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
+ * partial) cipher-text blocks produced, and adjust the current hash.
+ */
+
+ // Finish hash for partial AAD block
+ if (atBlockPos > 0)
+ {
+ gHASHPartial(S_at, atBlock, 0, atBlockPos);
+ }
+
+ // Find the difference between the AAD hashes
+ if (atLengthPre > 0)
+ {
+ xor(S_at, S_atPre);
+ }
+
+ // Number of cipher-text blocks produced
+ long c = ((totalLength * 8) + 127) >>> 7;
+
+ // Calculate the adjustment factor
+ byte[] H_c = new byte[16];
+ if (exp == null)
+ {
+ exp = new Tables1kGCMExponentiator();
+ exp.init(H);
+ }
+ exp.exponentiateX(c, H_c);
+
+ // Carry the difference forward
+ multiply(S_at, H_c);
+
+ // Adjust the current hash
+ xor(S, S_at);
}
// Final gHASH
- byte[] X = new byte[16];
- packLength((long)A.length * 8, X, 0);
- packLength(totalLength * 8, X, 8);
+ byte[] X = new byte[BLOCK_SIZE];
+ Pack.longToBigEndian(atLength * 8, X, 0);
+ Pack.longToBigEndian(totalLength * 8, X, 8);
- xor(S, X);
- multiplier.multiplyH(S);
+ gHASHBlock(S, X);
// TODO Fix this if tagLength becomes configurable
// T = MSBt(GCTRk(J0,S))
@@ -310,7 +418,15 @@ public class GCMBlockCipher
private void reset(
boolean clearMac)
{
- S = Arrays.clone(initS);
+ cipher.reset();
+
+ S = new byte[BLOCK_SIZE];
+ S_at = new byte[BLOCK_SIZE];
+ S_atPre = new byte[BLOCK_SIZE];
+ atBlock = new byte[BLOCK_SIZE];
+ atBlockPos = 0;
+ atLength = 0;
+ atLengthPre = 0;
counter = Arrays.clone(J0);
bufOff = 0;
totalLength = 0;
@@ -325,12 +441,59 @@ public class GCMBlockCipher
macBlock = null;
}
- cipher.reset();
+ if (initialAssociatedText != null)
+ {
+ processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
+ }
+ }
+
+ private void gCTRBlock(byte[] block, byte[] out, int outOff)
+ {
+ byte[] tmp = getNextCounterBlock();
+
+ xor(tmp, block);
+ System.arraycopy(tmp, 0, out, outOff, BLOCK_SIZE);
+
+ gHASHBlock(S, forEncryption ? tmp : block);
+
+ totalLength += BLOCK_SIZE;
+ }
+
+ private void gCTRPartial(byte[] buf, int off, int len, byte[] out, int outOff)
+ {
+ byte[] tmp = getNextCounterBlock();
+
+ xor(tmp, buf, off, len);
+ System.arraycopy(tmp, 0, out, outOff, len);
+
+ gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
+
+ totalLength += len;
+ }
+
+ private void gHASH(byte[] Y, byte[] b, int len)
+ {
+ for (int pos = 0; pos < len; pos += BLOCK_SIZE)
+ {
+ int num = Math.min(len - pos, BLOCK_SIZE);
+ gHASHPartial(Y, b, pos, num);
+ }
+ }
+
+ private void gHASHBlock(byte[] Y, byte[] b)
+ {
+ xor(Y, b);
+ multiplier.multiplyH(Y);
+ }
+
+ private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
+ {
+ xor(Y, b, off, len);
+ multiplier.multiplyH(Y);
}
- private void gCTRBlock(byte[] buf, int bufCount, byte[] out, int outOff)
+ private byte[] getNextCounterBlock()
{
-// inc(counter);
for (int i = 15; i >= 12; --i)
{
byte b = (byte)((counter[i] + 1) & 0xff);
@@ -343,68 +506,56 @@ public class GCMBlockCipher
}
byte[] tmp = new byte[BLOCK_SIZE];
+ // TODO Sure would be nice if ciphers could operate on int[]
cipher.processBlock(counter, 0, tmp, 0);
+ return tmp;
+ }
- byte[] hashBytes;
- if (forEncryption)
- {
- System.arraycopy(ZEROES, bufCount, tmp, bufCount, BLOCK_SIZE - bufCount);
- hashBytes = tmp;
- }
- else
- {
- hashBytes = buf;
- }
+ private static void multiply(byte[] block, byte[] val)
+ {
+ byte[] tmp = Arrays.clone(block);
+ byte[] c = new byte[16];
- for (int i = bufCount - 1; i >= 0; --i)
+ for (int i = 0; i < 16; ++i)
{
- tmp[i] ^= buf[i];
- out[outOff + i] = tmp[i];
- }
+ byte bits = val[i];
+ for (int j = 7; j >= 0; --j)
+ {
+ if ((bits & (1 << j)) != 0)
+ {
+ xor(c, tmp);
+ }
-// gHASHBlock(hashBytes);
- xor(S, hashBytes);
- multiplier.multiplyH(S);
+ boolean lsb = (tmp[15] & 1) != 0;
+ shiftRight(tmp);
+ if (lsb)
+ {
+ // R = new byte[]{ 0xe1, ... };
+// xor(v, R);
+ tmp[0] ^= (byte)0xe1;
+ }
+ }
+ }
- totalLength += bufCount;
+ System.arraycopy(c, 0, block, 0, 16);
}
- private byte[] gHASH(byte[] b)
+ private static void shiftRight(byte[] block)
{
- byte[] Y = new byte[16];
-
- for (int pos = 0; pos < b.length; pos += 16)
+ int i = 0;
+ int bit = 0;
+ for (;;)
{
- byte[] X = new byte[16];
- int num = Math.min(b.length - pos, 16);
- System.arraycopy(b, pos, X, 0, num);
- xor(Y, X);
- multiplier.multiplyH(Y);
+ int b = block[i] & 0xff;
+ block[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
}
-
- return Y;
}
-// private void gHASHBlock(byte[] block)
-// {
-// xor(S, block);
-// multiplier.multiplyH(S);
-// }
-
-// private static void inc(byte[] block)
-// {
-// for (int i = 15; i >= 12; --i)
-// {
-// byte b = (byte)((block[i] + 1) & 0xff);
-// block[i] = b;
-//
-// if (b != 0)
-// {
-// break;
-// }
-// }
-// }
-
private static void xor(byte[] block, byte[] val)
{
for (int i = 15; i >= 0; --i)
@@ -413,9 +564,11 @@ public class GCMBlockCipher
}
}
- private static void packLength(long count, byte[] bs, int off)
+ private static void xor(byte[] block, byte[] val, int off, int len)
{
- Pack.intToBigEndian((int)(count >>> 32), bs, off);
- Pack.intToBigEndian((int)count, bs, off + 4);
+ while (len-- > 0)
+ {
+ block[len] ^= val[off + len];
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
index 728a2e7..5297698 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OFBBlockCipher.java
@@ -65,36 +65,40 @@ public class OFBBlockCipher
{
if (params instanceof ParametersWithIV)
{
- ParametersWithIV ivParam = (ParametersWithIV)params;
- byte[] iv = ivParam.getIV();
-
- if (iv.length < IV.length)
- {
- // prepend the supplied IV with zeros (per FIPS PUB 81)
- System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
- for (int i = 0; i < IV.length - iv.length; i++)
- {
- IV[i] = 0;
- }
- }
- else
- {
- System.arraycopy(iv, 0, IV, 0, IV.length);
- }
-
- reset();
-
- // if null it's an IV changed only.
- if (ivParam.getParameters() != null)
+ ParametersWithIV ivParam = (ParametersWithIV)params;
+ byte[] iv = ivParam.getIV();
+
+ if (iv.length < IV.length)
+ {
+ // prepend the supplied IV with zeros (per FIPS PUB 81)
+ System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
+ for (int i = 0; i < IV.length - iv.length; i++)
{
- cipher.init(true, ivParam.getParameters());
+ IV[i] = 0;
}
+ }
+ else
+ {
+ System.arraycopy(iv, 0, IV, 0, IV.length);
+ }
+
+ reset();
+
+ // if null it's an IV changed only.
+ if (ivParam.getParameters() != null)
+ {
+ cipher.init(true, ivParam.getParameters());
+ }
}
else
{
- reset();
+ reset();
+ // if it's null, key is to be reused.
+ if (params != null)
+ {
cipher.init(true, params);
+ }
}
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
index af9f18d..da8c4ae 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/SICBlockCipher.java
@@ -9,7 +9,8 @@ import org.bouncycastle.crypto.params.ParametersWithIV;
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
* block cipher. This mode is also known as CTR mode.
*/
-public class SICBlockCipher implements BlockCipher
+public class SICBlockCipher
+ implements BlockCipher
{
private final BlockCipher cipher;
private final int blockSize;
@@ -94,22 +95,10 @@ public class SICBlockCipher implements BlockCipher
out[outOff + i] = (byte)(counterOut[i] ^ in[inOff + i]);
}
- int carry = 1;
-
- for (int i = counter.length - 1; i >= 0; i--)
+ // increment counter by 1.
+ for (int i = counter.length - 1; i >= 0 && ++counter[i] == 0; i--)
{
- int x = (counter[i] & 0xff) + carry;
-
- if (x > 0xff)
- {
- carry = 1;
- }
- else
- {
- carry = 0;
- }
-
- counter[i] = (byte)x;
+ ; // do nothing - pre-increment and test for 0 in counter does the job.
}
return counter.length;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java
new file mode 100644
index 0000000..e1cc5c7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMExponentiator.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+public interface GCMExponentiator
+{
+ void init(byte[] x);
+ void exponentiateX(long pow, byte[] output);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index ce02be4..4875301 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -19,14 +19,23 @@ abstract class GCMUtil
return tmp;
}
+ static byte[] asBytes(int[] ns)
+ {
+ byte[] output = new byte[16];
+ Pack.intToBigEndian(ns, output, 0);
+ return output;
+ }
+
static int[] asInts(byte[] bs)
{
- int[] us = new int[4];
- us[0] = Pack.bigEndianToInt(bs, 0);
- us[1] = Pack.bigEndianToInt(bs, 4);
- us[2] = Pack.bigEndianToInt(bs, 8);
- us[3] = Pack.bigEndianToInt(bs, 12);
- return us;
+ int[] output = new int[4];
+ Pack.bigEndianToInt(bs, 0, output);
+ return output;
+ }
+
+ static void asInts(byte[] bs, int[] output)
+ {
+ Pack.bigEndianToInt(bs, 0, output);
}
static void multiply(byte[] block, byte[] val)
@@ -71,6 +80,17 @@ abstract class GCMUtil
}
}
+ static void multiplyP(int[] x, int[] output)
+ {
+ boolean lsb = (x[3] & 1) != 0;
+ shiftRight(x, output);
+ if (lsb)
+ {
+ output[0] ^= 0xe1000000;
+ }
+ }
+
+ // P is the value with only bit i=1 set
static void multiplyP8(int[] x)
{
// for (int i = 8; i != 0; --i)
@@ -89,6 +109,19 @@ abstract class GCMUtil
}
}
+ static void multiplyP8(int[] x, int[] output)
+ {
+ int lsw = x[3];
+ shiftRightN(x, 8, output);
+ for (int i = 7; i >= 0; --i)
+ {
+ if ((lsw & (1 << i)) != 0)
+ {
+ output[0] ^= (0xe1000000 >>> (7 - i));
+ }
+ }
+ }
+
static void shiftRight(byte[] block)
{
int i = 0;
@@ -105,6 +138,22 @@ abstract class GCMUtil
}
}
+ static void shiftRight(byte[] block, byte[] output)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i] & 0xff;
+ output[i] = (byte) ((b >>> 1) | bit);
+ if (++i == 16)
+ {
+ break;
+ }
+ bit = (b & 1) << 7;
+ }
+ }
+
static void shiftRight(int[] block)
{
int i = 0;
@@ -121,6 +170,22 @@ abstract class GCMUtil
}
}
+ static void shiftRight(int[] block, int[] output)
+ {
+ int i = 0;
+ int bit = 0;
+ for (;;)
+ {
+ int b = block[i];
+ output[i] = (b >>> 1) | bit;
+ if (++i == 4)
+ {
+ break;
+ }
+ bit = b << 31;
+ }
+ }
+
static void shiftRightN(int[] block, int n)
{
int i = 0;
@@ -137,6 +202,22 @@ abstract class GCMUtil
}
}
+ static void shiftRightN(int[] block, int n, int[] output)
+ {
+ int i = 0;
+ int bits = 0;
+ for (;;)
+ {
+ int b = block[i];
+ output[i] = (b >>> n) | bits;
+ if (++i == 4)
+ {
+ break;
+ }
+ bits = b << (32 - n);
+ }
+ }
+
static void xor(byte[] block, byte[] val)
{
for (int i = 15; i >= 0; --i)
@@ -145,6 +226,22 @@ abstract class GCMUtil
}
}
+ static void xor(byte[] block, byte[] val, int off, int len)
+ {
+ while (len-- > 0)
+ {
+ block[len] ^= val[off + len];
+ }
+ }
+
+ static void xor(byte[] block, byte[] val, byte[] output)
+ {
+ for (int i = 15; i >= 0; --i)
+ {
+ output[i] = (byte)(block[i] ^ val[i]);
+ }
+ }
+
static void xor(int[] block, int[] val)
{
for (int i = 3; i >= 0; --i)
@@ -152,4 +249,12 @@ abstract class GCMUtil
block[i] ^= val[i];
}
}
+
+ static void xor(int[] block, int[] val, int[] output)
+ {
+ for (int i = 3; i >= 0; --i)
+ {
+ output[i] = block[i] ^ val[i];
+ }
+ }
}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
new file mode 100644
index 0000000..a051208
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -0,0 +1,57 @@
+package org.bouncycastle.crypto.modes.gcm;
+
+import java.util.Vector;
+
+import org.bouncycastle.util.Arrays;
+
+public class Tables1kGCMExponentiator implements GCMExponentiator
+{
+ // A lookup table of the power-of-two powers of 'x'
+ // - lookupPowX2[i] = x^(2^i)
+ private Vector lookupPowX2;
+
+ public void init(byte[] x)
+ {
+ if (lookupPowX2 != null && Arrays.areEqual(x, (byte[])lookupPowX2.elementAt(0)))
+ {
+ return;
+ }
+
+ lookupPowX2 = new Vector(8);
+ lookupPowX2.addElement(Arrays.clone(x));
+ }
+
+ public void exponentiateX(long pow, byte[] output)
+ {
+ byte[] y = GCMUtil.oneAsBytes();
+ int bit = 0;
+ while (pow > 0)
+ {
+ if ((pow & 1L) != 0)
+ {
+ ensureAvailable(bit);
+ GCMUtil.multiply(y, (byte[])lookupPowX2.elementAt(bit));
+ }
+ ++bit;
+ pow >>>= 1;
+ }
+
+ System.arraycopy(y, 0, output, 0, 16);
+ }
+
+ private void ensureAvailable(int bit)
+ {
+ int count = lookupPowX2.size();
+ if (count <= bit)
+ {
+ byte[] tmp = (byte[])lookupPowX2.elementAt(count - 1);
+ do
+ {
+ tmp = Arrays.clone(tmp);
+ GCMUtil.multiply(tmp, tmp);
+ lookupPowX2.addElement(tmp);
+ }
+ while (++count <= bit);
+ }
+ }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
index 9d21cf0..8535db5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables8kGCMMultiplier.java
@@ -1,41 +1,40 @@
package org.bouncycastle.crypto.modes.gcm;
import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Arrays;
-public class Tables8kGCMMultiplier implements GCMMultiplier
+public class Tables8kGCMMultiplier implements GCMMultiplier
{
- private final int[][][] M = new int[32][16][];
+ private byte[] H;
+ private int[][][] M;
public void init(byte[] H)
{
- M[0][0] = new int[4];
- M[1][0] = new int[4];
- M[1][8] = GCMUtil.asInts(H);
-
- for (int j = 4; j >= 1; j >>= 1)
+ if (M == null)
{
- int[] tmp = new int[4];
- System.arraycopy(M[1][j + j], 0, tmp, 0, 4);
-
- GCMUtil.multiplyP(tmp);
- M[1][j] = tmp;
+ M = new int[32][16][4];
}
-
+ else if (Arrays.areEqual(this.H, H))
{
- int[] tmp = new int[4];
- System.arraycopy(M[1][1], 0, tmp, 0, 4);
-
- GCMUtil.multiplyP(tmp);
- M[0][8] = tmp;
+ return;
}
+ this.H = Arrays.clone(H);
+
+ // M[0][0] is ZEROES;
+ // M[1][0] is ZEROES;
+ GCMUtil.asInts(H, M[1][8]);
+
for (int j = 4; j >= 1; j >>= 1)
{
- int[] tmp = new int[4];
- System.arraycopy(M[0][j + j], 0, tmp, 0, 4);
+ GCMUtil.multiplyP(M[1][j + j], M[1][j]);
+ }
+
+ GCMUtil.multiplyP(M[1][1], M[0][8]);
- GCMUtil.multiplyP(tmp);
- M[0][j] = tmp;
+ for (int j = 4; j >= 1; j >>= 1)
+ {
+ GCMUtil.multiplyP(M[0][j + j], M[0][j]);
}
int i = 0;
@@ -45,11 +44,7 @@ public class Tables8kGCMMultiplier implements GCMMultiplier
{
for (int k = 1; k < j; ++k)
{
- int[] tmp = new int[4];
- System.arraycopy(M[i][j], 0, tmp, 0, 4);
-
- GCMUtil.xor(tmp, M[i][k]);
- M[i][j + k] = tmp;
+ GCMUtil.xor(M[i][j], M[i][k], M[i][j + k]);
}
}
@@ -60,14 +55,10 @@ public class Tables8kGCMMultiplier implements GCMMultiplier
if (i > 1)
{
- M[i][0] = new int[4];
+ // M[i][0] is ZEROES;
for(int j = 8; j > 0; j >>= 1)
{
- int[] tmp = new int[4];
- System.arraycopy(M[i - 2][j], 0, tmp, 0, 4);
-
- GCMUtil.multiplyP8(tmp);
- M[i][j] = tmp;
+ GCMUtil.multiplyP8(M[i - 2][j], M[i][j]);
}
}
}
@@ -96,4 +87,4 @@ public class Tables8kGCMMultiplier implements GCMMultiplier
Pack.intToBigEndian(z, x, 0);
}
-}
+} \ No newline at end of file