Skip to content

Instantly share code, notes, and snippets.

@zafercavdar
Created November 3, 2016 22:58
Show Gist options
  • Save zafercavdar/b5a79b4c5c9f0c99b8a14befb133ad93 to your computer and use it in GitHub Desktop.
Save zafercavdar/b5a79b4c5c9f0c99b8a14befb133ad93 to your computer and use it in GitHub Desktop.
;;; zcavdar14@ku.edu.tr Wed Oct 19 12:48:37 2016
#lang sicp
(#%require (only racket/base random))
(define your-answer-here -1)
;;; +mod takes two numbers and modulo n
;;; it adds up to numbers and return the value of this number in modulo n
(define +mod
(lambda (a b n)
(modulo (+ a b) n)
))
;;; -mod takes two numbers and modulo n
;;; it substract first number from the second one and return the value of this number in modulo n
(define -mod
(lambda (a b n)
(modulo (- a b) n)
))
;;; +mod takes two numbers and modulo n
;;; it multiplies first 2 numbers and return the value of this number in modulo n
(define *mod
(lambda (a b n)
(modulo (* a b) n)
))
;;; Description for exptmod
;;; exptmod calculates modulo n of x to the n in an iterative way by using repeated squaring.
;;; x is an any integer, n is 0 or positive integer, m is any integer.
(define (exptmod x n m)
(define result 1)
(define (helper product now n m result)
(if (= n 0)
result
(if (< (* now 2) n)
(helper (*mod product product m) (* now 2) n m result)
(helper x 1 (- n now) m (*mod result product m)))))
(helper x 1 n m result))
;;; Description for random-k-digit-number:
;;; it takes n as input parameter and returns a random number such that
;;; it's maximum number of digits is n.
;;; how does it works? choose a random number between [0,9] and add it to the
;;; 10 times of previous solution. Iterate n times.
(define (random-k-digit-number n)
(define (helper number count max)
(define x (random 10))
(if (< count max)
(helper (+ x (* number 10)) (+ count 1) max)
number))
(helper 0 0 n)
)
;;; count-digits takes a number and returns its number of digits in an iterative way.
(define count-digits
(lambda (n)
(define (helper2 count n)
(if (< n 10)
count
(helper2 (+ 1 count) (/ n 10))))
(helper2 1 n)
))
;;; big-random takes n as input and calls k = (count-digits n).
;;; it calls random-k-digit-number k and checks if the number is smaller than n or not.
;;; if it's not smaller, calls itself again until finding a random number whose digit number is max k and smaller than n.
(define big-random
(lambda (n)
(define rand (random-k-digit-number (count-digits n)))
(if (< rand n) rand (big-random n))
))
;;; prime? takes a positive integer and returns if it is prime or not
;;; with a probabilistic approach using Fermat's Little Theorem.
(define prime-test-iterations 20)
(define (prime?-helper count a n)
(if (>= count prime-test-iterations) #t
(if (= (exptmod a n n) a) (prime?-helper (+ 1 count) (big-random n) n) #f)
)
)
(define prime?
(lambda (p)
(if (< p 2) #f
(prime?-helper 1 (big-random p) p)
)
))
;;; random-prime takes n as input returns a random prime smaller than n.
;;; it calls big-random n and prime? n procedures until finding a prime number.
(define random-prime
(lambda (n)
(define candidate-random (big-random n))
(if (prime? candidate-random)
candidate-random
(random-prime n))
))
;;; ax+by=1 takes a b as an input and returns a list of 2 numbers that satisfy
;;; this equation. it only succeeds iff gcd(a,b) = 1
(define ax+by=1
(lambda (a b)
(define q (quotient a b))
(define r (remainder a b))
(define (helper xx yy q)
(list yy (- xx (* q yy))))
(if (= r 1)
(list 1 (- q))
(let (( res (ax+by=1 b r)))
(helper (list-ref res 0) (list-ref res 1) q)))
))
;;; inverse-mod takes e and n (both positive integers) and calculates d such that
;;; e*d = 1 (mod n). it calls ax+by=1 to solve ed + (-k)n = 1 where e and n are knowns.
(define inverse-mod
(lambda (e n)
(define (helper e n)
(list-ref (ax+by=1 e n) 0))
(if (= (gcd e n) 1)
(modulo (list-ref (ax+by=1 e n) 0) n)
(display "gcd is not 1 \n")
)
))
(define (calc-exponent n p q)
(define e (big-random n))
(if (= (gcd e (* (- p 1) (- q 1))) 1)
e
(calc-exponent n p q)))
(define (calc-d-exponent e p q)
(inverse-mod e (* (- p 1) (- q 1)))
)
;;; Description of random-keypair
;;; make-key calls cons and returns exp and mod as a pair
(define (make-key exp mod)
(cons exp mod))
;;; get-exponent returns the first component of the key pair = exponent
(define (get-exponent key)
(car key))
;;; get-modulus returns the second component of the key pair = modulus
(define (get-modulus key)
(cdr key))
;;; generates p q and n such that n > m
;;; using the p and q, it finds an appropriate encryption key = e
;;; decryption key is calculated by finding inverse-mod of e in modulus (p-1)*(q-1)
;;; random-keypair returns the list: ((e,n) (d,n))
(define (random-keypair m)
(define p 0)
(define q 0)
(define e 0)
(define n 0)
(define d 0)
(set! p (random-prime m))
(set! q (random-prime m))
(set! n (* p q))
(define (get-pair n p q)
(set! e (calc-exponent n p q))
(set! d (calc-d-exponent e p q))
(list (make-key e n) (make-key d n)))
(if (< n m) (random-keypair m)
(get-pair n p q)))
;;; Description of rsa
;;; rsa takes key (e,n) pair and message
;;; returns (m^e) mod n
(define rsa
(lambda (key message)
(exptmod message (get-exponent key) (get-modulus key))
))
(define encrypt
(lambda (public-key string)
(define message (string->integer string))
(rsa public-key message)
))
;;; Description of encrypt:
(define decrypt
(lambda (private-key encrypted-message)
(define message (rsa private-key encrypted-message))
(integer->string message)
))
;; Test cases:
;(define key (random-keypair 1000000000000000000000000000))
;(define e1 (encrypt (car key) "hello Comp200!"))
;(decrypt (cadr key) e1) ; -> "hello Comp200!"
;(define e2 (encrypt (car key) ""))
;(decrypt (cadr key) e2) ; -> ""
;(define e3 (encrypt (car key) "This is fun!"))
;(decrypt (cadr key) e3) ; -> "This is fun!"
;(define e4 (encrypt (car key) "I am Zafer "))
;(decrypt (cadr key) e4) ;
(define slow-exptmod
(lambda (a b n)
(if (= b 0)
1
(*mod a (slow-exptmod a (- b 1) n) n))))
(define test-factors
(lambda (n k)
(cond ((>= k n) #t)
((= (remainder n k) 0) #f)
(else (test-factors n (+ k 1))))))
(define slow-prime?
(lambda (n)
(if (< n 2)
#f
(test-factors n 2))))
(define (join-numbers list radix)
; Takes a list of numbers (i_0 i_1 i_2 ... i_k)
; and returns the number
; n = i_0 + i_1*radix + i_2*radix2 + ... i_k*radix^k + radix^(k+1)
; The last term creates a leading 1, which allows us to distinguish
; between lists with trailing zeros.
(if (null? list)
1
(+ (car list) (* radix (join-numbers (cdr list) radix)))))
; test cases
;(join-numbers '(3 20 39 48) 100) ;-> 148392003
;(join-numbers '(5 2 3 5 1 9) 10) ;-> 1915325
;(join-numbers '() 10) ;-> 1
(define (split-number n radix)
; Inverse of join-numbers. Takes a number n generated by
; join-numbers and converts it to a list (i_0 i_1 i_2 ... i_k) such
; that
; n = i_0 + i_1*radix + i_2*radix2 + ... i_k*radix^k + radix^(k+1)
(if (<= n 1)
'()
(cons (remainder n radix)
(split-number (quotient n radix) radix))))
; test cases
;(split-number (join-numbers '(3 20 39 48) 100) 100) ;-> (3 20 39 48)
;(split-number (join-numbers '(5 2 3 5 1 9) 10) 10) ;-> (5 2 3 5 1 9)
;(split-number (join-numbers '() 10) 10) ; -> ()
(define chars->bytes
; Takes a list of 16-bit Unicode characters (or 8-bit ASCII
; characters) and returns a list of bytes (numbers in the range
; [0,255]). Characters whose code value is greater than 255 are
; encoded as a three-byte sequence, 255 <low byte> <high byte>.
(lambda (chars)
(if (null? chars)
'()
(let ((c (char->integer (car chars))))
(if (< c 255)
(cons c (chars->bytes (cdr chars)))
(let ((lowbyte (remainder c 256))
(highbyte (quotient c 256)))
(cons 255 (cons lowbyte (cons highbyte (chars->bytes (cdr chars)))))))))))
; test cases
;(chars->bytes (string->list "hello")) ; -> (104 101 108 108 111)
;(chars->bytes (string->list "\u0000\u0000\u0000")) ; -> (0 0 0)
;(chars->bytes (string->list "\u3293\u5953\uabab")) ; -> (255 147 50 255 83 89 255 171 171)
(define bytes->chars
; Inverse of chars->bytes. Takes a list of integers that encodes a
; sequence of characters, and returns the corresponding list of
; characters. Integers less than 255 are converted directly to the
; corresponding ASCII character, and sequences of 255 <low-byte>
; <high-byte> are converted to a 16-bit Unicode character.
(lambda (bytes)
(if (null? bytes)
'()
(let ((b (car bytes)))
(if (< b 255)
(cons (integer->char b)
(bytes->chars (cdr bytes)))
(let ((lowbyte (cadr bytes))
(highbyte (caddr bytes)))
(cons (integer->char (+ lowbyte (* highbyte 256)))
(bytes->chars (cdddr bytes)))))))))
; test cases
;(bytes->chars '(104 101 108 108 111)) ; -> (#\h #\e #\l #\l #\o)
;(bytes->chars '(255 147 50 255 83 89 255 171 171)) ; -> (#\u3293 #\u5953 #\uabab)
(define (string->integer string)
; returns an integer representation of an arbitrary string.
(join-numbers (chars->bytes (string->list string)) 256))
; test cases
;(string->integer "hello, world")
;(string->integer "")
;(string->integer "April is the cruelest month")
;(string->integer "\u0000\u0000\u0000")
(define (integer->string integer)
; inverse of string->integer. Returns the string corresponding to
; an integer produced by string->integer.
(list->string (bytes->chars (split-number integer 256))))
; test cases
;(integer->string (string->integer "hello, world"))
;(integer->string (string->integer ""))
;(integer->string (string->integer "April is the cruelest month"))
;(integer->string (string->integer "\u0000\u0000\u0000"))
;(integer->string (string->integer "\u3293\u5953\uabab"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment