首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >XTEA分组密码

XTEA分组密码
EN

Code Review用户
提问于 2018-11-03 00:39:02
回答 1查看 1.4K关注 0票数 10

通常,除非你是该领域的专家,否则人们建议永远不要自己实现密码算法。当然,这很有意义,主要是因为有许多事情可能出错,导致易受攻击的实现。

然而,为了学习的目的,我还是决定自己编写一个XTEA分组密码的实现。XTEA是一个具有128位密钥的64位块feistel密码.

在我的实施中,还有什么可以改进的呢?有什么重大缺陷吗?

ISymmetricEncryptionProvider.cs

代码语言:javascript
复制
namespace Crypto
{
    /// <summary>
    /// Provides a base for symmetric encryption algorithms
    /// </summary>
    public interface ISymmetricEncryptionProvider
    {

        /// <summary>
        /// Symmetric encryption routine
        /// </summary>
        /// <param name="data">The data that should get encrypted</param>
        /// <returns>The encrypted data</returns>
        byte[] Encrypt(byte[] data);


        /// <summary>
        /// Symmetric decryption routine 
        /// </summary>
        /// <param name="data">The data that should get decrypted</param>
        /// <returns>The decrypted data</returns>
        byte[] Decrypt(byte[] data);

    }
}

XTEA.cs

代码语言:javascript
复制
using System;
using System.IO;

namespace Crypto
{
    /// <summary>
    /// Like TEA, XTEA is a 64-bit block Feistel cipher with a 128-bit key and a suggested
    /// 64 rounds. Several differences from TEA are apparent, including a somewhat
    /// more complex key-schedule and a rearrangement of the shifts, XORs, and additions.
    /// 
    /// More information can be found here:
    /// + https://en.wikipedia.org/wiki/XTEA
    /// + http://www.tayloredge.com/reference/Mathematics/TEA-XTEA.pdf
    /// </summary>
    public class XTEA : ISymmetricEncryptionProvider
    {
        /// <summary>
        /// The 128 bit key used for encryption and decryption
        /// </summary>
        private readonly uint[] _key;


        /// <summary>
        /// The number of rounds, default is 32 because each iteration performs two Feistel-cipher rounds.
        /// </summary>
        private readonly uint _cycles;


        /// <summary>
        /// XTEA operates with a block size of 8 bytes
        /// </summary>
        private readonly uint _blockSize = 8;


        /// <summary>
        /// The delta is derived from the golden ratio where delta = (sqrt(2) - 1) * 2^31
        /// A different multiple of delta is used in each round so that no bit of
        /// the multiple will not change frequently
        /// </summary>
        private const uint Delta = 0x9E3779B9;


        /// <summary>
        /// Instantiate new XTEA object for encryption/decryption
        /// </summary>
        /// <param name="key">The encryption/decryption key</param>
        /// <param name="cycles">Number of cycles performed, default is 32</param>
        public XTEA(byte[] key, uint cycles = 32)
        {
            _key = GenerateKey(key);
            _cycles = cycles;
        }


        /// <summary>
        /// Calculates the next multiple of the block size of the input data because
        /// XTEA is a 64-bit cipher.
        /// </summary>
        /// <param name="length">Input data size</param>
        /// <returns>Input data extended to the next multiple of the block size.</returns>
        private uint NextMultipleOfBlockSize(uint length)
        {
            return (length + 7) / _blockSize * _blockSize;
        }


        /// <summary>
        /// Encrypts the provided data with XTEA
        /// </summary>
        /// <param name="data">The data to encrypt</param>
        /// <returns>Encrypted data as byte array</returns>
        public byte[] Encrypt(byte[] data)
        {
            var blockBuffer = new uint[2];
            var dataBuffer = new byte[NextMultipleOfBlockSize((uint)data.Length + 4)];
            var lengthBuffer = BitConverter.GetBytes(data.Length);

            Buffer.BlockCopy(lengthBuffer, 0, dataBuffer, 0, lengthBuffer.Length);
            Buffer.BlockCopy(data, 0, dataBuffer, lengthBuffer.Length, data.Length);

            using (var memoryStream = new MemoryStream(dataBuffer))
            using (var binaryWriter = new BinaryWriter(memoryStream))
                for (uint i = 0; i < dataBuffer.Length; i += _blockSize)
                {
                    blockBuffer[0] = BitConverter.ToUInt32(dataBuffer, (int) i);
                    blockBuffer[1] = BitConverter.ToUInt32(dataBuffer, (int) i + 4);

                    Encode(_cycles, blockBuffer, _key);

                    binaryWriter.Write(blockBuffer[0]);
                    binaryWriter.Write(blockBuffer[1]);
                }

            return dataBuffer;
        }


        /// <summary>
        /// Decrypts the provided data with XTEA
        /// </summary>
        /// <param name="data">The data to decrypt</param>
        /// <returns>The decrypted data as byte array</returns>
        public byte[] Decrypt(byte[] data)
        {
            // Encrypted data size must be a multiple of the block size
            if (data.Length % _blockSize != 0)
                throw new ArgumentOutOfRangeException(nameof(data));

            var blockBuffer = new uint[2];
            var buffer = new byte[data.Length];

            Buffer.BlockCopy(data, 0, buffer, 0, data.Length);

            using (var memoryStream = new MemoryStream(buffer))
            using (var binaryWriter = new BinaryWriter(memoryStream))
            {
                for (uint i = 0; i < buffer.Length; i += _blockSize)
                {
                    blockBuffer[0] = BitConverter.ToUInt32(buffer, (int) i);
                    blockBuffer[1] = BitConverter.ToUInt32(buffer, (int) i + 4);

                    Decode(_cycles, blockBuffer, _key);

                    binaryWriter.Write(blockBuffer[0]);
                    binaryWriter.Write(blockBuffer[1]);
                }
            }

            // Verify if length of output data is valid
            var length = BitConverter.ToUInt32(buffer, 0);
            VerifyDataLength(length, buffer.Length, 4);

            // Trim first 4 bytes of output data            
            return TrimOutputData(length, buffer, 4);
        }


        /// <summary>
        /// Removes the first n bytes from the buffer
        /// </summary>
        /// <param name="length">Length of the output buffer</param>
        /// <param name="buffer">The output buffer</param>
        /// <param name="trimSize">Number of bytes to trim from the start of the buffer</param>
        /// <returns>Trimmed output buffer array</returns>
        private byte[] TrimOutputData(uint length, byte[] buffer, int trimSize)
        {
            var result = new byte[length];
            Buffer.BlockCopy(buffer, trimSize, result, 0, (int) length);
            return result;
        }


        /// <summary>
        /// Compares the length of the output data from a specified offset to the length of the buffer
        /// </summary>
        /// <param name="dataLength">Length of the output data</param>
        /// <param name="bufferLength">Length of the buffer</param>
        /// <param name="offset">The offset</param>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if buffer data is corrupted</exception>
        private void VerifyDataLength(uint dataLength, int bufferLength, uint offset)
        {
            if (dataLength > bufferLength - offset)
                throw new ArgumentOutOfRangeException(nameof(bufferLength));
        }


        /// <summary>
        /// Transforms the key to uint[]
        /// </summary>
        /// <returns>Transformed key</returns>
        private uint[] GenerateKey(byte[] key)
        {
            if (key.Length != 16)
                throw new ArgumentOutOfRangeException(nameof(key));

            return new[]
            {
                BitConverter.ToUInt32(key, 0), BitConverter.ToUInt32(key, 4),
                BitConverter.ToUInt32(key, 8), BitConverter.ToUInt32(key, 12)
            };
        }


        /// <summary>
        /// TEA inplace encoding routine of the provided data array.
        /// </summary>
        /// <param name="rounds">The number of encryption rounds, default is 32.</param>
        /// <param name="v">The data array containing two values.</param>
        /// <param name="k">The key array containing 4 values.</param>
        private void Encode(uint rounds, uint[] v, uint[] k)
        {
            uint sum = 0;
            uint v0 = v[0], v1 = v[1];
            for (int i = 0; i < rounds; i++)
            {
                v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
                sum += Delta;
                v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
            }

            v[0] = v0;
            v[1] = v1;
        }


        /// <summary>
        /// TEA inplace decoding routine of the provided data array.
        /// </summary>
        /// <param name="rounds">The number of encryption rounds, default is 32.</param>
        /// <param name="v">The data array containing two values.</param>
        /// <param name="k">The key array containing 4 values.</param>
        private void Decode(uint rounds, uint[] v, uint[] k)
        {
            uint sum = Delta * rounds;
            uint v0 = v[0], v1 = v[1];
            for (int i = 0; i < rounds; i++)
            {
                v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
                sum -= Delta;
                v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
            }

            v[0] = v0;
            v[1] = v1;
        }

    }
}

下面是这个类的一个示例用法:

代码语言:javascript
复制
public static void Main(string[] args)
{
    byte[] key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
                  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};

    byte[] data = Encoding.UTF8.GetBytes("This is a message which will be encrypted with XTEA!");

    var xtea = new XTEA(key);
    var enc = xtea.Encrypt(data);
    var dec = xtea.Decrypt(enc);

    Console.WriteLine(Encoding.UTF8.GetString(dec));
    Console.ReadKey();
}

参考资料

信用属于的地方。在编写这门课的过程中,使用了以下网站/书籍作为指导:

微加密算法(茶)

XTEA的C#实现

C# CarestiaXTEA

C++ XTEA实现

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-01-12 02:44:03

您所称的Encode函数实际上似乎是您的块加密函数,我想将它命名为更有暗示意义的函数。正如评论中所指出的,如果你提供了欧洲央行以外的模式,那将是一次很好的设计。我的主要抱怨是,它面向字节数组,而当Stream-based实现更有用时。以下任一项:

代码语言:javascript
复制
void Encrypt(Stream input, Stream output)

或者更像是适配器模式:

代码语言:javascript
复制
class XTeaEncryptor : Stream
{
    XTeaEncryptor(Stream outputStream){}
}

使用为Streams设计的类来加密字节数组要比使用相反的类容易得多,特别是当您进入其他模式时。

使用TrimOutputData会导致分配一个与输入差不多大的数组,您可以通过使用索引来避免这种情况。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/206851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档