Translated using Weblate (Chinese (Simplified))
[cygwin-setup.git] / crypto.cc
bloba837f8da251c4328f3c74ca1c96cc65e3df302a6
1 /*
2 * Copyright (c) 2008, Dave Korn.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
12 * Written by Dave Korn <dave.korn.cygwin@gmail.com>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <vector>
20 #include "io_stream.h"
21 #include "crypto.h"
22 #include "compress.h"
23 #include "gcrypt.h"
24 #include "msg.h"
25 #include "LogSingleton.h"
26 #include "resource.h"
27 #include "getopt++/StringArrayOption.h"
28 #include "getopt++/BoolOption.h"
29 #include "KeysSetting.h"
30 #include "gpg-packet.h"
31 #include "geturl.h"
33 #ifndef CRYPTODEBUGGING
34 #define CRYPTODEBUGGING (0)
35 #endif
37 #define ERRKIND note
38 #if CRYPTODEBUGGING
39 #define MESSAGE LogBabblePrintf
40 #else
41 #define MESSAGE while (0) LogBabblePrintf
42 #endif
44 /* Command-line options for specifying and controlling extra keys. */
45 static StringArrayOption ExtraKeyOption ('K', "pubkey", IDS_HELPTEXT_PUBKEY);
46 static StringArrayOption SexprExtraKeyOption ('S', "sexpr-pubkey", IDS_HELPTEXT_SEXPR_PUBKEY);
47 static BoolOption UntrustedKeysOption (false, 'u', "untrusted-keys", IDS_HELPTEXT_UNTRUSTED_KEYS);
48 static BoolOption KeepUntrustedKeysOption (false, 'U', "keep-untrusted-keys", IDS_HELPTEXT_KEEP_UNTRUSTED_KEYS);
49 static BoolOption EnableOldKeysOption (false, '\0', "old-keys", IDS_HELPTEXT_OLD_KEYS,
50 BoolOption::BoolOptionType::pairedAble);
52 /* Embedded public half of Cygwin signing key. */
53 static const char *cygwin_pubkey_sexpr =
54 #include "cyg-pubkey.h"
57 static const char *cygwin_old_pubkey_sexpr =
58 #include "cyg-old-pubkey.h"
61 /* S-expr template for DSA pubkey. */
62 static const char *dsa_pubkey_templ = "(public-key (dsa (p %m) (q %m) (g %m) (y %m)))";
64 /* S-expr template for RSA pubkey. */
65 static const char *rsa_pubkey_templ = "(public-key (rsa (n %m) (e %m)))";
67 /* S-expr template for DSA signature. */
68 static const char *dsa_sig_templ = "(sig-val (dsa (r %m) (s %m)))";
70 /* S-expr template for RSA signature. */
71 static const char *rsa_sig_templ = "(sig-val (rsa (s %m)))";
73 /* S-expr template for DSA data block to be signed. */
74 static const char *dsa_data_hash_templ = "(data (flags raw) (hash %s %b))";
76 /* S-expr template for RSA data block to be signed. */
77 static const char *rsa_data_hash_templ = "(data (flags pkcs1) (hash %s %b))";
79 /* Information on a key to try */
80 struct key_info
82 key_info(std::string _name, bool _builtin, gcry_sexp_t _key, bool _owned=true) :
83 name(_name), builtin(_builtin), key(_key), owned(_owned)
87 std::string name;
88 bool builtin; // if true, we don't need to retain this key with add_key_from_sexpr()
89 gcry_sexp_t key;
90 bool owned; // if true, we own this key and should use gcry_sexp_release() on it
93 /* User context data for sig packet walk. */
94 struct sig_data
96 /* MPI values of sig components. */
97 gcry_mpi_t dsa_mpi_r, dsa_mpi_s;
98 gcry_mpi_t rsa_mpi_s;
100 /* Hash context. */
101 gcry_md_hd_t md;
103 /* Main data. */
104 io_stream *sign_data;
106 /* Auxiliary data. */
107 int sig_type;
108 int pk_alg;
109 int hash_alg;
111 /* Converted algo code. */
112 int algo;
114 /* Keys */
115 std::vector<struct key_info> *keys_to_try;
117 /* Final status. */
118 bool valid;
121 /* User context data for key packet walk. */
122 struct key_data
124 std::vector<gcry_sexp_t> keys;
127 /* Callback hook for walking packets in gpg key file. Extracts
128 the key coefficients from any public key packets encountered and
129 converts them into s-expr pubkey format, returning the public
130 keys thus found to the caller in a vector in the userdata context. */
131 static enum
132 pkt_cb_resp key_file_walker (struct packet_walker *wlk, unsigned char tag,
133 size_t packetsize, size_t hdrpos)
135 struct key_data *kdat = (struct key_data *)(wlk->userdata);
137 MESSAGE ("key packet %d size %d at offs $%04x kdat $%08x\n", tag,
138 packetsize, hdrpos, kdat);
140 if (tag != RFC4880_PT_PUBLIC_KEY)
141 return pktCONTINUE;
143 // So, get the data out. Version is first. In case of any errors during
144 // parsing, we just discard the key and continue, hoping to find a good one.
145 char ver = pkt_getch (wlk->pfile);
146 if (ver != 4)
148 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported key version.");
149 return pktCONTINUE;
152 // Only V4 accepted. Discard creation time.
153 if (pkt_getdword (wlk->pfile) == -1)
155 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing creation time.");
156 return pktCONTINUE;
159 char pkalg = pkt_getch (wlk->pfile);
160 if ((pkalg != RFC4880_PK_DSA) && (pkalg != RFC4880_PK_RSA))
162 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, pkalg, "unsupported key alg.");
163 return pktCONTINUE;
166 // Next, the key coefficient MPIs should be present. Read them out, convert
167 // to an s-expr and add that to the list of keys.
168 size_t erroff;
169 gcry_sexp_t new_key;
171 if (pkalg == RFC4880_PK_DSA)
173 gcry_mpi_t p, q, g, y;
174 p = q = g = y = 0;
176 if ((pkt_get_mpi (&p, wlk->pfile) >= 0)
177 && (pkt_get_mpi (&q, wlk->pfile) >= 0)
178 && (pkt_get_mpi (&g, wlk->pfile) >= 0)
179 && (pkt_get_mpi (&y, wlk->pfile) >= 0))
181 gcry_error_t rv = gcry_sexp_build (&new_key, &erroff, dsa_pubkey_templ, p, q, g, y);
182 if (rv != GPG_ERR_NO_ERROR)
184 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
185 return pktCONTINUE;
189 // Release temps and continue.
190 if (p)
191 gcry_mpi_release (p);
192 if (q)
193 gcry_mpi_release (q);
194 if (g)
195 gcry_mpi_release (g);
196 if (y)
197 gcry_mpi_release (y);
199 else if (pkalg == RFC4880_PK_RSA)
201 gcry_mpi_t n, e;
202 n = e = 0;
204 if ((pkt_get_mpi (&n, wlk->pfile) >= 0)
205 && (pkt_get_mpi (&e, wlk->pfile) >= 0))
207 gcry_error_t rv = gcry_sexp_build (&new_key, &erroff, rsa_pubkey_templ, n, e);
208 if (rv != GPG_ERR_NO_ERROR)
210 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
211 return pktCONTINUE;
215 if (n)
216 gcry_mpi_release (n);
217 if (e)
218 gcry_mpi_release (e);
221 #if CRYPTODEBUGGING
222 // Debugging
223 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
224 erroff = gcry_sexp_sprint (new_key, GCRYSEXP_FMT_ADVANCED, sexprbuf,
225 GPG_KEY_SEXPR_BUF_SIZE);
226 LogBabblePrintf ("key:%d\n'%s'", erroff, sexprbuf);
227 #endif /* CRYPTODEBUGGING */
229 // Return it to caller in the vector.
230 kdat->keys.push_back (new_key);
232 return pktCONTINUE;
235 /* Does what its name suggests: feeds a chosen amount of the data found
236 at the current seek position in an io_stream into the message digest
237 context passed in, using reasonably-sized chunks for efficiency. */
238 static size_t
239 shovel_stream_data_into_md (io_stream *stream, size_t nbytes, gcry_md_hd_t md)
241 const size_t TMPBUFSZ = 1024;
242 unsigned char tmpbuf[TMPBUFSZ];
243 size_t this_time, total = 0;
244 ssize_t actual;
245 MESSAGE ("shovel %d bytes at pos $%08x\n", nbytes, stream->tell ());
246 while (nbytes)
248 this_time = (nbytes > TMPBUFSZ) ? TMPBUFSZ : nbytes;
249 actual = stream->read (tmpbuf, this_time);
250 if (actual <= 0)
251 break;
252 gcry_md_write (md, tmpbuf, actual);
253 total += actual;
254 nbytes -= actual;
255 if (actual != (ssize_t)this_time)
256 break;
258 return total;
261 /* Canonicalise an s-expr by converting LFs to spaces so that
262 it's all on one line and folding multiple spaces as we go. */
263 static size_t
264 fold_lfs_and_spaces (char *buf, size_t n)
266 char *ptr1 = buf, *ptr2 = buf;
268 while (n--)
270 char ch = *ptr1++;
271 if (ch == 0x0a)
272 ch = ' ';
273 *ptr2++ = ch;
274 if (ch == 0x20)
275 while (n && ((*ptr1 == ' ') || (*ptr1 == 0x0a)))
277 --n;
278 ++ptr1;
281 return ptr2 - buf;
284 /* Size and allocate a temp buffer to print a representation
285 of a public key s-expr into, then add that to the extra keys
286 setting so it persists for the next run. */
287 static void
288 add_key_from_sexpr (gcry_sexp_t key)
290 size_t n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, 0, ~0);
291 char *sexprbuf = new char[n];
292 n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, sexprbuf, n);
293 // +1 because we want to include the nul-terminator.
294 n = fold_lfs_and_spaces (sexprbuf, n + 1);
295 ExtraKeysSetting::instance().add_key (sexprbuf);
296 MESSAGE ("keep:%d\n'%s'", n, sexprbuf);
297 delete [] sexprbuf;
300 static bool
301 verify_sig(struct sig_data *sigdat, HWND owner)
303 gcry_error_t rv;
304 size_t n;
306 /* sig coefficients in s-expr format. */
307 gcry_sexp_t sig;
309 /* signature hash data in s-expr format. */
310 gcry_sexp_t hash;
312 /* Build everything into s-exprs, and call the libgcrypt verification
313 routine. */
315 if (sigdat->pk_alg == RFC4880_PK_DSA)
317 rv = gcry_sexp_build (&sig, &n, dsa_sig_templ, sigdat->dsa_mpi_r,
318 sigdat->dsa_mpi_s);
319 if (rv != GPG_ERR_NO_ERROR)
321 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
322 return false;
325 rv = gcry_sexp_build (&hash, &n, dsa_data_hash_templ,
326 gcry_md_algo_name(sigdat->algo),
327 gcry_md_get_algo_dlen (sigdat->algo),
328 gcry_md_read (sigdat->md, 0));
329 if (rv != GPG_ERR_NO_ERROR)
331 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
332 return false;
335 else if (sigdat->pk_alg == RFC4880_PK_RSA)
337 rv = gcry_sexp_build (&sig, &n, rsa_sig_templ, sigdat->rsa_mpi_s);
338 if (rv != GPG_ERR_NO_ERROR)
340 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
341 return false;
344 rv = gcry_sexp_build (&hash, &n, rsa_data_hash_templ,
345 gcry_md_algo_name(sigdat->algo),
346 gcry_md_get_algo_dlen (sigdat->algo),
347 gcry_md_read (sigdat->md, 0));
348 if (rv != GPG_ERR_NO_ERROR)
350 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
351 return false;
355 #if CRYPTODEBUGGING
356 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
357 n = gcry_sexp_sprint (sig, GCRYSEXP_FMT_ADVANCED, sexprbuf,
358 GPG_KEY_SEXPR_BUF_SIZE);
359 LogBabblePrintf ("sig:%d\n'%s'", n, sexprbuf);
360 n = gcry_sexp_sprint (hash, GCRYSEXP_FMT_ADVANCED, sexprbuf,
361 GPG_KEY_SEXPR_BUF_SIZE);
362 LogBabblePrintf ("hash:%d\n'%s'", n, sexprbuf);
363 #endif /* CRYPTODEBUGGING */
365 // Well, we're actually there!
366 // Try it against each key in turn
368 std::vector<key_info>::iterator it;
369 for (it = sigdat->keys_to_try->begin ();
370 it < sigdat->keys_to_try->end ();
371 ++it)
373 rv = gcry_pk_verify (sig, hash, it->key);
375 LogBabblePrintf("signature: tried key %s, returned 0x%08x %s\n",
376 it->name.c_str(), rv, gcry_strerror(rv));
378 if (rv != GPG_ERR_NO_ERROR)
379 continue;
380 // Found it! This key gets kept!
381 if (!it->builtin)
382 add_key_from_sexpr (it->key);
383 break;
386 gcry_sexp_release (sig);
387 gcry_sexp_release (hash);
390 return (rv == GPG_ERR_NO_ERROR);
393 /* Do-nothing stubs called by the sig file walker to
394 walk over the embedded subpackets. In the event, we don't
395 actually need to do this as we aren't inspecting them. */
396 static enum
397 pkt_cb_resp hashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag,
398 size_t packetsize, size_t hdrpos)
400 return pktCONTINUE;
403 static enum
404 pkt_cb_resp unhashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag,
405 size_t packetsize, size_t hdrpos)
407 return pktCONTINUE;
410 /* Callback to parse the packets found in the setup.ini/setup.bz2
411 signature file. We have to parse the header to get the hash type
412 and other details. Once we have that we can create a message
413 digest context and start pumping data through it; first the ini
414 file itself, then the portion of the packet itself that is
415 covered by the hash. */
416 static enum
417 pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
418 size_t packetsize, size_t hdrpos)
420 struct sig_data *sigdat = (struct sig_data *)(wlk->userdata);
422 if (tag != RFC4880_PT_SIGNATURE)
423 return pktCONTINUE;
425 // To add the trailers later, we hang on to the current pos.
426 size_t v34hdrofs = wlk->pfile->tell ();
428 // So, get the data out. Version is first.
429 char ver = pkt_getch (wlk->pfile);
430 if ((ver < 3) || (ver > 4))
432 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported sig version.");
433 return pktHALT;
436 // Only V3 and V4 accepted.
437 if (ver == 4)
439 sigdat->sig_type = pkt_getch (wlk->pfile);
440 sigdat->pk_alg = pkt_getch (wlk->pfile);
441 sigdat->hash_alg = pkt_getch (wlk->pfile);
443 else
445 int hmsize = pkt_getch (wlk->pfile);
446 if (hmsize != RFC4880_SIGV3_HASHED_SIZE)
448 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size.");
449 return pktHALT;
451 v34hdrofs = wlk->pfile->tell ();
452 if ((pkt_getch (wlk->pfile) < 0) || (pkt_getdword (wlk->pfile) == -1))
454 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size.");
455 return pktHALT;
457 if ((pkt_getdword (wlk->pfile) == -1) || (pkt_getdword (wlk->pfile) == -1))
459 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing signer ID.");
460 return pktHALT;
463 sigdat->sig_type = 0;
464 sigdat->pk_alg = pkt_getch (wlk->pfile);
465 sigdat->hash_alg = pkt_getch (wlk->pfile);
468 LogBabblePrintf("signature: sig_type %d, pk_alg %d, hash_alg %d\n",
469 sigdat->sig_type, sigdat->pk_alg, sigdat->hash_alg);
471 // We only handle binary file signatures
472 if (sigdat->sig_type != RFC4880_ST_BINARY)
474 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->sig_type, "unsupported sig type.");
475 return pktHALT;
478 // We only handle RSA and DSA keys
479 if ((sigdat->pk_alg != RFC4880_PK_DSA) && (sigdat->pk_alg != RFC4880_PK_RSA))
481 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->pk_alg, "unsupported pk alg.");
482 return pktHALT;
485 // Start to hash all the data. Figure out what hash to use.
486 sigdat->algo = pkt_convert_hashcode (sigdat->hash_alg);
487 if (sigdat->algo == GCRY_MD_NONE)
489 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "unconvertible hash.");
490 return pktHALT;
493 // Now we know hash algo, we can create md context.
494 sigdat->md = 0;
495 gcry_error_t rv = gcry_md_open (&sigdat->md, sigdat->algo, 0);
496 if (rv != GPG_ERR_NO_ERROR)
498 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while initialising message digest.");
499 return pktHALT;
502 // Add all the sig_file data into the hash.
503 sigdat->sign_data->seek (0, IO_SEEK_SET);
504 size_t nbytes = sigdat->sign_data->get_size ();
505 if (nbytes != shovel_stream_data_into_md (sigdat->sign_data, nbytes, sigdat->md))
507 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error.");
508 return pktHALT;
510 sigdat->sign_data->seek (0, IO_SEEK_SET);
512 // V4 now has some hashed subpackets
513 int hashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0;
514 if (hashed_subpkt_size)
515 pkt_walk_subpackets (wlk->pfile, hashed_subpkt_walker, wlk->owner,
516 wlk->pfile->tell (), hashed_subpkt_size, wlk->userdata);
518 // V4 now has some unhashed subpackets
519 int unhashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0;
520 if (unhashed_subpkt_size)
521 pkt_walk_subpackets (wlk->pfile, unhashed_subpkt_walker, wlk->owner,
522 wlk->pfile->tell (), unhashed_subpkt_size, wlk->userdata);
524 // Both formats now have 16 bits of the hash value.
525 int hash_first = pkt_getword (wlk->pfile);
527 MESSAGE ("signature: hash leftmost 2 bytes 0x%04x\n", hash_first);
529 /* Algorithm-Specific Fields for signatures:
531 for DSA:
532 - MPI of DSA value r
533 - MPI of DSA value s
535 DSA signatures MUST use hashes that are equal in size to the number of
536 bits of q, the group generated by the DSA key's generator value.
538 for RSA:
539 - MPI of RSA value m^d mod n (aka s)
541 sigdat->dsa_mpi_r = sigdat->dsa_mpi_s = 0;
542 sigdat->rsa_mpi_s = 0;
544 if (sigdat->pk_alg == RFC4880_PK_DSA)
546 if ((pkt_get_mpi (&sigdat->dsa_mpi_r, wlk->pfile) < 0)
547 || (pkt_get_mpi (&sigdat->dsa_mpi_s, wlk->pfile) < 0))
549 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi.");
550 return pktHALT;
553 else if (sigdat->pk_alg == RFC4880_PK_RSA)
555 if (pkt_get_mpi (&sigdat->rsa_mpi_s, wlk->pfile) < 0)
557 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi.");
558 return pktHALT;
562 MESSAGE ("Read sig packets succesfully!\n");
564 // Now we got all the data out ok, rewind and hash the first trailer.
565 wlk->pfile->seek (v34hdrofs, IO_SEEK_SET);
566 nbytes = (ver == 4) ? (RFC4880_SIGV4_HASHED_OVERHEAD + hashed_subpkt_size)
567 : (RFC4880_SIGV3_HASHED_SIZE);
568 if (nbytes != shovel_stream_data_into_md (wlk->pfile, nbytes, sigdat->md))
570 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error 2.");
571 return pktHALT;
574 if (ver == 4)
576 // And now the synthetic final trailer.
577 gcry_md_putc (sigdat->md, 4);
578 gcry_md_putc (sigdat->md, 0xff);
579 gcry_md_putc (sigdat->md, (nbytes >> 24) & 0xff);
580 gcry_md_putc (sigdat->md, (nbytes >> 16) & 0xff);
581 gcry_md_putc (sigdat->md, (nbytes >> 8) & 0xff);
582 gcry_md_putc (sigdat->md, nbytes & 0xff);
585 // finalize the hash
586 gcry_md_final (sigdat->md);
587 MESSAGE("digest length is %d\n",gcry_md_get_algo_dlen (sigdat->algo));
589 // we have hashed all the data, and found the sig coefficients.
590 // heck this signature
591 if (verify_sig (sigdat, wlk->owner))
592 sigdat->valid = true;
594 // discard hash
595 if (sigdat->md)
596 gcry_md_close (sigdat->md);
598 // discard sig coefffcients
599 if (sigdat->dsa_mpi_r)
600 gcry_mpi_release (sigdat->dsa_mpi_r);
601 if (sigdat->dsa_mpi_s)
602 gcry_mpi_release (sigdat->dsa_mpi_s);
603 if (sigdat->rsa_mpi_s)
604 gcry_mpi_release (sigdat->rsa_mpi_s);
606 // we can stop immediately if we found a good signature
607 return sigdat->valid ? pktHALT : pktCONTINUE;
610 #if CRYPTODEBUGGING
611 static void
612 gcrypt_log_adaptor(void *priv, int level, const char *fmt, va_list args)
614 static std::string collected;
616 char buf[GPG_KEY_SEXPR_BUF_SIZE];
617 vsnprintf (buf, GPG_KEY_SEXPR_BUF_SIZE, fmt, args);
619 char *start = buf;
620 char *end;
624 if (collected.length() == 0)
626 collected = "gcrypt: ";
629 end = strchr(start, '\n');
630 if (end)
631 *end = '\0';
633 collected += start;
635 if (end)
637 if (level == GCRY_LOG_DEBUG)
638 Log (LOG_BABBLE) << collected << endLog;
639 else
640 Log (LOG_PLAIN) << collected << endLog;
642 collected.clear();
643 start = end + 1;
646 while (end);
648 #endif
650 /* Verify the signature on an ini file. Takes care of all key-handling. */
651 bool
652 verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner)
654 /* Data returned from packet walker. */
655 struct sig_data sigdat;
657 /* Vector of keys to use. */
658 std::vector<struct key_info> keys_to_try;
660 /* Overall status of signature. */
661 bool sig_ok = false;
663 // Temps for intermediate processing.
664 gcry_error_t rv;
665 size_t n;
667 /* Initialise the library. */
668 static bool gcrypt_init = false;
669 if (!gcrypt_init)
671 #if CRYPTODEBUGGING
672 gcry_set_log_handler (gcrypt_log_adaptor, NULL);
673 #endif
674 gcry_check_version (NULL);
676 if ((rv = gcry_control (GCRYCTL_SELFTEST)) != GPG_ERR_NO_ERROR)
677 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "libgcrypt selftest failed");
679 #if CRYPTODEBUGGING
680 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
681 #endif
682 gcrypt_init = true;
685 /* So first build the built-in key. */
686 gcry_sexp_t cygwin_key;
687 rv = gcry_sexp_new (&cygwin_key, cygwin_pubkey_sexpr, strlen (cygwin_pubkey_sexpr), 1);
688 if (rv != GPG_ERR_NO_ERROR)
690 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating pubkey s-expr.");
692 else
694 keys_to_try.push_back (key_info("cygwin", true, cygwin_key));
697 #if CRYPTODEBUGGING
698 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
699 n = gcry_sexp_sprint (cygwin_key, GCRYSEXP_FMT_ADVANCED, sexprbuf, GPG_KEY_SEXPR_BUF_SIZE);
700 LogBabblePrintf ("key:%d\n'%s'", n, sexprbuf);
701 #endif /* CRYPTODEBUGGING */
703 /* If not disabled, also try the old built-in key */
704 gcry_sexp_t cygwin_old_key;
705 if (EnableOldKeysOption)
707 rv = gcry_sexp_new (&cygwin_old_key, cygwin_old_pubkey_sexpr, strlen (cygwin_old_pubkey_sexpr), 1);
708 if (rv != GPG_ERR_NO_ERROR)
710 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating old pubkey s-expr.");
712 else
714 keys_to_try.push_back (key_info ("cygwin-old", TRUE, cygwin_old_key));
717 /* Vector of cached extra keys from last run. */
718 static std::vector<gcry_sexp_t> input_keys;
720 /* Next we should extract the keys from the extrakeys user
721 setting, and flush it; we'll only return them to it if they
722 get used. OTOH, should we do this at all? The user settings
723 file isn't heavily protected. So we only trust the extra
724 keys if we're told to by the user. We still read them in
725 and write them back out, which canonicalises and eliminates
726 any duplicates or garbage lines that may have crept in. */
727 static bool input_keys_read = false;
728 if (!input_keys_read)
730 // We only want to do this once, first time through:
731 input_keys_read = true;
732 // Copy all valid keys from ExtraKeysSetting into a
733 // static vector where we can keep them throughout the
734 // remainder of the run.
735 for (size_t i = 0; i < ExtraKeysSetting::instance().num_keys (); i++)
737 const char *keystring = ExtraKeysSetting::instance().get_key (i, &n);
738 gcry_sexp_t newkey;
739 rv = gcry_sexp_new (&newkey, keystring, n, 1);
740 if (rv == GPG_ERR_NO_ERROR)
741 input_keys.push_back (newkey);
744 // Now flush out the ExtraKeysSetting; from here on it
745 // will build up a list of the keys we want to retain.
746 ExtraKeysSetting::instance().flush ();
748 // Which, if we aren't using them, means all the ones
749 // we just read.
750 if (KeepUntrustedKeysOption || !UntrustedKeysOption)
752 std::vector<gcry_sexp_t>::iterator it;
753 for (it = input_keys.begin (); it < input_keys.end (); ++it)
754 add_key_from_sexpr (*it);
758 // We only use the untrusted keys if told to.
759 if (KeepUntrustedKeysOption || UntrustedKeysOption)
760 for (std::vector<gcry_sexp_t>::const_iterator it = input_keys.begin ();
761 it < input_keys.end ();
762 ++it)
764 keys_to_try.push_back (key_info ("saved key", false, *it, false));
767 /* Next, there may have been command-line options. */
768 std::vector<std::string> SexprExtraKeyStrings = SexprExtraKeyOption;
769 for (std::vector<std::string>::const_iterator it
770 = SexprExtraKeyStrings.begin ();
771 it != SexprExtraKeyStrings.end (); ++it)
773 MESSAGE ("key str is '%s'\n", it->c_str ());
774 gcry_sexp_t dsa_key2 = 0;
775 rv = gcry_sexp_new (&dsa_key2, it->c_str (), it->size (), 1);
776 if (rv == GPG_ERR_NO_ERROR)
778 // We probably want to add it to the extra keys setting
779 // if KeepUntrustedKeysOption is supplied.
780 if (KeepUntrustedKeysOption)
781 add_key_from_sexpr (dsa_key2);
782 #if CRYPTODEBUGGING
783 n = gcry_sexp_sprint (dsa_key2, GCRYSEXP_FMT_ADVANCED, sexprbuf,
784 GPG_KEY_SEXPR_BUF_SIZE);
785 // +1 because we want to include the nul-terminator.
786 n = fold_lfs_and_spaces (sexprbuf, n + 1);
787 ExtraKeysSetting::instance().add_key (sexprbuf);
788 LogBabblePrintf ("key2:%d\n'%s'", n, sexprbuf);
789 #endif /* CRYPTODEBUGGING */
790 keys_to_try.push_back (key_info ("from command-line option --sexpr-pubkey", false, dsa_key2));
792 else
794 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "invalid command-line pubkey s-expr.");
798 /* Also, we may have to read a key(s) file. */
799 std::vector<std::string> ExtraKeysFiles = ExtraKeyOption;
800 for (std::vector<std::string>::const_iterator it
801 = ExtraKeysFiles.begin ();
802 it != ExtraKeysFiles.end (); ++it)
804 io_stream *keys = get_url_to_membuf (*it, owner);
805 if (keys)
807 struct key_data kdat;
808 pkt_walk_packets (keys, key_file_walker, owner, 0, keys->get_size (), &kdat);
809 // We now have a vector of (some/any?) keys returned from
810 // the walker; add them to the list to try.
811 while (!kdat.keys.empty ())
813 // We probably want to add it to the extra keys setting
814 // if KeepUntrustedKeysOption is supplied.
815 if (KeepUntrustedKeysOption)
816 add_key_from_sexpr (kdat.keys.back ());
817 #if CRYPTODEBUGGING
818 n = gcry_sexp_sprint (kdat.keys.back (), GCRYSEXP_FMT_ADVANCED,
819 sexprbuf, GPG_KEY_SEXPR_BUF_SIZE);
820 // +1 because we want to include the nul-terminator.
821 n = fold_lfs_and_spaces (sexprbuf, n + 1);
822 ExtraKeysSetting::instance().add_key (sexprbuf);
823 LogBabblePrintf ("key3:%d\n'%s'", n, sexprbuf);
824 #endif /* CRYPTODEBUGGING */
825 keys_to_try.push_back (key_info ("from command-line option --pubkey", false, kdat.keys.back ()));
826 kdat.keys.pop_back ();
831 // We pass in a pointer to the ini file in the user context data,
832 // which the packet walker callback uses to create a new hash
833 // context preloaded with all the signature-covered data.
834 sigdat.valid = false;
835 sigdat.sign_data = ini_file;
836 sigdat.keys_to_try = &keys_to_try;
838 pkt_walk_packets (ini_sig_file, sig_file_walker, owner, 0,
839 ini_sig_file->get_size (), &sigdat);
841 sig_ok = sigdat.valid;
843 while (keys_to_try.size ())
845 if (keys_to_try.back ().owned)
846 gcry_sexp_release (keys_to_try.back ().key);
847 keys_to_try.pop_back ();
850 return sig_ok;