<commit_before>/*
 * pkcs11-object.c: PKCS#11 object management and handling functions
 *
 * Copyright (C) 2002  <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <string.h>
#include "sc-pkcs11.h"

/* Pseudo mechanism for the Find operation */
static sc_pkcs11_mechanism_type_t find_mechanism = {
	0, {0}, 0,
	sizeof(struct sc_pkcs11_find_operation),
};

static CK_RV get_object_from_session(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
				     struct sc_pkcs11_session **session,
				     struct sc_pkcs11_object **object)
{
	CK_RV rv;
	struct sc_pkcs11_session *sess;
	rv = get_session(hSession, &sess);
	if (rv != CKR_OK)
		return rv;

	*object = list_seek(&sess->slot->objects, &hObject);
	if (!*object)
		return CKR_OBJECT_HANDLE_INVALID;
	*session = sess;
	return CKR_OK;
}

CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		     CK_ATTRIBUTE_PTR pTemplate,	/* the object's template */
		     CK_ULONG ulCount,	/* attributes in template */
		     CK_OBJECT_HANDLE_PTR phObject)
{				/* receives new object's handle. */
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_card *card;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;
	SC_FUNC_CALLED(context, 4);

	if (pTemplate == NULL_PTR || ulCount == 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}
	dump_template("C_CreateObject()", pTemplate, ulCount);

	session = list_seek(&sessions, &hSession);
	if (!session) {
		rv = CKR_SESSION_HANDLE_INVALID;
		goto out;
	}

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	card = session->slot->card;
	if (card->framework->create_object == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else
		rv = card->framework->create_object(card, session->slot,
				pTemplate, ulCount, phObject);

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_OBJECT_HANDLE hObject,	/* the object's handle */
		   CK_ATTRIBUTE_PTR pTemplate,	/* template for new object */
		   CK_ULONG ulCount,	/* attributes in template */
		   CK_OBJECT_HANDLE_PTR phNewObject)/* receives handle of copy */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_OBJECT_HANDLE hObject)
{				/* the object's handle */
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	sc_debug(context, "C_DestroyObject(hSession=0x%lx, hObject=0x%lx)", hSession, hObject);

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	if (object->ops->destroy_object == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else
		rv = object->ops->destroy_object(session, object);

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_OBJECT_HANDLE hObject,	/* the object's handle */
		      CK_ULONG_PTR pulSize)
{				/* receives size of object */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,	/* the session's handle */
			  CK_OBJECT_HANDLE hObject,	/* the object's handle */
			  CK_ATTRIBUTE_PTR pTemplate,	/* specifies attributes, gets values */
			  CK_ULONG ulCount)
{				/* attributes in template */
	static int precedence[] = {
		CKR_OK,
		CKR_BUFFER_TOO_SMALL,
		CKR_ATTRIBUTE_TYPE_INVALID,
		CKR_ATTRIBUTE_SENSITIVE,
		-1
	};
	char object_name[64];
	int j, rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int res, res_type;
	unsigned int i;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pTemplate == NULL_PTR || ulCount == 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	/* Debug printf */
	snprintf(object_name, sizeof(object_name), "Object %lu", (unsigned long)hObject);

	res_type = 0;
	for (i = 0; i < ulCount; i++) {
		res = object->ops->get_attribute(session, object, &pTemplate[i]);
		if (res != CKR_OK)
			pTemplate[i].ulValueLen = (CK_ULONG) - 1;

		dump_template(object_name, &pTemplate[i], 1);

		/* the pkcs11 spec has complicated rules on
		 * what errors take precedence:
		 *      CKR_ATTRIBUTE_SENSITIVE
		 *      CKR_ATTRIBUTE_INVALID
		 *      CKR_BUFFER_TOO_SMALL
		 * It does not exactly specify how other errors
		 * should be handled - we give them highest
		 * precedence
		 */
		for (j = 0; precedence[j] != -1; j++) {
			if (precedence[j] == res)
				break;
		}
		if (j > res_type) {
			res_type = j;
			rv = res;
		}
	}

out:	sc_debug(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s",
			hSession, hObject, lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,	/* the session's handle */
			  CK_OBJECT_HANDLE hObject,	/* the object's handle */
			  CK_ATTRIBUTE_PTR pTemplate,	/* specifies attributes and values */
			  CK_ULONG ulCount)
{				/* attributes in template */
	int rv;
	unsigned int i;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pTemplate == NULL_PTR || ulCount == 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}
	dump_template("C_SetAttributeValue", pTemplate, ulCount);

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	if (object->ops->set_attribute == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else {
		for (i = 0; i < ulCount; i++) {
			rv = object->ops->set_attribute(session, object, &pTemplate[i]);
			if (rv != CKR_OK)
				break;
		}
	}

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
			CK_ATTRIBUTE_PTR pTemplate,	/* attribute values to match */
			CK_ULONG ulCount)
{				/* attributes in search template */
	CK_BBOOL is_private = TRUE;
	CK_ATTRIBUTE private_attribute = { CKA_PRIVATE, &is_private, sizeof(is_private) };

	int rv, match, hide_private;
	unsigned int i, j;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	struct sc_pkcs11_find_operation *operation;
	struct sc_pkcs11_slot *slot;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pTemplate == NULL_PTR && ulCount > 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	sc_debug(context, "C_FindObjectsInit(slot = %d)\n", session->slot->id);
	dump_template("C_FindObjectsInit()", pTemplate, ulCount);

	rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND,
				     &find_mechanism, (struct sc_pkcs11_operation **)&operation);
	if (rv != CKR_OK)
		goto out;

	operation->current_handle = 0;
	operation->num_handles = 0;
	slot = session->slot;

	/* Check whether we should hide private objects */
	hide_private = 0;
	if (slot->login_user != CKU_USER && (slot->token_info.flags & CKF_LOGIN_REQUIRED))
		hide_private = 1;
        
	/* For each object in token do */
	for (i=0; i<list_size(&slot->objects); i++) {
		object = (struct sc_pkcs11_object *)list_get_at(&slot->objects, i);
		sc_debug(context, "Object with handle 0x%lx", object->handle);

		/* User not logged in and private object? */ 
		if (hide_private) {
			if (object->ops->get_attribute(session, object, &private_attribute) != CKR_OK)
			        continue;
			if (is_private) {
				sc_debug(context,
					 "Object %d/%d: Private object and not logged in.\n",
					 slot->id, object->handle);
				continue;
			}
		}

		/* Try to match every attribute */
		match = 1;
		for (j = 0; j < ulCount; j++) {
			rv = object->ops->cmp_attribute(session, object, &pTemplate[j]);
			if (rv == 0) {
				if (context->debug >= 4) {
					sc_debug(context,
						 "Object %d/%d: Attribute 0x%x does NOT match.\n",
						 slot->id, object->handle, pTemplate[j].type);
				}
				match = 0;
				break;
			}

			if (context->debug >= 4) {
				sc_debug(context, "Object %d/%d: Attribute 0x%x matches.\n",
					 slot->id, object->handle, pTemplate[j].type);
			}
		}

		if (match) {
			sc_debug(context, "Object %d/%d matches\n", slot->id, object->handle);
			/* Avoid buffer overflow --okir */ 
			if (operation->num_handles >= SC_PKCS11_FIND_MAX_HANDLES) {
				sc_debug(context, "Too many matching objects\n");
				break;
			}
			operation->handles[operation->num_handles++] = object->handle;
		}
	}
	rv = CKR_OK;

	sc_debug(context, "%d matching objects\n", operation->num_handles);

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_OBJECT_HANDLE_PTR phObject,	/* receives object handle array */
		    CK_ULONG ulMaxObjectCount,	/* max handles to be returned */
		    CK_ULONG_PTR pulObjectCount)
{				/* actual number returned */
	int rv;
	CK_ULONG to_return;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_find_operation *operation;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (phObject == NULL_PTR || ulMaxObjectCount == 0 || pulObjectCount == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND,
				   (sc_pkcs11_operation_t **) & operation);
	if (rv != CKR_OK)
		goto out;

	to_return = (CK_ULONG) operation->num_handles - operation->current_handle;
	if (to_return > ulMaxObjectCount)
		to_return = ulMaxObjectCount;

	*pulObjectCount = to_return;

	memcpy(phObject,
	       &operation->handles[operation->current_handle],
	       to_return * sizeof(CK_OBJECT_HANDLE));

	operation->current_handle += to_return;

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
{				/* the session's handle */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, NULL);
	if (rv == CKR_OK)
		session_stop_operation(session, SC_PKCS11_OPERATION_FIND);

out:	sc_pkcs11_unlock();
	return rv;
}

/*
 * Below here all functions are wrappers to pass all object attribute and method
 * handling to appropriate object layer.
 */

CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_MECHANISM_PTR pMechanism)
{				/* the digesting mechanism */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pMechanism == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	sc_debug(context, "C_DigestInit(hSession=0x%lx)", hSession);
	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_init(session, pMechanism);

out:	sc_debug(context, "C_DigestInit() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_Digest(CK_SESSION_HANDLE hSession,	/* the session's handle */
	       CK_BYTE_PTR pData,	/* data to be digested */
	       CK_ULONG ulDataLen,	/* bytes of data to be digested */
	       CK_BYTE_PTR pDigest,	/* receives the message digest */
	       CK_ULONG_PTR pulDigestLen)
{				/* receives byte length of digest */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	sc_debug(context, "C_Digest(hSession=0x%lx)", hSession);
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = sc_pkcs11_md_update(session, pData, ulDataLen);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen);

out:	sc_debug(context, "C_Digest() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		     CK_BYTE_PTR pPart,	/* data to be digested */
		     CK_ULONG ulPartLen)
{				/* bytes of data to be digested */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_update(session, pPart, ulPartLen);

	sc_debug(context, "C_DigestUpdate() == %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		  CK_OBJECT_HANDLE hKey)
{				/* handle of secret key to digest */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_BYTE_PTR pDigest,	/* receives the message digest */
		    CK_ULONG_PTR pulDigestLen)
{				/* receives byte count of digest */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen);

	sc_debug(context, "C_DigestFinal() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		 CK_MECHANISM_PTR pMechanism,	/* the signature mechanism */
		 CK_OBJECT_HANDLE hKey)
{				/* handle of the signature key */
	CK_BBOOL can_sign;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE sign_attribute = { CKA_SIGN, &can_sign, sizeof(can_sign) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pMechanism == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->sign == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &sign_attribute);
	if (rv != CKR_OK || !can_sign) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = sc_pkcs11_sign_init(session, pMechanism, object, key_type);

out:	sc_debug(context, "C_SignInit() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_Sign(CK_SESSION_HANDLE hSession,	/* the session's handle */
	     CK_BYTE_PTR pData,	/* the data (digest) to be signed */
	     CK_ULONG ulDataLen,	/* count of bytes to be signed */
	     CK_BYTE_PTR pSignature,	/* receives the signature */
	     CK_ULONG_PTR pulSignatureLen)
{				/* receives byte count of signature */
	int rv;
	struct sc_pkcs11_session *session;
	CK_ULONG length;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* According to the pkcs11 specs, we must not do any calls that
	 * change our crypto state if the caller is just asking for the
	 * signature buffer size, or if the result would be
	 * CKR_BUFFER_TOO_SMALL. Thus we cannot do the sign_update call
	 * below. */
	if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK)
		goto out;

	if (pSignature == NULL || length > *pulSignatureLen) {
		*pulSignatureLen = length;
		rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK;
		goto out;
	}

	rv = sc_pkcs11_sign_update(session, pData, ulDataLen);
	if (rv == CKR_OK)
		rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen);

out:	sc_debug(context, "C_Sign() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_BYTE_PTR pPart,	/* the data (digest) to be signed */
		   CK_ULONG ulPartLen)
{				/* count of bytes to be signed */
	struct sc_pkcs11_session *session;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_sign_update(session, pPart, ulPartLen);

	sc_debug(context, "C_SignUpdate() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		  CK_BYTE_PTR pSignature,	/* receives the signature */
		  CK_ULONG_PTR pulSignatureLen)
{				/* receives byte count of signature */
	struct sc_pkcs11_session *session;
	CK_ULONG length;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* According to the pkcs11 specs, we must not do any calls that
	 * change our crypto state if the caller is just asking for the
	 * signature buffer size, or if the result would be
	 * CKR_BUFFER_TOO_SMALL.
	 */
	if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK)
		goto out;

	if (pSignature == NULL || length > *pulSignatureLen) {
		*pulSignatureLen = length;
		rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK;
	} else {
		rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen);
	}

out:	sc_debug(context, "C_SignFinal() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
			CK_MECHANISM_PTR pMechanism,	/* the signature mechanism */
			CK_OBJECT_HANDLE hKey)
{				/* handle of the signature key */
	CK_BBOOL can_sign;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE sign_attribute = { CKA_SIGN, &can_sign, sizeof(can_sign) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pMechanism == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->sign == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &sign_attribute);
	if (rv != CKR_OK || !can_sign) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);<commit_msg>#47: C_SignRecover is not implemented at the moment, don't give any promises with C_SignRecoverInit either.<commit_after>/*
 * pkcs11-object.c: PKCS#11 object management and handling functions
 *
 * Copyright (C) 2002  <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <string.h>
#include "sc-pkcs11.h"

/* Pseudo mechanism for the Find operation */
static sc_pkcs11_mechanism_type_t find_mechanism = {
	0, {0}, 0,
	sizeof(struct sc_pkcs11_find_operation),
};

static CK_RV get_object_from_session(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
				     struct sc_pkcs11_session **session,
				     struct sc_pkcs11_object **object)
{
	CK_RV rv;
	struct sc_pkcs11_session *sess;
	rv = get_session(hSession, &sess);
	if (rv != CKR_OK)
		return rv;

	*object = list_seek(&sess->slot->objects, &hObject);
	if (!*object)
		return CKR_OBJECT_HANDLE_INVALID;
	*session = sess;
	return CKR_OK;
}

CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		     CK_ATTRIBUTE_PTR pTemplate,	/* the object's template */
		     CK_ULONG ulCount,	/* attributes in template */
		     CK_OBJECT_HANDLE_PTR phObject)
{				/* receives new object's handle. */
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_card *card;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;
	SC_FUNC_CALLED(context, 4);

	if (pTemplate == NULL_PTR || ulCount == 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}
	dump_template("C_CreateObject()", pTemplate, ulCount);

	session = list_seek(&sessions, &hSession);
	if (!session) {
		rv = CKR_SESSION_HANDLE_INVALID;
		goto out;
	}

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	card = session->slot->card;
	if (card->framework->create_object == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else
		rv = card->framework->create_object(card, session->slot,
				pTemplate, ulCount, phObject);

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_OBJECT_HANDLE hObject,	/* the object's handle */
		   CK_ATTRIBUTE_PTR pTemplate,	/* template for new object */
		   CK_ULONG ulCount,	/* attributes in template */
		   CK_OBJECT_HANDLE_PTR phNewObject)/* receives handle of copy */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_OBJECT_HANDLE hObject)
{				/* the object's handle */
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	sc_debug(context, "C_DestroyObject(hSession=0x%lx, hObject=0x%lx)", hSession, hObject);

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	if (object->ops->destroy_object == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else
		rv = object->ops->destroy_object(session, object);

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_OBJECT_HANDLE hObject,	/* the object's handle */
		      CK_ULONG_PTR pulSize)
{				/* receives size of object */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,	/* the session's handle */
			  CK_OBJECT_HANDLE hObject,	/* the object's handle */
			  CK_ATTRIBUTE_PTR pTemplate,	/* specifies attributes, gets values */
			  CK_ULONG ulCount)
{				/* attributes in template */
	static int precedence[] = {
		CKR_OK,
		CKR_BUFFER_TOO_SMALL,
		CKR_ATTRIBUTE_TYPE_INVALID,
		CKR_ATTRIBUTE_SENSITIVE,
		-1
	};
	char object_name[64];
	int j, rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int res, res_type;
	unsigned int i;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pTemplate == NULL_PTR || ulCount == 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	/* Debug printf */
	snprintf(object_name, sizeof(object_name), "Object %lu", (unsigned long)hObject);

	res_type = 0;
	for (i = 0; i < ulCount; i++) {
		res = object->ops->get_attribute(session, object, &pTemplate[i]);
		if (res != CKR_OK)
			pTemplate[i].ulValueLen = (CK_ULONG) - 1;

		dump_template(object_name, &pTemplate[i], 1);

		/* the pkcs11 spec has complicated rules on
		 * what errors take precedence:
		 *      CKR_ATTRIBUTE_SENSITIVE
		 *      CKR_ATTRIBUTE_INVALID
		 *      CKR_BUFFER_TOO_SMALL
		 * It does not exactly specify how other errors
		 * should be handled - we give them highest
		 * precedence
		 */
		for (j = 0; precedence[j] != -1; j++) {
			if (precedence[j] == res)
				break;
		}
		if (j > res_type) {
			res_type = j;
			rv = res;
		}
	}

out:	sc_debug(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s",
			hSession, hObject, lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,	/* the session's handle */
			  CK_OBJECT_HANDLE hObject,	/* the object's handle */
			  CK_ATTRIBUTE_PTR pTemplate,	/* specifies attributes and values */
			  CK_ULONG ulCount)
{				/* attributes in template */
	int rv;
	unsigned int i;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pTemplate == NULL_PTR || ulCount == 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}
	dump_template("C_SetAttributeValue", pTemplate, ulCount);

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	if (object->ops->set_attribute == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else {
		for (i = 0; i < ulCount; i++) {
			rv = object->ops->set_attribute(session, object, &pTemplate[i]);
			if (rv != CKR_OK)
				break;
		}
	}

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
			CK_ATTRIBUTE_PTR pTemplate,	/* attribute values to match */
			CK_ULONG ulCount)
{				/* attributes in search template */
	CK_BBOOL is_private = TRUE;
	CK_ATTRIBUTE private_attribute = { CKA_PRIVATE, &is_private, sizeof(is_private) };

	int rv, match, hide_private;
	unsigned int i, j;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	struct sc_pkcs11_find_operation *operation;
	struct sc_pkcs11_slot *slot;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pTemplate == NULL_PTR && ulCount > 0) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	sc_debug(context, "C_FindObjectsInit(slot = %d)\n", session->slot->id);
	dump_template("C_FindObjectsInit()", pTemplate, ulCount);

	rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND,
				     &find_mechanism, (struct sc_pkcs11_operation **)&operation);
	if (rv != CKR_OK)
		goto out;

	operation->current_handle = 0;
	operation->num_handles = 0;
	slot = session->slot;

	/* Check whether we should hide private objects */
	hide_private = 0;
	if (slot->login_user != CKU_USER && (slot->token_info.flags & CKF_LOGIN_REQUIRED))
		hide_private = 1;
        
	/* For each object in token do */
	for (i=0; i<list_size(&slot->objects); i++) {
		object = (struct sc_pkcs11_object *)list_get_at(&slot->objects, i);
		sc_debug(context, "Object with handle 0x%lx", object->handle);

		/* User not logged in and private object? */ 
		if (hide_private) {
			if (object->ops->get_attribute(session, object, &private_attribute) != CKR_OK)
			        continue;
			if (is_private) {
				sc_debug(context,
					 "Object %d/%d: Private object and not logged in.\n",
					 slot->id, object->handle);
				continue;
			}
		}

		/* Try to match every attribute */
		match = 1;
		for (j = 0; j < ulCount; j++) {
			rv = object->ops->cmp_attribute(session, object, &pTemplate[j]);
			if (rv == 0) {
				if (context->debug >= 4) {
					sc_debug(context,
						 "Object %d/%d: Attribute 0x%x does NOT match.\n",
						 slot->id, object->handle, pTemplate[j].type);
				}
				match = 0;
				break;
			}

			if (context->debug >= 4) {
				sc_debug(context, "Object %d/%d: Attribute 0x%x matches.\n",
					 slot->id, object->handle, pTemplate[j].type);
			}
		}

		if (match) {
			sc_debug(context, "Object %d/%d matches\n", slot->id, object->handle);
			/* Avoid buffer overflow --okir */ 
			if (operation->num_handles >= SC_PKCS11_FIND_MAX_HANDLES) {
				sc_debug(context, "Too many matching objects\n");
				break;
			}
			operation->handles[operation->num_handles++] = object->handle;
		}
	}
	rv = CKR_OK;

	sc_debug(context, "%d matching objects\n", operation->num_handles);

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_OBJECT_HANDLE_PTR phObject,	/* receives object handle array */
		    CK_ULONG ulMaxObjectCount,	/* max handles to be returned */
		    CK_ULONG_PTR pulObjectCount)
{				/* actual number returned */
	int rv;
	CK_ULONG to_return;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_find_operation *operation;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (phObject == NULL_PTR || ulMaxObjectCount == 0 || pulObjectCount == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND,
				   (sc_pkcs11_operation_t **) & operation);
	if (rv != CKR_OK)
		goto out;

	to_return = (CK_ULONG) operation->num_handles - operation->current_handle;
	if (to_return > ulMaxObjectCount)
		to_return = ulMaxObjectCount;

	*pulObjectCount = to_return;

	memcpy(phObject,
	       &operation->handles[operation->current_handle],
	       to_return * sizeof(CK_OBJECT_HANDLE));

	operation->current_handle += to_return;

out:	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
{				/* the session's handle */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, NULL);
	if (rv == CKR_OK)
		session_stop_operation(session, SC_PKCS11_OPERATION_FIND);

out:	sc_pkcs11_unlock();
	return rv;
}

/*
 * Below here all functions are wrappers to pass all object attribute and method
 * handling to appropriate object layer.
 */

CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_MECHANISM_PTR pMechanism)
{				/* the digesting mechanism */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pMechanism == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	sc_debug(context, "C_DigestInit(hSession=0x%lx)", hSession);
	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_init(session, pMechanism);

out:	sc_debug(context, "C_DigestInit() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_Digest(CK_SESSION_HANDLE hSession,	/* the session's handle */
	       CK_BYTE_PTR pData,	/* data to be digested */
	       CK_ULONG ulDataLen,	/* bytes of data to be digested */
	       CK_BYTE_PTR pDigest,	/* receives the message digest */
	       CK_ULONG_PTR pulDigestLen)
{				/* receives byte length of digest */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	sc_debug(context, "C_Digest(hSession=0x%lx)", hSession);
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = sc_pkcs11_md_update(session, pData, ulDataLen);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen);

out:	sc_debug(context, "C_Digest() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		     CK_BYTE_PTR pPart,	/* data to be digested */
		     CK_ULONG ulPartLen)
{				/* bytes of data to be digested */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_update(session, pPart, ulPartLen);

	sc_debug(context, "C_DigestUpdate() == %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		  CK_OBJECT_HANDLE hKey)
{				/* handle of secret key to digest */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_BYTE_PTR pDigest,	/* receives the message digest */
		    CK_ULONG_PTR pulDigestLen)
{				/* receives byte count of digest */
	int rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen);

	sc_debug(context, "C_DigestFinal() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		 CK_MECHANISM_PTR pMechanism,	/* the signature mechanism */
		 CK_OBJECT_HANDLE hKey)
{				/* handle of the signature key */
	CK_BBOOL can_sign;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE sign_attribute = { CKA_SIGN, &can_sign, sizeof(can_sign) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pMechanism == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->sign == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &sign_attribute);
	if (rv != CKR_OK || !can_sign) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = sc_pkcs11_sign_init(session, pMechanism, object, key_type);

out:	sc_debug(context, "C_SignInit() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_Sign(CK_SESSION_HANDLE hSession,	/* the session's handle */
	     CK_BYTE_PTR pData,	/* the data (digest) to be signed */
	     CK_ULONG ulDataLen,	/* count of bytes to be signed */
	     CK_BYTE_PTR pSignature,	/* receives the signature */
	     CK_ULONG_PTR pulSignatureLen)
{				/* receives byte count of signature */
	int rv;
	struct sc_pkcs11_session *session;
	CK_ULONG length;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* According to the pkcs11 specs, we must not do any calls that
	 * change our crypto state if the caller is just asking for the
	 * signature buffer size, or if the result would be
	 * CKR_BUFFER_TOO_SMALL. Thus we cannot do the sign_update call
	 * below. */
	if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK)
		goto out;

	if (pSignature == NULL || length > *pulSignatureLen) {
		*pulSignatureLen = length;
		rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK;
		goto out;
	}

	rv = sc_pkcs11_sign_update(session, pData, ulDataLen);
	if (rv == CKR_OK)
		rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen);

out:	sc_debug(context, "C_Sign() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_BYTE_PTR pPart,	/* the data (digest) to be signed */
		   CK_ULONG ulPartLen)
{				/* count of bytes to be signed */
	struct sc_pkcs11_session *session;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_sign_update(session, pPart, ulPartLen);

	sc_debug(context, "C_SignUpdate() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		  CK_BYTE_PTR pSignature,	/* receives the signature */
		  CK_ULONG_PTR pulSignatureLen)
{				/* receives byte count of signature */
	struct sc_pkcs11_session *session;
	CK_ULONG length;
	int rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* According to the pkcs11 specs, we must not do any calls that
	 * change our crypto state if the caller is just asking for the
	 * signature buffer size, or if the result would be
	 * CKR_BUFFER_TOO_SMALL.
	 */
	if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK)
		goto out;

	if (pSignature == NULL || length > *pulSignatureLen) {
		*pulSignatureLen = length;
		rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK;
	} else {
		rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen);
	}

out:	sc_debug(context, "C_SignFinal() = %s", lookup_enum ( RV_T, rv ));
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
			CK_MECHANISM_PTR pMechanism,	/* the signature mechanism */
			CK_OBJECT_HANDLE hKey)
{				/* handle of the signature key */
	CK_BBOOL can_sign;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE sign_attribute = { CKA_SIGN, &can_sign, sizeof(can_sign) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	int rv;

	/* FIXME #47: C_SignRecover is not implemented */
	return CKR_FUNCTION_NOT_SUPPORTED;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	if (pMechanism == NULL_PTR) {
		rv = CKR_ARGUMENTS_BAD;
		goto out;
	}

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->sign == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &sign_attribute);
	if (rv != CKR_OK || !can_sign) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
{'fraction_non_alphanumeric': 0.11913206759858126, 'fraction_numerical': 0.012361777592322137, 'mean_word_length': 4.028852459016393, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 165, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5419096', 'n_tokens_mistral': 15828, 'n_tokens_neox': 14269, 'n_words': 3579}