Ejemplo n.º 1
0
/*-
 * _gnutls_pkcs11_privkey_sign_hash:
 * @key: Holds the key
 * @hash: holds the data to be signed (should be output of a hash)
 * @signature: will contain the signature allocated with gnutls_malloc()
 *
 * This function will sign the given data using a signature algorithm
 * supported by the private key. It is assumed that the given data
 * are the output of a hash function.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 -*/
int
_gnutls_pkcs11_privkey_sign_hash(gnutls_pkcs11_privkey_t key,
				 const gnutls_datum_t * hash,
				 gnutls_datum_t * signature)
{
	ck_rv_t rv;
	int ret;
	struct ck_mechanism mech;
	gnutls_datum_t tmp = { NULL, 0 };
	unsigned long siglen;
	struct pkcs11_session_info _sinfo;
	struct pkcs11_session_info *sinfo;
	ck_object_handle_t obj;

	PKCS11_CHECK_INIT;

	if (key->sinfo.init != 0) {
		sinfo = &key->sinfo;
		obj = key->obj;
	} else {
		sinfo = &_sinfo;
		memset(sinfo, 0, sizeof(*sinfo));
		FIND_OBJECT(sinfo, &key->pin, obj, key);
	}

	mech.mechanism = pk_to_mech(key->pk_algorithm);
	mech.parameter = NULL;
	mech.parameter_len = 0;

	/* Initialize signing operation; using the private key discovered
	 * earlier. */
	rv = pkcs11_sign_init(sinfo->module, sinfo->pks, &mech, obj);
	if (rv != CKR_OK) {
		gnutls_assert();
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	/* Work out how long the signature must be: */
	rv = pkcs11_sign(sinfo->module, sinfo->pks, hash->data, hash->size,
			 NULL, &siglen);
	if (rv != CKR_OK) {
		gnutls_assert();
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	tmp.data = gnutls_malloc(siglen);
	tmp.size = siglen;

	rv = pkcs11_sign(sinfo->module, sinfo->pks, hash->data, hash->size,
			 tmp.data, &siglen);
	if (rv != CKR_OK) {
		gnutls_assert();
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}


	if (key->pk_algorithm == GNUTLS_PK_EC
	    || key->pk_algorithm == GNUTLS_PK_DSA) {
		unsigned int hlen = siglen / 2;
		gnutls_datum_t r, s;

		if (siglen % 2 != 0) {
			gnutls_assert();
			ret = GNUTLS_E_PK_SIGN_FAILED;
			goto cleanup;
		}

		r.data = tmp.data;
		r.size = hlen;

		s.data = &tmp.data[hlen];
		s.size = hlen;

		ret = _gnutls_encode_ber_rs_raw(signature, &r, &s);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		gnutls_free(tmp.data);
		tmp.data = NULL;
	} else {
		signature->size = siglen;
		signature->data = tmp.data;
	}

	ret = 0;

      cleanup:
	if (sinfo != &key->sinfo)
		pkcs11_close_session(sinfo);
	if (ret < 0)
		gnutls_free(tmp.data);

	return ret;
}
Ejemplo n.º 2
0
/*-
 * _gnutls_pkcs11_privkey_sign_hash:
 * @key: Holds the key
 * @hash: holds the data to be signed (should be output of a hash)
 * @signature: will contain the signature allocated with gnutls_malloc()
 *
 * This function will sign the given data using a signature algorithm
 * supported by the private key. It is assumed that the given data
 * are the output of a hash function.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 -*/
int
_gnutls_pkcs11_privkey_sign_hash(gnutls_pkcs11_privkey_t key,
				 const gnutls_datum_t * hash,
				 gnutls_datum_t * signature)
{
	ck_rv_t rv;
	int ret;
	struct ck_mechanism mech;
	gnutls_datum_t tmp = { NULL, 0 };
	unsigned long siglen;
	struct pkcs11_session_info *sinfo;
	unsigned req_login = 0;
	unsigned login_flags = SESSION_LOGIN|SESSION_CONTEXT_SPECIFIC;

	PKCS11_CHECK_INIT_PRIVKEY(key);

	sinfo = &key->sinfo;

	mech.mechanism = pk_to_mech(key->pk_algorithm);
	mech.parameter = NULL;
	mech.parameter_len = 0;

	ret = gnutls_mutex_lock(&key->mutex);
	if (ret != 0)
		return gnutls_assert_val(GNUTLS_E_LOCKING_ERROR);

	/* Initialize signing operation; using the private key discovered
	 * earlier. */
	REPEAT_ON_INVALID_HANDLE(rv = pkcs11_sign_init(sinfo->module, sinfo->pks, &mech, key->ref));
	if (rv != CKR_OK) {
		gnutls_assert();
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

 retry_login:
	if (key->reauth || req_login) {
		if (req_login)
			login_flags = SESSION_LOGIN|SESSION_FORCE_LOGIN;

		ret =
		    pkcs11_login(&key->sinfo, &key->pin,
				 key->uinfo, login_flags);
		if (ret < 0) {
			gnutls_assert();
			_gnutls_debug_log("PKCS #11 login failed, trying operation anyway\n");
			/* let's try the operation anyway */
		}
	}

	/* Work out how long the signature must be: */
	rv = pkcs11_sign(sinfo->module, sinfo->pks, hash->data, hash->size,
			 NULL, &siglen);
	if (unlikely(rv == CKR_USER_NOT_LOGGED_IN && req_login == 0)) {
		req_login = 1;
		goto retry_login;
	}

	if (rv != CKR_OK) {
		gnutls_assert();
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}

	tmp.data = gnutls_malloc(siglen);
	tmp.size = siglen;

	rv = pkcs11_sign(sinfo->module, sinfo->pks, hash->data, hash->size,
			 tmp.data, &siglen);
	if (rv != CKR_OK) {
		gnutls_assert();
		ret = pkcs11_rv_to_err(rv);
		goto cleanup;
	}


	if (key->pk_algorithm == GNUTLS_PK_EC
	    || key->pk_algorithm == GNUTLS_PK_DSA) {
		unsigned int hlen = siglen / 2;
		gnutls_datum_t r, s;

		if (siglen % 2 != 0) {
			gnutls_assert();
			ret = GNUTLS_E_PK_SIGN_FAILED;
			goto cleanup;
		}

		r.data = tmp.data;
		r.size = hlen;

		s.data = &tmp.data[hlen];
		s.size = hlen;

		ret = _gnutls_encode_ber_rs_raw(signature, &r, &s);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

		gnutls_free(tmp.data);
		tmp.data = NULL;
	} else {
		signature->size = siglen;
		signature->data = tmp.data;
	}

	ret = 0;

      cleanup:
	gnutls_mutex_unlock(&key->mutex);
	if (sinfo != &key->sinfo)
		pkcs11_close_session(sinfo);
	if (ret < 0)
		gnutls_free(tmp.data);

	return ret;
}
/*-
 * _gnutls_pkcs11_privkey_sign_hash:
 * @key: Holds the key
 * @hash: holds the data to be signed (should be output of a hash)
 * @signature: will contain the signature allocated with gnutls_malloc()
 *
 * This function will sign the given data using a signature algorithm
 * supported by the private key. It is assumed that the given data
 * are the output of a hash function.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
 *   negative error value.
 -*/
int
_gnutls_pkcs11_privkey_sign_hash (gnutls_pkcs11_privkey_t key,
                                  const gnutls_datum_t * hash,
                                  gnutls_datum_t * signature)
{
  ck_rv_t rv;
  int ret;
  struct ck_mechanism mech;
  unsigned long siglen;
  struct ck_function_list *module;
  ck_session_handle_t pks;
  ck_object_handle_t obj;

  FIND_OBJECT (module, pks, obj, key);

  mech.mechanism = pk_to_mech(key->pk_algorithm);
  mech.parameter = NULL;
  mech.parameter_len = 0;

  /* Initialize signing operation; using the private key discovered
   * earlier. */
  rv = pkcs11_sign_init (module, pks, &mech, obj);
  if (rv != CKR_OK)
    {
      gnutls_assert ();
      ret = pkcs11_rv_to_err (rv);
      goto cleanup;
    }

  /* Work out how long the signature must be: */
  rv = pkcs11_sign (module, pks, hash->data, hash->size, NULL, &siglen);
  if (rv != CKR_OK)
    {
      gnutls_assert ();
      ret = pkcs11_rv_to_err (rv);
      goto cleanup;
    }

  signature->data = gnutls_malloc (siglen);
  signature->size = siglen;

  rv = pkcs11_sign (module, pks, hash->data, hash->size, signature->data, &siglen);
  if (rv != CKR_OK)
    {
      gnutls_free (signature->data);
      gnutls_assert ();
      ret = pkcs11_rv_to_err (rv);
      goto cleanup;
    }

  signature->size = siglen;
  
  if (key->pk_algorithm == GNUTLS_PK_EC || key->pk_algorithm == GNUTLS_PK_DSA)
    {
      bigint_t r,s;

      if (siglen % 2 != 0)
        {
          gnutls_assert();
          ret = GNUTLS_E_PK_SIGN_FAILED;
          goto cleanup;
        }

      ret = read_rs(&r, &s, signature->data, signature->size);
      if (ret < 0)
        {
          gnutls_assert();
          goto cleanup;
        }
      
      gnutls_free(signature->data);
      ret = _gnutls_encode_ber_rs (signature, r, s);
      _gnutls_mpi_release(&r);
      _gnutls_mpi_release(&s);
      
      if (ret < 0)
        {
          gnutls_assert();
          goto cleanup;
        }
    }

  ret = 0;

cleanup:
  pkcs11_close_session (module, pks);

  return ret;
}