|
@@ -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, ¶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 <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;
|
|
|
+}
|