From 8b1948ee2bd2bb467d2afe7d66c2675e654aead5 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 4 Mar 2023 18:01:17 +0700 Subject: Refactor VMPC classes --- crypto/src/crypto/engines/VMPCEngine.cs | 121 +++++------- crypto/src/crypto/engines/VMPCKSA3Engine.cs | 42 +---- crypto/src/crypto/macs/VMPCMac.cs | 259 ++++++++++++++------------ crypto/src/crypto/prng/VMPCRandomGenerator.cs | 7 + 4 files changed, 199 insertions(+), 230 deletions(-) diff --git a/crypto/src/crypto/engines/VMPCEngine.cs b/crypto/src/crypto/engines/VMPCEngine.cs index f34259248..897068e1a 100644 --- a/crypto/src/crypto/engines/VMPCEngine.cs +++ b/crypto/src/crypto/engines/VMPCEngine.cs @@ -7,10 +7,6 @@ namespace Org.BouncyCastle.Crypto.Engines public class VmpcEngine : IStreamCipher { - /* - * variables to hold the state of the VMPC engine during encryption and - * decryption - */ protected byte n = 0; protected byte[] P = null; protected byte s = 0; @@ -18,10 +14,7 @@ namespace Org.BouncyCastle.Crypto.Engines protected byte[] workingIV; protected byte[] workingKey; - public virtual string AlgorithmName - { - get { return "VMPC"; } - } + public virtual string AlgorithmName => "VMPC"; /** * initialise a VMPC cipher. @@ -33,64 +26,41 @@ namespace Org.BouncyCastle.Crypto.Engines * @exception ArgumentException * if the params argument is inappropriate. */ - public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + public virtual void Init(bool forEncryption, ICipherParameters parameters) { - if (!(parameters is ParametersWithIV)) + if (!(parameters is ParametersWithIV ivParams)) throw new ArgumentException("VMPC Init parameters must include an IV"); - - ParametersWithIV ivParams = (ParametersWithIV) parameters; - - if (!(ivParams.Parameters is KeyParameter)) + if (!(ivParams.Parameters is KeyParameter key)) throw new ArgumentException("VMPC Init parameters must include a key"); - KeyParameter key = (KeyParameter)ivParams.Parameters; - - this.workingIV = ivParams.GetIV(); + int keyLength = key.KeyLength; + if (keyLength < 16 || keyLength > 64) + throw new ArgumentException("VMPC requires 16 to 64 bytes of key"); - if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768) - throw new ArgumentException("VMPC requires 1 to 768 bytes of IV"); + int ivLength = ivParams.IVLength; + if (ivLength < 16 || ivLength > 64) + throw new ArgumentException("VMPC requires 16 to 64 bytes of IV"); this.workingKey = key.GetKey(); + this.workingIV = ivParams.GetIV(); InitKey(this.workingKey, this.workingIV); } - protected virtual void InitKey( - byte[] keyBytes, - byte[] ivBytes) + protected virtual void InitKey(byte[] keyBytes, byte[] ivBytes) { + n = 0; s = 0; P = new byte[256]; for (int i = 0; i < 256; i++) { - P[i] = (byte) i; + P[i] = (byte)i; } - - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - } - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - } - n = 0; + KsaRound(P, ref s, keyBytes); + KsaRound(P, ref s, ivBytes); } - public virtual void ProcessBytes( - byte[] input, - int inOff, - int len, - byte[] output, - int outOff) + public virtual void ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { Check.DataLength(input, inOff, len, "input buffer too short"); Check.OutputLength(output, outOff, len, "output buffer too short"); @@ -98,16 +68,12 @@ namespace Org.BouncyCastle.Crypto.Engines for (int i = 0; i < len; i++) { byte pn = P[n]; - s = P[(s + pn) & 0xff]; + s = P[(s + pn) & 0xFF]; byte ps = P[s]; - byte z = P[(P[ps] + 1) & 0xff]; - // encryption + output[outOff + i] = (byte)(input[inOff + i] ^ P[(P[ps] + 1) & 0xFF]); P[n] = ps; P[s] = pn; n = (byte)(n + 1); - - // xor - output[i + outOff] = (byte)(input[i + inOff] ^ z); } } @@ -119,16 +85,12 @@ namespace Org.BouncyCastle.Crypto.Engines for (int i = 0; i < input.Length; i++) { byte pn = P[n]; - s = P[(s + pn) & 0xff]; + s = P[(s + pn) & 0xFF]; byte ps = P[s]; - byte z = P[(P[ps] + 1) & 0xff]; - // encryption + output[i] = (byte)(input[i] ^ P[(P[ps] + 1) & 0xFF]); P[n] = ps; P[s] = pn; n = (byte)(n + 1); - - // xor - output[i] = (byte)(input[i] ^ z); } } #endif @@ -138,19 +100,36 @@ namespace Org.BouncyCastle.Crypto.Engines InitKey(this.workingKey, this.workingIV); } - public virtual byte ReturnByte( - byte input) + public virtual byte ReturnByte(byte input) { - s = P[(s + P[n & 0xff]) & 0xff]; - byte z = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; - // encryption - byte temp = P[n & 0xff]; - P[n & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - n = (byte) ((n + 1) & 0xff); - - // xor - return (byte) (input ^ z); + byte pn = P[n]; + s = P[(s + pn) & 0xFF]; + byte ps = P[s]; + byte output = (byte)(input ^ P[(P[ps] + 1) & 0xFF]); + P[n] = ps; + P[s] = pn; + n = (byte)(n + 1); + return output; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void KsaRound(byte[] P, ref byte S, ReadOnlySpan input) +#else + internal static void KsaRound(byte[] P, ref byte S, byte[] input) +#endif + { + byte s = S; + int modulus = input.Length, offset = 0; + for (int m = 0; m < 768; m++) + { + byte pm = P[m & 0xFF]; + s = P[(s + pm + input[offset]) & 0xFF]; + int t = offset + 1 - modulus; + offset = t + (modulus & (t >> 31)); + P[m & 0xFF] = P[s]; + P[s] = pm; + } + S = s; } } } diff --git a/crypto/src/crypto/engines/VMPCKSA3Engine.cs b/crypto/src/crypto/engines/VMPCKSA3Engine.cs index 95b6813b7..d44c15b0b 100644 --- a/crypto/src/crypto/engines/VMPCKSA3Engine.cs +++ b/crypto/src/crypto/engines/VMPCKSA3Engine.cs @@ -5,47 +5,13 @@ namespace Org.BouncyCastle.Crypto.Engines public class VmpcKsa3Engine : VmpcEngine { - public override string AlgorithmName - { - get { return "VMPC-KSA3"; } - } + public override string AlgorithmName => "VMPC-KSA3"; - protected override void InitKey( - byte[] keyBytes, - byte[] ivBytes) + protected override void InitKey(byte[] keyBytes, byte[] ivBytes) { - s = 0; - P = new byte[256]; - for (int i = 0; i < 256; i++) - { - P[i] = (byte) i; - } - - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - } - - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - } - - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - } + base.InitKey(keyBytes, ivBytes); - n = 0; + KsaRound(P, ref s, keyBytes); } } } diff --git a/crypto/src/crypto/macs/VMPCMac.cs b/crypto/src/crypto/macs/VMPCMac.cs index c2902179f..a64ce4ffc 100644 --- a/crypto/src/crypto/macs/VMPCMac.cs +++ b/crypto/src/crypto/macs/VMPCMac.cs @@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Macs @@ -13,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Macs private byte[] P = null; private byte s = 0; - private byte[] T; + private readonly byte[] T = new byte[32]; private byte[] workingIV; private byte[] workingKey; @@ -28,43 +29,43 @@ namespace Org.BouncyCastle.Crypto.Macs // Execute the Post-Processing Phase for (int r = 1; r < 25; r++) { - s = P[(s + P[n & 0xff]) & 0xff]; - - x4 = P[(x4 + x3 + r) & 0xff]; - x3 = P[(x3 + x2 + r) & 0xff]; - x2 = P[(x2 + x1 + r) & 0xff]; - x1 = P[(x1 + s + r) & 0xff]; - T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1); - T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2); - T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3); - T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4); - g = (byte) ((g + 4) & 0x1f); - - byte temp = P[n & 0xff]; - P[n & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - n = (byte) ((n + 1) & 0xff); + s = P[(s + P[n & 0xFF]) & 0xFF]; + + x4 = P[(x4 + x3 + r) & 0xFF]; + x3 = P[(x3 + x2 + r) & 0xFF]; + x2 = P[(x2 + x1 + r) & 0xFF]; + x1 = P[(x1 + s + r) & 0xFF]; + T[g & 0x1F] = (byte)(T[g & 0x1F] ^ x1); + T[(g + 1) & 0x1F] = (byte)(T[(g + 1) & 0x1F] ^ x2); + T[(g + 2) & 0x1F] = (byte)(T[(g + 2) & 0x1F] ^ x3); + T[(g + 3) & 0x1F] = (byte)(T[(g + 3) & 0x1F] ^ x4); + g = (byte)((g + 4) & 0x1F); + + byte temp = P[n & 0xFF]; + P[n & 0xFF] = P[s & 0xFF]; + P[s & 0xFF] = temp; + n = (byte)((n + 1) & 0xFF); } // Input T to the IV-phase of the VMPC KSA for (int m = 0; m < 768; m++) { - s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; + s = P[(s + P[m & 0xFF] + T[m & 0x1F]) & 0xFF]; + byte temp = P[m & 0xFF]; + P[m & 0xFF] = P[s & 0xFF]; + P[s & 0xFF] = temp; } // Store 20 new outputs of the VMPC Stream Cipher input table M byte[] M = new byte[20]; for (int i = 0; i < 20; i++) { - s = P[(s + P[i & 0xff]) & 0xff]; - M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + s = P[(s + P[i & 0xFF]) & 0xFF]; + M[i] = P[(P[(P[s & 0xFF]) & 0xFF] + 1) & 0xFF]; - byte temp = P[i & 0xff]; - P[i & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; + byte temp = P[i & 0xFF]; + P[i & 0xFF] = P[s & 0xFF]; + P[s & 0xFF] = temp; } Array.Copy(M, 0, output, outOff, M.Length); @@ -80,43 +81,43 @@ namespace Org.BouncyCastle.Crypto.Macs // Execute the Post-Processing Phase for (int r = 1; r < 25; r++) { - s = P[(s + P[n & 0xff]) & 0xff]; - - x4 = P[(x4 + x3 + r) & 0xff]; - x3 = P[(x3 + x2 + r) & 0xff]; - x2 = P[(x2 + x1 + r) & 0xff]; - x1 = P[(x1 + s + r) & 0xff]; - T[g & 0x1f] = (byte)(T[g & 0x1f] ^ x1); - T[(g + 1) & 0x1f] = (byte)(T[(g + 1) & 0x1f] ^ x2); - T[(g + 2) & 0x1f] = (byte)(T[(g + 2) & 0x1f] ^ x3); - T[(g + 3) & 0x1f] = (byte)(T[(g + 3) & 0x1f] ^ x4); - g = (byte)((g + 4) & 0x1f); - - byte temp = P[n & 0xff]; - P[n & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - n = (byte)((n + 1) & 0xff); + s = P[(s + P[n & 0xFF]) & 0xFF]; + + x4 = P[(x4 + x3 + r) & 0xFF]; + x3 = P[(x3 + x2 + r) & 0xFF]; + x2 = P[(x2 + x1 + r) & 0xFF]; + x1 = P[(x1 + s + r) & 0xFF]; + T[g & 0x1F] = (byte)(T[g & 0x1F] ^ x1); + T[(g + 1) & 0x1F] = (byte)(T[(g + 1) & 0x1F] ^ x2); + T[(g + 2) & 0x1F] = (byte)(T[(g + 2) & 0x1F] ^ x3); + T[(g + 3) & 0x1F] = (byte)(T[(g + 3) & 0x1F] ^ x4); + g = (byte)((g + 4) & 0x1F); + + byte temp = P[n & 0xFF]; + P[n & 0xFF] = P[s & 0xFF]; + P[s & 0xFF] = temp; + n = (byte)((n + 1) & 0xFF); } // Input T to the IV-phase of the VMPC KSA for (int m = 0; m < 768; m++) { - s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; + s = P[(s + P[m & 0xFF] + T[m & 0x1F]) & 0xFF]; + byte temp = P[m & 0xFF]; + P[m & 0xFF] = P[s & 0xFF]; + P[s & 0xFF] = temp; } // Store 20 new outputs of the VMPC Stream Cipher input table M byte[] M = new byte[20]; for (int i = 0; i < 20; i++) { - s = P[(s + P[i & 0xff]) & 0xff]; - M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]; + s = P[(s + P[i & 0xFF]) & 0xFF]; + M[i] = P[(P[(P[s & 0xFF]) & 0xFF] + 1) & 0xFF]; - byte temp = P[i & 0xff]; - P[i & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; + byte temp = P[i & 0xFF]; + P[i & 0xFF] = P[s & 0xFF]; + P[s & 0xFF] = temp; } M.CopyTo(output); @@ -126,113 +127,129 @@ namespace Org.BouncyCastle.Crypto.Macs } #endif - public virtual string AlgorithmName - { - get { return "VMPC-MAC"; } - } + public virtual string AlgorithmName => "VMPC-MAC"; - public virtual int GetMacSize() - { - return 20; - } + public virtual int GetMacSize() => 20; public virtual void Init(ICipherParameters parameters) { - if (!(parameters is ParametersWithIV)) + if (!(parameters is ParametersWithIV ivParams)) throw new ArgumentException("VMPC-MAC Init parameters must include an IV", "parameters"); - - ParametersWithIV ivParams = (ParametersWithIV) parameters; - KeyParameter key = (KeyParameter) ivParams.Parameters; - - if (!(ivParams.Parameters is KeyParameter)) + if (!(ivParams.Parameters is KeyParameter key)) throw new ArgumentException("VMPC-MAC Init parameters must include a key", "parameters"); - this.workingIV = ivParams.GetIV(); + int keyLength = key.KeyLength; + if (keyLength < 16 || keyLength > 64) + throw new ArgumentException("VMPC requires 16 to 64 bytes of key"); - if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768) - throw new ArgumentException("VMPC-MAC requires 1 to 768 bytes of IV", "parameters"); + int ivLength = ivParams.IVLength; + if (ivLength < 16 || ivLength > 64) + throw new ArgumentException("VMPC requires 16 to 64 bytes of IV"); - this.workingKey = key.GetKey(); + this.workingKey = key.GetKey(); + this.workingIV = ivParams.GetIV(); Reset(); - } - private void initKey(byte[] keyBytes, byte[] ivBytes) + private void InitKey(byte[] keyBytes, byte[] ivBytes) { - s = 0; + n = 0; + s = 0; P = new byte[256]; for (int i = 0; i < 256; i++) { - P[i] = (byte) i; - } - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + keyBytes[m % keyBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - } - for (int m = 0; m < 768; m++) - { - s = P[(s + P[m & 0xff] + ivBytes[m % ivBytes.Length]) & 0xff]; - byte temp = P[m & 0xff]; - P[m & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; + P[i] = (byte)i; } - n = 0; + VmpcEngine.KsaRound(P, ref s, keyBytes); + VmpcEngine.KsaRound(P, ref s, ivBytes); } public virtual void Reset() { - initKey(this.workingKey, this.workingIV); + InitKey(this.workingKey, this.workingIV); g = x1 = x2 = x3 = x4 = n = 0; - T = new byte[32]; - for (int i = 0; i < 32; i++) - { - T[i] = 0; - } + Array.Clear(T, 0, T.Length); } public virtual void Update(byte input) { - s = P[(s + P[n & 0xff]) & 0xff]; - byte c = (byte) (input ^ P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff]); - - x4 = P[(x4 + x3) & 0xff]; - x3 = P[(x3 + x2) & 0xff]; - x2 = P[(x2 + x1) & 0xff]; - x1 = P[(x1 + s + c) & 0xff]; - T[g & 0x1f] = (byte) (T[g & 0x1f] ^ x1); - T[(g + 1) & 0x1f] = (byte) (T[(g + 1) & 0x1f] ^ x2); - T[(g + 2) & 0x1f] = (byte) (T[(g + 2) & 0x1f] ^ x3); - T[(g + 3) & 0x1f] = (byte) (T[(g + 3) & 0x1f] ^ x4); - g = (byte) ((g + 4) & 0x1f); - - byte temp = P[n & 0xff]; - P[n & 0xff] = P[s & 0xff]; - P[s & 0xff] = temp; - n = (byte) ((n + 1) & 0xff); - } - - public virtual void BlockUpdate(byte[] input, int inOff, int inLen) + byte pn = P[n]; + s = P[(s + pn) & 0xFF]; + byte ps = P[s]; + byte c = (byte)(input ^ P[(P[ps] + 1) & 0xFF]); + + x4 = P[(x4 + x3) & 0xFF]; + x3 = P[(x3 + x2) & 0xFF]; + x2 = P[(x2 + x1) & 0xFF]; + x1 = P[(x1 + s + c) & 0xFF]; + T[g & 0x1F] = (byte)(T[g & 0x1F] ^ x1); + T[(g + 1) & 0x1F] = (byte)(T[(g + 1) & 0x1F] ^ x2); + T[(g + 2) & 0x1F] = (byte)(T[(g + 2) & 0x1F] ^ x3); + T[(g + 3) & 0x1F] = (byte)(T[(g + 3) & 0x1F] ^ x4); + g = (byte)((g + 4) & 0x1F); + + P[n] = ps; + P[s] = pn; + n = (byte)(n + 1); + } + + public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { Check.DataLength(input, inOff, inLen, "input buffer too short"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, inLen)); +#else for (int i = 0; i < inLen; i++) { - Update(input[inOff + i]); - } - } + byte pn = P[n]; + s = P[(s + pn) & 0xFF]; + byte ps = P[s]; + byte c = (byte)(input[inOff + i] ^ P[(P[ps] + 1) & 0xFF]); + + x4 = P[(x4 + x3) & 0xFF]; + x3 = P[(x3 + x2) & 0xFF]; + x2 = P[(x2 + x1) & 0xFF]; + x1 = P[(x1 + s + c) & 0xFF]; + T[g & 0x1F] = (byte)(T[g & 0x1F] ^ x1); + T[(g + 1) & 0x1F] = (byte)(T[(g + 1) & 0x1F] ^ x2); + T[(g + 2) & 0x1F] = (byte)(T[(g + 2) & 0x1F] ^ x3); + T[(g + 3) & 0x1F] = (byte)(T[(g + 3) & 0x1F] ^ x4); + g = (byte)((g + 4) & 0x1F); + + P[n] = ps; + P[s] = pn; + n = (byte)(n + 1); + } +#endif + } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public virtual void BlockUpdate(ReadOnlySpan input) { for (int i = 0; i < input.Length; i++) { - Update(input[i]); - } - } + byte pn = P[n]; + s = P[(s + pn) & 0xFF]; + byte ps = P[s]; + byte c = (byte)(input[i] ^ P[(P[ps] + 1) & 0xFF]); + + x4 = P[(x4 + x3) & 0xFF]; + x3 = P[(x3 + x2) & 0xFF]; + x2 = P[(x2 + x1) & 0xFF]; + x1 = P[(x1 + s + c) & 0xFF]; + T[g & 0x1F] = (byte)(T[g & 0x1F] ^ x1); + T[(g + 1) & 0x1F] = (byte)(T[(g + 1) & 0x1F] ^ x2); + T[(g + 2) & 0x1F] = (byte)(T[(g + 2) & 0x1F] ^ x3); + T[(g + 3) & 0x1F] = (byte)(T[(g + 3) & 0x1F] ^ x4); + g = (byte)((g + 4) & 0x1F); + + P[n] = ps; + P[s] = pn; + n = (byte)(n + 1); + } + } #endif - } + } } diff --git a/crypto/src/crypto/prng/VMPCRandomGenerator.cs b/crypto/src/crypto/prng/VMPCRandomGenerator.cs index 92d163710..23f2d2ec1 100644 --- a/crypto/src/crypto/prng/VMPCRandomGenerator.cs +++ b/crypto/src/crypto/prng/VMPCRandomGenerator.cs @@ -56,6 +56,12 @@ namespace Org.BouncyCastle.Crypto.Prng public void AddSeedMaterial(byte[] seed) { + if (seed == null) + return; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + AddSeedMaterial(seed.AsSpan()); +#else for (int m = 0; m < seed.Length; m++) { byte pn = P[n]; @@ -64,6 +70,7 @@ namespace Org.BouncyCastle.Crypto.Prng P[s] = pn; n = (byte)(n + 1); } +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER -- cgit 1.4.1