Use UTF8 encoding in .rc file
[cygwin-setup.git] / crypto.cc
blob88ced387f701d2250154e1d9d41899afcf33d481
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",
46 "URL or absolute path of extra public key file (RFC4880 format)");
48 static StringArrayOption SexprExtraKeyOption ('S', "sexpr-pubkey",
49 "Extra DSA public key in s-expr format");
51 static BoolOption UntrustedKeysOption (false, 'u', "untrusted-keys",
52 "Use untrusted saved extra keys");
53 static BoolOption KeepUntrustedKeysOption (false, 'U', "keep-untrusted-keys",
54 "Use untrusted keys and retain all");
55 static BoolOption EnableOldKeysOption (false, '\0', "old-keys",
56 "Enable old cygwin.com keys",
57 BoolOption::BoolOptionType::pairedAble);
59 /* Embedded public half of Cygwin signing key. */
60 static const char *cygwin_pubkey_sexpr =
61 #include "cyg-pubkey.h"
64 static const char *cygwin_old_pubkey_sexpr =
65 #include "cyg-old-pubkey.h"
68 /* S-expr template for DSA pubkey. */
69 static const char *dsa_pubkey_templ = "(public-key (dsa (p %m) (q %m) (g %m) (y %m)))";
71 /* S-expr template for RSA pubkey. */
72 static const char *rsa_pubkey_templ = "(public-key (rsa (n %m) (e %m)))";
74 /* S-expr template for DSA signature. */
75 static const char *dsa_sig_templ = "(sig-val (dsa (r %m) (s %m)))";
77 /* S-expr template for RSA signature. */
78 static const char *rsa_sig_templ = "(sig-val (rsa (s %m)))";
80 /* S-expr template for DSA data block to be signed. */
81 static const char *dsa_data_hash_templ = "(data (flags raw) (hash %s %b))";
83 /* S-expr template for RSA data block to be signed. */
84 static const char *rsa_data_hash_templ = "(data (flags pkcs1) (hash %s %b))";
86 /* Information on a key to try */
87 struct key_info
89 key_info(std::string _name, bool _builtin, gcry_sexp_t _key, bool _owned=true) :
90 name(_name), builtin(_builtin), key(_key), owned(_owned)
94 std::string name;
95 bool builtin; // if true, we don't need to retain this key with add_key_from_sexpr()
96 gcry_sexp_t key;
97 bool owned; // if true, we own this key and should use gcry_sexp_release() on it
100 /* User context data for sig packet walk. */
101 struct sig_data
103 /* MPI values of sig components. */
104 gcry_mpi_t dsa_mpi_r, dsa_mpi_s;
105 gcry_mpi_t rsa_mpi_s;
107 /* Hash context. */
108 gcry_md_hd_t md;
110 /* Main data. */
111 io_stream *sign_data;
113 /* Auxiliary data. */
114 int sig_type;
115 int pk_alg;
116 int hash_alg;
118 /* Converted algo code. */
119 int algo;
121 /* Keys */
122 std::vector<struct key_info> *keys_to_try;
124 /* Final status. */
125 bool valid;
128 /* User context data for key packet walk. */
129 struct key_data
131 std::vector<gcry_sexp_t> keys;
134 /* Callback hook for walking packets in gpg key file. Extracts
135 the key coefficients from any public key packets encountered and
136 converts them into s-expr pubkey format, returning the public
137 keys thus found to the caller in a vector in the userdata context. */
138 static enum
139 pkt_cb_resp key_file_walker (struct packet_walker *wlk, unsigned char tag,
140 size_t packetsize, size_t hdrpos)
142 struct key_data *kdat = (struct key_data *)(wlk->userdata);
144 MESSAGE ("key packet %d size %d at offs $%04x kdat $%08x\n", tag,
145 packetsize, hdrpos, kdat);
147 if (tag != RFC4880_PT_PUBLIC_KEY)
148 return pktCONTINUE;
150 // So, get the data out. Version is first. In case of any errors during
151 // parsing, we just discard the key and continue, hoping to find a good one.
152 char ver = pkt_getch (wlk->pfile);
153 if (ver != 4)
155 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported key version.");
156 return pktCONTINUE;
159 // Only V4 accepted. Discard creation time.
160 if (pkt_getdword (wlk->pfile) == -1)
162 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing creation time.");
163 return pktCONTINUE;
166 char pkalg = pkt_getch (wlk->pfile);
167 if ((pkalg != RFC4880_PK_DSA) && (pkalg != RFC4880_PK_RSA))
169 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, pkalg, "unsupported key alg.");
170 return pktCONTINUE;
173 // Next, the key coefficient MPIs should be present. Read them out, convert
174 // to an s-expr and add that to the list of keys.
175 size_t erroff;
176 gcry_sexp_t new_key;
178 if (pkalg == RFC4880_PK_DSA)
180 gcry_mpi_t p, q, g, y;
181 p = q = g = y = 0;
183 if ((pkt_get_mpi (&p, wlk->pfile) >= 0)
184 && (pkt_get_mpi (&q, wlk->pfile) >= 0)
185 && (pkt_get_mpi (&g, wlk->pfile) >= 0)
186 && (pkt_get_mpi (&y, wlk->pfile) >= 0))
188 gcry_error_t rv = gcry_sexp_build (&new_key, &erroff, dsa_pubkey_templ, p, q, g, y);
189 if (rv != GPG_ERR_NO_ERROR)
191 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
192 return pktCONTINUE;
196 // Release temps and continue.
197 if (p)
198 gcry_mpi_release (p);
199 if (q)
200 gcry_mpi_release (q);
201 if (g)
202 gcry_mpi_release (g);
203 if (y)
204 gcry_mpi_release (y);
206 else if (pkalg == RFC4880_PK_RSA)
208 gcry_mpi_t n, e;
209 n = e = 0;
211 if ((pkt_get_mpi (&n, wlk->pfile) >= 0)
212 && (pkt_get_mpi (&e, wlk->pfile) >= 0))
214 gcry_error_t rv = gcry_sexp_build (&new_key, &erroff, rsa_pubkey_templ, n, e);
215 if (rv != GPG_ERR_NO_ERROR)
217 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
218 return pktCONTINUE;
222 if (n)
223 gcry_mpi_release (n);
224 if (e)
225 gcry_mpi_release (e);
228 #if CRYPTODEBUGGING
229 // Debugging
230 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
231 erroff = gcry_sexp_sprint (new_key, GCRYSEXP_FMT_ADVANCED, sexprbuf,
232 GPG_KEY_SEXPR_BUF_SIZE);
233 LogBabblePrintf ("key:%d\n'%s'", erroff, sexprbuf);
234 #endif /* CRYPTODEBUGGING */
236 // Return it to caller in the vector.
237 kdat->keys.push_back (new_key);
239 return pktCONTINUE;
242 /* Does what its name suggests: feeds a chosen amount of the data found
243 at the current seek position in an io_stream into the message digest
244 context passed in, using reasonably-sized chunks for efficiency. */
245 static size_t
246 shovel_stream_data_into_md (io_stream *stream, size_t nbytes, gcry_md_hd_t md)
248 const size_t TMPBUFSZ = 1024;
249 unsigned char tmpbuf[TMPBUFSZ];
250 size_t this_time, total = 0;
251 ssize_t actual;
252 MESSAGE ("shovel %d bytes at pos $%08x\n", nbytes, stream->tell ());
253 while (nbytes)
255 this_time = (nbytes > TMPBUFSZ) ? TMPBUFSZ : nbytes;
256 actual = stream->read (tmpbuf, this_time);
257 if (actual <= 0)
258 break;
259 gcry_md_write (md, tmpbuf, actual);
260 total += actual;
261 nbytes -= actual;
262 if (actual != (ssize_t)this_time)
263 break;
265 return total;
268 /* Canonicalise an s-expr by converting LFs to spaces so that
269 it's all on one line and folding multiple spaces as we go. */
270 static size_t
271 fold_lfs_and_spaces (char *buf, size_t n)
273 char *ptr1 = buf, *ptr2 = buf;
275 while (n--)
277 char ch = *ptr1++;
278 if (ch == 0x0a)
279 ch = ' ';
280 *ptr2++ = ch;
281 if (ch == 0x20)
282 while (n && ((*ptr1 == ' ') || (*ptr1 == 0x0a)))
284 --n;
285 ++ptr1;
288 return ptr2 - buf;
291 /* Size and allocate a temp buffer to print a representation
292 of a public key s-expr into, then add that to the extra keys
293 setting so it persists for the next run. */
294 static void
295 add_key_from_sexpr (gcry_sexp_t key)
297 size_t n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, 0, ~0);
298 char *sexprbuf = new char[n];
299 n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, sexprbuf, n);
300 // +1 because we want to include the nul-terminator.
301 n = fold_lfs_and_spaces (sexprbuf, n + 1);
302 ExtraKeysSetting::instance().add_key (sexprbuf);
303 MESSAGE ("keep:%d\n'%s'", n, sexprbuf);
304 delete [] sexprbuf;
307 static bool
308 verify_sig(struct sig_data *sigdat, HWND owner)
310 gcry_error_t rv;
311 size_t n;
313 /* sig coefficients in s-expr format. */
314 gcry_sexp_t sig;
316 /* signature hash data in s-expr format. */
317 gcry_sexp_t hash;
319 /* Build everything into s-exprs, and call the libgcrypt verification
320 routine. */
322 if (sigdat->pk_alg == RFC4880_PK_DSA)
324 rv = gcry_sexp_build (&sig, &n, dsa_sig_templ, sigdat->dsa_mpi_r,
325 sigdat->dsa_mpi_s);
326 if (rv != GPG_ERR_NO_ERROR)
328 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
329 return false;
332 rv = gcry_sexp_build (&hash, &n, dsa_data_hash_templ,
333 gcry_md_algo_name(sigdat->algo),
334 gcry_md_get_algo_dlen (sigdat->algo),
335 gcry_md_read (sigdat->md, 0));
336 if (rv != GPG_ERR_NO_ERROR)
338 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
339 return false;
342 else if (sigdat->pk_alg == RFC4880_PK_RSA)
344 rv = gcry_sexp_build (&sig, &n, rsa_sig_templ, sigdat->rsa_mpi_s);
345 if (rv != GPG_ERR_NO_ERROR)
347 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
348 return false;
351 rv = gcry_sexp_build (&hash, &n, rsa_data_hash_templ,
352 gcry_md_algo_name(sigdat->algo),
353 gcry_md_get_algo_dlen (sigdat->algo),
354 gcry_md_read (sigdat->md, 0));
355 if (rv != GPG_ERR_NO_ERROR)
357 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
358 return false;
362 #if CRYPTODEBUGGING
363 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
364 n = gcry_sexp_sprint (sig, GCRYSEXP_FMT_ADVANCED, sexprbuf,
365 GPG_KEY_SEXPR_BUF_SIZE);
366 LogBabblePrintf ("sig:%d\n'%s'", n, sexprbuf);
367 n = gcry_sexp_sprint (hash, GCRYSEXP_FMT_ADVANCED, sexprbuf,
368 GPG_KEY_SEXPR_BUF_SIZE);
369 LogBabblePrintf ("hash:%d\n'%s'", n, sexprbuf);
370 #endif /* CRYPTODEBUGGING */
372 // Well, we're actually there!
373 // Try it against each key in turn
375 std::vector<key_info>::iterator it;
376 for (it = sigdat->keys_to_try->begin ();
377 it < sigdat->keys_to_try->end ();
378 ++it)
380 rv = gcry_pk_verify (sig, hash, it->key);
382 LogBabblePrintf("signature: tried key %s, returned 0x%08x %s\n",
383 it->name.c_str(), rv, gcry_strerror(rv));
385 if (rv != GPG_ERR_NO_ERROR)
386 continue;
387 // Found it! This key gets kept!
388 if (!it->builtin)
389 add_key_from_sexpr (it->key);
390 break;
393 gcry_sexp_release (sig);
394 gcry_sexp_release (hash);
397 return (rv == GPG_ERR_NO_ERROR);
400 /* Do-nothing stubs called by the sig file walker to
401 walk over the embedded subpackets. In the event, we don't
402 actually need to do this as we aren't inspecting them. */
403 static enum
404 pkt_cb_resp hashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag,
405 size_t packetsize, size_t hdrpos)
407 return pktCONTINUE;
410 static enum
411 pkt_cb_resp unhashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag,
412 size_t packetsize, size_t hdrpos)
414 return pktCONTINUE;
417 /* Callback to parse the packets found in the setup.ini/setup.bz2
418 signature file. We have to parse the header to get the hash type
419 and other details. Once we have that we can create a message
420 digest context and start pumping data through it; first the ini
421 file itself, then the portion of the packet itself that is
422 covered by the hash. */
423 static enum
424 pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
425 size_t packetsize, size_t hdrpos)
427 struct sig_data *sigdat = (struct sig_data *)(wlk->userdata);
429 if (tag != RFC4880_PT_SIGNATURE)
430 return pktCONTINUE;
432 // To add the trailers later, we hang on to the current pos.
433 size_t v34hdrofs = wlk->pfile->tell ();
435 // So, get the data out. Version is first.
436 char ver = pkt_getch (wlk->pfile);
437 if ((ver < 3) || (ver > 4))
439 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported sig version.");
440 return pktHALT;
443 // Only V3 and V4 accepted.
444 if (ver == 4)
446 sigdat->sig_type = pkt_getch (wlk->pfile);
447 sigdat->pk_alg = pkt_getch (wlk->pfile);
448 sigdat->hash_alg = pkt_getch (wlk->pfile);
450 else
452 int hmsize = pkt_getch (wlk->pfile);
453 if (hmsize != RFC4880_SIGV3_HASHED_SIZE)
455 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size.");
456 return pktHALT;
458 v34hdrofs = wlk->pfile->tell ();
459 if ((pkt_getch (wlk->pfile) < 0) || (pkt_getdword (wlk->pfile) == -1))
461 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size.");
462 return pktHALT;
464 if ((pkt_getdword (wlk->pfile) == -1) || (pkt_getdword (wlk->pfile) == -1))
466 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing signer ID.");
467 return pktHALT;
470 sigdat->sig_type = 0;
471 sigdat->pk_alg = pkt_getch (wlk->pfile);
472 sigdat->hash_alg = pkt_getch (wlk->pfile);
475 LogBabblePrintf("signature: sig_type %d, pk_alg %d, hash_alg %d\n",
476 sigdat->sig_type, sigdat->pk_alg, sigdat->hash_alg);
478 // We only handle binary file signatures
479 if (sigdat->sig_type != RFC4880_ST_BINARY)
481 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->sig_type, "unsupported sig type.");
482 return pktHALT;
485 // We only handle RSA and DSA keys
486 if ((sigdat->pk_alg != RFC4880_PK_DSA) && (sigdat->pk_alg != RFC4880_PK_RSA))
488 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->pk_alg, "unsupported pk alg.");
489 return pktHALT;
492 // Start to hash all the data. Figure out what hash to use.
493 sigdat->algo = pkt_convert_hashcode (sigdat->hash_alg);
494 if (sigdat->algo == GCRY_MD_NONE)
496 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "unconvertible hash.");
497 return pktHALT;
500 // Now we know hash algo, we can create md context.
501 sigdat->md = 0;
502 gcry_error_t rv = gcry_md_open (&sigdat->md, sigdat->algo, 0);
503 if (rv != GPG_ERR_NO_ERROR)
505 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while initialising message digest.");
506 return pktHALT;
509 // Add all the sig_file data into the hash.
510 sigdat->sign_data->seek (0, IO_SEEK_SET);
511 size_t nbytes = sigdat->sign_data->get_size ();
512 if (nbytes != shovel_stream_data_into_md (sigdat->sign_data, nbytes, sigdat->md))
514 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error.");
515 return pktHALT;
517 sigdat->sign_data->seek (0, IO_SEEK_SET);
519 // V4 now has some hashed subpackets
520 int hashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0;
521 if (hashed_subpkt_size)
522 pkt_walk_subpackets (wlk->pfile, hashed_subpkt_walker, wlk->owner,
523 wlk->pfile->tell (), hashed_subpkt_size, wlk->userdata);
525 // V4 now has some unhashed subpackets
526 int unhashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0;
527 if (unhashed_subpkt_size)
528 pkt_walk_subpackets (wlk->pfile, unhashed_subpkt_walker, wlk->owner,
529 wlk->pfile->tell (), unhashed_subpkt_size, wlk->userdata);
531 // Both formats now have 16 bits of the hash value.
532 int hash_first = pkt_getword (wlk->pfile);
534 MESSAGE ("signature: hash leftmost 2 bytes 0x%04x\n", hash_first);
536 /* Algorithm-Specific Fields for signatures:
538 for DSA:
539 - MPI of DSA value r
540 - MPI of DSA value s
542 DSA signatures MUST use hashes that are equal in size to the number of
543 bits of q, the group generated by the DSA key's generator value.
545 for RSA:
546 - MPI of RSA value m^d mod n (aka s)
548 sigdat->dsa_mpi_r = sigdat->dsa_mpi_s = 0;
549 sigdat->rsa_mpi_s = 0;
551 if (sigdat->pk_alg == RFC4880_PK_DSA)
553 if ((pkt_get_mpi (&sigdat->dsa_mpi_r, wlk->pfile) < 0)
554 || (pkt_get_mpi (&sigdat->dsa_mpi_s, wlk->pfile) < 0))
556 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi.");
557 return pktHALT;
560 else if (sigdat->pk_alg == RFC4880_PK_RSA)
562 if (pkt_get_mpi (&sigdat->rsa_mpi_s, wlk->pfile) < 0)
564 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi.");
565 return pktHALT;
569 MESSAGE ("Read sig packets succesfully!\n");
571 // Now we got all the data out ok, rewind and hash the first trailer.
572 wlk->pfile->seek (v34hdrofs, IO_SEEK_SET);
573 nbytes = (ver == 4) ? (RFC4880_SIGV4_HASHED_OVERHEAD + hashed_subpkt_size)
574 : (RFC4880_SIGV3_HASHED_SIZE);
575 if (nbytes != shovel_stream_data_into_md (wlk->pfile, nbytes, sigdat->md))
577 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error 2.");
578 return pktHALT;
581 if (ver == 4)
583 // And now the synthetic final trailer.
584 gcry_md_putc (sigdat->md, 4);
585 gcry_md_putc (sigdat->md, 0xff);
586 gcry_md_putc (sigdat->md, (nbytes >> 24) & 0xff);
587 gcry_md_putc (sigdat->md, (nbytes >> 16) & 0xff);
588 gcry_md_putc (sigdat->md, (nbytes >> 8) & 0xff);
589 gcry_md_putc (sigdat->md, nbytes & 0xff);
592 // finalize the hash
593 gcry_md_final (sigdat->md);
594 MESSAGE("digest length is %d\n",gcry_md_get_algo_dlen (sigdat->algo));
596 // we have hashed all the data, and found the sig coefficients.
597 // heck this signature
598 if (verify_sig (sigdat, wlk->owner))
599 sigdat->valid = true;
601 // discard hash
602 if (sigdat->md)
603 gcry_md_close (sigdat->md);
605 // discard sig coefffcients
606 if (sigdat->dsa_mpi_r)
607 gcry_mpi_release (sigdat->dsa_mpi_r);
608 if (sigdat->dsa_mpi_s)
609 gcry_mpi_release (sigdat->dsa_mpi_s);
610 if (sigdat->rsa_mpi_s)
611 gcry_mpi_release (sigdat->rsa_mpi_s);
613 // we can stop immediately if we found a good signature
614 return sigdat->valid ? pktHALT : pktCONTINUE;
617 #if CRYPTODEBUGGING
618 static void
619 gcrypt_log_adaptor(void *priv, int level, const char *fmt, va_list args)
621 static std::string collected;
623 char buf[GPG_KEY_SEXPR_BUF_SIZE];
624 vsnprintf (buf, GPG_KEY_SEXPR_BUF_SIZE, fmt, args);
626 char *start = buf;
627 char *end;
631 if (collected.length() == 0)
633 collected = "gcrypt: ";
636 end = strchr(start, '\n');
637 if (end)
638 *end = '\0';
640 collected += start;
642 if (end)
644 if (level == GCRY_LOG_DEBUG)
645 Log (LOG_BABBLE) << collected << endLog;
646 else
647 Log (LOG_PLAIN) << collected << endLog;
649 collected.clear();
650 start = end + 1;
653 while (end);
655 #endif
657 /* Verify the signature on an ini file. Takes care of all key-handling. */
658 bool
659 verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner)
661 /* Data returned from packet walker. */
662 struct sig_data sigdat;
664 /* Vector of keys to use. */
665 std::vector<struct key_info> keys_to_try;
667 /* Overall status of signature. */
668 bool sig_ok = false;
670 // Temps for intermediate processing.
671 gcry_error_t rv;
672 size_t n;
674 /* Initialise the library. */
675 static bool gcrypt_init = false;
676 if (!gcrypt_init)
678 #if CRYPTODEBUGGING
679 gcry_set_log_handler (gcrypt_log_adaptor, NULL);
680 #endif
681 gcry_check_version (NULL);
683 if ((rv = gcry_control (GCRYCTL_SELFTEST)) != GPG_ERR_NO_ERROR)
684 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "libgcrypt selftest failed");
686 #if CRYPTODEBUGGING
687 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
688 #endif
689 gcrypt_init = true;
692 /* So first build the built-in key. */
693 gcry_sexp_t cygwin_key;
694 rv = gcry_sexp_new (&cygwin_key, cygwin_pubkey_sexpr, strlen (cygwin_pubkey_sexpr), 1);
695 if (rv != GPG_ERR_NO_ERROR)
697 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating pubkey s-expr.");
699 else
701 keys_to_try.push_back (key_info("cygwin", true, cygwin_key));
704 #if CRYPTODEBUGGING
705 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
706 n = gcry_sexp_sprint (cygwin_key, GCRYSEXP_FMT_ADVANCED, sexprbuf, GPG_KEY_SEXPR_BUF_SIZE);
707 LogBabblePrintf ("key:%d\n'%s'", n, sexprbuf);
708 #endif /* CRYPTODEBUGGING */
710 /* If not disabled, also try the old built-in key */
711 gcry_sexp_t cygwin_old_key;
712 if (EnableOldKeysOption)
714 rv = gcry_sexp_new (&cygwin_old_key, cygwin_old_pubkey_sexpr, strlen (cygwin_old_pubkey_sexpr), 1);
715 if (rv != GPG_ERR_NO_ERROR)
717 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating old pubkey s-expr.");
719 else
721 keys_to_try.push_back (key_info ("cygwin-old", TRUE, cygwin_old_key));
724 /* Vector of cached extra keys from last run. */
725 static std::vector<gcry_sexp_t> input_keys;
727 /* Next we should extract the keys from the extrakeys user
728 setting, and flush it; we'll only return them to it if they
729 get used. OTOH, should we do this at all? The user settings
730 file isn't heavily protected. So we only trust the extra
731 keys if we're told to by the user. We still read them in
732 and write them back out, which canonicalises and eliminates
733 any duplicates or garbage lines that may have crept in. */
734 static bool input_keys_read = false;
735 if (!input_keys_read)
737 // We only want to do this once, first time through:
738 input_keys_read = true;
739 // Copy all valid keys from ExtraKeysSetting into a
740 // static vector where we can keep them throughout the
741 // remainder of the run.
742 for (size_t i = 0; i < ExtraKeysSetting::instance().num_keys (); i++)
744 const char *keystring = ExtraKeysSetting::instance().get_key (i, &n);
745 gcry_sexp_t newkey;
746 rv = gcry_sexp_new (&newkey, keystring, n, 1);
747 if (rv == GPG_ERR_NO_ERROR)
748 input_keys.push_back (newkey);
751 // Now flush out the ExtraKeysSetting; from here on it
752 // will build up a list of the keys we want to retain.
753 ExtraKeysSetting::instance().flush ();
755 // Which, if we aren't using them, means all the ones
756 // we just read.
757 if (KeepUntrustedKeysOption || !UntrustedKeysOption)
759 std::vector<gcry_sexp_t>::iterator it;
760 for (it = input_keys.begin (); it < input_keys.end (); ++it)
761 add_key_from_sexpr (*it);
765 // We only use the untrusted keys if told to.
766 if (KeepUntrustedKeysOption || UntrustedKeysOption)
767 for (std::vector<gcry_sexp_t>::const_iterator it = input_keys.begin ();
768 it < input_keys.end ();
769 ++it)
771 keys_to_try.push_back (key_info ("saved key", false, *it, false));
774 /* Next, there may have been command-line options. */
775 std::vector<std::string> SexprExtraKeyStrings = SexprExtraKeyOption;
776 for (std::vector<std::string>::const_iterator it
777 = SexprExtraKeyStrings.begin ();
778 it != SexprExtraKeyStrings.end (); ++it)
780 MESSAGE ("key str is '%s'\n", it->c_str ());
781 gcry_sexp_t dsa_key2 = 0;
782 rv = gcry_sexp_new (&dsa_key2, it->c_str (), it->size (), 1);
783 if (rv == GPG_ERR_NO_ERROR)
785 // We probably want to add it to the extra keys setting
786 // if KeepUntrustedKeysOption is supplied.
787 if (KeepUntrustedKeysOption)
788 add_key_from_sexpr (dsa_key2);
789 #if CRYPTODEBUGGING
790 n = gcry_sexp_sprint (dsa_key2, GCRYSEXP_FMT_ADVANCED, sexprbuf,
791 GPG_KEY_SEXPR_BUF_SIZE);
792 // +1 because we want to include the nul-terminator.
793 n = fold_lfs_and_spaces (sexprbuf, n + 1);
794 ExtraKeysSetting::instance().add_key (sexprbuf);
795 LogBabblePrintf ("key2:%d\n'%s'", n, sexprbuf);
796 #endif /* CRYPTODEBUGGING */
797 keys_to_try.push_back (key_info ("from command-line option --sexpr-pubkey", false, dsa_key2));
799 else
801 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "invalid command-line pubkey s-expr.");
805 /* Also, we may have to read a key(s) file. */
806 std::vector<std::string> ExtraKeysFiles = ExtraKeyOption;
807 for (std::vector<std::string>::const_iterator it
808 = ExtraKeysFiles.begin ();
809 it != ExtraKeysFiles.end (); ++it)
811 io_stream *keys = get_url_to_membuf (*it, owner);
812 if (keys)
814 struct key_data kdat;
815 pkt_walk_packets (keys, key_file_walker, owner, 0, keys->get_size (), &kdat);
816 // We now have a vector of (some/any?) keys returned from
817 // the walker; add them to the list to try.
818 while (!kdat.keys.empty ())
820 // We probably want to add it to the extra keys setting
821 // if KeepUntrustedKeysOption is supplied.
822 if (KeepUntrustedKeysOption)
823 add_key_from_sexpr (kdat.keys.back ());
824 #if CRYPTODEBUGGING
825 n = gcry_sexp_sprint (kdat.keys.back (), GCRYSEXP_FMT_ADVANCED,
826 sexprbuf, GPG_KEY_SEXPR_BUF_SIZE);
827 // +1 because we want to include the nul-terminator.
828 n = fold_lfs_and_spaces (sexprbuf, n + 1);
829 ExtraKeysSetting::instance().add_key (sexprbuf);
830 LogBabblePrintf ("key3:%d\n'%s'", n, sexprbuf);
831 #endif /* CRYPTODEBUGGING */
832 keys_to_try.push_back (key_info ("from command-line option --pubkey", false, kdat.keys.back ()));
833 kdat.keys.pop_back ();
838 // We pass in a pointer to the ini file in the user context data,
839 // which the packet walker callback uses to create a new hash
840 // context preloaded with all the signature-covered data.
841 sigdat.valid = false;
842 sigdat.sign_data = ini_file;
843 sigdat.keys_to_try = &keys_to_try;
845 pkt_walk_packets (ini_sig_file, sig_file_walker, owner, 0,
846 ini_sig_file->get_size (), &sigdat);
848 sig_ok = sigdat.valid;
850 while (keys_to_try.size ())
852 if (keys_to_try.back ().owned)
853 gcry_sexp_release (keys_to_try.back ().key);
854 keys_to_try.pop_back ();
857 return sig_ok;