RSA code: Difference between revisions
(→{{header|Go}}: added check on message size) |
m (→{{header|Go}}: library path update) |
||
Line 42: | Line 42: | ||
import ( |
import ( |
||
⚫ | |||
"fmt" |
"fmt" |
||
⚫ | |||
) |
) |
||
Revision as of 22:55, 5 December 2011
Given an RSA key (n,e,d), construct a program to encrypt and decrypt plaintext messages strings.
Background
RSA code is used to encode secret messages. It is named after Ron Rivest, Adi Shamir, and Leonard Adleman who published it at MIT in 1977. The advantage of this type of encryption is that you can distribute the number “” and “” (which makes up the Public Key used for encryption) to everyone. The Private Key used for decryption “” is kept secret, so that only the recipient can read the encrypted plaintext.
The process by which this is done is that a message, for example “Hello World” is encoded as numbers (This could be encoding as ASCII or as a subset of characters ). This yields a string of numbers, generally referred to as "numerical plaintext", “”. For example, “Hello World” encoded with a=1,...,z=26 by hundreds would yield .
The plaintext must also be split into blocks so that the numerical plaintext is smaller than otherwise the decryption will fail.
The ciphertext, , is then computed by taking each block of , and computing
Similarly, to decode, one computes
To generate a key, one finds 2 (ideally large) primes and . the value “” is simply: . One must then choose an “” such that . That is to say, and are relatively prime to each other.
The decryption value is then found by solving
The security of the code is based on the secrecy of the Private Key (decryption exponent) “” and the difficulty in factoring “”. Research into RSA facilitated advances in factoring and a number of factoring challenges. Keys of 768 bits have been successfully factored. While factoring of keys of 1024 bits has not been demonstrated, NIST expected them to be factorable by 2010 and now recommends 2048 bit keys going forward (see Asymmetric algorithm key lengths or NIST 800-57 Pt 1 Revised Table 4: Recommended algorithms and minimum key sizes).
Summary of the task requirements:
- Encrypt and Decrypt a short message or two using RSA with a demonstration key.
- Implement RSA do not call a library.
- Encode and decode the message using any reversible method of your choice (ASCII or a=1,..,z=26 are equally fine).
- Either support blocking or give an error if the message would require blocking)
- Demonstrate that your solution could support real keys by using a non-trivial key that requires large integer support (built-in or libraries). There is no need to include library code but it must be referenced unless it is built into the language. The following keys will be meet this requirement;however, they are NOT long enough to be considered secure:
- n = 9516311845790656153499716760847001433441357
- e = 65537
- d = 5617843187844953170308463622230283376298685
- Messages can be hard-coded into the program, there is no need for elaborate input coding.
- Demonstrate that your implementation works by showing plaintext, intermediate results, encrypted text, and decrypted text.
Go
<lang go>package main
import (
"fmt" "math/big"
)
func main() {
var n, e, d, bb, ptn, etn, dtn big.Int pt := "Rosetta Code" fmt.Println("Plain text: ", pt)
// a key set big enough to hold 16 bytes of plain text in // a single block (to simplify the example) and also big enough // to demonstrate efficiency of modular exponentiation. n.SetString("9516311845790656153499716760847001433441357", 10) e.SetString("65537", 10) d.SetString("5617843187844953170308463622230283376298685", 10)
// convert plain text to a number for _, b := range []byte(pt) { ptn.Or(ptn.Lsh(&ptn, 8), bb.SetInt64(int64(b))) } if ptn.Cmp(&n) >= 0 { fmt.Println("Plain text message too long") return } fmt.Println("Plain text as a number:", &ptn)
// encode a single number etn.Exp(&ptn, &e, &n) fmt.Println("Encoded: ", &etn)
// decode a single number dtn.Exp(&etn, &d, &n) fmt.Println("Decoded: ", &dtn)
// convert number to text var db [16]byte dx := 16 bff := big.NewInt(0xff) for dtn.BitLen() > 0 { dx-- db[dx] = byte(bb.And(&dtn, bff).Int64()) dtn.Rsh(&dtn, 8) } fmt.Println("Decoded number as text:", string(db[dx:]))
}</lang> Output:
Plain text: Rosetta Code Plain text as a number: 25512506514985639724585018469 Encoded: 916709442744356653386978770799029131264344 Decoded: 25512506514985639724585018469 Decoded number as text: Rosetta Code
Icon and Unicon
Please read talk pages.
<lang Icon>procedure main() # rsa demonstration
n := 9516311845790656153499716760847001433441357 e := 65537 d := 5617843187844953170308463622230283376298685 b := 2^integer(log(n,2)) # for blocking write("RSA Demo using\n n = ",n,"\n e = ",e,"\n d = ",d,"\n b = ",b)
every m := !["Rosetta Code", "Hello Word!", "This message is too long.", repl("x",*decode(n+1))] do { write("\nMessage = ",image(m)) write( "Encoded = ",m := encode(m)) if m := rsa(m,e,n) then { # unblocked write( "Encrypt = ",m) write( "Decrypt = ",m := rsa(m,d,n)) } else { # blocked every put(C := [], rsa(!block(m,b),e,n)) writes("Encrypt = ") ; every writes(!C," ") ; write() every put(P := [], rsa(!C,d,n)) writes("Decrypt = ") ; every writes(!P," ") ; write() write("Unblocked = ",m := unblock(P,b)) } write( "Decoded = ",image(decode(m))) }
end
procedure mod_power(base, exponent, modulus) # fast modular exponentation
result := 1 while exponent > 0 do { if exponent % 2 = 1 then result := (result * base) % modulus exponent /:= 2 base := base ^ 2 % modulus } return result
end
procedure rsa(text,e,n) # return rsa encryption of numerically encoded message; fail if text < n return mod_power(text,e,text < n) end
procedure encode(text) # numerically encode ascii text as int
every (message := 0) := ord(!text) + 256 * message return message
end
procedure decode(message) # numerically decode int to ascii text
text := "" while text ||:= char((0 < message) % 256) do message /:= 256 return reverse(text)
end
procedure block(m,b) # break lg int into blocks of size b
M := [] while push(M, x := (0 < m) % b) do m /:= b return M
end
procedure unblock(M,b) # reassemble blocks of size b into lg int
every (m := 0) := !M + b * m return m
end</lang>
Output:
RSA Demo using n = 9516311845790656153499716760847001433441357 e = 65537 d = 5617843187844953170308463622230283376298685 b = 5575186299632655785383929568162090376495104 Message = "Rosetta Code" Encoded = 25512506514985639724585018469 Encrypt = 916709442744356653386978770799029131264344 Decrypt = 25512506514985639724585018469 Decoded = "Rosetta Code" Message = "Hello Word!" Encoded = 87521618088882533792113697 Encrypt = 1798900477268307339588642263628429901019383 Decrypt = 87521618088882533792113697 Decoded = "Hello Word!" Message = "This message is too long." Encoded = 529836718428469753460978059376661024804668788418205881100078 Encrypt = 3376966937987363040878203966915676619521252 7002174816151673360605669161609885530980579 Decrypt = 95034800624219541 4481988526688939374478063610382714873472814 Unblocked = 529836718428469753460978059376661024804668788418205881100078 Decoded = "This message is too long." Message = "xxxxxxxxxxxxxxxxxx" Encoded = 10494468328720293243075632128305111296931960 Encrypt = 1 829820657892505002815717051746917810425013 Decrypt = 1 4919282029087637457691702560143020920436856 Unblocked = 10494468328720293243075632128305111296931960 Decoded = "xxxxxxxxxxxxxxxxxx"
J
Note, for an implementation with blocking (and a much smaller key) see [1]
<lang j> N=: 9516311845790656153499716760847001433441357x
E=: 65537x D=: 5617843187844953170308463622230283376298685x ] text=: 'Rosetta Code'
Rosetta Code
] num=: 256x #. a.i.text
25512506514985639724585018469
num >: N NB. check if blocking is necessary (0 means no)
0
] enc=: N&|@^&E num
916709442744356653386978770799029131264344
] dec=: N&|@^&D enc
25512506514985639724585018469
] final=: a. {~ 256x #.inv dec
Rosetta Code</lang>
Note: as indicated at http://www.jsoftware.com/help/dictionary/special.htm, N&|@^
does not bother with creating the exponential intermediate result.
PicoLisp
PicoLisp comes with an RSA library: <lang PicoLisp>### This is a copy of "lib/rsa.l" ###
- Generate long random number
(de longRand (N)
(use (R D) (while (=0 (setq R (abs (rand))))) (until (> R N) (unless (=0 (setq D (abs (rand)))) (setq R (* R D)) ) ) (% R N) ) )
- X power Y modulus N
(de **Mod (X Y N)
(let M 1 (loop (when (bit? 1 Y) (setq M (% (* M X) N)) ) (T (=0 (setq Y (>> 1 Y))) M ) (setq X (% (* X X) N)) ) ) )
- Probabilistic prime check
(de prime? (N)
(and (> N 1) (bit? 1 N) (let (Q (dec N) K 0) (until (bit? 1 Q) (setq Q (>> 1 Q) K (inc K) ) ) (do 50 (NIL (_prim? N Q K)) T ) ) ) )
- (Knuth Vol.2, p.379)
(de _prim? (N Q K)
(use (X J Y) (while (> 2 (setq X (longRand N)))) (setq J 0 Y (**Mod X Q N) ) (loop (T (or (and (=0 J) (= 1 Y)) (= Y (dec N)) ) T ) (T (or (and (> J 0) (= 1 Y)) (<= K (inc 'J)) ) NIL ) (setq Y (% (* Y Y) N)) ) ) )
- Find a prime number with `Len' digits
(de prime (Len)
(let P (longRand (** 10 (*/ Len 2 3))) (unless (bit? 1 P) (inc 'P) ) (until (prime? P) # P: Prime number of size 2/3 Len (inc 'P 2) ) # R: Random number of size 1/3 Len (let (R (longRand (** 10 (/ Len 3))) K (+ R (% (- P R) 3))) (when (bit? 1 K) (inc 'K 3) ) (until (prime? (setq R (inc (* K P)))) (inc 'K 6) ) R ) ) )
- Generate RSA key
(de rsaKey (N) #> (Encrypt . Decrypt)
(let (P (prime (*/ N 5 10)) Q (prime (*/ N 6 10))) (cons (* P Q) (/ (inc (* 2 (dec P) (dec Q))) 3 ) ) ) )
- Encrypt a list of characters
(de encrypt (Key Lst)
(let Siz (>> 1 (size Key)) (make (while Lst (let N (char (pop 'Lst)) (while (> Siz (size N)) (setq N (>> -16 N)) (inc 'N (char (pop 'Lst))) ) (link (**Mod N 3 Key)) ) ) ) ) )
- Decrypt a list of numbers
(de decrypt (Keys Lst)
(mapcan '((N) (let Res NIL (setq N (**Mod N (cdr Keys) (car Keys))) (until (=0 N) (push 'Res (char (& `(dec (** 2 16)) N))) (setq N (>> 16 N)) ) Res ) ) Lst ) )
- End of "lib/rsa.l" ###
- Generate 100-digit keys (private . public)
- (setq Keys (rsaKey 100))
-> (14394597526321726957429995133376978449624406217727317004742182671030....
- Encrypt
- (setq CryptText
(encrypt (car Keys) (chop "The quick brown fox jumped over the lazy dog's back") ) )
-> (72521958974980041245760752728037044798830723189142175108602418861716...
- Decrypt
- (pack (decrypt Keys CryptText))
-> "The quick brown fox jumped over the lazy dog's back"</lang>
Python
This code will open up a simple Tkinter window which has space to type a message. That message can then be encrypted by pressing the button labeled "encrypt". It will then print an output of ciphertext blocks, separated by commas. To decrypt a message, simply press the decrypt button. All ciphertext data must be entered with each block separated by commas. The ciphertext always goes (and appears) in the bottom box, while plaintext goes (and appears) in the topmost box. Upon decryption, random letters may have been appended to the end of the message, this is an aspect of the code to ensure the final block of plaintext is not a single letter, for example, a, 01, encoded is 01 (which means this letter was transmitted in the open!).
Note: the key given here is a toy key, it is easily broken. <lang python>from tkinter import * import random import time
letter = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
"r","s","t","u","v","w","x","y","z",",",".","!","?",' ']
number = ["01","02","03","04","05","06","07","08","09","10","11","12","13",
"14","15","16","17","18","19","20","21","22","23","24","25","26","27", "28","29","30",'31']
n = 2537 e = 13 d = 937 def decrypt(F,d):
# performs the decryption function on an block of ciphertext if d == 0: return 1 if d == 1: return F w,r = divmod(d,2) if r == 1: return decrypt(F*F%n,w)*F%n else: return decrypt(F*F%n,w)
def correct():
# Checks to see if the numerical ciphertext block should have started with a 0 (by seeing if the 0 is missing), if it is, it then adds the 0. # example - 0102 is output as 102, which would lead the computer to think the first letter is 10, not 01. This ensures this does not happen. for i in range(len(D)): if len(str(P[i]))%2 !=0: y = str(0)+str(P[i]) P.remove(str(P[i])) P.insert(i,y)
def cipher(b,e):
# Performs the Encryption function on a block of ciphertext if e == 0: return 1 if e == 1: return b w,r = divmod(e,2) if r == 1: return cipher(b*b%n,w)*b%n else: return cipher(b*b%n,w)
def group(j,h,z):
# Places the plaintext numbers into blocks for encryption for i in range(int(j)): y = 0 for n in range(h): y += int(numP[(h*i)+n])*(10**(z-2*n)) X.append(int(y))
class App:
# Creates a Tkineter window, for ease of operation def __init__(self, master):
frame = Frame(master) frame.grid()
#create a button with the quit command, and tell it where to go quitbutton = Button(frame, text = "quit", fg ="red", command = root.quit, width = 10) quitbutton.grid(row = 0, column =3)
#create an entry box, tell it where it goes, and how large it is entry = Entry(frame, width = 100) entry.grid(row = 0, column = 0)
#set initial content of the entry box self.contents = StringVar() self.contents.set("Type message here") entry["textvariable"] = self.contents
# Create a button which initializes the decryption of ciphertext decrypt = Button(frame,text = "Decrypt", fg = "blue", command = self.Decrypt) decrypt.grid(row = 2, column = 1)
#create a label to display the number of ciphertext blocks in an encoded message label = Label(frame, text = "# of blocks") label.grid(row = 1, column = 1)
#creates a button which initializes the encryption of plaintext encrypt = Button(frame, text="Encrypt", fg = "blue", command = self.Encrypt) encrypt.grid(row =0, column =1)
#create an entry box for the value of "n" nbox = Entry(frame, width = 100) nbox.grid(row = 3, column = 0)
self.n = StringVar() self.n.set(n) nbox["textvar"] = self.n nbox.bind('<Key-Return>', self.set_n) #key binding, when you press "return", the value of "n" is changed to the value now in the box
nlabel = Label(frame, text = "the value of 'n'") nlabel.grid(row = 3, column = 1)
#create an entry box for the value of "e" ebox = Entry(frame, width = 100) ebox.grid(row = 4, column = 0)
self.e = StringVar() self.e.set(e) ebox["textvar"] = self.e ebox.bind('<Key-Return>', self.set_e)
elabel = Label(frame, text = "the value of 'e'") elabel.grid(row = 4, column = 1)
#create an entry box for the value of "d" dbox = Entry(frame, width = 100) dbox.grid(row =5, column = 0)
self.d = StringVar() self.d.set(d) dbox["textvar"] = self.d dbox.bind('<Key-Return>', self.set_d)
dlabel = Label(frame, text = "the value of 'd'") dlabel.grid(row = 5, column =1)
blocks = Label(frame, width = 100) blocks.grid(row = 1, column =0)
self.block = StringVar() self.block.set("number of blocks") blocks["textvar"] = self.block output = Entry(frame, width = 100) output.grid(row = 2, column = 0)
self.answer = StringVar() self.answer.set("Ciphertext") output["textvar"] = self.answer
# The commands of all the buttons are defined below def set_n(self,event): global n n = int(self.n.get()) print("n set to", n)
def set_e(self, event): global e e = int(self.e.get()) print("e set to",e)
def set_d(self,event): global d d = int(self.d.get()) print("d set to", d) def Decrypt(self): #decrypts an encoded message global m,P,D,x,h,p,Text,y,w,PText P = [] D = str(self.answer.get()) #Pulls the ciphertext out of the ciphertext box D = D.lstrip('[') #removes the bracket "[" from the left side of the string D = D.rstrip(']') D = D.split(',') #splits the string into a list of strings, separating at each comma. for i in range(len(D)): #decrypts each block in the list of strings "D" x = decrypt(int(D[i]),d) P.append(str(x)) correct() #ensures each block is not missing a 0 at the start h = len(P[0]) p = [] for i in range(len(D)): #further separates the list P into individual characters, i.e. "0104" becomes "01,04" for n in range(int(h/2)): p.append(str(P[i][(2*n):((2*n)+2)])) # grabs every 2 character group from the larger block. It gets characters between 2*n, and (2*n)+2, i.e. characters 0,1 then 2,3 etc... Text = [] for i in range(len(p)): # converts each block back to text characters for j in range(len(letter)): if str(p[i]) == number[j]: Text.append(letter[j]) PText = str() for i in range(len(Text)): #places all text characters in one string PText = PText + str(Text[i]) self.contents.set(str(PText)) #places the decrypted plaintext in the plaintext box
def Encrypt(self): #encrypts a plaintext message using the current key global plaintext,numP,q,j,z,X,C plaintext = self.contents.get() #pulls the plaintext out of the entry box for use plaintext = plaintext.lower() #places all plaintext in lower case numP = [] for i in range(len(plaintext)): # converts letters and symbols to their numerical values for j in range(len(letter)): if plaintext[i] == letter[j]: numP.append(number[j]) h = (len(str(n))//2)-1 # This sets the block length for the code in question, based on the value of "n" q = len(numP)%h for i in range(h-q): numP.append(number[random.randint(0,25)]) # Ensures the final block of plaintext is filled with letters, and is not a single orphaned letter. j = len(numP) / h X = [] z = 0 for m in range(h-1): z+=2 group(j,h,z) # This sets the numerical plaintext into blocks of appropriate size, and places them in the list "X" k = len(X) C = [] for i in range(k): # performs the cipher function for each block in the list of plaintext blocks b = X[i] r = cipher(b,e) C.append(r) self.answer.set(C) self.block.set(len(C)) #places the ciphertext into the ciphertext box
root = Tk()
app = App(root)
root.mainloop() root.destroy()</lang>
Alternatively, a version without the tkinter window, which uses the same components as the program above, only without the Tkinter interface.
<lang python>import random import time
def decrypt(F,d):
if d == 0: return 1 if d == 1: return F w,r = divmod(d,2) if r == 1: return decrypt(F*F%n,w)*F%n else: return decrypt(F*F%n,w)
def correct():
for i in range(len(C)): if len(str(P[i]))%2 !=0: y = str(0)+str(P[i]) P.remove(str(P[i])) P.insert(i,y)
def cipher(b,e):
if e == 0: return 1 if e == 1: return b w,r = divmod(e,2) if r == 1: return cipher(b*b%n,w)*b%n else: return cipher(b*b%n,w)
def group(j,h,z):
for i in range(int(j)): y = 0 for n in range(h): y += int(numP[(h*i)+n])*(10**(z-2*n)) X.append(int(y))
def gcd(a, b):
while b != 0: (a, b) = (b, a%b) return a
letter = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
"r","s","t","u","v","w","x","y","z",",",".","!","?"," "]
number = ["01","02","03","04","05","06","07","08","09","10","11","12","13",
"14","15","16","17","18","19","20","21","22","23","24","25","26","27", "28","29","30","31"]
print( '\n' )
def Decrypt():
#decrypts an encoded message global m,P,C,x,h,p,Text,y,w P = [] C = str(input("Enter ciphertext blocks:")) C = C.lstrip('[') C = C.rstrip(']') C = C.split(',') for i in range(len(C)): x = decrypt(int(C[i]),d) P.append(str(x)) correct() #print(P) h = len(P[0]) p = [] for i in range(len(C)): for n in range(int(h/2)): p.append(str(P[i][(2*n):((2*n)+2)])) Text = [] for i in range(len(p)): for j in range(len(letter)): if str(p[i]) == number[j]: Text.append(letter[j]) PText = str() for i in range(len(Text)): PText = PText + str(Text[i]) print("Plaintext is:", PText)
def Encrypt():
#encrypts a plaintext message using the current key global plaintext,numP,q,j,z,X,C plaintext =(input("Enter Plaintext :")) plaintext = plaintext.lower() numP = [] for i in range(len(plaintext)): for j in range(len(letter)): if plaintext[i] == letter[j]: numP.append(number[j]) h = (len(str(n))//2)-1 q = len(numP)%h for i in range(h-q): numP.append(number[random.randint(0,25)]) j = len(numP) / h #print(numP) X = [] z = 0 for m in range(h-1): z+=2 group(j,h,z) k = len(X) C = [] for i in range(k): b = X[i] r = cipher(b,e) C.append(r) print("Ciphertext:",C) print("Number of Ciphertext blocks:",len(C))
def setup():
global n,e,d while True: try: n = int(input(" Enter a value for n :")) if n > 2: break except ValueError: print('please enter a number') while 1!=2 : try: e = int(input(" Enter a value for e :")) if e >= 2: break except ValueError: print('please enter a number') while True: try: d = int(input(" Enter a value for d. If d unknown, enter 0 :")) if d >= 0: break except ValueError: print('please enter a number')
- setup()
n = 2537 e = 13 d = 937
print("To redefine n,e, or d, type 'n','e',... etc.") print("To encrypt a message with the current key, type 'Encrypt'") print("To decrypt a message with the current key, type 'Decrypt'") print("Type quit to exit") print( '\n' ) print( '\n' )
mm = str() while mm != 'quit':
mm = input("Enter Command...") if mm.lower() == 'encrypt': Encrypt() elif mm.lower() == 'decrypt': Decrypt() elif mm.lower() == 'n': try: print('current n = ',n) n = int(input(" Enter a value for n :")) except ValueError: print('That is not a valid entry') elif mm.lower() == 'help': print("To redefine n,e, or d, type 'n','e',... etc.") print("To encrypt a message with the current key, type 'Encrypt'") print("To decrypt a message with the current key, type 'Decrypt'") print("Type quit to exit") print( '\n' ) print( '\n' ) elif mm.lower() == 'e': try: print('current e = ',e) e = int(input(" Enter a value for e :")) except ValueError: print('That is not a valid entry') elif mm.lower() == 'd': try: print('current d = ',d) d = int(input(" Enter a value for d :")) except ValueError: print('That is not a valid entry') else: if mm != 'quit': ii= random.randint(0,6) statements = ["I sorry, Dave. I'm afraid i can't do that","I'm begging you....read the directions","Nah ahh ahh, didnt say the magic word","This input is....UNACCEPTABLE!!","Seriously....was that even a word???","Please follow the directions","Just type 'help' if you are really that lost"] print(statements[ii])</lang>
Example use :
Any entered commands are not case sensitive, nor is the plaintext input. Commands must be spelled correctly. Ciphertext blocks are input all at once, but must be separated by commas. When decrypted, there may be random letters attached to the end of the message. The program does this in order to fill blocks completely, and not have orphaned characters. <lang python>>>> To redefine n,e, or d, type 'n','e',... etc. To encrypt a message with the current key, type 'Encrypt' To decrypt a message with the current key, type 'Decrypt' Type quit to exit
Enter Command...ENCRYPT
Enter Plaintext :drink MORE Ovaltine
Ciphertext: [140, 2222, 1864, 1616, 821, 384, 2038, 2116, 2222, 205, 384, 2116, 45, 1, 2497, 793, 1864, 1616, 205, 41]
Number of Ciphertext blocks: 20
Enter Command...decrypt
Enter ciphertext blocks:[140, 2222, 1864, 1616, 821, 384, 2038, 2116, 2222, 205, 384, 2116, 45, 1, 2497, 793, 1864, 1616, 205, 41]
Plaintext is: drink more ovaltineu
Enter Command...quit
>>> </lang>
Tcl
This code is careful to avoid the assumption that the input string is in a single-byte encoding, instead forcing the encryption to be performed on the UTF-8 form of the text. <lang tcl>package require Tcl 8.5
- This is a straight-forward square-and-multiply implementation that relies on
- Tcl 8.5's bignum support (based on LibTomMath) for speed.
proc modexp {b expAndMod} {
lassign $expAndMod -> e n if {$b >= $n} {puts stderr "WARNING: modulus too small"} for {set r 1} {$e != 0} {set e [expr {$e >> 1}]} {
if {$e & 1} { set r [expr {($r * $b) % $n}] } set b [expr {($b ** 2) % $n}]
} return $r
}
- Assumes that messages are shorter than the modulus
proc rsa_encrypt {message publicKey} {
if {[lindex $publicKey 0] ne "publicKey"} {error "key handling"} set toEnc 0 foreach char [split [encoding convertto utf-8 $message] ""] {
set toEnc [expr {$toEnc * 256 + [scan $char "%c"]}]
} return [modexp $toEnc $publicKey]
}
proc rsa_decrypt {encrypted privateKey} {
if {[lindex $privateKey 0] ne "privateKey"} {error "key handling"} set toDec [modexp $encrypted $privateKey] for {set message ""} {$toDec > 0} {set toDec [expr {$toDec >> 8}]} {
append message [format "%c" [expr {$toDec & 255}]]
} return [encoding convertfrom utf-8 [string reverse $message]]
}
- Assemble packaged public and private keys
set e 65537 set n 9516311845790656153499716760847001433441357 set d 5617843187844953170308463622230283376298685 set publicKey [list "publicKey" $e $n] set privateKey [list "privateKey" $d $n]
- Test on some input strings
foreach input {"Rosetta Code" "UTF-8 \u263a test"} {
set enc [rsa_encrypt $input $publicKey] set dec [rsa_decrypt $enc $privateKey] puts "$input -> $enc -> $dec"
}</lang> Output:
Rosetta Code -> 916709442744356653386978770799029131264344 -> Rosetta Code UTF-8 ☺ test -> 3905697186829810541171404594906488782823186 -> UTF-8 ☺ test