summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-12-05 17:53:47 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-12-05 17:53:47 +0700
commit7e78f2644963a3f52023232d930c98fac8b13d0c (patch)
tree3360317d061d1482994c267710b84959b2475283
parentRfc3394WrapEngine: check input length in Unwrap (diff)
downloadBouncyCastle.NET-ed25519-7e78f2644963a3f52023232d930c98fac8b13d0c.tar.xz
AesWrap: update from bc-java
- fix wrap/unwrap of 64-bit keys
-rw-r--r--crypto/src/crypto/engines/AesWrapEngine.cs30
-rw-r--r--crypto/src/crypto/engines/RFC3394WrapEngine.cs148
-rw-r--r--crypto/test/src/crypto/test/AESWrapTest.cs46
3 files changed, 131 insertions, 93 deletions
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];