From 66d50af39a4c51dc5f22feb7b153af5232c09ae2 Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Sun, 8 Nov 2020 14:11:51 +0800 Subject: [PATCH] New AesCfb --- .../Crypto/Stream/ExtendedCfbBlockCipher.cs | 235 ------------------ .../Stream/StreamAesBouncyCastleCrypto.cs | 56 +---- 2 files changed, 13 insertions(+), 278 deletions(-) delete mode 100644 Shadowsocks.Net/Crypto/Stream/ExtendedCfbBlockCipher.cs diff --git a/Shadowsocks.Net/Crypto/Stream/ExtendedCfbBlockCipher.cs b/Shadowsocks.Net/Crypto/Stream/ExtendedCfbBlockCipher.cs deleted file mode 100644 index fc912ebc..00000000 --- a/Shadowsocks.Net/Crypto/Stream/ExtendedCfbBlockCipher.cs +++ /dev/null @@ -1,235 +0,0 @@ -// original: https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/modes/CfbBlockCipher.cs -// changes: 5th parameter for ProcessBlock, to process without change internal state - - -using Org.BouncyCastle.Crypto.Parameters; -using System; - -namespace Org.BouncyCastle.Crypto.Modes -{ - /** - * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. - */ - public class ExtendedCfbBlockCipher - : IBlockCipher - { - private readonly byte[] IV; - private readonly byte[] cfbV; - private readonly byte[] cfbOutV; - private bool encrypting; - - private readonly int blockSize; - private readonly IBlockCipher cipher; - - /** - * Basic constructor. - * - * @param cipher the block cipher to be used as the basis of the - * feedback mode. - * @param blockSize the block size in bits (note: a multiple of 8) - */ - public ExtendedCfbBlockCipher( - IBlockCipher cipher, - int bitBlockSize) - { - this.cipher = cipher; - blockSize = bitBlockSize / 8; - IV = new byte[cipher.GetBlockSize()]; - cfbV = new byte[cipher.GetBlockSize()]; - cfbOutV = new byte[cipher.GetBlockSize()]; - } - /** - * return the underlying block cipher that we are wrapping. - * - * @return the underlying block cipher that we are wrapping. - */ - public IBlockCipher GetUnderlyingCipher() - { - return cipher; - } - /** - * Initialise the cipher and, possibly, the initialisation vector (IV). - * If an IV isn't passed as part of the parameter, the IV will be all zeros. - * An IV which is too short is handled in FIPS compliant fashion. - * - * @param forEncryption if true the cipher is initialised for - * encryption, if false for decryption. - * @param param the key and other data required by the cipher. - * @exception ArgumentException if the parameters argument is - * inappropriate. - */ - public void Init( - bool forEncryption, - ICipherParameters parameters) - { - encrypting = forEncryption; - if (parameters is ParametersWithIV ivParam) - { - byte[] iv = ivParam.GetIV(); - int diff = IV.Length - iv.Length; - Array.Copy(iv, 0, IV, diff, iv.Length); - Array.Clear(IV, 0, diff); - - parameters = ivParam.Parameters; - } - Reset(); - - // if it's null, key is to be reused. - if (parameters != null) - { - cipher.Init(true, parameters); - } - } - - /** - * return the algorithm name and mode. - * - * @return the name of the underlying algorithm followed by "/CFB" - * and the block size in bits. - */ - public string AlgorithmName => cipher.AlgorithmName + "/CFB" + (blockSize * 8); - - public bool IsPartialBlockOkay => true; - - /** - * return the block size we are operating at. - * - * @return the block size we are operating at (in bytes). - */ - public int GetBlockSize() - { - return blockSize; - } - - /** - * Process one block of input from the array in and write it to - * the out array. - * - * @param in the array containing the input data. - * @param inOff offset into the in array the data starts at. - * @param out the array the output data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @param updateContext update internal state after process. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff, - bool updateContext) - { - return (encrypting) - ? EncryptBlock(input, inOff, output, outOff, updateContext) - : DecryptBlock(input, inOff, output, outOff, updateContext); - } - - /** - * Do the appropriate processing for CFB mode encryption. - * - * @param in the array containing the data to be encrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int EncryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff, - bool updateContext = true) - { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - if ((outOff + blockSize) > outBytes.Length) - { - throw new DataLengthException("output buffer too short"); - } - cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); - // - // XOR the cfbV with the plaintext producing the ciphertext - // - for (int i = 0; i < blockSize; i++) - { - outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); - } - if (updateContext) - { - // - // change over the input block. - // - Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); - Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize); - } - return blockSize; - } - /** - * Do the appropriate processing for CFB mode decryption. - * - * @param in the array containing the data to be decrypted. - * @param inOff offset into the in array the data starts at. - * @param out the array the encrypted data will be copied into. - * @param outOff the offset into the out array the output will start at. - * @exception DataLengthException if there isn't enough data in in, or - * space in out. - * @exception InvalidOperationException if the cipher isn't initialised. - * @return the number of bytes processed and produced. - */ - public int DecryptBlock( - byte[] input, - int inOff, - byte[] outBytes, - int outOff, - bool updateContext = true) - { - if ((inOff + blockSize) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - if ((outOff + blockSize) > outBytes.Length) - { - throw new DataLengthException("output buffer too short"); - } - cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); - if (updateContext) - { - // - // change over the input block. - // - Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); - Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize); - } - // - // XOR the cfbV with the ciphertext producing the plaintext - // - for (int i = 0; i < blockSize; i++) - { - outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); - } - return blockSize; - } - /** - * reset the chaining vector back to the IV and reset the underlying - * cipher. - */ - public void Reset() - { - Array.Copy(IV, 0, cfbV, 0, IV.Length); - cipher.Reset(); - } - - public int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) - { - return ProcessBlock(inBuf, inOff, outBuf, outOff, true); - } - } -} \ No newline at end of file diff --git a/Shadowsocks.Net/Crypto/Stream/StreamAesBouncyCastleCrypto.cs b/Shadowsocks.Net/Crypto/Stream/StreamAesBouncyCastleCrypto.cs index 236d5dc9..e9675774 100644 --- a/Shadowsocks.Net/Crypto/Stream/StreamAesBouncyCastleCrypto.cs +++ b/Shadowsocks.Net/Crypto/Stream/StreamAesBouncyCastleCrypto.cs @@ -1,21 +1,18 @@ -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; +using Shadowsocks.Net.Crypto.Extensions; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Shadowsocks.Net.Crypto.Stream { - public class StreamAesCfbBouncyCastleCrypto : StreamCrypto { - readonly byte[] cfbBuf = new byte[MaxInputSize + 128]; - int ptr = 0; - readonly ExtendedCfbBlockCipher b; + private readonly MyCfbBlockCipher b; public StreamAesCfbBouncyCastleCrypto(string method, string password) : base(method, password) { - b = new ExtendedCfbBlockCipher(new AesEngine(), 128); + b = new MyCfbBlockCipher(new AesEngine(), 128); } protected override void InitCipher(byte[] iv, bool isEncrypt) @@ -26,49 +23,22 @@ namespace Shadowsocks.Net.Crypto.Stream protected override int CipherEncrypt(ReadOnlySpan plain, Span cipher) { - CipherUpdate(plain, cipher); - return plain.Length; + return CipherUpdate(plain, cipher); } protected override int CipherDecrypt(Span plain, ReadOnlySpan cipher) { - CipherUpdate(cipher, plain); - return cipher.Length; + return CipherUpdate(cipher, plain); } - [MethodImpl( MethodImplOptions.AggressiveInlining)] - private void CipherUpdate(ReadOnlySpan i, Span o) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int CipherUpdate(ReadOnlySpan input, Span output) { - Span ob = new byte[o.Length + 128]; - i.CopyTo(cfbBuf.AsSpan(ptr)); - // TODO: standard CFB, maybe with native aes - int total = i.Length + ptr; - - int blkSize = b.GetBlockSize(); - - int blkCount = total / blkSize; - int restSize = total % blkSize; - int readPtr = 0; - - byte[] tmp = new byte[blkSize]; - for (int j = 0; j < blkCount; j++) - { - b.ProcessBlock(cfbBuf, readPtr, tmp, 0); - tmp.CopyTo(ob.Slice(readPtr)); - readPtr += blkSize; - } - if (restSize != 0) - { - readPtr = blkSize * blkCount; - // process last (partial) block without update state - b.ProcessBlock(cfbBuf, readPtr, tmp, 0, false); - tmp.CopyTo(ob.Slice(readPtr)); - // write back the partial block block - Array.Copy(cfbBuf, readPtr, cfbBuf, 0, restSize); - } - // cut correct part to output - ob.Slice(ptr, o.Length).CopyTo(o); - ptr = restSize; + var i = input.ToArray(); + var o = new byte[i.Length]; + var res = b.ProcessBlock(i, 0, o, 0); + o.CopyTo(output); + return res; } #region Cipher Info