[tor-commits] [tor/master] Implement RSA for NSS.

nickm at torproject.org nickm at torproject.org
Wed Sep 5 00:47:14 UTC 2018


commit aa45511250ed9509ca06b76497f66835796a7998
Author: Nick Mathewson <nickm at torproject.org>
Date:   Thu Jul 19 12:03:01 2018 -0400

    Implement RSA for NSS.
---
 src/lib/crypt_ops/crypto_rsa.h         |  16 +-
 src/lib/crypt_ops/crypto_rsa_nss.c     | 699 +++++++++++++++++++++++++++++++++
 src/lib/crypt_ops/crypto_rsa_openssl.c |   2 +-
 src/lib/crypt_ops/include.am           |   7 +-
 4 files changed, 712 insertions(+), 12 deletions(-)

diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index bcb2b51dd..afc6c4201 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -110,14 +110,6 @@ int crypto_pk_get_common_digests(crypto_pk_t *pk,
 int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out);
 crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len);
 
-#ifdef TOR_UNIT_TESTS
-#ifdef ENABLE_NSS
-struct SECItemStr;
-STATIC int secitem_uint_cmp(const struct SECItemStr *a,
-                            const struct SECItemStr *b);
-#endif
-#endif
-
 #ifdef ENABLE_OPENSSL
 /* Prototypes for private functions only used by tortls.c, crypto.c, and the
  * unit tests. */
@@ -132,4 +124,12 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
 void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
 void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
 
+#ifdef TOR_UNIT_TESTS
+#ifdef ENABLE_NSS
+struct SECItemStr;
+STATIC int secitem_uint_cmp(const struct SECItemStr *a,
+                            const struct SECItemStr *b);
+#endif
+#endif
+
 #endif
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
new file mode 100644
index 000000000..0411687b9
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -0,0 +1,699 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, 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 <string.h>
+
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secder.h>
+
+#ifdef ENABLE_OPENSSL
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#endif
+
+/** Declaration for crypto_pk_t structure. */
+struct crypto_pk_t
+{
+  SECKEYPrivateKey *seckey;
+  SECKEYPublicKey *pubkey;
+};
+
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_t *key)
+{
+  return key && 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 <b>key</b> */
+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 <b>bits</b>-bit new public/private keypair in <b>env</b>.
+ * 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, &params,
+                                &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 <b>env</b> has a valid key.
+ */
+int
+crypto_pk_check_key(crypto_pk_t *key)
+{
+  return key && key->pubkey;
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals 65537.
+ */
+int
+crypto_pk_public_exponent_ok(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, 0 if a==b, and greater 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 <b>env</b>, 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 <b>env</b>, 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 <b>key</b> 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 <b>env</b>, 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 <b>fromlen</b> bytes from <b>from</b> with the public key
+ * in <b>env</b>, using the padding method <b>padding</b>.  On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written.  On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+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(!crypto_pk_check_key(env)))
+    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 <b>fromlen</b> bytes from <b>from</b> with the private key
+ * in <b>env</b>, using the padding method <b>padding</b>.  On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written.  On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>key</b>.
+ */
+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 <b>from</b> (<b>fromlen</b> bytes long) with the
+ * public key in <b>key</b>, using PKCS1 padding.  On success, write the
+ * signed data to <b>to</b>, and return the number of bytes written.
+ * On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>key</b>.
+ */
+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 <b>fromlen</b> bytes of data from <b>from</b> with the private key in
+ * <b>env</b>, using PKCS1 padding.  On success, write the signature to
+ * <b>to</b>, and return the number of bytes written.  On failure, return
+ * -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+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 <b>pk</b> into <b>dest</b>.
+ * 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 <b>str</b>; 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 <b>pk</b>, allocate a new buffer containing the Base64
+ * encoding of the DER representation of the private key into the
+ * <b>dest_len</b>-byte buffer in <b>dest</b>.
+ * 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 <b>str</b>, 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);
+
+  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,
+                            }
+  };
+
+  PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL);
+  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");
+  }
+
+  return output;
+}
diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c
index 9a3729a87..7ff71f6dd 100644
--- a/src/lib/crypt_ops/crypto_rsa_openssl.c
+++ b/src/lib/crypt_ops/crypto_rsa_openssl.c
@@ -76,7 +76,7 @@ crypto_new_pk_from_openssl_rsa_(RSA *rsa)
 RSA *
 crypto_pk_get_openssl_rsa_(crypto_pk_t *env)
 {
-  return RSA_PrivateKeyDup(env->key);
+  return RSAPrivateKey_dup(env->key);
 }
 
 /** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t.  Iff
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index 69bd7c2db..195dac6bd 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -19,7 +19,6 @@ src_lib_libtor_crypt_ops_a_SOURCES =			\
 	src/lib/crypt_ops/crypto_pwbox.c		\
 	src/lib/crypt_ops/crypto_rand.c			\
 	src/lib/crypt_ops/crypto_rsa.c			\
-	src/lib/crypt_ops/crypto_rsa_openssl.c		\
 	src/lib/crypt_ops/crypto_s2k.c			\
 	src/lib/crypt_ops/crypto_util.c                 \
 	src/lib/crypt_ops/digestset.c
@@ -28,10 +27,12 @@ if USE_NSS
 src_lib_libtor_crypt_ops_a_SOURCES +=			\
 	src/lib/crypt_ops/aes_nss.c			\
 	src/lib/crypt_ops/crypto_dh_nss.c		\
-	src/lib/crypt_ops/crypto_nss_mgt.c
+	src/lib/crypt_ops/crypto_nss_mgt.c		\
+	src/lib/crypt_ops/crypto_rsa_nss.c
 else
 src_lib_libtor_crypt_ops_a_SOURCES +=			\
-	src/lib/crypt_ops/aes_openssl.c
+	src/lib/crypt_ops/aes_openssl.c			\
+	src/lib/crypt_ops/crypto_rsa_openssl.c
 endif
 
 if USE_OPENSSL





More information about the tor-commits mailing list