Что плохого в переносе 32-битной переменной на 32 бита? - PullRequest
18 голосов
/ 16 апреля 2010

Я недавно взял копию прикладной криптографии Брюса Шнайера, и это было хорошее чтение. Теперь я понимаю, как работает несколько алгоритмов, описанных в книге, и я хотел бы начать реализацию нескольких из них на языке C.

Одной из общих черт многих алгоритмов является разделение x-битного ключа на несколько меньших y-битных ключей. Например, ключ Blowfish, X, является 64-битным, но вам необходимо разбить его на две 32-битные половины; Xl и Xr.

Это то место, где я застреваю. Я вполне приличен с Си, но я не самый сильный, когда дело доходит до побитовых операторов и тому подобного.

После некоторой помощи по IRC мне удалось придумать два макроса:

#define splitup(a, b, c) {b = a >> 32; c = a & 0xffffffff; }
#define combine(a, b, c) {a = (c << 32) | a;}

Где a - 64 бита, а b и c - 32 бита. Однако компилятор предупреждает меня о том, что я сдвигаю 32-битную переменную на 32 бита.

У меня такие вопросы:

  • Что плохого в переносе 32-битной переменной на 32 бита? Я предполагаю, что это не определено, но эти макросы действительно работают.
  • Кроме того, не могли бы вы предложить мне пойти другим путем?

Как я уже сказал, я довольно хорошо знаком с C, но побитовые операторы и тому подобное все еще вызывают у меня головную боль.

EDIT

Я понял, что мой макрос объединения на самом деле не объединяет две 32-битные переменные, а просто ORing 0 за a и в результате получает a. Итак, помимо моих предыдущих вопросов, у меня все еще нет метода объединения двух 32-битных переменных для получения 64-битной; предложение о том, как это сделать, будет оценено.

Ответы [ 5 ]

22 голосов
/ 16 апреля 2010

Да, это неопределенное поведение.

ИСО / МЭК 9899: 1999 6.5.7 Операторы битового сдвига ¶3

Целочисленные продвижения выполняются для каждого из операндов. Тип результата - тип повышенного левого операнда. Если значение правого операнда отрицательно или больше или равно ширине повышенного левого операнда, поведение не определено.

C11 или ISO / IEC 9899: 2011 говорит то же самое.

Сначала вы должны привести b к целевому целевому типу. Другой момент заключается в том, что вы должны ставить скобки вокруг параметров макроса, чтобы избежать неожиданностей из-за приоритетов операторов. Кроме того, здесь очень полезен оператор запятой, позволяющий избегать скобок, так что макрос можно использовать как обычную команду, заключенную в точку с запятой.

#define splitup(a,b,c) ( (b) = (a) >> 32, (c) = (a) & 0xffffffff )
#define combine(a,b,c) ( (a) = ((unsigned long long)(b) << 32) | (c) )

Могут потребоваться дополнительные приведения для `сплитпайпа, чтобы заставить замолчать предупреждения о потере точности компиляторами с чрезмерной параноидальностью.

#define splitup(a,b,c) ( (b) = (unsigned long)((a) >> 32), (c) = (unsigned long)((a) & 0xffffffff) )

И, пожалуйста, даже не думайте об использовании собственного написанного шифрования для производственного кода.

12 голосов
/ 16 апреля 2010

Смещение 32-битного значения на 32 или более бит не определено в C и C ++. Одна из причин, по которой он был оставлен неопределенным, заключается в том, что на некоторых аппаратных платформах 32-битная команда сдвига учитывает только 5 младших бит из предоставленного числа сдвигов. Это означает, что независимо от того, какое число сдвигов вы передадите, оно будет интерпретировано по модулю 32. Попытка смещения на 32 на такой платформе фактически сместит на 0, то есть не сместит вообще.

Авторы языка не хотели обременять компиляторы, написанные для такой платформы, задачей анализа числа смен перед выполнением смены. Вместо этого спецификация языка говорит, что поведение не определено. Это означает, что если вы хотите получить значение 0 из 32-разрядного сдвига на 32 (или более), вам нужно распознать ситуацию и обработать ее соответствующим образом.

3 голосов
/ 16 апреля 2010

что плохого в переносе 32-битной переменной на 32 бита?

Лучше присвоить 0 n-битному целому числу, чем сдвигать его на n-бит.

Пример:

 0 0 1 0 1 ----- 5 bit Integer  
 0 1 0 1 0 ----- 1st shift  
 1 0 1 0 0 ----- 2nd shift  
 0 1 0 0 0 ----- 3rd shift  
 1 0 0 0 0 ----- 4th shift  
 0 0 0 0 0 ----- 5th shift (all the bits are shifted!)  

У меня до сих пор нет метода объединения двух 32-битных переменных для получения 64-битной

Обратите внимание: a - это 64-битные, b и c - это 32-битные

a = b;
a = a << 32; //Note: a is 64 bit
a = a | c;
0 голосов
/ 06 сентября 2015

Что плохого в переносе 32-битной переменной на 32 бита?

В дополнение к тому, что уже было сказано, бит 32-й является знаковым битом, и вы можете получить расширение знака для сохранения пения, тем самым теряя значимые биты.

0 голосов
/ 16 апреля 2010

Если это не какой-то проект "заново изобретать колесо, чтобы понять, как он работает", не реализуйте свои собственные функции шифрования.

Ever.

Достаточно сложно использовать доступные алгоритмы для работы (и выбрать правильный), не стреляйте себе в ногу, запуская какой-то собственный крипто API Скорее всего, ваше шифрование не будет шифроваться

...