g10: If the set of UTKs changes, invalidate any changed policies.
[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 (setup-environment)
22
23 ;; Redefine GPG without --always-trust and a fixed time.
24 (define GPG `(,(tool 'gpg) --no-permission-warning
25               --faked-system-time=1466684990))
26 (define GNUPGHOME (getenv "GNUPGHOME"))
27 (if (string=? "" GNUPGHOME)
28     (error "GNUPGHOME not set"))
29
30 (catch (skip "Tofu not supported")
31        (call-check `(,@GPG --trust-model=tofu --list-config)))
32
33 (define KEYS '("2183839A" "BC15C85A" "EE37CF96"))
34
35 ;; Import the test keys.
36 (call-check `(,@GPG --import ,(in-srcdir "tofu-keys.asc")))
37
38 ;; Make sure the keys are imported.
39 (for-each (lambda (keyid)
40             (catch (error "Missing key" keyid)
41                    (call-check `(,@GPG --list-keys ,keyid))))
42           KEYS)
43
44 ;; Get tofu policy for KEYID.  Any remaining arguments are simply
45 ;; passed to GPG.
46 ;;
47 ;; This function only supports keys with a single user id.
48 (define (getpolicy keyid . args)
49   (let ((policy
50          (list-ref (assoc "tfs" (gpg-with-colons
51                                  `(--trust-model=tofu --with-tofu-info
52                                    ,@args
53                                    --list-keys ,keyid))) 5)))
54     (unless (member policy '("auto" "good" "unknown" "bad" "ask"))
55             (error "Bad policy:" policy))
56     policy))
57
58 ;; Check that KEYID's tofu policy matches EXPECTED-POLICY.  Any
59 ;; remaining arguments are simply passed to GPG.
60 ;;
61 ;; This function only supports keys with a single user id.
62 (define (checkpolicy keyid expected-policy . args)
63   (let ((policy (apply getpolicy `(,keyid ,@args))))
64     (unless (string=? policy expected-policy)
65             (error keyid ": Expected policy to be" expected-policy
66                    "but got" policy))))
67
68 ;; Get the trust level for KEYID.  Any remaining arguments are simply
69 ;; passed to GPG.
70 ;;
71 ;; This function only supports keys with a single user id.
72 (define (gettrust keyid . args)
73   (let ((trust
74          (list-ref (assoc "pub" (gpg-with-colons
75                                  `(--trust-model=tofu
76                                    ,@args
77                                    --list-keys ,keyid))) 1)))
78     (unless (and (= 1 (string-length trust))
79                  (member (string-ref trust 0) (string->list "oidreqnmfuws-")))
80             (error "Bad trust value:" trust))
81     trust))
82
83 ;; Check that KEYID's trust level matches EXPECTED-TRUST.  Any
84 ;; remaining arguments are simply passed to GPG.
85 ;;
86 ;; This function only supports keys with a single user id.
87 (define (checktrust keyid expected-trust . args)
88   (let ((trust (apply gettrust `(,keyid ,@args))))
89     (unless (string=? trust expected-trust)
90             (error keyid ": Expected trust to be" expected-trust
91                    "but got" trust))))
92
93 ;; Set key KEYID's policy to POLICY.  Any remaining arguments are
94 ;; passed as options to gpg.
95 (define (setpolicy keyid policy . args)
96   (call-check `(,@GPG --trust-model=tofu ,@args
97                       --tofu-policy ,policy ,keyid)))
98
99 (info "Checking tofu policies and trust...")
100
101 ;; Carefully remove the TOFU db.
102 (catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
103
104 ;; Verify a message.  There should be no conflict and the trust
105 ;; policy should be set to auto.
106 (call-check `(,@GPG --trust-model=tofu
107                     --verify ,(in-srcdir "tofu-2183839A-1.txt")))
108
109 (checkpolicy "2183839A" "auto")
110 ;; Check default trust.
111 (checktrust "2183839A" "m")
112
113 ;; Trust should be derived lazily.  Thus, if the policy is set to
114 ;; auto and we change --tofu-default-policy, then the trust should
115 ;; change as well.  Try it.
116 (checktrust "2183839A" "f" '--tofu-default-policy=good)
117 (checktrust "2183839A" "-" '--tofu-default-policy=unknown)
118 (checktrust "2183839A" "n" '--tofu-default-policy=bad)
119
120 ;; Change the policy to something other than auto and make sure the
121 ;; policy and the trust are correct.
122 (for-each-p
123  "Setting a fixed policy..."
124  (lambda (policy)
125    (let ((expected-trust
126           (cond
127            ((string=? "good" policy) "f")
128            ((string=? "unknown" policy) "-")
129            (else "n"))))
130      (setpolicy "2183839A" policy)
131
132      ;; Since we have a fixed policy, the trust level shouldn't
133      ;; change if we change the default policy.
134      (for-each-p
135       ""
136       (lambda (default-policy)
137         (checkpolicy "2183839A" policy
138                      '--tofu-default-policy default-policy)
139         (checktrust "2183839A" expected-trust
140                     '--tofu-default-policy default-policy))
141       '("auto" "good" "unknown" "bad" "ask"))))
142  '("good" "unknown" "bad"))
143
144 ;; At the end, 2183839A's policy should be bad.
145 (checkpolicy "2183839A" "bad")
146
147 ;; BC15C85A and 2183839A conflict.  A policy setting of "auto"
148 ;; (BC15C85A's state) will result in an effective policy of ask.  But,
149 ;; a policy setting of "bad" will result in an effective policy of
150 ;; bad.
151 (setpolicy "BC15C85A" "auto")
152 (checkpolicy "BC15C85A" "ask")
153 (checkpolicy "2183839A" "bad")
154
155 ;; EE37CF96, 2183839A, and BC15C85A conflict.  We change BC15C85A's
156 ;; policy to auto and leave 2183839A's policy at bad.  This conflict
157 ;; should cause BC15C85A's policy to be changed to ask (since it is
158 ;; auto), but not affect 2183839A's policy.
159 (setpolicy "BC15C85A" "auto")
160 (checkpolicy "BC15C85A" "ask")
161 (call-check `(,@GPG --trust-model=tofu
162                     --verify ,(in-srcdir "tofu-EE37CF96-1.txt")))
163 (checkpolicy "BC15C85A" "ask")
164 (checkpolicy "2183839A" "bad")
165 (checkpolicy "EE37CF96" "ask")
166
167
168
169 ;; Check that we detect the following attack:
170 ;;
171 ;; Alice and Bob each have a key and cross sign them.  Bob then adds a
172 ;; new user id, "Alice".  TOFU should now detect a conflict, because
173 ;; Alice only signed Bob's "Bob" user id.
174
175 (display "Checking cross sigs...\n")
176 (define GPG `(,(tool 'gpg) --no-permission-warning
177               --faked-system-time=1476304861))
178
179 ;; Carefully remove the TOFU db.
180 (catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
181
182 (define DIR "tofu/cross-sigs")
183 ;; The test keys.
184 (define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E")
185 (define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247")
186 (define KEYIDA (substring KEYA (- (string-length KEYA) 8)))
187 (define KEYIDB (substring KEYB (- (string-length KEYB) 8)))
188
189 (define (verify-messages)
190   (for-each
191    (lambda (key)
192      (for-each
193       (lambda (i)
194         (let ((fn (in-srcdir DIR (string-append key "-" i ".txt"))))
195           (call-check `(,@GPG --trust-model=tofu --verify ,fn))))
196       (list "1" "2")))
197    (list KEYIDA KEYIDB)))
198
199 ;; Import the public keys.
200 (display "    > Two keys. ")
201 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-1.gpg"))))
202 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-1.gpg"))))
203 ;; Make sure the tofu engine registers the keys.
204 (verify-messages)
205 (display "<\n")
206
207 ;; Since there is no conflict, the policy should be auto.
208 (checkpolicy KEYA "auto")
209 (checkpolicy KEYB "auto")
210
211 ;; Import the cross sigs.
212 (display "    > Adding cross signatures. ")
213 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-2.gpg"))))
214 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-2.gpg"))))
215 (verify-messages)
216 (display "<\n")
217
218 ;; There is still no conflict, so the policy shouldn't have changed.
219 (checkpolicy KEYA "auto")
220 (checkpolicy KEYB "auto")
221
222 ;; Import the conflicting user id.
223 (display "    > Adding conflicting user id. ")
224 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-3.gpg"))))
225 (verify-messages)
226 (display "<\n")
227
228 (checkpolicy KEYA "ask")
229 (checkpolicy KEYB "ask")
230
231 ;; Import Alice's signature on the conflicting user id.  Since there
232 ;; is now a cross signature, we should revert to the default policy.
233 (display "    > Adding cross signature on user id. ")
234 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
235 (verify-messages)
236 (display "<\n")
237
238 (checkpolicy KEYA "auto")
239 (checkpolicy KEYB "auto")
240
241 ;; Remove the keys.
242 (call-check `(,@GPG --delete-key ,KEYA))
243 (call-check `(,@GPG --delete-key ,KEYB))
244
245
246 ;; Check that we detect the following attack:
247 ;;
248 ;; Alice has an ultimately trusted key and she signs Bob's key.  Then
249 ;; Bob adds a new user id, "Alice".  TOFU should now detect a
250 ;; conflict, because Alice only signed Bob's "Bob" user id.
251 ;;
252 ;;
253 ;; The Alice key:
254 ;;   pub   rsa2048 2016-10-11 [SC]
255 ;;         1938C3A0E4674B6C217AC0B987DB2814EC38277E
256 ;;   uid           [ultimate] Spy Cow <spy@cow.com>
257 ;;   sub   rsa2048 2016-10-11 [E]
258 ;;
259 ;; The Bob key:
260 ;;
261 ;;   pub   rsa2048 2016-10-11 [SC]
262 ;;         DC463A16E42F03240D76E8BA8B48C6BD871C2247
263 ;;   uid           [  full  ] Spy R. Cow <spy@cow.com>
264 ;;   uid           [  full  ] Spy R. Cow <spy@cow.de>
265 ;;   sub   rsa2048 2016-10-11 [E]
266
267 (display "Checking UTK sigs...\n")
268 (define GPG `(,(tool 'gpg) --no-permission-warning
269               --faked-system-time=1476304861))
270
271 ;; Carefully remove the TOFU db.
272 (catch '() (unlink (string-append GNUPGHOME "/tofu.db")))
273
274 (define DIR "tofu/cross-sigs")
275 ;; The test keys.
276 (define KEYA "1938C3A0E4674B6C217AC0B987DB2814EC38277E")
277 (define KEYB "DC463A16E42F03240D76E8BA8B48C6BD871C2247")
278 (define KEYIDA (substring KEYA (- (string-length KEYA) 8)))
279 (define KEYIDB (substring KEYB (- (string-length KEYB) 8)))
280
281 (define (verify-messages)
282   (for-each
283    (lambda (key)
284      (for-each
285       (lambda (i)
286         (let ((fn (in-srcdir DIR (string-append key "-" i ".txt"))))
287           (call-check `(,@GPG --trust-model=tofu --verify ,fn))))
288       (list "1" "2")))
289    (list KEYIDA KEYIDB)))
290
291 ;; Import the public keys.
292 (display "    > Two keys. ")
293 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-1.gpg"))))
294 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-1.gpg"))))
295 (display "<\n")
296
297 (checkpolicy KEYA "auto")
298 (checkpolicy KEYB "auto")
299
300 ;; Import the cross sigs.
301 (display "    > Adding cross signatures. ")
302 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-2.gpg"))))
303 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-2.gpg"))))
304 (display "<\n")
305
306 (checkpolicy KEYA "auto")
307 (checkpolicy KEYB "auto")
308
309 ;; Make KEYA ultimately trusted.
310 (display (string-append "    > Marking " KEYA " as ultimately trusted. "))
311 (pipe:do
312  (pipe:echo (string-append KEYA ":6:\n"))
313  (pipe:gpg `(--import-ownertrust)))
314 (display "<\n")
315
316 ;; An ultimately trusted key's policy is good.
317 (checkpolicy KEYA "good")
318 ;; A key signed by a UTK for which there is no policy gets the default
319 ;; policy of good.
320 (checkpolicy KEYB "good")
321
322 ;; Import the conflicting user id.
323 (display "    > Adding conflicting user id. ")
324 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-3.gpg"))))
325 (verify-messages)
326 (display "<\n")
327
328 (checkpolicy KEYA "good")
329 (checkpolicy KEYB "ask")
330
331 ;; Import Alice's signature on the conflicting user id.
332 (display "    > Adding cross signature on user id. ")
333 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
334 (verify-messages)
335 (display "<\n")
336
337 (checkpolicy KEYA "good")
338 (checkpolicy KEYB "good")
339
340 ;; Remove the keys.
341 (call-check `(,@GPG --delete-key ,KEYA))
342 (call-check `(,@GPG --delete-key ,KEYB))