Строка в 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, чтобы прокомментировать, адаптируется ли он к вашим потребностям.

Другие вопросы по тегам