summaryrefslogtreecommitdiff
path: root/guile/email-key-rotation/openssl.scm
blob: 418056bf40796f99d29ada8f87f9004af5d83e17 (plain)
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
(define-module (email-key-rotation openssl)
  #:use-module (ice-9 exceptions)
  #:use-module (ice-9 rdelim)
  #:use-module (ice-9 textual-ports)
  #:use-module (ice-9 match)
  #:export (current-openssl-binary
	    generate-key
	    private-key->public-key
	    random-hex)
  #:declarative? #t
  #:duplicates (merge-generics))

(define current-openssl-binary
  (make-parameter
   "/usr/bin/openssl"
   (match-lambda
     ((? string? interpreter)
      (lambda (arguments)
	(with-exception-handler
	    (lambda (exn)
	      (raise-exception
	       (make-exception
		(make-exception-with-irritants (list interpreter arguments))
		(make-exception-with-message
		 "cannot run openssl:")
		exn)))
	  (lambda ()
	    (let ((ret (apply system* interpreter arguments)))
	      (unless (eqv? 0 (status:exit-val ret))
		(raise-exception
		 (make-exception
		  (make-error)
		  (make-exception-with-irritants (list (status:exit-val ret)))
		  (make-exception-with-message
		   (format #f "openssl failed with exit code ~s."
			   (status:exit-val ret)))))))))))
     ((? procedure? p)
      p))))

(define (call-openssl . args)
  ((current-openssl-binary) args))

(define (generate-key rsa-strength)
  (with-exception-handler
      (lambda (exn)
	(raise-exception
	 (make-exception
	  (make-exception-with-origin 'generate-key)
	  (make-exception-with-irritants (list rsa-strength))
	  (make-exception-with-message
	   "cannot generate a private key.")
	  exn)))
    (lambda ()
      (let ((port (mkstemp "/tmp/generate-openssl-key-XXXXXX"))
	    (openssl (current-openssl-binary)))
	(dynamic-wind
	  (lambda () #t)
	  (lambda ()
	    (chmod port #o600)
	    (call-openssl
	     "genrsa"
	     "-out" (port-filename port)
	     (number->string rsa-strength))
	    (call-with-input-file (port-filename port)
	      get-string-all))
	  (lambda ()
	    (delete-file (port-filename port))))))))

(define (private-key->public-key key)
  (with-exception-handler
      (lambda (exn)
	(raise-exception
	 (make-exception
	  (make-exception-with-origin 'private-key->public-key)
	  (make-exception-with-irritants (list key))
	  (make-exception-with-message
	   "cannot convert the private key to public key.")
	  exn)))
    (lambda ()
      (let ((input-port (mkstemp "/tmp/openssl-private-key-XXXXXX"))
	    (output-port (mkstemp "/tmp/openssl-public-key-XXXXXX"))
	    (openssl (current-openssl-binary)))
	(dynamic-wind
	  (lambda () #t)
	  (lambda ()
	    (chmod input-port #o600)
	    (chmod output-port #o600)
	    (call-with-output-file (port-filename input-port)
	      (lambda (init-port)
		(display key init-port)))
	    (call-openssl
	     "rsa"
	     "-in" (port-filename input-port)
	     "-out" (port-filename output-port)
	     "-pubout")
	    (call-with-input-file (port-filename output-port)
	      get-string-all))
	  (lambda ()
	    (delete-file (port-filename input-port))
	    (delete-file (port-filename output-port))))))))

(define (random-hex nbytes)
  (with-exception-handler
      (lambda (exn)
	(raise-exception
	 (make-exception
	  (make-exception-with-origin 'random-hex)
	  (make-exception-with-irritants (list nbytes))
	  (make-exception-with-message
	   (format #f "cannot generate ~a random bytes."
		   nbytes))
	  exn)))
    (lambda ()
      (let ((output-port (mkstemp "/tmp/openssl-public-key-XXXXXX"))
	    (openssl (current-openssl-binary)))
	(dynamic-wind
	  (lambda () #t)
	  (lambda ()
	    (chmod output-port #o600)
	    (call-openssl
	     "rand"
	     "-out" (port-filename output-port)
	     "-hex"
	     (number->string nbytes))
	    (call-with-input-file (port-filename output-port)
	      get-string-all))
	  (lambda ()
	    (delete-file (port-filename output-port))))))))