;; probabilistic prime testing and prime generation ;; run this using ;; > java -cp jscheme.jar:sisc.jar jscheme.REPL siscnum.scm ptestsisc.scm ;; > (genprime 123123123 10) ;; > (genprime (q "7489327489237489237489237849723947239478932749237498274897347239473479238749327493784932") 10) (use-module "siscnum.scm") (define google (s->q "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")) (define (numdigits x) (.length (.toString x))) ; this computes x to the nth power modulo m (define (fastexp x n m) (define (multmod a b) (remainder (* a b) m)) (define (squaremod a) (remainder (* a a) m)) (define (even? n) (= 0 (remainder n 2))) ; (display (list 'fastexp x n m)) (newline) (cond ((= 0 n) 1) ((= 1 n) x) ((even? n) (fastexp (squaremod x) (/ n 2) m)) (else (multmod x (fastexp (squaremod x) (/ (- n 1) 2) m))) )) ; this computes the gcd of A and B in log time (define (gcd A B) (if (< A B) (gcd B A) (if (= 0 B) A (gcd B (remainder A B))))) ; this returns the triple (u v d) such that (a*u+b*v = d) (define (gcd2 A B) (if (< A B) (let ((L (gcd2 B A))) (list (second L) (first L) (third L))) (if (= 0 B) (list 1 0 A) (let* ((m (quotient A B)) (r (remainder A B)) (L (gcd2 B r)) (u (first L)) (v (second L)) (d (third L))) (list v (- u (* v m)) d))))) ; this generates a random number with approximately the number of specified bits (define (random_bi num_bits) (s->q (.toString (java.math.BigInteger. (.intValue num_bits) (java.security.SecureRandom.getInstance "SHA1PRNG")) ))) ; this runs the Fermat test on p for a random x about the same size as p (define (primetest0 p) (define x (random_bi (* 3.3 (numdigits p)))) (define z (fastexp x (- p 1) p)) (= z 1) ) ; this runs the previous Fermat test n times (define (primetest p n) ; (display (list 'primetest p n)) (newline) (cond ((< n 1) (primetest0 p)) ((primetest0 p) (primetest p (- n 1))) (else #f) )) ; this returns the smallest prime number larger than or equal to n with probability 1-q^n for some q except in bad cases! (define (genprime p n) ; (display (list 'genprime p n)) (newline) (if (primetest p n) p (genprime (+ p 1) n))) ; this returns the largest prime number less than or equal to n with probability 1-q^n for some q except in bad cases! (define (genprime2 p n) ; (display (list 'genprime p n)) (newline) (if (primetest p n) p (genprime2 (+ -1 p) n))) ; here is java's builtin method for generating a prime of the specified number of bits (define (javaprime numbits) (java.math.BigInteger. numbits 100 (java.security.SecureRandom.getInstance "SHA1PRNG"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; RSA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; here we generate a keypair by repeatedly calling a primitive method that may throw errors... ; we call it until no errors are generated... (define (genkeypairs numbytes) (define KP (tryCatch (genkeypairs1 numbytes) (lambda(e) e))) (if (list? KP) KP (begin ; (display {trying to create a keypair [numbytes], got error [KP]\n}) (genkeypairs numbytes) ) )) ; this is the standard keypair generator, which can fail if the wrong random numbers are chosen (define (genkeypairs1 numbytes) ; returns public/private key pairs '((n e) (n f)) (define numbits (* 8 numbytes)) (define p (genprime (random_bi numbits) 10)) (define q (genprime (random_bi numbits) 10)) (define error1 (if (= p q) (throw "p = q !!"))) ;; test for an error condition (define n (* p q)) (define m (* (- p 1) (- q 1))) (define e (remainder (random_bi numbits) m)) (define gcdpair (gcd2 e m)) (define f1 (first gcdpair)) (define f (if (< f1 0) (+ f1 m) f1)) (define error2 (if (not (= (third gcdpair) 1)) (throw "e not relatively prime to m"))) (list (list n e) (list n f) m gcdpair p q f1) ) ; this creates a public key encoder (define (encoder PublicKey) (lambda(x) (fastexp x (second PublicKey) (first PublicKey)))) ; this converts a string into an array of integers by chunking the string into ; substrings of the specified number of bytes and then converting those byte string to numbers (define (string->code str numbytes) ; the idea is to transform the string into an array of bignums of size numbytes each (define numchars (.length str)) (define (substring-at k) (.substring str (.intValue k) (min numchars (.intValue (+ k numbytes))))) (define (iter start L) (if (>= start numchars) (reverse L) (iter (+ start numbytes) (cons (substring-at start) L)) )) (map java.math.BigInteger. (map .getBytes (iter 0 ()))) ) ; this reverse the process of string->code ; it converts a sequence of numbers into byte arrays and then into a string (define (code->string arr) (define bnums (map java.math.BigInteger. (map .toString arr))) (define ba (map .toByteArray bnums)) (define bs (map java.lang.String. ba)) (apply string-append bs)) ; this creates an RSA string encoder for the specified key and chunking factor (define (string-encoder publickey numbytes) ; this takes a string of characters and returns a list of integers using RSA to encode (lambda(x) (map (encoder publickey) (string->code x numbytes)))) ; this creates an RSA string decoder for the specified key (no chunking factor is needed here ...) (define (string-decoder publickey numbytes) ; this takes a list of integers and returns a string of characters using RSA to decode (lambda(x) (code->string (map (encoder publickey) x))))