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
12 * Written by Dave Korn <dave.korn.cygwin@gmail.com>
17 static const char *cvsid
=
25 #include "io_stream.h"
31 #include "getopt++/StringOption.h"
32 #include "getopt++/BoolOption.h"
33 #include "KeysSetting.h"
34 #include "gpg-packet.h"
37 #define CRYPTODEBUGGING (0)
40 #define ERRKIND __asm__ __volatile__ (".byte 0xcc"); note
42 #else /* !CRYPTODEBUGGING */
44 #define MESSAGE while (0) msg
45 #endif /* CRYPTODEBUGGING */
47 /* Command-line options for specifying and controlling extra keys. */
48 static StringOption
ExtraKeyOption ("", 'K', "pubkey",
49 "Path to extra public key file (gpg format)", true);
51 static StringOption
SexprExtraKeyOption ("", 'S', "sexpr-pubkey",
52 "Extra public key in s-expr format", true);
54 static BoolOption
UntrustedKeysOption (false, 'u', "untrusted-keys",
55 "Use untrusted keys from last-extrakeys");
56 static BoolOption
KeepUntrustedKeysOption (false, 'U', "keep-untrusted-keys",
57 "Use untrusted keys and retain all");
59 /* Embedded public half of Cygwin DSA signing key. */
60 static const char *cygwin_pubkey_sexpr
=
61 #include "cyg-pubkey.h"
64 /* S-expr template for DSA pubkey. */
65 static const char *dsa_pubkey_templ
= "(public-key (dsa (p %m) (q %m) (g %m) (y %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 data block to be signed. */
71 static const char *data_hash_templ
= "(data (flags raw) (value %m))";
73 /* User context data for sig packet walk. */
76 /* MPI values of sig components. */
77 gcry_mpi_t dsa_mpi_r
, dsa_mpi_s
;
90 /* Converted algo code. */
97 /* User context data for key packet walk. */
100 std::vector
<gcry_sexp_t
> keys
;
103 /* Callback hook for walking packets in gpg key file. Extracts
104 the DSA coefficients from any public key packets encountered and
105 converts them into s-expr pubkey format, returning the public
106 keys thus found to the caller in a vector in the userdata context. */
108 pkt_cb_resp
key_file_walker (struct packet_walker
*wlk
, unsigned char tag
,
109 size_t packetsize
, size_t hdrpos
)
111 struct key_data
*kdat
= (struct key_data
*)(wlk
->userdata
);
113 MESSAGE ("key packet %d size %d at offs $%04x kdat $%08x\n", tag
,
114 packetsize
, hdrpos
, kdat
);
116 if (tag
!= RFC4880_PT_PUBLIC_KEY
)
119 // So, get the data out. Version is first. In case of any errors during
120 // parsing, we just discard the key and continue, hoping to find a good one.
121 char ver
= pkt_getch (wlk
->pfile
);
124 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, ver
, "unsupported key version.");
128 // Only V4 accepted. Discard creation time.
129 if (pkt_getdword (wlk
->pfile
) == -1)
131 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, -1, "missing creation time.");
135 char pkalg
= pkt_getch (wlk
->pfile
);
136 if (pkalg
!= RFC4880_PK_DSA
)
138 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, pkalg
, "unsupported key alg.");
142 // Next, the four MPIs should be present. Read them out,
143 // convert to an s-expr and add that to the list.
144 gcry_mpi_t p
, q
, g
, y
;
147 if ((pkt_get_mpi (&p
, wlk
->pfile
) >= 0)
148 && (pkt_get_mpi (&q
, wlk
->pfile
) >= 0)
149 && (pkt_get_mpi (&g
, wlk
->pfile
) >= 0)
150 && (pkt_get_mpi (&y
, wlk
->pfile
) >= 0))
152 // Convert to s-expr.
156 gcry_error_t rv
= gcry_sexp_build (&new_key
, &n
, dsa_pubkey_templ
, p
, q
, g
, y
);
157 if (rv
!= GPG_ERR_NO_ERROR
)
159 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, rv
, "while creating sig s-expr.");
165 char sexprbuf
[GPG_KEY_SEXPR_BUF_SIZE
];
166 n
= gcry_sexp_sprint (new_key
, GCRYSEXP_FMT_ADVANCED
, sexprbuf
,
167 GPG_KEY_SEXPR_BUF_SIZE
);
168 msg ("key:%d\n'%s'", n
, sexprbuf
);
169 #endif /* CRYPTODEBUGGING */
171 // Return it to caller in the vector.
172 kdat
->keys
.push_back (new_key
);
175 // Release temps and continue.
177 gcry_mpi_release (p
);
179 gcry_mpi_release (q
);
181 gcry_mpi_release (g
);
183 gcry_mpi_release (y
);
188 /* Does what its name suggests: feeds a chosen amount of the data found
189 at the current seek position in an io_stream into the message digest
190 context passed in, using reasonably-sized chunks for efficiency. */
192 shovel_stream_data_into_md (io_stream
*stream
, size_t nbytes
, gcry_md_hd_t md
)
194 const size_t TMPBUFSZ
= 1024;
195 unsigned char tmpbuf
[TMPBUFSZ
];
196 size_t this_time
, total
= 0;
198 MESSAGE ("shovel %d bytes at pos $%08x\n", nbytes
, stream
->tell ());
201 this_time
= (nbytes
> TMPBUFSZ
) ? TMPBUFSZ
: nbytes
;
202 actual
= stream
->read (tmpbuf
, this_time
);
205 gcry_md_write (md
, tmpbuf
, actual
);
208 if (actual
!= (ssize_t
)this_time
)
214 /* Canonicalise an s-expr by converting LFs to spaces so that
215 it's all on one line and folding multiple spaces as we go. */
217 fold_lfs_and_spaces (char *buf
, size_t n
)
219 char *ptr1
= buf
, *ptr2
= buf
;
228 while (n
&& ((*ptr1
== ' ') || (*ptr1
== 0x0a)))
237 /* Do-nothing stubs called by the sig file walker to
238 walk over the embedded subpackets. In the event, we don't
239 actually need to do this as we aren't inspecting them. */
241 pkt_cb_resp
hashed_subpkt_walker (struct packet_walker
*wlk
, unsigned char tag
,
242 size_t packetsize
, size_t hdrpos
)
248 pkt_cb_resp
unhashed_subpkt_walker (struct packet_walker
*wlk
, unsigned char tag
,
249 size_t packetsize
, size_t hdrpos
)
254 /* Callback to parse the packets found in the setup.ini/setup.bz2
255 signature file. We have to parse the header to get the hash type
256 and other details. Once we have that we can create a message
257 digest context and start pumping data through it; first the ini
258 file itself, then the portion of the packet itself that is
259 covered by the hash. */
261 pkt_cb_resp
sig_file_walker (struct packet_walker
*wlk
, unsigned char tag
,
262 size_t packetsize
, size_t hdrpos
)
264 struct sig_data
*sigdat
= (struct sig_data
*)(wlk
->userdata
);
265 sigdat
->complete
= false;
267 if (tag
!= RFC4880_PT_SIGNATURE
)
270 // To add the trailers later, we hang on to the current pos.
271 size_t v34hdrofs
= wlk
->pfile
->tell ();
273 // So, get the data out. Version is first.
274 char ver
= pkt_getch (wlk
->pfile
);
275 if ((ver
< 3) || (ver
> 4))
277 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, ver
, "unsupported sig version.");
281 // Only V3 and V4 accepted.
284 sigdat
->sig_type
= pkt_getch (wlk
->pfile
);
285 sigdat
->pk_alg
= pkt_getch (wlk
->pfile
);
286 sigdat
->hash_alg
= pkt_getch (wlk
->pfile
);
290 int hmsize
= pkt_getch (wlk
->pfile
);
291 if (hmsize
!= RFC4880_SIGV3_HASHED_SIZE
)
293 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, hmsize
, "wrong hashed material size.");
296 v34hdrofs
= wlk
->pfile
->tell ();
297 if ((pkt_getch (wlk
->pfile
) < 0) || (pkt_getdword (wlk
->pfile
) == -1))
299 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, hmsize
, "wrong hashed material size.");
302 if ((pkt_getdword (wlk
->pfile
) == -1) || (pkt_getdword (wlk
->pfile
) == -1))
304 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, -1, "missing signer ID.");
308 sigdat
->sig_type
= 0;
309 sigdat
->pk_alg
= pkt_getch (wlk
->pfile
);
310 sigdat
->hash_alg
= pkt_getch (wlk
->pfile
);
313 MESSAGE ("sig type %d, pk_alg %d, hash_alg %d\n", sigdat
->sig_type
,
314 sigdat
->pk_alg
, sigdat
->hash_alg
);
316 // We only handle binary file signatures
317 if (sigdat
->sig_type
!= RFC4880_ST_BINARY
)
319 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, sigdat
->sig_type
, "unsupported sig type.");
322 // And we only speak DSA.
323 if (sigdat
->pk_alg
!= RFC4880_PK_DSA
)
325 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, sigdat
->pk_alg
, "unsupported pk alg.");
329 // Start to hash all the data. Figure out what hash to use.
330 sigdat
->algo
= pkt_convert_hashcode (sigdat
->hash_alg
);
331 if (sigdat
->algo
== GCRY_MD_NONE
)
333 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, sigdat
->hash_alg
, "unconvertible hash.");
337 // Now we know hash algo, we can create md context.
338 gcry_error_t rv
= gcry_md_open (&sigdat
->md
, sigdat
->algo
, 0);
339 if (rv
!= GPG_ERR_NO_ERROR
)
341 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, rv
, "while initialising message digest.");
345 // Add all the sig_file data into the hash.
346 sigdat
->sign_data
->seek (0, IO_SEEK_SET
);
347 size_t nbytes
= sigdat
->sign_data
->get_size ();
348 if (nbytes
!= shovel_stream_data_into_md (sigdat
->sign_data
, nbytes
, sigdat
->md
))
350 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, sigdat
->hash_alg
, "internal buffer error.");
353 sigdat
->sign_data
->seek (0, IO_SEEK_SET
);
355 // V4 now has some hashed subpackets
356 int hashed_subpkt_size
= (ver
== 4) ? pkt_getword (wlk
->pfile
) : 0;
357 if (hashed_subpkt_size
)
358 pkt_walk_subpackets (wlk
->pfile
, hashed_subpkt_walker
, wlk
->owner
,
359 wlk
->pfile
->tell (), hashed_subpkt_size
, wlk
->userdata
);
361 // V4 now has some unhashed subpackets
362 int unhashed_subpkt_size
= (ver
== 4) ? pkt_getword (wlk
->pfile
) : 0;
363 if (unhashed_subpkt_size
)
364 pkt_walk_subpackets (wlk
->pfile
, unhashed_subpkt_walker
, wlk
->owner
,
365 wlk
->pfile
->tell (), unhashed_subpkt_size
, wlk
->userdata
);
367 // Both formats now have 16 bits of the hash value.
368 int hash_first
= pkt_getword (wlk
->pfile
);
370 MESSAGE ("sig type %d, pk_alg %d, hash_alg %d - first $%04x\n", sigdat
->sig_type
,
371 sigdat
->pk_alg
, sigdat
->hash_alg
, hash_first
);
373 /* Algorithm-Specific Fields for DSA signatures:
375 - MPI of DSA value r.
377 - MPI of DSA value s.
379 DSA signatures MUST use hashes that are equal in size to the number
380 of bits of q, the group generated by the DSA key's generator value. */
382 if ((pkt_get_mpi (&sigdat
->dsa_mpi_r
, wlk
->pfile
) < 0)
383 || (pkt_get_mpi (&sigdat
->dsa_mpi_s
, wlk
->pfile
) < 0))
385 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, "unpacking mpi.");
389 MESSAGE ("Read sig packets succesfully!\n");
391 // Now we got all the data out ok, rewind and hash the first trailer.
392 wlk
->pfile
->seek (v34hdrofs
, IO_SEEK_SET
);
393 nbytes
= (ver
== 4) ? (RFC4880_SIGV4_HASHED_OVERHEAD
+ hashed_subpkt_size
)
394 : (RFC4880_SIGV3_HASHED_SIZE
);
395 if (nbytes
!= shovel_stream_data_into_md (wlk
->pfile
, nbytes
, sigdat
->md
))
397 ERRKIND (wlk
->owner
, IDS_CRYPTO_ERROR
, sigdat
->hash_alg
, "internal buffer error 2.");
403 // And now the synthetic final trailer.
404 gcry_md_putc (sigdat
->md
, 4);
405 gcry_md_putc (sigdat
->md
, 0xff);
406 gcry_md_putc (sigdat
->md
, (nbytes
>> 24) & 0xff);
407 gcry_md_putc (sigdat
->md
, (nbytes
>> 16) & 0xff);
408 gcry_md_putc (sigdat
->md
, (nbytes
>> 8) & 0xff);
409 gcry_md_putc (sigdat
->md
, nbytes
& 0xff);
412 // Hooray, succeeded!
413 sigdat
->complete
= true;
418 /* Size and allocate a temp buffer to print a representation
419 of a public key s-expr into, then add that to the extra keys
420 setting so it persists for the next run. */
422 add_key_from_sexpr (gcry_sexp_t key
)
424 size_t n
= gcry_sexp_sprint (key
, GCRYSEXP_FMT_ADVANCED
, 0, ~0);
425 char *sexprbuf
= new char[n
];
426 n
= gcry_sexp_sprint (key
, GCRYSEXP_FMT_ADVANCED
, sexprbuf
, n
);
427 // +1 because we want to include the nul-terminator.
428 n
= fold_lfs_and_spaces (sexprbuf
, n
+ 1);
429 ExtraKeysSetting::instance().add_key (sexprbuf
);
430 MESSAGE ("keep:%d\n'%s'", n
, sexprbuf
);
434 /* Verify the signature on an ini file. Takes care of all key-handling. */
436 verify_ini_file_sig (io_stream
*ini_file
, io_stream
*ini_sig_file
, HWND owner
)
438 /* DSA public key in s-expr format. */
441 /* Data returned from packet walker. */
442 struct sig_data sigdat
;
444 /* Vector of extra keys to use. */
445 std::vector
<gcry_sexp_t
> keys_to_try
;
447 /* Vector of cached extra keys from last run. */
448 static std::vector
<gcry_sexp_t
> input_keys
;
450 /* Overall status of signature. */
453 // Temps for intermediate processing.
457 /* Initialise the library. */
458 gcry_check_version (NULL
);
460 /* So first build the built-in key. */
461 rv
= gcry_sexp_new (&dsa_key
, cygwin_pubkey_sexpr
, strlen (cygwin_pubkey_sexpr
), 1);
462 if (rv
!= GPG_ERR_NO_ERROR
)
464 ERRKIND (owner
, IDS_CRYPTO_ERROR
, rv
, "while creating pubkey s-expr.");
468 char sexprbuf
[GPG_KEY_SEXPR_BUF_SIZE
];
469 n
= gcry_sexp_sprint (dsa_key
, GCRYSEXP_FMT_ADVANCED
, sexprbuf
, GPG_KEY_SEXPR_BUF_SIZE
);
470 msg ("key:%d\n'%s'", n
, sexprbuf
);
471 #endif /* CRYPTODEBUGGING */
473 /* Next we should extract the keys from the last-extrakeys
474 file, and flush it; we'll only return them to it if they
475 get used. OTOH, should we do this at all? The extrakeys
476 file isn't heavily protected. So we only trust the extra
477 keys if we're told to by the user. We still read them in
478 and write them back out, which canonicalises and eliminates
479 any duplicates or garbage lines that may have crept in. */
480 static bool input_keys_read
= false;
481 if (!input_keys_read
)
483 // We only want to do this once, first time through:
484 input_keys_read
= true;
485 // Copy all valid keys from ExtraKeysSetting into a
486 // static vector where we can keep them throughout the
487 // remainder of the run.
488 for (size_t i
= 0; i
< ExtraKeysSetting::instance().num_keys (); i
++)
490 const char *keystring
= ExtraKeysSetting::instance().get_key (i
, &n
);
492 rv
= gcry_sexp_new (&newkey
, keystring
, n
, 1);
493 if (rv
== GPG_ERR_NO_ERROR
)
494 input_keys
.push_back (newkey
);
497 // Now flush out the ExtraKeysSetting; from here on it
498 // will build up a list of the keys we want to retain.
499 ExtraKeysSetting::instance().flush ();
501 // Which, if we aren't using them, means all the ones
503 if (KeepUntrustedKeysOption
|| !UntrustedKeysOption
)
505 std::vector
<gcry_sexp_t
>::iterator it
;
506 for (it
= input_keys
.begin (); it
< input_keys
.end (); ++it
)
507 add_key_from_sexpr (*it
);
511 /* Next, there may have been command-line options. */
512 std::string SexprExtraKeyString
= SexprExtraKeyOption
;
513 MESSAGE ("key str is '%s'\n", SexprExtraKeyString
.c_str ());
514 if (SexprExtraKeyString
.size ())
516 gcry_sexp_t dsa_key2
= 0;
517 rv
= gcry_sexp_new (&dsa_key2
, SexprExtraKeyString
.c_str (),
518 SexprExtraKeyString
.size (), 1);
519 if (rv
== GPG_ERR_NO_ERROR
)
521 // We probably want to add it to the extra keys setting
522 // if KeepUntrustedKeysOption is supplied.
523 if (KeepUntrustedKeysOption
)
524 add_key_from_sexpr (dsa_key2
);
526 n
= gcry_sexp_sprint (dsa_key2
, GCRYSEXP_FMT_ADVANCED
, sexprbuf
,
527 GPG_KEY_SEXPR_BUF_SIZE
);
528 // +1 because we want to include the nul-terminator.
529 n
= fold_lfs_and_spaces (sexprbuf
, n
+ 1);
530 ExtraKeysSetting::instance().add_key (sexprbuf
);
531 msg ("key2:%d\n'%s'", n
, sexprbuf
);
532 #endif /* CRYPTODEBUGGING */
533 keys_to_try
.push_back (dsa_key2
);
537 ERRKIND (owner
, IDS_CRYPTO_ERROR
, rv
, "invalid command-line pubkey s-expr.");
541 /* Also, we may have to read a key(s) file. */
542 std::string ExtraKeysFile
= ExtraKeyOption
;
543 if (ExtraKeysFile
.size ())
545 io_stream
*keys
= get_url_to_membuf (ExtraKeysFile
, owner
);
548 struct key_data kdat
;
549 pkt_walk_packets (keys
, key_file_walker
, owner
, 0, keys
->get_size (), &kdat
);
550 // We now have a vector of (some/any?) keys returned from
551 // the walker; add them to the list to try.
552 while (!kdat
.keys
.empty ())
554 // We probably want to add it to the extra keys setting
555 // if KeepUntrustedKeysOption is supplied.
556 if (KeepUntrustedKeysOption
)
557 add_key_from_sexpr (kdat
.keys
.back ());
559 n
= gcry_sexp_sprint (kdat
.keys
.back (), GCRYSEXP_FMT_ADVANCED
,
560 sexprbuf
, GPG_KEY_SEXPR_BUF_SIZE
);
561 // +1 because we want to include the nul-terminator.
562 n
= fold_lfs_and_spaces (sexprbuf
, n
+ 1);
563 ExtraKeysSetting::instance().add_key (sexprbuf
);
564 msg ("key3:%d\n'%s'", n
, sexprbuf
);
565 #endif /* CRYPTODEBUGGING */
566 keys_to_try
.push_back (kdat
.keys
.back ());
567 kdat
.keys
.pop_back ();
572 // We pass in a pointer to the ini file in the user context data,
573 // which the packet walker callback uses to create a new hash
574 // context preloaded with all the signature-covered data.
575 sigdat
.complete
= false;
576 sigdat
.sign_data
= ini_file
;
577 sigdat
.dsa_mpi_r
= sigdat
.dsa_mpi_s
= 0;
579 pkt_walk_packets (ini_sig_file
, sig_file_walker
, owner
, 0,
580 ini_sig_file
->get_size (), &sigdat
);
583 /* DSA sig coefficients in s-expr format. */
586 /* DSA signature hash data in s-expr format. */
587 gcry_sexp_t dsa_hash
;
589 /* So, we have hashed all the data, and found the sig coefficients.
590 Next stages are to finalise the hash, build everything into
591 s-exprs, and call the libgcrypt verification routine. */
593 rv
= gcry_sexp_build (&dsa_sig
, &n
, dsa_sig_templ
, sigdat
.dsa_mpi_r
,
595 if (rv
!= GPG_ERR_NO_ERROR
)
597 ERRKIND (owner
, IDS_CRYPTO_ERROR
, rv
, "while creating sig s-expr.");
601 gcry_md_final (sigdat
.md
);
603 // Make a temp mpi from the hash output, then an s-expr from that.
604 gcry_mpi_t dsa_mpi_hash
= 0;
605 unsigned char *tmpbuf
= gcry_md_read (sigdat
.md
, 0);
606 size_t dlen
= gcry_md_get_algo_dlen (sigdat
.algo
);
607 rv
= gcry_mpi_scan (&dsa_mpi_hash
, GCRYMPI_FMT_USG
, tmpbuf
, dlen
, 0UL);
608 if (rv
!= GPG_ERR_NO_ERROR
)
610 ERRKIND (owner
, IDS_CRYPTO_ERROR
, rv
, "while creating hash MPI.");
614 rv
= gcry_sexp_build (&dsa_hash
, &n
, data_hash_templ
, dsa_mpi_hash
);
615 if (rv
!= GPG_ERR_NO_ERROR
)
617 ERRKIND (owner
, IDS_CRYPTO_ERROR
, rv
, "while creating hash s-expr.");
622 n
= gcry_sexp_sprint (dsa_sig
, GCRYSEXP_FMT_ADVANCED
, sexprbuf
,
623 GPG_KEY_SEXPR_BUF_SIZE
);
624 msg ("sig:%d\n'%s'", n
, sexprbuf
);
625 n
= gcry_sexp_sprint (dsa_hash
, GCRYSEXP_FMT_ADVANCED
, sexprbuf
,
626 GPG_KEY_SEXPR_BUF_SIZE
);
627 msg ("hash:%d\n'%s'", n
, sexprbuf
);
628 #endif /* CRYPTODEBUGGING */
630 // Well, we're actually there! Try it against the main key.
631 rv
= gcry_pk_verify (dsa_sig
, dsa_hash
, dsa_key
);
632 // If not that, try any supplied on the commandline.
633 if (rv
!= GPG_ERR_NO_ERROR
)
635 std::vector
<gcry_sexp_t
>::iterator it
;
636 for (it
= keys_to_try
.begin (); it
< keys_to_try
.end (); ++it
)
638 MESSAGE ("Testing a key to try\n");
639 rv
= gcry_pk_verify (dsa_sig
, dsa_hash
, *it
);
640 if (rv
!= GPG_ERR_NO_ERROR
)
642 // Found it! This key gets kept!
643 add_key_from_sexpr (*it
);
647 // We only use the untrusted keys if told to.
648 it
= ((rv
!= GPG_ERR_NO_ERROR
)
649 && (KeepUntrustedKeysOption
|| UntrustedKeysOption
))
650 ? input_keys
.begin ()
652 for ( ; it
< input_keys
.end (); ++it
)
654 MESSAGE ("Testing an input key\n");
655 rv
= gcry_pk_verify (dsa_sig
, dsa_hash
, *it
);
656 if (rv
!= GPG_ERR_NO_ERROR
)
658 // Found it! This key gets kept!
659 add_key_from_sexpr (*it
);
664 sig_ok
= (rv
== GPG_ERR_NO_ERROR
);
667 gcry_err_code_t code
;
668 gcry_err_source_t src
;
669 code
= gcry_err_code (rv
);
670 src
= gcry_err_source (rv
);
671 msg ("Well, pk verify returned $%08x - code %d src %d\n", rv
, code
, src
);
672 #endif /* CRYPTODEBUGGING */
674 gcry_mpi_release (dsa_mpi_hash
);
675 gcry_sexp_release (dsa_sig
);
676 gcry_sexp_release (dsa_hash
);
679 // Discard the temp data then.
680 gcry_sexp_release (dsa_key
);
681 if (sigdat
.dsa_mpi_r
)
682 gcry_mpi_release (sigdat
.dsa_mpi_r
);
683 if (sigdat
.dsa_mpi_s
)
684 gcry_mpi_release (sigdat
.dsa_mpi_s
);
686 gcry_md_close (sigdat
.md
);
687 while (keys_to_try
.size ())
689 gcry_sexp_release (keys_to_try
.back ());
690 keys_to_try
.pop_back ();