/* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file crypto_rsa.c * \brief NSS implementations of our RSA code. **/ #include "lib/crypt_ops/crypto_rsa.h" #include "lib/crypt_ops/crypto_nss_mgt.h" #include "lib/crypt_ops/crypto_util.h" #include "lib/ctime/di_ops.h" #include "lib/encoding/binascii.h" #include "lib/fs/files.h" #include "lib/intmath/cmp.h" #include "lib/intmath/muldiv.h" #include "lib/log/log.h" #include "lib/log/util_bug.h" #include #include #include #include #ifdef ENABLE_OPENSSL #include #include #endif /** Declaration for crypto_pk_t structure. */ struct crypto_pk_t { SECKEYPrivateKey *seckey; SECKEYPublicKey *pubkey; }; /** Return true iff key contains the private-key portion of the RSA * key. */ int crypto_pk_key_is_private(const crypto_pk_t *key) { return key && key->seckey; } /** used by tortls.c: wrap a SecKEYPublicKey in a crypto_pk_t. Take ownership * of the RSA object. */ crypto_pk_t * crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub) { crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t)); result->pubkey = pub; return result; } /** Return the SECKEYPublicKey for the provided crypto_pk_t. */ const SECKEYPublicKey * crypto_pk_get_nss_pubkey(const crypto_pk_t *key) { tor_assert(key); return key->pubkey; } /** Return the SECKEYPrivateKey for the provided crypto_pk_t, or NULL if it * does not exist. */ const SECKEYPrivateKey * crypto_pk_get_nss_privkey(const crypto_pk_t *key) { tor_assert(key); return key->seckey; } #ifdef ENABLE_OPENSSL /** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the * RSA object. */ crypto_pk_t * crypto_new_pk_from_openssl_rsa_(RSA *rsa) { crypto_pk_t *pk = NULL; unsigned char *buf = NULL; int len = i2d_RSAPublicKey(rsa, &buf); RSA_free(rsa); if (len < 0 || buf == NULL) goto end; pk = crypto_pk_asn1_decode((const char *)buf, len); end: if (buf) OPENSSL_free(buf); return pk; } /** Helper, used by tor-gencert.c. Return the RSA from a * crypto_pk_t. */ struct rsa_st * crypto_pk_get_openssl_rsa_(crypto_pk_t *pk) { size_t buflen = crypto_pk_keysize(pk)*16; unsigned char *buf = tor_malloc_zero(buflen); const unsigned char *cp = buf; RSA *rsa = NULL; int used = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen); if (used < 0) goto end; rsa = d2i_RSAPrivateKey(NULL, &cp, used); end: memwipe(buf, 0, buflen); tor_free(buf); return rsa; } /** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff * private is set, include the private-key portion of the key. Return a valid * pointer on success, and NULL on failure. */ MOCK_IMPL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *pk, int private)) { size_t buflen = crypto_pk_keysize(pk)*16; unsigned char *buf = tor_malloc_zero(buflen); const unsigned char *cp = buf; RSA *rsa = NULL; EVP_PKEY *result = NULL; if (private) { int len = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen); if (len < 0) goto end; rsa = d2i_RSAPrivateKey(NULL, &cp, len); } else { int len = crypto_pk_asn1_encode(pk, (char*)buf, buflen); if (len < 0) goto end; rsa = d2i_RSAPublicKey(NULL, &cp, len); } if (!rsa) goto end; if (!(result = EVP_PKEY_new())) goto end; if (!(EVP_PKEY_assign_RSA(result, rsa))) { EVP_PKEY_free(result); RSA_free(rsa); result = NULL; } end: memwipe(buf, 0, buflen); tor_free(buf); return result; } #endif /** Allocate and return storage for a public key. The key itself will not yet * be set. */ MOCK_IMPL(crypto_pk_t *, crypto_pk_new,(void)) { crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t)); return result; } /** Release the NSS objects held in key */ static void crypto_pk_clear(crypto_pk_t *key) { if (key->pubkey) SECKEY_DestroyPublicKey(key->pubkey); if (key->seckey) SECKEY_DestroyPrivateKey(key->seckey); memset(key, 0, sizeof(crypto_pk_t)); } /** Release a reference to an asymmetric key; when all the references * are released, free the key. */ void crypto_pk_free_(crypto_pk_t *key) { if (!key) return; crypto_pk_clear(key); tor_free(key); } /** Generate a bits-bit new public/private keypair in env. * Return 0 on success, -1 on failure. */ MOCK_IMPL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits)) { tor_assert(key); PK11RSAGenParams params = { .keySizeInBits = bits, .pe = TOR_RSA_EXPONENT }; int result = -1; PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, NULL); SECKEYPrivateKey *seckey = NULL; SECKEYPublicKey *pubkey = NULL; if (!slot) { crypto_nss_log_errors(LOG_WARN, "getting slot for RSA keygen"); goto done; } seckey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶ms, &pubkey, PR_FALSE /*isPerm */, PR_FALSE /*isSensitive*/, NULL); if (seckey == NULL || pubkey == NULL) { crypto_nss_log_errors(LOG_WARN, "generating an RSA key"); goto done; } crypto_pk_clear(key); key->seckey = seckey; key->pubkey = pubkey; seckey = NULL; pubkey = NULL; result = 0; done: if (slot) PK11_FreeSlot(slot); if (pubkey) SECKEY_DestroyPublicKey(pubkey); if (seckey) SECKEY_DestroyPrivateKey(seckey); return result; } /** Return true iff env is a valid private key. */ int crypto_pk_is_valid_private_key(const crypto_pk_t *key) { /* We don't need to do validation here, since unlike OpenSSL, NSS won't let * us load private keys without validating them. */ return key && key->seckey; } /** Return true iff env contains a public key whose public exponent * equals 65537. */ int crypto_pk_public_exponent_ok(const crypto_pk_t *key) { return key && key->pubkey && key->pubkey->keyType == rsaKey && DER_GetUInteger(&key->pubkey->u.rsa.publicExponent) == TOR_RSA_EXPONENT; } /** Compare two big-endian integers stored in a and b; return a tristate. */ STATIC int secitem_uint_cmp(const SECItem *a, const SECItem *b) { const unsigned abits = SECKEY_BigIntegerBitLength(a); const unsigned bbits = SECKEY_BigIntegerBitLength(b); if (abits < bbits) return -1; else if (abits > bbits) return 1; /* okay, they have the same number of bits set. Get a pair of aligned * pointers to their bytes that are set... */ const unsigned nbytes = CEIL_DIV(abits, 8); tor_assert(nbytes <= a->len); tor_assert(nbytes <= b->len); const unsigned char *aptr = a->data + (a->len - nbytes); const unsigned char *bptr = b->data + (b->len - nbytes); /* And compare them. */ return fast_memcmp(aptr, bptr, nbytes); } /** Compare the public-key components of a and b. Return less than 0 * if a\b. A NULL key is * considered to be less than all non-NULL keys, and equal to itself. * * Note that this may leak information about the keys through timing. */ int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) { int result; char a_is_non_null = (a != NULL) && (a->pubkey != NULL); char b_is_non_null = (b != NULL) && (b->pubkey != NULL); char an_argument_is_null = !a_is_non_null | !b_is_non_null; result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null)); if (an_argument_is_null) return result; // This is all Tor uses with this structure. tor_assert(a->pubkey->keyType == rsaKey); tor_assert(b->pubkey->keyType == rsaKey); const SECItem *a_n, *a_e, *b_n, *b_e; a_n = &a->pubkey->u.rsa.modulus; b_n = &b->pubkey->u.rsa.modulus; a_e = &a->pubkey->u.rsa.publicExponent; b_e = &b->pubkey->u.rsa.publicExponent; result = secitem_uint_cmp(a_n, b_n); if (result) return result; return secitem_uint_cmp(a_e, b_e); } /** Return the size of the public key modulus in env, in bytes. */ size_t crypto_pk_keysize(const crypto_pk_t *key) { tor_assert(key); tor_assert(key->pubkey); return SECKEY_PublicKeyStrength(key->pubkey); } /** Return the size of the public key modulus of env, in bits. */ int crypto_pk_num_bits(crypto_pk_t *key) { tor_assert(key); tor_assert(key->pubkey); return SECKEY_PublicKeyStrengthInBits(key->pubkey); } /** * Make a copy of key and return it. */ crypto_pk_t * crypto_pk_dup_key(crypto_pk_t *key) { crypto_pk_t *result = crypto_pk_new(); if (key->pubkey) result->pubkey = SECKEY_CopyPublicKey(key->pubkey); if (key->seckey) result->seckey = SECKEY_CopyPrivateKey(key->seckey); return result; } /** For testing: replace dest with src. (Dest must have a refcount * of 1) */ void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src) { crypto_pk_clear(dest); if (src->pubkey) dest->pubkey = SECKEY_CopyPublicKey(src->pubkey); } /** For testing: replace dest with src. (Dest must have a refcount * of 1) */ void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src) { crypto_pk_clear(dest); if (src->pubkey) dest->pubkey = SECKEY_CopyPublicKey(src->pubkey); if (src->seckey) dest->seckey = SECKEY_CopyPrivateKey(src->seckey); } /** Make a real honest-to-goodness copy of env, and return it. * Returns NULL on failure. */ crypto_pk_t * crypto_pk_copy_full(crypto_pk_t *key) { // These aren't reference-counted is nss, so it's fine to just // use the same function. return crypto_pk_dup_key(key); } static const CK_RSA_PKCS_OAEP_PARAMS oaep_params = { .hashAlg = CKM_SHA_1, .mgf = CKG_MGF1_SHA1, .source = CKZ_DATA_SPECIFIED, .pSourceData = NULL, .ulSourceDataLen = 0 }; static const SECItem oaep_item = { .type = siBuffer, .data = (unsigned char *) &oaep_params, .len = sizeof(oaep_params) }; /** Return the mechanism code and parameters for a given padding method when * used with RSA */ static CK_MECHANISM_TYPE padding_to_mechanism(int padding, SECItem **item_out) { switch (padding) { case PK_PKCS1_OAEP_PADDING: *item_out = (SECItem *)&oaep_item; return CKM_RSA_PKCS_OAEP; default: tor_assert_unreached(); *item_out = NULL; return CKM_INVALID_MECHANISM; } } /** Encrypt fromlen bytes from from with the public key * in env, using the padding method padding. On success, * write the result to to, and return the number of bytes * written. On failure, return -1. * * tolen is the number of writable bytes in to, and must be * at least the length of the modulus of env. */ int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding) { tor_assert(env); tor_assert(to); tor_assert(from); tor_assert(tolen < INT_MAX); tor_assert(fromlen < INT_MAX); if (BUG(! env->pubkey)) return -1; unsigned int result_len = 0; SECItem *item = NULL; CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item); SECStatus s = PK11_PubEncrypt(env->pubkey, m, item, (unsigned char *)to, &result_len, (unsigned int)tolen, (const unsigned char *)from, (unsigned int)fromlen, NULL); if (s != SECSuccess) { crypto_nss_log_errors(LOG_WARN, "encrypting to an RSA key"); return -1; } return (int)result_len; } /** Decrypt fromlen bytes from from with the private key * in env, using the padding method padding. On success, * write the result to to, and return the number of bytes * written. On failure, return -1. * * tolen is the number of writable bytes in to, and must be * at least the length of the modulus of key. */ int crypto_pk_private_decrypt(crypto_pk_t *key, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure) { tor_assert(key); tor_assert(to); tor_assert(from); tor_assert(tolen < INT_MAX); tor_assert(fromlen < INT_MAX); if (!crypto_pk_key_is_private(key)) return -1; /* Not a private key. */ unsigned int result_len = 0; SECItem *item = NULL; CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item); SECStatus s = PK11_PrivDecrypt(key->seckey, m, item, (unsigned char *)to, &result_len, (unsigned int)tolen, (const unsigned char *)from, (unsigned int)fromlen); if (s != SECSuccess) { const int severity = warnOnFailure ? LOG_WARN : LOG_INFO; crypto_nss_log_errors(severity, "decrypting with an RSA key"); return -1; } return (int)result_len; } /** Check the signature in from (fromlen bytes long) with the * public key in key, using PKCS1 padding. On success, write the * signed data to to, and return the number of bytes written. * On failure, return -1. * * tolen is the number of writable bytes in to, and must be * at least the length of the modulus of key. */ MOCK_IMPL(int, crypto_pk_public_checksig,(const crypto_pk_t *key, char *to, size_t tolen, const char *from, size_t fromlen)) { tor_assert(key); tor_assert(to); tor_assert(from); tor_assert(tolen < INT_MAX); tor_assert(fromlen < INT_MAX); tor_assert(key->pubkey); SECItem sig = { .type = siBuffer, .data = (unsigned char *) from, .len = (unsigned int) fromlen, }; SECItem dsig = { .type = siBuffer, .data = (unsigned char *) to, .len = (unsigned int) tolen }; SECStatus s; s = PK11_VerifyRecover(key->pubkey, &sig, &dsig, NULL); if (s != SECSuccess) return -1; return (int)dsig.len; } /** Sign fromlen bytes of data from from with the private key in * env, using PKCS1 padding. On success, write the signature to * to, and return the number of bytes written. On failure, return * -1. * * tolen is the number of writable bytes in to, and must be * at least the length of the modulus of env. */ int crypto_pk_private_sign(const crypto_pk_t *key, char *to, size_t tolen, const char *from, size_t fromlen) { tor_assert(key); tor_assert(to); tor_assert(from); tor_assert(tolen < INT_MAX); tor_assert(fromlen < INT_MAX); if (BUG(!crypto_pk_key_is_private(key))) return -1; SECItem sig = { .type = siBuffer, .data = (unsigned char *)to, .len = (unsigned int) tolen }; SECItem hash = { .type = siBuffer, .data = (unsigned char *)from, .len = (unsigned int) fromlen }; CK_MECHANISM_TYPE m = CKM_RSA_PKCS; SECStatus s = PK11_SignWithMechanism(key->seckey, m, NULL, &sig, &hash); if (s != SECSuccess) { crypto_nss_log_errors(LOG_WARN, "signing with an RSA key"); return -1; } return (int)sig.len; } /* "This has lead to people trading hard-to-find object identifiers and ASN.1 * definitions like baseball cards" - Peter Gutmann, "X.509 Style Guide". */ static const unsigned char RSA_OID[] = { /* RSADSI */ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, /* PKCS1 */ 0x01, 0x01, /* RSA */ 0x01 }; /** ASN.1-encode the public portion of pk into dest. * Return -1 on error, or the number of characters used on success. */ int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len) { tor_assert(pk); if (pk->pubkey == NULL) return -1; CERTSubjectPublicKeyInfo *info; info = SECKEY_CreateSubjectPublicKeyInfo(pk->pubkey); if (! info) return -1; const SECItem *item = &info->subjectPublicKey; size_t actual_len = (item->len) >> 3; /* bits to bytes */ size_t n_used = MIN(actual_len, dest_len); memcpy(dest, item->data, n_used); SECKEY_DestroySubjectPublicKeyInfo(info); return (int) n_used; } /** Decode an ASN.1-encoded public key from str; return the result on * success and NULL on failure. */ crypto_pk_t * crypto_pk_asn1_decode(const char *str, size_t len) { tor_assert(str); if (len >= INT_MAX) return NULL; CERTSubjectPublicKeyInfo info = { .algorithm = { .algorithm = { .type = siDEROID, .data = (unsigned char *)RSA_OID, .len = sizeof(RSA_OID) } }, .subjectPublicKey = { .type = siBuffer, .data = (unsigned char *)str, .len = (unsigned int)(len << 3) /* bytes to bits */ } }; SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(&info); if (pub == NULL) return NULL; crypto_pk_t *result = crypto_pk_new(); result->pubkey = pub; return result; } DISABLE_GCC_WARNING(unused-parameter) /** Given a crypto_pk_t pk, allocate a new buffer containing the Base64 * encoding of the DER representation of the private key into the * dest_len-byte buffer in dest. * Return the number of bytes written on success, -1 on failure. */ int crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest, size_t destlen) { tor_assert(destlen <= INT_MAX); if (!crypto_pk_key_is_private(pk)) return -1; SECKEYPrivateKeyInfo *info = PK11_ExportPrivKeyInfo(pk->seckey, NULL); if (!info) return -1; SECItem *item = &info->privateKey; if (destlen < item->len) { SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE); return -1; } int result = (int)item->len; memcpy(dest, item->data, item->len); SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE); return result; } /** Given a buffer containing the DER representation of the * private key str, decode and return the result on success, or NULL * on failure. */ crypto_pk_t * crypto_pk_asn1_decode_private(const char *str, size_t len) { tor_assert(str); tor_assert(len < INT_MAX); PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL); if (!slot) return NULL; SECKEYPrivateKeyInfo info = { .algorithm = { .algorithm = { .type = siBuffer, .data = (unsigned char *)RSA_OID, .len = sizeof(RSA_OID) } }, .privateKey = { .type = siBuffer, .data = (unsigned char *)str, .len = (int)len, } }; SECStatus s; SECKEYPrivateKey *seckey = NULL; s = PK11_ImportPrivateKeyInfoAndReturnKey(slot, &info, NULL /* nickname */, NULL /* publicValue */, PR_FALSE /* isPerm */, PR_FALSE /* isPrivate */, KU_ALL /* keyUsage */, &seckey, NULL); crypto_pk_t *output = NULL; if (s == SECSuccess && seckey) { output = crypto_pk_new(); output->seckey = seckey; output->pubkey = SECKEY_ConvertToPublicKey(seckey); tor_assert(output->pubkey); } else { crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key"); } if (! crypto_pk_is_valid_private_key(output)) { crypto_pk_free(output); output = NULL; } if (slot) PK11_FreeSlot(slot); return output; }