From 7e78f2644963a3f52023232d930c98fac8b13d0c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 5 Dec 2022 17:53:47 +0700 Subject: AesWrap: update from bc-java - fix wrap/unwrap of 64-bit keys --- crypto/src/crypto/engines/AesWrapEngine.cs | 30 +++-- crypto/src/crypto/engines/RFC3394WrapEngine.cs | 148 +++++++++++++------------ crypto/test/src/crypto/test/AESWrapTest.cs | 46 +++++--- 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 { - /// - /// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification. - ///

- /// For further details see: http://csrc.nist.gov/encryption/kms/key-wrap.pdf. - /// - public class AesWrapEngine + /// + /// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification. + ///

+ /// For further details see: http://csrc.nist.gov/encryption/kms/key-wrap.pdf. + /// + public class AesWrapEngine : Rfc3394WrapEngine { - public AesWrapEngine() + ///

+ /// Create a regular AesWrapEngine specifying the encrypt for wrapping, decrypt for unwrapping. + /// + public AesWrapEngine() : base(AesUtilities.CreateEngine()) { } - } + + /// + /// Create an AESWrapEngine where the underlying cipher is (optionally) set to decrypt for wrapping, encrypt for + /// unwrapping. + /// + /// true if underlying cipher should be used in decryption mode, false + /// otherwise. + 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]; -- cgit 1.4.1