diff options
Diffstat (limited to 'bcprov/src/main/java/org/bouncycastle/crypto/modes')
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 |