X-Git-Url: https://the.earth.li/gitweb/?p=onak.git;a=blobdiff_plain;f=sigcheck.c;h=2f72a492af2599859efc1ae8079c3eae0a4d2a4d;hp=89b3f92970205df3ff70f629ef201f6bb839c705;hb=c981a80699901eb3d513a4cc9355574a69016037;hpb=6565bed3065d1751abf469da1a85884d9ddde759 diff --git a/sigcheck.c b/sigcheck.c index 89b3f92..2f72a49 100644 --- a/sigcheck.c +++ b/sigcheck.c @@ -17,12 +17,14 @@ */ #include +#include #include "build-config.h" #include "decodekey.h" #include "keyid.h" #include "keystructs.h" #include "log.h" +#include "onak.h" #include "openpgp.h" #include "sigcheck.h" @@ -34,8 +36,457 @@ #include "md5.h" #include "sha1.h" #endif + #include "sha1x.h" +#ifdef HAVE_CRYPTO +#include +#include +#include +#include +#include +#include +#include +#include "rsa.h" +#endif + +/* Take an MPI from a buffer and import it into a GMP mpz_t */ +#define MPI_TO_MPZ(pk, v) \ +{ \ + /* MPI length is stored in bits, convert it to bytes */ \ + if (pk->length < (ofs + 2)) { \ + ret = ONAK_E_INVALID_PKT; \ + } else { \ + len = pk->data[ofs] << 8 | pk->data[ofs + 1]; \ + len += 7; \ + len = len >> 3; \ + if (pk->length < (ofs + len + 2)) { \ + ret = ONAK_E_INVALID_PKT; \ + } else { \ + mpz_import(v, len, 1, 1, 0, 0, &pk->data[ofs + 2]); \ + ofs += len + 2; \ + } \ + } \ +} + +#if HAVE_CRYPTO + +/* + * Hold the crypto material for a public key. + * May want to move to a header at some point. + */ +struct onak_key_material { + uint8_t type; + union { + struct dsa_params dsa; + struct ecc_point ecc; + struct rsa_public_key rsa; + uint8_t ed25519[32]; + }; + mpz_t y; +}; + +static void onak_free_key_material(struct onak_key_material *key) +{ + switch (key->type) { + case OPENPGP_PKALGO_ECDSA: + ecc_point_clear(&key->ecc); + break; + case OPENPGP_PKALGO_DSA: + mpz_clear(key->dsa.p); + mpz_clear(key->dsa.q); + mpz_clear(key->dsa.g); + mpz_clear(key->y); + break; + case OPENPGP_PKALGO_RSA: + case OPENPGP_PKALGO_RSA_ENC: + case OPENPGP_PKALGO_RSA_SIGN: + mpz_clear(key->rsa.n); + mpz_clear(key->rsa.e); + break; + } + + /* Set the key type back to 0 to indicate we cleared it */ + key->type = 0; + + return; +} + +static onak_status_t onak_parse_key_material(struct openpgp_packet *pk, + struct onak_key_material *key) +{ + int i, len, ofs; + enum onak_oid oid; + mpz_t x, y; + onak_status_t ret = ONAK_E_OK; + + /* Clear the key type; only set it when fully parsed */ + key->type = 0; + + /* + * Shortest valid key is v4 Ed25519, which takes 51 bytes, so do a + * quick sanity check which will ensure we have enough data to check + * the packet header and OID info. + */ + if (pk->length < 51) + return ONAK_E_INVALID_PKT; + + if (pk->data[0] != 4 && pk->data[0] != 5) + return ONAK_E_UNSUPPORTED_FEATURE; + + /* + * MPIs are after version byte, 4 byte creation time + + * type byte plus length for v5. + */ + ofs = (pk->data[0] == 4) ? 6 : 10; + switch (pk->data[5]) { + case OPENPGP_PKALGO_ECDSA: + oid = onak_parse_oid(&pk->data[ofs], pk->length - ofs); + if (oid == ONAK_OID_INVALID) + return ONAK_E_INVALID_PKT; + if (oid == ONAK_OID_UNKNOWN) + return ONAK_E_UNSUPPORTED_FEATURE; + + if (oid == ONAK_OID_NISTP256) { + if (pk->length - ofs != 76) + return ONAK_E_INVALID_PKT; + /* Move past the OID to the key data MPI */ + ofs += pk->data[ofs] + 1; + len = pk->data[ofs] << 8 | pk->data[ofs + 1]; + if (len != 515) + return ONAK_E_INVALID_PKT; + if (pk->data[ofs + 2] != 4) + return ONAK_E_INVALID_PKT; + mpz_init(x); + mpz_init(y); + ecc_point_init(&key->ecc, nettle_get_secp_256r1()); + ofs += 3; + mpz_import(x, 32, 1, 1, 0, 0, &pk->data[ofs]); + ofs += 32; + mpz_import(y, 32, 1, 1, 0, 0, &pk->data[ofs]); + ofs += 32; + ecc_point_set(&key->ecc, x, y); + } else if (oid == ONAK_OID_NISTP384) { + if (pk->length - ofs != 105) + return ONAK_E_INVALID_PKT; + /* Move past the OID to the key data MPI */ + ofs += pk->data[ofs] + 1; + len = pk->data[ofs] << 8 | pk->data[ofs + 1]; + if (len != 771) + return ONAK_E_INVALID_PKT; + if (pk->data[ofs + 2] != 4) + return ONAK_E_INVALID_PKT; + mpz_init(x); + mpz_init(y); + ecc_point_init(&key->ecc, nettle_get_secp_384r1()); + ofs += 3; + mpz_import(x, 48, 1, 1, 0, 0, &pk->data[ofs]); + ofs += 48; + mpz_import(y, 48, 1, 1, 0, 0, &pk->data[ofs]); + ofs += 48; + ecc_point_set(&key->ecc, x, y); + } else if (oid == ONAK_OID_NISTP521) { + if (pk->length - ofs != 141) + return ONAK_E_INVALID_PKT; + /* Move past the OID to the key data MPI */ + ofs += pk->data[ofs] + 1; + len = pk->data[ofs] << 8 | pk->data[ofs + 1]; + if (len != 1059) + return ONAK_E_INVALID_PKT; + if (pk->data[ofs + 2] != 4) + return ONAK_E_INVALID_PKT; + mpz_init(x); + mpz_init(y); + ecc_point_init(&key->ecc, nettle_get_secp_521r1()); + ofs += 3; + mpz_import(x, 66, 1, 1, 0, 0, &pk->data[ofs]); + ofs += 66; + mpz_import(y, 66, 1, 1, 0, 0, &pk->data[ofs]); + ofs += 66; + ecc_point_set(&key->ecc, x, y); + } else { + return ONAK_E_UNSUPPORTED_FEATURE; + } + mpz_clear(y); + mpz_clear(x); + break; + case OPENPGP_PKALGO_EDDSA: + if (pk->length - ofs != 45) + return ONAK_E_INVALID_PKT; + oid = onak_parse_oid(&pk->data[ofs], pk->length - ofs); + if (oid == ONAK_OID_INVALID) + return ONAK_E_INVALID_PKT; + if (oid == ONAK_OID_UNKNOWN) + return ONAK_E_UNSUPPORTED_FEATURE; + + /* Move past the OID to the key data MPI */ + ofs += pk->data[ofs] + 1; + + if (oid == ONAK_OID_ED25519) { + len = pk->data[ofs] << 8 | pk->data[ofs + 1]; + if (len != 263) + return ONAK_E_INVALID_PKT; + if (pk->data[ofs + 2] != 0x40) + return ONAK_E_INVALID_PKT; + ofs += 3; + memcpy(key->ed25519, &pk->data[ofs], 32); + ofs += 32; + } else { + return ONAK_E_UNSUPPORTED_FEATURE; + } + break; + case OPENPGP_PKALGO_DSA: + mpz_init(key->dsa.p); + mpz_init(key->dsa.q); + mpz_init(key->dsa.g); + mpz_init(key->y); + MPI_TO_MPZ(pk, key->dsa.p); + if (ret == ONAK_E_OK) + MPI_TO_MPZ(pk, key->dsa.q); + if (ret == ONAK_E_OK) + MPI_TO_MPZ(pk, key->dsa.g); + if (ret == ONAK_E_OK) + MPI_TO_MPZ(pk, key->y); + break; + case OPENPGP_PKALGO_RSA: + case OPENPGP_PKALGO_RSA_ENC: + case OPENPGP_PKALGO_RSA_SIGN: + mpz_init(key->rsa.n); + mpz_init(key->rsa.e); + key->rsa.size = ((pk->data[6] << 8) + pk->data[7] + 7) >> 3; + MPI_TO_MPZ(pk, key->rsa.n); + if (ret == ONAK_E_OK) + MPI_TO_MPZ(pk, key->rsa.e); + break; + default: + return ONAK_E_UNSUPPORTED_FEATURE; + } + + key->type = pk->data[5]; + + if (ret != ONAK_E_OK) { + onak_free_key_material(key); + } + + return ret; +} + +onak_status_t onak_check_hash_sig(struct openpgp_publickey *sigkey, + struct openpgp_packet *sig, + uint8_t *hash, + uint8_t hashtype) +{ + onak_status_t ret; + struct onak_key_material pubkey; + struct dsa_signature dsasig; + uint8_t edsig[64]; + uint64_t keyid; + int len, ofs; + mpz_t s; + + ret = onak_parse_key_material(sigkey->publickey, &pubkey); + if (ret != ONAK_E_OK) { + return ret; + } + + /* Sanity check the length of the signature packet */ + if (sig->length < 8) { + ret = ONAK_E_INVALID_PKT; + goto out; + } + + /* Is the key the same type as the signature we're checking? */ + if (pubkey.type != sig->data[2]) { + ret = ONAK_E_INVALID_PARAM; + goto out; + } + + /* Skip the hashed data */ + ofs = (sig->data[4] << 8) + sig->data[5] + 6; + if (sig->length < ofs + 2) { + ret = ONAK_E_INVALID_PKT; + goto out; + } + /* Skip the unhashed data */ + ofs += (sig->data[ofs] << 8) + sig->data[ofs + 1] + 2; + if (sig->length < ofs + 2) { + ret = ONAK_E_INVALID_PKT; + goto out; + } + /* Skip the sig hash bytes */ + ofs += 2; + + /* Parse the actual signature values */ + switch (sig->data[2]) { + case OPENPGP_PKALGO_ECDSA: + case OPENPGP_PKALGO_DSA: + mpz_init(dsasig.r); + mpz_init(dsasig.s); + MPI_TO_MPZ(sig, dsasig.r); + if (ret == ONAK_E_OK) + MPI_TO_MPZ(sig, dsasig.s); + break; + case OPENPGP_PKALGO_EDDSA: + mpz_init(dsasig.r); + mpz_init(dsasig.s); + MPI_TO_MPZ(sig, dsasig.r); + if (ret == ONAK_E_OK) + MPI_TO_MPZ(sig, dsasig.s); + mpz_export(edsig, NULL, 1, 1, 0, 0, dsasig.r); + mpz_export(&edsig[32], NULL, 1, 1, 0, 0, dsasig.s); + break; + case OPENPGP_PKALGO_RSA: + case OPENPGP_PKALGO_RSA_SIGN: + mpz_init(s); + MPI_TO_MPZ(sig, s); + break; + } + + /* If we didn't parse the signature properly then do clean-up */ + if (ret != ONAK_E_OK) + goto sigerr; + + /* Squash a signing only RSA key to a standard RSA key for below */ + if (pubkey.type == OPENPGP_PKALGO_RSA_SIGN) { + pubkey.type = OPENPGP_PKALGO_RSA; + } + +#define KEYHASH(key, hash) ((key << 8) | hash) + + switch KEYHASH(pubkey.type, hashtype) { + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_MD5): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + MD5_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_WEAK_SIGNATURE : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_RIPEMD160): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + RIPEMD160_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_SHA1): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + SHA1_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_SHA1X): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + SHA1X_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_SHA224): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + SHA224_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_SHA256): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + SHA256_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_SHA384): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + SHA384_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_DSA, OPENPGP_HASH_SHA512): + ret = dsa_verify(&pubkey.dsa, pubkey.y, + SHA512_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_ECDSA, OPENPGP_HASH_SHA1): + ret = ecdsa_verify(&pubkey.ecc, + SHA1_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + case KEYHASH(OPENPGP_PKALGO_ECDSA, OPENPGP_HASH_SHA256): + ret = ecdsa_verify(&pubkey.ecc, + SHA256_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_ECDSA, OPENPGP_HASH_SHA384): + ret = ecdsa_verify(&pubkey.ecc, + SHA384_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_ECDSA, OPENPGP_HASH_SHA512): + ret = ecdsa_verify(&pubkey.ecc, + SHA512_DIGEST_SIZE, hash, &dsasig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_EDDSA, OPENPGP_HASH_RIPEMD160): + ret = ed25519_sha512_verify(pubkey.ed25519, + RIPEMD160_DIGEST_SIZE, hash, edsig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_EDDSA, OPENPGP_HASH_SHA256): + ret = ed25519_sha512_verify(pubkey.ed25519, + SHA256_DIGEST_SIZE, hash, edsig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_EDDSA, OPENPGP_HASH_SHA384): + ret = ed25519_sha512_verify(pubkey.ed25519, + SHA384_DIGEST_SIZE, hash, edsig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_EDDSA, OPENPGP_HASH_SHA512): + ret = ed25519_sha512_verify(pubkey.ed25519, + SHA512_DIGEST_SIZE, hash, edsig) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_MD5): + ret = rsa_md5_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_WEAK_SIGNATURE : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_RIPEMD160): + ret = rsa_ripemd160_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_SHA1): + ret = rsa_sha1_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_SHA224): + ret = rsa_sha224_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_SHA256): + ret = rsa_sha256_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_SHA384): + ret = rsa_sha384_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + case KEYHASH(OPENPGP_PKALGO_RSA, OPENPGP_HASH_SHA512): + ret = rsa_sha512_verify_digest(&pubkey.rsa, hash, s) ? + ONAK_E_OK : ONAK_E_BAD_SIGNATURE; + break; + default: + ret = ONAK_E_UNSUPPORTED_FEATURE; + } + +sigerr: + switch (sig->data[2]) { + case OPENPGP_PKALGO_ECDSA: + case OPENPGP_PKALGO_EDDSA: + case OPENPGP_PKALGO_DSA: + mpz_clear(dsasig.r); + mpz_clear(dsasig.s); + break; + case OPENPGP_PKALGO_RSA: + case OPENPGP_PKALGO_RSA_SIGN: + mpz_clear(s); + break; + } + +out: + onak_free_key_material(&pubkey); + + return ret; +} + +#endif /* HAVE_CRYPTO */ + onak_status_t calculate_packet_sighash(struct openpgp_publickey *key, struct openpgp_packet *packet, struct openpgp_packet *sig,