Kyle Ivey
Kyle Ivey

Reputation: 6042

Unsigned Integer Implementation - Porting C# to Java

I'm porting several thousand lines of cryptographic C# functions to a Java project. The C# code extensively uses unsigned values and bitwise operations.

I am aware of the necessary Java work-arounds to support unsigned values. However, it would be much more convenient if there were implementations of unsigned 32bit and 64bit Integers that I could drop into my code. Please link to such a library.

Quick google queries reveal several that are part of commercial applications:

Upvotes: 3

Views: 1269

Answers (2)

Thomas Pornin
Thomas Pornin

Reputation: 74482

Operations with signed and unsigned integers are mostly identical, when using two's complement notation, which is what Java does. What this means is that if you have two 32-bit words a and b and want to compute their sum a+b, the same internal operation will produce the right answer regardless of whether you consider the words as being signed or unsigned. This will work properly for additions, subtractions, and multiplications.

The operations which must be sign-aware include:

  • Right shifts: a signed right shift duplicates the sign bit, while an unsigned right shift always inserts zeros. Java provides the ">>>" operator for unsigned right-shifting.
  • Divisions: an unsigned division is distinct from a signed division. When using 32-bit integers, you can convert the values to the 64-bit long type ("x & 0xFFFFFFFFL" does the "unsigned conversion" trick).
  • Comparisons: if you want to compare a with b as two 32-bit unsigned words, then you have two standard idioms:

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

    if ((a & 0xFFFFFFFFL) < (b & 0xFFFFFFFFL)) { ... }

Knowing that, the signed Java types are not a big hassle for cryptographic code. I have implemented many cryptographic primitives in Java, and the signed types are not an issue provided that you understand what you are writing. For instance, have a look at sphlib: this is an opensource library which implements many cryptographic hash functions, both in C and in Java. The Java code uses Java's signed types (int, long...) quite seamlessly, and it simply works.

Java does not have operator overloading, so Java-only "solutions" to get unsigned types will involve custom classes (such as the UInt64 class you link to), which will imply a massive performance penalty. You really do not want to do that.

Theoretically, one could define a Java-like language with unsigned types and implement a compiler which produces bytecode for the JVM (internally using the tricks I detail above for shifts, divisions and comparisons). I am not aware of any available tool which does that; and, as I said above, Java's signed types are just fine for cryptographic code (in other words, if you have trouble with such signed types, then I daresay that you do not know enough to implement cryptographic code securely, and you should refrain from doing so; instead, use existing opensource libraries).

Upvotes: 2

user541686
user541686

Reputation: 210705

This is a language feature, not a library feature, so there is no way to extend Java to support this functionality unless you change the language itself, in which case you'd need to make your own compiler.

However, if you need unsigned right-shifts, Java supports the >>> operator which works like the >> operator for unsigned types.

You can, however, make your own methods to perform arithmetic with signed types as though they were unsigned; this should work, for example:

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

Edit:

Thanks to @Ben's comment, we can simplify this:

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

Neither of these methods works, though, for the long type. You'd have to cast to a double, negate, multiply, and cast it back again in that case, which would likely kill any and all of your optimizations.

Upvotes: 2

Related Questions