【问题标题】:Nibble shifting半字节移位
【发布时间】:2011-09-22 18:31:28
【问题描述】:

我正在研究一种加密算法,我想知道如何将以下代码更改为更简单的代码以及如何反转此代码。

typedef struct 
{
    unsigned low : 4;
    unsigned high : 4;
} nibles;

static void crypt_enc(char *data, int size)
{
    char last = 0;

    //...

    // Pass 2
    for (i = 0; i < size; i++)
    {
        nibles *n = (nibles *)&data[i];

        n->low = last;
        last = n->high;
        n->high = n->low;
    }
    ((nibles *)&data[0])->low = last;
}

data 是这段代码的输入和输出。

【问题讨论】:

  • 反转这段代码是什么意思?你是说解密?你设计了一个加密算法却不知道如何解密?
  • @CodeMonkey 是的,解密。我的设计并不完整,我只知道我想做什么。
  • data 是什么?什么是小食?
  • @R.MartinhoFernandes 抱歉,代码有误。修复 xD
  • @KevinDTimm 不,其余部分不会被移动。其余的将有两个半字节等于前一个高半字节的前一个值。

标签: c++ cryptography byte shift nibble


【解决方案1】:

您将每个字节的两个半字节设置为相同的值,因为您最终将高半字节设置为与低半字节相同。我假设这是一个错误,您的意图是移动数据中的所有半字节,从一个字节传送到另一个字节,然后滚动。 Id est,ABCDEF(从低到高的半字节顺序)将变为 FABCDE。如果我错了,请纠正我。

代码应该是这样的:

static void crypt_enc(char *data, int size)
{
    char last = 0;

    //...

    // Pass 2
    for (i = 0; i < size; i++)
    {
        nibles *n = (nibles *)&data[i];

        unsigned char old_low = n->low;
        n->low = last;
        last = n->high;
        n->high = old_low;
    }
    ((nibles *)&data[0])->low = last;
}

现在一切都好吗?不会。只有当nibbles 的对齐方式不比char 的对齐方式更严格时,才能明确定义为nibbles*。还有that is not guaranteed(但是,使用small change,GCC 会生成一个具有相同对齐方式的类型)。

就我个人而言,我会完全避免这个问题。这是我的做法:

void set_low_nibble(char& c, unsigned char nibble) {
    // assumes nibble has no bits set in the four higher bits)
    unsigned char& b = reinterpret_cast<unsigned char&>(c);
    b = (b & 0xF0) | nibble;
}

void set_high_nibble(char& c, unsigned char nibble) {
    unsigned char& b = reinterpret_cast<unsigned char&>(c);
    b = (b & 0x0F) | (nibble << 4);
}

unsigned char get_low_nibble(unsigned char c) {
    return c & 0x0F;
}

unsigned char get_high_nibble(unsigned char c) {
    return (c & 0xF0) >> 4;
}

static void crypt_enc(char *data, int size)
{
    char last;

    //...

    // Pass 2
    for (i = 0; i < size; ++i)
    {
        unsigned char old_low = get_low_nibble(data[i]);
        set_low_nibble(data[i], last);
        last = get_high_nibble(data[i]);
        set_high_nibble(data[i], old_low);
    }
    set_low_nibble(data[0], last);
}

反过来相当于将“低”变为“高”,反之亦然;滚动到最后一口,而不是第一口;并以相反的方向浏览数据:

for (i = size-1; i >= 0; --i)
{
    unsigned char old_high = get_high_nibble(data[i]);
    set_high_nibble(data[i], last);
    last = get_low_nibble(data[i]);
    set_low_nibble(data[i], old_high);
}
set_high_nibble(data[size-1], last);

如果您愿意,您可以摆脱对临时last 的所有转移。您只需要保存最后一个半字节,然后直接移动半字节而不使用另一个变量:

last = get_high_nibble(data[size-1]);
for (i = size-1; i > 0; --i) // the last one needs special care
{
    set_high_nibble(data[i], get_low_nibble(data[i]));
    set_low_nibble(data[i], get_high_nibble(data[i-1]));
}
set_high_nibble(data[0], get_low_nibble(data[0]));
set_low_nibble(data[0], last);

【讨论】:

  • n-&gt;high = n-&gt;low; -- 我想你的意思是n-&gt;high = old_low;
  • 该死,我错过了关于铸造成小食的部分。这就是我喜欢 SO 的原因。
  • +1 便于阅读代码。我认为,您最好从阵列端转移到开始,以减少临时存储使用和传输。将最后一个 nibble 保存到 last,并继续传输 prev 的高 nibble。字节到电流的低半字节。
  • @vhallac 这是一个很好的建议!虽然我认为这不会有太大的性能提升。我希望任何体面的编译器最后注册。不过我还是会考虑的。
【解决方案2】:

看起来您只是将每个半字节移动一个位置,然后将最后一个字节的低半字节移到开头。逆向解密即可(从data的末尾开始,移到开头)

【讨论】:

    【解决方案3】:

    由于您使用的是位域,因此不太可能有移位样式的方法来移动半字节。如果这种转移对您很重要,那么我建议您考虑将它们存储在某种无符号整数中。在这种形式下,可以有效地执行位操作。

    【讨论】:

      【解决方案4】:

      Kevin's answer 是正确的。但是,您犯了一个基本错误。最终结果是整个数组都被零填充,而不是旋转半字节。

      要了解为什么会出现这种情况,我建议您首先以相同的方式实现字节轮换 ({a, b, c} -&gt; {c, a, b}),即使用从 0 增加到数组大小的循环计数器。看看是否可以通过减少对变量 last 的传输来做得更好。

      一旦您了解如何做到这一点,您就可以简单地将相同的逻辑应用于半字节 ({al:ah, bl:bh, cl:ch} -&gt; {ch:al, ah:bl, bh:cl})。如果您从十六进制值的角度考虑,我在这里的表示是不正确的。在我的符号中,十六进制值 0xXYY:X。如果您考虑一下您是如何完成字节轮换的,您可以弄清楚如何只保存一个 nibble,并且只需传输 nibbles 而无需将它们实际移动到 last

      【讨论】:

      • 我看到了他的意思,但同意,它会充满零:)
      【解决方案5】:

      逆向代码是不可能的,因为该算法完全删除了第一个字节并丢弃了其余部分的下半部分。

      在 for 循环的第一次迭代中,第一个字节的较低部分设置为零。

      n->low = last;
      

      它从未在任何地方保存过。它已经消失了。

      // I think this is what you were trying for
      last = ((nibbles *)&data[0])->low;
      for (i = 0; i < size-1; i++)
      {
          nibbles *n = (nibbles *)&data[i];
          nibbles *next = (nibbles *)&data[i+1];
          n->low = n->high;
          n->high = next->low;
      }
      ((nibbles *)&data[size-1])->high = last;
      

      反转它:

      last = ((nibbles *)&data[size-1])->high;
      for (i = size-1; i > 0; i--)
      {
          nibbles *n = (nibbles *)&data[i];
          nibbles *prev = (nibbles *)&data[i-1];
          n->high = n->low;
          n->low = prev->high;
      }
      ((nibbles *)&data[0])->low = last;
      

      ...除非我倒退高低。

      但无论如何,这在加密领域附近是无处可去的。这充其量是混淆。通过默默无闻的安全性是一种可怕的可怕做法,而自制加密会让人们陷入困境。如果你在玩,你的力量就更大了。但是,如果您确实希望某些东西是安全的,请出于对所有字节的热爱,请使用众所周知且安全的加密方案。

      【讨论】:

      • 有一个未指定的 pass 1... 这可能是 真正的 加密 :) 或者不是。
      • 嗯。可能。嘿,我见过一些困惑的人认为添加第二层“他们的加密”会增加安全性。它没有。 ......很难用几句话解释为什么。所以,你可以相信我,我来自互联网!
      • 即使第一次通过,没有密钥也无法加密。这不在参数列表中。 :)
      猜你喜欢
      • 1970-01-01
      • 2023-03-17
      • 2010-09-11
      • 2023-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-19
      相关资源
      最近更新 更多