/* Check the math used with Twisted Edwards curves. */ static void twistededwards_math (void) { gpg_error_t err; gcry_ctx_t ctx; gcry_mpi_point_t G, Q; gcry_mpi_t k; gcry_mpi_t w, a, x, y, z, p, n, b, I; wherestr = "twistededwards_math"; info ("checking basic Twisted Edwards math\n"); err = gcry_mpi_ec_new (&ctx, NULL, "Ed25519"); if (err) die ("gcry_mpi_ec_new failed: %s\n", gpg_strerror (err)); k = hex2mpi ("2D3501E723239632802454EE5DDC406EFB0BDF18486A5BDE9C0390A9C2984004" "F47252B628C953625B8DEB5DBCB8DA97AA43A1892D11FA83596F42E0D89CB1B6"); G = gcry_mpi_ec_get_point ("g", ctx, 1); if (!G) die ("gcry_mpi_ec_get_point(G) failed\n"); Q = gcry_mpi_point_new (0); w = gcry_mpi_new (0); a = gcry_mpi_new (0); x = gcry_mpi_new (0); y = gcry_mpi_new (0); z = gcry_mpi_new (0); I = gcry_mpi_new (0); p = gcry_mpi_ec_get_mpi ("p", ctx, 1); n = gcry_mpi_ec_get_mpi ("n", ctx, 1); b = gcry_mpi_ec_get_mpi ("b", ctx, 1); /* Check: 2^{p-1} mod p == 1 */ gcry_mpi_sub_ui (a, p, 1); gcry_mpi_powm (w, GCRYMPI_CONST_TWO, a, p); if (gcry_mpi_cmp_ui (w, 1)) fail ("failed assertion: 2^{p-1} mod p == 1\n"); /* Check: p % 4 == 1 */ gcry_mpi_mod (w, p, GCRYMPI_CONST_FOUR); if (gcry_mpi_cmp_ui (w, 1)) fail ("failed assertion: p %% 4 == 1\n"); /* Check: 2^{n-1} mod n == 1 */ gcry_mpi_sub_ui (a, n, 1); gcry_mpi_powm (w, GCRYMPI_CONST_TWO, a, n); if (gcry_mpi_cmp_ui (w, 1)) fail ("failed assertion: 2^{n-1} mod n == 1\n"); /* Check: b^{(p-1)/2} mod p == p-1 */ gcry_mpi_sub_ui (a, p, 1); gcry_mpi_div (x, NULL, a, GCRYMPI_CONST_TWO, -1); gcry_mpi_powm (w, b, x, p); gcry_mpi_abs (w); if (gcry_mpi_cmp (w, a)) fail ("failed assertion: b^{(p-1)/2} mod p == p-1\n"); /* I := 2^{(p-1)/4} mod p */ gcry_mpi_sub_ui (a, p, 1); gcry_mpi_div (x, NULL, a, GCRYMPI_CONST_FOUR, -1); gcry_mpi_powm (I, GCRYMPI_CONST_TWO, x, p); /* Check: I^2 mod p == p-1 */ gcry_mpi_powm (w, I, GCRYMPI_CONST_TWO, p); if (gcry_mpi_cmp (w, a)) fail ("failed assertion: I^2 mod p == p-1\n"); /* Check: G is on the curve */ if (!gcry_mpi_ec_curve_point (G, ctx)) fail ("failed assertion: G is on the curve\n"); /* Check: nG == (0,1) */ gcry_mpi_ec_mul (Q, n, G, ctx); if (gcry_mpi_ec_get_affine (x, y, Q, ctx)) fail ("failed to get affine coordinates\n"); if (gcry_mpi_cmp_ui (x, 0) || gcry_mpi_cmp_ui (y, 1)) fail ("failed assertion: nG == (0,1)\n"); /* Now two arbitrary point operations taken from the ed25519.py sample data. */ gcry_mpi_release (a); a = hex2mpi ("4f71d012df3c371af3ea4dc38385ca5bb7272f90cb1b008b3ed601c76de1d496" "e30cbf625f0a756a678d8f256d5325595cccc83466f36db18f0178eb9925edd3"); gcry_mpi_ec_mul (Q, a, G, ctx); if (gcry_mpi_ec_get_affine (x, y, Q, ctx)) fail ("failed to get affine coordinates\n"); if (cmp_mpihex (x, ("157f7361c577aad36f67ed33e38dc7be" "00014fecc2165ca5cee9eee19fe4d2c1")) || cmp_mpihex (y, ("5a69dbeb232276b38f3f5016547bb2a2" "4025645f0b820e72b8cad4f0a909a092"))) { fail ("sample point multiply failed:\n"); print_mpi ("r", a); print_mpi ("Rx", x); print_mpi ("Ry", y); } gcry_mpi_release (a); a = hex2mpi ("2d3501e723239632802454ee5ddc406efb0bdf18486a5bde9c0390a9c2984004" "f47252b628c953625b8deb5dbcb8da97aa43a1892d11fa83596f42e0d89cb1b6"); gcry_mpi_ec_mul (Q, a, G, ctx); if (gcry_mpi_ec_get_affine (x, y, Q, ctx)) fail ("failed to get affine coordinates\n"); if (cmp_mpihex (x, ("6218e309d40065fcc338b3127f468371" "82324bd01ce6f3cf81ab44e62959c82a")) || cmp_mpihex (y, ("5501492265e073d874d9e5b81e7f8784" "8a826e80cce2869072ac60c3004356e5"))) { fail ("sample point multiply failed:\n"); print_mpi ("r", a); print_mpi ("Rx", x); print_mpi ("Ry", y); } gcry_mpi_release (I); gcry_mpi_release (b); gcry_mpi_release (n); gcry_mpi_release (p); gcry_mpi_release (w); gcry_mpi_release (a); gcry_mpi_release (x); gcry_mpi_release (y); gcry_mpi_release (z); gcry_mpi_point_release (Q); gcry_mpi_point_release (G); gcry_mpi_release (k); gcry_ctx_release (ctx); }
/* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP should be the hex encoded keygrip of that key to be used with the caching mechanism. DESC_TEXT may be set to override the default description used for the pinentry. If LOOKUP_TTL is given this function is used to lookup the default ttl. If R_PASSPHRASE is not NULL, the function succeeded and the key was protected the used passphrase (entered or from the cache) is stored there; if not NULL will be stored. The caller needs to free the returned passphrase. */ static int unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, unsigned char **keybuf, const unsigned char *grip, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, char **r_passphrase) { struct pin_entry_info_s *pi; struct try_unprotect_arg_s arg; int rc; unsigned char *result; size_t resultlen; char hexgrip[40+1]; if (r_passphrase) *r_passphrase = NULL; bin2hex (grip, 20, hexgrip); /* Initially try to get it using a cache nonce. */ if (cache_nonce) { char *pw; pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); if (pw) { rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen); if (!rc) { if (r_passphrase) *r_passphrase = pw; else xfree (pw); xfree (*keybuf); *keybuf = result; return 0; } xfree (pw); } } /* First try to get it from the cache - if there is none or we can't unprotect it, we fall back to ask the user */ if (cache_mode != CACHE_MODE_IGNORE) { char *pw; retry: pw = agent_get_cache (hexgrip, cache_mode); if (pw) { rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen); if (!rc) { if (r_passphrase) *r_passphrase = pw; else xfree (pw); xfree (*keybuf); *keybuf = result; return 0; } xfree (pw); rc = 0; } /* If the pinentry is currently in use, we wait up to 60 seconds for it to close and check the cache again. This solves a common situation where several requests for unprotecting a key have been made but the user is still entering the passphrase for the first request. Because all requests to agent_askpin are serialized they would then pop up one after the other to request the passphrase - despite that the user has already entered it and is then available in the cache. This implementation is not race free but in the worst case the user has to enter the passphrase only once more. */ if (pinentry_active_p (ctrl, 0)) { /* Active - wait */ if (!pinentry_active_p (ctrl, 60)) { /* We need to give the other thread a chance to actually put it into the cache. */ npth_sleep (1); goto retry; } /* Timeout - better call pinentry now the plain way. */ } } pi = gcry_calloc_secure (1, sizeof (*pi) + 100); if (!pi) return gpg_error_from_syserror (); pi->max_length = 100; pi->min_digits = 0; /* we want a real passphrase */ pi->max_digits = 16; pi->max_tries = 3; pi->check_cb = try_unprotect_cb; arg.ctrl = ctrl; arg.protected_key = *keybuf; arg.unprotected_key = NULL; arg.change_required = 0; pi->check_cb_arg = &arg; rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi); if (!rc) { assert (arg.unprotected_key); if (arg.change_required) { size_t canlen, erroff; gcry_sexp_t s_skey; assert (arg.unprotected_key); canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)arg.unprotected_key, canlen); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); wipememory (arg.unprotected_key, canlen); xfree (arg.unprotected_key); xfree (pi); return rc; } rc = agent_protect_and_store (ctrl, s_skey, NULL); gcry_sexp_release (s_skey); if (rc) { log_error ("changing the passphrase failed: %s\n", gpg_strerror (rc)); wipememory (arg.unprotected_key, canlen); xfree (arg.unprotected_key); xfree (pi); return rc; } } else { agent_put_cache (hexgrip, cache_mode, pi->pin, lookup_ttl? lookup_ttl (hexgrip) : 0); if (r_passphrase && *pi->pin) *r_passphrase = xtrystrdup (pi->pin); } xfree (*keybuf); *keybuf = arg.unprotected_key; } xfree (pi); return rc; }
/* Return the secret key as an S-Exp in RESULT after locating it using the GRIP. Stores NULL at RESULT if the operation shall be diverted to a token; in this case an allocated S-expression with the shadow_info part from the file is stored at SHADOW_INFO. CACHE_MODE defines now the cache shall be used. DESC_TEXT may be set to present a custom description for the pinentry. LOOKUP_TTL is an optional function to convey a TTL to the cache manager; we do not simply pass the TTL value because the value is only needed if an unprotect action was needed and looking up the TTL may have some overhead (e.g. scanning the sshcontrol file). If a CACHE_NONCE is given that cache item is first tried to get a passphrase. If R_PASSPHRASE is not NULL, the function succeeded and the key was protected the used passphrase (entered or from the cache) is stored there; if not NULL will be stored. The caller needs to free the returned passphrase. */ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, gcry_sexp_t *result, char **r_passphrase) { int rc; unsigned char *buf; size_t len, buflen, erroff; gcry_sexp_t s_skey; int got_shadow_info = 0; *result = NULL; if (shadow_info) *shadow_info = NULL; if (r_passphrase) *r_passphrase = NULL; rc = read_key_file (grip, &s_skey); if (rc) return rc; /* For use with the protection functions we also need the key as an canonical encoded S-expression in a buffer. Create this buffer now. */ rc = make_canon_sexp (s_skey, &buf, &len); if (rc) return rc; switch (agent_private_key_type (buf)) { case PRIVATE_KEY_CLEAR: break; /* no unprotection needed */ case PRIVATE_KEY_PROTECTED: { char *desc_text_final; char *comment = NULL; /* Note, that we will take the comment as a C string for display purposes; i.e. all stuff beyond a Nul character is ignored. */ { gcry_sexp_t comment_sexp; comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); if (comment_sexp) comment = gcry_sexp_nth_string (comment_sexp, 1); gcry_sexp_release (comment_sexp); } desc_text_final = NULL; if (desc_text) rc = modify_description (desc_text, comment? comment:"", s_skey, &desc_text_final); gcry_free (comment); if (!rc) { rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip, cache_mode, lookup_ttl, r_passphrase); if (rc) log_error ("failed to unprotect the secret key: %s\n", gpg_strerror (rc)); } xfree (desc_text_final); } break; case PRIVATE_KEY_SHADOWED: if (shadow_info) { const unsigned char *s; size_t n; rc = agent_get_shadow_info (buf, &s); if (!rc) { n = gcry_sexp_canon_len (s, 0, NULL,NULL); assert (n); *shadow_info = xtrymalloc (n); if (!*shadow_info) rc = out_of_core (); else { memcpy (*shadow_info, s, n); rc = 0; got_shadow_info = 1; } } if (rc) log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); } else rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY); break; default: log_error ("invalid private key format\n"); rc = gpg_error (GPG_ERR_BAD_SECKEY); break; } gcry_sexp_release (s_skey); s_skey = NULL; if (rc || got_shadow_info) { xfree (buf); if (r_passphrase) { xfree (*r_passphrase); *r_passphrase = NULL; } return rc; } buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); wipememory (buf, buflen); xfree (buf); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); if (r_passphrase) { xfree (*r_passphrase); *r_passphrase = NULL; } return rc; } *result = s_skey; return 0; }
static gpg_error_t new_part (void *cookie, const char *mediatype, const char *mediasubtype) { receive_ctx_t ctx = cookie; gpg_error_t err = 0; ctx->collect_key_data = 0; ctx->collect_wkd_data = 0; if (!strcmp (mediatype, "application") && !strcmp (mediasubtype, "pgp-keys")) { log_info ("new '%s/%s' message part\n", mediatype, mediasubtype); if (ctx->key_data) { log_error ("we already got a key - ignoring this part\n"); err = gpg_error (GPG_ERR_FALSE); } else { ctx->key_data = es_fopenmem (0, "w+b"); if (!ctx->key_data) { err = gpg_error_from_syserror (); log_error ("error allocating space for key: %s\n", gpg_strerror (err)); } else { ctx->collect_key_data = 1; err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */ } } } else if (!strcmp (mediatype, "application") && !strcmp (mediasubtype, "vnd.gnupg.wks")) { log_info ("new '%s/%s' message part\n", mediatype, mediasubtype); if (ctx->wkd_data) { log_error ("we already got a wkd part - ignoring this part\n"); err = gpg_error (GPG_ERR_FALSE); } else { ctx->wkd_data = es_fopenmem (0, "w+b"); if (!ctx->wkd_data) { err = gpg_error_from_syserror (); log_error ("error allocating space for key: %s\n", gpg_strerror (err)); } else { ctx->collect_wkd_data = 1; err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */ } } } else { log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype); err = gpg_error (GPG_ERR_FALSE); /* We do not want the part. */ } return err; }
int main (int argc, char **argv ) { ARGPARSE_ARGS pargs; assuan_context_t ctx; gpg_error_t err; unsigned char *certbuf; size_t certbuflen = 0; int cmd_ping = 0; int cmd_cache_cert = 0; int cmd_validate = 0; int cmd_lookup = 0; int cmd_loadcrl = 0; int cmd_squid_mode = 0; set_strusage (my_strusage); log_set_prefix ("dirmngr-client", JNLIB_LOG_WITH_PREFIX); /* For W32 we need to initialize the socket subsystem. Becuase we don't use Pth we need to do this explicit. */ #ifdef HAVE_W32_SYSTEM { WSADATA wsadat; WSAStartup (0x202, &wsadat); } #endif /*HAVE_W32_SYSTEM*/ /* Init Assuan. */ assuan_set_assuan_log_prefix (log_get_prefix (NULL)); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); /* Setup I18N. */ i18n_init(); /* Parse the command line. */ pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* Do not remove the args. */ while (arg_parse (&pargs, opts) ) { switch (pargs.r_opt) { case oVerbose: opt.verbose++; break; case oQuiet: opt.quiet++; break; case oOCSP: opt.use_ocsp++; break; case oPing: cmd_ping = 1; break; case oCacheCert: cmd_cache_cert = 1; break; case oValidate: cmd_validate = 1; break; case oLookup: cmd_lookup = 1; break; case oUrl: opt.url = 1; break; case oLocal: opt.local = 1; break; case oLoadCRL: cmd_loadcrl = 1; break; case oPEM: opt.pem = 1; break; case oSquidMode: opt.pem = 1; opt.escaped_pem = 1; cmd_squid_mode = 1; break; case oForceDefaultResponder: opt.force_default_responder = 1; break; default : pargs.err = 2; break; } } if (log_get_errorcount (0)) exit (2); /* Build the helptable for radix64 to bin conversion. */ if (opt.pem) { int i; unsigned char *s; for (i=0; i < 256; i++ ) asctobin[i] = 255; /* Used to detect invalid characters. */ for (s=bintoasc, i=0; *s; s++, i++) asctobin[*s] = i; } if (cmd_ping) err = 0; else if (cmd_lookup || cmd_loadcrl) { if (!argc) usage (1); err = 0; } else if (cmd_squid_mode) { err = 0; if (argc) usage (1); } else if (!argc) { err = read_certificate (NULL, &certbuf, &certbuflen); if (err) log_error (_("error reading certificate from stdin: %s\n"), gpg_strerror (err)); } else if (argc == 1) { err = read_certificate (*argv, &certbuf, &certbuflen); if (err) log_error (_("error reading certificate from '%s': %s\n"), *argv, gpg_strerror (err)); } else { err = 0; usage (1); } if (log_get_errorcount (0)) exit (2); if (certbuflen > 20000) { log_error (_("certificate too large to make any sense\n")); exit (2); } ctx = start_dirmngr (1); if (!ctx) exit (2); if (cmd_ping) ; else if (cmd_squid_mode) { while (!(err = squid_loop_body (ctx))) ; if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; } else if (cmd_lookup) { int last_err = 0; for (; argc; argc--, argv++) { err = do_lookup (ctx, *argv); if (err) { log_error (_("lookup failed: %s\n"), gpg_strerror (err)); last_err = err; } } err = last_err; } else if (cmd_loadcrl) { int last_err = 0; for (; argc; argc--, argv++) { err = do_loadcrl (ctx, *argv); if (err) { log_error (_("loading CRL '%s' failed: %s\n"), *argv, gpg_strerror (err)); last_err = err; } } err = last_err; } else if (cmd_cache_cert) { err = do_cache (ctx, certbuf, certbuflen); xfree (certbuf); } else if (cmd_validate) { err = do_validate (ctx, certbuf, certbuflen); xfree (certbuf); } else { err = do_check (ctx, certbuf, certbuflen); xfree (certbuf); } assuan_release (ctx); if (cmd_ping) { if (!opt.quiet) log_info (_("a dirmngr daemon is up and running\n")); return 0; } else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode) return err? 1:0; else if (cmd_cache_cert) { if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE ) { if (!opt.quiet) log_info (_("certificate already cached\n")); } else if (err) { log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); return 1; } return 0; } else if (cmd_validate && err) { log_error (_("validation of certificate failed: %s\n"), gpg_strerror (err)); return 1; } else if (!err) { if (!opt.quiet) log_info (_("certificate is valid\n")); return 0; } else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED ) { if (!opt.quiet) log_info (_("certificate has been revoked\n")); return 1; } else { log_error (_("certificate check failed: %s\n"), gpg_strerror (err)); return 2; } }
/* Check whether CERT is a root certificate. ISSUERDN and SUBJECTDN are the DNs already extracted by the caller from CERT. Returns True if this is the case. */ static int is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn) { gpg_error_t err; int result = 0; ksba_sexp_t serialno; ksba_sexp_t ak_keyid; ksba_name_t ak_name; ksba_sexp_t ak_sn; const char *ak_name_str; ksba_sexp_t subj_keyid = NULL; if (!issuerdn || !subjectdn) return 0; /* No. */ if (strcmp (issuerdn, subjectdn)) return 0; /* No. */ err = ksba_cert_get_auth_key_id (cert, &ak_keyid, &ak_name, &ak_sn); if (err) { if (gpg_err_code (err) == GPG_ERR_NO_DATA) return 1; /* Yes. Without a authorityKeyIdentifier this needs to be the Root certifcate (our trust anchor). */ log_error ("error getting authorityKeyIdentifier: %s\n", gpg_strerror (err)); return 0; /* Well, it is broken anyway. Return No. */ } serialno = ksba_cert_get_serial (cert); if (!serialno) { log_error ("error getting serialno: %s\n", gpg_strerror (err)); goto leave; } /* Check whether the auth name's matches the issuer name+sn. If that is the case this is a root certificate. */ ak_name_str = ksba_name_enum (ak_name, 0); if (ak_name_str && !strcmp (ak_name_str, issuerdn) && !cmp_simple_canon_sexp (ak_sn, serialno)) { result = 1; /* Right, CERT is self-signed. */ goto leave; } /* Similar for the ak_keyid. */ if (ak_keyid && !ksba_cert_get_subj_key_id (cert, NULL, &subj_keyid) && !cmp_simple_canon_sexp (ak_keyid, subj_keyid)) { result = 1; /* Right, CERT is self-signed. */ goto leave; } leave: ksba_free (subj_keyid); ksba_free (ak_keyid); ksba_name_release (ak_name); ksba_free (ak_sn); ksba_free (serialno); return result; }
/* Check the signature on CERT using the ISSUER_CERT. This function does only test the cryptographic signature and nothing else. It is assumed that the ISSUER_CERT is valid. */ static gpg_error_t check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) { gpg_error_t err; const char *algoid; gcry_md_hd_t md; int i, algo; ksba_sexp_t p; size_t n; gcry_sexp_t s_sig, s_hash, s_pkey; const char *s; char algo_name[16+1]; /* hash algorithm name converted to lower case. */ int digestlen; unsigned char *digest; /* Hash the target certificate using the algorithm from that certificate. */ algoid = ksba_cert_get_digest_algo (cert); algo = gcry_md_map_name (algoid); if (!algo) { log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); return gpg_error (GPG_ERR_GENERAL); } s = gcry_md_algo_name (algo); for (i=0; *s && i < sizeof algo_name - 1; s++, i++) algo_name[i] = tolower (*s); algo_name[i] = 0; err = gcry_md_open (&md, algo, 0); if (err) { log_error ("md_open failed: %s\n", gpg_strerror (err)); return err; } if (DBG_HASHING) gcry_md_debug (md, "hash.cert"); err = ksba_cert_hash (cert, 1, HASH_FNC, md); if (err) { log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (err)); gcry_md_close (md); return err; } gcry_md_final (md); /* Get the signature value out of the target certificate. */ p = ksba_cert_get_sig_val (cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); gcry_md_close (md); ksba_free (p); return gpg_error (GPG_ERR_BUG); } if (DBG_CRYPTO) { int j; log_debug ("signature value:"); for (j=0; j < n; j++) log_printf (" %02X", p[j]); log_printf ("\n"); } err = gcry_sexp_sscan ( &s_sig, NULL, p, n); ksba_free (p); if (err) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err)); gcry_md_close (md); return err; } /* Get the public key from the issuer certificate. */ p = ksba_cert_get_public_key (issuer_cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); gcry_md_close (md); ksba_free (p); gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_BUG); } err = gcry_sexp_sscan ( &s_pkey, NULL, p, n); ksba_free (p); if (err) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (err)); gcry_md_close (md); gcry_sexp_release (s_sig); return err; } /* Prepare the values for signature verification. At this point we have these values: S_PKEY - S-expression with the issuer's public key. S_SIG - Signature value as given in the certrificate. MD - Finalized hash context with hash of the certificate. ALGO_NAME - Lowercase hash algorithm name */ digestlen = gcry_md_get_algo_dlen (algo); digest = gcry_md_read (md, algo); if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA) { if (digestlen != 20) { log_error (_("DSA requires the use of a 160 bit hash algorithm\n")); gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_pkey); return gpg_error (GPG_ERR_INTERNAL); } if ( gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", (int)digestlen, digest) ) BUG (); } else /* Not DSA. */ { if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", algo_name, (int)digestlen, digest) ) BUG (); } err = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return err; }
int verify_signatures (ctrl_t ctrl, int nfiles, char **files ) { IOBUF fp; armor_filter_context_t *afx = NULL; progress_filter_context_t *pfx = new_progress_context (); const char *sigfile; int i, rc; strlist_t sl; /* Decide whether we should handle a detached or a normal signature, * which is needed so that the code later can hash the correct data and * not have a normal signature act as detached signature and ignoring the * indended signed material from the 2nd file or stdin. * 1. gpg <file - normal * 2. gpg file - normal (or detached) * 3. gpg file <file2 - detached * 4. gpg file file2 - detached * The question is how decide between case 2 and 3? The only way * we can do it is by reading one byte from stdin and then unget * it; the problem here is that we may be reading from the * terminal (which could be detected using isatty() but won't work * when under contol of a pty using program (e.g. expect)) and * might get us in trouble when stdin is used for another purpose * (--passphrase-fd 0). So we have to break with the behaviour * prior to gpg 1.0.4 by assuming that case 3 is a normal * signature (where file2 is ignored and require for a detached * signature to indicate signed material comes from stdin by using * case 4 with a file2 of "-". * * Actually we don't have to change anything here but can handle * that all quite easily in mainproc.c */ sigfile = nfiles? *files : NULL; /* open the signature file */ fp = iobuf_open(sigfile); if (fp && is_secured_file (iobuf_get_fd (fp))) { iobuf_close (fp); fp = NULL; gpg_err_set_errno (EPERM); } if( !fp ) { rc = gpg_error_from_syserror (); log_error(_("can't open '%s': %s\n"), print_fname_stdin(sigfile), gpg_strerror (rc)); goto leave; } handle_progress (pfx, fp, sigfile); if ( !opt.no_armor && use_armor_filter( fp ) ) { afx = new_armor_context (); push_armor_filter (afx, fp); } sl = NULL; for(i=nfiles-1 ; i > 0 ; i-- ) add_to_strlist( &sl, files[i] ); rc = proc_signature_packets (ctrl, NULL, fp, sl, sigfile ); free_strlist(sl); iobuf_close(fp); if( (afx && afx->no_openpgp_data && rc == -1) || rc == G10ERR_NO_DATA ) { log_error(_("the signature could not be verified.\n" "Please remember that the signature file (.sig or .asc)\n" "should be the first file given on the command line.\n") ); rc = 0; } leave: release_armor_context (afx); release_progress_context (pfx); return rc; }
/* Perform an encrypt operation. Encrypt the data received on DATA-FD and write it to OUT_FP. The recipients are take from the certificate given in recplist; if this is NULL it will be encrypted for a default recipient */ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { int rc = 0; Base64Context b64writer = NULL; gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; struct encrypt_cb_parm_s encparm; DEK dek = NULL; int recpno; estream_t data_fp = NULL; certlist_t cl; int count; memset (&encparm, 0, sizeof encparm); audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT); /* Check that the certificate list is not empty and that at least one certificate is not flagged as encrypt_to; i.e. is a real recipient. */ for (cl = recplist; cl; cl = cl->next) if (!cl->is_encrypt_to) break; if (!cl) { log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); rc = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } for (count = 0, cl = recplist; cl; cl = cl->next) count++; audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count); kh = keydb_new (0); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Fixme: We should use the unlocked version of the es functions. */ data_fp = es_fdopen_nc (data_fd, "rb"); if (!data_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } err = ksba_reader_new (&reader); if (err) rc = err; if (!rc) rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (rc) goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are going to create enveloped data with uninterpreted data as inner content */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) rc = out_of_core (); else { dek->algoid = opt.def_cipher_algoid; rc = init_dek (dek); } if (rc) { log_error ("failed to create the session key: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen); if (err) { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } encparm.dek = dek; /* Use a ~8k (AES) or ~4k (3DES) buffer */ encparm.bufsize = 500 * dek->ivlen; encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { rc = out_of_core (); goto leave; } audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid); /* Gather certificates of recipients, encrypt the session key for each and store them in the CMS object */ for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next) { unsigned char *encval; rc = encrypt_dek (dek, cl->cert, &encval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); log_error ("encryption failed for recipient no. %d: %s\n", recpno, gpg_strerror (rc)); goto leave; } err = ksba_cms_add_recipient (cms, cl->cert); if (err) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); rc = err; xfree (encval); goto leave; } err = ksba_cms_set_enc_val (cms, recpno, encval); xfree (encval); audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); if (err) { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Main control loop for encryption. */ recpno = 0; do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } while (stopreason != KSBA_SR_READY); if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } rc = gpgsm_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); log_info ("encrypted data created\n"); leave: ksba_cms_release (cms); gpgsm_destroy_writer (b64writer); ksba_reader_release (reader); keydb_release (kh); xfree (dek); es_fclose (data_fp); xfree (encparm.buffer); return rc; }
/* Try to connect to the dirmngr via a socket. On platforms supporting it, start it up if needed. Returns a new assuan context at R_CTX or an error code. */ gpg_error_t start_new_dirmngr (assuan_context_t *r_ctx, gpg_err_source_t errsource, const char *homedir, const char *dirmngr_program, int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg) { gpg_error_t err; assuan_context_t ctx; const char *sockname; int did_success_msg = 0; *r_ctx = NULL; err = assuan_new (&ctx); if (err) { log_error ("error allocating assuan context: %s\n", gpg_strerror (err)); return err; } sockname = dirmngr_socket_name (); err = assuan_socket_connect (ctx, sockname, 0, 0); #ifdef USE_DIRMNGR_AUTO_START if (err) { lock_spawn_t lock; const char *argv[2]; /* With no success try start a new Dirmngr. On most systems this will fail because the Dirmngr is expected to be a system service. However on Wince we don't distinguish users and thus we can start it. A future extension might be to use the userv system to start the Dirmngr as a system service. */ if (!dirmngr_program || !*dirmngr_program) dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR); if (verbose) log_info (_("no running Dirmngr - starting '%s'\n"), dirmngr_program); if (status_cb) status_cb (status_cb_arg, STATUS_PROGRESS, "starting_dirmngr ? 0 0", NULL); if (fflush (NULL)) { gpg_error_t tmperr = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error ("error flushing pending output: %s\n", strerror (errno)); assuan_release (ctx); return tmperr; } argv[0] = "--daemon"; argv[1] = NULL; if (!(err = lock_spawning (&lock, homedir, "dirmngr", verbose)) && assuan_socket_connect (ctx, sockname, 0, 0)) { err = gnupg_spawn_process_detached (dirmngr_program, argv,NULL); if (err) log_error ("failed to start the dirmngr '%s': %s\n", dirmngr_program, gpg_strerror (err)); else { int i; for (i=0; i < SECS_TO_WAIT_FOR_DIRMNGR; i++) { if (verbose) log_info (_("waiting for the dirmngr " "to come up ... (%ds)\n"), SECS_TO_WAIT_FOR_DIRMNGR - i); gnupg_sleep (1); err = assuan_socket_connect (ctx, sockname, 0, 0); if (!err) { if (verbose) { log_info (_("connection to the dirmngr" " established\n")); did_success_msg = 1; } break; } } } } unlock_spawning (&lock, "dirmngr"); } #else (void)homedir; (void)dirmngr_program; (void)verbose; (void)status_cb; (void)status_cb_arg; #endif /*USE_DIRMNGR_AUTO_START*/ if (err) { log_error ("connecting dirmngr at '%s' failed: %s\n", sockname, gpg_strerror (err)); assuan_release (ctx); return gpg_err_make (errsource, GPG_ERR_NO_DIRMNGR); } if (debug && !did_success_msg) log_debug (_("connection to the dirmngr established\n")); *r_ctx = ctx; return 0; }
/**************** * Decrypt the data, specified by ED with the key DEK. */ int decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) { decode_filter_ctx_t dfx; byte *p; int rc=0, c, i; byte temp[32]; unsigned blocksize; unsigned nprefix; dfx = xtrycalloc (1, sizeof *dfx); if (!dfx) return gpg_error_from_syserror (); dfx->refcount = 1; if ( opt.verbose && !dek->algo_info_printed ) { if (!openpgp_cipher_test_algo (dek->algo)) log_info (_("%s encrypted data\n"), openpgp_cipher_algo_name (dek->algo)); else log_info (_("encrypted with unknown algorithm %d\n"), dek->algo ); dek->algo_info_printed = 1; } { char buf[20]; snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo); write_status_text (STATUS_DECRYPTION_INFO, buf); } if (opt.show_session_key) { char numbuf[25]; char *hexbuf; snprintf (numbuf, sizeof numbuf, "%d:", dek->algo); hexbuf = bin2hex (dek->key, dek->keylen, NULL); if (!hexbuf) { rc = gpg_error_from_syserror (); goto leave; } log_info ("session key: '%s%s'\n", numbuf, hexbuf); write_status_strings (STATUS_SESSION_KEY, numbuf, hexbuf, NULL); xfree (hexbuf); } rc = openpgp_cipher_test_algo (dek->algo); if (rc) goto leave; blocksize = openpgp_cipher_get_algo_blklen (dek->algo); if ( !blocksize || blocksize > 16 ) log_fatal ("unsupported blocksize %u\n", blocksize ); nprefix = blocksize; if ( ed->len && ed->len < (nprefix+2) ) { /* An invalid message. We can't check that during parsing because we may not know the used cipher then. */ rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if ( ed->mdc_method ) { if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 )) BUG (); if ( DBG_HASHING ) gcry_md_debug (dfx->mdc_hash, "checkmdc"); } rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | ((ed->mdc_method || dek->algo >= 100)? 0 : GCRY_CIPHER_ENABLE_SYNC))); if (rc) { /* We should never get an error here cause we already checked * that the algorithm is available. */ BUG(); } /* log_hexdump( "thekey", dek->key, dek->keylen );*/ rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen); if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY ) { log_info(_("WARNING: message was encrypted with" " a weak key in the symmetric cipher.\n")); rc=0; } else if( rc ) { log_error("key setup failed: %s\n", gpg_strerror (rc) ); goto leave; } if (!ed->buf) { log_error(_("problem handling encrypted packet\n")); goto leave; } gcry_cipher_setiv (dfx->cipher_hd, NULL, 0); if ( ed->len ) { for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) { if ( (c=iobuf_get(ed->buf)) == -1 ) break; else temp[i] = c; } } else { for (i=0; i < (nprefix+2); i++ ) if ( (c=iobuf_get(ed->buf)) == -1 ) break; else temp[i] = c; } gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0); gcry_cipher_sync (dfx->cipher_hd); p = temp; /* log_hexdump( "prefix", temp, nprefix+2 ); */ if (dek->symmetric && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) ) { rc = gpg_error (GPG_ERR_BAD_KEY); goto leave; } if ( dfx->mdc_hash ) gcry_md_write (dfx->mdc_hash, temp, nprefix+2); dfx->refcount++; dfx->partial = ed->is_partial; dfx->length = ed->len; if ( ed->mdc_method ) iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx ); else iobuf_push_filter ( ed->buf, decode_filter, dfx ); if (opt.unwrap_encryption) { char *filename; estream_t fp; rc = get_output_file ("", 0, ed->buf, &filename, &fp); if (! rc) { iobuf_t output = iobuf_esopen (fp, "w", 0); armor_filter_context_t *afx = NULL; if (opt.armor) { afx = new_armor_context (); push_armor_filter (afx, output); } iobuf_copy (output, ed->buf); if ((rc = iobuf_error (ed->buf))) log_error (_("error reading '%s': %s\n"), filename, gpg_strerror (rc)); else if ((rc = iobuf_error (output))) log_error (_("error writing '%s': %s\n"), filename, gpg_strerror (rc)); iobuf_close (output); if (afx) release_armor_context (afx); } } else proc_packets (ctrl, procctx, ed->buf ); ed->buf = NULL; if (dfx->eof_seen > 1 ) rc = gpg_error (GPG_ERR_INV_PACKET); else if ( ed->mdc_method ) { /* We used to let parse-packet.c handle the MDC packet but this turned out to be a problem with compressed packets: With old style packets there is no length information available and the decompressor uses an implicit end. However we can't know this implicit end beforehand (:-) and thus may feed the decompressor with more bytes than actually needed. It would be possible to unread the extra bytes but due to our weird iobuf system any unread is non reliable due to filters already popped off. The easy and sane solution is to care about the MDC packet only here and never pass it to the packet parser. Fortunatley the OpenPGP spec requires a strict format for the MDC packet so that we know that 22 bytes are appended. */ int datalen = gcry_md_get_algo_dlen (ed->mdc_method); log_assert (dfx->cipher_hd); log_assert (dfx->mdc_hash); gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0); gcry_md_write (dfx->mdc_hash, dfx->defer, 2); gcry_md_final (dfx->mdc_hash); if ( dfx->defer[0] != '\xd3' || dfx->defer[1] != '\x14' || datalen != 20 || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->defer+2, datalen)) rc = gpg_error (GPG_ERR_BAD_SIGNATURE); /* log_printhex("MDC message:", dfx->defer, 22); */ /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */ } leave: release_dfx_context (dfx); return rc; }
/* Try to connect to the agent via socket or fork it off and work by pipes. Handle the server's initial greeting. Returns a new assuan context at R_CTX or an error code. */ gpg_error_t start_new_gpg_agent (assuan_context_t *r_ctx, gpg_err_source_t errsource, const char *homedir, const char *agent_program, const char *opt_lc_ctype, const char *opt_lc_messages, session_env_t session_env, int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg) { /* If we ever failed to connect via a socket we will force the use of the pipe based server for the lifetime of the process. */ static int force_pipe_server = 0; gpg_error_t err = 0; char *infostr, *p; assuan_context_t ctx; int did_success_msg = 0; *r_ctx = NULL; err = assuan_new (&ctx); if (err) { log_error ("error allocating assuan context: %s\n", gpg_strerror (err)); return err; } restart: infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO"); if (!infostr || !*infostr) { char *sockname; const char *argv[3]; pid_t pid; int excode; /* First check whether we can connect at the standard socket. */ sockname = make_filename (homedir, "S.gpg-agent", NULL); err = assuan_socket_connect (ctx, sockname, 0, 0); if (err) { /* With no success start a new server. */ if (!agent_program || !*agent_program) agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT); if (verbose) log_info (_("no running gpg-agent - starting '%s'\n"), agent_program); if (status_cb) status_cb (status_cb_arg, STATUS_PROGRESS, "starting_agent ? 0 0", NULL); if (fflush (NULL)) { gpg_error_t tmperr = gpg_err_make (errsource, gpg_err_code_from_syserror ()); log_error ("error flushing pending output: %s\n", strerror (errno)); xfree (sockname); assuan_release (ctx); return tmperr; } argv[0] = "--use-standard-socket-p"; argv[1] = NULL; err = gnupg_spawn_process_fd (agent_program, argv, -1, -1, -1, &pid); if (err) log_debug ("starting '%s' for testing failed: %s\n", agent_program, gpg_strerror (err)); else if ((err = gnupg_wait_process (agent_program, pid, 1, &excode))) { if (excode == -1) log_debug ("running '%s' for testing failed (wait): %s\n", agent_program, gpg_strerror (err)); } gnupg_release_process (pid); if (!err && !excode) { /* If the agent has been configured for use with a standard socket, an environment variable is not required and thus we we can savely start the agent here. */ lock_spawn_t lock; argv[0] = "--daemon"; argv[1] = "--use-standard-socket"; argv[2] = NULL; if (!(err = lock_spawning (&lock, homedir, "agent", verbose)) && assuan_socket_connect (ctx, sockname, 0, 0)) { err = gnupg_spawn_process_detached (agent_program, argv,NULL); if (err) log_error ("failed to start agent '%s': %s\n", agent_program, gpg_strerror (err)); else { int i; for (i=0; i < SECS_TO_WAIT_FOR_AGENT; i++) { if (verbose) log_info (_("waiting for the agent " "to come up ... (%ds)\n"), SECS_TO_WAIT_FOR_AGENT - i); gnupg_sleep (1); err = assuan_socket_connect (ctx, sockname, 0, 0); if (!err) { if (verbose) { log_info (_("connection to agent " "established\n")); did_success_msg = 1; } break; } } } } unlock_spawning (&lock, "agent"); } else { /* If using the standard socket is not the default we start the agent as a pipe server which gives us most of the required features except for passphrase caching etc. */ const char *pgmname; int no_close_list[3]; int i; if ( !(pgmname = strrchr (agent_program, '/'))) pgmname = agent_program; else pgmname++; argv[0] = pgmname; argv[1] = "--server"; argv[2] = NULL; i=0; if (log_get_fd () != -1) no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr)); no_close_list[i] = -1; /* Connect to the agent and perform initial handshaking. */ err = assuan_pipe_connect (ctx, agent_program, argv, no_close_list, NULL, NULL, 0); } } xfree (sockname); } else { int prot; int pid; infostr = xstrdup (infostr); if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) { log_error (_("malformed GPG_AGENT_INFO environment variable\n")); xfree (infostr); force_pipe_server = 1; goto restart; } *p++ = 0; pid = atoi (p); while (*p && *p != PATHSEP_C) p++; prot = *p? atoi (p+1) : 0; if (prot != 1) { log_error (_("gpg-agent protocol version %d is not supported\n"), prot); xfree (infostr); force_pipe_server = 1; goto restart; } err = assuan_socket_connect (ctx, infostr, pid, 0); xfree (infostr); if (gpg_err_code (err) == GPG_ERR_ASS_CONNECT_FAILED) { log_info (_("can't connect to the agent - trying fall back\n")); force_pipe_server = 1; goto restart; } } if (err) { log_error ("can't connect to the agent: %s\n", gpg_strerror (err)); assuan_release (ctx); return gpg_err_make (errsource, GPG_ERR_NO_AGENT); } if (debug && !did_success_msg) log_debug (_("connection to agent established\n")); err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (!err) err = send_pinentry_environment (ctx, errsource, opt_lc_ctype, opt_lc_messages, session_env); if (err) { assuan_release (ctx); return err; } *r_ctx = ctx; return 0; }
/* Startup the server. DEFAULT_RECPLIST is the list of recipients as set from the command line or config file. We only require those marked as encrypt-to. */ void gpgsm_server (certlist_t default_recplist) { int rc; assuan_fd_t filedes[2]; assuan_context_t ctx; struct server_control_s ctrl; static const char hello[] = ("GNU Privacy Guard's S/M server " VERSION " ready"); memset (&ctrl, 0, sizeof ctrl); gpgsm_init_default_ctrl (&ctrl); /* We use a pipe based server so that we can work from scripts. assuan_init_pipe_server will automagically detect when we are called with a socketpair and ignore FILEDES in this case. */ #ifdef HAVE_W32CE_SYSTEM #define SERVER_STDIN es_fileno(es_stdin) #define SERVER_STDOUT es_fileno(es_stdout) #else #define SERVER_STDIN 0 #define SERVER_STDOUT 1 #endif filedes[0] = assuan_fdopen (SERVER_STDIN); filedes[1] = assuan_fdopen (SERVER_STDOUT); rc = assuan_new (&ctx); if (rc) { log_error ("failed to allocate assuan context: %s\n", gpg_strerror (rc)); gpgsm_exit (2); } rc = assuan_init_pipe_server (ctx, filedes); if (rc) { log_error ("failed to initialize the server: %s\n", gpg_strerror (rc)); gpgsm_exit (2); } rc = register_commands (ctx); if (rc) { log_error ("failed to the register commands with Assuan: %s\n", gpg_strerror(rc)); gpgsm_exit (2); } if (opt.verbose || opt.debug) { char *tmp = NULL; const char *s1 = getenv ("GPG_AGENT_INFO"); if (asprintf (&tmp, "Home: %s\n" "Config: %s\n" "AgentInfo: %s\n" "DirmngrInfo: %s\n" "%s", opt.homedir, opt.config_filename, s1?s1:"[not set]", dirmngr_socket_name (), hello) > 0) { assuan_set_hello_line (ctx, tmp); free (tmp); } } else assuan_set_hello_line (ctx, hello); assuan_register_reset_notify (ctx, reset_notify); assuan_register_input_notify (ctx, input_notify); assuan_register_output_notify (ctx, output_notify); assuan_register_option_handler (ctx, option_handler); assuan_set_pointer (ctx, &ctrl); ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); ctrl.server_local->assuan_ctx = ctx; ctrl.server_local->message_fd = -1; ctrl.server_local->list_internal = 1; ctrl.server_local->list_external = 0; ctrl.server_local->default_recplist = default_recplist; for (;;) { rc = assuan_accept (ctx); if (rc == -1) { break; } else if (rc) { log_info ("Assuan accept problem: %s\n", gpg_strerror (rc)); break; } rc = assuan_process (ctx); if (rc) { log_info ("Assuan processing failed: %s\n", gpg_strerror (rc)); continue; } } gpgsm_release_certlist (ctrl.server_local->recplist); ctrl.server_local->recplist = NULL; gpgsm_release_certlist (ctrl.server_local->signerlist); ctrl.server_local->signerlist = NULL; xfree (ctrl.server_local); audit_release (ctrl.audit); ctrl.audit = NULL; assuan_release (ctx); }
static int ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) { int rc, i; char *serialno; int no_card = 0; char *desc; char *want_sn, *want_kid; int want_sn_displen; *r_kid = NULL; rc = parse_shadow_info (shadow_info, &want_sn, &want_kid); if (rc) return rc; /* We assume that a 20 byte serial number is a standard one which has the property to have a zero in the last nibble (Due to BCD representation). We don't display this '0' because it may confuse the user. */ want_sn_displen = strlen (want_sn); if (want_sn_displen == 20 && want_sn[19] == '0') want_sn_displen--; for (;;) { rc = agent_card_serialno (ctrl, &serialno); if (!rc) { log_debug ("detected card with S/N %s\n", serialno); i = strcmp (serialno, want_sn); xfree (serialno); serialno = NULL; if (!i) { xfree (want_sn); *r_kid = want_kid; return 0; /* yes, we have the correct card */ } } else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) { log_debug ("no card present\n"); rc = 0; no_card = 1; } else { log_error ("error accessing card: %s\n", gpg_strerror (rc)); } if (!rc) { if (asprintf (&desc, "%s:%%0A%%0A" " \"%.*s\"", no_card ? _("Please insert the card with serial number") : _("Please remove the current card and " "insert the one with serial number"), want_sn_displen, want_sn) < 0) { rc = out_of_core (); } else { rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); xfree (desc); } } if (rc) { xfree (want_sn); xfree (want_kid); return rc; } } }
/* Include the implementation of map_spwq_error. */ MAP_SPWQ_ERROR_IMPL static void preset_passphrase (const char *keygrip) { int rc; char *line; /* FIXME: Use secure memory. */ char passphrase[500]; char *passphrase_esc; if (!opt_passphrase) { rc = read (0, passphrase, sizeof (passphrase) - 1); if (rc < 0) { log_error ("reading passphrase failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); return; } passphrase[rc] = '\0'; line = strchr (passphrase, '\n'); if (line) { if (line > passphrase && line[-1] == '\r') line--; *line = '\0'; } /* FIXME: How to handle empty passwords? */ } { const char *s = opt_passphrase ? opt_passphrase : passphrase; passphrase_esc = bin2hex (s, strlen (s), NULL); } if (!passphrase_esc) { log_error ("can not escape string: %s\n", gpg_strerror (gpg_error_from_syserror ())); return; } rc = asprintf (&line, "PRESET_PASSPHRASE %s -1 %s\n", keygrip, passphrase_esc); wipememory (passphrase_esc, strlen (passphrase_esc)); xfree (passphrase_esc); if (rc < 0) { log_error ("caching passphrase failed: %s\n", gpg_strerror (gpg_error_from_syserror ())); return; } if (!opt_passphrase) wipememory (passphrase, sizeof (passphrase)); rc = map_spwq_error (simple_query (line)); if (rc) { log_error ("caching passphrase failed: %s\n", gpg_strerror (rc)); return; } wipememory (line, strlen (line)); xfree (line); }
/* Initialize the data encryption key (session key). */ static int init_dek (DEK dek) { int rc=0, mode, i; dek->algo = gcry_cipher_map_name (dek->algoid); mode = gcry_cipher_mode_from_oid (dek->algoid); if (!dek->algo || !mode) { log_error ("unsupported algorithm '%s'\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } /* Extra check for algorithms we consider to be too weak for encryption, although we support them for decryption. Note that there is another check below discriminating on the key length. */ switch (dek->algo) { case GCRY_CIPHER_DES: case GCRY_CIPHER_RFC2268_40: log_error ("cipher algorithm '%s' not allowed: too weak\n", gnupg_cipher_algo_name (dek->algo)); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); default: break; } dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); if (!dek->keylen || dek->keylen > sizeof (dek->key)) return gpg_error (GPG_ERR_BUG); dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo); if (!dek->ivlen || dek->ivlen > sizeof (dek->iv)) return gpg_error (GPG_ERR_BUG); /* Make sure we don't use weak keys. */ if (dek->keylen < 100/8) { log_error ("key length of '%s' too small\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE); if (rc) { log_error ("failed to create cipher context: %s\n", gpg_strerror (rc)); return rc; } for (i=0; i < 8; i++) { gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen); if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) break; log_info(_("weak key created - retrying\n") ); } if (rc) { log_error ("failed to set the key: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } gcry_create_nonce (dek->iv, dek->ivlen); rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen); if (rc) { log_error ("failed to set the IV: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } return 0; }
/* Return 0 if the cert is usable for encryption. A MODE of 0 checks for signing, a MODE of 1 checks for encryption, a MODE of 2 checks for verification and a MODE of 3 for decryption (just for debugging). MODE 4 is for certificate signing, MODE 5 for OCSP response signing, MODE 6 is for CRL signing. */ static int cert_usage_p (ksba_cert_t cert, int mode) { gpg_error_t err; unsigned int use; char *extkeyusages; int have_ocsp_signing = 0; err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; /* No policy given. */ if (!err) { unsigned int extusemask = ~0; /* Allow all. */ if (extkeyusages) { char *p, *pend; int any_critical = 0; extusemask = 0; p = extkeyusages; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; /* Only care about critical flagged usages. */ if ( *pend == 'C' ) { any_critical = 1; if ( !strcmp (p, oid_kp_serverAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_clientAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_codeSigning)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE); else if ( !strcmp (p, oid_kp_emailProtection)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_timeStamping)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION); } /* This is a hack to cope with OCSP. Note that we do not yet fully comply with the requirements and that the entire CRL/OCSP checking thing should undergo a thorough review and probably redesign. */ if ( !strcmp (p, oid_kp_ocspSigning)) have_ocsp_signing = 1; if ((p = strchr (pend, '\n'))) p++; } ksba_free (extkeyusages); extkeyusages = NULL; if (!any_critical) extusemask = ~0; /* Reset to the don't care mask. */ } err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; if (opt.verbose && mode < 2) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } /* Apply extKeyUsage. */ use &= extusemask; } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); ksba_free (extkeyusages); return err; } if (mode == 4) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; log_info (_("certificate should not have " "been used for certification\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (mode == 5) { if (use != ~0 && (have_ocsp_signing || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN |KSBA_KEYUSAGE_CRL_SIGN)))) return 0; log_info (_("certificate should not have " "been used for OCSP response signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (mode == 6) { if ((use & (KSBA_KEYUSAGE_CRL_SIGN))) return 0; log_info (_("certificate should not have " "been used for CRL signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if ((use & ((mode&1)? (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) ) return 0; log_info (mode==3? _("certificate should not have been used " "for encryption\n"): mode==2? _("certificate should not have been used for signing\n"): mode==1? _("certificate is not usable for encryption\n"): _("certificate is not usable for signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); }
int _keybox_dump_file (const char *filename, int stats_only, FILE *outfp) { FILE *fp; KEYBOXBLOB blob; int rc; unsigned long count = 0; struct file_stats_s stats; memset (&stats, 0, sizeof stats); if (!(fp = open_file (&filename, outfp))) return gpg_error_from_syserror (); for (;;) { rc = _keybox_read_blob (&blob, fp); if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX) { if (stats_only) stats.skipped_long_blobs++; else { fprintf (outfp, "BEGIN-RECORD: %lu\n", count ); fprintf (outfp, "# Record too large\nEND-RECORD\n"); } count++; continue; } if (rc) break; if (stats_only) { update_stats (blob, &stats); } else { fprintf (outfp, "BEGIN-RECORD: %lu\n", count ); _keybox_dump_blob (blob, outfp); fprintf (outfp, "END-RECORD\n"); } _keybox_release_blob (blob); count++; } if (rc == -1) rc = 0; if (rc) fprintf (outfp, "# error reading '%s': %s\n", filename, gpg_strerror (rc)); if (fp != stdin) fclose (fp); if (stats_only) { fprintf (outfp, "Total number of blobs: %8lu\n" " header: %8lu\n" " empty: %8lu\n" " openpgp: %8lu\n" " x509: %8lu\n" " non flagged: %8lu\n" " secret flagged: %8lu\n" " ephemeral flagged: %8lu\n", stats.total_blob_count, stats.header_blob_count, stats.empty_blob_count, stats.pgp_blob_count, stats.x509_blob_count, stats.non_flagged, stats.secret_flagged, stats.ephemeral_flagged); if (stats.skipped_long_blobs) fprintf (outfp, " skipped long blobs: %8lu\n", stats.skipped_long_blobs); if (stats.unknown_blob_count) fprintf (outfp, " unknown blob types: %8lu\n", stats.unknown_blob_count); if (stats.too_short_blobs) fprintf (outfp, " too short blobs: %8lu (error)\n", stats.too_short_blobs); if (stats.too_large_blobs) fprintf (outfp, " too large blobs: %8lu (error)\n", stats.too_large_blobs); } return rc; }
/* Validate the certificate CHAIN up to the trust anchor. Optionally return the closest expiration time in R_EXPTIME (this is useful for caching issues). MODE is one of the VALIDATE_MODE_* constants. If R_TRUST_ANCHOR is not NULL and the validation would fail only because the root certificate is not trusted, the hexified fingerprint of that root certificate is stored at R_TRUST_ANCHOR and success is returned. The caller needs to free the value at R_TRUST_ANCHOR; in all other cases NULL is stored there. */ gpg_error_t validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, int mode, char **r_trust_anchor) { gpg_error_t err = 0; int depth, maxdepth; char *issuer = NULL; char *subject = NULL; ksba_cert_t subject_cert = NULL, issuer_cert = NULL; ksba_isotime_t current_time; ksba_isotime_t exptime; int any_expired = 0; int any_no_policy_match = 0; chain_item_t chain; if (r_exptime) *r_exptime = 0; *exptime = 0; if (r_trust_anchor) *r_trust_anchor = NULL; if (!opt.system_daemon) { /* For backward compatibility we only do this in daemon mode. */ log_info (_("running in compatibility mode - " "certificate chain not checked!\n")); return 0; /* Okay. */ } if (DBG_X509) dump_cert ("subject", cert); /* May the target certificate be used for this purpose? */ switch (mode) { case VALIDATE_MODE_OCSP: err = cert_use_ocsp_p (cert); break; case VALIDATE_MODE_CRL: case VALIDATE_MODE_CRL_RECURSIVE: err = cert_use_crl_p (cert); break; default: err = 0; break; } if (err) return err; /* If we already validated the certificate not too long ago, we can avoid the excessive computations and lookups unless the caller asked for the expiration time. */ if (!r_exptime) { size_t buflen; time_t validated_at; err = ksba_cert_get_user_data (cert, "validated_at", &validated_at, sizeof (validated_at), &buflen); if (err || buflen != sizeof (validated_at) || !validated_at) err = 0; /* Not available or other error. */ else { /* If the validation is not older than 30 minutes we are ready. */ if (validated_at < gnupg_get_time () + (30*60)) { if (opt.verbose) log_info ("certificate is good (cached)\n"); /* Note, that we can't jump to leave here as this would falsely updated the validation timestamp. */ return 0; } } } /* Get the current time. */ gnupg_get_isotime (current_time); /* We walk up the chain until we find a trust anchor. */ subject_cert = cert; maxdepth = 10; chain = NULL; depth = 0; for (;;) { /* Get the subject and issuer name from the current certificate. */ ksba_free (issuer); ksba_free (subject); issuer = ksba_cert_get_issuer (subject_cert, 0); subject = ksba_cert_get_subject (subject_cert, 0); if (!issuer) { log_error (_("no issuer found in certificate\n")); err = gpg_error (GPG_ERR_BAD_CERT); goto leave; } /* Handle the notBefore and notAfter timestamps. */ { ksba_isotime_t not_before, not_after; err = ksba_cert_get_validity (subject_cert, 0, not_before); if (!err) err = ksba_cert_get_validity (subject_cert, 1, not_after); if (err) { log_error (_("certificate with invalid validity: %s"), gpg_strerror (err)); err = gpg_error (GPG_ERR_BAD_CERT); goto leave; } /* Keep track of the nearest expiration time in EXPTIME. */ if (*not_after) { if (!*exptime) gnupg_copy_time (exptime, not_after); else if (strcmp (not_after, exptime) < 0 ) gnupg_copy_time (exptime, not_after); } /* Check whether the certificate is already valid. */ if (*not_before && strcmp (current_time, not_before) < 0 ) { log_error (_("certificate not yet valid")); log_info ("(valid from "); dump_isotime (not_before); log_printf (")\n"); err = gpg_error (GPG_ERR_CERT_TOO_YOUNG); goto leave; } /* Now check whether the certificate has expired. */ if (*not_after && strcmp (current_time, not_after) > 0 ) { log_error (_("certificate has expired")); log_info ("(expired at "); dump_isotime (not_after); log_printf (")\n"); any_expired = 1; } } /* Do we have any critical extensions in the certificate we can't handle? */ err = unknown_criticals (subject_cert); if (err) goto leave; /* yes. */ /* Check that given policies are allowed. */ err = check_cert_policy (subject_cert); if (gpg_err_code (err) == GPG_ERR_NO_POLICY_MATCH) { any_no_policy_match = 1; err = 0; } else if (err) goto leave; /* Is this a self-signed certificate? */ if (is_root_cert ( subject_cert, issuer, subject)) { /* Yes, this is our trust anchor. */ if (check_cert_sig (subject_cert, subject_cert) ) { log_error (_("selfsigned certificate has a BAD signature")); err = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN : GPG_ERR_BAD_CERT); goto leave; } /* Is this certificate allowed to act as a CA. */ err = allowed_ca (subject_cert, NULL); if (err) goto leave; /* No. */ err = is_trusted_cert (subject_cert); if (!err) ; /* Yes we trust this cert. */ else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED) { char *fpr; log_error (_("root certificate is not marked trusted")); fpr = get_fingerprint_hexstring (subject_cert); log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); dump_cert ("issuer", subject_cert); if (r_trust_anchor) { /* Caller wants to do another trustiness check. */ *r_trust_anchor = fpr; err = 0; } else xfree (fpr); } else { log_error (_("checking trustworthiness of " "root certificate failed: %s\n"), gpg_strerror (err)); } if (err) goto leave; /* Prepend the certificate to our list. */ { chain_item_t ci; ci = xtrycalloc (1, sizeof *ci); if (!ci) { err = gpg_error_from_errno (errno); goto leave; } ksba_cert_ref (subject_cert); ci->cert = subject_cert; cert_compute_fpr (subject_cert, ci->fpr); ci->next = chain; chain = ci; } if (opt.verbose) { if (r_trust_anchor && *r_trust_anchor) log_info ("root certificate is good but not trusted\n"); else log_info ("root certificate is good and trusted\n"); } break; /* Okay: a self-signed certicate is an end-point. */ } /* To avoid loops, we use an arbitrary limit on the length of the chain. */ depth++; if (depth > maxdepth) { log_error (_("certificate chain too long\n")); err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } /* Find the next cert up the tree. */ ksba_cert_release (issuer_cert); issuer_cert = NULL; err = find_issuing_cert (ctrl, subject_cert, &issuer_cert); if (err) { if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { log_error (_("issuer certificate not found")); log_info ("issuer certificate: #/"); dump_string (issuer); log_printf ("\n"); } else log_error (_("issuer certificate not found: %s\n"), gpg_strerror (err)); /* Use a better understandable error code. */ err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } /* try_another_cert: */ if (DBG_X509) { log_debug ("got issuer's certificate:\n"); dump_cert ("issuer", issuer_cert); } /* Now check the signature of the certificate. Well, we should delay this until later so that faked certificates can't be turned into a DoS easily. */ err = check_cert_sig (issuer_cert, subject_cert); if (err) { log_error (_("certificate has a BAD signature")); #if 0 if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE) { /* We now try to find other issuer certificates which might have been used. This is required because some CAs are reusing the issuer and subject DN for new root certificates without using a authorityKeyIdentifier. */ rc = find_up (kh, subject_cert, issuer, 1); if (!rc) { ksba_cert_t tmp_cert; rc = keydb_get_cert (kh, &tmp_cert); if (rc || !compare_certs (issuer_cert, tmp_cert)) { /* The find next did not work or returned an identical certificate. We better stop here to avoid infinite checks. */ rc = gpg_error (GPG_ERR_BAD_SIGNATURE); ksba_cert_release (tmp_cert); } else { do_list (0, lm, fp, _("found another possible matching " "CA certificate - trying again")); ksba_cert_release (issuer_cert); issuer_cert = tmp_cert; goto try_another_cert; } } } #endif /* We give a more descriptive error code than the one returned from the signature checking. */ err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } /* Check that the length of the chain is not longer than allowed by the CA. */ { int chainlen; err = allowed_ca (issuer_cert, &chainlen); if (err) goto leave; if (chainlen >= 0 && (depth - 1) > chainlen) { log_error (_("certificate chain longer than allowed by CA (%d)"), chainlen); err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } } /* May that certificate be used for certification? */ err = cert_use_cert_p (issuer_cert); if (err) goto leave; /* No. */ /* Prepend the certificate to our list. */ { chain_item_t ci; ci = xtrycalloc (1, sizeof *ci); if (!ci) { err = gpg_error_from_errno (errno); goto leave; } ksba_cert_ref (subject_cert); ci->cert = subject_cert; cert_compute_fpr (subject_cert, ci->fpr); ci->next = chain; chain = ci; } if (opt.verbose) log_info (_("certificate is good\n")); /* Now to the next level up. */ subject_cert = issuer_cert; issuer_cert = NULL; } if (!err) { /* If we encountered an error somewhere during the checks, set the error code to the most critical one */ if (any_expired) err = gpg_error (GPG_ERR_CERT_EXPIRED); else if (any_no_policy_match) err = gpg_error (GPG_ERR_NO_POLICY_MATCH); } if (!err && opt.verbose) { chain_item_t citem; log_info (_("certificate chain is good\n")); for (citem = chain; citem; citem = citem->next) cert_log_name (" certificate", citem->cert); } if (!err && mode != VALIDATE_MODE_CRL) { /* Now that everything is fine, walk the chain and check each certificate for revocations. 1. item in the chain - The root certificate. 2. item - the CA below the root last item - the target certificate. Now for each certificate in the chain check whether it has been included in a CRL and thus be revoked. We don't do OCSP here because this does not seem to make much sense. This might become a recursive process and we should better cache our validity results to avoid double work. Far worse a catch-22 may happen for an improper setup hierarchy and we need a way to break up such a deadlock. */ err = check_revocations (ctrl, chain); } if (!err && opt.verbose) { if (r_trust_anchor && *r_trust_anchor) log_info ("target certificate may be valid\n"); else log_info ("target certificate is valid\n"); } else if (err && opt.verbose) log_info ("target certificate is NOT valid\n"); leave: if (!err && !(r_trust_anchor && *r_trust_anchor)) { /* With no error we can update the validation cache. We do this for all certificates in the chain. Note that we can't use the cache if the caller requested to check the trustiness of the root certificate himself. Adding such a feature would require us to also store the fingerprint of root certificate. */ chain_item_t citem; time_t validated_at = gnupg_get_time (); for (citem = chain; citem; citem = citem->next) { err = ksba_cert_set_user_data (citem->cert, "validated_at", &validated_at, sizeof (validated_at)); if (err) { log_error ("set_user_data(validated_at) failed: %s\n", gpg_strerror (err)); err = 0; } } } if (r_exptime) gnupg_copy_time (r_exptime, exptime); ksba_free (issuer); ksba_free (subject); ksba_cert_release (issuer_cert); if (subject_cert != cert) ksba_cert_release (subject_cert); while (chain) { chain_item_t ci_next = chain->next; if (chain->cert) ksba_cert_release (chain->cert); xfree (chain); chain = ci_next; } if (err && r_trust_anchor && *r_trust_anchor) { xfree (*r_trust_anchor); *r_trust_anchor = NULL; } return err; }
int _keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp) { FILE *fp; KEYBOXBLOB blob; int rc; unsigned long recno = 0; unsigned char zerodigest[20]; struct dupitem_s *dupitems; size_t dupitems_size, dupitems_count, lastn, n; char fprbuf[3*20+1]; (void)print_them; memset (zerodigest, 0, sizeof zerodigest); if (!(fp = open_file (&filename, outfp))) return gpg_error_from_syserror (); dupitems_size = 1000; dupitems = malloc (dupitems_size * sizeof *dupitems); if (!dupitems) { gpg_error_t tmperr = gpg_error_from_syserror (); fprintf (outfp, "error allocating array for '%s': %s\n", filename, strerror(errno)); return tmperr; } dupitems_count = 0; while ( !(rc = _keybox_read_blob (&blob, fp)) ) { unsigned char digest[20]; if (hash_blob_rawdata (blob, digest)) fprintf (outfp, "error in blob %ld of '%s'\n", recno, filename); else if (memcmp (digest, zerodigest, 20)) { if (dupitems_count >= dupitems_size) { struct dupitem_s *tmp; dupitems_size += 1000; tmp = realloc (dupitems, dupitems_size * sizeof *dupitems); if (!tmp) { gpg_error_t tmperr = gpg_error_from_syserror (); fprintf (outfp, "error reallocating array for '%s': %s\n", filename, strerror(errno)); free (dupitems); return tmperr; } dupitems = tmp; } dupitems[dupitems_count].recno = recno; memcpy (dupitems[dupitems_count].digest, digest, 20); dupitems_count++; } _keybox_release_blob (blob); recno++; } if (rc == -1) rc = 0; if (rc) fprintf (outfp, "error reading '%s': %s\n", filename, gpg_strerror (rc)); if (fp != stdin) fclose (fp); qsort (dupitems, dupitems_count, sizeof *dupitems, cmp_dupitems); for (lastn=0, n=1; n < dupitems_count; lastn=n, n++) { if (!memcmp (dupitems[lastn].digest, dupitems[n].digest, 20)) { bin2hexcolon (dupitems[lastn].digest, 20, fprbuf); fprintf (outfp, "fpr=%s recno=%lu", fprbuf, dupitems[lastn].recno); do fprintf (outfp, " %lu", dupitems[n].recno); while (++n < dupitems_count && !memcmp (dupitems[lastn].digest, dupitems[n].digest, 20)); putc ('\n', outfp); n--; } } free (dupitems); return rc; }
/* Get the key from URL which is expected to specify a http style scheme. On success R_FP has an open stream to read the data. */ gpg_error_t ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) { gpg_error_t err; http_session_t session = NULL; http_t http = NULL; int redirects_left = MAX_REDIRECTS; estream_t fp = NULL; char *request_buffer = NULL; err = http_session_new (&session, NULL, NULL); if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); *r_fp = NULL; once_more: err = http_open (&http, HTTP_REQ_GET, url, /* httphost */ NULL, /* fixme: AUTH */ NULL, ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) | (opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), ctrl->http_proxy, session, NULL, /*FIXME curl->srvtag*/NULL); if (!err) { fp = http_get_write_ptr (http); /* Avoid caches to get the most recent copy of the key. We set both the Pragma and Cache-Control versions of the header, so we're good with both HTTP 1.0 and 1.1. */ es_fputs ("Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n", fp); http_start_data (http); if (es_ferror (fp)) err = gpg_error_from_syserror (); } if (err) { /* Fixme: After a redirection we show the old host name. */ log_error (_("error connecting to '%s': %s\n"), url, gpg_strerror (err)); goto leave; } /* Wait for the response. */ dirmngr_tick (ctrl); err = http_wait_response (http); if (err) { log_error (_("error reading HTTP response for '%s': %s\n"), url, gpg_strerror (err)); goto leave; } switch (http_get_status_code (http)) { case 200: err = 0; break; /* Success. */ case 301: case 302: case 307: { const char *s = http_get_header (http, "Location"); log_info (_("URL '%s' redirected to '%s' (%u)\n"), url, s?s:"[none]", http_get_status_code (http)); if (s && *s && redirects_left-- ) { xfree (request_buffer); request_buffer = xtrystrdup (s); if (request_buffer) { url = request_buffer; http_close (http, 0); http = NULL; goto once_more; } err = gpg_error_from_syserror (); } else err = gpg_error (GPG_ERR_NO_DATA); log_error (_("too many redirections\n")); } goto leave; default: log_error (_("error accessing '%s': http status %u\n"), url, http_get_status_code (http)); err = gpg_error (GPG_ERR_NO_DATA); goto leave; } fp = http_get_read_ptr (http); if (!fp) { err = gpg_error (GPG_ERR_BUG); goto leave; } /* Return the read stream and close the HTTP context. */ *r_fp = fp; http_close (http, 1); http = NULL; leave: http_close (http, 0); http_session_release (session); xfree (request_buffer); return err; }
static void check_scrypt (void) { /* Test vectors are from draft-josefsson-scrypt-kdf-01. */ static struct { const char *p; /* Passphrase. */ size_t plen; /* Length of P. */ const char *salt; size_t saltlen; int parm_n; /* CPU/memory cost. */ int parm_r; /* blocksize */ unsigned long parm_p; /* parallelization. */ int dklen; /* Requested key length. */ const char *dk; /* Derived key. */ int disabled; } tv[] = { { "", 0, "", 0, 16, 1, 1, 64, "\x77\xd6\x57\x62\x38\x65\x7b\x20\x3b\x19\xca\x42\xc1\x8a\x04\x97" "\xf1\x6b\x48\x44\xe3\x07\x4a\xe8\xdf\xdf\xfa\x3f\xed\xe2\x14\x42" "\xfc\xd0\x06\x9d\xed\x09\x48\xf8\x32\x6a\x75\x3a\x0f\xc8\x1f\x17" "\xe8\xd3\xe0\xfb\x2e\x0d\x36\x28\xcf\x35\xe2\x0c\x38\xd1\x89\x06" }, { "password", 8, "NaCl", 4, 1024, 8, 16, 64, "\xfd\xba\xbe\x1c\x9d\x34\x72\x00\x78\x56\xe7\x19\x0d\x01\xe9\xfe" "\x7c\x6a\xd7\xcb\xc8\x23\x78\x30\xe7\x73\x76\x63\x4b\x37\x31\x62" "\x2e\xaf\x30\xd9\x2e\x22\xa3\x88\x6f\xf1\x09\x27\x9d\x98\x30\xda" "\xc7\x27\xaf\xb9\x4a\x83\xee\x6d\x83\x60\xcb\xdf\xa2\xcc\x06\x40" }, { "pleaseletmein", 13, "SodiumChloride", 14, 16384, 8, 1, 64, "\x70\x23\xbd\xcb\x3a\xfd\x73\x48\x46\x1c\x06\xcd\x81\xfd\x38\xeb" "\xfd\xa8\xfb\xba\x90\x4f\x8e\x3e\xa9\xb5\x43\xf6\x54\x5d\xa1\xf2" "\xd5\x43\x29\x55\x61\x3f\x0f\xcf\x62\xd4\x97\x05\x24\x2a\x9a\xf9" "\xe6\x1e\x85\xdc\x0d\x65\x1e\x40\xdf\xcf\x01\x7b\x45\x57\x58\x87" }, { "pleaseletmein", 13, "SodiumChloride", 14, 1048576, 8, 1, 64, "\x21\x01\xcb\x9b\x6a\x51\x1a\xae\xad\xdb\xbe\x09\xcf\x70\xf8\x81" "\xec\x56\x8d\x57\x4a\x2f\xfd\x4d\xab\xe5\xee\x98\x20\xad\xaa\x47" "\x8e\x56\xfd\x8f\x4b\xa5\xd0\x9f\xfa\x1c\x6d\x92\x7c\x40\xf4\xc3" "\x37\x30\x40\x49\xe8\xa9\x52\xfb\xcb\xf4\x5c\x6f\xa7\x7a\x41\xa4", 2 /* Only in debug mode. */ } }; int tvidx; gpg_error_t err; unsigned char outbuf[64]; int i; for (tvidx=0; tvidx < DIM(tv); tvidx++) { if (tv[tvidx].disabled && !(tv[tvidx].disabled == 2 && debug)) continue; if (verbose) fprintf (stderr, "checking SCRYPT test vector %d\n", tvidx); assert (tv[tvidx].dklen <= sizeof outbuf); err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, tv[tvidx].parm_r == 1 ? 41 : GCRY_KDF_SCRYPT, tv[tvidx].parm_n, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].parm_p, tv[tvidx].dklen, outbuf); if (err) fail ("scrypt test %d failed: %s\n", tvidx, gpg_strerror (err)); else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) { fail ("scrypt test %d failed: mismatch\n", tvidx); fputs ("got:", stderr); for (i=0; i < tv[tvidx].dklen; i++) fprintf (stderr, " %02x", outbuf[i]); putc ('\n', stderr); } } }
/* Decrypt the collected data. */ static void decrypt_data (receive_ctx_t ctx) { gpg_error_t err; ccparray_t ccp; const char **argv; int c; es_rewind (ctx->encrypted); if (!ctx->plaintext) ctx->plaintext = es_fopenmem (0, "w+b"); if (!ctx->plaintext) { err = gpg_error_from_syserror (); log_error ("error allocating space for plaintext: %s\n", gpg_strerror (err)); return; } ccparray_init (&ccp, 0); /* We limit the output to 64 KiB to avoid DoS using compression * tricks. A regular client will anyway only send a minimal key; * that is one w/o key signatures and attribute packets. */ ccparray_put (&ccp, "--max-output=0xf0000"); /*FIXME: Change s/F/1/ */ ccparray_put (&ccp, "--batch"); if (opt.verbose) ccparray_put (&ccp, "--verbose"); ccparray_put (&ccp, "--always-trust"); ccparray_put (&ccp, "--decrypt"); ccparray_put (&ccp, "--"); ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); if (!argv) { err = gpg_error_from_syserror (); goto leave; } err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted, NULL, ctx->plaintext, decrypt_data_status_cb, ctx); if (err) { log_error ("decryption failed: %s\n", gpg_strerror (err)); goto leave; } if (opt.debug) { es_rewind (ctx->plaintext); log_debug ("plaintext: '"); while ((c = es_getc (ctx->plaintext)) != EOF) log_printf ("%c", c); log_printf ("'\n"); } es_rewind (ctx->plaintext); leave: xfree (argv); }
static void check_openpgp (void) { /* Test vectors manually created with gpg 1.4 derived code: In passphrase.c:hash_passpharse, add this code to the end of the function: ===8<=== printf ("{\n" " \""); for (i=0; i < pwlen; i++) { if (i && !(i%16)) printf ("\"\n \""); printf ("\\x%02x", ((const unsigned char *)pw)[i]); } printf ("\", %d,\n", pwlen); printf (" %s, %s,\n", s2k->mode == 0? "GCRY_KDF_SIMPLE_S2K": s2k->mode == 1? "GCRY_KDF_SALTED_S2K": s2k->mode == 3? "GCRY_KDF_ITERSALTED_S2K":"?", s2k->hash_algo == DIGEST_ALGO_MD5 ? "GCRY_MD_MD5" : s2k->hash_algo == DIGEST_ALGO_SHA1 ? "GCRY_MD_SHA1" : s2k->hash_algo == DIGEST_ALGO_RMD160? "GCRY_MD_RMD160" : s2k->hash_algo == DIGEST_ALGO_SHA256? "GCRY_MD_SHA256" : s2k->hash_algo == DIGEST_ALGO_SHA384? "GCRY_MD_SHA384" : s2k->hash_algo == DIGEST_ALGO_SHA512? "GCRY_MD_SHA512" : s2k->hash_algo == DIGEST_ALGO_SHA224? "GCRY_MD_SHA224" : "?"); if (s2k->mode == 0) printf (" NULL, 0,\n"); else { printf (" \""); for (i=0; i < 8; i++) printf ("\\x%02x", (unsigned int)s2k->salt[i]); printf ("\", %d,\n", 8); } if (s2k->mode == 3) printf (" %lu,\n", (unsigned long)S2K_DECODE_COUNT(s2k->count)); else printf (" 0,\n"); printf (" %d,\n", (int)dek->keylen); printf (" \""); for (i=0; i < dek->keylen; i++) { if (i && !(i%16)) printf ("\"\n \""); printf ("\\x%02x", ((unsigned char *)dek->key)[i]); } printf ("\"\n},\n"); ===>8=== Then prepare a file x.inp with utf8 encoding: ===8<=== 0 aes md5 1024 a 0 aes md5 1024 ab 0 aes md5 1024 abc 0 aes md5 1024 abcd 0 aes md5 1024 abcde 0 aes md5 1024 abcdef 0 aes md5 1024 abcdefg 0 aes md5 1024 abcdefgh 0 aes md5 1024 abcdefghi 0 aes md5 1024 abcdefghijklmno 0 aes md5 1024 abcdefghijklmnop 0 aes md5 1024 abcdefghijklmnopq 0 aes md5 1024 Long_sentence_used_as_passphrase 0 aes md5 1024 With_utf8_umlauts:äüÖß 0 aes sha1 1024 a 0 aes sha1 1024 ab 0 aes sha1 1024 abc 0 aes sha1 1024 abcd 0 aes sha1 1024 abcde 0 aes sha1 1024 abcdef 0 aes sha1 1024 abcdefg 0 aes sha1 1024 abcdefgh 0 aes sha1 1024 abcdefghi 0 aes sha1 1024 abcdefghijklmno 0 aes sha1 1024 abcdefghijklmnop 0 aes sha1 1024 abcdefghijklmnopq 0 aes sha1 1024 abcdefghijklmnopqr 0 aes sha1 1024 abcdefghijklmnopqrs 0 aes sha1 1024 abcdefghijklmnopqrst 0 aes sha1 1024 abcdefghijklmnopqrstu 0 aes sha1 1024 Long_sentence_used_as_passphrase 0 aes256 sha1 1024 Long_sentence_used_as_passphrase 0 aes sha1 1024 With_utf8_umlauts:äüÖß 3 aes sha1 1024 a 3 aes sha1 1024 ab 3 aes sha1 1024 abc 3 aes sha1 1024 abcd 3 aes sha1 1024 abcde 3 aes sha1 1024 abcdef 3 aes sha1 1024 abcdefg 3 aes sha1 1024 abcdefgh 3 aes sha1 1024 abcdefghi 3 aes sha1 1024 abcdefghijklmno 3 aes sha1 1024 abcdefghijklmnop 3 aes sha1 1024 abcdefghijklmnopq 3 aes sha1 1024 abcdefghijklmnopqr 3 aes sha1 1024 abcdefghijklmnopqrs 3 aes sha1 1024 abcdefghijklmnopqrst 3 aes sha1 1024 abcdefghijklmnopqrstu 3 aes sha1 1024 With_utf8_umlauts:äüÖß 3 aes sha1 1024 Long_sentence_used_as_passphrase 3 aes sha1 10240 Long_sentence_used_as_passphrase 3 aes sha1 102400 Long_sentence_used_as_passphrase 3 aes192 sha1 1024 a 3 aes192 sha1 1024 abcdefg 3 aes192 sha1 1024 abcdefghi 3 aes192 sha1 1024 abcdefghi 3 aes192 sha1 1024 Long_sentence_used_as_passphrase 3 aes256 sha1 1024 a 3 aes256 sha1 1024 abcdefg 3 aes256 sha1 1024 abcdefghi 3 aes256 sha1 1024 abcdefghi 3 aes256 sha1 1024 Long_sentence_used_as_passphrase 0 aes sha256 1024 Long_sentence_used_as_passphrase 1 aes sha256 1024 Long_sentence_used_as_passphrase 3 aes sha256 1024 Long_sentence_used_as_passphrase 3 aes sha256 10240 Long_sentence_used_as_passphrase 3 aes sha384 1024 Long_sentence_used_as_passphrase 3 aes sha512 1024 Long_sentence_used_as_passphrase 3 aes256 sha512 1024 Long_sentence_used_as_passphrase 3 3des sha512 1024 Long_sentence_used_as_passphrase ===>8=== and finally using a proper utf-8 enabled shell, run: cat x.inp | while read mode cipher digest count pass dummy; do \ ./gpg </dev/null -o /dev/null -c --passphrase "$pass" \ --s2k-mode $mode --s2k-digest $digest --s2k-count $count \ --cipher-algo $cipher ; done >x.out */ static struct { const char *p; /* Passphrase. */ size_t plen; /* Length of P. */ int algo; int hashalgo; const char *salt; size_t saltlen; unsigned long c; /* Iterations. */ int dklen; /* Requested key length. */ const char *dk; /* Derived key. */ int disabled; } tv[] = { { "\x61", 1, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8\x31\xc3\x99\xe2\x69\x77\x26\x61" }, { "\x61\x62", 2, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x18\x7e\xf4\x43\x61\x22\xd1\xcc\x2f\x40\xdc\x2b\x92\xf0\xeb\xa0" }, { "\x61\x62\x63", 3, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x90\x01\x50\x98\x3c\xd2\x4f\xb0\xd6\x96\x3f\x7d\x28\xe1\x7f\x72" }, { "\x61\x62\x63\x64", 4, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xe2\xfc\x71\x4c\x47\x27\xee\x93\x95\xf3\x24\xcd\x2e\x7f\x33\x1f" }, { "\x61\x62\x63\x64\x65", 5, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xab\x56\xb4\xd9\x2b\x40\x71\x3a\xcc\x5a\xf8\x99\x85\xd4\xb7\x86" }, { "\x61\x62\x63\x64\x65\x66", 6, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xe8\x0b\x50\x17\x09\x89\x50\xfc\x58\xaa\xd8\x3c\x8c\x14\x97\x8e" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x7a\xc6\x6c\x0f\x14\x8d\xe9\x51\x9b\x8b\xd2\x64\x31\x2c\x4d\x64" }, { "\x61\x62\x63\x64\x65\x66\x67\x68", 8, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\xe8\xdc\x40\x81\xb1\x34\x34\xb4\x51\x89\xa7\x20\xb7\x7b\x68\x18" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x8a\xa9\x9b\x1f\x43\x9f\xf7\x12\x93\xe9\x53\x57\xba\xc6\xfd\x94" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", 15, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x8a\x73\x19\xdb\xf6\x54\x4a\x74\x22\xc9\xe2\x54\x52\x58\x0e\xa5" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70", 16, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x1d\x64\xdc\xe2\x39\xc4\x43\x7b\x77\x36\x04\x1d\xb0\x89\xe1\xb9" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71", 17, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x9a\x8d\x98\x45\xa6\xb4\xd8\x2d\xfc\xb2\xc2\xe3\x51\x62\xc8\x30" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x35\x2a\xf0\xfc\xdf\xe9\xbb\x62\x16\xfc\x99\x9d\x8d\x58\x05\xcb" }, { "\x57\x69\x74\x68\x5f\x75\x74\x66\x38\x5f\x75\x6d\x6c\x61\x75\x74" "\x73\x3a\xc3\xa4\xc3\xbc\xc3\x96\xc3\x9f", 26, GCRY_KDF_SIMPLE_S2K, GCRY_MD_MD5, NULL, 0, 0, 16, "\x21\xa4\xeb\xd8\xfd\xf0\x59\x25\xd1\x32\x31\xdb\xe7\xf2\x13\x5d" }, { "\x61", 1, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x86\xf7\xe4\x37\xfa\xa5\xa7\xfc\xe1\x5d\x1d\xdc\xb9\xea\xea\xea" }, { "\x61\x62", 2, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xda\x23\x61\x4e\x02\x46\x9a\x0d\x7c\x7b\xd1\xbd\xab\x5c\x9c\x47" }, { "\x61\x62\x63", 3, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e\x25\x71\x78\x50\xc2\x6c" }, { "\x61\x62\x63\x64", 4, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x81\xfe\x8b\xfe\x87\x57\x6c\x3e\xcb\x22\x42\x6f\x8e\x57\x84\x73" }, { "\x61\x62\x63\x64\x65", 5, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x03\xde\x6c\x57\x0b\xfe\x24\xbf\xc3\x28\xcc\xd7\xca\x46\xb7\x6e" }, { "\x61\x62\x63\x64\x65\x66", 6, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x1f\x8a\xc1\x0f\x23\xc5\xb5\xbc\x11\x67\xbd\xa8\x4b\x83\x3e\x5c" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x2f\xb5\xe1\x34\x19\xfc\x89\x24\x68\x65\xe7\xa3\x24\xf4\x76\xec" }, { "\x61\x62\x63\x64\x65\x66\x67\x68", 8, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x42\x5a\xf1\x2a\x07\x43\x50\x2b\x32\x2e\x93\xa0\x15\xbc\xf8\x68" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xc6\x3b\x19\xf1\xe4\xc8\xb5\xf7\x6b\x25\xc4\x9b\x8b\x87\xf5\x7d" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", 15, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x29\x38\xdc\xc2\xe3\xaa\x77\x98\x7c\x7e\x5d\x4a\x0f\x26\x96\x67" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70", 16, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x14\xf3\x99\x52\x88\xac\xd1\x89\xe6\xe5\x0a\x7a\xf4\x7e\xe7\x09" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71", 17, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xd8\x3d\x62\x1f\xcd\x2d\x4d\x29\x85\x54\x70\x43\xa7\xa5\xfd\x4d" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72", 18, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xe3\x81\xfe\x42\xc5\x7e\x48\xa0\x82\x17\x86\x41\xef\xfd\x1c\xb9" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73", 19, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x89\x3e\x69\xff\x01\x09\xf3\x45\x9c\x42\x43\x01\x3b\x3d\xe8\xb1" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74", 20, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x14\xa2\x3a\xd7\x0f\x2a\x5d\xd7\x25\x57\x5d\xe6\xc4\x3e\x1c\xdd" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74\x75", 21, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xec\xa9\x86\xb9\x5d\x58\x7f\x34\xd7\x1c\xa7\x75\x2a\x4e\x00\x10" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\x3e\x1b\x9a\x50\x7d\x6e\x9a\xd8\x93\x64\x96\x7a\x3f\xcb\x27\x3f" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 32, "\x3e\x1b\x9a\x50\x7d\x6e\x9a\xd8\x93\x64\x96\x7a\x3f\xcb\x27\x3f" "\xc3\x7b\x3a\xb2\xef\x4d\x68\xaa\x9c\xd7\xe4\x88\xee\xd1\x5e\x70" }, { "\x57\x69\x74\x68\x5f\x75\x74\x66\x38\x5f\x75\x6d\x6c\x61\x75\x74" "\x73\x3a\xc3\xa4\xc3\xbc\xc3\x96\xc3\x9f", 26, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA1, NULL, 0, 0, 16, "\xe0\x4e\x1e\xe3\xad\x0b\x49\x7c\x7a\x5f\x37\x3b\x4d\x90\x3c\x2e" }, { "\x61", 1, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x6d\x47\xe3\x68\x5d\x2c\x36\x16", 8, 1024, 16, "\x41\x9f\x48\x6e\xbf\xe6\xdd\x05\x9a\x72\x23\x17\x44\xd8\xd3\xf3" }, { "\x61\x62", 2, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x7c\x34\x78\xfb\x28\x2d\x25\xc7", 8, 1024, 16, "\x0a\x9d\x09\x06\x43\x3d\x4f\xf9\x87\xd6\xf7\x48\x90\xde\xd1\x1c" }, { "\x61\x62\x63", 3, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xc3\x16\x37\x2e\x27\xf6\x9f\x6f", 8, 1024, 16, "\xf8\x27\xa0\x07\xc6\xcb\xdd\xf1\xfe\x5c\x88\x3a\xfc\xcd\x84\x4d" }, { "\x61\x62\x63\x64", 4, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xf0\x0c\x73\x38\xb7\xc3\xd5\x14", 8, 1024, 16, "\x9b\x5f\x26\xba\x52\x3b\xcd\xd9\xa5\x2a\xef\x3c\x03\x4d\xd1\x52" }, { "\x61\x62\x63\x64\x65", 5, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xe1\x7d\xa2\x36\x09\x59\xee\xc5", 8, 1024, 16, "\x94\x9d\x5b\x1a\x5a\x66\x8c\xfa\x8f\x6f\x22\xaf\x8b\x60\x9f\xaf" }, { "\x61\x62\x63\x64\x65\x66", 6, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xaf\xa7\x0c\x68\xdf\x7e\xaa\x27", 8, 1024, 16, "\xe5\x38\xf4\x39\x62\x27\xcd\xcc\x91\x37\x7f\x1b\xdc\x58\x64\x27" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x40\x57\xb2\x9d\x5f\xbb\x11\x4f", 8, 1024, 16, "\xad\xa2\x33\xd9\xdd\xe0\xfb\x94\x8e\xcc\xec\xcc\xb3\xa8\x3a\x9e" }, { "\x61\x62\x63\x64\x65\x66\x67\x68", 8, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x38\xf5\x65\xc5\x0f\x8c\x19\x61", 8, 1024, 16, "\xa0\xb0\x3e\x29\x76\xe6\x8f\xa0\xd8\x34\x8f\xa4\x2d\xfd\x65\xee" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xc3\xb7\x99\xcc\xda\x2d\x05\x7b", 8, 1024, 16, "\x27\x21\xc8\x99\x5f\xcf\x20\xeb\xf2\xd9\xff\x6a\x69\xff\xad\xe8" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f", 15, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x7d\xd8\x68\x8a\x1c\xc5\x47\x22", 8, 1024, 16, "\x0f\x96\x7a\x12\x23\x54\xf6\x92\x61\x67\x07\xb4\x68\x17\xb8\xaa" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70", 16, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x8a\x95\xd4\x88\x0b\xb8\xe9\x9d", 8, 1024, 16, "\xcc\xe4\xc8\x82\x53\x32\xf1\x93\x5a\x00\xd4\x7f\xd4\x46\xfa\x07" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71", 17, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xb5\x22\x48\xa6\xc4\xad\x74\x67", 8, 1024, 16, "\x0c\xe3\xe0\xee\x3d\x8f\x35\xd2\x35\x14\x14\x29\x0c\xf1\xe3\x34" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72", 18, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xac\x9f\x04\x63\x83\x0e\x3c\x95", 8, 1024, 16, "\x49\x0a\x04\x68\xa8\x2a\x43\x6f\xb9\x73\x94\xb4\x85\x9a\xaa\x0e" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73", 19, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x03\x6f\x60\x30\x3a\x19\x61\x0d", 8, 1024, 16, "\x15\xe5\x9b\xbf\x1c\xf0\xbe\x74\x95\x1a\xb2\xc4\xda\x09\xcd\x99" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74", 20, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x51\x40\xa5\x57\xf5\x28\xfd\x03", 8, 1024, 16, "\xa6\xf2\x7e\x6b\x30\x4d\x8d\x67\xd4\xa2\x7f\xa2\x57\x27\xab\x96" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74\x75", 21, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x4c\xf1\x10\x11\x04\x70\xd3\x6e", 8, 1024, 16, "\x2c\x50\x79\x8d\x83\x23\xac\xd6\x22\x29\x37\xaf\x15\x0d\xdd\x8f" }, { "\x57\x69\x74\x68\x5f\x75\x74\x66\x38\x5f\x75\x6d\x6c\x61\x75\x74" "\x73\x3a\xc3\xa4\xc3\xbc\xc3\x96\xc3\x9f", 26, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xfe\x3a\x25\xcb\x78\xef\xe1\x21", 8, 1024, 16, "\x2a\xb0\x53\x08\xf3\x2f\xd4\x6e\xeb\x01\x49\x5d\x87\xf6\x27\xf6" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x04\x97\xd0\x02\x6a\x44\x2d\xde", 8, 1024, 16, "\x57\xf5\x70\x41\xa0\x9b\x8c\x09\xca\x74\xa9\x22\xa5\x82\x2d\x17" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xdd\xf3\x31\x7c\xce\xf4\x81\x26", 8, 10240, 16, "\xc3\xdd\x01\x6d\xaf\xf6\x58\xc8\xd7\x79\xb4\x40\x00\xb5\xe8\x0b" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x95\xd6\x72\x4e\xfb\xe1\xc3\x1a", 8, 102400, 16, "\xf2\x3f\x36\x7f\xb4\x6a\xd0\x3a\x31\x9e\x65\x11\x8e\x2b\x99\x9b" }, { "\x61", 1, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x6d\x69\x15\x18\xe4\x13\x42\x82", 8, 1024, 24, "\x28\x0c\x7e\xf2\x31\xf6\x1c\x6b\x5c\xef\x6a\xd5\x22\x64\x97\x91" "\xe3\x5e\x37\xfd\x50\xe2\xfc\x6c" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x9b\x76\x5e\x81\xde\x13\xdf\x15", 8, 1024, 24, "\x91\x1b\xa1\xc1\x7b\x4f\xc3\xb1\x80\x61\x26\x08\xbe\x53\xe6\x50" "\x40\x6f\x28\xed\xc6\xe6\x67\x55" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x7a\xac\xcc\x6e\x15\x56\xbd\xa1", 8, 1024, 24, "\xfa\x7e\x20\x07\xb6\x47\xb0\x09\x46\xb8\x38\xfb\xa1\xaf\xf7\x75" "\x2a\xfa\x77\x14\x06\x54\xcb\x34" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x1c\x68\xf8\xfb\x98\xf7\x8c\x39", 8, 1024, 24, "\xcb\x1e\x86\xf5\xe0\xe4\xfb\xbf\x71\x34\x99\x24\xf4\x39\x8c\xc2" "\x8e\x25\x1c\x4c\x96\x47\x22\xe8" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x10\xa9\x4e\xc1\xa5\xec\x17\x52", 8, 1024, 24, "\x0f\x83\xa2\x77\x92\xbb\xe4\x58\x68\xc5\xf2\x14\x6e\x6e\x2e\x6b" "\x98\x17\x70\x92\x07\x44\xe0\x51" }, { "\x61", 1, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xef\x8f\x37\x61\x8f\xab\xae\x4f", 8, 1024, 32, "\x6d\x65\xae\x86\x23\x91\x39\x98\xec\x1c\x23\x44\xb6\x0d\xad\x32" "\x54\x46\xc7\x23\x26\xbb\xdf\x4b\x54\x6e\xd4\xc2\xfa\xc6\x17\x17" }, { "\x61\x62\x63\x64\x65\x66\x67", 7, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xaa\xfb\xd9\x06\x7d\x7c\x40\xaf", 8, 1024, 32, "\x7d\x10\x54\x13\x3c\x43\x7a\xb3\x54\x1f\x38\xd4\x8f\x70\x0a\x09" "\xe2\xfa\xab\x97\x9a\x70\x16\xef\x66\x68\xca\x34\x2e\xce\xfa\x1f" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x58\x03\x4f\x56\x8b\x97\xd4\x98", 8, 1024, 32, "\xf7\x40\xb1\x25\x86\x0d\x35\x8f\x9f\x91\x2d\xce\x04\xee\x5a\x04" "\x9d\xbd\x44\x23\x4c\xa6\xbb\xab\xb0\xd0\x56\x82\xa9\xda\x47\x16" }, { "\x61\x62\x63\x64\x65\x66\x67\x68\x69", 9, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\x5d\x41\x3d\xa3\xa7\xfc\x5d\x0c", 8, 1024, 32, "\x4c\x7a\x86\xed\x81\x8a\x94\x99\x7d\x4a\xc4\xf7\x1c\xf8\x08\xdb" "\x09\x35\xd9\xa3\x2d\x22\xde\x32\x2d\x74\x38\xe5\xc8\xf2\x50\x6e" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA1, "\xca\xa7\xdc\x59\xce\x31\xe7\x49", 8, 1024, 32, "\x67\xe9\xd6\x29\x49\x1c\xb6\xa0\x85\xe8\xf9\x8b\x85\x47\x3a\x7e" "\xa7\xee\x89\x52\x6f\x19\x00\x53\x93\x07\x0a\x8b\xb9\xa8\x86\x94" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SIMPLE_S2K, GCRY_MD_SHA256, NULL, 0, 0, 16, "\x88\x36\x78\x6b\xd9\x5a\x62\xff\x47\xd3\xfb\x79\xc9\x08\x70\x56" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_SALTED_S2K, GCRY_MD_SHA256, "\x05\x8b\xfe\x31\xaa\xf3\x29\x11", 8, 0, 16, "\xb2\x42\xfe\x5e\x09\x02\xd9\x62\xb9\x35\xf3\xa8\x43\x80\x9f\xb1" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA256, "\xd3\x4a\xea\xc9\x97\x1b\xcc\x83", 8, 1024, 16, "\x35\x37\x99\x62\x07\x26\x68\x23\x05\x47\xb2\xa0\x0b\x2b\x2b\x8d" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA256, "\x5e\x71\xbd\x00\x5f\x96\xc4\x23", 8, 10240, 16, "\xa1\x6a\xee\xba\xde\x73\x25\x25\xd1\xab\xa0\xc5\x7e\xc6\x39\xa7" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA384, "\xc3\x08\xeb\x17\x62\x08\x89\xef", 8, 1024, 16, "\x9b\x7f\x0c\x81\x6f\x71\x59\x9b\xd5\xf6\xbf\x3a\x86\x20\x16\x33" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA512, "\xe6\x7d\x13\x6b\x39\xe3\x44\x05", 8, 1024, 16, "\xc8\xcd\x4b\xa4\xf3\xf1\xd5\xb0\x59\x06\xf0\xbb\x89\x34\x6a\xad" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA512, "\xed\x7d\x30\x47\xe4\xc3\xf8\xb6", 8, 1024, 32, "\x89\x7a\xef\x70\x97\xe7\x10\xdb\x75\xcc\x20\x22\xab\x7b\xf3\x05" "\x4b\xb6\x2e\x17\x11\x9f\xd6\xeb\xbf\xdf\x4d\x70\x59\xf0\xf9\xe5" }, { "\x4c\x6f\x6e\x67\x5f\x73\x65\x6e\x74\x65\x6e\x63\x65\x5f\x75\x73" "\x65\x64\x5f\x61\x73\x5f\x70\x61\x73\x73\x70\x68\x72\x61\x73\x65", 32, GCRY_KDF_ITERSALTED_S2K, GCRY_MD_SHA512, "\xbb\x1a\x45\x30\x68\x62\x6d\x63", 8, 1024, 24, "\xde\x5c\xb8\xd5\x75\xf6\xad\x69\x5b\xc9\xf6\x2f\xba\xeb\xfb\x36" "\x34\xf2\xb8\xee\x3b\x37\x21\xb7" } }; int tvidx; gpg_error_t err; unsigned char outbuf[32]; int i; for (tvidx=0; tvidx < DIM(tv); tvidx++) { if (tv[tvidx].disabled) continue; /* MD5 isn't supported in fips mode */ if (gcry_fips_mode_active() && tv[tvidx].hashalgo == GCRY_MD_MD5) continue; if (verbose) fprintf (stderr, "checking S2K test vector %d\n", tvidx); assert (tv[tvidx].dklen <= sizeof outbuf); err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, tv[tvidx].algo, tv[tvidx].hashalgo, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].c, tv[tvidx].dklen, outbuf); if (err) fail ("s2k test %d failed: %s\n", tvidx, gpg_strerror (err)); else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) { fail ("s2k test %d failed: mismatch\n", tvidx); fputs ("got:", stderr); for (i=0; i < tv[tvidx].dklen; i++) fprintf (stderr, " %02x", outbuf[i]); putc ('\n', stderr); } } }
/* Try to connect to the dirmngr via socket or fork it off and work by pipes. Handle the server's initial greeting */ static assuan_context_t start_dirmngr (int only_daemon) { int rc; char *infostr, *p; assuan_context_t ctx; int try_default = 0; infostr = opt.force_pipe_server? NULL : getenv ("DIRMNGR_INFO"); if (only_daemon && (!infostr || !*infostr)) { infostr = xstrdup (dirmngr_socket_name ()); try_default = 1; } rc = assuan_new (&ctx); if (rc) { log_error (_("failed to allocate assuan context: %s\n"), gpg_strerror (rc)); return NULL; } if (!infostr || !*infostr) { const char *pgmname; const char *argv[3]; int no_close_list[3]; int i; if (only_daemon) { log_error (_("apparently no running dirmngr\n")); return NULL; } if (opt.verbose) log_info (_("no running dirmngr - starting one\n")); if (!opt.dirmngr_program || !*opt.dirmngr_program) opt.dirmngr_program = "./dirmngr"; if ( !(pgmname = strrchr (opt.dirmngr_program, '/'))) pgmname = opt.dirmngr_program; else pgmname++; argv[0] = pgmname; argv[1] = "--server"; argv[2] = NULL; i=0; if (log_get_fd () != -1) no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr)); no_close_list[i] = -1; /* Connect to the agent and perform initial handshaking. */ rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv, no_close_list, NULL, NULL, 0); } else /* Connect to a daemon. */ { int prot; int pid; infostr = xstrdup (infostr); if (!try_default && *infostr) { if ( !(p = strchr (infostr, ':')) || p == infostr) { log_error (_("malformed DIRMNGR_INFO environment variable\n")); xfree (infostr); if (only_daemon) return NULL; /* Try again by starting a new instance. */ opt.force_pipe_server = 1; return start_dirmngr (0); } *p++ = 0; pid = atoi (p); while (*p && *p != ':') p++; prot = *p? atoi (p+1) : 0; if (prot != 1) { log_error (_("dirmngr protocol version %d is not supported\n"), prot); xfree (infostr); if (only_daemon) return NULL; opt.force_pipe_server = 1; return start_dirmngr (0); } } else pid = -1; rc = assuan_socket_connect (ctx, infostr, pid, 0); xfree (infostr); if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon) { log_error (_("can't connect to the dirmngr - trying fall back\n")); opt.force_pipe_server = 1; return start_dirmngr (0); } } if (rc) { assuan_release (ctx); log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (rc)); return NULL; } return ctx; }
static void check_pbkdf2 (void) { /* Test vectors are from RFC-6070. */ static struct { const char *p; /* Passphrase. */ size_t plen; /* Length of P. */ const char *salt; size_t saltlen; int hashalgo; unsigned long c; /* Iterations. */ int dklen; /* Requested key length. */ const char *dk; /* Derived key. */ int disabled; } tv[] = { { "password", 8, "salt", 4, GCRY_MD_SHA1, 1, 20, "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9" "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6" }, { "password", 8, "salt", 4, GCRY_MD_SHA1, 2, 20, "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e" "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57" }, { "password", 8, "salt", 4, GCRY_MD_SHA1, 4096, 20, "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad" "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1" }, { "password", 8, "salt", 4, GCRY_MD_SHA1, 16777216, 20, "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94" "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", 1 /* This test takes too long. */ }, { "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, GCRY_MD_SHA1, 4096, 25, "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" "\x4c\xf2\xf0\x70\x38" }, { "pass\0word", 9, "sa\0lt", 5, GCRY_MD_SHA1, 4096, 16, "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37" "\xd7\xf0\x34\x25\xe0\xc3" }, { /* empty password test, not in RFC-6070 */ "", 0, "salt", 4, GCRY_MD_SHA1, 2, 20, "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97" }, { "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 1, 32, "\x73\x14\xe7\xc0\x4f\xb2\xe6\x62\xc5\x43\x67\x42\x53\xf6\x8b\xd0" "\xb7\x34\x45\xd0\x7f\x24\x1b\xed\x87\x28\x82\xda\x21\x66\x2d\x58" }, { "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 2, 32, "\x99\x0d\xfa\x2b\xd9\x65\x63\x9b\xa4\x8b\x07\xb7\x92\x77\x5d\xf7" "\x9f\x2d\xb3\x4f\xef\x25\xf2\x74\x37\x88\x72\xfe\xd7\xed\x1b\xb3" }, { "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 4096, 32, "\x1f\x18\x29\xa9\x4b\xdf\xf5\xbe\x10\xd0\xae\xb3\x6a\xf4\x98\xe7" "\xa9\x74\x67\xf3\xb3\x11\x16\xa5\xa7\xc1\xaf\xff\x9d\xea\xda\xfe" }, /* { -- takes too long (4-5 min) to calculate "password", 8, "salt", 4, GCRY_MD_GOSTR3411_CP, 16777216, 32, "\xa5\x7a\xe5\xa6\x08\x83\x96\xd1\x20\x85\x0c\x5c\x09\xde\x0a\x52" "\x51\x00\x93\x8a\x59\xb1\xb5\xc3\xf7\x81\x09\x10\xd0\x5f\xcd\x97" }, */ { "passwordPASSWORDpassword", 24, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, GCRY_MD_GOSTR3411_CP, 4096, 40, "\x78\x83\x58\xc6\x9c\xb2\xdb\xe2\x51\xa7\xbb\x17\xd5\xf4\x24\x1f" "\x26\x5a\x79\x2a\x35\xbe\xcd\xe8\xd5\x6f\x32\x6b\x49\xc8\x50\x47" "\xb7\x63\x8a\xcb\x47\x64\xb1\xfd" }, { "pass\0word", 9, "sa\0lt", 5, GCRY_MD_GOSTR3411_CP, 4096, 20, "\x43\xe0\x6c\x55\x90\xb0\x8c\x02\x25\x24" "\x23\x73\x12\x7e\xdf\x9c\x8e\x9c\x32\x91" } }; int tvidx; gpg_error_t err; unsigned char outbuf[40]; int i; for (tvidx=0; tvidx < DIM(tv); tvidx++) { if (tv[tvidx].disabled) continue; if (verbose) fprintf (stderr, "checking PBKDF2 test vector %d algo %d\n", tvidx, tv[tvidx].hashalgo); assert (tv[tvidx].dklen <= sizeof outbuf); err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, GCRY_KDF_PBKDF2, tv[tvidx].hashalgo, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].c, tv[tvidx].dklen, outbuf); if (err) fail ("pbkdf2 test %d failed: %s\n", tvidx, gpg_strerror (err)); else if (memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) { fail ("pbkdf2 test %d failed: mismatch\n", tvidx); fputs ("got:", stderr); for (i=0; i < tv[tvidx].dklen; i++) fprintf (stderr, " %02x", outbuf[i]); putc ('\n', stderr); } } }
/* Read the key identified by GRIP from the private key directory and return it as an gcrypt S-expression object in RESULT. On failure returns an error code and stores NULL at RESULT. */ static gpg_error_t read_key_file (const unsigned char *grip, gcry_sexp_t *result) { int rc; char *fname; estream_t fp; struct stat st; unsigned char *buf; size_t buflen, erroff; gcry_sexp_t s_skey; char hexgrip[40+4+1]; *result = NULL; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); fp = es_fopen (fname, "rb"); if (!fp) { rc = gpg_error_from_syserror (); if (gpg_err_code (rc) != GPG_ERR_ENOENT) log_error ("can't open '%s': %s\n", fname, strerror (errno)); xfree (fname); return rc; } if (fstat (es_fileno (fp), &st)) { rc = gpg_error_from_syserror (); log_error ("can't stat '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } buflen = st.st_size; buf = xtrymalloc (buflen+1); if (!buf) { rc = gpg_error_from_syserror (); log_error ("error allocating %zu bytes for '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } if (es_fread (buf, buflen, 1, fp) != 1) { rc = gpg_error_from_syserror (); log_error ("error reading %zu bytes from '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } /* Convert the file into a gcrypt S-expression object. */ rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); xfree (fname); es_fclose (fp); xfree (buf); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); return rc; } *result = s_skey; return 0; }
static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_g13_t g13 = (engine_g13_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (g13->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (g13->assuan_ctx, "BYE"); */ TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: error reading assuan line: %s", fd, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: ERR line: %s", fd, err ? gpg_strerror (err) : "ok"); /* Command execution errors are not fatal, as we use a session based protocol. */ data->op_err = err; /* The caller will do the rest (namely, call cancel_op, which closes status_fd). */ return 0; } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: OK line", fd); _gpgme_io_close (g13->status_cb.fd); return 0; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ') { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst = src; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; if (linelen && g13->user.data_cb) err = g13->user.data_cb (g13->user.data_cb_value, src, linelen); else err = 0; TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: D inlinedata; status from cb: %s", fd, (g13->user.data_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *src; char *args; src = line + 2; while (*src == ' ') src++; args = strchr (line + 2, ' '); if (!args) args = line + linelen; /* set to an empty string */ else *(args++) = 0; while (*args == ' ') args++; if (g13->user.status_cb) err = g13->user.status_cb (g13->user.status_cb_value, src, args); else err = 0; TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: S line (%s) - status from cb: %s", fd, line+2, (g13->user.status_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *src; char *args; for (src=line+7; *src == ' '; src++) ; args = strchr (src, ' '); if (!args) args = line + linelen; /* Let it point to an empty string. */ else *(args++) = 0; while (*args == ' ') args++; err = default_inq_cb (g13, src, args); if (!err) { /* Flush and send END. */ err = assuan_send_data (g13->assuan_ctx, NULL, 0); } else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED) { /* Flush and send CANcel. */ err = assuan_send_data (g13->assuan_ctx, NULL, 1); } assuan_write_line (g13->assuan_ctx, "END"); } } while (!err && assuan_pending_line (g13->assuan_ctx)); return err; }
/* The thread started by start_writer. */ static void * writer_thread_main (void *arg) { gpg_error_t err = 0; struct writer_thread_parms *parm = arg; char _buffer[4096]; char *buffer; size_t length; if (parm->stream) { buffer = _buffer; err = es_read (parm->stream, buffer, sizeof _buffer, &length); if (err) { log_error ("reading stream failed: %s\n", gpg_strerror (err)); goto leave; } } else { buffer = (char *) parm->data; length = parm->datalen; } while (length) { ssize_t nwritten; nwritten = npth_write (parm->fd, buffer, length < 4096? length:4096); if (nwritten < 0) { if (errno == EINTR) continue; err = my_error_from_syserror (); break; /* Write error. */ } length -= nwritten; if (parm->stream) { if (length == 0) { err = es_read (parm->stream, buffer, sizeof _buffer, &length); if (err) { log_error ("reading stream failed: %s\n", gpg_strerror (err)); break; } if (length == 0) /* We're done. */ break; } } else buffer += nwritten; } leave: *parm->err_addr = err; if (close (parm->fd)) log_error ("closing writer fd %d failed: %s\n", parm->fd, strerror (errno)); xfree (parm); return NULL; }
/* This is the same as basic_ec_math but uses more advanced features. */ static void basic_ec_math_simplified (void) { gpg_error_t err; gcry_ctx_t ctx; gcry_mpi_point_t G, Q; gcry_mpi_t d; gcry_mpi_t x, y, z; gcry_sexp_t sexp; wherestr = "basic_ec_math_simplified"; info ("checking basic math functions for EC (variant)\n"); d = hex2mpi ("D4EF27E32F8AD8E2A1C6DDEBB1D235A69E3CEF9BCE90273D"); Q = gcry_mpi_point_new (0); err = gcry_mpi_ec_new (&ctx, NULL, "NIST P-192"); if (err) die ("gcry_mpi_ec_new failed: %s\n", gpg_strerror (err)); G = gcry_mpi_ec_get_point ("g", ctx, 1); if (!G) die ("gcry_mpi_ec_get_point(G) failed\n"); gcry_mpi_ec_mul (Q, d, G, ctx); x = gcry_mpi_new (0); y = gcry_mpi_new (0); z = gcry_mpi_new (0); gcry_mpi_point_get (x, y, z, Q); if (cmp_mpihex (x, "222D9EC717C89D047E0898C9185B033CD11C0A981EE6DC66") || cmp_mpihex (y, "605DE0A82D70D3E0F84A127D0739ED33D657DF0D054BFDE8") || cmp_mpihex (z, "00B06B519071BC536999AC8F2D3934B3C1FC9EACCD0A31F88F")) fail ("computed public key does not match\n"); if (debug) { print_mpi ("Q.x", x); print_mpi ("Q.y", y); print_mpi ("Q.z", z); } if (gcry_mpi_ec_get_affine (x, y, Q, ctx)) fail ("failed to get affine coordinates\n"); if (cmp_mpihex (x, "008532093BA023F4D55C0424FA3AF9367E05F309DC34CDC3FE") || cmp_mpihex (y, "00C13CA9E617C6C8487BFF6A726E3C4F277913D97117939966")) fail ("computed affine coordinates of public key do not match\n"); if (debug) { print_mpi ("q.x", x); print_mpi ("q.y", y); } gcry_mpi_release (z); gcry_mpi_release (y); gcry_mpi_release (x); /* Let us also check whether we can update the context. */ err = gcry_mpi_ec_set_point ("g", G, ctx); if (err) die ("gcry_mpi_ec_set_point(G) failed\n"); err = gcry_mpi_ec_set_mpi ("d", d, ctx); if (err) die ("gcry_mpi_ec_set_mpi(d) failed\n"); /* FIXME: Below we need to check that the returned S-expression is as requested. For now we use manual inspection using --debug. */ /* Does get_sexp return the private key? */ err = gcry_pubkey_get_sexp (&sexp, 0, ctx); if (err) fail ("gcry_pubkey_get_sexp(0) failed: %s\n", gpg_strerror (err)); else if (debug) print_sexp ("Result of gcry_pubkey_get_sexp (0):\n", sexp); gcry_sexp_release (sexp); /* Does get_sexp return the public key if requested? */ err = gcry_pubkey_get_sexp (&sexp, GCRY_PK_GET_PUBKEY, ctx); if (err) fail ("gcry_pubkey_get_sexp(GET_PUBKEY) failed: %s\n", gpg_strerror (err)); else if (debug) print_sexp ("Result of gcry_pubkey_get_sexp (GET_PUBKEY):\n", sexp); gcry_sexp_release (sexp); /* Does get_sexp return the public key after d has been deleted? */ err = gcry_mpi_ec_set_mpi ("d", NULL, ctx); if (err) die ("gcry_mpi_ec_set_mpi(d=NULL) failed\n"); err = gcry_pubkey_get_sexp (&sexp, 0, ctx); if (err) fail ("gcry_pubkey_get_sexp(0 w/o d) failed: %s\n", gpg_strerror (err)); else if (debug) print_sexp ("Result of gcry_pubkey_get_sexp (0 w/o d):\n", sexp); gcry_sexp_release (sexp); /* Does get_sexp return an error after d has been deleted? */ err = gcry_pubkey_get_sexp (&sexp, GCRY_PK_GET_SECKEY, ctx); if (gpg_err_code (err) != GPG_ERR_NO_SECKEY) fail ("gcry_pubkey_get_sexp(GET_SECKEY) returned wrong error: %s\n", gpg_strerror (err)); gcry_sexp_release (sexp); /* Does get_sexp return an error after d and Q have been deleted? */ err = gcry_mpi_ec_set_point ("q", NULL, ctx); if (err) die ("gcry_mpi_ec_set_point(q=NULL) failed\n"); err = gcry_pubkey_get_sexp (&sexp, 0, ctx); if (gpg_err_code (err) != GPG_ERR_BAD_CRYPT_CTX) fail ("gcry_pubkey_get_sexp(0 w/o Q,d) returned wrong error: %s\n", gpg_strerror (err)); gcry_sexp_release (sexp); gcry_mpi_point_release (Q); gcry_mpi_release (d); gcry_mpi_point_release (G); gcry_ctx_release (ctx); }