Bitshifts to get 128-bit product

1

First, I'm aware that Python handles this fine without the use of bitwise operators, as the third line of code should make clear. I'm ultimately trying to do this in C for an assignment - implementing RSA (hence p, q, and n variables in the code) on an architecture that doesn't have uint_128t support; I'm just better with Python, and trying to use it to understand. EDIT: Storing total0 (128 bits) is its own issue without said support, but this proof-of-concept should allow me to figure out the rest.

I found this archived magazine discussing doing this with 64-bit products on the Motorola 68000 CPU, and tried to implement that in Python, but doubled. I got the upper 32 bits right, but the remaining 96 are incorrect (well, the last 16 are also correct, but they aren't shifted at all, so that's not surprising).

Just in case I had a math error in counting bits, I tried a brute force for/for/for loop to run total1/total2/total3 in range(0,81). No match.

p = 10259632476918722477
q = 17757547285565901767
n = p*q

big_mask =    0xffffffff
small_mask =      0xffff

p_lo = p & big_mask
p_hi = p >> 32
q_lo = q & big_mask
q_hi = q >> 32

p_lo_q_lo = p_lo * q_lo
p_lo_q_hi = p_lo * q_hi
p_hi_q_lo = p_hi * q_lo
p_hi_q_hi = p_hi * q_hi

p_lo_q_lo_UPPER = p_lo_q_lo >> 16
p_lo_q_lo_LOWER = p_lo_q_lo & small_mask
p_lo_q_hi_UPPER = p_lo_q_hi >> 16
p_lo_q_hi_LOWER = p_lo_q_hi & small_mask
p_hi_q_lo_UPPER = p_hi_q_lo >> 16
p_hi_q_lo_LOWER = p_hi_q_lo & small_mask
p_hi_q_hi_UPPER = p_hi_q_hi >> 16
p_hi_q_hi_LOWER = p_hi_q_hi & small_mask

# Original, incorrect implementation
#total0 = p_hi_q_hi_UPPER
#total1 = (p_lo_q_hi_UPPER + p_hi_q_lo_UPPER + p_hi_q_hi_LOWER)
#total2 = (p_lo_q_lo_UPPER + p_lo_q_hi_LOWER + p_hi_q_lo_LOWER)
#total3 = p_lo_q_lo_LOWER
#total = total0 << 80 | total1 << 33 | total2 << 14 | total3 << 0

# Corrected implementation
total0 = p_hi_q_hi_UPPER << 80
total1 = p_hi_q_hi_LOWER << 64
total2 = (p_lo_q_hi_UPPER + p_hi_q_lo_UPPER) << 48
total3 = (p_lo_q_hi_LOWER + p_hi_q_lo_LOWER) << 32
total4 = p_lo_q_lo_UPPER << 16
total5 = p_lo_q_lo_LOWER

total = total0 + total1 + total2 + total3 + total4 + total5

print("\n")
if (n==total):
  print("Match!")
else:
  print("No match.")
print("\n")
print("Actual:   " + str(n))
print("Computed: " + str(total))

print("\n")
# Strip '0b' off of the binary string so the list groupings display logically
hex_n = hex(n)[2:]
hex_total = hex(total)[2:]
print("Actual:   ", end="")
print("\t" + str([hex_n[i:i+4] for i in range(0, len(hex_n), 4)]))
print("Computed: ", end="")
print("\t" + str([hex_total[i:i+4] for i in range(0, len(hex_total), 4)]))

My thought with shifting total0 by 80 bits is that it's a 48-bit number, so it should have 80 0s padding it out to the right. total1 is 47 bits, so left-shift by 33 (80-47). total2 is 44 bits, so left-shift by 14 (47-33). Finally, leave total3 as-is, since it's the LSDW.

python
bit-manipulation
bitwise-operators
asked on Stack Overflow Jun 23, 2019 by Stephan Garland • edited Jun 23, 2019 by Stephan Garland

1 Answer

3

I think your shift amounts are off when creating the total values at the end. Here are the shift amounts I get for the various intermediate values:

p_lo         0
p_hi        32
q_lo         0
q_hi        32

p_lo_q_lo    0
p_lo_q_hi   32
p_hi_q_lo   32
p_hi_q_hi   64

p_lo_q_lo_UPPER    16
p_lo_q_lo_LOWER     0
p_lo_q_hi_UPPER    48
p_lo_q_hi_LOWER    32
p_hi_q_lo_UPPER    48
p_hi_q_lo_LOWER    32
p_hi_q_hi_UPPER    80
p_hi_q_hi_LOWER    64

Sorting the final set:

p_hi_q_hi_UPPER    80
p_hi_q_hi_LOWER    64
p_lo_q_hi_UPPER    48
p_hi_q_lo_UPPER    48
p_lo_q_hi_LOWER    32
p_hi_q_lo_LOWER    32
p_lo_q_lo_UPPER    16
p_lo_q_lo_LOWER     0

So there are 6 different final shift amounts, not 4. It should be easy to fix by creating 6 total variables at the end, and adding them with the shift amounts shown above.

answered on Stack Overflow Jun 23, 2019 by Tom Karzes • edited Jun 23, 2019 by Tom Karzes

User contributions licensed under CC BY-SA 3.0