Skip to content

BCrypt implementation #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 157 additions & 45 deletions core/src/main/java/org/bouncycastle/crypto/engines/BlowfishEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,79 @@
import org.bouncycastle.crypto.params.KeyParameter;

/**
* A class that provides Blowfish key encryption operations,
* such as encoding data and generating keys.
* All the algorithms herein are from Applied Cryptography
* and implement a simplified cryptography interface.
* Implementation of the <a href="https://www.schneier.com/blowfish.html">Blowfish block cipher</a>
* as described in Applied Cryptography.
*/
public final class BlowfishEngine
public class BlowfishEngine
implements BlockCipher
{
private final static int[]

/** Minimum key size for Blowfish */
private static final int MIN_KEY_SIZE = 4;

/** Maximum key size for Blowfish */
// Some implementations allow up to 72 byte keys, but key bits beyond 448 do not affect every
// bit in ciphertext and are technically unspecified behaviour (despite ref impl not enforcing
// limit).
private static final int MAX_KEY_SIZE = 56;

/**
* A cyclic producer of 4 byte words.
*/
protected interface WordCycle
{
/**
* Produce the next word, cycling over the internal byte sequence as necessary.
*/
public int next();

}

/**
* A cycle of words over a byte sequence of arbitrary length.
*/
protected static class ByteCycle
implements WordCycle
{
private byte[] vals;
private int pos;

public ByteCycle(byte[] vals)
{
this.vals = vals;
}

public int next()
{
int current = 0;
for (int i = 0; i < 4; i++)
{
current = (current << 8) | (vals[pos++] & 0xFF);
if (pos == vals.length)
{
pos = 0;
}
}
return current;
}

}

/**
* A word cycle that always returns the value 0.
*/
protected static class ZeroCycle
implements WordCycle
{

public int next()
{
return 0;
}

}

private final static int[]
KP = {
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
Expand Down Expand Up @@ -308,22 +372,48 @@ public final class BlowfishEngine

private byte[] workingKey = null;

private boolean checkKeySize;

public BlowfishEngine()
{
this(true);
}

/**
* Construct a Blowfish engine.
*
* @param checkKeySize <code>true</code> to enforce spec defined min/max keysizes.
*/
protected BlowfishEngine(boolean checkKeySize)
{
this.checkKeySize = checkKeySize;
S0 = new int[SBOX_SK];
S1 = new int[SBOX_SK];
S2 = new int[SBOX_SK];
S3 = new int[SBOX_SK];
P = new int[P_SZ];
}

/**
* Factory method to construct a BlowfishEngine instance that does not check min/max key sizes.
* <p>
* Blowfish is defined for keys 32.448 bits in size, but some applications may require relaxed
* checking of key sizes.<br/>
* The original implementation of this engine did not check key sizes, so this factory can be
* used if interoperability/compatibility with such a system is required.
*/
public static BlowfishEngine uncheckedKeySize()
{
return new BlowfishEngine(false);
}

/**
* initialise a Blowfish cipher.
*
*
* @param encrypting whether or not we are for encryption.
* @param params the parameters required to set up the cipher.
* @exception IllegalArgumentException if the params argument is
* inappropriate.
* @param params the parameters required to set up the cipher - a {@link KeyParameter} with a
* 32..448 bit key.
* @exception IllegalArgumentException if the params argument is inappropriate.
*/
public void init(
boolean encrypting,
Expand All @@ -333,6 +423,18 @@ public void init(
{
this.encrypting = encrypting;
this.workingKey = ((KeyParameter)params).getKey();

/*
* (1)
* Initialise the S-boxes and the P-array, with a fixed string
* This string contains the hexadecimal digits of pi (3.141...)
*/
System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
System.arraycopy(KP, 0, P, 0, P_SZ);

setKey(this.workingKey);

return;
Expand Down Expand Up @@ -404,12 +506,16 @@ private int F(int x)
private void processTable(
int xl,
int xr,
int[] table)
int[] table,
WordCycle salt)
{
int size = table.length;

for (int s = 0; s < size; s += 2)
{
xl ^= salt.next();
xr ^= salt.next();

xl ^= P[0];

for (int i = 1; i < ROUNDS; i += 2)
Expand All @@ -428,23 +534,45 @@ private void processTable(
}
}

private void setKey(byte[] key)
/**
* Sets the key, using the key schedule.
*
* @param key the bytes of the key.
*/
protected void setKey(byte[] key)
{
setKey(key, new ZeroCycle());
}

/**
* The core Blowfish key schedule.
*
* @param key the bytes of the key
* @param salt a word sequence to xor into the words encrypted during the key schedule.
*/
protected final void setKey(byte[] key, WordCycle salt)
{
// WordCycle hook is to support bcrypt (which mixes key/salt into the key schedule).
// For Blowfish salt is always zeros.

if (key == null || key.length == 0)
{
throw new IllegalArgumentException("Key must be specified.");
}
if (checkKeySize && key.length < MIN_KEY_SIZE)
{
throw new IllegalArgumentException("Minimum size is " + (MIN_KEY_SIZE * 8) + " bits.");
}
if (checkKeySize && key.length > MAX_KEY_SIZE)
{
throw new IllegalArgumentException("Maximum size is " + (MAX_KEY_SIZE * 8) + " bits.");
}

/*
* - comments are from _Applied Crypto_, Schneier, p338
* please be careful comparing the two, AC numbers the
* arrays from 1, the enclosed code from 0.
*
* (1)
* Initialise the S-boxes and the P-array, with a fixed string
* This string contains the hexadecimal digits of pi (3.141...)
*/
System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
System.arraycopy(KS3, 0, S3, 0, SBOX_SK);

System.arraycopy(KP, 0, P, 0, P_SZ);

/*
* (2)
Expand All @@ -453,26 +581,10 @@ private void setKey(byte[] key)
* (up to P[17]). Repeatedly cycle through the key bits until the
* entire P-array has been XOR-ed with the key bits
*/
int keyLength = key.length;
int keyIndex = 0;

WordCycle keyCycle = new ByteCycle(key);
for (int i=0; i < P_SZ; i++)
{
// get the 32 bits of the key, in 4 * 8 bit chunks
int data = 0x0000000;
for (int j=0; j < 4; j++)
{
// create a 32 bit block
data = (data << 8) | (key[keyIndex++] & 0xff);

// wrap when we get to the end of the key
if (keyIndex >= keyLength)
{
keyIndex = 0;
}
}
// XOR the newly created 32 bit chunk onto the P-array
P[i] ^= data;
P[i] ^= keyCycle.next();
}

/*
Expand All @@ -496,11 +608,11 @@ private void setKey(byte[] key)
* continuously changing Blowfish algorithm
*/

processTable(0, 0, P);
processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
processTable(0, 0, P, salt);
processTable(P[P_SZ - 2], P[P_SZ - 1], S0, salt);
processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1, salt);
processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2, salt);
processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3, salt);
}

/**
Expand Down
Loading