309 lines
10 KiB
Scheme
309 lines
10 KiB
Scheme
|
;;; rsa.ss
|
||
|
;;; Bruce T. Smith, University of North Carolina at Chapel Hill
|
||
|
;;; (circa 1984)
|
||
|
|
||
|
;;; Updated for Chez Scheme Version 7, May 2005
|
||
|
|
||
|
;;; This is a toy example of an RSA public-key encryption system. It
|
||
|
;;; is possible to create users who register their public keys with a
|
||
|
;;; center and hide their private keys. Then, it is possible to have
|
||
|
;;; the users exchange messages. To a limited extent one can look at
|
||
|
;;; the intermediate steps of the process by using encrypt and decrypt.
|
||
|
;;; The encrypted messages are represented by lists of numbers.
|
||
|
|
||
|
;;; Example session:
|
||
|
|
||
|
#|
|
||
|
> (make-user bonzo)
|
||
|
Registered with Center
|
||
|
User: bonzo
|
||
|
Base: 152024296883113044375867034718782727467
|
||
|
Encryption exponent: 7
|
||
|
> (make-user bobo)
|
||
|
Registered with Center
|
||
|
User: bobo
|
||
|
Base: 244692569127295893294157219042233636899
|
||
|
Encryption exponent: 5
|
||
|
> (make-user tiger)
|
||
|
Registered with Center
|
||
|
User: tiger
|
||
|
Base: 138555414233087084786368622588289286073
|
||
|
Encryption exponent: 7
|
||
|
> (show-center)
|
||
|
|
||
|
User: tiger
|
||
|
Base: 138555414233087084786368622588289286073
|
||
|
Encryption exponent: 7
|
||
|
|
||
|
User: bobo
|
||
|
Base: 244692569127295893294157219042233636899
|
||
|
Encryption exponent: 5
|
||
|
|
||
|
User: bonzo
|
||
|
Base: 152024296883113044375867034718782727467
|
||
|
Encryption exponent: 7
|
||
|
> (send "hi there" bonzo bobo)
|
||
|
"hi there"
|
||
|
> (send "hi there to you" bobo bonzo)
|
||
|
"hi there to you"
|
||
|
> (decrypt (encrypt "hi there" bonzo bobo) tiger)
|
||
|
" #z R4WN Zbb E8J"
|
||
|
|#
|
||
|
|
||
|
;;; Implementation:
|
||
|
|
||
|
(module ((make-user user) show-center encrypt decrypt send)
|
||
|
|
||
|
;;; (make-user name) creates a user with the chosen name. When it
|
||
|
;;; creates the user, it tells him what his name is. He will use
|
||
|
;;; this when registering with the center.
|
||
|
|
||
|
(define-syntax make-user
|
||
|
(syntax-rules ()
|
||
|
[(_ uid)
|
||
|
(begin (define uid (user 'uid)) (uid 'register))]))
|
||
|
|
||
|
;;; (encrypt mesg u1 u2) causes user 1 to encrypt mesg using the public
|
||
|
;;; keys for user 2.
|
||
|
|
||
|
(define-syntax encrypt
|
||
|
(syntax-rules ()
|
||
|
[(_ mesg u1 u2) ((u1 'send) mesg 'u2)]))
|
||
|
|
||
|
;;; (decrypt number-list u) causes the user to decrypt the list of
|
||
|
;;; numbers using his private key.
|
||
|
|
||
|
(define-syntax decrypt
|
||
|
(syntax-rules ()
|
||
|
[(_ numbers u) ((u 'receive) numbers)]))
|
||
|
|
||
|
;;; (send mesg u1 u2) this combines the functions 'encrypt' and 'decrypt',
|
||
|
;;; calling on user 1 to encrypt the message for user 2 and calling on
|
||
|
;;; user 2 to decrypt the message.
|
||
|
|
||
|
(define-syntax send
|
||
|
(syntax-rules ()
|
||
|
[(_ mesg u1 u2) (decrypt (encrypt mesg u1 u2) u2)]))
|
||
|
|
||
|
;;; A user is capable of the following:
|
||
|
;;; - choosing public and private keys and registering with the center
|
||
|
;;; - revealing his public and private keys
|
||
|
;;; - retrieving user's private keys from the center and encrypting a
|
||
|
;;; message for that user
|
||
|
;;; - decrypting a message with his private key
|
||
|
|
||
|
(define user
|
||
|
(lambda (name)
|
||
|
(let* ([low (expt 2 63)] ; low, high = bounds on p and q
|
||
|
[high (* 2 low)]
|
||
|
[p 0] ; p,q = two large, probable primes
|
||
|
[q 0]
|
||
|
[n 0] ; n = p * q, base for modulo arithmetic
|
||
|
[phi 0] ; phi = lcm(p-1,q-1), not quite the Euler phi function,
|
||
|
; but it will serve for our purposes
|
||
|
[e 0] ; e = exponent for encryption
|
||
|
[d 0]) ; d = exponent for decryption
|
||
|
(lambda (request)
|
||
|
(case request
|
||
|
;; choose keys and register with the center
|
||
|
[register
|
||
|
(set! p (find-prime low high))
|
||
|
(set! q
|
||
|
(let loop ([q1 (find-prime low high)])
|
||
|
(if (= 1 (gcd p q1))
|
||
|
q1
|
||
|
(loop (find-prime low high)))))
|
||
|
(set! n (* p q))
|
||
|
(set! phi
|
||
|
(/ (* (1- p) (1- q))
|
||
|
(gcd (1- p) (1- q))))
|
||
|
(set! e
|
||
|
(do ([i 3 (+ 2 i)])
|
||
|
((= 1 (gcd i phi)) i)))
|
||
|
(set! d (mod-inverse e phi))
|
||
|
(register-center (cons name (list n e)))
|
||
|
(printf "Registered with Center~%")
|
||
|
(printf "User: ~s~%" name)
|
||
|
(printf "Base: ~d~%" n)
|
||
|
(printf "Encryption exponent: ~d~%" e)]
|
||
|
|
||
|
;; divulge your keys-- you should resist doing this...
|
||
|
[show-all
|
||
|
(printf "p = ~d ; q = ~d~%" p q)
|
||
|
(printf "n = ~d~%" n)
|
||
|
(printf "phi = ~d~%" (* (1- p) (1- q)))
|
||
|
(printf "e = ~d ; d = ~d~%" e d)]
|
||
|
|
||
|
;; get u's public key from the center and encode
|
||
|
;; a message for him
|
||
|
[send
|
||
|
(lambda (mesg u)
|
||
|
(let* ([public (request-center u)]
|
||
|
[base (car public)]
|
||
|
[exponent (cadr public)]
|
||
|
[mesg-list (string->numbers mesg base)])
|
||
|
(map (lambda (x) (expt-mod x exponent base))
|
||
|
mesg-list)))]
|
||
|
|
||
|
;; decrypt a message with your private key
|
||
|
[receive
|
||
|
(lambda (crypt-mesg)
|
||
|
(let ([mesg-list (map (lambda (x) (expt-mod x d n)) crypt-mesg)])
|
||
|
(numbers->string mesg-list)))])))))
|
||
|
|
||
|
;;; The center maintains the list of public keys. It can register
|
||
|
;;; new users, provide the public keys for any particular user, or
|
||
|
;;; display the whole public file.
|
||
|
|
||
|
(module (register-center request-center show-center)
|
||
|
(define public-keys '())
|
||
|
(define register-center
|
||
|
(lambda (entry)
|
||
|
(set! public-keys
|
||
|
(cons entry
|
||
|
(remq (assq (car entry) public-keys) public-keys)))))
|
||
|
(define request-center
|
||
|
(lambda (u)
|
||
|
(let ([a (assoc u public-keys)])
|
||
|
(when (null? a)
|
||
|
(error 'request-center
|
||
|
"User ~s not registered in center"
|
||
|
u))
|
||
|
(cdr a))))
|
||
|
(define show-center
|
||
|
(lambda ()
|
||
|
(for-each
|
||
|
(lambda (entry)
|
||
|
(printf "~%User: ~s~%" (car entry))
|
||
|
(printf "Base: ~s~%" (cadr entry))
|
||
|
(printf "Encryption exponent: ~s~%" (caddr entry)))
|
||
|
public-keys)))
|
||
|
)
|
||
|
|
||
|
;;; string->numbers encodes a string as a list of numbers
|
||
|
;;; numbers->string decodes a string from a list of numbers
|
||
|
|
||
|
;;; string->numbers and numbers->string are defined with respect to
|
||
|
;;; an alphabet. Any characters in the alphabet are translated into
|
||
|
;;; integers---their regular ascii codes. Any characters outside
|
||
|
;;; the alphabet cause an error during encoding. An invalid code
|
||
|
;;; during decoding is translated to a space.
|
||
|
|
||
|
(module (string->numbers numbers->string)
|
||
|
(define first-code 32)
|
||
|
(define last-code 126)
|
||
|
(define alphabet
|
||
|
; printed form of the characters, indexed by their ascii codes
|
||
|
(let ([alpha (make-string 128 #\space)])
|
||
|
(do ([i first-code (1+ i)])
|
||
|
((= i last-code) alpha)
|
||
|
(string-set! alpha i (integer->char i)))))
|
||
|
|
||
|
(define string->integer
|
||
|
(lambda (str)
|
||
|
(let ([ln (string-length str)])
|
||
|
(let loop ([i 0] [m 0])
|
||
|
(if (= i ln)
|
||
|
m
|
||
|
(let* ([c (string-ref str i)] [code (char->integer c)])
|
||
|
(when (or (< code first-code) (>= code last-code))
|
||
|
(error 'rsa "Illegal character ~s" c))
|
||
|
(loop (1+ i) (+ code (* m 128)))))))))
|
||
|
|
||
|
(define integer->string
|
||
|
(lambda (n)
|
||
|
(list->string
|
||
|
(map (lambda (n) (string-ref alphabet n))
|
||
|
(let loop ([m n] [lst '()])
|
||
|
(if (zero? m)
|
||
|
lst
|
||
|
(loop (quotient m 128)
|
||
|
(cons (remainder m 128) lst))))))))
|
||
|
|
||
|
; turn a string into a list of numbers, each no larger than base
|
||
|
(define string->numbers
|
||
|
(lambda (str base)
|
||
|
(letrec ([block-size
|
||
|
(do ([i -1 (1+ i)] [m 1 (* m 128)]) ((>= m base) i))]
|
||
|
[substring-list
|
||
|
(lambda (str)
|
||
|
(let ([ln (string-length str)])
|
||
|
(if (>= block-size ln)
|
||
|
(list str)
|
||
|
(cons (substring str 0 block-size)
|
||
|
(substring-list
|
||
|
(substring str block-size ln))))))])
|
||
|
(map string->integer (substring-list str)))))
|
||
|
|
||
|
; turn a list of numbers into a string
|
||
|
(define numbers->string
|
||
|
(lambda (lst)
|
||
|
(letrec ([reduce
|
||
|
(lambda (f l)
|
||
|
(if (null? (cdr l))
|
||
|
(car l)
|
||
|
(f (car l) (reduce f (cdr l)))))])
|
||
|
(reduce
|
||
|
string-append
|
||
|
(map (lambda (x) (integer->string x)) lst)))))
|
||
|
)
|
||
|
|
||
|
;;; find-prime finds a probable prime between two given arguments.
|
||
|
;;; find-prime uses a cheap but fairly dependable test for primality
|
||
|
;;; for large numbers, by first weeding out multiples of first 200
|
||
|
;;; primes, then applies Fermat's theorem with base 2.
|
||
|
|
||
|
(module (find-prime)
|
||
|
(define product-of-primes
|
||
|
; compute product of first n primes, n > 0
|
||
|
(lambda (n)
|
||
|
(let loop ([n (1- n)] [p 2] [i 3])
|
||
|
(cond
|
||
|
[(zero? n) p]
|
||
|
[(= 1 (gcd i p)) (loop (1- n) (* p i) (+ i 2))]
|
||
|
[else (loop n p (+ i 2))]))))
|
||
|
(define prod-first-200-primes (product-of-primes 200))
|
||
|
(define probable-prime
|
||
|
; first check is quick, and weeds out most non-primes
|
||
|
; second check is slower, but weeds out almost all non-primes
|
||
|
(lambda (p)
|
||
|
(and (= 1 (gcd p prod-first-200-primes))
|
||
|
(= 1 (expt-mod 2 (1- p) p)))))
|
||
|
(define find-prime
|
||
|
; find probable prime in range low to high (inclusive)
|
||
|
(lambda (low high)
|
||
|
(let ([guess
|
||
|
(lambda (low high)
|
||
|
(let ([g (+ low (random (1+ (- high low))))])
|
||
|
(if (odd? g) g (1+ g))))])
|
||
|
(let loop ([g (guess low high)])
|
||
|
(cond
|
||
|
; start over if already too high
|
||
|
[(> g high) (loop (guess low high))]
|
||
|
; if guess is probably prime, return
|
||
|
[(probable-prime g) g]
|
||
|
; don't bother with even guesses
|
||
|
[else (loop (+ 2 g))])))))
|
||
|
)
|
||
|
|
||
|
;;; mod-inverse finds the multiplicative inverse of x mod b, if it exists
|
||
|
|
||
|
(module (mod-inverse)
|
||
|
(define gcdx
|
||
|
; extended Euclid's gcd algorithm, x <= y
|
||
|
(lambda (x y)
|
||
|
(let loop ([x x] [y y] [u1 1] [u2 0] [v1 0] [v2 1])
|
||
|
(if (zero? y)
|
||
|
(list x u1 v1)
|
||
|
(let ([q (quotient x y)] [r (remainder x y)])
|
||
|
(loop y r u2 (- u1 (* q u2)) v2 (- v1 (* q v2))))))))
|
||
|
|
||
|
(define mod-inverse
|
||
|
(lambda (x b)
|
||
|
(let* ([x1 (modulo x b)] [g (gcdx x1 b)])
|
||
|
(unless (= (car g) 1)
|
||
|
(error 'mod-inverse "~d and ~d not relatively prime" x b))
|
||
|
(modulo (cadr g) b))))
|
||
|
)
|
||
|
)
|