ECE291 Computer Engineering II Lockwood, Fall 1998

Machine Problem 2: RSA Encryption

Purpose Subroutines, User I/O, Encryption algorithms 
Points 50
Due Date Wednesday 9/30/98 (30/50 points) - Checkpoint 1
Wednesday 10/7/98 (50/50 points) - Checkpoint 2

Introduction

Encryption provides a way to encode the content of a message in a way that is only useful for a specific recipient.  In a public key cryptosystem, each person has their own set of keys: a public key and a secret key.  The public key can be thought of as a lock, for which the secret key is the key.  Each person will send out their public key, with which anyone can encrypt data.  Once a message has been encrypted using the public key, it can only be decrypted using the secret key.  If an eavesdropper gets the public key, it could not be used to decrypt messages sent to the holder of the secret key.

The two keys describe a function and it's inverse.  That is for a public key, with a function P() and a secret key with a function S().  So, for a message M, P(S(M)) = M, and S(P(M)) = M.  This is in contrast to private key cryptosystems in which there is only one function which is it's own inverse.  Under private key encryption, for a private key with a function P() and a message M, P(P(M)) = M.  Since it is possible to decrypt and encrypt a message with the same key, under private key encryption, it is necessary to keep the encryption key private.

RSA encryption, which stands for RSA stands for Ron Rivest, Adi Shamir and Leonard Adelman, is one of the most common methods of encrypting data today.  Many algorithms use RSA as a basis for their encryption.  PGP for example uses RSA to encrypt an IDEA key which is sent with the IDEA-encrypted message.  Only with the correct RSA private key can you decrypt the IDEA key to decrypt the message.

In this MP, we will write an interactive assembly program that encodes and decodes text messages using RSA.

RSA Encryption

The function RSA encryption is based on encrypts a block of text (T) into a block of cypher (C) as:
 
   C =  T^e mod n

where (e, n) make up the public key.  Decryption is accomplished by the inverse operation:
 
    T = C^d mod n

where (d, n) make up the secret key.

It should be noted that for a modulus n, you can only encode a block of text (T) that is less than n, which will produce a block of encrypted text (C) which is also less than n.  For that reason, in this MP, we will be encoding 7-bit clean ASCII using a modulus > 127.  The   8 bit codes can then be decrypted back into 7-bit clean ASCII.

To determine an RSA key pair, the following steps need to be followed:

        1. Choose two prime numbers p and q.
        2. Multiply p*q to obtain n.
        3. Choose an encryption key (e) which is less than {(p-1)(q-1)} and relatively prime* to {(p-1)(q-1)}.
        4. Determine a decryption key (d) such that d*e % {(p-1)(q-1)} = 1.
            * Two numbers are relatively prime if they share no common factors. In other words, the greatest common factor of two relatively prime numbers is 1.

The public key (e, n) can now be given out freely, because it can only be used to encrypt a message.  Determining the secret key from the public key (or breaking the code) is only as difficult as factoring n into it's two prime factors. For our version of RSA encryption, we are imposing the limit that n must be 8 bits long.  However, RSA encryption typically uses keys that are 512, 768 or 1024 bits long. 

Our Implementation

Our implementation of RSA is greatly simplified.  We will force n to be exactly 8 bits long.  As noted earlier, to use RSA, you can only encode values that are smaller than your modulus.  This is sufficient to encode 7-bit clean ASCII.

Rather than generating random prime numbers for the key, the user can enter p, q and e.  Then n and d will be calculated by the computer. This implementation is limited to encoding and decoding a single 20 byte buffer.  Each byte will be encrypted and decrypted individually. The user can either enter hex values or ASCII text into the buffer to be encrypted or decrypted.

Some algorithms are described below to help you program your mp.

Exponentials in a modulus

  For very large values of e and d, it could be very difficult to exponential a large number.  In RSA 200+ bit exponents are often used, with a 500+ bit modulus.  To perform this exponential, a 100,000 bit number would be produced. Fortunately, there is a simple modular exponentiation algorithm below which computes R=yx mod n, where x is w bits long:
    Let s = 1.
    For k = w-1 to 0:
      If (bit k of x) is 1 then
        Let R = (s · y) mod n
      Else
        Let R = s
      Let s = R2 mod n
    Return (R)
(From: http://www.cryptography.com/timingattack/ )

Please note that this operation runs from the most significant bit of the exponent to the least significant bit of the exponent. This allows exponentiation to be done in a loop relative to the length of the exponent.  So, for an 200 bit exponent, only 200 iterations need to be done, not 2^200.

Calculating d*e mod n = 1

    Calculating d can be done recursively.  In the general case, for calculating d where (d*a) mod b = c can be calculated using the following:
     Let f(a, b, c) be a function such that
        f(a, b, c)*a mod b = c. 
     For simplicity, let f(a, b, c) = x0 where x0 is an integer 

     Note that the definition of A mod B = C states that 
          (x * B) + C = A 
     where A, B, and C are constants and x is some integer.

     The Original Equation. Note that c < a 
     (1)  (x0 * a) mod b = c

     By the defintion of the modulus:
     (2)  (x1 * b) + c = (x0 * a) , Where x1 is some integer.

     Subtract c from both sides
     (3)  (x1 * b)  = (x0 * a) + (-c)

     Taking both sides of the equation mod a:
     (4a) (x1 * b) mod a = ((x0 * a) + (-c)) mod a   
           Since a is a factor of (x0 * a)
     (4b) (x1 * b) mod a = (-c) mod a               
           Note that this step is the inverse of how 
           (2) was derived from (1).  

     By the property (A*B) mod C = (A*(B mod C)) mod C
     (5)  (x1 * (b mod a)) mod a = (-c) mod a

     Note that (b mod a) and ((-c) mod a) are both constants defined 
     in terms of the inputs to the function a, b, and c. So, let:
        b mod a = e
        (-c) mod a = f
     Performing this substitution gives:
     (6) (x1 * e) mod a = f

     Noting that this is exactly in the form of (1), we can solve for x1:
     (7a) x1 = f(e, a, f)
      Performing the back substitutions 
     (7b) x1 = f(b mod a, a, -c mod a)
 
     Combining (7b) with (2):
     (8a) (x0 * a) = f(b mod a, a, -c mod a) * b + c 
        and using the definition x0 = f(a,b,c)
     (8b) (f(a,b,c) * a) = f(b mod a, a, -c mod a) * b + c
 
     Dividing through by a we get:
     (9) f(a, b, c) = (b*f(b mod a, a, -c mod a) + c)/a

      Note that (b mod a) < a < b, and c < a.  
      This suggests that the recursion will shrink down to 
      some base case, or a set of base cases.  
      It is also possible that f(a, b, c) is undefined 
      if a = 0 (division by 0).  To see what it means if f(a,b,c) 
      is undefined, we will work the recursion backwards. 

        After the nth iteration
          a(n) = 0
        Therefore
          b(n-1) mod a(n-1) = 0
 
        This implies that a(n-1) is a factor of b(n-1), however
          b(n-1) = a(n-2)
        So, a(n-1) is also a factor of a(n-2)

        Also,
          a(n-1) = b(n-2) mod a(n-2)

        Now, taking both sides mod a(n-1)
          a(n-1) mod a(n-1) = (b(n-2) mod a(n-2)) mod a(n-1)

        Since A mod A = 0:
          0 = (b(n-2) mod a(n-2)) mod a(n-1)

        Using the definition of the modulus twice:
          x1 * a(n-1) = b(n-2) mod a(n-2) ,      Where x1 is an integer
          (x1 * a(n-1)) = b(n-2) - x2 * a(n-2) , Where x2 is an integer

        But, since a(n-1) is a factor of a(n-2)
          (x1 * a(n-1)) = b(n-2) - x2 * x3 * a(n-1) , Where x3 is an integer

        Letting x2 absorb x3 since both are arbitrary integers:
          (x1 * a(n-1) = b(n-2) - x2 * a(n-1)

        Adding x2 * a(n-1) and distributing the a(n-1)
          a(n-1)(x1 + x2) = b(n-2)

        Letting x1 absorb x2 since both are arbitrary integers:
          a(n-1)*x1 = b(n-2)

       Therefore, a(n-1) is a factor of b(n-2).  It was already shown
       that a(n-1) is a factor of a(n-2).   By induction, a(n-1) is a
       factor of a(k) and b(k) where k < n and a(n) = 0.  Actually, with a
       slight modification to the proof, it could be shown that a(n-1) is the
       greatest common factor of a(k) and b(k) for k < n.

Prime numbers

    Since the security of RSA depends on the size of n, finding large prime numbers is very important to the RSA algorithm. In general, one would choose a vary large random number and then determine if it is prime.  For our implementation, the user will enter a number, and the computer will determine if it is prime.  There are a number of complex algorithms for determining if large  numbers are prime, but this operation is simple for small numbers.  You will be responsible for your own algorithm.  It should be able to calculate if any number between 1 and 127 is prime.

User Interface

Sample Input and Output

Data Structures

Procedures

Preliminary Procedure

Revision History

Final Steps

  1. Print a copy of the MP2 grading sheet.
  2. Demonstrate MP2.EXE to a TA or to the instructor.
  3. Handin in your program by running:
  4.     Staple the MP2 grading sheet to the front of your MP2.ASM file and give both to the same TA that approved your demonstration.

MP2.ASM

TITLE ECE291:MP2-RSA - Your Name - Date

COMMENT % RSA Data Encryption.

          For this MP, you will write an interactive program which uses
          RSA encryption to encrypt and decrypt textual data.

          ECE291: Machine Problem 2
          Prof. John W. Lockwood
          Guest Author: Brandon Tipp
          Unversity of Illinois
          Dept. of Electrical & Computer Engineering
          Fall 1998

          Ver 1.1
        %

;====== Constants =========================================================

BEEP    EQU 7
BS      EQU 8
CR      EQU 13
LF      EQU 10

ESCKEY  EQU 27
SPACE   EQU 32

BufferMaxLength     EQU 20 ; Bytes ; Limit text messages to one line 
                                   ; when displaying message in HEX

ASCII   EQU 0
HEX     EQU 1
DECIMAL EQU 2

PUBLIC BEEP, BS, CR, LF, ESCKEY, SPACE, ASCII, HEX, DECIMAL, BufferMaxLength
;====== Externals =========================================================

; -- LIB291 Routines (Free) ---

extrn kbdine:near, kbdin:near, dspout:near   ; LIB291 Routines
extrn dspmsg:near, binasc:near, ascbin:near  ; (Always Free)

extrn mp2xit:near ; Exit program with a call to this procedure

; -- LIBMP2 Routines (Replace these with your own code) ---

extrn PrintBuffer:near     ; Print contents of Buffer
extrn ReadBuffer:near      ; Read Buffer from keyboard
extrn CodeBuffer:near      ; Encode or decode the buffer
extrn ExpModN:near         ; Calculate X^Y mod Z
extrn CheckPrime:near      ; Determine if a number is prime
extrn ReadKeys:near        ; Read and verify valid values for p, q, e, d   
extrn ReadPublicKeys:near  ; Read in d, n

;====== SECTION 3: Define stack segment ===================================
stkseg  segment stack                   ; *** STACK SEGMENT ***
        db      64 dup ('STACK   ')     ; 64*8 = 512 Bytes of Stack
stkseg  ends

;====== SECTION 4: Define code segment ====================================
cseg    segment public  'CODE'    ; *** CODE SEGMENT ***
        assume  cs:cseg, ds:cseg, ss:stkseg, es:nothing

;====== SECTION 5: Variables ==============================================

Buffer  db BufferMaxLength    dup(0)   ; Data Buffer for message

BufferLength db 0 ; Number of bytes in buffer

crlf    db CR,LF,'$' ; DOS uses carriage return + Linefeed for new line
PBuf    db 7 dup(?)

Pmsg    db 'Plese enter a prime number (p): p <= 127: ','$'
Perr1   db BEEP,'One is not prime!',CR,LF,'$'
Perr2   db BEEP,'P must be less than or equal to 127!',CR,LF,'$' 
Perr3   db BEEP,'P must be a prime number!',CR,LF,'$'
Qmsg1   db 'Plese enter a prime number (q): ','$'
Qmsg2   db ' < q <= ','$'
Qerr1   db BEEP,'Q must be greater than ','$'
Qerr2   db BEEP,'Q must be less than or equal to ','$'
Qerr3   db BEEP,'Q must be a prime number!',CR,LF,'$'
PUBmsg  db 'Please enter a public key (e): ',CR,LF,'$'
Nmsg    db 'Please enter the modulus (n = p * q : n>128, n<255): ',CR,LF,'$'
Emsg    db 'Please enter a key (e) that is less than and relatively prime to ','$'
Eerr1   db BEEP,'E must be less than ','$'
Eerr2   db BEEP,'E must be relatively prime to ','$'
Derr    db BEEP,'It is insecure for D and E to be the same!',CR,LF,'$'
PubKeyMsg db 'The public key (e, n) is (','$'
PriKeyMsg db 'The private key (d, n) is (','$'
Prompt  db ': ','$'

TextMsg db 'Enter text for the buffer',CR,LF,'$'
HexMsg  db 'Enter hex for the buffer',CR,LF,'$'
KeyMsg  db 'Generating new public keys...',CR,LF,'$'
PKeyMsg db 'Reading private key information...',CR,LF,'$'
DAscMsg db 'Here is the buffer in ASCII',CR,LF,'$'
DHexMsg db 'Here is the buffer in HEX',CR,LF,'$'
EncMsg  db 'Encrypting the buffer...',CR,LF,'$'
DecMsg  db 'Decrypting the buffer...',CR,LF,'$'

PUBLIC Pmsg, Perr1, Perr2, Perr3, Qmsg1, Qmsg2, Qerr1, Qerr2, Qerr3
PUBLIC Emsg, Eerr1, Eerr2, Derr, PubKeyMsg, PriKeyMsg, Prompt, crlf, PBuf
PUBLIC PUBmsg, Nmsg


p       db 5    ; a prime number
q       db 29   ; another prime number
n       db 145  ; modulus (p * q)
e       db 3    ; default encryption key [relatively prime to (p-1)*(q-1)]
d       db 75   ; default decryption key [relatively prime to (p-1)*(q-1)]

   ; Note that keys are initialized with a valid encoding/decoding values.
   ; They can be changed while running the program

PUBLIC Buffer, BufferLength, e, d, p, q, n

                                   
;====== Procedures ========================================================


; Your Subroutines go here !
; ---- ----------- -- ----


;====== Main procedure ====================================================

MenuMessage db CR,LF
  db '----------------------- MP2 Menu ---------------------',CR,LF
  db ' enter:    (T)ext message   (C)oded hex message',CR,LF
  db ' display:  (A)SCII message  (H)ex encoding',CR,LF
  db ' generate: (K)eys           (P)ublic key',CR,LF
  db ' coding:   (E)ncrypt        (D)ecrypt',CR,LF
  db '--------- [ESC] = (Q)uit -- redisplay (M)enu ---------',CR,LF,'$'

MPrompt db '>$'

main    proc    far
        mov     ax, cseg
        mov     ds, ax

          MOV DX, Offset MenuMessage
          CALL DSPMSG                   ; Display Menu

MainLoop: MOV DX, Offset CRLF
          CALL DSPMSG

          MOV DX, Offset MPrompt
          CALL DSPMSG

MainRead: CALL KBDIN                    ; Read Input

          CMP AL,'a'
          JB  MainOpt
          CMP AL,'z'                    ; Convert Lowercase to Uppercase
          JA  MainOpt
          SUB AL,'a'-'A'

MainOpt:  CMP AL,'K'
          JNE MainNotK                   
          MOV DX, offset KeyMsg
          CALL DSPMSG          
          CALL ReadKeys                 ; Generate a new key
          JMP MainLoop

MainNotK: CMP AL,'P'
          JNE MainNotP                   
          MOV DX, offset PKeyMsg
          CALL DSPMSG          
          CALL ReadPublicKeys           ; Read in a public key
          JMP MainLoop

MainNotP: CMP AL,'T'
          JNE MainNotT
          MOV DX, offset TextMsg
          CALL DSPMSG
          MOV AL, BufferMaxLength       ; Read in a text message
          MOV AH, ASCII
          MOV BX, Offset Buffer
          CALL ReadBuffer               
          MOV DX, Offset crlf
          CALL DSPMSG
          JMP MainLoop

MainNotT: CMP AL,'C'
          JNE MainNotC
          MOV DX, offset HexMsg
          CALL DSPMSG
          MOV AL, BufferMaxLength       ; Read in a hex message
          MOV AH, HEX
          MOV BX, Offset Buffer
          CALL ReadBuffer
          JMP MainLoop

MainNotC: CMP AL,'E'
          JNE MainNotE
          MOV DX, offset EncMsg
          CALL DSPMSG
          MOV DH, byte ptr e            ; Encode the message
          MOV CL, BufferLength
          MOV BX, offset Buffer
          CALL CodeBuffer 
          JMP MainLoop                  

MainNotE: CMP AL,'D'
          JNE MainNotD
          MOV DX, offset DecMsg
          CALL DSPMSG
          MOV DH, byte ptr d            ; Decode the message
          MOV CL, BufferLength
          MOV BX, offset Buffer
          CALL CodeBuffer             
          JMP MainLoop

MainNotD: CMP AL,'A'
          JNE MainNotA
          MOV DX, offset DAscMsg
          CALL DSPMSG
          MOV AL, BufferLength          ; Display the message in ASCII
          MOV AH, ASCII
          MOV BX, offset Buffer
          CALL PrintBuffer
          JMP MainLoop

MainNotA: CMP AL,'H'
          JNE MainNotH
          MOV DX, offset DHexMsg
          CALL DSPMSG
          MOV AL, BufferLength          ; Display the message in Hex
          MOV AH, HEX
          MOV BX, Offset Buffer
          CALL PrintBuffer
          JMP MainLoop

MainNotH: CMP AL,'M'
          JNE MainNotM
          MOV DX, offset MenuMessage
          CALL DSPMSG
          JMP MainLoop

MainNotM: CMP AL,ESCKEY
          JE  MainDone                  ; Quit program
          CMP AL,'Q'
          JE  MainDone

          JMP MainRead                  ; Ignore any other character 

MainDone: call MP2XIT ; Exit to DOS

main    endp

cseg    ends
        end     main