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

乱序字谜算法

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2011年12月4日

GPL3

4分钟阅读

viewsIcon

23223

一种以字谜形式执行加密的算法

引言

乱序字谜算法通过使用基于密码的规则更改字节数组中字节的位置来执行加密。 当然,它也可以应用于string或其他类型的数据; 我选择字节数组是因为它们用途广泛,可以轻松地与string相互转换,并且也可以轻松地从文件中读取。

在思考如何克服块密码算法(例如,用于 AES 的 Rijndael)在加密文件时的弱点时,我开发了此算法。 我想知道是否已经存在具有不同名称的算法……(如果不存在,那么我将不得不使用更漂亮的名称来更改此名称)。

算法描述

该算法需要两个输入字节数组:cleardatapassword,并返回一个字节数组(从现在起称为:output)。 输入字节数组 (cleardata) 从头(字节 0)到尾读取两次,一次一个字节; 同时读取password,一次一个 BIT,这样,对于 cleardata 中的每个字节,我们在密码中都有一个对应的 BIT(如果密码字节数组小于cleardata,那么我们循环遍历密码以获得相应的位; 换句话说:密码中 BIT 的位置 = cleardata 中字节的位置 MODULO 密码长度;或者,用公式表示:bitpositioninpassword = bytepositionincleardata % password.length;

现在,我们可以开始编写输出文件。

第一次读取cleardata时,一次读取一个字节,如果密码中对应的位是 1,我们将该字节添加到输出中; 然后我们再次读取cleardata第二次,一次读取一个字节,如果密码中对应的位是 0,我们将该字节添加到输出中; (还有其他方法可以做到这一点,甚至只需一次传递,但这种方法似乎最容易理解)。

因此,输出的第一部分将包含与密码中为 1 的位对应的cleardata字节,而输出的第二部分将包含与密码中为 0 的位对应的cleardata字节。

Using the Code

该算法已在此处实现,具有编码和解码功能,可用作字节数组的扩展,即用法

byte[] mydata; byte[] mypass;
// here: allocate and set or read the two arrays
byte[] myoutput = mydata.Cry_ScrambleByteRightEnc(mypass);

可以在下面的代码中找到对字节进行操作的从字谜编码/解码的两个主要函数(Cry_ScrambleByteRightEnc, Cry_ScrambleByteRightDec)。

可以轻松修改相同的算法以移动 cleardata 的 BIT,而不是字节。 当然,输出不会是字谜,如果密码未知,则更难以解码; 并且它会比原始的运行速度慢。 有关此实现,请参阅函数Cry_ScrambleBitRightEnc, Cry_ScrambleBitRightDec

public static class ByteArrayExtensions
{
const int bitsinbyte = 8;

public static byte[] Cry_ScrambleByteRightEnc(this byte[] cleardata, byte[] password)
    {
    long cdlen = cleardata.LongLength;
    byte[] cryptdata = new byte[cdlen];
    // first loop: fill crypt array with bytes from cleardata 
    // corresponding to the '1' in passwords bit
    long ci = 0;
    for (long b = cdlen - 1; b >= 0; b--)
        {
        if (password.GetBitR(b))
            {
            cryptdata[ci] = cleardata[b];
            ci++;
            }
        }
    // second loop: fill crypt array with bytes from cleardata 
    // corresponding to the '0' in passwords bit
    for (long b = cdlen - 1; b >= 0; b--)
        {
        if (!password.GetBitR(b))
            {
            cryptdata[ci] = cleardata[b];
            ci++;
            }
        }
    return cryptdata;
    }

public static byte[] Cry_ScrambleByteRightDec(this byte[] cryptdata, byte[] password)
    {
    long cdlen = cryptdata.LongLength;
    byte[] cleardata = new byte[cdlen];
    long ci = 0;
    for (long b = cdlen - 1; b >= 0; b--)
        {
        if (password.GetBitR(b))
            {
            cleardata[b] = cryptdata[ci];
            ci++;
            }
        }
    for (long b = cdlen - 1; b >= 0; b--)
        {
        if (!password.GetBitR(b))
            {
            cleardata[b] = cryptdata[ci];
            ci++;
            }
        }
    return cleardata;
    }

// --------------------------------------------------------------------------------------

public static byte[] Cry_ScrambleBitRightEnc(this byte[] cleardata, byte[] password)
    {
    long cdlen = cleardata.LongLength;
    byte[] cryptdata = new byte[cdlen];
    // first loop: fill crypt array with bits from cleardata 
    // corresponding to the '1' in passwords bit
    long ci = 0;

    for (long b = cdlen * bitsinbyte - 1; b >= 0; b--)
        {
        if (password.GetBitR(b))
            {
            SetBitR(cryptdata, ci, cleardata.GetBitR(b));
            ci++;
            }
        }
    // second loop: fill crypt array with bits from cleardata 
    // corresponding to the '0' in passwords bit
    for (long b = cdlen * bitsinbyte - 1; b >= 0; b--)
        {
        if (!password.GetBitR(b))
            {
            SetBitR(cryptdata, ci, cleardata.GetBitR(b));
            ci++;
            }
        }
    return cryptdata;
    }
public static byte[] Cry_ScrambleBitRightDec(this byte[] cryptdata, byte[] password)
    {
    long cdlen = cryptdata.LongLength;
    byte[] cleardata = new byte[cdlen];
    long ci = 0;

    for (long b = cdlen * bitsinbyte - 1; b >= 0; b--)
        {
        if (password.GetBitR(b))
            {
            SetBitR(cleardata, b, cryptdata.GetBitR(ci));
            ci++;
            }
        }
    for (long b = cdlen * bitsinbyte - 1; b >= 0; b--)
        {
        if (!password.GetBitR(b))
            {
            SetBitR(cleardata, b, cryptdata.GetBitR(ci));
            ci++;
            }
        }
    return cleardata;
    }

// -----------------------------------------------------------------------------------

public static bool GetBitR(this byte[] bytearray, long bit)
    {
    return ((bytearray[(bit / bitsinbyte) % bytearray.LongLength] >> 
            ((int)bit % bitsinbyte)) & 1) == 1;
    }

public static void SetBitR(byte[] bytearray, long bit, bool set)
    {
    long bytepos = bit / bitsinbyte;
    if (bytepos < bytearray.LongLength)
        {
        int bitpos = (int)bit % bitsinbyte;
        byte adder;
        if (set)
            {
            adder = (byte)(1 << bitpos);
            bytearray[bytepos] = (byte)(bytearray[bytepos] | adder);
            }
        else
            {
            adder = (byte)(byte.MaxValue ^ (byte)(1 << bitpos));
            bytearray[bytepos] = (byte)(bytearray[bytepos] & adder);
            }
        }
    }
}

其他用途(密码学)

无论如何,除了用于字谜游戏之外,我认为该算法在某些类型的密码学中具有有趣的应用程序。 单独使用时,它不能提供足够的安全性,因为它不会更改 cleardata 字节,而只是移动它们。 即使是位版本也存在很大的弱点:outputcleardata中 1 位的数量是相同的(当然,对于 0 位也是如此)。 但是,与通常的块密码算法相比,它作用于整个输入,而不仅仅是一次作用于 16 或 32 个字节的块; 这意味着,输入越大,解码工作就越困难(这也意味着它在流密码中会非常弱,因为流块很小)。 将乱序字谜与例如 Rijndael 算法结合使用,可以产生更强的密码算法,该算法结合了两种算法的优点。

可以尝试以下操作序列(每个操作使用相同的密码,并将每个操作的输出用作下一个操作的输入)

  1. 乱序字谜字节
  2. 乱序字谜 BIT
  3. Rijndael
  4. 乱序字谜 BIT
  5. 乱序字谜字节

它会比使用简单的 Rijndael 更强大吗? 这个问题留给密码分析师。 此外,为了增加更多的安全性,还可以将上述操作放入一个循环中,并将这些操作重复 n 次(每次都使用上一次的输出作为输入),每次重复(希望)获得更大的安全性(并且在执行时间方面也具有更大的成本)。

历史

  • 2011 年 12 月 2 日 - 首次发布
© . All rights reserved.