bool Crypto::checkAlgorithms() { if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { m_errorStr = "GCRY_CIPHER_AES256 not found."; qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); return false; } if (gcry_cipher_algo_info(GCRY_CIPHER_TWOFISH, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { m_errorStr = "GCRY_CIPHER_TWOFISH not found."; qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); return false; } if (gcry_cipher_algo_info(GCRY_CIPHER_SALSA20, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { m_errorStr = "GCRY_CIPHER_SALSA20 not found."; qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); return false; } if (gcry_md_test_algo(GCRY_MD_SHA256) != 0) { m_errorStr = "GCRY_MD_SHA256 not found."; qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); return false; } return true; }
/* Put the DIGEST into an DER encoded container and return it in R_VAL. */ static int encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo, unsigned char **r_val, size_t *r_len) { unsigned char *frame; unsigned char asn[100]; size_t asnlen; *r_val = NULL; *r_len = 0; asnlen = DIM(asn); if (!algo || gcry_md_test_algo (algo)) return gpg_error (GPG_ERR_DIGEST_ALGO); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { log_error ("no object identifier for algo %d\n", algo); return gpg_error (GPG_ERR_INTERNAL); } frame = xtrymalloc (asnlen + digestlen); if (!frame) return out_of_core (); memcpy (frame, asn, asnlen); memcpy (frame+asnlen, digest, digestlen); if (DBG_CRYPTO) log_printhex ("encoded hash:", frame, asnlen+digestlen); *r_val = frame; *r_len = asnlen+digestlen; return 0; }
int init_hash(void) { /* * Version check should be the very first call because it makes sure * that important subsystems are intialized. */ if (!gcry_check_version(GCRYPT_VERSION)) return 1; /* Disable secure memory. */ gcry_control(GCRYCTL_DISABLE_SECMEM, 0); /* Tell Libgcrypt that initialization has completed. */ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); if (gcry_md_test_algo(HASH_FUNC)) return 1; digest_len = gcry_md_get_algo_dlen(HASH_FUNC); if (!digest_len) return 1; abort_on(digest_len == 0 || digest_len > DIGEST_LEN_MAX); return 0; }
void hash_bench (char **argv, int argc) { int i, algo; bench_print_section ("hash", "Hash"); bench_print_header (14, ""); if (argv && argc) { for (i = 0; i < argc; i++) { algo = gcry_md_map_name (argv[i]); if (algo) _hash_bench (algo); } } else { for (i = 1; i < 400; i++) if (!gcry_md_test_algo (i)) _hash_bench (i); } bench_print_footer (14); }
static int rpmcdsaAvailableDigest(pgpDig dig, int algo) { int rc = 0; /* assume available */ #ifdef REFERENCE rc = rpmgcAvailable(dig->impl, algo, (gcry_md_test_algo(algo) || algo == PGPHASHALGO_MD5)); #endif return rc; }
void kdf_bench (char **argv, int argc) { char algo_name[32]; int i, j; bench_print_section ("kdf", "KDF"); if (!csv_mode) { printf (" %-*s | ", 24, ""); printf ("%14s %13s\n", "nanosecs/iter", "cycles/iter"); } if (argv && argc) { for (i = 0; i < argc; i++) { for (j = 1; j < 400; j++) { if (gcry_md_test_algo (j)) continue; snprintf (algo_name, sizeof(algo_name), "PBKDF2-HMAC-%s", gcry_md_algo_name (j)); if (!strcmp(argv[i], algo_name)) kdf_bench_one (GCRY_KDF_PBKDF2, j); } } } else { for (i = 1; i < 400; i++) if (!gcry_md_test_algo (i)) kdf_bench_one (GCRY_KDF_PBKDF2, i); } bench_print_footer (24); }
/** * Proceed to a hash on a kbuffer, putting the result in the * hash kbuffer. */ void kmocrypt_hash(kbuffer * input, kbuffer * hash, int algo) { int digest_len; uint32_t input_size; /* Simple hashing, result is put directly in the buffer, no copy needed, no payment until 2099. */ assert(gcry_md_test_algo(algo) == 0); digest_len = gcry_md_get_algo_dlen(algo); /* Get the length and addresse of unread data from input, and read it to * the end */ input_size = input->len - input->pos; /* Hash the buffer. */ gcry_md_hash_buffer(algo, kbuffer_append_nbytes(hash, digest_len), kbuffer_read_nbytes(input, input_size), (size_t)input_size); }
/* This function recognizes the KSP content. * This function sets the KMO error string. It returns -1 on failure. */ int kmocrypt_recognize_ksp2(struct kmocrypt_signature2 *self, kbuffer *buffer) { uint32_t header_len = 27; uint32_t subpackets_len; if (buffer->len < header_len) { kmo_seterror("KSP header too short"); return -1; } /* MAGIC */ kbuffer_read32(buffer); /* MAJOR */ self->major= kbuffer_read32(buffer); /* MINOR */ self->minor = kbuffer_read32(buffer); /* MEMBER ID */ self->mid = kbuffer_read64(buffer); /* HASH ALGO */ self->hash_algo = kbuffer_read8(buffer); if (gcry_md_test_algo(self->hash_algo)) { kmo_seterror("unsupported hash algorithm"); return -1; } /* SIG ALGO */ self->sig_algo = kbuffer_read8(buffer); if (gcry_pk_test_algo(self->sig_algo)) { kmo_seterror("unsupported signature algorithm"); return -1; } /* PACKAGING TYPE */ self->pkg_type = kbuffer_read8(buffer); if (self->pkg_type >= KMO_P_NB_TYPE) { kmo_seterror("invalid signature packet type"); return -1; } /* SUBPACKETS LENGTH */ subpackets_len = kbuffer_read32(buffer); if (! subpackets_len) { kmo_seterror("no subpacket in KSP"); return -1; } if (buffer->len < header_len + subpackets_len) { kmo_seterror("KSP subpacket section is too short"); return -1; } /* Recognize the subpackets. */ if (recognize_subpackets(self, buffer, subpackets_len)) { return -1; } /* Recognize the signature of the KSP, unless it's the encryption key. */ if (self->mid && recognize_ksp_signature(self, buffer, buffer->len - buffer->pos)) { return -1; } return 0; }
static int xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, int *canceled ) { gpg_error_t err; byte *buffer; u16 csum=0; int i, res; size_t nbytes; if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ gcry_cipher_hd_t cipher_hd=NULL; PKT_secret_key *save_sk; if( sk->protect.s2k.mode == 1001 ) { log_info(_("secret key parts are not available\n")); return GPG_ERR_UNUSABLE_SECKEY; } if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); if( openpgp_cipher_test_algo( sk->protect.algo ) ) { log_info(_("protection algorithm %d%s is not supported\n"), sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" ); return GPG_ERR_CIPHER_ALGO; } if(gcry_md_test_algo (sk->protect.s2k.hash_algo)) { log_info(_("protection digest %d is not supported\n"), sk->protect.s2k.hash_algo); return GPG_ERR_DIGEST_ALGO; } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if (!sk->flags.primary) { keyid[2] = sk->main_keyid[0]; keyid[3] = sk->main_keyid[1]; } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, mode, tryagain_text, canceled ); if (!dek && canceled && *canceled) return GPG_ERR_CANCELED; err = openpgp_cipher_open (&cipher_hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | (sk->protect.algo >= 100 ? 0 : GCRY_CIPHER_ENABLE_SYNC))); if (err) log_fatal ("cipher open failed: %s\n", gpg_strerror (err) ); err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen); if (err) log_fatal ("set key failed: %s\n", gpg_strerror (err) ); xfree(dek); save_sk = copy_secret_key( NULL, sk ); gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen ); csum = 0; if( sk->version >= 4 ) { int ndata; unsigned int ndatabits; byte *p, *data; u16 csumc = 0; i = pubkey_get_npkey(sk->pubkey_algo); assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE )); p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits ); ndata = (ndatabits+7)/8; if ( ndata > 1 ) csumc = buf16_to_u16 (p+ndata-2); data = xmalloc_secure ( ndata ); gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata ); gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ; p = data; if (sk->protect.sha1chk) { /* This is the new SHA1 checksum method to detect tampering with the key as used by the Klima/Rosa attack */ sk->csum = 0; csum = 1; if( ndata < 20 ) log_error("not enough bytes for SHA-1 checksum\n"); else { gcry_md_hd_t h; if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1)) BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1), data + ndata - 20, 20) ) { /* Digest does match. We have to keep the old style checksum in sk->csum, so that the test used for unprotected keys does work. This test gets used when we are adding new keys. */ sk->csum = csum = checksum (data, ndata-20); } gcry_md_close (h); } } else { if( ndata < 2 ) { log_error("not enough bytes for checksum\n"); sk->csum = 0; csum = 1; } else { csum = checksum( data, ndata-2); sk->csum = data[ndata-2] << 8 | data[ndata-1]; if ( sk->csum != csum ) { /* This is a PGP 7.0.0 workaround */ sk->csum = csumc; /* take the encrypted one */ } } } /* Must check it here otherwise the mpi_read_xx would fail because the length may have an arbitrary value */ if( sk->csum == csum ) { for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, p, ndata, &nbytes)) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } ndata -= nbytes; p += nbytes; } /* Note: at this point ndata should be 2 for a simple checksum or 20 for the sha1 digest */ } xfree(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { byte *p; size_t ndata; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); ndata = (ndatabits+7)/8; assert (ndata >= 2); assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2); buffer = xmalloc_secure (ndata); gcry_cipher_sync (cipher_hd); buffer[0] = p[0]; buffer[1] = p[1]; gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2); csum += checksum (buffer, ndata); gcry_mpi_release (sk->skey[i]); err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, buffer, ndata, &ndata ); xfree (buffer); if (err) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } /* csum += checksum_mpi (sk->skey[i]); */ } } gcry_cipher_close ( cipher_hd ); /* Now let's see whether we have used the correct passphrase. */ if( csum != sk->csum ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } /* The checksum may fail, so we also check the key itself. */ res = pk_check_secret_key ( sk->pubkey_algo, sk->skey ); if( res ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } free_secret_key( save_sk ); sk->is_protected = 0; } else { /* not protected, assume it is okay if the checksum is okay */ csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) return GPG_ERR_CHECKSUM; } return 0; }
static void md_bench ( const char *algoname ) { int algo; gcry_md_hd_t hd; int i, j, repcount; char buf_base[1000+15]; size_t bufsize = 1000; char *buf; char *largebuf_base; char *largebuf; char digest[512/8]; gcry_error_t err = GPG_ERR_NO_ERROR; if (!algoname) { for (i=1; i < 400; i++) if (in_fips_mode && i == GCRY_MD_MD5) ; /* Don't use MD5 in fips mode. */ else if ( !gcry_md_test_algo (i) ) md_bench (gcry_md_algo_name (i)); return; } buf = buf_base + ((16 - ((size_t)buf_base & 0x0f)) % buffer_alignment); algo = gcry_md_map_name (algoname); if (!algo) { fprintf (stderr, PGM ": invalid hash algorithm `%s'\n", algoname); exit (1); } err = gcry_md_open (&hd, algo, 0); if (err) { fprintf (stderr, PGM ": error opening hash algorithm `%s'\n", algoname); exit (1); } for (i=0; i < bufsize; i++) buf[i] = i; printf ("%-12s", gcry_md_algo_name (algo)); start_timer (); for (repcount=0; repcount < hash_repetitions; repcount++) for (i=0; i < 1000; i++) gcry_md_write (hd, buf, bufsize); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); fflush (stdout); gcry_md_reset (hd); start_timer (); for (repcount=0; repcount < hash_repetitions; repcount++) for (i=0; i < 10000; i++) gcry_md_write (hd, buf, bufsize/10); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); fflush (stdout); gcry_md_reset (hd); start_timer (); for (repcount=0; repcount < hash_repetitions; repcount++) for (i=0; i < 1000000; i++) gcry_md_write (hd, buf, 1); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); fflush (stdout); start_timer (); for (repcount=0; repcount < hash_repetitions; repcount++) for (i=0; i < 1000; i++) for (j=0; j < bufsize; j++) gcry_md_putc (hd, buf[j]); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); fflush (stdout); gcry_md_close (hd); /* Now 100 hash operations on 10000 bytes using the fast function. We initialize the buffer so that all memory pages are committed and we have repeatable values. */ if (gcry_md_get_algo_dlen (algo) > sizeof digest) die ("digest buffer too short\n"); largebuf_base = malloc (10000+15); if (!largebuf_base) die ("out of core\n"); largebuf = (largebuf_base + ((16 - ((size_t)largebuf_base & 0x0f)) % buffer_alignment)); for (i=0; i < 10000; i++) largebuf[i] = i; start_timer (); for (repcount=0; repcount < hash_repetitions; repcount++) for (i=0; i < 100; i++) gcry_md_hash_buffer (algo, digest, largebuf, 10000); stop_timer (); printf (" %s", elapsed_time ()); free (largebuf_base); putchar ('\n'); fflush (stdout); }
// gcry_md_close(my_hash_hdl); gcry_md_write(my_hash_hdl,data_buf,data_len); gcry_md_putc(my_hash_hdl,c); gcry_md_final(my_hash_hdl); unsigned char *digest; digest = gcry_md_read(my_hash_hdl,0); // shortcut function: gcry_md_get_algo_dlen(the_hash_algo); // get required digest size unsigned char digest_buf[DIGEST_SIZE]; gcry_md_hash_buffer(the_hash_algo,digest_buf,inbuf,inlen); // to verify algo available for use: status = gcry_md_test_algo(the_hash_algo); #endif // ELSEWHERE #else /* ! HAVE_LIBGCRYPT */ /* dummy functions which allow the thing to link */ /* Linkage to libgcrypt */ size_t _decrypt_char_buf(QSP_ARG_DECL const uint8_t *in_buf, size_t in_len, char *out_buf, size_t out_len ) { warn("decrypt_char_buf: libgcrypt not present!?"); return 0; } size_t _encrypt_char_buf(QSP_ARG_DECL const char *in_buf, size_t in_len, uint8_t *out_buf, size_t out_len)
static void md_bench ( const char *algoname ) { int algo; gcry_md_hd_t hd; int i; char buf[1000]; gcry_error_t err = GPG_ERR_NO_ERROR; if (!algoname) { for (i=1; i < 400; i++) if ( !gcry_md_test_algo (i) ) md_bench (gcry_md_algo_name (i)); return; } algo = gcry_md_map_name (algoname); if (!algo) { fprintf (stderr, PGM ": invalid hash algorithm `%s'\n", algoname); exit (1); } err = gcry_md_open (&hd, algo, 0); if (err) { fprintf (stderr, PGM ": error opening hash algorithm `%s'\n", algoname); exit (1); } for (i=0; i < sizeof buf; i++) buf[i] = i; printf ("%-12s", gcry_md_algo_name (algo)); start_timer (); for (i=0; i < 1000; i++) gcry_md_write (hd, buf, sizeof buf); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); gcry_md_reset (hd); start_timer (); for (i=0; i < 10000; i++) gcry_md_write (hd, buf, sizeof buf/10); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); gcry_md_reset (hd); start_timer (); for (i=0; i < 1000000; i++) gcry_md_write (hd, "", 1); gcry_md_final (hd); stop_timer (); printf (" %s", elapsed_time ()); gcry_md_close (hd); putchar ('\n'); }
int main (int argc, char **argv) { int last_argc = -1; int gigs = 0; int algo = 0; int idx; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--help")) { fputs ("usage: " PGM " [options] [algos]\n" "Options:\n" " --verbose print timings etc.\n" " --debug flyswatter\n" " --gigs N Run a test on N GiB\n", stdout); exit (0); } else if (!strcmp (*argv, "--verbose")) { verbose++; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose += 2; debug++; argc--; argv++; } else if (!strcmp (*argv, "--gigs")) { argc--; argv++; if (argc) { gigs = atoi (*argv); argc--; argv++; } } else if (!strncmp (*argv, "--", 2)) die ("unknown option '%s'", *argv); } if (gigs < 0 || gigs > 1024*1024) die ("value for --gigs must be in the range 0 to %d", 1024*1024); gcry_control (GCRYCTL_DISABLE_SECMEM, 0); if (!gcry_check_version (GCRYPT_VERSION)) die ("version mismatch\n"); if (debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0); gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); /* A quick check that all given algorithms are valid. */ for (idx=0; idx < argc; idx++) { algo = gcry_md_map_name (argv[idx]); if (!algo) fail ("invalid algorithm '%s'", argv[idx]); } if (error_count) exit (1); /* Start checking. */ start_timer (); if (!argc) { for (algo=1; algo < 400; algo++) if (!gcry_md_test_algo (algo)) { if (!gigs) run_selftest (algo); else run_longtest (algo, gigs); } } else { for (idx=0; idx < argc; idx++) { algo = gcry_md_map_name (argv[idx]); if (!algo) die ("invalid algorithm '%s'", argv[idx]); if (!gigs) run_selftest (algo); else run_longtest (algo, gigs); } } stop_timer (); if (missing_test_vectors) fail ("Some test vectors are missing"); if (verbose) show ("All tests completed in %s. Errors: %d\n", elapsed_time (), error_count); return !!error_count; }
static int do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits, gcry_sexp_t pkey, gcry_mpi_t *r_val) { int n; size_t nframe; unsigned char *frame; if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA) { unsigned int qbits; if ( pkalgo == GCRY_PK_ECDSA ) qbits = gcry_pk_get_nbits (pkey); else qbits = get_dsa_qbits (pkey); if ( (qbits%8) ) { log_error(_("DSA requires the hash length to be a" " multiple of 8 bits\n")); return gpg_error (GPG_ERR_INTERNAL); } /* Don't allow any Q smaller than 160 bits. We don't want someone to issue signatures from a key with a 16-bit Q or something like that, which would look correct but allow trivial forgeries. Yes, I know this rules out using MD5 with DSA. ;) */ if (qbits < 160) { log_error (_("%s key uses an unsafe (%u bit) hash\n"), gcry_pk_algo_name (pkalgo), qbits); return gpg_error (GPG_ERR_INTERNAL); } /* Check if we're too short. Too long is safe as we'll automatically left-truncate. */ nframe = gcry_md_get_algo_dlen (algo); if (nframe < qbits/8) { log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), (unsigned int)nframe*8, gcry_pk_get_nbits (pkey), gcry_pk_algo_name (pkalgo)); /* FIXME: we need to check the requirements for ECDSA. */ if (nframe < 20 || pkalgo == GCRY_PK_DSA ) return gpg_error (GPG_ERR_INTERNAL); } frame = xtrymalloc (nframe); if (!frame) return out_of_core (); memcpy (frame, gcry_md_read (md, algo), nframe); n = nframe; /* Truncate. */ if (n > qbits/8) n = qbits/8; } else { int i; unsigned char asn[100]; size_t asnlen; size_t len; nframe = (nbits+7) / 8; asnlen = DIM(asn); if (!algo || gcry_md_test_algo (algo)) return gpg_error (GPG_ERR_DIGEST_ALGO); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { log_error ("no object identifier for algo %d\n", algo); return gpg_error (GPG_ERR_INTERNAL); } len = gcry_md_get_algo_dlen (algo); if ( len + asnlen + 4 > nframe ) { log_error ("can't encode a %d bit MD into a %d bits frame\n", (int)(len*8), (int)nbits); return gpg_error (GPG_ERR_INTERNAL); } /* We encode the MD in this way: * * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) * * PAD consists of FF bytes. */ frame = xtrymalloc (nframe); if (!frame) return out_of_core (); n = 0; frame[n++] = 0; frame[n++] = 1; /* block type */ i = nframe - len - asnlen -3 ; assert ( i > 1 ); memset ( frame+n, 0xff, i ); n += i; frame[n++] = 0; memcpy ( frame+n, asn, asnlen ); n += asnlen; memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len; assert ( n == nframe ); } if (DBG_CRYPTO) { int j; log_debug ("encoded hash:"); for (j=0; j < nframe; j++) log_printf (" %02X", frame[j]); log_printf ("\n"); } gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe); xfree (frame); return 0; }