2939250e8ecfad25448f1a45f03942bb0a83e61e
[gnupg.git] / tests / openpgp / tofu.scm
1 #!/usr/bin/env gpgscm
2
3 ;; Copyright (C) 2016 g10 Code GmbH
4 ;;
5 ;; This file is part of GnuPG.
6 ;;
7 ;; GnuPG is free software; you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation; either version 3 of the License, or
10 ;; (at your option) any later version.
11 ;;
12 ;; GnuPG is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ;; GNU General Public License for more details.
16 ;;
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with this program; if not, see <http://www.gnu.org/licenses/>.
19
20 (load (with-path "defs.scm"))
21
22  ;; Redefine GPG without --always-trust and a fixed time.
23 (define GPG `(,(tool 'gpg) --no-permission-warning
24               --faked-system-time=1466684990))
25 (define GNUPGHOME (getenv "GNUPGHOME"))
26 (if (string=? "" GNUPGHOME)
27     (error "GNUPGHOME not set"))
28
29 (catch (skip "Tofu not supported")
30        (call-check `(,@GPG --trust-model=tofu --list-config)))
31
32 (define KEYS '("2183839A" "BC15C85A" "EE37CF96"))
33
34 ;; Import the test keys.
35 (call-check `(,@GPG --import ,(in-srcdir "tofu-keys.asc")))
36
37 ;; Make sure the keys are imported.
38 (for-each (lambda (keyid)
39             (catch (error "Missing key" keyid)
40                    (call-check `(,@GPG --list-keys ,keyid))))
41           KEYS)
42
43 ;; Get tofu policy for KEYID.  Any remaining arguments are simply
44 ;; passed to GPG.
45 ;;
46 ;; This function only supports keys with a single user id.
47 (define (getpolicy keyid . args)
48   (let ((policy
49          (list-ref (assoc "tfs" (gpg-with-colons
50                                  `(--trust-model=tofu --with-tofu-info
51                                    ,@args
52                                    --list-keys ,keyid))) 5)))
53     (unless (member policy '("auto" "good" "unknown" "bad" "ask"))
54             (error "Bad policy:" policy))
55     policy))
56
57 ;; Check that KEYID's tofu policy matches EXPECTED-POLICY.  Any
58 ;; remaining arguments are simply passed to GPG.
59 ;;
60 ;; This function only supports keys with a single user id.
61 (define (checkpolicy keyid expected-policy . args)
62   (let ((policy (apply getpolicy `(,keyid ,@args))))
63     (unless (string=? policy expected-policy)
64             (error keyid ": Expected policy to be" expected-policy
65                    "but got" policy))))
66
67 ;; Get the trust level for KEYID.  Any remaining arguments are simply
68 ;; passed to GPG.
69 ;;
70 ;; This function only supports keys with a single user id.
71 (define (gettrust keyid . args)
72   (let ((trust
73          (list-ref (assoc "pub" (gpg-with-colons
74                                  `(--trust-model=tofu
75                                    ,@args
76                                    --list-keys ,keyid))) 1)))
77     (unless (and (= 1 (string-length trust))
78                  (member (string-ref trust 0) (string->list "oidreqnmfuws-")))
79             (error "Bad trust value:" trust))
80     trust))
81
82 ;; Check that KEYID's trust level matches EXPECTED-TRUST.  Any
83 ;; remaining arguments are simply passed to GPG.
84 ;;
85 ;; This function only supports keys with a single user id.
86 (define (checktrust keyid expected-trust . args)
87   (let ((trust (apply gettrust `(,keyid ,@args))))
88     (unless (string=? trust expected-trust)
89             (error keyid ": Expected trust to be" expected-trust
90                    "but got" trust))))
91
92 ;; Set key KEYID's policy to POLICY.  Any remaining arguments are
93 ;; passed as options to gpg.
94 (define (setpolicy keyid policy . args)
95   (call-check `(,@GPG --trust-model=tofu ,@args
96                       --tofu-policy ,policy ,keyid)))
97
98 (info "Checking tofu policies and trust...")
99
100 ;; Carefully remove the TOFU db.
101 (catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
102
103 ;; Verify a message.  There should be no conflict and the trust
104 ;; policy should be set to auto.
105 (call-check `(,@GPG --trust-model=tofu
106                     --verify ,(in-srcdir "tofu-2183839A-1.txt")))
107
108 (checkpolicy "2183839A" "auto")
109 ;; Check default trust.
110 (checktrust "2183839A" "m")
111
112 ;; Trust should be derived lazily.  Thus, if the policy is set to
113 ;; auto and we change --tofu-default-policy, then the trust should
114 ;; change as well.  Try it.
115 (checktrust "2183839A" "f" '--tofu-default-policy=good)
116 (checktrust "2183839A" "-" '--tofu-default-policy=unknown)
117 (checktrust "2183839A" "n" '--tofu-default-policy=bad)
118
119 ;; Change the policy to something other than auto and make sure the
120 ;; policy and the trust are correct.
121 (for-each-p
122  "Setting a fixed policy..."
123  (lambda (policy)
124    (let ((expected-trust
125           (cond
126            ((string=? "good" policy) "f")
127            ((string=? "unknown" policy) "-")
128            (else "n"))))
129      (setpolicy "2183839A" policy)
130
131      ;; Since we have a fixed policy, the trust level shouldn't
132      ;; change if we change the default policy.
133      (for-each-p
134       ""
135       (lambda (default-policy)
136         (checkpolicy "2183839A" policy
137                      '--tofu-default-policy default-policy)
138         (checktrust "2183839A" expected-trust
139                     '--tofu-default-policy default-policy))
140       '("auto" "good" "unknown" "bad" "ask"))))
141  '("good" "unknown" "bad"))
142
143 ;; BC15C85A conflicts with 2183839A.  On conflict, this will set
144 ;; BC15C85A to ask.  If 2183839A is auto (it's not, it's bad), then
145 ;; it will be set to ask.
146 (call-check `(,@GPG --trust-model=tofu
147                     --verify ,(in-srcdir "tofu-BC15C85A-1.txt")))
148 (checkpolicy "BC15C85A" "ask")
149 (checkpolicy "2183839A" "bad")
150
151 ;; EE37CF96 conflicts with 2183839A and BC15C85A.  We change
152 ;; BC15C85A's policy to auto and leave 2183839A's policy at bad.
153 ;; This conflict should cause BC15C85A's policy to be changed to
154 ;; ask (since it is auto), but not affect 2183839A's policy.
155 (setpolicy "BC15C85A" "auto")
156 (checkpolicy "BC15C85A" "auto")
157 (call-check `(,@GPG --trust-model=tofu
158                     --verify ,(in-srcdir "tofu-EE37CF96-1.txt")))
159 (checkpolicy "BC15C85A" "ask")
160 (checkpolicy "2183839A" "bad")
161 (checkpolicy "EE37CF96" "ask")
162
163
164
165 ;; Check that we detect the following attack:
166 ;;
167 ;; Alice and Bob each have a key and cross sign them.  Bob then adds a
168 ;; new user id, "Alice".  TOFU should now detect a conflict, because
169 ;; Alice only signed Bob's "Bob" user id.
170
171 (display "Checking cross sigs...\n")
172 (define GPG `(,(tool 'gpg) --no-permission-warning
173               --faked-system-time=1476304861))
174
175 ;; Carefully remove the TOFU db.
176 (catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
177
178 (define DIR "tofu/cross-sigs")
179 ;; The test keys.
180 (define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E")
181 (define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247")
182
183 (define (verify-messages)
184   (for-each
185    (lambda (key)
186      (for-each
187       (lambda (i)
188         (let ((fn (in-srcdir DIR (string-append key "-" i ".txt"))))
189           (call-check `(,@GPG --trust-model=tofu --verify ,fn))))
190       (list "1" "2")))
191    (list KEYA KEYB)))
192
193 ;; Import the public keys.
194 (display "    > Two keys. ")
195 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYA "-1.gpg"))))
196 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-1.gpg"))))
197 ;; Make sure the tofu engine registers the keys.
198 (verify-messages)
199 (display "<\n")
200
201 ;; Since there is no conflict, the policy should be auto.
202 (checkpolicy KEYA "auto")
203 (checkpolicy KEYB "auto")
204
205 ;; Import the cross sigs.
206 (display "    > Adding cross signatures. ")
207 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYA "-2.gpg"))))
208 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-2.gpg"))))
209 (verify-messages)
210 (display "<\n")
211
212 ;; There is still no conflict, so the policy shouldn't have changed.
213 (checkpolicy KEYA "auto")
214 (checkpolicy KEYB "auto")
215
216 ;; Import the conflicting user id.
217 (display "    > Adding conflicting user id. ")
218 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-3.gpg"))))
219 (call-check `(,@GPG --trust-model=tofu
220                     --verify ,(in-srcdir DIR (string-append KEYB "-1.txt"))))
221 (verify-messages)
222 (display "<\n")
223
224 (checkpolicy KEYA "ask")
225 (checkpolicy KEYB "ask")
226
227 ;; Import Alice's signature on the conflicting user id.
228 (display "    > Adding cross signature on user id. ")
229 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-4.gpg"))))
230 (verify-messages)
231 (display "<\n")
232
233 (checkpolicy KEYA "auto")
234 (checkpolicy KEYB "auto")
235
236 ;; Remove the keys.
237 (call-check `(,@GPG --delete-key ,KEYA))
238 (call-check `(,@GPG --delete-key ,KEYB))
239
240
241 ;; Check that we detect the following attack:
242 ;;
243 ;; Alice has an ultimately trusted key and she signs Bob's key.  Then
244 ;; Bob adds a new user id, "Alice".  TOFU should now detect a
245 ;; conflict, because Alice only signed Bob's "Bob" user id.
246
247 (display "Checking UTK sigs...\n")
248 (define GPG `(,(tool 'gpg) --no-permission-warning
249               --faked-system-time=1476304861))
250
251 ;; Carefully remove the TOFU db.
252 (catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
253
254 (define DIR "tofu/cross-sigs")
255 ;; The test keys.
256 (define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E")
257 (define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247")
258
259 (define (verify-messages)
260   (for-each
261    (lambda (key)
262      (for-each
263       (lambda (i)
264         (let ((fn (in-srcdir DIR (string-append key "-" i ".txt"))))
265           (call-check `(,@GPG --trust-model=tofu --verify ,fn))))
266       (list "1" "2")))
267    (list KEYA KEYB)))
268
269 ;; Import the public keys.
270 (display "    > Two keys. ")
271 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYA "-1.gpg"))))
272 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-1.gpg"))))
273 (display "<\n")
274
275 ;; Import the cross sigs.
276 (display "    > Adding cross signatures. ")
277 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYA "-2.gpg"))))
278 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-2.gpg"))))
279 (display "<\n")
280
281 ;; Make KEYA ultimately trusted.
282 (display (string-append "    > Marking " KEYA " as ultimately trusted. "))
283 (pipe:do
284  (pipe:echo (string-append KEYA ":6:\n"))
285  (pipe:gpg `(--import-ownertrust)))
286 (display "<\n")
287
288 ;; An ultimately trusted key's policy is good.
289 (checkpolicy KEYA "good")
290 ;; A key signed by a UTK for which there is no policy gets the default
291 ;; policy of good.
292 (checkpolicy KEYB "good")
293
294 ;; Import the conflicting user id.
295 (display "    > Adding conflicting user id. ")
296 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-3.gpg"))))
297 (call-check `(,@GPG --trust-model=tofu
298                     --verify ,(in-srcdir DIR (string-append KEYB "-1.txt"))))
299 (verify-messages)
300 (display "<\n")
301
302 (checkpolicy KEYA "good")
303 (checkpolicy KEYB "ask")
304
305 ;; Import Alice's signature on the conflicting user id.
306 (display "    > Adding cross signature on user id. ")
307 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYB "-4.gpg"))))
308 (verify-messages)
309 (display "<\n")
310
311 (checkpolicy KEYA "good")
312 (checkpolicy KEYB "good")
313
314 ;; Remove the keys.
315 (call-check `(,@GPG --delete-key ,KEYA))
316 (call-check `(,@GPG --delete-key ,KEYB))