MCS 320 Project One due Monday 26 September 2005 at 2PM
| > | restart; |
1. The Knapsack Cryptosystem
The Knapsack Cryptosystem is a public key cryptosystem based on the hardness of the knapsack problem.
1.1. Encoding and decoding strings
The messages we write and read are strings of characters. To compute with those strings we encode the strings into bit sequences. In this section we show how to encode a string of characters as a list of list of bits and to decode such lists of lists into strings of characters. Here comes our message:
| > | message := "mcs 320 is fun": |
With convert we can encode and decode this message into sequences of bytes:
| > | a := convert(message,bytes); |
| > | convert(a,bytes); |
Again with convert, we compute binary expansions of numbers in decimal format:
| > | b := convert(109,base,2); |
To convert back into decimal, we evaluate the binary number, observing that the least significant bit comes first.
| > | evalbase2 := x -> sum(x[i]*2^(i-1),i=1..nops(x)): |
| > | evalbase2(b); |
To encode and decode, we now only have to assemble the different conversion:
| > | encode := s -> map(x->convert(x,base,2),convert(s,bytes)): |
| > | c := encode(message); |
![]()
![]()
| > | decode := s -> convert(map(x->evalbase2(x),s),bytes): |
| > | decode(c); |
We will use the encode and decode. Keep in mind that the sequences of bits are of unequal length.
1.2. The subset sum problem for super increasing sequences
A sequence of weights is super increasing if the k-th weigth is larger than the sum of all weights with lower index.
The function pack_byte takes a sequence of weights and bits and computes the total weight. If bit k is 1, then we pack the object with weight w[k].
| > | pack_byte := (w,x) -> sum(w['i']*x['i'],'i'=1..min(nops(w),nops(x))): |
For example:
| > | w := [2,3,6,13,27,52,105,220]: # a super increasing sequence of weights |
| > | x := convert(109,base,2); |
| > | pack_byte(w,x); |
We apply the dot product to every byte in the encoded message:
| > | pack_bytes := (w,x) -> map(t->pack_byte(w,t),x); |
| > | p := pack_bytes(w,c); |
The reverse direction is executed by the following procedure:
| > | unpack_byte := proc(w,s)
description `solves the subset sum problem for super increasing w`: local n,i,x,r: x := []: n := nops(w): r := s: for i from n by -1 to 1 do if r >= w[i] then x := [1,op(x)]: r := r-w[i]: elif nops(x) > 0 then x := [0,op(x)]: end if: end do: return x: end proc: |
| > | unpack_byte(w,178); |
We apply the unpack_byte to every sum in the list of packed bytes:
| > | unpack_bytes := (w,x) -> map(t->unpack_byte(w,t),x); |
| > | unpack_bytes(w,p); |
![]()
![]()
| > | decode(%); # make sure we still have our message ... |
1.3. Encrypting and decrypting lists of lists of bits
The subset sum problem is hard when the weights are not super increasing. We generate secret numbers u and v to generate a public key from w. To take relatively prime numbers, we use the nextprime command on randomly generated numbers:
| > | bound := add(w[i],i=1..nops(w)); |
| > | u := nextprime(bound): v := nextprime(234): |
| > | public_key := map(t->v*t mod u,w); |
| > | encrypt := (key,s) -> pack_bytes(key,encode(s)); |
| > | mm := encrypt(public_key,message); |
| > | private_key := 1/v mod u; |
| > | decrypt := (key,w,s) -> decode(unpack_bytes(w,map(t->key*t mod u,s))); |
| > | decrypt(private_key,w,mm); |
2. Lattice Basis Reduction
We will show that, for relatively small numbers, we can solve the subset sum problem with the LLL algorithm.
| > | with(IntegerRelations,LLL): |
| > | v := matrix(9,1,[-op(public_key),mm[2]]): |
| > | id := matrix(9,8,(i,j) -> piecewise(i=j,1,0)): |
| > | A := linalg[augment](id,v); |
| > | B := [seq(convert(linalg[row](A,i),list),i=1..9)]: |
| > | r := LLL(B,'integer'); |
![]()
![]()
| > | r[5]; |
| > | decode([r[5]]); |
We see that we recovered the second letter from the message.