-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrsa.el
133 lines (109 loc) · 4.78 KB
/
rsa.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
;;; rsa.el --- RSA crypto in Emacs Lisp -*- lexical-binding: t; -*-
;; This is free and unencumbered software released into the public domain.
;; Author: Christopher Wellons <[email protected]>
;; URL: https://github.com/skeeto/emacs-rsa
;;; Commentary:
;; RSA signature algorithms built on Emacs' calc.
;; Quick start:
;; (setf keypair (rsa-generate-keypair 1024))
;; (setf message "hello, world!")
;; (setf sig (rsa-sign (plist-get keypair :private) message))
;; (rsa-verify (plist-get keypair :public) message sig)
;; For large keys you may need to adjust `max-lisp-eval-depth' and
;; `max-specpdl-size', just as you would for other large calc
;; operations.
;; Time estimates (Emacs 24.4 + Core i7):
;; Keylen Key generation Signature generation
;; 512 bits 12 sec 3 sec
;; 1024 bits 2 min 11 sec
;; 2048 bits 29 min 1 min
;; Signature verification time is negligible.
;;; Code:
(require 'calc)
(require 'cl-lib)
(defun rsa--buffer-to-calc-hex ()
"Return a calc number of the bytes of the current buffer."
(let ((f (apply-partially #'format "%02x")))
(concat "16#" (mapconcat f (buffer-string) ""))))
(defun rsa-generate-prime (bits)
"Generate a random prime number of BITS length from /dev/urandom."
(with-temp-buffer
(set-buffer-multibyte nil)
(call-process "head" "/dev/urandom" (current-buffer) nil
"-c" (number-to-string (/ bits 8)))
(calc-eval "nextprime($1, 10)" nil (rsa--buffer-to-calc-hex))))
(defun rsa--inverse (a n)
"Multiplicative inverse using extended Euclidean algorithm."
(let ((y 0)
(r n)
(newy 1)
(newr a))
(while (calc-eval "$1 != 0" 'pred newr)
(let ((quotient (calc-eval "$1 \\ $2" nil r newr)))
(cl-psetf y newy
newy (calc-eval "$1 - $2 * $3" nil
y quotient newy))
(cl-psetf r newr
newr (calc-eval "$1 - $2 * $3" nil
r quotient newr))))
(when (calc-eval "$1 > 1" 'pred r)
(error "not invertable"))
(if (calc-eval "$1 < 0" 'pred y)
(calc-eval "$1 + $2" nil y n)
y)))
(defun rsa-generate-keypair (bits)
"Generate a fresh RSA keypair plist of BITS length."
(let* ((p (rsa-generate-prime (+ 1 (/ bits 2))))
(q (rsa-generate-prime (+ 1 (/ bits 2))))
(n (calc-eval "$1 * $2" nil p q))
(i (calc-eval "($1 - 1) * ($2 - 1)" nil p q))
(e (calc-eval "2^16+1"))
(d (rsa--inverse e i)))
`(:public (:n ,n :e ,e) :private (:n ,n :d ,d))))
(defun rsa--mod-pow (base exponent modulus)
"Modular exponentiation using right-to-left binary method."
(let ((result 1))
(setf base (calc-eval "$1 % $2" nil base modulus))
(while (calc-eval "$1 > 0" 'pred exponent)
(when (calc-eval "$1 % 2 == 1" 'pred exponent)
(setf result (calc-eval "($1 * $2) % $3" nil result base modulus)))
(setf exponent (calc-eval "$1 \\ 2" nil exponent)
base (calc-eval "($1 * $1) % $2" nil base modulus)))
result))
(defun rsa--encode-sig (number)
"Encode signature as short string."
(substring (calc-eval '("$1" calc-number-radix 36) nil number) 3))
(defun rsa--decode-sig (sig)
(concat "36#" sig))
(cl-defun rsa-sign (private-key object &optional (hash-algo 'sha384))
"Compute the base-36 signature by PRIVATE-KEY for OBJECT.
OBJECT is a buffer or string. HASH-ALGO must be a valid symbol
for the first argument of `secure-hash'."
(let ((n (plist-get private-key :n))
(d (plist-get private-key :d))
(hash (concat "16#" (secure-hash hash-algo object))))
(while (calc-eval "$1 > $2" 'pred hash n)
(setf hash (calc-eval "$1 \\ 2" nil hash)))
(rsa--encode-sig (rsa--mod-pow hash d n))))
(cl-defun rsa-verify (public-key object sig &optional (hash-algo 'sha384))
"Return non-nil nil if the signature matches PUBLIC-KEY for OBJECT.
HASH-ALGO must match the algorithm used in generating the signature."
(let ((n (plist-get public-key :n))
(e (plist-get public-key :e))
(hash (concat "16#" (secure-hash hash-algo object))))
(while (calc-eval "$1 > $2" 'pred hash n)
(setf hash (calc-eval "$1 \\ 2" nil hash)))
(let* ((result (rsa--mod-pow (rsa--decode-sig sig) e n)))
(calc-eval "$1 == $2" 'pred result hash))))
(cl-defun rsa--stretch-passphrase (passphrase bits &optional (iter 500000))
"Stretch passphrase to a size of BITS over ITER hash iterations.
Currently unused."
(with-temp-buffer
(set-buffer-multibyte nil)
(dotimes (_ iter)
(setf passphrase (secure-hash 'sha512 passphrase nil nil t)))
(dotimes (i (ceiling bits 512))
(insert (secure-hash 'sha512 (format "%d%s" i passphrase) nil nil t)))
(buffer-substring (point-min) (+ (point-min) (/ bits 8)))))
(provide 'rsa)
;;; rsa.el ends here