1 ;;; ffi-gcrypt.el -- FFI access to libgcrypt
3 ;; Copyright (C) 2005, 2006 Sebastian Freundt
4 ;; Author: Sebastian Freundt <hroptatyr@sxemacs.org>
7 ;; Maintainer: Sebastian Freundt <hroptatyr@sxemacs.org>
8 ;; Keywords: cryptography, security
10 ;; This file is part of SXEmacs.
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.
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.
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/>.
26 ;; This is an FFI implementation of routines in the libgcrypt library.
33 ;; this is our spine, barf if it does not exist
34 (ffi-load "libgcrypt")
36 (defgroup ffi-gcrypt nil
37 "FFI bindings for libgcrypt (part of GPG)."
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)))
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)))
52 (ffi-enum gcry:md_flags
53 "Flags used with the open function."
58 (defconst gcry:md_open
59 (ffi-defun '(function int (pointer gcry_md_hd_t) int unsigned-int)
61 "Return a handle for message digests.")
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)
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)))
74 (ffi-call-function gcry:md_open g-hd g-algo g-flags)))
77 (error "gcry:md-open: Cannot get initial MD handle"))
79 (ffi-set-object-type md-handle 'gcry_md_hd_t)
82 (defalias 'gcry:make-message-digest #'gcry:md-open)
84 (defconst gcry:md_close
85 (ffi-defun '(function void gcry_md_hd_t)
87 "Destroy a handle for message digests.")
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))
96 (defalias 'gcry:destroy-message-digest #'gcry:md-close)
98 (defconst gcry:md_enable
99 (ffi-defun '(function int gcry_md_hd_t int)
101 "Enable hash-algorithm within a message digest context.")
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)
108 (ffi-call-function gcry:md_enable md-handle g-algo))))
111 (defconst gcry:md_map_name
112 (ffi-defun '(function int (pointer char))
114 "Return the enumeration value of a hash algorithm.")
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))))
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")
131 (defconst gcry:md_write
132 (ffi-defun '(function void gcry_md_hd_t (pointer void) unsigned-int)
134 "Write data into message digest context.")
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)
143 ;; (gcry:md-write handle "kfjf")
144 ;;(ffi-create-fo 'c-string "sifistring")
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.")
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))))
156 (defconst gcry:md_read
157 (ffi-defun '(function c-data gcry_md_hd_t int)
159 "Return the message digest.")
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))))))
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"))
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)))
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)))
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)
199 (ffi-enum gcry:cipher_modes
200 "Supported encryption modes.
201 Not all of them are supported for each algorithm."
210 (defconst gcry:cipher_open
211 (ffi-defun '(function int gcry_cipher_hd_t int int unsigned-int)
213 "Return a handle for symmetric ciphers.")
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)
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)))
230 gcry:cipher_open g-hd g-algo g-mode g-flags)))
232 (when (ffi-null-p hd)
233 (error "gcry:cipher-open: Cannot get initial cipher handle"))
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)
240 (defalias 'gcry:make-symmetric-cipher #'gcry:cipher-open)
242 (defconst gcry:cipher_close
243 (ffi-defun '(function void gcry_cipher_hd_t)
245 "Destroy a handle for symmetric ciphers.")
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))
254 (defalias 'gcry:destroy-symmetric-cipher #'gcry:cipher-close)
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.")
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))))
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")
276 (ffi-enum gcry:ctl_cmds
277 "Enum of control commands."
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
303 gcryctl_disable_secmem_warn = 27
304 gcryctl_suspend_secmem_warn
305 gcryctl_resume_secmem_warn
307 gcryctl_enable_m_guard
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
320 gcryctl_enable_quick_random
321 gcryctl_set_random_seed_file
322 gcryctl_update_random_seed_file
323 gcryctl_set_thread_cbs
326 (defconst gcry:cipher_ctl
327 (ffi-defun '(function int gcry_cipher_hd_t int (pointer void) unsigned-int)
329 "Generic cipher accessor.")
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))))
340 (ffi-call-function gcry:cipher_ctl
341 sc-handle g-cmd g-buffer g-nbytes))))
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))))
354 (ffi-call-function gcry:cipher_ctl
355 sc-handle g-cmd g-buffer g-nbytes))))
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)))
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)
380 (defun gcry:unpadded-string (string)
381 "Return the unpadded version of STRING assumed a correct padding has
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)
388 (char-to-int (aref string (- strlen padchr)))))
389 (pad-valid-p (when first-padchr
390 (= first-padchr padchr))))
392 (substring string 0 (- strlen padchr)))))
393 ;;(gcry:unpadded-string "abcd
\0\0")
395 ;; encryption/decryption routines
396 (defconst gcry:cipher_encrypt
397 (ffi-defun '(function int
398 gcry_cipher_hd_t c-data unsigned-int
400 "gcry_cipher_encrypt")
401 "Encrypt data under a cipher context.")
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)))
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)))
418 (ffi-call-function gcry:cipher_encrypt
419 sc-handle g-out g-outlen g-in g-inlen))))
421 (ffi-get g-out)))))))
423 (defconst gcry:cipher_decrypt
424 (ffi-defun '(function int
425 gcry_cipher_hd_t c-data unsigned-int
427 "gcry_cipher_decrypt")
428 "Decrypt data under a cipher context.")
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))
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)))
443 (ffi-call-function gcry:cipher_decrypt
444 sc-handle g-out g-outlen g-in g-inlen))))
446 (gcry:unpadded-string
447 (ffi-fetch g-out 0 (cons 'c-data outlen)))))))))
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.")
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
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)))
466 (ffi-create-fo 'int cipher-algo))
467 ((and (ffi-object-p cipher-algo)
468 (eq (ffi-object-type cipher-algo) 'int))
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)
474 (ffi-address-of g-nbytes)))
477 (ffi-call-function gcry:cipher_algo_info
478 g-algo g-what g-buffer g-nbytes*))))
480 (if (eq which-info 'gcryctl_test_algo)
482 (ffi-get g-nbytes)))))))
485 (defun gcry:cipher-get-key-length (cipher)
486 "Return the key-length of CIPHER in bytes, or `nil' if an error
489 (gcry:cipher-algo-info cipher 'gcryctl_get_keylen)))
491 (defun gcry:cipher-get-block-length (cipher)
492 "Return the block-length of CIPHER in bytes, or `nil' if an error
495 (gcry:cipher-algo-info cipher 'gcryctl_get_blklen)))
497 (defun gcry:cipher-available-p (cipher)
498 "Return non-`nil' iff CIPHER is available for use."
500 (gcry:cipher-algo-info cipher 'gcryctl_test_algo)))
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)
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"))
517 ;;;;;;;;;;;;;;;;;;;;;;;;;;
518 ;;; Asymmetric ciphers ;;;
519 ;;;;;;;;;;;;;;;;;;;;;;;;;;
523 (provide 'ffi-gcrypt)
525 ;;; ffi-gcrypt.el ends here