Introduction to Public Key Cryptography Background The problem with symmetric or secret key cryptography is keeping the secret key secret. If some nefarious person gets your secret key, it’s like the guy said in the movie, Aliens, “Game over, man! Game over!” The biggest problem with secret key cryptography is distributing the key. Wouldn’t it be great to have a system so that you could send secret messages without having to send secret keys back and forth? Wouldn’t it be great if you could send and receive secret messages to people you hadn’t met yet? This sounds like the Holy Grail of Cryptography. It also sounds impossible. If you were to quiz the world’s cryptographers in 1975, if this could be done someday, they would have said told you it was impossible. Imagine everyone’s surprise in 1976, when Whitfield Diffie and Martin Hellman sprung asymmetric or public key cryptography on the world! To be fair, James Ellis, Clifford Cocks, and M.J. Williamson of British Intelligence discovered the same thing a few years earlier, but they didn’t do anything with it. Of course, the NSA claims they knew about it first. The basic idea of the public key cryptography is that it’s easy to do a mathematical function in one direction and very difficult to do it in reverse. Multiplication and factoring are good examples. It’s easy to multiply two large prime numbers together but given the product, it’s difficult to say what the original two numbers were that produced the product. This is the kind of math used to make public cryptography work; it involves multiplication, modular arithmetic, and exponentiation. Instead of a single key, there are now two keys, one used to encrypt and the other used to decrypt. The keys are different and it is not possible to compute one from the other. That means, just because you have the encryption key, you can’t figure out what the decryption key is. Here’s the great part about this: I can create pair of these keys. I can put the encryption key on my website, my business cards, in my e-mail signature. I can give it to my friends; they can give it to their friends. Somebody I’ve never met can send me email encrypted with my public key. Then this person can send me the email. I take my decryption key (which I do not reveal) and decrypt the message. The sender of the email and I did not have to go thru all sorts of covert contortions to keep keys secret. Dramatis Personae The players in tonight’s drama are Alice, Bob, and Eve. Alice and Bob want to carry on an exchange of information in an encrypted manner. Eve is a nefarious person interested in finding out what information Alice and Bob are carrying on. The Mathematics Communicating "in the clear", Alice and Bob select two numbers, q and n. Alice then selects the secret number xa. Bob selects the secret number xb. From the two public numbers, q and n, and her secret number xa, Alice calculates ya and sends the number to Bob. Using the same two public numbers, q and n, and his secret number xb, Bob calculates yb and sends the number to Alice. Alice and Bob have completed step one of the Diffie-Hellman process. The program crypto.bc shows how Alice calculates ya using the formula ya = (n ^ xa) % q This says, multiply n by itself xa times, then divide the product by q and save only the remainder. Alice sends the number ya to Bob. In the meantime, Bob applies the same formula to the numbers n, xb and q to calculate the number yb: yb = (n ^ xb) % q Bob sends the number yb to Alice. They are ready for step two. Using the number yb received from Bob, Alice calculates ka = (yb ^ xa) % q Again, this means multiply yb by itself xa times, and then save the remainder after dividing the product by q. When Bob receives Alice's number ya he calculates: kb = (ya ^ xb) % q Alice and Bob have completed the Diffie-Hellman encryption process. The Magic Alice applied her secret number xa to Bob's value yb and calculated ka. Bob applied his secret number xb to Alice's value ya and calculated kb. Presto: it turns out that ka = kb, a number now known to Alice and Bob, but to no one else. Even though Eve, the eavesdropper, may have been monitoring their communications studiously, Eve cannot discover the number ka easily. The Diffie-Hellman Algorithm The crypto.bc program, written for the bc compiler, was adapted from Simson Garfinkel's book on Phil Zimmerman's Pretty Good Privacy. Where large numbers would be used in practice, we use small numbers for clarity. The operation x % y returns x modulo y, that is, the remainder obtained when x is divided by y. The operation n ^ xa multiplies the number n times itself xa times. In programs designed to handle really large numbers, we can speed up the calculation of (n ^ xa) % q by applying the modulo operation after each multiply. While using relatively small numbers, illustration is made simpler. When executed with the command bc crypto.bc, this program writes the following output to stdout: Alice sends ya = 78 to Bob Bob sends yb = 534 to Alice from yb^xa = 3530785328217308860798464 Alice calculates ka = 117 from ya^xb = 308549209196654470906527744 Bob calculates kb = 117 They can now use 117 to encrypt messages. Passing Secrets Operating completely in the clear, Alice and Bob have passed the number 117 between them without disclosing it to anyone else. Eve, who may have been monitoring their communications or recording them for analysis, may have difficulty discovering the number ka = 117. Eve knows the numbers n, q, ya and yb, so she could write the equation ya = ( n ^ xa) % q and try all possible values of xa until she finds one that generates the known value of ya. Using that value of xa in the equation ka = (yb ^ xa) % q Eve could calculate the value of ka. This "brute force" method of attack, however, is practical only if Alice and Bob use small numbers. By using large numbers, preferably large prime numbers, Alice and Bob can make Eve's brute force attack impractical. Of Prime (Number) Importance We need prime numbers for the Diffie-Hellman process. To find prime numbers, we can use the program, testprime2.bc to find large prime numbers. The program first asks for a starting value. It tests the number entered, then loops repeatedly asking for an increment, advancing the test value by that amount and then testing again. Here is an example interaction: enter a starting value, then increments (or 0 to quit): 123487 is not prime, smallest factor is 7 2 123489 is not prime, smallest factor is 3 2 123491 is a prime number We need two prime numbers, so call the program again: enter a starting value, then increments (or 0 to quit): 394323 is not prime, smallest factor is 3 2 394325 is not prime, smallest factor is 5 2 394327 is a prime number Alice and Bob now have prime numbers to use. Alice has a program, alice.bc, and Bob has bob.bc to calculate keys using the Diffie-Hellman algorithm. Trying It Out Alice selects a secret number, such as 76697, and calls her program. Her interactive session looks like this: Alice: Enter public value q: 394327 Enter public value n: 123493 Enter your secret number xa: 76697 Alice, here is the number to send to Bob, ya: 323823 Bob selects the secret number 69623. His interactive session looks like this: Bob: Enter public value q: 394327 Enter public value n: 123493 Enter your secret number xb: 69623 Bob, here is the number to send to Alice, yb: 32675 Enter the number received from Alice, ya: 323823 Result: 74297 Bob has completed his side of the Diffie-Hellman process. When Alice receives the number yb from Bob, she cam complete the process on her side: Enter the number you received from Bob, yb: 32675 Result: 74297 Alice and Bob each have slipped the number 74297 past Eve, and they now can use that number to generate a key for encrypting messages. Eve Strikes Eve, of course, has been watching all this and has written a program of her own. When Eve calls her program eve.bc, her interactive session looks like this: Eve: enter public value q: 394327 enter public value n: 123493 enter observed public number ya: 323823 enter observed public number yb: 32675 enter a starting value for xa: 2 trying 3 trying 4 trying 5 trying 6 trying 7 <snip> trying 76693 trying 76694 trying 76695 trying 76696 trying 76697 found xa = 76697 Result: 74297 Depending on the speed of Eve's computer, this may take several minutes. Selecting q and n When selecting two prime numbers for the Diffie-Hellman process, use the larger number for q and the smaller number for n. To discover why, try swapping the two numbers around. With the values of q and n swapped, that is, with n larger than q, while Alice and Bob may see nothing out of the ordinary, Eve's cryptanalysis becomes much easier. And there is more. In addition to q being a prime number, the value (q-1)/2 should also be a prime number. This makes it harder for Eve to work backwards to the value of xa. Use the program, testqm1.bc to find prime numbers that fit this criteria. An example session looks like: enter a starting value, then state how many primes to find: 2426697105 3 q = 2426697107 is prime and (q-1)/2 = 1213348553 is ALSO prime q = 2426697359 is prime and (q-1)/2 = 1213348679 is ALSO prime q = 2426698727 is prime and (q-1)/2 = 1213349363 is ALSO prime Size Does Matter Knowing all of this, Alice selects 2426697107 for q and calls the testqm1 program again to find a smaller prime to use for n: enter a starting value, then state how many primes to find: 17123201 2 q = 17123207 is prime and (q-1)/2 = 8561603 is ALSO prime q = 17124539 is prime and (q-1)/2 = 8562269 is ALSO prime Alice selects 17123207 for n. With these numbers, and selecting the arbitrary number 31925631 for xa, Alice's interactive session looks like: Alice: Enter public value q: 2426697107 Enter public value n: 17123207 Enter your secret number xa: 31925631 Alice, here is the number to send to Bob, ya: 919478360 Alice sends the numbers q, n and ya to Bob. Using his selected secret number 19758139, Bob's interactive session looks like: Bob: Enter public value q: 2426697107 Enter public value n: 17123207 Enter your secret number xb: 19758139 Bob, here is the number to send to Alice, yb: 1724324231 Enter the number received from Alice, ya: 919478360 Result: 1490225501 When Alice receives Bob's number yb, she completes her calculation: Enter the number you received from Bob, yb: 1724324231 Result: 1490225501 Eve Strikes Back Eve calls her program Eve: enter public value q: 2426697107 enter public value n: 17123207 enter observed public number ya: 919478360 enter observed public number yb: 1724324231 enter a starting value for xa: 2 trying 3 trying 4 trying 5 trying 6 trying 7 ... <snip> Eve's program found xa = 31925631 and the result = 1490225501. On a 866 MHz Pentium Xeon with 1GB of memory and 1723.59 BogoMIPS, it took 9 hours to do the calculation. If Eve is on a network where she has access to, say, 30 other similar systems, she could run 30 copies of her program simultaneously; starting the first program near 0, the second at 1000000, the third at 2000000 and so on. One of the computers should find the solution within about 30 minutes. That being the case, perhaps Alice and Bob should use numbers even larger. Picking Secret Numbers Because it is xa that Eve's program must find, it might appear that simply selecting a very large value for xa would defeat Eve's brute force attack. The reality, though, is exactly the opposite. For the values q = 394327 n = 123493 xa = 76697 xb = 59232 Alice and Bob found result 365557. Eve's program found the same xa = 77797 and result = 365557 in about three minutes. For the same values of q and n with the larger values xa = 766973423 xb = 592329871 Alice and Bob obtained the result = 32000. In only eight seconds, Eve's program found this same result at a test value of xa = 9353. So it seems that if xa has more digits than q, then Eve's program doesn't even need to discover Alice's secret number xa at all. Instead it can find a solution at the much smaller trial value of xa. Modulo arithmetic is like a rain in Texas – you can’t be sure where it’s going to fall. Resources GNU Privacy Guard, a free replacement for PGP, can be downloaded from www.gnupg.org. Also available from the Free Software Foundation is the GNU bc compiler written by Philip A. Nelson. The entire bc package, including sources, can be downloaded from www.gnu.org/ directory/bc.html. An introduction to Pretty Good Privacy, including a blow-by-blow account of Phil Zimmerman's many battles, can be found in the book PGP: Pretty Good Privacy, by Simson Garfinkel, published in 1995 by O'Reilly. ISBN 1-56592-098-8. For additional background on PGP, see Phil Zimmerman's own book, The Official PGP User's Guide, published in 1995 by The MIT Press. For background on cryptography in practice that is written by a professional in the field, with details on Diffie-Hellman, RSA and many other algorithms, find a copy of Bruce Schneier's Applied Cryptography, published by Wiley. The second edition is available on Amazon. Programs Testprime2.bc /* testprime2.bc: test for prime numbers The exhaustive search method used here works ok for numbers up to about 12 digits. */ define qprime(value) { auto d, rem small_factor = 2 rem = value % 2 if ( rem == 0 ) return(rem) # it is even small_factor = 3 if ( value == 9) return(0) # skip it so we can start at 3 for (d = 3; d^2 <= value; d += 2) { rem = value % d /* print "d = ",d print "rem = ",rem print "\n" */ if ( rem == 0 ) break } small_factor = d return(rem) } scale = 0 print "enter a starting value, then increments (or 0 to quit):\n" testvalue = read(); /* get the starting value */ while ( 1 ) { a = qprime(testvalue) print testvalue if ( a == 0 && testvalue != 2 ) { print " is not prime, smallest factor is ",small_factor } if ( a != 0 ) { print " is a prime number" } if ( testvalue == 2 ) { print " is a special case, Ducky" } print "\n" increment = read() if (increment == 0) { break } testvalue += increment } halt Alice.bc /* alice.bc: Diffie-Hellman encryption demo */ # this modexp() bc routine is a transliteration # of the C routine found in Bruce Schneier's # "Applied Cryptography" Wiley, New York. 1994. # ISBN 0-471-59756-2 # modexp: from page 200 define modexp(a, x, n) { # return a ^ x mod n auto r r=1 while ( x > 0 ) { if ( (x % 2) == 1 ) { r = (r * a) % n } a=(a*a)%n x /= 2 } return(r) } print "Alice:\n" print "Enter public value q: "; q = read() print "Enter public value n: "; n = read() print "\nEnter your secret number xa: "; xa = read() ya = modexp(n,xa,q) print "\nAlice, here is the number to send to Bob, ya: "; ya print "\nEnter the number you received from Bob, yb: "; yb = read() ka = modexp(yb,xa,q) print "Result: "; ka print "\n\n" halt Bob.bc /* bob.bc: Diffie-Hellman encryption demo for Bob */ # this modexp() bc routine is a transliteration # of the C routine found in Bruce Schneier's # "Applied Cryptography" Wiley, New York. 1994. # ISBN 0-471-59756-2 # modexp: from page 200 define modexp(a, x, n) { # return a ^ x mod n auto r r=1 while ( x > 0 ) { if ( (x % 2) == 1 ) { r = (r * a) % n } a=(a*a)%n x /= 2 } return(r) } print "Bob:\n" print "Enter public value q: "; q = read() print "Enter public value n: "; n = read() print "\nEnter your secret number xb: "; xb = read() yb = modexp(n,xb,q) print "\nBob, here is the number to send to Alice, yb: "; yb print "\nEnter the number received from Alice, ya: "; ya = read() kb = modexp(ya,xb,q) print "\nResult: "; kb print "\n\n" halt eve.bc /* * eve.bc: Brute force attack on Diffie-Hellman encryption */ define modexp(a, x, n) { # return a ^ x mod n auto r r=1 while ( x > 0 ) { if ( (x % 2) == 1 ) { r = (r * a) % n } a=(a*a)%n x /= 2 } return(r) } print "Eve:\n" print "enter public value q: "; q = read() print "enter public value n: "; n = read() #print "\nenter your secret number x: "; xa = read() print "\nenter observed public number ya: "; ya = read() print "\nenter observed public number yb: "; yb = read() print "\nenter a starting value for xa: "; xa = read() while ( 1 ) { tya = modexp(n,xa,q) if (tya == ya) { break } xa += 1 print "trying ", xa, "\n" } print "\nfound xa = "; xa ka = modexp(yb,xa,q) print "\nResult: "; ka quit testqm1.bc /* testqm1.bc: find prime number q and test (q-1)/2 also. The exhaustive search method used here works ok for numbers up to about 12 digits. */ define qprime(value) { auto d, rem small_factor = 2 rem = value % 2 if ( rem == 0 ) return(rem) # it is even small_factor = 3 if ( value == 9) return(0) # skip it so we can start at 3 for (d = 3; d^2 <= value; d += 2) { rem = value % d if ( rem == 0 ) break } small_factor = d return(rem) } scale = 0 print "enter a starting value, then state how many primes to find:\n" testvalue = read() /* get the starting value */ find = read() /* get the count of primes to find */ i = 0; increment = 2 while ( i < find ) { a = qprime(testvalue) if ( a != 0 ) { savetest = testvalue t = (testvalue - 1)/2 aa = qprime(t) if ( aa != 0 ) { print "q = ", testvalue print " is prime and (q-1)/2 = ", t print " is ALSO prime\n" i += 1 } } if ( testvalue == 2 ) { print testvalue print " is a special case, Ducky\n" testvalue += 1 } testvalue += increment } halt