本文共 5652 字,大约阅读时间需要 18 分钟。
目录
用于内存中gzip和lzma压缩的.NET库与基于强blakeb的流密码引擎结合使用。
该项目为内存数据(如byte数组和MemoryStream)压缩和加密引入了一个自持的.NET库(dll)。当然,处理后的输出可以保存到硬盘中,但项目集中在有效的内存数据处理方方法上。
压缩的明显选择是内置的.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_All和LZMA_All代表所有可用的机器处理器(通常等于8,因此LZMA_All和LZMA8具有相同的含义)。
提供的功能的主要和唯一的目的是内存操作。因此保存到硬盘的单线程LZMA1压缩结果(字节数组)形成了可以使用7zip应用程序打开的正确7z文件,但多线程选项不能提供格式良好的7z文件。
对于加密部分,该库引入了基于BlakeB压缩功能的快速流密码,由Samuel Neves,Christian Winnerlein和Uli Riehm实现。
BlakeB压缩功能在生成器的核心工作,该生成器在输入的键、盐(salt)和分布值中创建用户独有的输出字节数组(pad)。
生成的pad以下列方式用于加密/解密:
加密:纯文本(字节数组p)XOR pad =>密文(字节数组x)
解密:密文(x)XOR pad =>纯文本(p)
由于加密和解密功能都只是使用pad对字节进行异或,因此密码引擎包括覆盖这两种情况的单个函数Crypt,并为我们提供了流密码实现。
发生器(程序中的BGen类)和流密码(程序中的Streamer类)都使用多核处理器以并行模式工作。
例如,如果机器有8个处理器,则生成器将生成8个并行阵列并将它们粘合到单个输出(pad)中。不用说,这些数组保证是独一无二的,不会重复。
类似地,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次。
为了使对手的生活更有趣,当前的实现最初基于用户密码(密钥),盐(salt)和分布输入生成字节数组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));}
因此,压缩函数利用取决于用户输入的密钥,盐(salt)和分布的唯一组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;
在您的实现中设置唯一常量,并享受可靠灵活的个人流密码,其中没有任何外部预定义。
用户提供的盐(salt)和分布输入会影响生成器的初始状态,因此会增加结果的分散。
Tango库的Tang类是一个单一的入口点,具有仅用于压缩、仅用于加密或同时用于两者的接口。
这是构造函数用法示例。
Tang tang1 = new Tang(CompressionAlgo.GZip1); // use GZip on 1 processorTang tang2 = new Tang(CompressionAlgo.LZMA8); // use LZMA on 8 processors
Tang
tang3 = new Tang("key", "salt", "distr", 1); // use 1 processor for encryptionTang 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 encryptionTang 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输入参数,而不是字节数组作为参数。
不要忘记在un-unzip和Untangle操作中放置try / catch块。
初始化后,tang对象可以生成给定长度的字节数组,该字节数组对于提供的密钥,盐(salt)和分布是唯一的:
Tang tang = new Tang("key", "salt", "distr");byte[] hash = tang.Generate(int
length);
PC:i7 2GHz 64位8核
Tangle()
Source 9,653,618 B (fb2 file - allows for high compression)
time compression rate
GZip1 2,463,446 B 1259 ms 25.52%
GZip8 2,472,430 B 303 ms 25.61%
LZMA1 1,777,219 B 30995 ms 18.41%
LZMA8 1,914,649 B 6124 ms 19.83%
仅限压缩
Source 9653618 B timing compression rate
GZip1 2,463,446 B 1169 ms 3.7% 25.52%
GZip8 2,472,430 B 293 ms 0.9% 25.61%
LZMA1 1,777,219 B 31377 ms 100% 18.41%
LZMA8 1,914,649 B 6131 ms 19.5% 19.83%
预期最有效的压缩算法是LZMA1,它具有最高的时间成本。
GZip8比GZip1快4倍,压缩效果几乎不差。
仅限加密
Source 9653618 B
time
1 core 903 ms 100%
2 cores 457 ms 50.6%
4 cores 295 ms 32.7%
8 cores 219 ms 24.3%
生成
250毫秒内生成10,000,000个字节
初始化速度
Tang tang
= newTang
(CompressionAlgo.LZMA8, "key", "salt", "distr");
Tang构造时间大约需要0.08 ms,与应用的压缩算法无关。
让您轻松与Tango纠缠在一起!
原文地址:
转载地址:http://cuzhj.baihongyu.com/