Debug message fix
[sxemacs] / lisp / ffi / ffi-gcrypt.el
1 ;;; ffi-gcrypt.el -- FFI access to libgcrypt
2 ;;
3 ;; Copyright (C) 2005, 2006 Sebastian Freundt
4 ;; Author: Sebastian Freundt <hroptatyr@sxemacs.org>
5 ;; Version: 0.1
6 ;; URL: none
7 ;; Maintainer: Sebastian Freundt <hroptatyr@sxemacs.org>
8 ;; Keywords: cryptography, security
9 ;;
10 ;; This file is part of SXEmacs.
11 ;;
12 ;; SXEmacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; SXEmacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 ;;
25 ;;; Comments:
26 ;; This is an FFI implementation of routines in the libgcrypt library.
27 ;;
28 ;;; Code:
29
30 (require 'ffi)
31 (require 'ffi-libc)
32
33 ;; this is our spine, barf if it does not exist
34 (ffi-load "libgcrypt")
35
36 (defgroup ffi-gcrypt nil
37   "FFI bindings for libgcrypt (part of GPG)."
38   :group 'extensions)
39
40 \f
41 ;;;;;;;;;;;;;;;;;;;;;;;
42 ;;; Message digests ;;;
43 ;;;;;;;;;;;;;;;;;;;;;;;
44 (unless (ffi-find-named-type 'gcry_md_hd_t)
45   (define-ffi-type gcry_md_hd_t (pointer void)))
46
47 (defun gcry:md-handle-p (md-handle)
48   "Return non-`nil' iff MD-HANDLE is a valid handle for message digests."
49   (and (ffi-object-p md-handle)
50        (eq (ffi-object-type md-handle) 'gcry_md_hd_t)))
51
52 (ffi-enum gcry:md_flags
53   "Flags used with the open function."
54   gcry:md_flag_empty
55   gcry:md_flag_secure
56   gcry:md_flag_hmac)
57
58 (defconst gcry:md_open
59   (ffi-defun '(function int (pointer gcry_md_hd_t) int unsigned-int)
60              "gcry_md_open")
61   "Return a handle for message digests.")
62
63 ;;;###autoload
64 (defun gcry:md-open (&optional hash-algo)
65   "Return a message digest handle, initialised by HASH-ALGO."
66   (let ((md-handle (make-ffi-object '(pointer void)))
67         (md-number (if hash-algo
68                        (gcry:md-map-name hash-algo)
69                      0)))
70     (let ((g-hd (ffi-address-of md-handle))
71           (g-algo (ffi-create-fo 'int md-number))
72           (g-flags (gcry:md_flags 'gcry:md_flag_empty)))
73       (let ((ret (ffi-get
74                   (ffi-call-function gcry:md_open g-hd g-algo g-flags)))
75             (hd (ffi-get g-hd)))
76         (when (ffi-null-p hd)
77           (error "gcry:md-open: Cannot get initial MD handle"))
78         (and (zerop ret)
79              (ffi-set-object-type md-handle 'gcry_md_hd_t)
80              md-handle)))))
81
82 (defalias 'gcry:make-message-digest #'gcry:md-open)
83
84 (defconst gcry:md_close
85   (ffi-defun '(function void gcry_md_hd_t)
86              "gcry_md_close")
87   "Destroy a handle for message digests.")
88
89 (defmacro gcry:md-close (md-handle)
90   "Free resources occupied by MD-HANDLE."
91   (when (gcry:md-handle-p (symbol-value md-handle))
92     (ffi-call-function gcry:md_close (symbol-value md-handle))
93     (set md-handle nil)
94     t))
95
96 (defalias 'gcry:destroy-message-digest #'gcry:md-close)
97
98 (defconst gcry:md_enable
99   (ffi-defun '(function int gcry_md_hd_t int)
100              "gcry_md_enable")
101   "Enable hash-algorithm within a message digest context.")
102
103 (defun gcry:md-enable (md-handle hash-algo)
104   "Additionally make MD-HANDLE support the algorithm HASH-ALGO."
105   (let ((g-algo (ffi-create-fo 'int (gcry:md-map-name hash-algo))))
106     (when (ffi-object-p md-handle)
107       (let ((ret (ffi-get
108                   (ffi-call-function gcry:md_enable md-handle g-algo))))
109         (zerop ret)))))
110
111 (defconst gcry:md_map_name
112   (ffi-defun '(function int (pointer char))
113              "gcry_md_map_name")
114   "Return the enumeration value of a hash algorithm.")
115
116 (defun gcry:md-map-name (string)
117   "Return the internal number of a hash algorithm specified by STRING."
118   (let ((fo (ffi-create-fo 'c-string string)))
119     (ffi-get (ffi-call-function gcry:md_map_name fo))))
120
121 (defun gcry:md-algo-to-enum (hash-algo)
122   "Return the internal form of HASH-ALGO."
123   (let ((g-enum (gcry:md-map-name hash-algo)))
124     (unless (zerop g-enum)
125       (ffi-create-fo 'int g-enum))))
126 ;; (gcry:md-algo-to-enum "SHA512")
127 ;; (gcry:md-map-name "SHA512")
128 ;; (gcry:md-map-name "SHA44")
129
130
131 (defconst gcry:md_write
132   (ffi-defun '(function void gcry_md_hd_t (pointer void) unsigned-int)
133              "gcry_md_write")
134   "Write data into message digest context.")
135
136 (defun gcry:md-write (md-handle data)
137   "Write DATA to the digest machinery specified by MD-HANDLE."
138   (let ((g-buffer (ffi-create-fo 'c-data data))
139         (g-length (ffi-create-fo 'unsigned-int (length data))))
140     (when (ffi-object-p md-handle)
141       (ffi-call-function gcry:md_write md-handle g-buffer g-length)
142       t)))
143 ;; (gcry:md-write handle "kfjf")
144 ;;(ffi-create-fo 'c-string "sifistring")
145
146 (defconst gcry:md_get_algo_dlen
147   (ffi-defun '(function unsigned-int int)
148              "gcry_md_get_algo_dlen")
149   "Return the length of the message digest.")
150
151 (defun gcry:md-get-algo-dlen (hash-algo)
152   "Return the length of the resulting message hash of HASH-ALGO."
153   (let ((g-algo (ffi-create-fo 'int (gcry:md-map-name hash-algo))))
154     (ffi-get (ffi-call-function gcry:md_get_algo_dlen g-algo))))
155
156 (defconst gcry:md_read
157   (ffi-defun '(function c-data gcry_md_hd_t int)
158              "gcry_md_read")
159   "Return the message digest.")
160
161 (defun gcry:md-read (md-handle hash-algo)
162   "Return the message hash of MD-HANDLE under HASH-ALGO."
163   (let ((g-algo (ffi-create-fo 'int (gcry:md-map-name hash-algo))))
164     (when (ffi-object-p md-handle)
165       (let ((ret (ffi-call-function gcry:md_read md-handle g-algo))
166             (len (ffi-get (ffi-call-function gcry:md_get_algo_dlen g-algo))))
167         ;;(ffi-get ret :type (cons 'c-data len))
168         (ffi-fetch ret 0 (cons 'c-data len))))))
169
170 ;;(setq handle (gcry:md-open "SHA1"))
171 ;;(gcry:md-enable handle "MD5")
172 ;;(gcry:md-write handle "test string what's my hash value?")
173 ;;(setq md1 (gcry:md-read handle "SHA1"))
174 ;;(setq md2 (gcry:md-read handle "MD5"))
175 ;;(gcry:md-close handle)
176 ;;(ossl-digest 'MD5 "test string what's my hash value?")
177 ;;(setq handle (gcry:md-open "SHA4"))
178
179 \f
180 ;;;;;;;;;;;;;;;;;;;;;;;;;
181 ;;; Symmetric ciphers ;;;
182 ;;;;;;;;;;;;;;;;;;;;;;;;;
183 (unless (ffi-find-named-type 'gcry_cipher_hd_t)
184   (define-ffi-type gcry_cipher_hd_t (pointer void)))
185
186 (defun gcry:cipher-handle-p (sc-handle)
187   "Return non-`nil' iff SC-HANDLE is a valid handle for symmetric ciphers."
188   (and (ffi-object-p sc-handle)
189        (eq (ffi-object-type sc-handle) 'gcry_cipher_hd_t)))
190
191 (ffi-enum gcry:cipher_flags
192   "Flags used with the open function."
193   gcry:cipher_flag_empty
194   gcry:cipher_flag_secure
195   gcry:cipher_flag_enable_sync
196   gcry:cipher_flag_cbc_cts
197   gcry:cipher_flag_cbc_mac)
198
199 (ffi-enum gcry:cipher_modes
200   "Supported encryption modes.
201 Not all of them are supported for each algorithm."
202   none
203   ecb
204   cfb
205   cbc
206   stream
207   ofb
208   ctr)
209
210 (defconst gcry:cipher_open
211   (ffi-defun '(function int gcry_cipher_hd_t int int unsigned-int)
212              "gcry_cipher_open")
213   "Return a handle for symmetric ciphers.")
214
215 ;;;###autoload
216 (defun gcry:cipher-open (cipher-algo &optional mode)
217   "Return a symmetric cipher handle, initialised by CIPHER-ALGO."
218   (let ((sc-handle (make-ffi-object '(pointer void)))
219         (sc-number (if cipher-algo
220                        (gcry:cipher-map-name cipher-algo)
221                      0)))
222     (when (positivep sc-number)
223       (let ((g-hd (ffi-address-of sc-handle))
224             (g-algo (ffi-create-fo 'int sc-number))
225             (g-mode (or (gcry:cipher_modes mode)
226                        (gcry:cipher_modes 'none)))
227             (g-flags (gcry:cipher_flags 'gcry:cipher_flag_empty)))
228         (let ((ret (ffi-get
229                     (ffi-call-function
230                      gcry:cipher_open g-hd g-algo g-mode g-flags)))
231               (hd (ffi-get g-hd)))
232           (when (ffi-null-p hd)
233             (error "gcry:cipher-open: Cannot get initial cipher handle"))
234           (and (zerop ret)
235                (ffi-set-object-type sc-handle 'gcry_cipher_hd_t)
236                (put sc-handle 'cipher-algo g-algo)
237                (put sc-handle 'cipher-mode g-mode)
238                sc-handle))))))
239
240 (defalias 'gcry:make-symmetric-cipher #'gcry:cipher-open)
241
242 (defconst gcry:cipher_close
243   (ffi-defun '(function void gcry_cipher_hd_t)
244              "gcry_cipher_close")
245   "Destroy a handle for symmetric ciphers.")
246
247 (defmacro gcry:cipher-close (sc-handle)
248   "Free resources occupied by SC-HANDLE."
249   (when (gcry:cipher-handle-p (symbol-value sc-handle))
250     (ffi-call-function gcry:cipher_close (symbol-value sc-handle))
251     (set sc-handle nil)
252     t))
253
254 (defalias 'gcry:destroy-symmetric-cipher #'gcry:cipher-close)
255
256
257 (defconst gcry:cipher_map_name
258   (ffi-defun '(function int (pointer char))
259              "gcry_cipher_map_name")
260   "Return the enumeration value of a cipher algorithm.")
261
262 (defun gcry:cipher-map-name (string)
263   "Return the internal number of a cipher algorithm specified by STRING."
264   (let ((fo (ffi-create-fo 'c-string string)))
265     (ffi-get (ffi-call-function gcry:cipher_map_name fo))))
266
267 (defun gcry:cipher-algo-to-enum (cipher-algo)
268   "Return the internal form of CIPHER-ALGO."
269   (let ((g-enum (gcry:cipher-map-name cipher-algo)))
270     (unless (zerop g-enum)
271       (ffi-create-fo 'int g-enum))))
272 ;; (gcry:cipher-algo-to-enum "SHA512")
273 ;; (gcry:cipher-map-name "SHA512")
274 ;; (gcry:cipher-map-name "AES")
275
276 (ffi-enum gcry:ctl_cmds
277   "Enum of control commands."
278   gcryctl_set_key = 1
279   gcryctl_set_iv
280   gcryctl_cfb_sync
281   gcryctl_reset
282   gcryctl_finalize
283   gcryctl_get_keylen
284   gcryctl_get_blklen
285   gcryctl_test_algo
286   gcryctl_is_secure
287   gcryctl_get_asnoid
288   gcryctl_enable_algo
289   gcryctl_disable_algo
290   gcryctl_dump_random_stats
291   gcryctl_dump_secmem_stats
292   gcryctl_get_algo_npkey
293   gcryctl_get_algo_nskey
294   gcryctl_get_algo_nsign
295   gcryctl_get_algo_nencr
296   gcryctl_set_verbosity
297   gcryctl_set_debug_flags
298   gcryctl_clear_debug_flags
299   gcryctl_use_secure_rndpool
300   gcryctl_dump_memory_stats
301   gcryctl_init_secmem
302   gcryctl_term_secmem
303   gcryctl_disable_secmem_warn = 27
304   gcryctl_suspend_secmem_warn
305   gcryctl_resume_secmem_warn
306   gcryctl_drop_privs
307   gcryctl_enable_m_guard
308   gcryctl_start_dump
309   gcryctl_stop_dump
310   gcryctl_get_algo_usage
311   gcryctl_is_algo_enabled
312   gcryctl_disable_internal_locking
313   gcryctl_disable_secmem
314   gcryctl_initialization_finished
315   gcryctl_initialization_finished_p
316   gcryctl_any_initialization_p
317   gcryctl_set_cbc_cts
318   gcryctl_set_cbc_mac
319   gcryctl_set_ctr
320   gcryctl_enable_quick_random
321   gcryctl_set_random_seed_file
322   gcryctl_update_random_seed_file
323   gcryctl_set_thread_cbs
324   gcryctl_fast_poll)
325
326 (defconst gcry:cipher_ctl
327   (ffi-defun '(function int gcry_cipher_hd_t int (pointer void) unsigned-int)
328              "gcry_cipher_ctl")
329   "Generic cipher accessor.")
330
331 (defun gcry:cipher-setkey (sc-handle key)
332   "Set the key of SC-HANDLE to KEY."
333   (when (and (stringp key)
334              (gcry:cipher-handle-p sc-handle))
335     (let ((g-cmd (cdr (assq 'gcryctl_set_key gcry:ctl_cmds)))
336           (g-buffer (ffi-create-fo 'c-string key))
337           (g-nbytes (ffi-create-fo 'unsigned-int (length key))))
338       (let ((ret
339              (ffi-get
340               (ffi-call-function gcry:cipher_ctl
341                                  sc-handle g-cmd g-buffer g-nbytes))))
342         (when (zerop ret)
343           t)))))
344
345 (defun gcry:cipher-setiv (sc-handle iv)
346   "Set the initialisation vector of SC-HANDLE to IV."
347   (when (and (stringp iv)
348              (gcry:cipher-handle-p sc-handle))
349     (let ((g-cmd (cdr (assq 'gcryctl_set_iv gcry:ctl_cmds)))
350           (g-buffer (ffi-create-fo 'c-string iv))
351           (g-nbytes (ffi-create-fo 'unsigned-int (length iv))))
352       (let ((ret
353              (ffi-get
354               (ffi-call-function gcry:cipher_ctl
355                                  sc-handle g-cmd g-buffer g-nbytes))))
356         (when (zerop ret)
357           t)))))
358
359 (defun gcry:padded-length (string &optional block-length)
360   "Return the length of STRING after correct padding to
361 BLOCK-LENGTH (defaults to 8)."
362   (let* ((blklen (or block-length 8))
363          (slen (length string))
364          (blks (1+ (div slen blklen)))
365          (plen (* blks blklen)))
366     plen))
367
368 (defun gcry:padded-string (string &optional block-length)
369   "Return the padded version of STRING after correct padding to
370 BLOCK-LENGTH (defaults to 8)."
371   (let* ((blklen (or block-length 8))
372          (padlen (gcry:padded-length string blklen))
373          (strlen (length string))
374          (defect (- padlen strlen))
375          (pad (make-string defect defect)))
376     (concat string pad)))
377 ;; (setq somestring "testffff")
378 ;; (gcry:padded-string somestring)
379
380 (defun gcry:unpadded-string (string)
381   "Return the unpadded version of STRING assumed a correct padding has
382 been applied."
383   (let* ((strlen (length string))
384          (padchr (char-to-int (aref string (1- strlen))))
385          ;; validate the padding
386          (first-padchr (when (and (positivep padchr)
387                                   (<= padchr strlen))
388                          (char-to-int (aref string (- strlen padchr)))))
389          (pad-valid-p (when first-padchr
390                         (= first-padchr padchr))))
391     (when pad-valid-p
392       (substring string 0 (- strlen padchr)))))
393 ;;(gcry:unpadded-string "abcd\0\0")
394
395 ;; encryption/decryption routines
396 (defconst gcry:cipher_encrypt
397   (ffi-defun '(function int
398                         gcry_cipher_hd_t c-data unsigned-int
399                         c-data unsigned-int)
400              "gcry_cipher_encrypt")
401   "Encrypt data under a cipher context.")
402
403 (defun gcry:cipher-encrypt (sc-handle plain)
404   "Encrypt PLAIN with the settings in SC-HANDLE and return the result."
405   (when (and (stringp plain)
406              (gcry:cipher-handle-p sc-handle))
407     (let* ((blklen (gcry:cipher-get-block-length (get sc-handle 'cipher-algo)))
408            ;; add openssl conform padding (gcrypt obviously does not care)
409            (plain (gcry:padded-string plain blklen))
410            (outlen (length plain)))
411
412       (let ((g-in (ffi-create-fo (cons 'c-data (1+ outlen)) plain))
413             (g-inlen (ffi-create-fo 'unsigned-int outlen))
414             (g-out (make-ffi-object (cons 'c-data outlen)))
415             (g-outlen (ffi-create-fo 'unsigned-int outlen)))
416         (let ((ret
417                (ffi-get
418                 (ffi-call-function gcry:cipher_encrypt
419                                    sc-handle g-out g-outlen g-in g-inlen))))
420           (when (zerop ret)
421             (ffi-get g-out)))))))
422
423 (defconst gcry:cipher_decrypt
424   (ffi-defun '(function int
425                         gcry_cipher_hd_t c-data unsigned-int
426                         c-data unsigned-int)
427              "gcry_cipher_decrypt")
428   "Decrypt data under a cipher context.")
429
430 (defun gcry:cipher-decrypt (sc-handle ciphered)
431   "Decrypt CIPHERED with the settings in SC-HANDLE and return the result."
432   (when (and (stringp ciphered)
433              (gcry:cipher-handle-p sc-handle))
434     (let* (;;(blklen
435            ;; (gcry:cipher-get-block-length (get sc-handle 'cipher-algo)))
436            (outlen (length ciphered)))
437       (let ((g-in (ffi-create-fo 'c-data ciphered))
438             (g-inlen (ffi-create-fo 'unsigned-int outlen))
439             (g-out (make-ffi-object (cons 'c-data outlen)))
440             (g-outlen (ffi-create-fo 'unsigned-int outlen)))
441         (let ((ret
442                (ffi-get
443                 (ffi-call-function gcry:cipher_decrypt
444                                    sc-handle g-out g-outlen g-in g-inlen))))
445           (when (zerop ret)
446             (gcry:unpadded-string
447              (ffi-fetch g-out 0 (cons 'c-data outlen)))))))))
448
449
450 (defconst gcry:cipher_algo_info
451   (ffi-defun '(function int int int (pointer void) (pointer unsigned-int))
452              "gcry_cipher_algo_info")
453   "Return information generically of a cipher algorithm.")
454
455 (defun gcry:cipher-algo-info (cipher-algo which-info)
456   "Return information on a specific CIPHER-ALGO.
457 WHICH-INFO must be one of 'gcryctl_get_keylen, 'gcryctl_get_blklen and
458 'gcryctl_test_algo."
459   (when (or (eq which-info 'gcryctl_get_keylen)
460             (eq which-info 'gcryctl_get_blklen)
461             (eq which-info 'gcryctl_test_algo))
462     (let ((g-what (cdr (assq which-info gcry:ctl_cmds)))
463           (g-algo (cond ((stringp cipher-algo)
464                         (ffi-create-fo 'int (gcry:cipher-map-name cipher-algo)))
465                        ((intp cipher-algo)
466                         (ffi-create-fo 'int cipher-algo))
467                        ((and (ffi-object-p cipher-algo)
468                              (eq (ffi-object-type cipher-algo) 'int))
469                         cipher-algo)))
470           (g-buffer (ffi-null-pointer))
471           (g-nbytes (make-ffi-object 'unsigned-int)))
472       (let* ((g-nbytes* (if (eq which-info 'gcryctl_test_algo)
473                            (ffi-null-pointer)
474                          (ffi-address-of g-nbytes)))
475              (ret
476               (ffi-get
477                (ffi-call-function gcry:cipher_algo_info
478                                   g-algo g-what g-buffer g-nbytes*))))
479         (when (zerop ret)
480           (if (eq which-info 'gcryctl_test_algo)
481               t
482             (ffi-get g-nbytes)))))))
483
484 ;; derived funs
485 (defun gcry:cipher-get-key-length (cipher)
486   "Return the key-length of CIPHER in bytes, or `nil' if an error
487 has occurred."
488   (when cipher
489     (gcry:cipher-algo-info cipher 'gcryctl_get_keylen)))
490
491 (defun gcry:cipher-get-block-length (cipher)
492   "Return the block-length of CIPHER in bytes, or `nil' if an error
493 has occurred."
494   (when cipher
495     (gcry:cipher-algo-info cipher 'gcryctl_get_blklen)))
496
497 (defun gcry:cipher-available-p (cipher)
498   "Return non-`nil' iff CIPHER is available for use."
499   (when cipher
500     (gcry:cipher-algo-info cipher 'gcryctl_test_algo)))
501
502 ;;(setq handle (gcry:cipher-open "AES256" 'ecb))
503 ;;(gcry:cipher-algo-info "AES256" 'gcryctl_get_blklen)
504 ;;(gcry:cipher-setkey handle "12345678901234567890123456789012")
505 ;;(gcry:cipher-setiv handle "1234567890123456")
506 ;;(gcry:cipher-available-p "AES256")
507 ;;(gcry:cipher-get-block-length (get handle 'cipher-algo))
508 ;;(setq enc (gcry:cipher-encrypt handle "Hallo dies ist ein Test-Text > 16 Z"))
509 ;;(setq dec (gcry:cipher-decrypt handle enc2))
510 ;;(gcry:cipher-close handle)
511
512 ;;(setq enc2 (ossl-encrypt 'AES-256-ECB "Hallo dies ist ein Test-Text > 16 Zeichen noch3" "12345678901234567890123456789012" "1234567890123456"))
513 ;;(setq dec2 (ossl-decrypt 'AES-256-ECB enc "12345678901234567890123456789012" "1234567890123456"))
514
515
516 \f
517 ;;;;;;;;;;;;;;;;;;;;;;;;;;
518 ;;; Asymmetric ciphers ;;;
519 ;;;;;;;;;;;;;;;;;;;;;;;;;;
520
521
522
523 (provide 'ffi-gcrypt)
524
525 ;;; ffi-gcrypt.el ends here