Skip to content
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

Allow extended keys in BlowfishEngine for interoperability #519

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
15 changes: 7 additions & 8 deletions crypto/src/crypto/engines/BlowfishEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,15 @@ public void Init(
bool forEncryption,
ICipherParameters parameters)
{
if (!(parameters is KeyParameter))
if (!(parameters is BlowfishParameters) && !(parameters is KeyParameter))
throw new ArgumentException("invalid parameter passed to Blowfish init - " + Platform.GetTypeName(parameters));

var blowfishParameters = parameters is BlowfishParameters ?
(BlowfishParameters)parameters :
new BlowfishParameters(((KeyParameter)parameters).GetKey(), extendedKey: false);

this.encrypting = forEncryption;
this.workingKey = ((KeyParameter)parameters).GetKey();
this.workingKey = blowfishParameters.GetKey();
SetKey(this.workingKey);
}

Expand Down Expand Up @@ -441,11 +445,6 @@ private void ProcessTable(

private void SetKey(byte[] key)
{
if (key.Length < 4 || key.Length > 56)
{
throw new ArgumentException("key length must be in range 32 to 448 bits");
}

/*
* - comments are from _Applied Crypto_, Schneier, p338
* please be careful comparing the two, AC numbers the
Expand All @@ -469,7 +468,7 @@ 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 keyLength = System.Math.Min(key.Length, P_SZ*4);
int keyIndex = 0;

for (int i=0; i < P_SZ; i++)
Expand Down
46 changes: 46 additions & 0 deletions crypto/src/crypto/parameters/BlowfishParameters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using Org.BouncyCastle.Crypto;

namespace Org.BouncyCastle.Crypto.Parameters
{
public class BlowfishParameters
: KeyParameter
{
/**
* Blowfish takes a variable-length key, from 32 bits to 448 bits [1].
*
* Some implementations like OpenSSL [2] and Nettle [3] do not restrict the key size.
* Other algorithms like bcrypt [4] assume that Blowfish supports keys up to 576 bits,
* which is the maximum size for which all bits of the key will be used
* to initialize the P box, assuming the designed 16 rounds.
*
* For interoperability, BlowfishParameters can be created with an extended key,
* using the `extendedKey` parameter. It is not restricted in length,
* as neither OpenSSL nor Nettle restricts it, but only the first 576 bits
* will be used if longer.
*
* [1] https://datatracker.ietf.org/doc/html/draft-schneier-blowfish-00
* [2] https://github.com/openssl/openssl/blob/openssl-3.0/crypto/bf/bf_skey.c#L31
* [3] https://github.com/gnutls/nettle/blob/nettle_3.8.1_release_20220727/blowfish.c#L386
* [4] https://github.com/bcgit/bc-csharp/blob/release/v2.3/crypto/src/crypto/generators/BCrypt.cs#L587
*/
private const int MinKeyLen = 4;
private const int MaxKeyLen = 56;

public BlowfishParameters(
byte[] key,
bool extendedKey = false)
: base(key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
if (key.Length < MinKeyLen || (!extendedKey && key.Length > MaxKeyLen))
throw new ArgumentException($"key length must be in range {MinKeyLen * 8} to {MaxKeyLen * 8} bits");
}

public bool IsExtendedKey
{
get { return KeyLength > MaxKeyLen; }
}
}
}
45 changes: 44 additions & 1 deletion crypto/test/src/crypto/test/BlowfishTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,29 @@ public override string Name
new BlockCipherVectorTest(4, new BlowfishEngine(), new KeyParameter(Hex.Decode("0123456789ABCDEF")), "1111111111111111", "61F9C3802281B096"),
new BlockCipherVectorTest(5, new BlowfishEngine(), new KeyParameter(Hex.Decode("FEDCBA9876543210")), "0123456789ABCDEF", "0ACEAB0FC6A0A28D"),
new BlockCipherVectorTest(6, new BlowfishEngine(), new KeyParameter(Hex.Decode("7CA110454A1A6E57")), "01A1D6D039776742", "59C68245EB05282B"),
new BlockCipherVectorTest(7, new BlowfishEngine(), new KeyParameter(Hex.Decode("0131D9619DC1376E")), "5CD54CA83DEF57DA", "B1B8CC0B250F09A0")
new BlockCipherVectorTest(7, new BlowfishEngine(), new KeyParameter(Hex.Decode("0131D9619DC1376E")), "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"),

// with BlowfishParameters
new BlockCipherVectorTest(10, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("0000000000000000")), "0000000000000000", "4EF997456198DD78"),
new BlockCipherVectorTest(11, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("FFFFFFFFFFFFFFFF")), "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"),
new BlockCipherVectorTest(12, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("3000000000000000")), "1000000000000001", "7D856F9A613063F2"),
new BlockCipherVectorTest(13, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("1111111111111111")), "1111111111111111", "2466DD878B963C9D"),
new BlockCipherVectorTest(14, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("0123456789ABCDEF")), "1111111111111111", "61F9C3802281B096"),
new BlockCipherVectorTest(15, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("FEDCBA9876543210")), "0123456789ABCDEF", "0ACEAB0FC6A0A28D"),
new BlockCipherVectorTest(16, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("7CA110454A1A6E57")), "01A1D6D039776742", "59C68245EB05282B"),
new BlockCipherVectorTest(17, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("0131D9619DC1376E")), "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"),

// with BlowfishParameters and extended keys
new BlockCipherVectorTest(20, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "0000000000000000", "4ef997456198dd78"),
new BlockCipherVectorTest(21, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "0000000000000000", "4ef997456198dd78"),
new BlockCipherVectorTest(22, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), extendedKey: true), "ffffffffffffffff", "51866fd5b85ecb8a"),
new BlockCipherVectorTest(23, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), extendedKey: true), "ffffffffffffffff", "51866fd5b85ecb8a"),
new BlockCipherVectorTest(24, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"), extendedKey: true), "1111111111111111", "2466dd878b963c9d"),
new BlockCipherVectorTest(25, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"), extendedKey: true), "1111111111111111", "2466dd878b963c9d"),
new BlockCipherVectorTest(26, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "1000000000000001", "6252d3fc90256722"),
new BlockCipherVectorTest(27, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "1000000000000001", "6252d3fc90256722"),
new BlockCipherVectorTest(28, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("4f8afc23a1daac522510982b41c9186081b2a00537e193d85d004013ce520cc77aeb3c7822668c425adf7a9af977ad0c380f471229dcc73478d6a560ce3bc730df05e975a6d06d4e"), extendedKey: true), "63038f81aff43d3e", "88ccd0c218b35b0b"),
new BlockCipherVectorTest(29, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("4f8afc23a1daac522510982b41c9186081b2a00537e193d85d004013ce520cc77aeb3c7822668c425adf7a9af977ad0c380f471229dcc73478d6a560ce3bc730df05e975a6d06d4e9be8ca0e"), extendedKey: true), "63038f81aff43d3e", "88ccd0c218b35b0b"),
};

public BlowfishTest()
Expand Down Expand Up @@ -63,6 +85,27 @@ public void TestFunction()
Assert.AreEqual("key length must be in range 32 to 448 bits", e.Message);
}

// key range check -- new BlowfishParameters
try
{
blowfish.Init(true, new BlowfishParameters(new byte[1]));
Fail("no exception");
}
catch (ArgumentException e)
{
Assert.AreEqual("key length must be in range 32 to 448 bits", e.Message);
}

try
{
blowfish.Init(true, new BlowfishParameters(new byte[59]));
Fail("no exception");
}
catch (ArgumentException e)
{
Assert.AreEqual("key length must be in range 32 to 448 bits", e.Message);
}

Assert.AreEqual(Name + ": Okay", resultText);
}
}
Expand Down