diff --git a/crypto/src/crypto/engines/AesWrapEngine.cs b/crypto/src/crypto/engines/AesWrapEngine.cs
index bf9e724cd..641e22f78 100644
--- a/crypto/src/crypto/engines/AesWrapEngine.cs
+++ b/crypto/src/crypto/engines/AesWrapEngine.cs
@@ -1,16 +1,30 @@
namespace Org.BouncyCastle.Crypto.Engines
{
- /// <remarks>
- /// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification.
- /// <p/>
- /// For further details see: <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
- /// </remarks>
- public class AesWrapEngine
+ /// <remarks>
+ /// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification.
+ /// <p/>
+ /// For further details see: <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
+ /// </remarks>
+ public class AesWrapEngine
: Rfc3394WrapEngine
{
- public AesWrapEngine()
+ /// <summary>
+ /// Create a regular AesWrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
+ /// </summary>
+ public AesWrapEngine()
: base(AesUtilities.CreateEngine())
{
}
- }
+
+ /// <summary>
+ /// Create an AESWrapEngine where the underlying cipher is (optionally) set to decrypt for wrapping, encrypt for
+ /// unwrapping.
+ /// </summary>
+ /// <param name="useReverseDirection">true if underlying cipher should be used in decryption mode, false
+ /// otherwise.</param>
+ public AesWrapEngine(bool useReverseDirection)
+ : base(AesUtilities.CreateEngine(), useReverseDirection)
+ {
+ }
+ }
}
diff --git a/crypto/src/crypto/engines/RFC3394WrapEngine.cs b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
index ff3a4e0a0..9744130d2 100644
--- a/crypto/src/crypto/engines/RFC3394WrapEngine.cs
+++ b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
@@ -16,22 +16,24 @@ namespace Org.BouncyCastle.Crypto.Engines
: IWrapper
{
private readonly IBlockCipher engine;
+ private readonly bool wrapCipherMode;
- private KeyParameter param;
+ private KeyParameter param;
private bool forWrapping;
- private byte[] iv =
- {
- 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6
- };
+ private byte[] iv = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6 };
- public Rfc3394WrapEngine(
- IBlockCipher engine)
+ public Rfc3394WrapEngine(IBlockCipher engine)
+ : this(engine, false)
{
- this.engine = engine;
}
+ public Rfc3394WrapEngine(IBlockCipher engine, bool useReverseDirection)
+ {
+ this.engine = engine;
+ this.wrapCipherMode = !useReverseDirection;
+ }
+
public virtual void Init(
bool forWrapping,
ICipherParameters parameters)
@@ -69,111 +71,113 @@ namespace Org.BouncyCastle.Crypto.Engines
get { return engine.AlgorithmName; }
}
- public virtual byte[] Wrap(
- byte[] input,
- int inOff,
- int inLen)
+ public virtual byte[] Wrap(byte[] input, int inOff, int inLen)
{
if (!forWrapping)
- {
throw new InvalidOperationException("not set for wrapping");
- }
+ if (inLen < 8)
+ throw new DataLengthException("wrap data must be at least 8 bytes");
- int n = inLen / 8;
+ int n = inLen / 8;
if ((n * 8) != inLen)
- {
throw new DataLengthException("wrap data must be a multiple of 8 bytes");
- }
- byte[] block = new byte[inLen + iv.Length];
- byte[] buf = new byte[8 + iv.Length];
+ engine.Init(wrapCipherMode, param);
+ byte[] block = new byte[inLen + iv.Length];
Array.Copy(iv, 0, block, 0, iv.Length);
Array.Copy(input, inOff, block, iv.Length, inLen);
- engine.Init(true, param);
-
- for (int j = 0; j != 6; j++)
+ if (n == 1)
{
- for (int i = 1; i <= n; i++)
- {
- Array.Copy(block, 0, buf, 0, iv.Length);
- Array.Copy(block, 8 * i, buf, iv.Length, 8);
- engine.ProcessBlock(buf, 0, buf, 0);
+ engine.ProcessBlock(block, 0, block, 0);
+ }
+ else
+ {
+ byte[] buf = new byte[8 + iv.Length];
- int t = n * j + i;
- for (int k = 1; t != 0; k++)
+ for (int j = 0; j != 6; j++)
+ {
+ for (int i = 1; i <= n; i++)
{
- byte v = (byte)t;
+ Array.Copy(block, 0, buf, 0, iv.Length);
+ Array.Copy(block, 8 * i, buf, iv.Length, 8);
+ engine.ProcessBlock(buf, 0, buf, 0);
- buf[iv.Length - k] ^= v;
- t = (int) ((uint)t >> 8);
- }
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
- Array.Copy(buf, 0, block, 0, 8);
- Array.Copy(buf, 8, block, 8 * i, 8);
+ buf[iv.Length - k] ^= v;
+ t = (int) ((uint)t >> 8);
+ }
+
+ Array.Copy(buf, 0, block, 0, 8);
+ Array.Copy(buf, 8, block, 8 * i, 8);
+ }
}
- }
+ }
- return block;
+ return block;
}
- public virtual byte[] Unwrap(
- byte[] input,
- int inOff,
- int inLen)
+ public virtual byte[] Unwrap(byte[] input, int inOff, int inLen)
{
if (forWrapping)
- {
throw new InvalidOperationException("not set for unwrapping");
- }
- if (inLen < iv.Length)
- {
+ if (inLen < 16)
throw new InvalidCipherTextException("unwrap data too short");
- }
int n = inLen / 8;
if ((n * 8) != inLen)
- {
throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
- }
-
- byte[] block = new byte[inLen - iv.Length];
- byte[] a = new byte[iv.Length];
- byte[] buf = new byte[8 + iv.Length];
- Array.Copy(input, inOff, a, 0, iv.Length);
- Array.Copy(input, inOff + iv.Length, block, 0, inLen - iv.Length);
+ engine.Init(!wrapCipherMode, param);
- engine.Init(false, param);
+ byte[] block = new byte[inLen - iv.Length];
+ byte[] a = new byte[iv.Length];
+ byte[] buf = new byte[8 + iv.Length];
n = n - 1;
- for (int j = 5; j >= 0; j--)
+ if (n == 1)
{
- for (int i = n; i >= 1; i--)
- {
- Array.Copy(a, 0, buf, 0, iv.Length);
- Array.Copy(block, 8 * (i - 1), buf, iv.Length, 8);
+ engine.ProcessBlock(input, inOff, buf, 0);
+ Array.Copy(buf, 0, a, 0, iv.Length);
+ Array.Copy(buf, iv.Length, block, 0, 8);
+ }
+ else
+ {
+ Array.Copy(input, inOff, a, 0, iv.Length);
+ Array.Copy(input, inOff + iv.Length, block, 0, inLen - iv.Length);
- int t = n * j + i;
- for (int k = 1; t != 0; k++)
+ for (int j = 5; j >= 0; j--)
+ {
+ for (int i = n; i >= 1; i--)
{
- byte v = (byte)t;
+ Array.Copy(a, 0, buf, 0, iv.Length);
+ Array.Copy(block, 8 * (i - 1), buf, iv.Length, 8);
- buf[iv.Length - k] ^= v;
- t = (int) ((uint)t >> 8);
- }
+ int t = n * j + i;
+ for (int k = 1; t != 0; k++)
+ {
+ byte v = (byte)t;
- engine.ProcessBlock(buf, 0, buf, 0);
- Array.Copy(buf, 0, a, 0, 8);
- Array.Copy(buf, 8, block, 8 * (i - 1), 8);
+ buf[iv.Length - k] ^= v;
+ t = (int) ((uint)t >> 8);
+ }
+
+ engine.ProcessBlock(buf, 0, buf, 0);
+ Array.Copy(buf, 0, a, 0, 8);
+ Array.Copy(buf, 8, block, 8 * (i - 1), 8);
+ }
}
- }
+ }
- if (!Arrays.FixedTimeEquals(a, iv))
+ if (!Arrays.FixedTimeEquals(a, iv))
throw new InvalidCipherTextException("checksum failed");
return block;
diff --git a/crypto/test/src/crypto/test/AESWrapTest.cs b/crypto/test/src/crypto/test/AESWrapTest.cs
index 7d9bcd497..27881a391 100644
--- a/crypto/test/src/crypto/test/AESWrapTest.cs
+++ b/crypto/test/src/crypto/test/AESWrapTest.cs
@@ -26,13 +26,14 @@ namespace Org.BouncyCastle.Crypto.Tests
}
}
- private ITestResult wrapTest(
- int id,
- byte[] kek,
- byte[] inBytes,
- byte[] outBytes)
+ private ITestResult WrapTest(int id, byte[] kek, byte[] inBytes, byte[] outBytes)
+ {
+ return WrapTest(id, kek, inBytes, outBytes, false);
+ }
+
+ private ITestResult WrapTest(int id, byte[] kek, byte[] inBytes, byte[] outBytes, bool useReverseDirection)
{
- IWrapper wrapper = new AesWrapEngine();
+ IWrapper wrapper = new AesWrapEngine(useReverseDirection);
wrapper.Init(true, new KeyParameter(kek));
@@ -76,7 +77,7 @@ namespace Org.BouncyCastle.Crypto.Tests
byte[] kek1 = Hex.Decode("000102030405060708090a0b0c0d0e0f");
byte[] in1 = Hex.Decode("00112233445566778899aabbccddeeff");
byte[] out1 = Hex.Decode("1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5");
- ITestResult result = wrapTest(1, kek1, in1, out1);
+ ITestResult result = WrapTest(1, kek1, in1, out1);
if (!result.IsSuccessful())
{
@@ -86,7 +87,7 @@ namespace Org.BouncyCastle.Crypto.Tests
byte[] kek2 = Hex.Decode("000102030405060708090a0b0c0d0e0f1011121314151617");
byte[] in2 = Hex.Decode("00112233445566778899aabbccddeeff");
byte[] out2 = Hex.Decode("96778b25ae6ca435f92b5b97c050aed2468ab8a17ad84e5d");
- result = wrapTest(2, kek2, in2, out2);
+ result = WrapTest(2, kek2, in2, out2);
if (!result.IsSuccessful())
{
return result;
@@ -95,7 +96,7 @@ namespace Org.BouncyCastle.Crypto.Tests
byte[] kek3 = Hex.Decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
byte[] in3 = Hex.Decode("00112233445566778899aabbccddeeff");
byte[] out3 = Hex.Decode("64e8c3f9ce0f5ba263e9777905818a2a93c8191e7d6e8ae7");
- result = wrapTest(3, kek3, in3, out3);
+ result = WrapTest(3, kek3, in3, out3);
if (!result.IsSuccessful())
{
return result;
@@ -104,7 +105,7 @@ namespace Org.BouncyCastle.Crypto.Tests
byte[] kek4 = Hex.Decode("000102030405060708090a0b0c0d0e0f1011121314151617");
byte[] in4 = Hex.Decode("00112233445566778899aabbccddeeff0001020304050607");
byte[] out4 = Hex.Decode("031d33264e15d33268f24ec260743edce1c6c7ddee725a936ba814915c6762d2");
- result = wrapTest(4, kek4, in4, out4);
+ result = WrapTest(4, kek4, in4, out4);
if (!result.IsSuccessful())
{
return result;
@@ -113,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Tests
byte[] kek5 = Hex.Decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
byte[] in5 = Hex.Decode("00112233445566778899aabbccddeeff0001020304050607");
byte[] out5 = Hex.Decode("a8f9bc1612c68b3ff6e6f4fbe30e71e4769c8b80a32cb8958cd5d17d6b254da1");
- result = wrapTest(5, kek5, in5, out5);
+ result = WrapTest(5, kek5, in5, out5);
if (!result.IsSuccessful())
{
return result;
@@ -122,13 +123,32 @@ namespace Org.BouncyCastle.Crypto.Tests
byte[] kek6 = Hex.Decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
byte[] in6 = Hex.Decode("00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f");
byte[] out6 = Hex.Decode("28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21");
- result = wrapTest(6, kek6, in6, out6);
+ result = WrapTest(6, kek6, in6, out6);
if (!result.IsSuccessful())
{
return result;
}
- IWrapper wrapper = new AesWrapEngine();
+ byte[] kek7 = Hex.Decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
+ byte[] in7 = Hex.Decode("00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f");
+ byte[] out7 = Hex.Decode("cba01acbdb4c7c39fa59babb383c485f318837208731a81c735b5be6ba710375a1159e26a9b57228");
+ result = WrapTest(7, kek7, in7, out7, true);
+ if (!result.IsSuccessful())
+ {
+ return result;
+ }
+
+ // Example of 64-bit input (which uses a simplified wrapping algorithm)
+ byte[] kek8 = Hex.Decode("574957151fc2afe0fa3dc7a9a7da6495");
+ byte[] in8 = Hex.Decode("0001020304050607");
+ byte[] out8 = Hex.Decode("6f0b501f1f2f59e3ae605aa679ce43a6");
+ result = WrapTest(8, kek8, in8, out8);
+ if (!result.IsSuccessful())
+ {
+ return result;
+ }
+
+ IWrapper wrapper = new AesWrapEngine();
KeyParameter key = new KeyParameter(new byte[16]);
byte[] buf = new byte[16];
|