/* Check a signature. * * Looks up the public key that created the signature (SIG->KEYID) * from the key db. Makes sure that the signature is valid (it was * not created prior to the key, the public key was created in the * past, and the signature does not include any unsupported critical * features), finishes computing the hash of the signature data, and * checks that the signature verifies the digest. If the key that * generated the signature is a subkey, this function also verifies * that there is a valid backsig from the subkey to the primary key. * Finally, if status fd is enabled and the signature class is 0x00 or * 0x01, then a STATUS_SIG_ID is emitted on the status fd. * * SIG is the signature to check. * * DIGEST contains a valid hash context that already includes the * signed data. This function adds the relevant meta-data from the * signature packet to compute the final hash. (See Section 5.2 of * RFC 4880: "The concatenation of the data being signed and the * signature data from the version number through the hashed subpacket * data (inclusive) is hashed.") * * If R_EXPIREDATE is not NULL, R_EXPIREDATE is set to the key's * expiry. * * If R_EXPIRED is not NULL, *R_EXPIRED is set to 1 if PK has expired * (0 otherwise). Note: PK being expired does not cause this function * to fail. * * If R_REVOKED is not NULL, *R_REVOKED is set to 1 if PK has been * revoked (0 otherwise). Note: PK being revoked does not cause this * function to fail. * * If R_PK is not NULL, the public key is stored at that address if it * was found; other wise NULL is stored. * * Returns 0 on success. An error code otherwise. */ gpg_error_t check_signature2 (ctrl_t ctrl, PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate, int *r_expired, int *r_revoked, PKT_public_key **r_pk) { int rc=0; PKT_public_key *pk; if (r_expiredate) *r_expiredate = 0; if (r_expired) *r_expired = 0; if (r_revoked) *r_revoked = 0; if (r_pk) *r_pk = NULL; pk = xtrycalloc (1, sizeof *pk); if (!pk) return gpg_error_from_syserror (); if ((rc=openpgp_md_test_algo(sig->digest_algo))) { /* We don't have this digest. */ } else if (!gnupg_digest_is_allowed (opt.compliance, 0, sig->digest_algo)) { /* Compliance failure. */ log_info (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (sig->digest_algo), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_DIGEST_ALGO); } else if ((rc=openpgp_pk_test_algo(sig->pubkey_algo))) { /* We don't have this pubkey algo. */ } else if (!gcry_md_is_enabled (digest,sig->digest_algo)) { /* Sanity check that the md has a context for the hash that the * sig is expecting. This can happen if a onepass sig header * does not match the actual sig, and also if the clearsign * "Hash:" header is missing or does not match the actual sig. */ log_info(_("WARNING: signature digest conflict in message\n")); rc = gpg_error (GPG_ERR_GENERAL); } else if (get_pubkey_for_sig (ctrl, pk, sig)) rc = gpg_error (GPG_ERR_NO_PUBKEY); else if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, pk->pubkey_algo, pk->pkey, nbits_from_pk (pk), NULL)) { /* Compliance failure. */ log_error (_("key %s may not be used for signing in %s mode\n"), keystr_from_pk (pk), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_PUBKEY_ALGO); } else if (!pk->flags.valid) { /* You cannot have a good sig from an invalid key. */ rc = gpg_error (GPG_ERR_BAD_PUBKEY); } else { if (r_expiredate) *r_expiredate = pk->expiredate; rc = check_signature_end (pk, sig, digest, r_expired, r_revoked, NULL); /* Check the backsig. This is a back signature (0x19) from * the subkey on the primary key. The idea here is that it * should not be possible for someone to "steal" subkeys and * claim them as their own. The attacker couldn't actually * use the subkey, but they could try and claim ownership of * any signatures issued by it. */ if (!rc && !pk->flags.primary && pk->flags.backsig < 2) { if (!pk->flags.backsig) { log_info (_("WARNING: signing subkey %s is not" " cross-certified\n"),keystr_from_pk(pk)); log_info (_("please see %s for more information\n"), "https://gnupg.org/faq/subkey-cross-certify.html"); /* The default option --require-cross-certification * makes this warning an error. */ if (opt.flags.require_cross_cert) rc = gpg_error (GPG_ERR_GENERAL); } else if(pk->flags.backsig == 1) { log_info (_("WARNING: signing subkey %s has an invalid" " cross-certification\n"), keystr_from_pk(pk)); rc = gpg_error (GPG_ERR_GENERAL); } } } if( !rc && sig->sig_class < 2 && is_status_enabled() ) { /* This signature id works best with DLP algorithms because * they use a random parameter for every signature. Instead of * this sig-id we could have also used the hash of the document * and the timestamp, but the drawback of this is, that it is * not possible to sign more than one identical document within * one second. Some remote batch processing applications might * like this feature here. * * Note that before 2.0.10, we used RIPE-MD160 for the hash * and accidentally didn't include the timestamp and algorithm * information in the hash. Given that this feature is not * commonly used and that a replay attacks detection should * not solely be based on this feature (because it does not * work with RSA), we take the freedom and switch to SHA-1 * with 2.0.10 to take advantage of hardware supported SHA-1 * implementations. We also include the missing information * in the hash. Note also the SIG_ID as computed by gpg 1.x * and gpg 2.x didn't matched either because 2.x used to print * MPIs not in PGP format. */ u32 a = sig->timestamp; int nsig = pubkey_get_nsig( sig->pubkey_algo ); unsigned char *p, *buffer; size_t n, nbytes; int i; char hashbuf[20]; /* We use SHA-1 here. */ nbytes = 6; for (i=0; i < nsig; i++ ) { if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n, sig->data[i])) BUG(); nbytes += n; } /* Make buffer large enough to be later used as output buffer. */ if (nbytes < 100) nbytes = 100; nbytes += 10; /* Safety margin. */ /* Fill and hash buffer. */ buffer = p = xmalloc (nbytes); *p++ = sig->pubkey_algo; *p++ = sig->digest_algo; *p++ = (a >> 24) & 0xff; *p++ = (a >> 16) & 0xff; *p++ = (a >> 8) & 0xff; *p++ = a & 0xff; nbytes -= 6; for (i=0; i < nsig; i++ ) { if (gcry_mpi_print (GCRYMPI_FMT_PGP, p, nbytes, &n, sig->data[i])) BUG(); p += n; nbytes -= n; } gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, buffer, p-buffer); p = make_radix64_string (hashbuf, 20); sprintf (buffer, "%s %s %lu", p, strtimestamp (sig->timestamp), (ulong)sig->timestamp); xfree (p); write_status_text (STATUS_SIG_ID, buffer); xfree (buffer); }
/* * Get the session key from a pubkey enc packet and return it in DEK, * which should have been allocated in secure memory by the caller. */ gpg_error_t get_session_key (ctrl_t ctrl, PKT_pubkey_enc * k, DEK * dek) { PKT_public_key *sk = NULL; int rc; if (DBG_CLOCK) log_clock ("get_session_key enter"); rc = openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC); if (rc) goto leave; if ((k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets) { sk = xmalloc_clear (sizeof *sk); sk->pubkey_algo = k->pubkey_algo; /* We want a pubkey with this algo. */ if (!(rc = get_seckey (ctrl, sk, k->keyid))) { /* Check compliance. */ if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, sk->pubkey_algo, sk->pkey, nbits_from_pk (sk), NULL)) { log_info (_("key %s is not suitable for decryption" " in %s mode\n"), keystr_from_pk (sk), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_PUBKEY_ALGO); } else rc = get_it (ctrl, k, dek, sk, k->keyid); } } else if (opt.skip_hidden_recipients) rc = gpg_error (GPG_ERR_NO_SECKEY); else /* Anonymous receiver: Try all available secret keys. */ { void *enum_context = NULL; u32 keyid[2]; for (;;) { free_public_key (sk); sk = xmalloc_clear (sizeof *sk); rc = enum_secret_keys (ctrl, &enum_context, sk); if (rc) { rc = GPG_ERR_NO_SECKEY; break; } if (sk->pubkey_algo != k->pubkey_algo) continue; if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC)) continue; keyid_from_pk (sk, keyid); if (!opt.quiet) log_info (_("anonymous recipient; trying secret key %s ...\n"), keystr (keyid)); /* Check compliance. */ if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, sk->pubkey_algo, sk->pkey, nbits_from_pk (sk), NULL)) { log_info (_("key %s is not suitable for decryption" " in %s mode\n"), keystr_from_pk (sk), gnupg_compliance_option_string (opt.compliance)); continue; } rc = get_it (ctrl, k, dek, sk, keyid); if (!rc) { if (!opt.quiet) log_info (_("okay, we are the anonymous recipient.\n")); break; } else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) break; /* Don't try any more secret keys. */ } enum_secret_keys (ctrl, &enum_context, NULL); /* free context */ } leave: free_public_key (sk); if (DBG_CLOCK) log_clock ("get_session_key leave"); return rc; }