// freeze_private_key finishes initializing |rsa|'s private key components. // After this function has returned, |rsa| may not be changed. This is needed // because |RSA| is a public struct and, additionally, OpenSSL 1.1.0 opaquified // it wrong (see https://github.com/openssl/openssl/issues/5158). static int freeze_private_key(RSA *rsa, BN_CTX *ctx) { CRYPTO_MUTEX_lock_read(&rsa->lock); int frozen = rsa->private_key_frozen; CRYPTO_MUTEX_unlock_read(&rsa->lock); if (frozen) { return 1; } int ret = 0; CRYPTO_MUTEX_lock_write(&rsa->lock); if (rsa->private_key_frozen) { ret = 1; goto err; } // Pre-compute various intermediate values, as well as copies of private // exponents with correct widths. Note that other threads may concurrently // read from |rsa->n|, |rsa->e|, etc., so any fixes must be in separate // copies. We use |mont_n->N|, |mont_p->N|, and |mont_q->N| as copies of |n|, // |p|, and |q| with the correct minimal widths. if (rsa->mont_n == NULL) { rsa->mont_n = BN_MONT_CTX_new_for_modulus(rsa->n, ctx); if (rsa->mont_n == NULL) { goto err; } } const BIGNUM *n_fixed = &rsa->mont_n->N; // The only public upper-bound of |rsa->d| is the bit length of |rsa->n|. The // ASN.1 serialization of RSA private keys unfortunately leaks the byte length // of |rsa->d|, but normalize it so we only leak it once, rather than per // operation. if (rsa->d != NULL && !ensure_fixed_copy(&rsa->d_fixed, rsa->d, n_fixed->width)) { goto err; } if (rsa->p != NULL && rsa->q != NULL) { if (rsa->mont_p == NULL) { rsa->mont_p = BN_MONT_CTX_new_for_modulus(rsa->p, ctx); if (rsa->mont_p == NULL) { goto err; } } const BIGNUM *p_fixed = &rsa->mont_p->N; if (rsa->mont_q == NULL) { rsa->mont_q = BN_MONT_CTX_new_for_modulus(rsa->q, ctx); if (rsa->mont_q == NULL) { goto err; } } const BIGNUM *q_fixed = &rsa->mont_q->N; if (rsa->dmp1 != NULL && rsa->dmq1 != NULL) { // Key generation relies on this function to compute |iqmp|. if (rsa->iqmp == NULL) { BIGNUM *iqmp = BN_new(); if (iqmp == NULL || !bn_mod_inverse_secret_prime(iqmp, rsa->q, rsa->p, ctx, rsa->mont_p)) { BN_free(iqmp); goto err; } rsa->iqmp = iqmp; } // CRT components are only publicly bounded by their corresponding // moduli's bit lengths. |rsa->iqmp| is unused outside of this one-time // setup, so we do not compute a fixed-width version of it. if (!ensure_fixed_copy(&rsa->dmp1_fixed, rsa->dmp1, p_fixed->width) || !ensure_fixed_copy(&rsa->dmq1_fixed, rsa->dmq1, q_fixed->width)) { goto err; } // Compute |inv_small_mod_large_mont|. Note that it is always modulo the // larger prime, independent of what is stored in |rsa->iqmp|. if (rsa->inv_small_mod_large_mont == NULL) { BIGNUM *inv_small_mod_large_mont = BN_new(); int ok; if (BN_cmp(rsa->p, rsa->q) < 0) { ok = inv_small_mod_large_mont != NULL && bn_mod_inverse_secret_prime(inv_small_mod_large_mont, rsa->p, rsa->q, ctx, rsa->mont_q) && BN_to_montgomery(inv_small_mod_large_mont, inv_small_mod_large_mont, rsa->mont_q, ctx); } else { ok = inv_small_mod_large_mont != NULL && BN_to_montgomery(inv_small_mod_large_mont, rsa->iqmp, rsa->mont_p, ctx); } if (!ok) { BN_free(inv_small_mod_large_mont); goto err; } rsa->inv_small_mod_large_mont = inv_small_mod_large_mont; } } } rsa->private_key_frozen = 1; ret = 1; err: CRYPTO_MUTEX_unlock_write(&rsa->lock); return ret; }
// freeze_private_key finishes initializing |rsa|'s private key components. // After this function has returned, |rsa| may not be changed. This is needed // because |RSA| is a public struct and, additionally, OpenSSL 1.1.0 opaquified // it wrong (see https://github.com/openssl/openssl/issues/5158). static int freeze_private_key(RSA *rsa, BN_CTX *ctx) { CRYPTO_MUTEX_lock_read(&rsa->lock); int flags = rsa->flags; CRYPTO_MUTEX_unlock_read(&rsa->lock); if (flags & RSA_FLAG_PRIVATE_KEY_FROZEN) { return 1; } int ret = 0; CRYPTO_MUTEX_lock_write(&rsa->lock); if (rsa->flags & RSA_FLAG_PRIVATE_KEY_FROZEN) { ret = 1; goto err; } // |rsa->n| is public. Normalize the width. bn_set_minimal_width(rsa->n); if (rsa->mont_n == NULL) { rsa->mont_n = BN_MONT_CTX_new_for_modulus(rsa->n, ctx); if (rsa->mont_n == NULL) { goto err; } } // The only public upper-bound of |rsa->d| is the bit length of |rsa->n|. The // ASN.1 serialization of RSA private keys unfortunately leaks the byte length // of |rsa->d|, but normalize it so we only leak it once, rather than per // operation. if (rsa->d != NULL && !bn_resize_words(rsa->d, rsa->n->width)) { goto err; } if (rsa->p != NULL && rsa->q != NULL) { // |p| and |q| have public bit lengths. bn_set_minimal_width(rsa->p); bn_set_minimal_width(rsa->q); if (rsa->mont_p == NULL) { rsa->mont_p = BN_MONT_CTX_new_for_modulus(rsa->p, ctx); if (rsa->mont_p == NULL) { goto err; } } if (rsa->mont_q == NULL) { rsa->mont_q = BN_MONT_CTX_new_for_modulus(rsa->q, ctx); if (rsa->mont_q == NULL) { goto err; } } if (rsa->dmp1 != NULL && rsa->dmq1 != NULL) { // Key generation relies on this function to compute |iqmp|. if (rsa->iqmp == NULL) { BIGNUM *iqmp = BN_new(); if (iqmp == NULL || !bn_mod_inverse_secret_prime(iqmp, rsa->q, rsa->p, ctx, rsa->mont_p)) { BN_free(iqmp); goto err; } rsa->iqmp = iqmp; } // CRT components are only publicly bounded by their corresponding // moduli's bit lengths. if (!bn_resize_words(rsa->dmp1, rsa->p->width) || !bn_resize_words(rsa->dmq1, rsa->q->width) || !bn_resize_words(rsa->iqmp, rsa->p->width)) { goto err; } // Compute |inv_small_mod_large_mont|. Note that it is always modulo the // larger prime, independent of what is stored in |rsa->iqmp|. if (rsa->inv_small_mod_large_mont == NULL) { BIGNUM *inv_small_mod_large_mont = BN_new(); int ok; if (BN_cmp(rsa->p, rsa->q) < 0) { ok = inv_small_mod_large_mont != NULL && bn_mod_inverse_secret_prime(inv_small_mod_large_mont, rsa->p, rsa->q, ctx, rsa->mont_q) && BN_to_montgomery(inv_small_mod_large_mont, inv_small_mod_large_mont, rsa->mont_q, ctx); } else { ok = inv_small_mod_large_mont != NULL && BN_to_montgomery(inv_small_mod_large_mont, rsa->iqmp, rsa->mont_p, ctx); } if (!ok) { BN_free(inv_small_mod_large_mont); goto err; } rsa->inv_small_mod_large_mont = inv_small_mod_large_mont; } } } rsa->flags |= RSA_FLAG_PRIVATE_KEY_FROZEN; ret = 1; err: CRYPTO_MUTEX_unlock_write(&rsa->lock); return ret; }