65.9K
CodeProject 正在变化。 阅读更多。
Home

高效数据处理:使用 Tango 库进行压缩和加密

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2019年1月1日

CPOL

4分钟阅读

viewsIcon

7628

downloadIcon

108

.NET 库,用于内存中的 GZip 和 LZMA 压缩,并结合强大的基于 BlakeB 的流密码引擎

引言

本项目介绍了一个自包含的 .NET 库 (DLL),用于内存数据(作为字节数组和 MemoryStream)的压缩和加密。当然,处理后的输出可以保存到硬盘,但本项目侧重于内存数据的有效处理方式。

压缩

压缩的显而易见的选择是 .NET 内置的 GZip 功能以及 7zip SDK C# 实现提供的 LZM 算法,后者是迄今为止最有效的压缩函数。

由于项目的目的只是内存转换,因此它不提供带有特定头结构的标准 7Zip 文件。该库调用 GZip 和 LZM 压缩算法,利用并行编程来利用多核 CPU 来解压缩/压缩内存数据。

该库可用的压缩选项来自以下

       public enum CompressionAlgo {
           GZip_All,
           GZip1,
           GZip2,
           GZip4,
           GZip8,
           LZMA_All,
           LZMA1,
           LZMA2,
           LZMA4,
           LZMA8,
           None
       }

其中 1、2、4、8 代表压缩过程中要使用的处理器数量,而 Gzip_AllLZMA_All 则代表所有可用机器处理器(通常等于 8,因此 LZMA_AllLZMA8 含义相同)。

提供的功能的主要且唯一目的是内存操作。因此,保存到硬盘的单线程 LZMA1 压缩结果(字节数组)会形成一个可以被 7zip 应用程序打开的正确 7z 文件,但多线程选项无法生成格式正确的 7z 文件。

加密

对于加密部分,该库引入了基于 BlakeB 压缩函数的快速流密码,该函数由 Samuel Neves、Christian Winnerlein 和 Uli Riehm 实现。

BlakeB 压缩函数是生成器核心,该生成器会创建唯一对应于用户输入的密钥、盐和分布值的输出字节数组(填充)。

生成的填充以以下方式用于加密/解密

  • 加密:明文(字节数组 p)异或填充 => 密文(字节数组 x)
  • 解密:密文(x)异或填充 => 明文(p)

由于 encryptdecrypt 函数都只是用填充对字节进行异或操作,因此密码引擎包含单个函数 Crypt,该函数涵盖了这两种情况,为我们提供了流密码实现。

生成器(程序中的 BGen 类)和流密码(程序中的 Streamer 类)都以并行模式工作,利用多核处理器。

例如,如果机器有 8 个处理器,生成器将生成 8 个并行数组并将它们粘合到单个输出(填充)中。不用说,这些片段保证是唯一的且不重复的。

同样,Streamer 会将输入流分成 8 个部分,将它们与 8 个对应的填充部分进行异或操作,并生成汇总结果。

让我们对 BlakeB 功能进行一些修改。

核心压缩函数由如下块组成

         v0 = v0 + v4 + m0;
         v12 = v12 ^ v0;
         v12 = ((v12 >> 32) | (v12 << (32)));
         v8 = v8 + v12;
         v4 = v4 ^ v8;
         v4 = ((v4 >> 24) | (v4 << (40)));
         v0 = v0 + v4 + m1;
         v12 = v12 ^ v0;
         v12 = ((v12 >> 16) | (v12 << (48)));
         v8 = v8 + v12;
         v4 = v4 ^ v8;
         v4 = ((v4 >> 63) | (v4 << (1)));

         v1 = v1 + v5 + m2;
         v13 = v13 ^ v1;
         v13 = ((v13 >> 32) | (v13 << (32)));
         v9 = v9 + v13;
         v5 = v5 ^ v9;
         v5 = ((v5 >> 24) | (v5 << (40)));
         v1 = v1 + v5 + m3;
         v13 = v13 ^ v1;
         v13 = ((v13 >> 16) | (v13 << (48)));
         v9 = v9 + v13;
         v5 = v5 ^ v9;
         v5 = ((v5 >> 63) | (v5 << (1)));
...

带有重复的移位常量 32、24、16 和 63。每个常量在 12 轮处理中出现 96 次,总共出现 384 次。

为了增加攻击者的难度,当前实现最初会基于用户密码(密钥)、盐和分布输入生成字节数组 S[384]。这使得压缩函数具有以下模式

         int i = 0;

         v0 = v0 + v4 + m0;
         v12 = v12 ^ v0;
         v12 = RR(v12, S[i++]);
         v8 = v8 + v12;
         v4 = v4 ^ v8;
         v4 = RR(v4, S[i++]);
         v0 = v0 + v4 + m1;
         v12 = v12 ^ v0;
         v12 = RR(v12, S[i++]);
         v8 = v8 + v12;
         v4 = v4 ^ v8;
         v4 = RR(v4, S[i++]);
...

其中 RR 是右旋转函数

         private ulong RR(ulong x, byte n) {

              return (x >> n) | (x << (-n & 63));
         }

因此,压缩函数利用了一组 384 个唯一的移位数字(字节),这些数字取决于用户输入的密钥、盐和分布,从而消除了计算中的任意常量。

生成器中唯一的常量是程序员定义的 ulongs

        private const ulong IV0 =  9111111111111111111;
        private const ulong IV1 =  8222222222222222222;
        private const ulong IV2 =  7333333333333333333;
        private const ulong IV3 =  6444444444444444444;
        private const ulong IV4 =  5555555555555555555;
        private const ulong IV5 =  4666666666666666666;
        private const ulong IV6 =  3777777777777777777;
        private const ulong IV7 =  2888888888888888888;
        private const ulong IV8 =  1999999999999999999;
        private const ulong IV9 =  1111111111111111111;
        private const ulong IV10 = 2222222222222222222;
        private const ulong IV11 = 3333333333333333333;
        private const ulong IV12 = 4444444444444444444;
        private const ulong IV13 = 5555555555555555555;
        private const ulong IV14 = 6666666666666666666;
        private const ulong IV15 = 7777777777777777777;

        private const ulong FNL0 = 123456789;
        private const ulong FNL1 = 987654321;

在您的实现中设置唯一的常量,并享受可靠而灵活的个人流密码,其中没有任何外部预定义项。

用户提供的盐和分布输入会影响生成器的初始状态,从而增加了结果的分散性。

Tango 库

Tango 库的 Tang 类是一个单一的入口点,提供仅压缩、仅加密或两者兼有的接口。

以下是构造函数使用示例。

压缩

Tang tang1 = new Tang(CompressionAlgo.GZip1); // use GZip on 1 processor

Tang tang2 = new Tang(CompressionAlgo.LZMA8); // use LZMA on 8 processors

加密

Tang tang3 = new Tang(“key”, “salt”, “distr”, 1); // use 1 processor for encryption

Tang tang4 = new Tang(key, salt, distr);          // use all processors for encryption. 
                                                  // key,salt and distr are byte arrays

加密与压缩

Tang tang5 = new Tang(CompressionAlgo.GZip8, “key”,
     “salt”, “distr”, 4); // GZip8 compression, 4 processors for encryption

Tang tang6 = new Tang
(CompressionAlgo.LZMA1, “key”, “salt”, “distr”); // LZMA1 compression, 
                                                 // all processors for encryption

实际应用

Tang tang1 = new Tang(CompressionAlgo.GZip1);

byte[] plain = ...

byte[] compressed = tang1.Zip(plain);
byte[] uncompressed = tang1.Unzip(compressed);


Tang tang 2 = new Tang(“key”, “salt”, “distr”);

byte[] encrypted= tang2.Crypt(plain);

// The reset is needed when using the same tang object for the
// paired encrypt/decrypt and tangle/untangle actions.

tang2.Reset();

byte[] decrypted = tang2.Crypt (encrypted);


Tang tang3 = new Tang(CompressionAlgo.LZMA1, “key”, “salt”, “distr”);

byte[] tangled = tang3.Tangle(plain);       // Tangle is Crypt(Zip(plain))

tang3.Reset();

byte[] untangled = tang3.Untangle(tangled); // Untangle is Unzip(Crypt(tangled))

Tango 函数也支持使用 MemoryStream 输入参数而不是字节数组。

不要忘记将 try/catch 块放在 UnzipUntangle 操作周围。

生成

初始化后,tang 对象可以生成给定长度的字节数组,该数组对于提供的密钥、盐和分布是唯一的

Tang tang = new Tang(“key”, “salt”, “distr”);

byte[] hash = tang.Generate(int length);

测量

PC:i7 2GHz 64 位 8 核

Tangle()

来源:9,653,618 B(fb2 文件 - 允许高压缩)
    时间 压缩率
GZip1 2,463,446 B 1259 毫秒 25.52%
GZip8 2,472,430 B 303 毫秒 25.61%
LZMA1 1,777,219 B 30995 毫秒 18.41%
LZMA8 1,914,649 B 6124 毫秒 19.83%

仅压缩

来源:9653618 B
    时间 压缩率
GZip1 2,463,446 B 1169 毫秒 3.7% 25.52%
GZip8 2,472,430 B 293 毫秒 0.9% 25.61%
LZMA1 1,777,219 B 31377 毫秒 100% 18.41%
LZMA8 1,914,649 B 6131 毫秒 19.5% 19.83%

如预期,最有效的压缩算法是 LZMA1,但其时间成本最高。

GZip8 比 GZip1 快 4 倍,且在压缩方面几乎没有劣势。

仅加密

来源:9653618 B
  时间  
1 核 903 毫秒 100%
2 核 457 毫秒 50.6%
4 核 295 毫秒 32.7%
8 核 219 毫秒 24.3%
生成

10,000,000 字节在 250 毫秒内生成

初始化速度
Tang tang = new Tang (CompressionAlgo.LZMA8, "key", "salt", "distr");

Tang 的构造时间约为 0.08 毫秒,与应用的 CompressionAlgo 无关。

轻松掌握 Tango 的纠缠!

历史

  • 2019 年 1 月 1 日:初始版本
© . All rights reserved.