Skip to content

Commit 48fa32e

Browse files
committed
Allow extended keys in BlowfishEngine for interoperability
1 parent 7e33620 commit 48fa32e

File tree

3 files changed

+97
-9
lines changed

3 files changed

+97
-9
lines changed

crypto/src/crypto/engines/BlowfishEngine.cs

+7-8
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,15 @@ public void Init(
329329
bool forEncryption,
330330
ICipherParameters parameters)
331331
{
332-
if (!(parameters is KeyParameter))
332+
if (!(parameters is BlowfishParameters) && !(parameters is KeyParameter))
333333
throw new ArgumentException("invalid parameter passed to Blowfish init - " + Platform.GetTypeName(parameters));
334334

335+
var blowfishParameters = parameters is BlowfishParameters ?
336+
(BlowfishParameters)parameters :
337+
new BlowfishParameters(((KeyParameter)parameters).GetKey(), extendedKey: false);
338+
335339
this.encrypting = forEncryption;
336-
this.workingKey = ((KeyParameter)parameters).GetKey();
340+
this.workingKey = blowfishParameters.GetKey();
337341
SetKey(this.workingKey);
338342
}
339343

@@ -441,11 +445,6 @@ private void ProcessTable(
441445

442446
private void SetKey(byte[] key)
443447
{
444-
if (key.Length < 4 || key.Length > 56)
445-
{
446-
throw new ArgumentException("key length must be in range 32 to 448 bits");
447-
}
448-
449448
/*
450449
* - comments are from _Applied Crypto_, Schneier, p338
451450
* please be careful comparing the two, AC numbers the
@@ -469,7 +468,7 @@ private void SetKey(byte[] key)
469468
* (up to P[17]). Repeatedly cycle through the key bits until the
470469
* entire P-array has been XOR-ed with the key bits
471470
*/
472-
int keyLength = key.Length;
471+
int keyLength = System.Math.Min(key.Length, P_SZ*4);
473472
int keyIndex = 0;
474473

475474
for (int i=0; i < P_SZ; i++)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using Org.BouncyCastle.Crypto;
3+
4+
namespace Org.BouncyCastle.Crypto.Parameters
5+
{
6+
public class BlowfishParameters
7+
: KeyParameter
8+
{
9+
/**
10+
* Blowfish takes a variable-length key, from 32 bits to 448 bits [1].
11+
*
12+
* Some implementations like OpenSSL [2] and Nettle [3] do not restrict the key size.
13+
* Other algorithms like bcrypt [4] assume that Blowfish supports keys up to 576 bits,
14+
* which is the maximum size for which all bits of the key will be used
15+
* to initialize the P box, assuming the designed 16 rounds.
16+
*
17+
* For interoperability, BlowfishParameters can be created with an extended key,
18+
* using the `extendedKey` parameter. It is not restricted in length,
19+
* as neither OpenSSL nor Nettle restricts it, but only the first 576 bits
20+
* will be used if longer.
21+
*
22+
* [1] https://datatracker.ietf.org/doc/html/draft-schneier-blowfish-00
23+
* [2] https://github.com/openssl/openssl/blob/openssl-3.0/crypto/bf/bf_skey.c#L31
24+
* [3] https://github.com/gnutls/nettle/blob/nettle_3.8.1_release_20220727/blowfish.c#L386
25+
* [4] https://github.com/bcgit/bc-csharp/blob/release/v2.3/crypto/src/crypto/generators/BCrypt.cs#L587
26+
*/
27+
private const int MinKeyLen = 4;
28+
private const int MaxKeyLen = 56;
29+
30+
public BlowfishParameters(
31+
byte[] key,
32+
bool extendedKey = false)
33+
: base(key)
34+
{
35+
if (key == null)
36+
throw new ArgumentNullException(nameof(key));
37+
if (key.Length < MinKeyLen || (!extendedKey && key.Length > MaxKeyLen))
38+
throw new ArgumentException($"key length must be in range {MinKeyLen * 8} to {MaxKeyLen * 8} bits");
39+
}
40+
41+
public bool IsExtendedKey
42+
{
43+
get { return KeyLength > MaxKeyLen; }
44+
}
45+
}
46+
}

crypto/test/src/crypto/test/BlowfishTest.cs

+44-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,29 @@ public override string Name
2727
new BlockCipherVectorTest(4, new BlowfishEngine(), new KeyParameter(Hex.Decode("0123456789ABCDEF")), "1111111111111111", "61F9C3802281B096"),
2828
new BlockCipherVectorTest(5, new BlowfishEngine(), new KeyParameter(Hex.Decode("FEDCBA9876543210")), "0123456789ABCDEF", "0ACEAB0FC6A0A28D"),
2929
new BlockCipherVectorTest(6, new BlowfishEngine(), new KeyParameter(Hex.Decode("7CA110454A1A6E57")), "01A1D6D039776742", "59C68245EB05282B"),
30-
new BlockCipherVectorTest(7, new BlowfishEngine(), new KeyParameter(Hex.Decode("0131D9619DC1376E")), "5CD54CA83DEF57DA", "B1B8CC0B250F09A0")
30+
new BlockCipherVectorTest(7, new BlowfishEngine(), new KeyParameter(Hex.Decode("0131D9619DC1376E")), "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"),
31+
32+
// with BlowfishParameters
33+
new BlockCipherVectorTest(10, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("0000000000000000")), "0000000000000000", "4EF997456198DD78"),
34+
new BlockCipherVectorTest(11, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("FFFFFFFFFFFFFFFF")), "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"),
35+
new BlockCipherVectorTest(12, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("3000000000000000")), "1000000000000001", "7D856F9A613063F2"),
36+
new BlockCipherVectorTest(13, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("1111111111111111")), "1111111111111111", "2466DD878B963C9D"),
37+
new BlockCipherVectorTest(14, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("0123456789ABCDEF")), "1111111111111111", "61F9C3802281B096"),
38+
new BlockCipherVectorTest(15, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("FEDCBA9876543210")), "0123456789ABCDEF", "0ACEAB0FC6A0A28D"),
39+
new BlockCipherVectorTest(16, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("7CA110454A1A6E57")), "01A1D6D039776742", "59C68245EB05282B"),
40+
new BlockCipherVectorTest(17, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("0131D9619DC1376E")), "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"),
41+
42+
// with BlowfishParameters and extended keys
43+
new BlockCipherVectorTest(20, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "0000000000000000", "4ef997456198dd78"),
44+
new BlockCipherVectorTest(21, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "0000000000000000", "4ef997456198dd78"),
45+
new BlockCipherVectorTest(22, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), extendedKey: true), "ffffffffffffffff", "51866fd5b85ecb8a"),
46+
new BlockCipherVectorTest(23, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), extendedKey: true), "ffffffffffffffff", "51866fd5b85ecb8a"),
47+
new BlockCipherVectorTest(24, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"), extendedKey: true), "1111111111111111", "2466dd878b963c9d"),
48+
new BlockCipherVectorTest(25, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"), extendedKey: true), "1111111111111111", "2466dd878b963c9d"),
49+
new BlockCipherVectorTest(26, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "1000000000000001", "6252d3fc90256722"),
50+
new BlockCipherVectorTest(27, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), extendedKey: true), "1000000000000001", "6252d3fc90256722"),
51+
new BlockCipherVectorTest(28, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("4f8afc23a1daac522510982b41c9186081b2a00537e193d85d004013ce520cc77aeb3c7822668c425adf7a9af977ad0c380f471229dcc73478d6a560ce3bc730df05e975a6d06d4e"), extendedKey: true), "63038f81aff43d3e", "88ccd0c218b35b0b"),
52+
new BlockCipherVectorTest(29, new BlowfishEngine(), new BlowfishParameters(Hex.Decode("4f8afc23a1daac522510982b41c9186081b2a00537e193d85d004013ce520cc77aeb3c7822668c425adf7a9af977ad0c380f471229dcc73478d6a560ce3bc730df05e975a6d06d4e9be8ca0e"), extendedKey: true), "63038f81aff43d3e", "88ccd0c218b35b0b"),
3153
};
3254

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

88+
// key range check -- new BlowfishParameters
89+
try
90+
{
91+
blowfish.Init(true, new BlowfishParameters(new byte[1]));
92+
Fail("no exception");
93+
}
94+
catch (ArgumentException e)
95+
{
96+
Assert.AreEqual("key length must be in range 32 to 448 bits", e.Message);
97+
}
98+
99+
try
100+
{
101+
blowfish.Init(true, new BlowfishParameters(new byte[59]));
102+
Fail("no exception");
103+
}
104+
catch (ArgumentException e)
105+
{
106+
Assert.AreEqual("key length must be in range 32 to 448 bits", e.Message);
107+
}
108+
66109
Assert.AreEqual(Name + ": Okay", resultText);
67110
}
68111
}

0 commit comments

Comments
 (0)