/* This function gets the signature parameters and encodes * them into a way for _gnutls_pk_verify to use. */ static cdk_error_t sig_to_datum (gnutls_datum_t * r_sig, cdk_pkt_signature_t sig) { int err; cdk_error_t rc; if (!r_sig || !sig) return CDK_Inv_Value; rc = 0; if (is_RSA (sig->pubkey_algo)) { err = _gnutls_mpi_dprint (sig->mpi[0], r_sig); if (err < 0) rc = map_gnutls_error (err); } else if (is_DSA (sig->pubkey_algo)) { err = _gnutls_encode_ber_rs (r_sig, sig->mpi[0], sig->mpi[1]); if (err < 0) rc = map_gnutls_error (err); } else rc = CDK_Inv_Algo; return rc; }
/* Hash all multi precision integers of the key PK with the given message digest context MD. */ static int hash_mpibuf (cdk_pubkey_t pk, digest_hd_st * md, int usefpr) { byte buf[MAX_MPI_BYTES]; /* FIXME: do not use hardcoded length. */ size_t nbytes; size_t i, npkey; int err; /* We have to differ between two modes for v3 keys. To form the fingerprint, we hash the MPI values without the length prefix. But if we calculate the hash for verifying/signing we use all data. */ npkey = cdk_pk_get_npkey (pk->pubkey_algo); for (i = 0; i < npkey; i++) { nbytes = MAX_MPI_BYTES; err = _gnutls_mpi_print_pgp (pk->mpi[i], buf, &nbytes); if (err < 0) return map_gnutls_error (err); if (!usefpr || pk->version == 4) _gnutls_hash (md, buf, nbytes); else /* without the prefix. */ _gnutls_hash (md, buf + 2, nbytes - 2); } return 0; }
/** * cdk_pk_get_fingerprint: * @pk: the public key * @fpr: the buffer to hold the fingerprint * * Return the fingerprint of the given public key. * The buffer must be at least 20 octets. * This function should be considered deprecated and * the new cdk_pk_to_fingerprint() should be used whenever * possible to avoid overflows. **/ cdk_error_t cdk_pk_get_fingerprint (cdk_pubkey_t pk, byte * fpr) { digest_hd_st hd; int md_algo; int dlen = 0; int err; if (!pk || !fpr) return CDK_Inv_Value; if (pk->version < 4 && is_RSA (pk->pubkey_algo)) md_algo = GNUTLS_DIG_MD5; /* special */ else md_algo = GNUTLS_DIG_SHA1; dlen = _gnutls_hash_get_algo_len (md_algo); err = _gnutls_hash_init (&hd, md_algo); if (err < 0) { gnutls_assert (); return map_gnutls_error (err); } _cdk_hash_pubkey (pk, &hd, 1); _gnutls_hash_deinit (&hd, fpr); if (dlen == 16) memset (fpr + 16, 0, 4); return 0; }
/* You can use a NULL buf to get the output size only */ static cdk_error_t mpi_to_buffer (bigint_t a, byte * buf, size_t buflen, size_t * r_nwritten, size_t * r_nbits) { size_t nbits; int err; if (!a || !r_nwritten) { gnutls_assert (); return CDK_Inv_Value; } nbits = _gnutls_mpi_get_nbits (a); if (r_nbits) *r_nbits = nbits; if (r_nwritten) *r_nwritten = (nbits + 7) / 8 + 2; if ((nbits + 7) / 8 + 2 > buflen) return CDK_Too_Short; *r_nwritten = buflen; err = _gnutls_mpi_print (a, buf, r_nwritten); if (err < 0) { gnutls_assert (); return map_gnutls_error (err); } return 0; }
static int write_mpi (cdk_stream_t out, bigint_t m) { byte buf[MAX_MPI_BYTES + 2]; size_t nbits, nread; int err; if (!out || !m) return CDK_Inv_Value; nbits = _gnutls_mpi_get_nbits (m); if (nbits > MAX_MPI_BITS || nbits < 1) return CDK_MPI_Error; nread = MAX_MPI_BYTES + 2; err = _gnutls_mpi_print_pgp (m, buf, &nread); if (err < 0) return map_gnutls_error (err); return stream_write (out, buf, nread); }
static cdk_error_t read_mpi (cdk_stream_t inp, bigint_t * ret_m, int secure) { bigint_t m; int err; byte buf[MAX_MPI_BYTES + 2]; size_t nread, nbits; cdk_error_t rc; if (!inp || !ret_m) return CDK_Inv_Value; *ret_m = NULL; nbits = read_16 (inp); nread = (nbits + 7) / 8; if (nbits > MAX_MPI_BITS || nbits == 0) { _cdk_log_debug ("read_mpi: too large %d bits\n", nbits); return CDK_MPI_Error; /* Sanity check */ } rc = stream_read (inp, buf + 2, nread, &nread); if (!rc && nread != ((nbits + 7) / 8)) { _cdk_log_debug ("read_mpi: too short %d < %d\n", nread, (nbits + 7) / 8); return CDK_MPI_Error; } buf[0] = nbits >> 8; buf[1] = nbits >> 0; nread += 2; err = _gnutls_mpi_scan_pgp (&m, buf, nread); if (err < 0) return map_gnutls_error (err); *ret_m = m; return rc; }
static cdk_error_t hash_encode (void *data, FILE * in, FILE * out) { md_filter_t *mfx = data; byte buf[BUFSIZE]; int err; int nread; if (!mfx) { gnutls_assert (); return CDK_Inv_Value; } _cdk_log_debug ("hash filter: encode algo=%d\n", mfx->digest_algo); if (!mfx->md_initialized) { err = _gnutls_hash_init (&mfx->md, mfx->digest_algo); if (err < 0) { gnutls_assert (); return map_gnutls_error (err); } mfx->md_initialized = 1; } while (!feof (in)) { nread = fread (buf, 1, BUFSIZE, in); if (!nread) break; _gnutls_hash (&mfx->md, buf, nread); } wipemem (buf, sizeof (buf)); return 0; }
/** * cdk_pk_verify: * @pk: the public key * @sig: signature * @md: the message digest * * Verify the signature in @sig and compare it with the message digest in @md. **/ cdk_error_t cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md) { gnutls_datum_t s_sig; byte *encmd = NULL; size_t enclen; cdk_error_t rc; int ret, algo; unsigned int i; gnutls_datum_t data; gnutls_pk_params_st params; if (!pk || !sig || !md) { gnutls_assert (); return CDK_Inv_Value; } if (is_DSA (pk->pubkey_algo)) algo = GNUTLS_PK_DSA; else if (is_RSA (pk->pubkey_algo)) algo = GNUTLS_PK_RSA; else { gnutls_assert (); return CDK_Inv_Value; } rc = sig_to_datum (&s_sig, sig); if (rc) { gnutls_assert (); goto leave; } rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, pk->pubkey_algo, md, sig->digest_algo, cdk_pk_get_nbits (pk)); if (rc) { gnutls_assert (); goto leave; } data.data = encmd; data.size = enclen; params.params_nr = cdk_pk_get_npkey (pk->pubkey_algo); for (i = 0; i < params.params_nr; i++) params.params[i] = pk->mpi[i]; params.flags = 0; ret = _gnutls_pk_verify (algo, &data, &s_sig, ¶ms); if (ret < 0) { gnutls_assert (); rc = map_gnutls_error (ret); goto leave; } rc = 0; leave: _gnutls_free_datum (&s_sig); cdk_free (encmd); return rc; }
static cdk_error_t file_verify_clearsign (cdk_ctx_t hd, const char *file, const char *output) { cdk_stream_t inp = NULL, out = NULL, tmp = NULL; digest_hd_st md; char buf[512], chk[512]; const char *s; int i, is_signed = 0, nbytes; int digest_algo = 0; int err; cdk_error_t rc; memset(&md, 0, sizeof(md)); if (output) { rc = cdk_stream_create (output, &out); if (rc) return rc; } rc = cdk_stream_open (file, &inp); if (rc) { if (output) cdk_stream_close (out); return rc; } s = "-----BEGIN PGP SIGNED MESSAGE-----"; while (!cdk_stream_eof (inp)) { nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); if (!nbytes || nbytes == -1) break; if (!strncmp (buf, s, strlen (s))) { is_signed = 1; break; } } if (cdk_stream_eof (inp) && !is_signed) { rc = CDK_Armor_Error; goto leave; } while (!cdk_stream_eof (inp)) { nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); if (!nbytes || nbytes == -1) break; if (nbytes == 1) /* Empty line */ break; else if (!strncmp (buf, "Hash: ", 6)) { for (i = 0; digest_table[i].name; i++) { if (!strcmp (buf + 6, digest_table[i].name)) { digest_algo = digest_table[i].algo; break; } } } } if (digest_algo && _gnutls_hash_get_algo_len (digest_algo) <= 0) { rc = CDK_Inv_Algo; goto leave; } if (!digest_algo) digest_algo = GNUTLS_DIG_MD5; err = _gnutls_hash_init (&md, digest_algo); if (err < 0) { rc = map_gnutls_error (err); goto leave; } s = "-----BEGIN PGP SIGNATURE-----"; while (!cdk_stream_eof (inp)) { nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); if (!nbytes || nbytes == -1) break; if (!strncmp (buf, s, strlen (s))) break; else { cdk_stream_peek (inp, (byte *) chk, DIM (chk) - 1); i = strncmp (chk, s, strlen (s)); if (strlen (buf) == 0 && i == 0) continue; /* skip last '\n' */ _cdk_trim_string (buf, i == 0 ? 0 : 1); _gnutls_hash (&md, buf, strlen (buf)); } if (!strncmp (buf, "- ", 2)) /* FIXME: handle it recursive. */ memmove (buf, buf + 2, nbytes - 2); if (out) { if (strstr (buf, "\r\n")) buf[strlen (buf) - 2] = '\0'; cdk_stream_write (out, buf, strlen (buf)); _cdk_stream_puts (out, _cdk_armor_get_lineend ()); } } /* We create a temporary stream object to store the signature data in there. */ rc = cdk_stream_tmp_new (&tmp); if (rc) goto leave; s = "-----BEGIN PGP SIGNATURE-----\n"; _cdk_stream_puts (tmp, s); while (!cdk_stream_eof (inp)) { nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); if (!nbytes || nbytes == -1) break; if (nbytes < (int) (DIM (buf) - 3)) { buf[nbytes - 1] = '\n'; buf[nbytes] = '\0'; } cdk_stream_write (tmp, buf, nbytes); } /* FIXME: This code is not very elegant. */ cdk_stream_tmp_set_mode (tmp, STREAMCTL_READ); cdk_stream_seek (tmp, 0); cdk_stream_set_armor_flag (tmp, 0); cdk_stream_read (tmp, NULL, 0); /* the digest handle will be closed there. */ rc = _cdk_proc_packets (hd, tmp, NULL, NULL, NULL, &md); leave: _gnutls_hash_deinit (&md, NULL); cdk_stream_close (out); cdk_stream_close (tmp); cdk_stream_close (inp); return rc; }
/** * cdk_pk_verify: * @pk: the public key * @sig: signature * @md: the message digest * * Verify the signature in @sig and compare it with the message digest in @md. **/ cdk_error_t cdk_pk_verify(cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md) { gnutls_datum_t s_sig = { NULL, 0 }, di = { NULL, 0}; byte *encmd = NULL; cdk_error_t rc; int ret, algo; unsigned int i; gnutls_pk_params_st params; const mac_entry_st *me; if (!pk || !sig || !md) { gnutls_assert(); return CDK_Inv_Value; } if (is_DSA(pk->pubkey_algo)) algo = GNUTLS_PK_DSA; else if (is_RSA(pk->pubkey_algo)) algo = GNUTLS_PK_RSA; else { gnutls_assert(); return CDK_Inv_Value; } rc = sig_to_datum(&s_sig, sig); if (rc) { gnutls_assert(); goto leave; } me = mac_to_entry(sig->digest_algo); rc = _gnutls_set_datum(&di, md, _gnutls_hash_get_algo_len(me)); if (rc < 0) { rc = gnutls_assert_val(CDK_Out_Of_Core); goto leave; } rc = pk_prepare_hash(algo, me, &di); if (rc < 0) { rc = gnutls_assert_val(CDK_General_Error); goto leave; } params.params_nr = cdk_pk_get_npkey(pk->pubkey_algo); for (i = 0; i < params.params_nr; i++) params.params[i] = pk->mpi[i]; params.flags = 0; ret = _gnutls_pk_verify(algo, &di, &s_sig, ¶ms); if (ret < 0) { gnutls_assert(); rc = map_gnutls_error(ret); goto leave; } rc = 0; leave: _gnutls_free_datum(&s_sig); _gnutls_free_datum(&di); cdk_free(encmd); return rc; }