1 /* keybox-search.c - Search operations
2 * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
27 #include "../jnlib/stringhelp.h" /* ascii_xxxx() */
29 #include "keybox-defs.h"
33 #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
34 *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
35 #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
46 get32 (const byte
*buffer
)
57 get16 (const byte
*buffer
)
68 blob_get_type (KEYBOXBLOB blob
)
70 const unsigned char *buffer
;
73 buffer
= _keybox_get_blob_image (blob
, &length
);
75 return -1; /* blob too short */
80 static inline unsigned int
81 blob_get_blob_flags (KEYBOXBLOB blob
)
83 const unsigned char *buffer
;
86 buffer
= _keybox_get_blob_image (blob
, &length
);
90 return get16 (buffer
+ 6);
94 /* Return information on the flag WHAT within the blob BUFFER,LENGTH.
95 Return the offset and the length (in bytes) of the flag in
98 _keybox_get_flag_location (const unsigned char *buffer
, size_t length
,
99 int what
, size_t *flag_off
, size_t *flag_size
)
102 size_t nkeys
, keyinfolen
;
103 size_t nuids
, uidinfolen
;
105 size_t nsigs
, siginfolen
;
109 case KEYBOX_FLAG_BLOB
:
111 return GPG_ERR_INV_OBJ
;
116 case KEYBOX_FLAG_OWNERTRUST
:
117 case KEYBOX_FLAG_VALIDITY
:
118 case KEYBOX_FLAG_CREATED_AT
:
120 return GPG_ERR_INV_OBJ
;
122 nkeys
= get16 (buffer
+ 16);
123 keyinfolen
= get16 (buffer
+ 18 );
125 return GPG_ERR_INV_OBJ
;
126 pos
= 20 + keyinfolen
*nkeys
;
128 return GPG_ERR_INV_OBJ
; /* Out of bounds. */
130 nserial
= get16 (buffer
+pos
);
133 return GPG_ERR_INV_OBJ
; /* Out of bounds. */
135 nuids
= get16 (buffer
+ pos
); pos
+= 2;
136 uidinfolen
= get16 (buffer
+ pos
); pos
+= 2;
137 if (uidinfolen
< 12 )
138 return GPG_ERR_INV_OBJ
;
139 pos
+= uidinfolen
*nuids
;
141 return GPG_ERR_INV_OBJ
; /* Out of bounds. */
142 /* Signature info. */
143 nsigs
= get16 (buffer
+ pos
); pos
+= 2;
144 siginfolen
= get16 (buffer
+ pos
); pos
+= 2;
146 return GPG_ERR_INV_OBJ
;
147 pos
+= siginfolen
*nsigs
;
148 if (pos
+1+1+2+4+4+4+4 > length
)
149 return GPG_ERR_INV_OBJ
; /* Out of bounds. */
154 case KEYBOX_FLAG_VALIDITY
:
157 case KEYBOX_FLAG_CREATED_AT
:
159 *flag_off
+= 1+2+4+4+4;
167 return GPG_ERR_INV_FLAG
;
174 /* Return one of the flags WHAT in VALUE from teh blob BUFFER of
175 LENGTH bytes. Return 0 on success or an raw error code. */
176 static gpg_err_code_t
177 get_flag_from_image (const unsigned char *buffer
, size_t length
,
178 int what
, unsigned int *value
)
184 ec
= _keybox_get_flag_location (buffer
, length
, what
, &pos
, &size
);
188 case 1: *value
= buffer
[pos
]; break;
189 case 2: *value
= get16 (buffer
+ pos
); break;
190 case 4: *value
= get32 (buffer
+ pos
); break;
191 default: ec
= GPG_ERR_BUG
; break;
199 blob_cmp_sn (KEYBOXBLOB blob
, const unsigned char *sn
, int snlen
)
201 const unsigned char *buffer
;
204 size_t nkeys
, keyinfolen
;
207 buffer
= _keybox_get_blob_image (blob
, &length
);
209 return 0; /* blob too short */
212 nkeys
= get16 (buffer
+ 16);
213 keyinfolen
= get16 (buffer
+ 18 );
215 return 0; /* invalid blob */
216 pos
= 20 + keyinfolen
*nkeys
;
218 return 0; /* out of bounds */
221 nserial
= get16 (buffer
+pos
);
223 if (off
+nserial
> length
)
224 return 0; /* out of bounds */
226 return nserial
== snlen
&& !memcmp (buffer
+off
, sn
, snlen
);
231 blob_cmp_fpr (KEYBOXBLOB blob
, const unsigned char *fpr
)
233 const unsigned char *buffer
;
236 size_t nkeys
, keyinfolen
;
239 buffer
= _keybox_get_blob_image (blob
, &length
);
241 return 0; /* blob too short */
244 nkeys
= get16 (buffer
+ 16);
245 keyinfolen
= get16 (buffer
+ 18 );
247 return 0; /* invalid blob */
249 if (pos
+ keyinfolen
*nkeys
> length
)
250 return 0; /* out of bounds */
252 for (idx
=0; idx
< nkeys
; idx
++)
254 off
= pos
+ idx
*keyinfolen
;
255 if (!memcmp (buffer
+ off
, fpr
, 20))
256 return 1; /* found */
258 return 0; /* not found */
262 blob_cmp_fpr_part (KEYBOXBLOB blob
, const unsigned char *fpr
,
263 int fproff
, int fprlen
)
265 const unsigned char *buffer
;
268 size_t nkeys
, keyinfolen
;
271 buffer
= _keybox_get_blob_image (blob
, &length
);
273 return 0; /* blob too short */
276 nkeys
= get16 (buffer
+ 16);
277 keyinfolen
= get16 (buffer
+ 18 );
279 return 0; /* invalid blob */
281 if (pos
+ keyinfolen
*nkeys
> length
)
282 return 0; /* out of bounds */
284 for (idx
=0; idx
< nkeys
; idx
++)
286 off
= pos
+ idx
*keyinfolen
;
287 if (!memcmp (buffer
+ off
+ fproff
, fpr
, fprlen
))
288 return 1; /* found */
290 return 0; /* not found */
295 blob_cmp_name (KEYBOXBLOB blob
, int idx
,
296 const char *name
, size_t namelen
, int substr
)
298 const unsigned char *buffer
;
300 size_t pos
, off
, len
;
301 size_t nkeys
, keyinfolen
;
302 size_t nuids
, uidinfolen
;
305 buffer
= _keybox_get_blob_image (blob
, &length
);
307 return 0; /* blob too short */
310 nkeys
= get16 (buffer
+ 16);
311 keyinfolen
= get16 (buffer
+ 18 );
313 return 0; /* invalid blob */
314 pos
= 20 + keyinfolen
*nkeys
;
316 return 0; /* out of bounds */
319 nserial
= get16 (buffer
+pos
);
322 return 0; /* out of bounds */
325 nuids
= get16 (buffer
+ pos
); pos
+= 2;
326 uidinfolen
= get16 (buffer
+ pos
); pos
+= 2;
327 if (uidinfolen
< 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */)
328 return 0; /* invalid blob */
329 if (pos
+ uidinfolen
*nuids
> length
)
330 return 0; /* out of bounds */
333 { /* compare all names starting with that (negated) index */
336 for ( ;idx
< nuids
; idx
++)
340 mypos
+= idx
*uidinfolen
;
341 off
= get32 (buffer
+mypos
);
342 len
= get32 (buffer
+mypos
+4);
343 if (off
+len
> length
)
344 return 0; /* error: better stop here out of bounds */
346 continue; /* empty name */
349 if (ascii_memcasemem (buffer
+off
, len
, name
, namelen
))
350 return 1; /* found */
354 if (len
== namelen
&& !memcmp (buffer
+off
, name
, len
))
355 return 1; /* found */
358 return 0; /* not found */
363 return 0; /* no user ID with that idx */
364 pos
+= idx
*uidinfolen
;
365 off
= get32 (buffer
+pos
);
366 len
= get32 (buffer
+pos
+4);
367 if (off
+len
> length
)
368 return 0; /* out of bounds */
370 return 0; /* empty name */
374 return !!ascii_memcasemem (buffer
+off
, len
, name
, namelen
);
378 return len
== namelen
&& !memcmp (buffer
+off
, name
, len
);
384 /* compare all email addresses of the subject. With SUBSTR given as
385 True a substring search is done in the mail address */
387 blob_cmp_mail (KEYBOXBLOB blob
, const char *name
, size_t namelen
, int substr
)
389 const unsigned char *buffer
;
391 size_t pos
, off
, len
;
392 size_t nkeys
, keyinfolen
;
393 size_t nuids
, uidinfolen
;
397 /* fixme: this code is common to blob_cmp_mail */
398 buffer
= _keybox_get_blob_image (blob
, &length
);
400 return 0; /* blob too short */
403 nkeys
= get16 (buffer
+ 16);
404 keyinfolen
= get16 (buffer
+ 18 );
406 return 0; /* invalid blob */
407 pos
= 20 + keyinfolen
*nkeys
;
409 return 0; /* out of bounds */
412 nserial
= get16 (buffer
+pos
);
415 return 0; /* out of bounds */
418 nuids
= get16 (buffer
+ pos
); pos
+= 2;
419 uidinfolen
= get16 (buffer
+ pos
); pos
+= 2;
420 if (uidinfolen
< 12 /* should add a: || nuidinfolen > MAX_UIDINFOLEN */)
421 return 0; /* invalid blob */
422 if (pos
+ uidinfolen
*nuids
> length
)
423 return 0; /* out of bounds */
428 for (idx
=1 ;idx
< nuids
; idx
++)
432 mypos
+= idx
*uidinfolen
;
433 off
= get32 (buffer
+mypos
);
434 len
= get32 (buffer
+mypos
+4);
435 if (off
+len
> length
)
436 return 0; /* error: better stop here out of bounds */
437 if (len
< 2 || buffer
[off
] != '<')
438 continue; /* empty name or trailing 0 not stored */
439 len
--; /* one back */
440 if ( len
< 3 || buffer
[off
+len
] != '>')
441 continue; /* not a proper email address */
445 if (ascii_memcasemem (buffer
+off
+1, len
, name
, namelen
))
446 return 1; /* found */
450 if (len
== namelen
&& !ascii_memcasecmp (buffer
+off
+1, name
, len
))
451 return 1; /* found */
454 return 0; /* not found */
458 #ifdef KEYBOX_WITH_X509
459 /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
460 We don't have the keygrips as meta data, thus we need to parse the
461 certificate. Fixme: We might want to return proper error codes
462 instead of failing a search for invalid certificates etc. */
464 blob_x509_has_grip (KEYBOXBLOB blob
, const unsigned char *grip
)
467 const unsigned char *buffer
;
469 size_t cert_off
, cert_len
;
470 ksba_reader_t reader
= NULL
;
471 ksba_cert_t cert
= NULL
;
472 ksba_sexp_t p
= NULL
;
474 unsigned char array
[20];
478 buffer
= _keybox_get_blob_image (blob
, &length
);
480 return 0; /* Too short. */
481 cert_off
= get32 (buffer
+8);
482 cert_len
= get32 (buffer
+12);
483 if (cert_off
+cert_len
> length
)
484 return 0; /* Too short. */
486 rc
= ksba_reader_new (&reader
);
488 return 0; /* Problem with ksba. */
489 rc
= ksba_reader_set_mem (reader
, buffer
+cert_off
, cert_len
);
492 rc
= ksba_cert_new (&cert
);
495 rc
= ksba_cert_read_der (cert
, reader
);
498 p
= ksba_cert_get_public_key (cert
);
501 n
= gcry_sexp_canon_len (p
, 0, NULL
, NULL
);
504 rc
= gcry_sexp_sscan (&s_pkey
, NULL
, (char*)p
, n
);
507 gcry_sexp_release (s_pkey
);
510 rcp
= gcry_pk_get_keygrip (s_pkey
, array
);
511 gcry_sexp_release (s_pkey
);
513 goto failed
; /* Can't calculate keygrip. */
516 ksba_cert_release (cert
);
517 ksba_reader_release (reader
);
518 return !memcmp (array
, grip
, 20);
521 ksba_cert_release (cert
);
522 ksba_reader_release (reader
);
525 #endif /*KEYBOX_WITH_X509*/
530 The has_foo functions are used as helpers for search
533 has_short_kid (KEYBOXBLOB blob
, const unsigned char *kid
)
535 return blob_cmp_fpr_part (blob
, kid
+4, 16, 4);
539 has_long_kid (KEYBOXBLOB blob
, const unsigned char *kid
)
541 return blob_cmp_fpr_part (blob
, kid
, 12, 8);
545 has_fingerprint (KEYBOXBLOB blob
, const unsigned char *fpr
)
547 return blob_cmp_fpr (blob
, fpr
);
551 has_keygrip (KEYBOXBLOB blob
, const unsigned char *grip
)
553 #ifdef KEYBOX_WITH_X509
554 if (blob_get_type (blob
) == BLOBTYPE_X509
)
555 return blob_x509_has_grip (blob
, grip
);
562 has_issuer (KEYBOXBLOB blob
, const char *name
)
566 return_val_if_fail (name
, 0);
568 if (blob_get_type (blob
) != BLOBTYPE_X509
)
571 namelen
= strlen (name
);
572 return blob_cmp_name (blob
, 0 /* issuer */, name
, namelen
, 0);
576 has_issuer_sn (KEYBOXBLOB blob
, const char *name
,
577 const unsigned char *sn
, int snlen
)
581 return_val_if_fail (name
, 0);
582 return_val_if_fail (sn
, 0);
584 if (blob_get_type (blob
) != BLOBTYPE_X509
)
587 namelen
= strlen (name
);
589 return (blob_cmp_sn (blob
, sn
, snlen
)
590 && blob_cmp_name (blob
, 0 /* issuer */, name
, namelen
, 0));
594 has_sn (KEYBOXBLOB blob
, const unsigned char *sn
, int snlen
)
596 return_val_if_fail (sn
, 0);
598 if (blob_get_type (blob
) != BLOBTYPE_X509
)
600 return blob_cmp_sn (blob
, sn
, snlen
);
604 has_subject (KEYBOXBLOB blob
, const char *name
)
608 return_val_if_fail (name
, 0);
610 if (blob_get_type (blob
) != BLOBTYPE_X509
)
613 namelen
= strlen (name
);
614 return blob_cmp_name (blob
, 1 /* subject */, name
, namelen
, 0);
618 has_subject_or_alt (KEYBOXBLOB blob
, const char *name
, int substr
)
622 return_val_if_fail (name
, 0);
624 if (blob_get_type (blob
) != BLOBTYPE_X509
)
627 namelen
= strlen (name
);
628 return blob_cmp_name (blob
, -1 /* all subject names*/, name
,
634 has_mail (KEYBOXBLOB blob
, const char *name
, int substr
)
638 return_val_if_fail (name
, 0);
640 if (blob_get_type (blob
) != BLOBTYPE_X509
)
643 namelen
= strlen (name
);
644 if (namelen
&& name
[namelen
-1] == '>')
646 return blob_cmp_mail (blob
, name
, namelen
, substr
);
651 release_sn_array (struct sn_array_s
*array
, size_t size
)
655 for (n
=0; n
< size
; n
++)
668 keybox_search_reset (KEYBOX_HANDLE hd
)
671 return gpg_error (GPG_ERR_INV_VALUE
);
675 _keybox_release_blob (hd
->found
.blob
);
676 hd
->found
.blob
= NULL
;
690 /* Note: When in ephemeral mode the search function does visit all
691 blobs but in standard mode, blobs flagged as ephemeral are ignored. */
693 keybox_search (KEYBOX_HANDLE hd
, KEYBOX_SEARCH_DESC
*desc
, size_t ndesc
)
697 int need_words
, any_skip
;
698 KEYBOXBLOB blob
= NULL
;
699 struct sn_array_s
*sn_array
= NULL
;
702 return gpg_error (GPG_ERR_INV_VALUE
);
704 /* clear last found result */
707 _keybox_release_blob (hd
->found
.blob
);
708 hd
->found
.blob
= NULL
;
712 return hd
->error
; /* still in error state */
714 return -1; /* still EOF */
716 /* figure out what information we need */
717 need_words
= any_skip
= 0;
718 for (n
=0; n
< ndesc
; n
++)
720 switch (desc
[n
].mode
)
722 case KEYDB_SEARCH_MODE_WORDS
:
725 case KEYDB_SEARCH_MODE_FIRST
:
726 /* always restart the search in this mode */
727 keybox_search_reset (hd
);
734 if (desc
[n
].snlen
== -1 && !sn_array
)
736 sn_array
= xtrycalloc (ndesc
, sizeof *sn_array
);
738 return (hd
->error
= gpg_error_from_syserror ());
742 (void)need_words
; /* Not yet implemented. */
746 hd
->fp
= fopen (hd
->kb
->fname
, "rb");
749 hd
->error
= gpg_error_from_syserror ();
755 /* Kludge: We need to convert an SN given as hexstring to its binary
756 representation - in some cases we are not able to store it in the
757 search descriptor, because due to the way we use it, it is not
758 possible to free allocated memory. */
761 const unsigned char *s
;
765 for (n
=0; n
< ndesc
; n
++)
769 else if (desc
[n
].snlen
== -1)
774 for (i
=0; *s
&& *s
!= '/'; s
++, i
++)
778 sn_array
[n
].sn
= xtrymalloc (snlen
);
781 hd
->error
= gpg_error_from_syserror ();
782 release_sn_array (sn_array
, n
);
785 sn_array
[n
].snlen
= snlen
;
793 for (; *s
&& *s
!= '/'; s
+= 2)
798 const unsigned char *sn
;
801 snlen
= desc
[n
].snlen
;
802 sn_array
[n
].sn
= xtrymalloc (snlen
);
805 hd
->error
= gpg_error_from_syserror ();
806 release_sn_array (sn_array
, n
);
809 sn_array
[n
].snlen
= snlen
;
810 memcpy (sn_array
[n
].sn
, sn
, snlen
);
818 unsigned int blobflags
;
820 _keybox_release_blob (blob
); blob
= NULL
;
821 rc
= _keybox_read_blob (&blob
, hd
->fp
);
825 if (blob_get_type (blob
) == BLOBTYPE_HEADER
)
829 blobflags
= blob_get_blob_flags (blob
);
830 if (!hd
->ephemeral
&& (blobflags
& 2))
831 continue; /* Not in ephemeral mode but blob is flagged ephemeral. */
833 for (n
=0; n
< ndesc
; n
++)
835 switch (desc
[n
].mode
)
837 case KEYDB_SEARCH_MODE_NONE
:
840 case KEYDB_SEARCH_MODE_EXACT
:
841 if (has_subject_or_alt (blob
, desc
[n
].u
.name
, 0))
844 case KEYDB_SEARCH_MODE_MAIL
:
845 if (has_mail (blob
, desc
[n
].u
.name
, 0))
848 case KEYDB_SEARCH_MODE_MAILSUB
:
849 if (has_mail (blob
, desc
[n
].u
.name
, 1))
852 case KEYDB_SEARCH_MODE_SUBSTR
:
853 if (has_subject_or_alt (blob
, desc
[n
].u
.name
, 1))
856 case KEYDB_SEARCH_MODE_MAILEND
:
857 case KEYDB_SEARCH_MODE_WORDS
:
858 never_reached (); /* not yet implemented */
860 case KEYDB_SEARCH_MODE_ISSUER
:
861 if (has_issuer (blob
, desc
[n
].u
.name
))
864 case KEYDB_SEARCH_MODE_ISSUER_SN
:
865 if (has_issuer_sn (blob
, desc
[n
].u
.name
,
866 sn_array
? sn_array
[n
].sn
: desc
[n
].sn
,
867 sn_array
? sn_array
[n
].snlen
: desc
[n
].snlen
))
870 case KEYDB_SEARCH_MODE_SN
:
871 if (has_sn (blob
, sn_array
? sn_array
[n
].sn
: desc
[n
].sn
,
872 sn_array
? sn_array
[n
].snlen
: desc
[n
].snlen
))
875 case KEYDB_SEARCH_MODE_SUBJECT
:
876 if (has_subject (blob
, desc
[n
].u
.name
))
879 case KEYDB_SEARCH_MODE_SHORT_KID
:
880 if (has_short_kid (blob
, desc
[n
].u
.kid
))
883 case KEYDB_SEARCH_MODE_LONG_KID
:
884 if (has_long_kid (blob
, desc
[n
].u
.kid
))
887 case KEYDB_SEARCH_MODE_FPR
:
888 case KEYDB_SEARCH_MODE_FPR20
:
889 if (has_fingerprint (blob
, desc
[n
].u
.fpr
))
892 case KEYDB_SEARCH_MODE_KEYGRIP
:
893 if (has_keygrip (blob
, desc
[n
].u
.grip
))
896 case KEYDB_SEARCH_MODE_FIRST
:
899 case KEYDB_SEARCH_MODE_NEXT
:
903 rc
= gpg_error (GPG_ERR_INV_VALUE
);
909 for (n
=any_skip
?0:ndesc
; n
< ndesc
; n
++)
911 /* if (desc[n].skipfnc */
912 /* && desc[n].skipfnc (desc[n].skipfncvalue, aki)) */
921 hd
->found
.blob
= blob
;
925 _keybox_release_blob (blob
);
930 _keybox_release_blob (blob
);
935 release_sn_array (sn_array
, ndesc
);
944 Functions to return a certificate or a keyblock. To be used after
945 a successful search operation.
947 #ifdef KEYBOX_WITH_X509
949 Return the last found cert. Caller must free it.
952 keybox_get_cert (KEYBOX_HANDLE hd
, ksba_cert_t
*r_cert
)
954 const unsigned char *buffer
;
956 size_t cert_off
, cert_len
;
957 ksba_reader_t reader
= NULL
;
958 ksba_cert_t cert
= NULL
;
962 return gpg_error (GPG_ERR_INV_VALUE
);
964 return gpg_error (GPG_ERR_NOTHING_FOUND
);
966 if (blob_get_type (hd
->found
.blob
) != BLOBTYPE_X509
)
967 return gpg_error (GPG_ERR_WRONG_BLOB_TYPE
);
969 buffer
= _keybox_get_blob_image (hd
->found
.blob
, &length
);
971 return gpg_error (GPG_ERR_TOO_SHORT
);
972 cert_off
= get32 (buffer
+8);
973 cert_len
= get32 (buffer
+12);
974 if (cert_off
+cert_len
> length
)
975 return gpg_error (GPG_ERR_TOO_SHORT
);
977 rc
= ksba_reader_new (&reader
);
980 rc
= ksba_reader_set_mem (reader
, buffer
+cert_off
, cert_len
);
983 ksba_reader_release (reader
);
984 /* fixme: need to map the error codes */
985 return gpg_error (GPG_ERR_GENERAL
);
988 rc
= ksba_cert_new (&cert
);
991 ksba_reader_release (reader
);
995 rc
= ksba_cert_read_der (cert
, reader
);
998 ksba_cert_release (cert
);
999 ksba_reader_release (reader
);
1000 /* fixme: need to map the error codes */
1001 return gpg_error (GPG_ERR_GENERAL
);
1005 ksba_reader_release (reader
);
1009 #endif /*KEYBOX_WITH_X509*/
1011 /* Return the flags named WHAT at the address of VALUE. IDX is used
1012 only for certain flags and should be 0 if not required. */
1014 keybox_get_flags (KEYBOX_HANDLE hd
, int what
, int idx
, unsigned int *value
)
1016 const unsigned char *buffer
;
1020 (void)idx
; /* Not yet used. */
1023 return gpg_error (GPG_ERR_INV_VALUE
);
1024 if (!hd
->found
.blob
)
1025 return gpg_error (GPG_ERR_NOTHING_FOUND
);
1027 buffer
= _keybox_get_blob_image (hd
->found
.blob
, &length
);
1028 ec
= get_flag_from_image (buffer
, length
, what
, value
);
1029 return ec
? gpg_error (ec
):0;