Строка в BigNum и обратно (в Ruby), чтобы обеспечить круговое смещение
Как личный вызов я пытаюсь внедрить блочный шифр SIMON в Ruby. Я сталкиваюсь с некоторыми проблемами, находящими лучший способ работать с данными. Полный код, связанный с этим вопросом, находится по адресу: https://github.com/Rami114/Personal/blob/master/Simon/Simon.rb
SIMON требует как операций XOR, сдвига, так и операций циклического сдвига, последняя из которых вынуждает меня работать с BigNums, чтобы я мог выполнять левое круговое смещение с помощью математики, а не более сложного / более медленного двойного цикла в байтовых массивах.
Есть ли лучший способ конвертировать строку в BigNum и обратно.
String -> BigNum (где N- 64, а pt - строка открытого текста)
pt = pt.chars.each_slice(N/8).map {|x| x.join.unpack('b*')[0].to_i(2)}.to_a
Поэтому я разбиваю строку на отдельные символы, разрезаю на массивы N-размера (размер слова в SIMON) и распаковываю каждый набор в BigNum. Это, кажется, работает нормально, и я могу преобразовать его обратно.
Теперь мой код SIMON в данный момент не работает, но это скорее математика, я думаю / надеюсь, а не код. Обратное преобразование таково (где ct - массив бигнумов, представляющих зашифрованный текст):
ct.map { |x| [x.to_s(2).rjust(128,'0')].pack('b*') }.join
Я, кажется, должен выровнять по правому краю строку, так как bignums имеют неопределенную ширину, поэтому у меня нет начальных 0. К сожалению, пакет требует определенного, чтобы иметь разумный вывод.
Это действительный метод конвертации? Есть ли способ лучше? Я не уверен ни в одном из них и надеюсь, что кто-то здесь может помочь.
E: Для @torimus - реализация кругового сдвига, которую я использую (из ссылки выше)
def self.lcs (bytes, block_size, shift)
((bytes << shift) | (bytes >> (block_size - shift))) & ((1<< block_size)-1)
end
1 ответ
Если бы вы были одинаково довольны unpack('B*')
с MSB первые двоичные числа (что вы могли бы быть, если вся ваша обработка является круговой), то вы также можете использовать .unpack('Q>')
вместо .unpack('B*')[0].to_i(2)
для генерации pt
:
pt = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@"
# Your version (with 'B' == msb first) for comparison:
pt_nums = pt.chars.each_slice(N/8).map {|x| x.join.unpack('B*')[0].to_i(2)}.to_a
=> [8176115190769218921, 8030025283835160424, 7668342063789995618, 7957105551900562521,
6145530372635706438, 5136437062280042563, 6215616529169527604, 3834312847369707840]
# unpack to 64-bit unsigned integers directly
pt_nums = pt.unpack('Q>8')
=> [8176115190769218921, 8030025283835160424, 7668342063789995618, 7957105551900562521,
6145530372635706438, 5136437062280042563, 6215616529169527604, 3834312847369707840]
Нет никаких 128-битных пакетов / распаковок для возврата в другом направлении, но вы можете использовать Fixnum
чтобы решить это тоже:
split128 = 1 << 64
ct = pt # Just to show round-trip
ct.map { |x| [ x / split128, x % split128 ].pack('Q>2') }.join
=> "\x00\x00\x00\x00\x00\x00\x00\x00qwertyui . . . " # truncated
Это позволяет избежать многих временных этапов в вашем коде, но за счет использования другого байтового кодирования - я не знаю достаточно о SIMON, чтобы прокомментировать, адаптируется ли он к вашим потребностям.