RSA code

From Rosetta Code
Revision as of 22:19, 20 May 2014 by rosettacode>Cheryllium (Added Common Lisp)
Task
RSA code
You are encouraged to solve this task according to the task description, using any language you may know.

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.
Warning
Rosetta Code is not a place you should rely on for examples of code in critical roles, including security.
Cryptographic routines should be validated before being used.
For a discussion of limitations and please refer to Talk:RSA_code#Difference_from_practical_cryptographical_version.

Common Lisp

<lang Common Lisp>(defparameter *n* 9516311845790656153499716760847001433441357) (defparameter *e* 65537) (defparameter *d* 5617843187844953170308463622230283376298685)

The string is converted into characters by taking the ASCII value of
each character and subtracting 22, making SPACE=10 and so on. Only
ASCII values 32 - 121 can be represented. Who needs lowercase z anyway.
The next two functions convert the string to an integer and back.
They aren't really that important. You can really do whatever you want.
magic

(defun encode-string (message)

 (parse-integer (reduce #'(lambda (x y) (concatenate 'string x y))
    (loop for c across message collect (write-to-string (- (char-code c) 22))))))
sorcery

(defun decode-string (message) (coerce (loop for (a b) on

 (loop for char across (write-to-string message) collect char) 
   by #'cddr collect (code-char (+ (parse-integer (coerce (list a b) 'string)) 22))) 'string))
fast modular exponentiation
runs in O(log exponent)
c is initially 1 and contains the result by the end

(defun mod-exp (base exponent modulus c)

 (if (= exponent 0) c 
   (mod-exp (mod (* base base) modulus) (ash exponent -1) modulus 

(if (= (mod exponent 2) 1) (mod (* c base) modulus) c))))

(defun encode-rsa (message)

 (mod-exp (encode-string message) *e* *n* 1))

(defun decode-rsa (message)

 (decode-string (mod-exp message *d* *n* 1)))

</lang> Interpreter output (the star * represents the interpreter prompt):

* (load "rsa.lisp")

T
* (encode-rsa "Rosetta Code")

867933120839212932734170529033006473069860
* (decode-rsa 867933120839212932734170529033006473069860) 

"Rosetta Code"

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.


Perl 6

No blocking here. Algorithm doesn't really work if either red or black text begins with 'A'. <lang perl6>constant $n = 9516311845790656153499716760847001433441357; constant $e = 65537; constant $d = 5617843187844953170308463622230283376298685;

my $secret-message = "ROSETTA CODE";

package Message {

   my @alphabet = 'A' .. 'Z', ' ';
   my $rad = +@alphabet;
   my %code = @alphabet Z=> 0 .. *;
   subset Text of Str where /^^ @alphabet+ $$/;
   our sub encode(Text $t) {

[+] %code{$t.flip.comb} Z* (1, $rad, $rad*$rad ... *);

   }
   our sub decode(Int $n is copy) {

@alphabet[ gather loop { take $n % $rad; last if $n < $rad; $n div= $rad; } ].join.flip;

   }

}

use Test; plan 1;

say "Secret message is $secret-message"; say "Secret message in integer form is $_" given

   my $numeric-message = Message::encode $secret-message;

say "After exponentiation with public exponent we get: $_" given

   my $numeric-cipher = expmod $numeric-message, $e, $n;

say "This turns into the string $_" given

   my $text-cipher = Message::decode $numeric-cipher;

say "If we re-encode it in integer form we get $_" given

   my $numeric-cipher2 = Message::encode $text-cipher;

say "After exponentiation with SECRET exponent we get: $_" given

   my $numeric-message2 = expmod $numeric-cipher2, $d, $n;

say "This turns into the string $_" given

   my $secret-message2 = Message::decode $numeric-message2;

is $secret-message, $secret-message2, "the message has been correctly decrypted";</lang>

Output:
1..1
Secret message is ROSETTA CODE
Secret message in integer form is 97525102075211938
After exponentiation with public exponent we get: 8326171774113983822045243488956318758396426
This turns into the string ZULYDCEZOWTFXFRRNLIMGNUPHVCJSX
If we re-encode it in integer form we get 8326171774113983822045243488956318758396426
After exponentiation with SECRET exponent we get: 97525102075211938
This turns into the string ROSETTA CODE
ok 1 - the message has been correctly decrypted

PicoLisp

PicoLisp comes with an RSA library: <lang PicoLisp>### This is a copy of "lib/rsa.l" ###

  1. 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) ) )
  1. 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)) ) ) )
  1. 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 ) ) ) )
  1. (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)) ) ) )
  1. 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 ) ) )
  1. 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 ) ) ) )
  1. 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)) ) ) ) ) )
  1. 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 ) )
      1. End of "lib/rsa.l" ###
  1. Generate 100-digit keys (private . public)
(setq Keys (rsaKey 100))

-> (14394597526321726957429995133376978449624406217727317004742182671030....

  1. Encrypt
(setq CryptText
  (encrypt (car Keys)
     (chop "The quick brown fox jumped over the lazy dog's back") ) )

-> (72521958974980041245760752728037044798830723189142175108602418861716...

  1. Decrypt
(pack (decrypt Keys CryptText))

-> "The quick brown fox jumped over the lazy dog's back"</lang>

Python

This example may be incorrect due to a recent change in the task requirements or a lack of testing. Please verify it and remove this message. If the example does not match the requirements or does not work, replace this message with Template:incorrect or fix the code yourself.

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')
           
  1. 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>

Racket

This implementation does key generation and demonstrates digital signature as a freebie.

Thanks again to the wonderful math/number-theory package (distributed as standard).

Cutting messages into blocks has not been done.

<lang racket>#lang racket (require math/number-theory) (define-logger rsa) (current-logger rsa-logger)

-| STRING TO NUMBER MAPPING |----------------------------------------------------------------------

(define (bytes->number B) ; We'll need our data in numerical form ..

 (for/fold ((rv 0)) ((b B)) (+ b (* rv 256))))

(define (number->bytes N) ; .. and back again

 (define (inr n b) (if (zero? n) b (inr (quotient n 256) (bytes-append (bytes (modulo n 256)) b))))
 (inr N (bytes)))
-| RSA PUBLIC / PRIVATE FUNCTIONS |----------------------------------------------------------------
The basic definitions... pretty well lifted from the text book!

(define ((C e n) p)

 ;; Just do the arithmetic to demonstrate RSA...
 ;; breaking large messages into blocks is something for another day.
 (unless (< p n) (raise-argument-error 'C (format "(and/c integer? (</c ~a))" n) p))
 (modular-expt p e n))

(define ((P d n) c)

 (modular-expt c d n))
-| RSA KEY GENERATION |----------------------------------------------------------------------------
Key generation
Full description of the steps can be found on Wikipedia

(define (RSA-keyset function-base-name)

 (log-info "RSA-keyset: ~s" function-base-name)
 (define max-k 4294967087)
 ;; I'm guessing this RNG is about as cryptographically strong as replacing spaces with tabs.
 (define (big-random n-rolls)
   (for/fold ((rv 1)) ((roll (in-range n-rolls 0 -1))) (+ (* rv (add1 max-k)) 1 (random max-k))))
 (define (big-random-prime)
   (define start-number (big-random (/ 1024 32)))
   (log-debug "got large (possibly non-prime) number, finding next prime")
   (next-prime (match start-number ((? odd? o) o) ((app add1 e) e))))
 
 ;; [1] Choose two distinct prime numbers p and q.
 (log-debug "generating p")
 (define p (big-random-prime))
 (log-debug "p generated")
 (log-debug "generating q")
 (define q (big-random-prime))
 (log-debug "q generated")
 (log-info "primes generated")
 
 ;; [2] Compute n = pq.
 (define n (* p q))
 
 ;; [3] Compute φ(n) = φ(p)φ(q) = (p − 1)(q − 1) = n - (p + q -1),
 ;;                    where φ is Euler's totient function.
 (define φ (- n (+ p q -1)))
 
 ;; [4] Choose an integer e such that 1 < e < φ(n) and gcd(e, φ(n)) = 1; i.e., e and φ(n) are
 ;;     coprime. ... most commonly 2^16 + 1 = 65,537 ...
 (define e (+ (expt 2 16) 1))
 
 ;; [5] Determine d as d ≡ e−1 (mod φ(n)); i.e., d is the multiplicative inverse of e (modulo φ(n)).
 (log-debug "generating d")
 (define d (modular-inverse e φ))
 (log-info "d generated")
 (values n e d))
-| GIVE A USABLE SET OF PRIVATE STUFF TO A USER |--------------------------------------------------
six values
the public (encrypt) function (numeric)
the private (decrypt) function (numeric)
the public (encrypt) function (bytes)
the private (decrypt) function (bytes)
private (list n e d)
public (list n e)

(define (RSA-key-pack #:function-base-name function-base-name)

 (define (rnm-fn f s) (procedure-rename f (string->symbol (format "~a-~a" function-base-name s))))
 (define-values (n e d) (RSA-keyset function-base-name))
 (define my-C (rnm-fn (C e n) "C"))
 (define my-P (rnm-fn (P d n) "P"))
 (define my-encrypt (rnm-fn (compose number->bytes my-C bytes->number) "encrypt"))
 (define my-decrypt (rnm-fn (compose number->bytes my-P bytes->number) "decrypt"))
 (values my-C my-P my-encrypt my-decrypt (list n e d) (list n e)))
-| HEREON IS JUST A LOAD OF CHATTY DEMOS |---------------------------------------------------------

(define (narrated-encrypt-bytes C who plain-text)

 (define plain-n (bytes->number plain-text))
 (define cypher-n (C plain-n))
 (define cypher-text (number->bytes cypher-n))
 (printf #<<EOS

~a wants to send plain text: ~s

 as number: ~s
 cyphered number: ~s

sent by ~a over the public interwebs: ~s ...


EOS

         who plain-text plain-n cypher-n who cypher-text)
 cypher-text)

(define (narrated-decrypt-bytes P who cypher-text)

 (define cypher-n (bytes->number cypher-text))
 (define plain-n (P cypher-n))
 (define plain-text (number->bytes plain-n))
 (printf #<<EOS

... ~s

 received by ~a
 as number: ~s
 decyphered (with P) number: ~s

decyphered text: ~s


EOS

         cypher-text who cypher-n plain-n plain-text)
 plain-text)
ENCRYPT AND DECRYPT A MESSAGE WITH THE e.g. KEYS

(define-values (given-n given-e given-d)

 (values 9516311845790656153499716760847001433441357
         65537
         5617843187844953170308463622230283376298685))
Get the keys specific RSA functions

(for ((message-text (list #"hello world" #"TOP SECRET!")))

 (define Bobs-public-function (C given-e given-n))
 (define Bobs-private-function (P given-d given-n))
 (define cypher-text (narrated-encrypt-bytes Bobs-public-function "Alice" message-text))
 (define plain-text (narrated-decrypt-bytes Bobs-private-function "Bob" cypher-text))
 plain-text)
Demonstrate with larger keys.
(And include a free recap on digital signatures, too)

(define-values (A-pub-C A-pvt-P A-pub-encrypt A-pvt-decrypt A-pvt-keys A-pub-keys)

 (RSA-key-pack #:function-base-name 'Alice))

(define-values (B-pub-C B-pvt-P B-pub-encrypt B-pvt-decrypt B-pvt-keys B-pub-keys)

 (RSA-key-pack #:function-base-name 'Bob))
Since p and q are random, it is possible that message' = "message modulo {A,B}-key-n" will be too
big for "message' modulo {B,A}-key-n", if that happens then I run the program again until it
works. Strictly, we need blocking of the signed message -- which is not yet implemented.

(let* ((plain-A-to-B #"Dear Bob, meet you in Lymm at 1200, Alice")

      (signed-A-to-B         (A-pvt-decrypt plain-A-to-B))
      (unsigned-A-to-B       (A-pub-encrypt signed-A-to-B))
      (crypt-signed-A-to-B   (B-pub-encrypt signed-A-to-B))
      (decrypt-signed-A-to-B (B-pvt-decrypt crypt-signed-A-to-B))
      (decrypt-verified-B    (A-pub-encrypt decrypt-signed-A-to-B)))
 (printf
  #<<EOS

Alice wants to send ~s to Bob. She "encrypts" with her private "decryption" key. (A-prv msg) -> ~s Only she could have done this (only she has the her private key data) -- so this is a signature on the message. Anyone can verify the signature by "decrypting" the message with the public "encryption" key. (A-pub (A-prv msg)) -> ~s But anyone is able to do this, so there is no privacy here. Everyone knows that it can only be Alice at Lymm at noon, but this message is for Bob's eyes only. We need to encrypt this with his public key: (B-pub (A-prv msg)) -> ~s Which is what gets posted to alt.chat.secret-rendezvous Bob decrypts this to get the signed message from Alice: (B-prv (B-pub (A-prv msg))) -> ~s And verifies Alice's signature: (A-pub (B-prv (B-pub (A-prv msg)))) -> ~s Alice genuinely sent the message. And nobody else (on a.c.s-r, at least) has read it.

KEYS:

Alice's full set: ~s
Bob's full set: ~s

EOS

  plain-A-to-B signed-A-to-B unsigned-A-to-B crypt-signed-A-to-B decrypt-signed-A-to-B
  decrypt-verified-B A-pvt-keys B-pvt-keys))</lang>
Output:
Alice wants to send plain text: #"hello world"
  as number: 126207244316550804821666916
  cyphered number: 4109627268073579506944196826730512948879423
sent by Alice over the public interwebs:
#"/-\e\355\225\327\244\222<R@\20\4\233\275\333`?"
...

...
#"/-\e\355\225\327\244\222<R@\20\4\233\275\333`?"
  received by Bob
  as number: 4109627268073579506944196826730512948879423
  decyphered (with P) number: 126207244316550804821666916
decyphered text:
#"hello world"

Alice wants to send plain text: #"TOP SECRET!"
  as number: 101924313868583037137409057
  cyphered number: 5346093164296793050289700489360581430628365
sent by Alice over the public interwebs:
#"=^\301{\17p\201AE\341D\357 \237IPP\r"
...

...
#"=^\301{\17p\201AE\341D\357 \237IPP\r"
  received by Bob
  as number: 5346093164296793050289700489360581430628365
  decyphered (with P) number: 101924313868583037137409057
decyphered text:
#"TOP SECRET!"

Alice wants to send #"Dear Bob, meet you in Lymm at 1200, Alice" to Bob.
She "encrypts" with her private "decryption" key.
(A-prv msg) -> #"B}\4<G\373-\217\350;\0214\226\233\236\333\215\226\225=\236\350\277X\241*J\356\302\250\350fO\5\375u\367\365\315\270\312\334\204U\332\224\322\357u=\262\326\274e\31\301\321\210:i\361\361g\361\16\5a\304X\306\313\350(^\374 \353\350t\2662\305\346a\300\244b\337JI\343\335\21j\202\236\242\335<rA\a\233\375\23\t\32(d`\237i\267\336\270\340L\26\f\260\346&\t\301\326\331k@\253\242\241VKw\365 \204U\270*\r,\334h=\257\230\320V\357\304\242\4B\240\356\200\204\252\35\20c\220LJ}\275x!\25\23\262\325{\246\304?\36\272\343\17\230\2449Q[y\334(m1\252N<\253?^#\236p\311\3006\f\245M*<\273H\333\225\256\317\322\363\273\335\303\243\354\a\253\342\312\302\372vTQ\247\r\210\343\264\323*E\364\2\ba\305Z79\273M\327\310F\301,\235\32\323"
Only she could have done this (only she has the her private key data) -- so this is a signature on the
message. Anyone can verify the signature by "decrypting" the message with the public "encryption" key.
(A-pub (A-prv msg)) -> #"Dear Bob, meet you in Lymm at 1200, Alice"
But anyone is able to do this, so there is no privacy here.
Everyone knows that it can only be Alice at Lymm at noon, but this message is for Bob's eyes only.
We need to encrypt this with his public key:
(B-pub (A-prv msg)) -> #"\eXq\4/\207\250hs\244<ym\3716\210\357'0\351E\202D\360\177\361\325\24\310+s\340j1\36\0\213\353\254\314*\212a;\300\210\\\347\371z`\226\230 \230A\337d\31\262nwp\6m\312\320D@h\232d]{sN\312xAW\216'c\27V\5\270\267>@\305\312\210\262|tGU?\266\325\250\227\270X\235\6C\307\323D\301q{\266S\351,i\211,~X\341\225z4\320F\353\361I\313M\270&d\267m\207 \2736s_\272\307\275\31T\301\247\317@\16D\263X\"\340\262\204\277g\30\337\311o\205\236\34\370)\323\275\5\1\301>\226Q,\255\213\\\2\307\215c\342\323\16\226a\3U\254\214\275\274\214\325\f\226\347\325\225\354~\32z)\340re5I\321\254\34'T\n\220p\316#\1\347\6;*\347\303\351\342\221\244\eey\31\275y\271\2605y\344\261\202B\321E\335\212"
Which is what gets posted to alt.chat.secret-rendezvous
Bob decrypts this to get the signed message from Alice:
(B-prv (B-pub (A-prv msg))) -> #"B}\4<G\373-\217\350;\0214\226\233\236\333\215\226\225=\236\350\277X\241*J\356\302\250\350fO\5\375u\367\365\315\270\312\334\204U\332\224\322\357u=\262\326\274e\31\301\321\210:i\361\361g\361\16\5a\304X\306\313\350(^\374 \353\350t\2662\305\346a\300\244b\337JI\343\335\21j\202\236\242\335<rA\a\233\375\23\t\32(d`\237i\267\336\270\340L\26\f\260\346&\t\301\326\331k@\253\242\241VKw\365 \204U\270*\r,\334h=\257\230\320V\357\304\242\4B\240\356\200\204\252\35\20c\220LJ}\275x!\25\23\262\325{\246\304?\36\272\343\17\230\2449Q[y\334(m1\252N<\253?^#\236p\311\3006\f\245M*<\273H\333\225\256\317\322\363\273\335\303\243\354\a\253\342\312\302\372vTQ\247\r\210\343\264\323*E\364\2\ba\305Z79\273M\327\310F\301,\235\32\323"
And verifies Alice's signature:
(A-pub (B-prv (B-pub (A-prv msg)))) -> #"Dear Bob, meet you in Lymm at 1200, Alice"
Alice genuinely sent the message.
And nobody else (on a.c.s-r, at least) has read it.

KEYS:
 Alice's full set: (104685401856522903402850023081275254628934665755538824520013952439504139625115997713509448983980532229245117213463882915048453185855818576471069794363975118091990355601850994380420233190180031768156385491949949615002445375960925665247160785747787333124376845288066200576472845984390877385677186819996627676676634639097055255729270941154875343472575964213139374388405182305309760553726485659960423106167598201105611690471457085541574734821426421348095213778701793437599877207476950721505461275161623234401077010666082709757697115644957960465476529769011591060857755043525709942747005873747546230596592939772042294065813 65537 2717089103223300710869400327467677924284264864888101995949520623458451584636500177162207191689440855119183734075439291369721208922299577316283774359722319847940485449116507338466741179127763462435479373816422239271238530669843516739939583694050479174276592059981393826091830737132442474221232201364332570578846365932454353567371199951546467136016124284306002875511872761969350668537869576342139916560099770887726733315363999637008436783521647128919138309876487426046116064403375745886449924998134881929018589019826525209406457786746758225025770316010686323085528641486882271019011944746635302695471929361680924424193)
 Bob's full set: (66453628687555934925745945778628604864426900808865010224295559035622434292221884343034084372211796572669001844069872620681089178469116242843088008182594366670325110214571956032739850447309116856946000976960287962443982329056724158946509616005091553738944083845350969457626620995817549009173612137879065054069514627974682257866567361928526254468372096924822844681482943840826594855542477801468633973217959659919448029041762191501507078772425126221108904380853918736930072466664169841898502073182029626689571748335731893924787111612947107622008163390629359277338946499436753837554940480109336784185532913066463754681017 65537 40065645823449313467522156271281139875308606308813084959528059327930012759030216763756439504389958618427304726562596348031980052624474573194667691034359998325290386803002604616043604539794698176121997293172282195706991070204897106862588071733664686557012032668284371518061563321600604452438728297053809260134398419618573423334221995863281726034127469856978901426632431740449516515840848181614406772903883730243825332015822633960435748349249507976445585019837204822017018926054553157019477128528700400452557614873387735038266042290443427594857847964184247833486305612600841625002197933394323058877499979801727736278613)

The burden seems to be in finding (proving) the next large prime after the big random number. However, once keys are generated, things are pretty snappy.

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

  1. This is a straight-forward square-and-multiply implementation that relies on
  2. 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

}

  1. 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]]

}

  1. 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]

  1. 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