February 10, 2015

Swiftly Secure, Part 2 – Multiplying Huge Numbers

Posted in iPhone development, mac development tagged , , , , , , , at 12:11 am by tetontech

In my previous post I mentioned how two 64 bit numbers could be multiplied together without ever overflowing. The approach taken was a variation of the grade school multiplication algorithm. In this posting I continue my path to an implementation of the Socialist Millionaire protocol used to securely communicate between 2 or more peers. In order use socialist millionaire I will have to multiply numbers that are much too large to fit into a 64 bit integer. This means I’ll need to multiply arrays of numbers by arrays of numbers.

There are algorithms out there for doing multi-word multiplication. Warren, at hackers delight, lays out a C version of Knuth’s grade school algorithm. It works, but has a design point I was unwilling to follow. It requires the arrays being multiplied to consist of half-word numbers rather than full word numbers. I wanted to explore code using arrays of unsigned 64 bit numbers to represent the very long numbers. It seemed a waste to only use 32 bits per number.

With that in mind I sat down to figure out how to apply the multiplication algorithm used in the last post in this new situation. I wasn’t surprised when I found was easily adaptable from multiplying two Swift UInt64’s to multiplying two Swift UInt64 arrays (Please see the graphics and discussion in the previous post to understand the algorithm basics).

Part of the new algorithm implementation includes adding two UInt64’s in situations where they could readily overflow. That meant I needed to create an add method to handle this correctly. It isn’t that complicated but does require the use of bitwise operators and comparisons.

func add(leftAddend:UInt64, rightAddend:UInt64) 
                                ->(UInt64,UInt64){
    var carryAmount:UInt64 = 0
    let sum:UInt64 = 0
    //check for overflow
    if ~leftAddend < rightAddend{
        carryAmount = 1
        sum = rightAddend - ~leftAddend - 1
    }
    else{
       sum = rightAddend + leftAddend
    }
    return (carryAmount,sum)
}

This implementation of add checks to see if overflow will happen before doing the addition. If there is no danger of overflow the two addends are combined. If overflow will happen the amount of overflow is stored and the sum is set to the maximum value of a UInt64.

In either case the add function returns the sum and a number representing how much too large the real sum is if it was to fit into a UInt64. We’ll use this carry amount in the multi-word multiplication algorithm.

Another change I made from Warren’s implementation was to have the arrays of numbers be in big endian format. This didn’t add significantly to the complicatedness of the algorithm but is how people ‘normally’ think of numbers. This can make it easier for some to see how the input relates to the algorithm’s output.

If the algorithm is implemented correctly some simple multiplications and their results should match these below. All arrays of numbers are base 2^64 where each array element represents a 64 bit ‘digit’ in big endian bit order.

  • multiply([UInt64.max/2 + 1],[2])     => [1,0]
  •  multiply([1,0,0],[1,1])                          => [0,1,1,0,0]
  • multiply([2,2],[UInt64.max/2 + 1])  => [1,1,0]
  • multiply([UInt64.max, UInt64.max, UInt64.max], [UInt64.max, UInt64.max])          => [18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 1]

In this implementation of the grade school algorithm discussed in the previous post the ‘bit’ size of the multiplicand and the multiplier are unknown. This means we can’t use the nice trick with the << operator and bit masks to create portions of the product we can add together. Instead we’ll collect the product portions directly into an array of UInt64’s.

If we create an array for the resultant product sized to be the sum of the sizes of the multiplicand and the multiplier it will always be large enough to hold the product. Sometimes it will be 1 digit too large but that will always be in the most significant digit location when it does happen. You can see an example of this if you look at the second bullet point of the list above. This is OK since a zero value in that place doesn’t affect the value of the number anyway.

The multi-word multiplication implementation of the algorithm looks like this.

func multiply(multiplicand:[UInt64], multiplier:[UInt64]) 
                                              ->[UInt64]{
    let multiplicandSize  = multiplicand.count
    let multiplierSize = multiplier.count
    let productSize = multiplicandSize + multiplierSize
    //create an array that is large enough 
    //to hold the product
    var product = [UInt64](count: productSize, 
                                         repeatedValue: 0)
    //follow the grade school methodology 
    //to do multiplication
    for index in stride(from: multiplierSize - 1, 
                                          to: -1, by: -1){
        let multiplierElement = multiplier[index]
        for subIndex in stride(from: multiplicandSize - 1, 
                                           to: -1, by: -1){
            let multiplicandElement = multiplicand[subIndex]
            //calculate the product and the carry amount
            let partialProduct:[UInt64] = 
        multiplyWithOverflow(multiplicandElement, multiplierElement)
            //calculate the location of the product
            let productIndex = (subIndex + index + 1)
            //calculate the index of the carry amount
            let carryIndex = productIndex - 1
            
            //calculate the amount to be inserted 
            //into the product and carry locations. 
            //Add the current calculated product to any amount that 
            //was previously carried forward.
            let (carryAmount, productSum) = add(product[productIndex], 
                                                    partialProduct[1])
            //the carrySum will never be able overflow.
            let (_, carrySum) = add(product[carryIndex], 
                                      carryAmount + partialProduct[0])
            product[carryIndex] = carrySum
            product[productIndex] = productSum
        }
    }
    return product
}

In this implementation each partial product is calculated and the overflow is placed in the next higher bit location. The partial product is then added to anything that was ‘carried’ over previously into the product’s location. For both the carryAmount and the partial product the add method described above was used to ensure overflows were handled correctly. While this algorithm isn’t quite as clean as the one in the last post, It is still fairly straight forward.

Since the socialist millionaire algorithm requires a division as one of its steps, my next posting will show an algorithm for multi-word division and subtraction. At this point, it looks like there will be some very interesting ideas to point out.

January 20, 2015

Swiftly Secure, Part 1 – Large Number Multiplication

Posted in iPhone development, mac development, Uncategorized tagged , , , at 8:52 pm by tetontech

I have a swift iOS project where I want users to be able to communicate between their devices by sharing data via a server. I want their communications to be encrypted in such a way that no one can decrypt them except the two parties communicating. In fact, I want their communications to be encrypted end-to-end without them being de-cryptable when they are on the server. To accomplish this, I’ve decided on using the Socialist Millionaire protocol since it handles both encryption and authentication.

Doing encryption of this type means I’m going to need to multiply integers that are too large to fit in any of the standard Swift or Objective-C integers. I explored some existing large number libraries in C, but the most commonly used ones appear to be GPL or LGPL licensed. I won’t use libraries under those licenses in a closed source app so I decided to create something of my own.

In this posting I’m going to show you a part of what I’m creating — how to multiply numbers in swift without encountering overflow problems. Overflow issues aren’t very common in business situations, numbers there tend to be small compared to the size of a 64 bit signed integer, but in some scientific computation situations and in my Socialist Millionaire implementation encryption overflow is an issue. Sampling from a random number stream and multiplying the results is another source of overflow situations. In fact, random sampling and multiplying has a very high probability of overflow if the random number generator yields results that are close to being evenly distributed.

I want to keep the example in this post simple, so I will show you an implementation of the grade school method. Multiplying 99 x 99 the way I was taught in grade school (multiply, carry, and then add) is a good place to start.

prod99

There is an alternative way of doing this same multiplication (multiply, add, then carry). In this example each time 9 is multiplied by 9 the result is written on a new line. All except the first partial product are shifted left —  one place for the second and third product, two places for the fourth product.

product99_2

The second method is the one I’m going to show in this posting. An example of bits being manipulated is helpful. To make it easier to understand we’ll stick with 8 bit numbers where each nibble (4 bits) represents one ‘digit’ for multiplication purposes.

 

Following the second grade school multiplication method using nibbles, lets multiply 255 x 255. Multiplying those two numbers should overflow an 8 bit result but we’ll keep that from happening without loosing any data.

Keep in mind that 0b1111 x 0b1111 = 0b11100001 (15 x 15 = 225).
255Mult2
The two part base 256 product, one 8 bit number representing the ones place and the other representing the 256’s place, is the result of left shifting the upper half of two nibble pairs and adding them to their corresponding bottom halves,

1111 << 4 + 1110 yields 11111110 and

0000 << 4 + 0001 yields 00000001.

The code example below follows the same pattern as the 8 bit multiplication without overflow table and uses the constant and variable names listed in the first column.

 

/*
 * I did this big endian since it was easier to think about this part of the algorithm this way
 */
func multiplyWithOverflow(multiplicand:UInt64, multiplier:UInt64) ->[UInt64]{
    
    //multiply to get all addends
    let lowerLowBits = (multiplier & bottomHalfBitMask) 
                       * (multiplicand & bottomHalfBitMask)
    let lowerHighBits = (multiplier & bottomHalfBitMask) 
                       * (multiplicand >> 32)
    let upperLowBits = (multiplier >> 32) 
                       * (multiplicand & bottomHalfBitMask)
    let upperHighBits = (multiplier >> 32) 
                       * (multiplicand >> 32)
    
    //get the 4 bit sums of the addends. There are 4. Some may be zero
    let bottom = lowerLowBits & bottomHalfBitMask
    let middleBottom = (lowerLowBits >> 32) + (lowerHighBits & bottomHalfBitMask) 
                  + (upperLowBits & bottomHalfBitMask) + (bottom >> 32)
    let middleTop = (lowerHighBits >> 32) + (upperLowBits >> 32) 
                  + (upperHighBits & bottomHalfBitMask) + (middleBottom >> 32)
    let top = (upperHighBits >> 32) + (middleTop >> 32)
    
    //combine the 32 bit sums to make two 64 bit values
    let bottom2N64 = (middleBottom << 32) + bottom
    let top2N64 = (top << 32) + (middleTop & bottomHalfBitMask)
    
    return [top2N64,bottom2N64]
}

If you want to use this overflow protection in a situation where overflow is infrequent, business, games, some simulations, etc., you will want to add an if statement to this code or allow the multiplication to overflow and then handle the situation as an error. Checking to see if two numbers will overflow before multiplying them turns out to be fairly easy. All you have to do is find the location of the highest bit in both numbers and add those locations together. If the location sum is greater that 63 the two numbers will overflow when multiplied.

In my next post, I’ll show how to represent huge numbers as arrays of UInt64’s and multiply those together, another component I need when implementing the Socialist Millionaire protocol.

%d bloggers like this: