CSE306 Processing Systems and Structures Lockwood, Spring 2003

Machine Problem 2/3: RSA Encryption

Purpose User I/O, Modulo Arithmetic, Encryption algorithms 
Points 100 [50 * 2]
Due Date Wednesday, March 12, 2003 (First 50 points)
Wednesday, March 19, 2003 (Last 50 points)

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 or MP3 grading sheet or
  2. Demonstrate MP2.EXE to a TA or to the instructor.
  3. Demonstrate your program to the TA and submit your source code on-line

  4. MP2.ASM

    
    TITLE CSE306:MP2-RSA - Your Name - Feb 2003
    
    ; RSA Data Encryption.
    ;
    ; Interactive program which uses
    ; RSA encryption to encrypt and decrypt textual data.
    ;
    ; CSE306: Machine Problem 2
    ; Prof. John W. Lockwood
    ; Co-Author: Brandon Tipp
    ; Washington University
    ; Dept. of Computer Science and Engineering
    ; Spring 2003
    ;
    ;  Ver 2.0
    
    ;====== 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 =========================================================
    
    ; -- LIB306 Routines (Use Freely. Same as those used in MP1) ---
    
    extrn kbdine:near, kbdin:near, dspout:near  
    extrn dspmsg:near, binasc:near, ascbin:near 
    extrn mp2xit:near ; Exit program with a call to this procedure
    
      ; You are GIVEN the following routines in LIB306.LIB
      ;	 (You may find these subroutines useful for your program!)
    
               extrn kbdine:near  ; From lib306
                    ; Reads a character from standard input and saves
                    ; the ASCII code of that register into register AL.
                    ; The character is echoed to standard output.
    
               extrn kbdin:near
                    ; Same as kbdine, but without the echo (input stored in AL)
    
               extrn binasc:near
    		; Converts a 16 bit binary integer into a string of decimal
    		; characters (ASCII-coded digits), writing them as a byte
                    ; string into memory.
    		; Call with:
    		;   AX = The 16-bit, signed integer to be converted.
    		;   BX = Starting offset address for a 7-byte buffer to 
                    ;        hold result
    		;	    (Typically the address of a variable like 'pbuf')
    		; Exits with:
                    ;   BX = The offset address of the first non-blank character 
                    ;           of the string (this may be a minus sign, if the
                    ;           input number was negative). The string will be 
                    ;           right-justified within the 7-byte buffer 
                    ;           (padded with blanks to the left), and will
    		;	    have a "$" delimiter character after the last
                    ;           digit.
    		;   CL = Number of non-blank characters generated in the
                    ;           string (including sign if number is negative).
    		;	    Example: CL = 2 for the number 78.
                    ;           Example: CL = 3 for the number -78,
    
               extrn ascbin:near
                    ; Converts an ASCII string in memory to 16-bit integer
                    
    		; Call with:
                    ;   BX = Starting offset address for a 7-byte buffer
                    ;        holding input string, terminated with '$'
    		;	    (Typically the address of a variable like 'pbuf')
    		; Exits with:
                    ;   AX = Signed 16-bit integer having value of the string
                    ;   BX = Offset address of first non-convertible character
                    ;           in the string (typically ignore this field)
                    ;   DL = Error Code
                    ;           0=No Error
                    ;           1=String had no valid digits
                    ;           2=String had too many digits
                    ;           3=Overflow
                    ;           4=Underflow (too negative)
    
    
               extrn dspout:near ; From lib306
                    ; Prints to display screen the ASCII-coded character in DL.
    		; Call with:
    		;   DL = ASCII code of character to be typed on the display.
    
               extrn dspmsg:near ; From lib306
    		; Prints a string of ASCII-coded characters on the display 
                    ;   screen.
                    ; String must be terminated by an ASCII dollar sign ("$").
    		; The starting offset of the string is to be given as an 
                    ;    input in DX.
    		; Call with:
    		;   DX = Offset address of first byte of the ASCII string 
                    ;   to be typed
    
               extrn mp2xit:near  ; From lib306 
    		; Exit your program
    
    ; -- LIBMP2 Routines (Replace these with your own code) ---
    
    extrn LibPrintBuffer:near     ; Print contents of Buffer
    extrn LibReadBuffer:near      ; Read Buffer from keyboard
    extrn LibCodeBuffer:near      ; Encode or decode the buffer
    extrn LibExpModN:near         ; Calculate X^Y mod Z
    extrn LibCheckPrime:near      ; Determine if a number is prime
    extrn LibReadKeys:near        ; Read and verify valid values for p, q, e, d   
    extrn LibReadPublicKeys:near  ; Read in d, n
    extrn LibGetD:near            ; Calculate d so that a*d mod b=c
    
    PUBLIC PrintBuffer            ; Allow library to call routines
    PUBLIC ReadBuffer
    PUBLIC CodeBuffer
    PUBLIC ExpModN
    PUBLIC CheckPrime
    PUBLIC ReadKeys      
    PUBLIC ReadPublicKeys
    PUBLIC GetD
    
    ;====== 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 !
    ; ---- ----------- -- ----
    
    PrintBuffer Proc near     ; Print contents of Buffer
      Call LibPrintBuffer     ; Replace with your own code!
      ret
    PrintBuffer ENDP
    
    
    ReadBuffer Proc near      ; Read Buffer from keyboard
      Call LibReadBuffer      ; Replace with your own code!
      ret
    ReadBuffer ENDP
    
    
    CodeBuffer Proc near      ; Encode or decode the buffer
      Call LibCodeBuffer      ; Replace with your own code!
      ret
    CodeBuffer ENDP
    
    
    ExpModN Proc near         ; Calculate X^Y mod Z
      Call LibExpModN         ; Replace with your own code!
      ret
    ExpModN ENDP
    
    
    CheckPrime Proc near      ; Determine if a number is prime
      Call LibCheckPrime      ; Replace with your own code!
      ret
    CheckPrime ENDP
    
    
    ReadKeys Proc near        ; Read and verify valid values for p, q, e, d
      Call LibReadKeys        ; Replace with your own code!
      ret
    ReadKeys ENDP
    
    
    ReadPublicKeys Proc near  ; Read in d, n
      Call LibReadPublicKeys  ; Replace with your own code!
      ret
    ReadPublicKeys ENDP
    
    
    GetD Proc near            ; Calculate d so that a*d mod b=c
      Call LibGetD            ; Replace with your own code!
      ret
    GetD ENDP
    
    ;====== 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