PowerBASIC Forums
  Source Code
  64-bit RSA! Public key cryptography comes to PB

Post New Topic  Post A Reply
profile | register | preferences | faq | search

UBBFriend: Email This Page to Someone! next newest topic | next oldest topic
Author Topic:   64-bit RSA! Public key cryptography comes to PB
Wayne Diamond
Member
posted March 14, 2002 01:01 AM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
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     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
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     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
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

All times are EasternTime (US)

next newest topic | next oldest topic

Administrative Options: Close Topic | Archive/Move | Delete Topic
Post New Topic  Post A Reply
Hop to:

Contact Us | PowerBASIC BASIC Compilers

Copyright © 1999-2005 PowerBASIC, Inc. All Rights Reserved.


Ultimate Bulletin Board 5.45c