Реализация целого числа без знака — перенос C # на Java

#c# #java #cryptography

#c# #java #криптография

Вопрос:

Я переношу несколько тысяч строк криптографических функций C # в проект Java. Код C # широко использует значения без знака и побитовые операции.

Я знаю о необходимых обходных путях Java для поддержки значений без знака. Однако было бы намного удобнее, если бы существовали реализации 32-битных и 64-битных целых чисел без знака, которые я мог бы вставить в свой код. Пожалуйста, дайте ссылку на такую библиотеку.

Быстрые запросы Google показывают несколько, которые являются частью коммерческих приложений:

Комментарии:

1. Кстати, я забыл спросить: какую именно функцию вы ищете в этих библиотеках, которую нельзя выполнить с помощью чего-то подобного тому, что я написал ниже?

2. @Mehrdad Мои неправильные представления о Java, возможно, также исказили то, что я предполагал возможным. Я, вероятно, приму ответ Томаса Порнина за объединение общих методов — хотя это именно то, чего я пытался избежать.

3. Честно говоря, в побитовой арифметике задействовано не так уж много «опыта»; вы действительно можете сделать это самостоятельно. Умный в каком смысле, однако? Если вы имеете в виду скорость, это одно, но в остальном, я думаю, проблема в правильности, а не в сообразительности. (А также, очевидно, примите ответ Томаса, если вы нашли это более полезным! :] )

Ответ №1:

Операции с целыми числами со знаком и без знака в основном идентичны, когда используются обозначения дополнения two, что и делает Java. Это означает, что если у вас есть два 32-разрядных слова a и b и вы хотите вычислить их сумму a b , одна и та же внутренняя операция выдаст правильный ответ, независимо от того, считаете ли вы слова подписанными или беззнаковыми. Это будет работать должным образом для сложений, вычитаний и умножений.

Операции, которые должны учитывать знак, включают:

  • Сдвиги вправо: сдвиг вправо со знаком дублирует знаковый бит, в то время как сдвиг вправо без знака всегда вставляет нули. Java предоставляет оператор « >>> » для сдвига вправо без знака.
  • Деления: деление без знака отличается от деления со знаком. При использовании 32-разрядных целых чисел вы можете преобразовать значения в 64-разрядный long тип (« x amp; 0xFFFFFFFFL » выполняет трюк с «преобразованием без знака»).
  • Сравнения: если вы хотите сравнить a с b как два 32-разрядных слова без знака, тогда у вас есть две стандартные идиомы:

    if ((a Integer.MIN_VALUE) < (b Integer.MIN_VALUE)) { ... }

    if ((a amp; 0xFFFFFFFFL) < (b amp; 0xFFFFFFFFL)) { ... }

Зная это, подписанные типы Java не представляют большой проблемы для криптографического кода. Я реализовал много криптографических примитивов на Java, и подписанные типы не являются проблемой при условии, что вы понимаете, что пишете. Например, взгляните на sphlib: это библиотека с открытым исходным кодом, которая реализует множество криптографических хэш-функций как на C, так и на Java. Java-код использует подписанные типы Java ( int , long …) довольно плавно, и это просто работает.

В Java нет перегрузки операторов, поэтому «решения» только для Java для получения неподписанных типов будут включать пользовательские классы (такие как UInt64 класс, на который вы ссылаетесь), что приведет к значительному снижению производительности. Вы действительно не хотите этого делать.

Теоретически, можно было бы определить Java-подобный язык с неподписанными типами и реализовать компилятор, который создает байт-код для JVM (внутренне используя приемы, которые я подробно описал выше для сдвигов, делений и сравнений). Я не знаю ни одного доступного инструмента, который делает это; и, как я сказал выше, подписанные типы Java просто хороши для криптографического кода (другими словами, если у вас возникли проблемы с такими подписанными типами, то осмелюсь предположить, что вы недостаточно знаете для безопасной реализации криптографического кода, и вам следует воздержаться от этого; вместо этого используйте существующие библиотеки с открытым исходным кодом).

Ответ №2:

Это языковая функция, а не библиотечная, поэтому нет способа расширить Java для поддержки этой функциональности, если вы не измените сам язык, и в этом случае вам нужно будет создать свой собственный компилятор.

Однако, если вам нужен сдвиг вправо без знака, Java поддерживает >>> оператор, который работает аналогично >> оператору для неподписанных типов.

Однако вы можете создать свои собственные методы для выполнения арифметики со знаковыми типами, как если бы они были беззнаковыми; это должно сработать, например:

 static int multiplyUnsigned(int a, int b)
{
    final bool highBitA = a < 0,    highBitB = b < 0;
    final long a2 = a amp; ~(1 << 31), b2 = b amp; ~(1 << 31);
    final long result = (highBitA ? a2 | (1 << 31) : a2)
                      * (highBitB ? b2 | (1 << 31) : b2);
    return (int)resu<
}
  

Редактировать:

Благодаря комментарию @Ben’s мы можем упростить это:

 static int multiplyUnsigned(int a, int b)
{
    final long mask = (1L << 32) - 1;
    return (int)((a amp; mask) * (b amp; mask));
}
  

Однако ни один из этих методов не работает для long типа. В этом случае вам пришлось бы привести к double , отрицать, умножать и приводить его обратно, что, вероятно, уничтожило бы все ваши оптимизации.

Комментарии:

1. Спасибо за ваш ответ, но я хорошо знаком с >>> оператором. Я действительно ожидал бы, что было бы возможно реализовать и целочисленный объект без знака в Java, включая необходимые перегрузки оператора. Документы для коммерческих проектов, таких как этот , дают мне надежду.

2. Я не думаю, что функция multiply возвращает что-либо похожее на правильные результаты, когда установлен старший бит.

3. @Karl: Java не поддерживает перегрузку операторов. И для криптографических целей объекты были бы очень медленными; вам понадобились бы примитивные типы без знака, которых не существует в Java.

4. @Ben: Да, вы были правы; Я отредактировал это, это должно сработать.

5. @Mehrdad: Если у вас более широкий целочисленный тип, вы можете сделать просто long mask = (1L << 32) - 1; return (a amp; mask) * (b amp; mask); , но это не распространяется на самый широкий целочисленный тип.