From 54566cfecb83dfe66a8febc652533718fdea54d8 Mon Sep 17 00:00:00 2001 From: GitHubProUser67 <127040195+GitHubProUser67@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:59:34 +0100 Subject: [PATCH] Implement the LIBSECURE Xtea cryptography library. Implement the LIBSECURE XTEA cryptography library used on PlayStation devices. This uses custom XTEA designed to be faster than the classic version. The Number of rounds by default is 16, but in theory this can be adjusted up to 64. NOTA: the number of rounds must be an even number! (actual library issue). --- .../src/crypto/engines/LibSecureXTEAEngine.cs | 383 ++++++++++++++++++ crypto/src/security/CipherUtilities.cs | 4 + 2 files changed, 387 insertions(+) create mode 100644 crypto/src/crypto/engines/LibSecureXTEAEngine.cs diff --git a/crypto/src/crypto/engines/LibSecureXTEAEngine.cs b/crypto/src/crypto/engines/LibSecureXTEAEngine.cs new file mode 100644 index 0000000000..a5965f5182 --- /dev/null +++ b/crypto/src/crypto/engines/LibSecureXTEAEngine.cs @@ -0,0 +1,383 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An XTEA engine. + */ + public class LibSecureXteaEngine + : IBlockCipher + { + private const int + rounds = 16, + block_size = 8; +// key_size = 16, + + /* + * the expanded key array of 4 subkeys + */ + private uint[] _S = new uint[4], + _sum0 = new uint[rounds], + _sum1 = new uint[rounds], + _pass1sum0 = new uint[rounds], + _pass1sum1 = new uint[rounds], + _pass2sum0 = new uint[rounds], + _pass2sum1 = new uint[rounds], + _pass3sum0 = new uint[rounds], + _pass3sum1 = new uint[rounds], + _pass4sum0 = new uint[rounds], + _pass4sum1 = new uint[rounds], + _pass5sum0 = new uint[rounds], + _pass5sum1 = new uint[rounds], + _pass6sum0 = new uint[rounds], + _pass6sum1 = new uint[rounds], + _pass7sum0 = new uint[rounds], + _pass7sum1 = new uint[rounds], + _pass8sum0 = new uint[rounds], + _pass8sum1 = new uint[rounds]; + int[] SubSum = new int[rounds + 1]; // Create an array to store data for each round + private bool _initialised, _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public LibSecureXteaEngine() + { + _initialised = false; + } + + public virtual string AlgorithmName + { + get { return "LIBSECUREXTEA"; } + } + + public virtual int GetBlockSize() + { + return block_size; + } + + /** + * initialise + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception ArgumentException if the params argument is + * inappropriate. + */ + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to TEA init - " + + Platform.GetTypeName(parameters)); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter)parameters; + + setKey(p.GetKey()); + } + + public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.DataLength(inBytes, inOff, block_size, "input buffer too short"); + Check.OutputLength(outBytes, outOff, block_size, "output buffer too short"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return _forEncryption + ? EncryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)) + : DecryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff)); +#else + return _forEncryption + ? EncryptBlock(inBytes, inOff, outBytes, outOff) + : DecryptBlock(inBytes, inOff, outBytes, outOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual int ProcessBlock(ReadOnlySpan input, Span output) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + Check.DataLength(input, block_size, "input buffer too short"); + Check.OutputLength(output, block_size, "output buffer too short"); + + return _forEncryption + ? EncryptBlock(input, output) + : DecryptBlock(input, output); + } +#endif + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void setKey( + byte[] key) + { + int i, j; + for (i = j = 0; i < 4; i++, j += 4) + { + _S[i] = Pack.BE_To_UInt32(key, j); + } + + int sum = 0; + + for (i = 0; i < (rounds & 7); i++) + { + _sum0[i] = ((uint)sum + _S[sum & 3]); + sum = sum + unchecked((int)0x9E3779B9); + _sum1[i] = ((uint)sum + _S[sum >> 11 & 3]); + } + + for (i = 8; i <= rounds; i = i * 2) + { + SubSum[i] = sum; + sum = ProcessSubKeys(sum, i, true); + } + } + + /** + * Process Per-Block Keys. + * + * Libsecure does this, where each block is custom XTEA. + */ + private int ProcessSubKeys(int sum, int index, bool lookup) + { + if (!lookup) + { + for (int i = 0; i < ((rounds & index) >> 3); i++) + { + _pass1sum0[i] = ((uint)sum + _S[sum & 3]); + int blocksum = sum + unchecked((int)0x9E3779B9); + _pass1sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass2sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + blocksum = sum + unchecked((int)0x3C6EF372); + _pass2sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass3sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + blocksum = sum + unchecked((int)0xDAA66D2B); + _pass3sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass4sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + blocksum = sum + unchecked((int)0x78DDE6E4); + _pass4sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass5sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + blocksum = sum + unchecked((int)0x1715609D); + _pass5sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass6sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + blocksum = sum + unchecked((int)0xB54CDA56); + _pass6sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass7sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + blocksum = sum + unchecked((int)0x5384540F); + _pass7sum1[i] = ((uint)blocksum + _S[blocksum >> 11 & 3]); + _pass8sum0[i] = ((uint)blocksum + _S[blocksum & 3]); + sum = sum + unchecked((int)0xF1BBCDC8); + _pass8sum1[i] = ((uint)sum + _S[sum >> 11 & 3]); + } + } + else + { + for (int i = 0; i < ((rounds & index) >> 3); i++) + { + sum = sum + unchecked((int)0xF1BBCDC8); + } + } + + return sum; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int EncryptBlock(ReadOnlySpan input, Span output) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(input); + uint v1 = Pack.BE_To_UInt32(input[4..]); + + for (int i = 0; i < (rounds & 7); i++) + { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i]; + } + + for (int i = 8; i <= rounds; i = i * 2) + { + ProcessSubKeys(SubSum[i], i, false); + + for (int j = 0; j < ((rounds & i) >> 3); j++) + { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass1sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass1sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass2sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass2sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass3sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass3sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass4sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass4sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass5sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass5sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass6sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass6sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass7sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass7sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass8sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass8sum1[j]; + } + } + + Pack.UInt32_To_BE(v0, output); + Pack.UInt32_To_BE(v1, output[4..]); + + return block_size; + } + + private int DecryptBlock(ReadOnlySpan input, Span output) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(input); + uint v1 = Pack.BE_To_UInt32(input[4..]); + + int i = rounds; + + while (i >= 8) + { + ProcessSubKeys(SubSum[i], i, false); + + for (int j = ((rounds & i) >> 3) - 1; j >= 0; j--) + { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass8sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass8sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass7sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass7sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass6sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass6sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass5sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass5sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass4sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass4sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass3sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass3sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass2sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass2sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass1sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass1sum0[j]; + } + + i = i / 2; + } + + for (i = (rounds & 7) - 1; i >= 0; i--) + { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i]; + } + + Pack.UInt32_To_BE(v0, output); + Pack.UInt32_To_BE(v1, output[4..]); + + return block_size; + } +#else + private int EncryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + uint v0 = Pack.BE_To_UInt32(inBytes, inOff); + uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4); + + for (int i = 0; i < (rounds & 7); i++) + { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i]; + } + + for (int i = 8; i <= rounds; i = i * 2) + { + ProcessSubKeys(SubSum[i], i, false); + + for (int j = 0; j < ((rounds & i) >> 3); j++) + { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass1sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass1sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass2sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass2sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass3sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass3sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass4sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass4sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass5sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass5sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass6sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass6sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass7sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass7sum1[j]; + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass8sum0[j]; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass8sum1[j]; + } + } + + Pack.UInt32_To_BE(v0, outBytes, outOff); + Pack.UInt32_To_BE(v1, outBytes, outOff + 4); + + return block_size; + } + + private int DecryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(inBytes, inOff); + uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4); + + int i = rounds; + + while (i >= 8) + { + ProcessSubKeys(SubSum[i], i, false); + + for (int j = ((rounds & i) >> 3) - 1; j >= 0; j--) + { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass8sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass8sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass7sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass7sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass6sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass6sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass5sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass5sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass4sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass4sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass3sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass3sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass2sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass2sum0[j]; + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _pass1sum1[j]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _pass1sum0[j]; + } + + i = i / 2; + } + + for (i = (rounds & 7) - 1; i >= 0; i--) + { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i]; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i]; + } + + Pack.UInt32_To_BE(v0, outBytes, outOff); + Pack.UInt32_To_BE(v1, outBytes, outOff + 4); + + return block_size; + } +#endif + } +} \ No newline at end of file diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs index 715bd81fe6..b76323622e 100644 --- a/crypto/src/security/CipherUtilities.cs +++ b/crypto/src/security/CipherUtilities.cs @@ -70,6 +70,7 @@ private enum CipherAlgorithm { VMPC, VMPC_KSA3, XTEA, + LIBSECUREXTEA }; private enum CipherMode { ECB, NONE, CBC, CCM, CFB, CTR, CTS, EAX, GCM, GOFB, OCB, OFB, OPENPGPCFB, SIC }; @@ -540,6 +541,8 @@ private static IBufferedCipher GetCipherForMechanism(string mechanism) break; case CipherAlgorithm.XTEA: blockCipher = new XteaEngine(); + case CipherAlgorithm.LIBSECUREXTEA: + blockCipher = new LibSecureXteaEngine(); break; default: return null; @@ -857,6 +860,7 @@ private static IBlockCipher CreateBlockCipher(CipherAlgorithm cipherAlgorithm) case CipherAlgorithm.TNEPRES: return new TnepresEngine(); case CipherAlgorithm.TWOFISH: return new TwofishEngine(); case CipherAlgorithm.XTEA: return new XteaEngine(); + case CipherAlgorithm.LIBSECUREXTEA: return new LibSecureXteaEngine(); default: throw new SecurityUtilityException("Cipher " + cipherAlgorithm + " not recognised or not a block cipher"); }