|
Author
|
Topic: 64-bit RSA! Public key cryptography comes to PB
|
Wayne Diamond Member
|
posted March 14, 2002 01:01 AM
I think this is the only RSA source available so far for PB. Don Dickinson has posted a nice demo that demonstrates how to use Microsoft's RSA base cryptographic service, but this demo provides the actual encryption/decryption sources itself, not relying on any 3rd party components. Don Dickinsons sample can be found at http://www.powerbasic.com/support/forums/Forum7/HTML/000366.html and is recommended reading  This is a 'direct' port from VB to PB. I used an RSA sample that I downloaded sometime last year, probably from PlanetSourceCode or somewhere similar, but wasn't able to find exactly where I got it from. However, I do know that the author's name is W.G. Griffiths, and he is the person to thank for making this possible -- it only took me half an hour to port, and porting can't really be considered 'work'  This is not optimised! In fact its probably as unoptimised as you could get due to porting it from VB, but it works well and could easily be converted to fast inline assembly. Anyway, enjoy!
#COMPILE EXE #INCLUDE "win32api.inc" GLOBAL key() AS DOUBLE GLOBAL p AS DOUBLE, q AS DOUBLE GLOBAL PHI AS DOUBLE DECLARE FUNCTION dec(tIp AS STRING, dD AS DOUBLE, dN AS DOUBLE) AS STRING DECLARE FUNCTION enc(tIp AS STRING, eE AS DOUBLE, eN AS DOUBLE) AS STRING DECLARE FUNCTION nMod(x AS DOUBLE, y AS DOUBLE) AS DOUBLE DECLARE FUNCTION Mult(BYVAL x AS DOUBLE, BYVAL p AS DOUBLE, BYVAL m AS DOUBLE) AS DOUBLE DECLARE FUNCTION IsPrime(lngNumber AS DOUBLE) AS BYTE DECLARE FUNCTION GCD(nPHI AS DOUBLE) AS DOUBLE DECLARE FUNCTION Euler(E3 AS DOUBLE, PHI3 AS DOUBLE) AS DOUBLE DECLARE SUB keyGen() SUB keyGen() 'Generates the keys for E, D and N DIM E#, D#, N# %PQ_UP = 9999 'set upper limit of random number %PQ_LW = 3170 'set lower limit of random number %KEY_LOWER_LIMIT = 10000000 'set for 64bit minimum p = 0: q = 0 RANDOMIZE TIMER DO UNTIL D > %KEY_LOWER_LIMIT 'makes sure keys are 64bit minimum DO UNTIL IsPrime(p) AND IsPrime(q) ' make sure q and q are primes p = INT((%PQ_UP - %PQ_LW + 1) * RND + %PQ_LW) q = INT((%PQ_UP - %PQ_LW + 1) * RND + %PQ_LW) LOOP N = p * q PHI = (p - 1) * (q - 1) E = GCD(PHI) D = Euler(E, PHI) LOOP key(1) = E key(2) = D key(3) = N END SUB FUNCTION Euler(E3 AS DOUBLE, PHI3 AS DOUBLE) AS DOUBLE 'genetates D from (E and PHI) using the Euler algorithm ON ERROR RESUME NEXT DIM u1#, u2#, u3#, v1#, v2#, v3#, q# DIM t1#, t2#, t3#, z#, uu#, vv#, inverse# u1 = 1 u2 = 0 u3 = PHI3 v1 = 0 v2 = 1 v3 = E3 DO UNTIL (v3 = 0) q = INT(u3 / v3) t1 = u1 - q * v1 t2 = u2 - q * v2 t3 = u3 - q * v3 u1 = v1 u2 = v2 u3 = v3 v1 = t1 v2 = t2 v3 = t3 z = 1 LOOP uu = u1 vv = u2 IF (vv < 0) THEN inverse = vv + PHI3 ELSE inverse = vv END IF Euler = inverse END FUNCTION FUNCTION GCD(nPHI AS DOUBLE) AS DOUBLE 'generates a random number relatively prime to PHI ON ERROR RESUME NEXT DIM nE#, y# DIM x AS DOUBLE %N_UP = 99999999 'set upper limit of random number for E %N_LW = 10000000 'set lower limit of random number for E RANDOMIZE nE = INT((%N_UP - %N_LW + 1) * RND + %N_LW) top: x = nPHI MOD nE y = x MOD nE IF y <> 0 AND IsPrime(nE) THEN GCD = nE EXIT FUNCTION ELSE nE = nE + 1 END IF GOTO top END FUNCTION FUNCTION IsPrime(lngNumber AS DOUBLE) AS BYTE 'Returns '%TRUE' if lngNumber is a prime ON ERROR RESUME NEXT DIM lngCount# DIM lngSqr# DIM x# lngSqr = INT(SQR(lngNumber)) ' Get the int square root IF lngNumber < 2 THEN IsPrime = %FALSE EXIT FUNCTION END IF lngCount = 2 IsPrime = %TRUE IF lngNumber MOD lngCount = 0 THEN IsPrime = %FALSE EXIT FUNCTION END IF lngCount = 3 FOR x = lngCount TO lngSqr STEP 2 IF lngNumber MOD x = 0 THEN IsPrime = %FALSE EXIT FUNCTION END IF NEXT END FUNCTION FUNCTION Mult(BYVAL x AS DOUBLE, BYVAL p AS DOUBLE, BYVAL m AS DOUBLE) AS DOUBLE DIM y AS DOUBLE 'encrypts, decrypts values passed to the function.. e.g. 'Mult = M^E mod N (encrypt) where M = x , E = p, N = m 'Mult = M^D mod N (decrypt) ON ERROR GOTO error1 y = 1 DO WHILE p > 0 DO WHILE (p / 2) = INT((p / 2)) x = nMod((x * x), m) p = p / 2 LOOP y = nMod((x * y), m) p = p - 1 LOOP Mult = y EXIT FUNCTION error1: y = 0 END FUNCTION FUNCTION nMod(x AS DOUBLE, y AS DOUBLE) AS DOUBLE 'this function replaces the Mod command. instead of z = x Mod y 'it is now z = nMod(x,y) ON ERROR RESUME NEXT DIM z# z = x - (INT(x / y) * y) nMod = z END FUNCTION FUNCTION enc(tIp AS STRING, eE AS DOUBLE, eN AS DOUBLE) AS STRING 'returns the long value of the characters, chained with a + 'e.g. 12345678+23456789+ etc.. '**Taken out encryption algorithm to simplify program** ON ERROR RESUME NEXT DIM encSt AS STRING DIM e2st AS STRING DIM I AS LONG encSt = "" e2st = "" IF tIp = "" THEN EXIT FUNCTION FOR i = 1 TO LEN(tIp) encSt = encSt & TRIM$(STR$(Mult(CLNG(ASC(MID$(tIp, i, 1))), eE, eN)) & "+") NEXT i '** put your encryption algorithm code here ** enc = encSt END FUNCTION FUNCTION dec(tIp AS STRING, dD AS DOUBLE, dN AS DOUBLE) AS STRING 'returns the characters from the long values 'e.g A = 12345678, B = 23456789 etc.. '**Taken out decryption algorithm to simplify program** ON ERROR RESUME NEXT DIM decSt AS STRING DIM z AS LONG DIM zptr AS LONG DIM tok AS LONG decSt = "" '** put your decryption algorithm code here ** FOR z = 1 TO LEN(tIp) zptr = INSTR(z, tIp, "+") tok = VAL(MID$(tIp, z, zptr)) decSt = decSt + CHR$(Mult(tok, dD, dN)) z = zptr NEXT z dec = decSt END FUNCTION '///////////////////////////////////////////////////// '// Demo program - creates private and public keys, // '// then encrypts and decrypts a test string. // '///////////////////////////////////////////////////// FUNCTION PBMAIN() AS LONG REDIM key(3) AS DOUBLE DIM EncStr AS STRING DIM DecStr AS STRING DIM PlainText AS STRING PlainText = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890" 'The message to encrypt keyGen STDOUT "Result:" STDOUT " p=" & STR$(p) STDOUT " q=" & STR$(q) STDOUT " PHI=" & STR$(PHI) STDOUT " E=" & LEFT$(STR$(Key(1)) & SPACE$(12),12) & "PRIVATE KEY" STDOUT " D=" & LEFT$(STR$(Key(2)) & SPACE$(12),12) & "PUBLIC KEY" STDOUT " N=" & LEFT$(STR$(Key(3)) & SPACE$(12),12) & "PUBLIC KEY" STDOUT STDOUT "Plaintext: " & PlainText EncStr = enc(Plaintext, key(1), key(3)) STDOUT "Encrypted: " & EncStr DecStr = dec(EncStr, key(2), key(3)) STDOUT "Decrypted: " & DecStr WAITKEY$ END FUNCTION
------------------
IP: Logged |
Wayne Diamond Member
|
posted March 14, 2002 01:09 AM
NB. Obviously the above source is for PBCC, but you can easily get it going in PBDLL by changing STDOUT to MSGBOX, and deleting the WAITKEY$ statement. Also, 64-bit is not very strong (it's not WEAK, but it's not bulletproof) -- always look for at least 128-bit for really strong security. Here is the descriptive text that accompanied the source: How RSA Works ============= We begin with choosing two random large distinct primes p and q. We also pick e, a random integer that is relatively prime to (p-1)*(q-1). The random integer e is the encryption exponent. Let n = p*q. Using Euclid's greatest common divisor algorithm, one can compute d, the decryption exponent, such that: e*d = 1 (mod (p-1)*(q-1)) Both plaintext m and ciphertext c should be in the set of nonnegative integers. Furthermore, before encrypting a plaintext message m, we need to make sure that 0 <= m < n. If m is greater than the modulus n, the result c of the encryption will not be a unique one-to-one mapping from m to c. From one of the theorems of Euler's, we know that for all integers m, med = m (mod n) Therefore, provided that 0 <= m < n, med (mod n) = m To encrypt a message m, we perform the following algorithm: Ek(m) = me (mod n) = c where Ek( ) denotes the encryption algorithm. To decipher the ciphertext c with the private key d, we perform the following algorithm: Dk(c) = cd (mod n) = med (mod n) = m1 (mod n) = m where Dk( ) denotes the decryption algorithm. The pair (e, n) make up the public-key of the RSA Cryptosystem. Everyone can use the pair (e, n) to encrypt a message. For example, Alice can publish her (e, n) public-key pair on the network. When Bob wants to send a secret message to Alice, he finds Alice's public-key set (e, n) from the network and encrypts his message using Alice's public-key: c = me (mod n). p, q, and d make up Alice's private-key. Only Alice knows p, q, and d. A third party, Carol, cannot understand what Bob wrote Alice because Carol does not have the private-key. When Alice gets the message from Bob, she decrypts it using her private-key set d, n by performing cd (mod n) = m. Without knowing d, one cannot decrypt the ciphertext c and get message m back. To get d, one needs to know (p-1)*(q-1) in order to find d from the equation e*d = 1 (mod (p-1)*(q-1)). Furthermore, to get (p-1)*(q-1), one needs to first be able to factor the large number n into its primes p and q. Since all the numbers involved are very large numbers, we can say that it is essentially computationally impossible for an illegitimate party to obtain d, and thus decrypt the ciphertext. · RSA Example Given p = 29, q = 31, e = 13, m = 123; ==>We compute: n = p * q = 899 (p-1)*(q-1) = 840 d = 517 since e*d = 13*517 = 8*(p-1)*(q-1) + 1 To encrypt, c = 123^13 (mod 899) = 402 And to decrypt, m = 402^517 (mod 899) = 123
------------------
IP: Logged |
Wayne Diamond Member
|
posted March 25, 2002 01:58 AM
I've enhanced the encrypt and decrypt functions so that rather than having each character encrypted to 8-9 bytes (eg. "a" might encrypt to become "3042913+"), each encrypted character is now only 4 bytes in size. Its simple - it pads the number with 0's on the left to ensure that it's 8 characters, eg. "03042913". That is then broken into 4 bytes, eg 03, 04, 29, 13. Before decrypting, it simply turns 03, 04, 29, 13 back into "3042913" which it can then decrypt back to "a" -- too easy!  Simply replace the enc and dec functions with these three functions:
FUNCTION GoHex(xLng AS LONG) AS STRING ON ERROR RESUME NEXT DIM I AS LONG DIM xStr AS STRING DIM OutStr$ xStr = RIGHT$("00000000" & TRIM$(STR$(xLng)), 8) FOR I = 1 TO LEN(xStr) OutStr$ = OutStr$ & CHR$(VAL("&h" & MID$(xStr, I, 2))) ! inc I NEXT I FUNCTION = OutStr$ END FUNCTION FUNCTION enc(tIp AS STRING, eE AS DOUBLE, eN AS DOUBLE) AS STRING ON ERROR RESUME NEXT DIM encSt AS STRING DIM e2st AS STRING DIM I AS LONG encSt = "" e2st = "" IF tIp = "" THEN EXIT FUNCTION FOR i = 1 TO LEN(tIp) encSt = encSt & GoHex(Mult(CLNG(ASC(MID$(tIp, i, 1))), eE, eN)) NEXT i enc = encSt END FUNCTION FUNCTION dec(tIp AS STRING, dD AS DOUBLE, dN AS DOUBLE) AS STRING ON ERROR RESUME NEXT DIM decSt AS STRING DIM z AS LONG DIM zptr AS LONG DIM sTmp AS STRING DIM tok AS LONG decSt = "" FOR z = 1 TO LEN(tIp) sTmp = sTmp & HEX$(ASC(MID$(tIp,z,1)),2) sTmp = sTmp & HEX$(ASC(MID$(tIp,z+1,1)),2) sTmp = sTmp & HEX$(ASC(MID$(tIp,z+2,1)),2) sTmp = sTmp & HEX$(ASC(MID$(tIp,z+3,1)),2) tok = VAL(sTmp) decSt = decSt + CHR$(Mult(tok, dD, dN)) ! add z, 3 sTmp = "" NEXT z dec = decSt END FUNCTION ------------------
IP: Logged |