// Copyright 2013 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run const_generate.go

// Package pkcs11 is a wrapper around the PKCS#11 cryptographic library.
package pkcs11

// It is *assumed*, that:
//
// * Go's uint size == PKCS11's CK_ULONG size
// * CK_ULONG never overflows an Go int

/*
#cgo windows CFLAGS: -DPACKED_STRUCTURES
#cgo linux LDFLAGS: -ldl
#cgo darwin LDFLAGS: -ldl
#cgo openbsd LDFLAGS:
#cgo freebsd LDFLAGS: -ldl

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "pkcs11go.h"

#ifdef _WIN32
#include <windows.h>

struct ctx {
	HMODULE handle;
	CK_FUNCTION_LIST_PTR sym;
};

// New initializes a ctx and fills the symbol table.
struct ctx *New(const char *module)
{
	CK_C_GetFunctionList list;
	struct ctx *c = calloc(1, sizeof(struct ctx));
	c->handle = LoadLibrary(module);
	if (c->handle == NULL) {
		free(c);
		return NULL;
	}
	list = (CK_C_GetFunctionList) GetProcAddress(c->handle, "C_GetFunctionList");
	if (list == NULL) {
		free(c);
		return NULL;
	}
	list(&c->sym);
	return c;
}

// Destroy cleans up a ctx.
void Destroy(struct ctx *c)
{
	if (!c) {
		return;
	}
	free(c);
}
#else
#include <dlfcn.h>

struct ctx {
	void *handle;
	CK_FUNCTION_LIST_PTR sym;
};

// New initializes a ctx and fills the symbol table.
struct ctx *New(const char *module)
{
	CK_C_GetFunctionList list;
	struct ctx *c = calloc(1, sizeof(struct ctx));
	c->handle = dlopen(module, RTLD_LAZY);
	if (c->handle == NULL) {
		free(c);
		return NULL;
	}
	list = (CK_C_GetFunctionList) dlsym(c->handle, "C_GetFunctionList");
	if (list == NULL) {
		free(c);
		return NULL;
	}
	list(&c->sym);
	return c;
}

// Destroy cleans up a ctx.
void Destroy(struct ctx *c)
{
	if (!c) {
		return;
	}
	if (c->handle == NULL) {
		return;
	}
	if (dlclose(c->handle) < 0) {
		return;
	}
	free(c);
}
#endif

CK_RV Initialize(struct ctx * c)
{
	CK_C_INITIALIZE_ARGS args;
	memset(&args, 0, sizeof(args));
	args.flags = CKF_OS_LOCKING_OK;
	return c->sym->C_Initialize(&args);
}

CK_RV Finalize(struct ctx * c)
{
	return c->sym->C_Finalize(NULL);
}

CK_RV GetInfo(struct ctx * c, ckInfoPtr info)
{
	CK_INFO p;
	CK_RV e = c->sym->C_GetInfo(&p);
	if (e != CKR_OK) {
		return e;
	}
	info->cryptokiVersion = p.cryptokiVersion;
	memcpy(info->manufacturerID, p.manufacturerID, sizeof(p.manufacturerID));
	info->flags = p.flags;
	memcpy(info->libraryDescription, p.libraryDescription, sizeof(p.libraryDescription));
	info->libraryVersion = p.libraryVersion;
	return e;
}

CK_RV GetSlotList(struct ctx * c, CK_BBOOL tokenPresent,
		  CK_ULONG_PTR * slotList, CK_ULONG_PTR ulCount)
{
	CK_RV e = c->sym->C_GetSlotList(tokenPresent, NULL, ulCount);
	if (e != CKR_OK) {
		return e;
	}
	*slotList = calloc(*ulCount, sizeof(CK_SLOT_ID));
	e = c->sym->C_GetSlotList(tokenPresent, *slotList, ulCount);
	return e;
}

CK_RV GetSlotInfo(struct ctx * c, CK_ULONG slotID, CK_SLOT_INFO_PTR info)
{
	CK_RV e = c->sym->C_GetSlotInfo((CK_SLOT_ID) slotID, info);
	return e;
}

CK_RV GetTokenInfo(struct ctx * c, CK_ULONG slotID, CK_TOKEN_INFO_PTR info)
{
	CK_RV e = c->sym->C_GetTokenInfo((CK_SLOT_ID) slotID, info);
	return e;
}

CK_RV GetMechanismList(struct ctx * c, CK_ULONG slotID,
		       CK_ULONG_PTR * mech, CK_ULONG_PTR mechlen)
{
	CK_RV e =
	    c->sym->C_GetMechanismList((CK_SLOT_ID) slotID, NULL, mechlen);
	// Gemaltos PKCS11 implementation returns CKR_BUFFER_TOO_SMALL on a NULL ptr instad of CKR_OK as the spec states.
	if (e != CKR_OK && e != CKR_BUFFER_TOO_SMALL) {
		return e;
	}
	*mech = calloc(*mechlen, sizeof(CK_MECHANISM_TYPE));
	e = c->sym->C_GetMechanismList((CK_SLOT_ID) slotID,
				       (CK_MECHANISM_TYPE_PTR) * mech, mechlen);
	return e;
}

CK_RV GetMechanismInfo(struct ctx * c, CK_ULONG slotID, CK_MECHANISM_TYPE mech,
		       CK_MECHANISM_INFO_PTR info)
{
	CK_RV e = c->sym->C_GetMechanismInfo((CK_SLOT_ID) slotID, mech, info);
	return e;
}

CK_RV InitToken(struct ctx * c, CK_ULONG slotID, char *pin, CK_ULONG pinlen,
		char *label)
{
	CK_RV e =
	    c->sym->C_InitToken((CK_SLOT_ID) slotID, (CK_UTF8CHAR_PTR) pin,
				pinlen, (CK_UTF8CHAR_PTR) label);
	return e;
}

CK_RV InitPIN(struct ctx * c, CK_SESSION_HANDLE sh, char *pin, CK_ULONG pinlen)
{
	CK_RV e = c->sym->C_InitPIN(sh, (CK_UTF8CHAR_PTR) pin, pinlen);
	return e;
}

CK_RV SetPIN(struct ctx * c, CK_SESSION_HANDLE sh, char *oldpin,
	     CK_ULONG oldpinlen, char *newpin, CK_ULONG newpinlen)
{
	CK_RV e = c->sym->C_SetPIN(sh, (CK_UTF8CHAR_PTR) oldpin, oldpinlen,
				   (CK_UTF8CHAR_PTR) newpin, newpinlen);
	return e;
}

CK_RV OpenSession(struct ctx * c, CK_ULONG slotID, CK_ULONG flags,
		  CK_SESSION_HANDLE_PTR session)
{
	CK_RV e =
	    c->sym->C_OpenSession((CK_SLOT_ID) slotID, (CK_FLAGS) flags, NULL,
				  NULL, session);
	return e;
}

CK_RV CloseSession(struct ctx * c, CK_SESSION_HANDLE session)
{
	CK_RV e = c->sym->C_CloseSession(session);
	return e;
}

CK_RV CloseAllSessions(struct ctx * c, CK_ULONG slotID)
{
	CK_RV e = c->sym->C_CloseAllSessions(slotID);
	return e;
}

CK_RV GetSessionInfo(struct ctx * c, CK_SESSION_HANDLE session,
		     CK_SESSION_INFO_PTR info)
{
	CK_RV e = c->sym->C_GetSessionInfo(session, info);
	return e;
}

CK_RV GetOperationState(struct ctx * c, CK_SESSION_HANDLE session,
			CK_BYTE_PTR * state, CK_ULONG_PTR statelen)
{
	CK_RV rv = c->sym->C_GetOperationState(session, NULL, statelen);
	if (rv != CKR_OK) {
		return rv;
	}
	*state = calloc(*statelen, sizeof(CK_BYTE));
	if (*state == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_GetOperationState(session, *state, statelen);
	return rv;
}

CK_RV SetOperationState(struct ctx * c, CK_SESSION_HANDLE session,
			CK_BYTE_PTR state, CK_ULONG statelen,
			CK_OBJECT_HANDLE encryptkey, CK_OBJECT_HANDLE authkey)
{
	return c->sym->C_SetOperationState(session, state, statelen, encryptkey,
					   authkey);
}

CK_RV Login(struct ctx *c, CK_SESSION_HANDLE session, CK_USER_TYPE userType,
	    char *pin, CK_ULONG pinLen)
{
	if (pinLen == 0) {
		pin = NULL;
	}
	CK_RV e =
	    c->sym->C_Login(session, userType, (CK_UTF8CHAR_PTR) pin, pinLen);
	return e;
}

CK_RV Logout(struct ctx * c, CK_SESSION_HANDLE session)
{
	CK_RV e = c->sym->C_Logout(session);
	return e;
}

CK_RV CreateObject(struct ctx * c, CK_SESSION_HANDLE session,
		   CK_ATTRIBUTE_PTR temp, CK_ULONG tempCount,
		   CK_OBJECT_HANDLE_PTR obj)
{
	return c->sym->C_CreateObject(session, temp, tempCount, obj);
}

CK_RV CopyObject(struct ctx * c, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE o,
		 CK_ATTRIBUTE_PTR temp, CK_ULONG tempCount,
		 CK_OBJECT_HANDLE_PTR obj)
{
	return c->sym->C_CopyObject(session, o, temp, tempCount, obj);
}

CK_RV DestroyObject(struct ctx * c, CK_SESSION_HANDLE session,
		    CK_OBJECT_HANDLE object)
{
	CK_RV e = c->sym->C_DestroyObject(session, object);
	return e;
}

CK_RV GetObjectSize(struct ctx * c, CK_SESSION_HANDLE session,
		    CK_OBJECT_HANDLE object, CK_ULONG_PTR size)
{
	CK_RV e = c->sym->C_GetObjectSize(session, object, size);
	return e;
}

CK_RV GetAttributeValue(struct ctx * c, CK_SESSION_HANDLE session,
			CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR temp,
			CK_ULONG templen)
{
	// Call for the first time, check the returned ulValue in the attributes, then
	// allocate enough space and try again.
	CK_RV e = c->sym->C_GetAttributeValue(session, object, temp, templen);
	if (e != CKR_OK) {
		return e;
	}
	CK_ULONG i;
	for (i = 0; i < templen; i++) {
		if ((CK_LONG) temp[i].ulValueLen == -1) {
			// either access denied or no such object
			continue;
		}
		temp[i].pValue = calloc(temp[i].ulValueLen, sizeof(CK_BYTE));
	}
	return c->sym->C_GetAttributeValue(session, object, temp, templen);
}

CK_RV SetAttributeValue(struct ctx * c, CK_SESSION_HANDLE session,
			CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR temp,
			CK_ULONG templen)
{
	return c->sym->C_SetAttributeValue(session, object, temp, templen);
}

CK_RV FindObjectsInit(struct ctx * c, CK_SESSION_HANDLE session,
		      CK_ATTRIBUTE_PTR temp, CK_ULONG tempCount)
{
	return c->sym->C_FindObjectsInit(session, temp, tempCount);
}

CK_RV FindObjects(struct ctx * c, CK_SESSION_HANDLE session,
		  CK_OBJECT_HANDLE_PTR * obj, CK_ULONG max,
		  CK_ULONG_PTR objCount)
{
	*obj = calloc(max, sizeof(CK_OBJECT_HANDLE));
	CK_RV e = c->sym->C_FindObjects(session, *obj, max, objCount);
	return e;
}

CK_RV FindObjectsFinal(struct ctx * c, CK_SESSION_HANDLE session)
{
	CK_RV e = c->sym->C_FindObjectsFinal(session);
	return e;
}

CK_RV EncryptInit(struct ctx * c, CK_SESSION_HANDLE session,
		  CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key)
{
	return c->sym->C_EncryptInit(session, mechanism, key);
}

CK_RV Encrypt(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR message,
	      CK_ULONG mlen, CK_BYTE_PTR * enc, CK_ULONG_PTR enclen)
{
	CK_RV rv = c->sym->C_Encrypt(session, message, mlen, NULL, enclen);
	if (rv != CKR_OK) {
		return rv;
	}
	*enc = calloc(*enclen, sizeof(CK_BYTE));
	if (*enc == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_Encrypt(session, message, mlen, *enc, enclen);
	return rv;
}

CK_RV EncryptUpdate(struct ctx * c, CK_SESSION_HANDLE session,
		    CK_BYTE_PTR plain, CK_ULONG plainlen, CK_BYTE_PTR * cipher,
		    CK_ULONG_PTR cipherlen)
{
	CK_RV rv =
	    c->sym->C_EncryptUpdate(session, plain, plainlen, NULL, cipherlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*cipher = calloc(*cipherlen, sizeof(CK_BYTE));
	if (*cipher == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_EncryptUpdate(session, plain, plainlen, *cipher,
				     cipherlen);
	return rv;
}

CK_RV EncryptFinal(struct ctx * c, CK_SESSION_HANDLE session,
		   CK_BYTE_PTR * cipher, CK_ULONG_PTR cipherlen)
{
	CK_RV rv = c->sym->C_EncryptFinal(session, NULL, cipherlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*cipher = calloc(*cipherlen, sizeof(CK_BYTE));
	if (*cipher == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_EncryptFinal(session, *cipher, cipherlen);
	return rv;
}

CK_RV DecryptInit(struct ctx * c, CK_SESSION_HANDLE session,
		  CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key)
{
	return c->sym->C_DecryptInit(session, mechanism, key);
}

CK_RV Decrypt(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR cipher,
	      CK_ULONG clen, CK_BYTE_PTR * plain, CK_ULONG_PTR plainlen)
{
	CK_RV e = c->sym->C_Decrypt(session, cipher, clen, NULL, plainlen);
	if (e != CKR_OK) {
		return e;
	}
	*plain = calloc(*plainlen, sizeof(CK_BYTE));
	if (*plain == NULL) {
		return CKR_HOST_MEMORY;
	}
	e = c->sym->C_Decrypt(session, cipher, clen, *plain, plainlen);
	return e;
}

CK_RV DecryptUpdate(struct ctx * c, CK_SESSION_HANDLE session,
		    CK_BYTE_PTR cipher, CK_ULONG cipherlen, CK_BYTE_PTR * part,
		    CK_ULONG_PTR partlen)
{
	CK_RV rv =
	    c->sym->C_DecryptUpdate(session, cipher, cipherlen, NULL, partlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*part = calloc(*partlen, sizeof(CK_BYTE));
	if (*part == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_DecryptUpdate(session, cipher, cipherlen, *part,
				     partlen);
	return rv;
}

CK_RV DecryptFinal(struct ctx * c, CK_SESSION_HANDLE session,
		   CK_BYTE_PTR * plain, CK_ULONG_PTR plainlen)
{
	CK_RV rv = c->sym->C_DecryptFinal(session, NULL, plainlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*plain = calloc(*plainlen, sizeof(CK_BYTE));
	if (*plain == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_DecryptFinal(session, *plain, plainlen);
	return rv;
}

CK_RV DigestInit(struct ctx * c, CK_SESSION_HANDLE session,
		 CK_MECHANISM_PTR mechanism)
{
	return c->sym->C_DigestInit(session, mechanism);
}

CK_RV Digest(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR message,
	     CK_ULONG mlen, CK_BYTE_PTR * hash, CK_ULONG_PTR hashlen)
{
	CK_RV rv = c->sym->C_Digest(session, message, mlen, NULL, hashlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*hash = calloc(*hashlen, sizeof(CK_BYTE));
	if (*hash == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_Digest(session, message, mlen, *hash, hashlen);
	return rv;
}

CK_RV DigestUpdate(struct ctx * c, CK_SESSION_HANDLE session,
		   CK_BYTE_PTR message, CK_ULONG mlen)
{
	CK_RV rv = c->sym->C_DigestUpdate(session, message, mlen);
	return rv;
}

CK_RV DigestKey(struct ctx * c, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key)
{
	CK_RV rv = c->sym->C_DigestKey(session, key);
	return rv;
}

CK_RV DigestFinal(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR * hash,
		  CK_ULONG_PTR hashlen)
{
	CK_RV rv = c->sym->C_DigestFinal(session, NULL, hashlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*hash = calloc(*hashlen, sizeof(CK_BYTE));
	if (*hash == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_DigestFinal(session, *hash, hashlen);
	return rv;
}

CK_RV SignInit(struct ctx * c, CK_SESSION_HANDLE session,
	       CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key)
{
	return c->sym->C_SignInit(session, mechanism, key);
}

CK_RV Sign(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR message,
	   CK_ULONG mlen, CK_BYTE_PTR * sig, CK_ULONG_PTR siglen)
{
	CK_RV rv = c->sym->C_Sign(session, message, mlen, NULL, siglen);
	if (rv != CKR_OK) {
		return rv;
	}
	*sig = calloc(*siglen, sizeof(CK_BYTE));
	if (*sig == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_Sign(session, message, mlen, *sig, siglen);
	return rv;
}

CK_RV SignUpdate(struct ctx * c, CK_SESSION_HANDLE session,
		 CK_BYTE_PTR message, CK_ULONG mlen)
{
	CK_RV rv = c->sym->C_SignUpdate(session, message, mlen);
	return rv;
}

CK_RV SignFinal(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR * sig,
		CK_ULONG_PTR siglen)
{
	CK_RV rv = c->sym->C_SignFinal(session, NULL, siglen);
	if (rv != CKR_OK) {
		return rv;
	}
	*sig = calloc(*siglen, sizeof(CK_BYTE));
	if (*sig == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_SignFinal(session, *sig, siglen);
	return rv;
}

CK_RV SignRecoverInit(struct ctx * c, CK_SESSION_HANDLE session,
		      CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key)
{
	return c->sym->C_SignRecoverInit(session, mechanism, key);
}

CK_RV SignRecover(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR data,
		  CK_ULONG datalen, CK_BYTE_PTR * sig, CK_ULONG_PTR siglen)
{
	CK_RV rv = c->sym->C_SignRecover(session, data, datalen, NULL, siglen);
	if (rv != CKR_OK) {
		return rv;
	}
	*sig = calloc(*siglen, sizeof(CK_BYTE));
	if (*sig == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_SignRecover(session, data, datalen, *sig, siglen);
	return rv;
}

CK_RV VerifyInit(struct ctx * c, CK_SESSION_HANDLE session,
		 CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key)
{
	return c->sym->C_VerifyInit(session, mechanism, key);
}

CK_RV Verify(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR message,
	     CK_ULONG mesglen, CK_BYTE_PTR sig, CK_ULONG siglen)
{
	CK_RV rv = c->sym->C_Verify(session, message, mesglen, sig, siglen);
	return rv;
}

CK_RV VerifyUpdate(struct ctx * c, CK_SESSION_HANDLE session,
		   CK_BYTE_PTR part, CK_ULONG partlen)
{
	CK_RV rv = c->sym->C_VerifyUpdate(session, part, partlen);
	return rv;
}

CK_RV VerifyFinal(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR sig,
		  CK_ULONG siglen)
{
	CK_RV rv = c->sym->C_VerifyFinal(session, sig, siglen);
	return rv;
}

CK_RV VerifyRecoverInit(struct ctx * c, CK_SESSION_HANDLE session,
			CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key)
{
	return c->sym->C_VerifyRecoverInit(session, mechanism, key);
}

CK_RV VerifyRecover(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR sig,
		    CK_ULONG siglen, CK_BYTE_PTR * data, CK_ULONG_PTR datalen)
{
	CK_RV rv = c->sym->C_VerifyRecover(session, sig, siglen, NULL, datalen);
	if (rv != CKR_OK) {
		return rv;
	}
	*data = calloc(*datalen, sizeof(CK_BYTE));
	if (*data == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_VerifyRecover(session, sig, siglen, *data, datalen);
	return rv;
}

CK_RV DigestEncryptUpdate(struct ctx * c, CK_SESSION_HANDLE session,
			  CK_BYTE_PTR part, CK_ULONG partlen, CK_BYTE_PTR * enc,
			  CK_ULONG_PTR enclen)
{
	CK_RV rv =
	    c->sym->C_DigestEncryptUpdate(session, part, partlen, NULL, enclen);
	if (rv != CKR_OK) {
		return rv;
	}
	*enc = calloc(*enclen, sizeof(CK_BYTE));
	if (*enc == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_DigestEncryptUpdate(session, part, partlen, *enc,
					   enclen);
	return rv;
}

CK_RV DecryptDigestUpdate(struct ctx * c, CK_SESSION_HANDLE session,
			  CK_BYTE_PTR cipher, CK_ULONG cipherlen,
			  CK_BYTE_PTR * part, CK_ULONG_PTR partlen)
{
	CK_RV rv =
	    c->sym->C_DecryptDigestUpdate(session, cipher, cipherlen, NULL,
					  partlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*part = calloc(*partlen, sizeof(CK_BYTE));
	if (*part == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_DecryptDigestUpdate(session, cipher, cipherlen, *part,
					   partlen);
	return rv;
}

CK_RV SignEncryptUpdate(struct ctx * c, CK_SESSION_HANDLE session,
			CK_BYTE_PTR part, CK_ULONG partlen, CK_BYTE_PTR * enc,
			CK_ULONG_PTR enclen)
{
	CK_RV rv =
	    c->sym->C_SignEncryptUpdate(session, part, partlen, NULL, enclen);
	if (rv != CKR_OK) {
		return rv;
	}
	*enc = calloc(*enclen, sizeof(CK_BYTE));
	if (*enc == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_SignEncryptUpdate(session, part, partlen, *enc, enclen);
	return rv;
}

CK_RV DecryptVerifyUpdate(struct ctx * c, CK_SESSION_HANDLE session,
			  CK_BYTE_PTR cipher, CK_ULONG cipherlen,
			  CK_BYTE_PTR * part, CK_ULONG_PTR partlen)
{
	CK_RV rv =
	    c->sym->C_DecryptVerifyUpdate(session, cipher, cipherlen, NULL,
					  partlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*part = calloc(*partlen, sizeof(CK_BYTE));
	if (*part == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_DecryptVerifyUpdate(session, cipher, cipherlen, *part,
					   partlen);
	return rv;
}

CK_RV GenerateKey(struct ctx * c, CK_SESSION_HANDLE session,
		  CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR temp,
		  CK_ULONG tempCount, CK_OBJECT_HANDLE_PTR key)
{
	return c->sym->C_GenerateKey(session, mechanism, temp, tempCount, key);
}

CK_RV GenerateKeyPair(struct ctx * c, CK_SESSION_HANDLE session,
		      CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR pub,
		      CK_ULONG pubCount, CK_ATTRIBUTE_PTR priv,
		      CK_ULONG privCount, CK_OBJECT_HANDLE_PTR pubkey,
		      CK_OBJECT_HANDLE_PTR privkey)
{
	return c->sym->C_GenerateKeyPair(session, mechanism, pub, pubCount,
		priv, privCount, pubkey, privkey);
}

CK_RV WrapKey(struct ctx * c, CK_SESSION_HANDLE session,
	      CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE wrappingkey,
	      CK_OBJECT_HANDLE key, CK_BYTE_PTR * wrapped,
	      CK_ULONG_PTR wrappedlen)
{
	CK_RV rv = c->sym->C_WrapKey(session, mechanism, wrappingkey, key, NULL,
				     wrappedlen);
	if (rv != CKR_OK) {
		return rv;
	}
	*wrapped = calloc(*wrappedlen, sizeof(CK_BYTE));
	if (*wrapped == NULL) {
		return CKR_HOST_MEMORY;
	}
	rv = c->sym->C_WrapKey(session, mechanism, wrappingkey, key, *wrapped,
			       wrappedlen);
	return rv;
}

CK_RV DeriveKey(struct ctx * c, CK_SESSION_HANDLE session,
		CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE basekey,
		CK_ATTRIBUTE_PTR a, CK_ULONG alen, CK_OBJECT_HANDLE_PTR key)
{
	return c->sym->C_DeriveKey(session, mechanism, basekey, a, alen, key);
}

CK_RV UnwrapKey(struct ctx * c, CK_SESSION_HANDLE session,
		CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE unwrappingkey,
		CK_BYTE_PTR wrappedkey, CK_ULONG wrappedkeylen,
		CK_ATTRIBUTE_PTR a, CK_ULONG alen, CK_OBJECT_HANDLE_PTR key)
{
	return c->sym->C_UnwrapKey(session, mechanism, unwrappingkey, wrappedkey,
				      wrappedkeylen, a, alen, key);
}

CK_RV SeedRandom(struct ctx * c, CK_SESSION_HANDLE session, CK_BYTE_PTR seed,
		 CK_ULONG seedlen)
{
	CK_RV e = c->sym->C_SeedRandom(session, seed, seedlen);
	return e;
}

CK_RV GenerateRandom(struct ctx * c, CK_SESSION_HANDLE session,
		     CK_BYTE_PTR * rand, CK_ULONG length)
{
	*rand = calloc(length, sizeof(CK_BYTE));
	if (*rand == NULL) {
		return CKR_HOST_MEMORY;
	}
	CK_RV e = c->sym->C_GenerateRandom(session, *rand, length);
	return e;
}

CK_RV WaitForSlotEvent(struct ctx * c, CK_FLAGS flags, CK_ULONG_PTR slot)
{
	CK_RV e =
	    c->sym->C_WaitForSlotEvent(flags, (CK_SLOT_ID_PTR) slot, NULL);
	return e;
}

static inline CK_VOID_PTR getAttributePval(CK_ATTRIBUTE_PTR a)
{
	return a->pValue;
}

*/
import "C"
import (
	"strings"
	"unsafe"
)

// Ctx contains the current pkcs11 context.
type Ctx struct {
	ctx *C.struct_ctx
}

// New creates a new context and initializes the module/library for use.
func New(module string) *Ctx {
	c := new(Ctx)
	mod := C.CString(module)
	defer C.free(unsafe.Pointer(mod))
	c.ctx = C.New(mod)
	if c.ctx == nil {
		return nil
	}
	return c
}

// Destroy unloads the module/library and frees any remaining memory.
func (c *Ctx) Destroy() {
	if c == nil || c.ctx == nil {
		return
	}
	C.Destroy(c.ctx)
	c.ctx = nil
}

// Initialize initializes the Cryptoki library.
func (c *Ctx) Initialize() error {
	e := C.Initialize(c.ctx)
	return toError(e)
}

// Finalize indicates that an application is done with the Cryptoki library.
func (c *Ctx) Finalize() error {
	if c.ctx == nil {
		return toError(CKR_CRYPTOKI_NOT_INITIALIZED)
	}
	e := C.Finalize(c.ctx)
	return toError(e)
}

// GetInfo returns general information about Cryptoki.
func (c *Ctx) GetInfo() (Info, error) {
	var p C.ckInfo
	e := C.GetInfo(c.ctx, &p)
	i := Info{
		CryptokiVersion:    toVersion(p.cryptokiVersion),
		ManufacturerID:     strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&p.manufacturerID[0]), 32)), " "),
		Flags:              uint(p.flags),
		LibraryDescription: strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&p.libraryDescription[0]), 32)), " "),
		LibraryVersion:     toVersion(p.libraryVersion),
	}
	return i, toError(e)
}

// GetSlotList obtains a list of slots in the system.
func (c *Ctx) GetSlotList(tokenPresent bool) ([]uint, error) {
	var (
		slotList C.CK_ULONG_PTR
		ulCount  C.CK_ULONG
	)
	e := C.GetSlotList(c.ctx, cBBool(tokenPresent), &slotList, &ulCount)
	if toError(e) != nil {
		return nil, toError(e)
	}
	l := toList(slotList, ulCount)
	return l, nil
}

// GetSlotInfo obtains information about a particular slot in the system.
func (c *Ctx) GetSlotInfo(slotID uint) (SlotInfo, error) {
	var csi C.CK_SLOT_INFO
	e := C.GetSlotInfo(c.ctx, C.CK_ULONG(slotID), &csi)
	s := SlotInfo{
		SlotDescription: strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&csi.slotDescription[0]), 64)), " "),
		ManufacturerID:  strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&csi.manufacturerID[0]), 32)), " "),
		Flags:           uint(csi.flags),
		HardwareVersion: toVersion(csi.hardwareVersion),
		FirmwareVersion: toVersion(csi.firmwareVersion),
	}
	return s, toError(e)
}

// GetTokenInfo obtains information about a particular token
// in the system.
func (c *Ctx) GetTokenInfo(slotID uint) (TokenInfo, error) {
	var cti C.CK_TOKEN_INFO
	e := C.GetTokenInfo(c.ctx, C.CK_ULONG(slotID), &cti)
	s := TokenInfo{
		Label:              strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&cti.label[0]), 32)), " "),
		ManufacturerID:     strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&cti.manufacturerID[0]), 32)), " "),
		Model:              strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&cti.model[0]), 16)), " "),
		SerialNumber:       strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&cti.serialNumber[0]), 16)), " "),
		Flags:              uint(cti.flags),
		MaxSessionCount:    uint(cti.ulMaxSessionCount),
		SessionCount:       uint(cti.ulSessionCount),
		MaxRwSessionCount:  uint(cti.ulMaxRwSessionCount),
		RwSessionCount:     uint(cti.ulRwSessionCount),
		MaxPinLen:          uint(cti.ulMaxPinLen),
		MinPinLen:          uint(cti.ulMinPinLen),
		TotalPublicMemory:  uint(cti.ulTotalPublicMemory),
		FreePublicMemory:   uint(cti.ulFreePublicMemory),
		TotalPrivateMemory: uint(cti.ulTotalPrivateMemory),
		FreePrivateMemory:  uint(cti.ulFreePrivateMemory),
		HardwareVersion:    toVersion(cti.hardwareVersion),
		FirmwareVersion:    toVersion(cti.firmwareVersion),
		UTCTime:            strings.TrimRight(string(C.GoBytes(unsafe.Pointer(&cti.utcTime[0]), 16)), " "),
	}
	return s, toError(e)
}

// GetMechanismList obtains a list of mechanism types supported by a token.
func (c *Ctx) GetMechanismList(slotID uint) ([]*Mechanism, error) {
	var (
		mech    C.CK_ULONG_PTR // in pkcs#11 we're all CK_ULONGs \o/
		mechlen C.CK_ULONG
	)
	e := C.GetMechanismList(c.ctx, C.CK_ULONG(slotID), &mech, &mechlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	// Although the function returns only type, cast them back into real
	// attributes as this is used in other functions.
	m := make([]*Mechanism, int(mechlen))
	for i, typ := range toList(mech, mechlen) {
		m[i] = NewMechanism(typ, nil)
	}
	return m, nil
}

// GetMechanismInfo obtains information about a particular
// mechanism possibly supported by a token.
func (c *Ctx) GetMechanismInfo(slotID uint, m []*Mechanism) (MechanismInfo, error) {
	var cm C.CK_MECHANISM_INFO
	e := C.GetMechanismInfo(c.ctx, C.CK_ULONG(slotID), C.CK_MECHANISM_TYPE(m[0].Mechanism),
		C.CK_MECHANISM_INFO_PTR(&cm))
	mi := MechanismInfo{
		MinKeySize: uint(cm.ulMinKeySize),
		MaxKeySize: uint(cm.ulMaxKeySize),
		Flags:      uint(cm.flags),
	}
	return mi, toError(e)
}

// InitToken initializes a token. The label must be 32 characters
// long, it is blank padded if it is not. If it is longer it is capped
// to 32 characters.
func (c *Ctx) InitToken(slotID uint, pin string, label string) error {
	p := C.CString(pin)
	defer C.free(unsafe.Pointer(p))
	ll := len(label)
	for ll < 32 {
		label += " "
		ll++
	}
	l := C.CString(label[:32])
	defer C.free(unsafe.Pointer(l))
	e := C.InitToken(c.ctx, C.CK_ULONG(slotID), p, C.CK_ULONG(len(pin)), l)
	return toError(e)
}

// InitPIN initializes the normal user's PIN.
func (c *Ctx) InitPIN(sh SessionHandle, pin string) error {
	p := C.CString(pin)
	defer C.free(unsafe.Pointer(p))
	e := C.InitPIN(c.ctx, C.CK_SESSION_HANDLE(sh), p, C.CK_ULONG(len(pin)))
	return toError(e)
}

// SetPIN modifies the PIN of the user who is logged in.
func (c *Ctx) SetPIN(sh SessionHandle, oldpin string, newpin string) error {
	old := C.CString(oldpin)
	defer C.free(unsafe.Pointer(old))
	new := C.CString(newpin)
	defer C.free(unsafe.Pointer(new))
	e := C.SetPIN(c.ctx, C.CK_SESSION_HANDLE(sh), old, C.CK_ULONG(len(oldpin)), new, C.CK_ULONG(len(newpin)))
	return toError(e)
}

// OpenSession opens a session between an application and a token.
func (c *Ctx) OpenSession(slotID uint, flags uint) (SessionHandle, error) {
	var s C.CK_SESSION_HANDLE
	e := C.OpenSession(c.ctx, C.CK_ULONG(slotID), C.CK_ULONG(flags), C.CK_SESSION_HANDLE_PTR(&s))
	return SessionHandle(s), toError(e)
}

// CloseSession closes a session between an application and a token.
func (c *Ctx) CloseSession(sh SessionHandle) error {
	if c.ctx == nil {
		return toError(CKR_CRYPTOKI_NOT_INITIALIZED)
	}
	e := C.CloseSession(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

// CloseAllSessions closes all sessions with a token.
func (c *Ctx) CloseAllSessions(slotID uint) error {
	if c.ctx == nil {
		return toError(CKR_CRYPTOKI_NOT_INITIALIZED)
	}
	e := C.CloseAllSessions(c.ctx, C.CK_ULONG(slotID))
	return toError(e)
}

// GetSessionInfo obtains information about the session.
func (c *Ctx) GetSessionInfo(sh SessionHandle) (SessionInfo, error) {
	var csi C.CK_SESSION_INFO
	e := C.GetSessionInfo(c.ctx, C.CK_SESSION_HANDLE(sh), &csi)
	s := SessionInfo{SlotID: uint(csi.slotID),
		State:       uint(csi.state),
		Flags:       uint(csi.flags),
		DeviceError: uint(csi.ulDeviceError),
	}
	return s, toError(e)
}

// GetOperationState obtains the state of the cryptographic operation in a session.
func (c *Ctx) GetOperationState(sh SessionHandle) ([]byte, error) {
	var (
		state    C.CK_BYTE_PTR
		statelen C.CK_ULONG
	)
	e := C.GetOperationState(c.ctx, C.CK_SESSION_HANDLE(sh), &state, &statelen)
	defer C.free(unsafe.Pointer(state))
	if toError(e) != nil {
		return nil, toError(e)
	}
	b := C.GoBytes(unsafe.Pointer(state), C.int(statelen))
	return b, nil
}

// SetOperationState restores the state of the cryptographic operation in a session.
func (c *Ctx) SetOperationState(sh SessionHandle, state []byte, encryptKey, authKey ObjectHandle) error {
	e := C.SetOperationState(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&state[0])),
		C.CK_ULONG(len(state)), C.CK_OBJECT_HANDLE(encryptKey), C.CK_OBJECT_HANDLE(authKey))
	return toError(e)
}

// Login logs a user into a token.
func (c *Ctx) Login(sh SessionHandle, userType uint, pin string) error {
	p := C.CString(pin)
	defer C.free(unsafe.Pointer(p))
	e := C.Login(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_USER_TYPE(userType), p, C.CK_ULONG(len(pin)))
	return toError(e)
}

// Logout logs a user out from a token.
func (c *Ctx) Logout(sh SessionHandle) error {
	if c.ctx == nil {
		return toError(CKR_CRYPTOKI_NOT_INITIALIZED)
	}
	e := C.Logout(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

// CreateObject creates a new object.
func (c *Ctx) CreateObject(sh SessionHandle, temp []*Attribute) (ObjectHandle, error) {
	var obj C.CK_OBJECT_HANDLE
	arena, t, tcount := cAttributeList(temp)
	defer arena.Free()
	e := C.CreateObject(c.ctx, C.CK_SESSION_HANDLE(sh), t, tcount, C.CK_OBJECT_HANDLE_PTR(&obj))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(obj), nil
	}
	return 0, e1
}

// CopyObject copies an object, creating a new object for the copy.
func (c *Ctx) CopyObject(sh SessionHandle, o ObjectHandle, temp []*Attribute) (ObjectHandle, error) {
	var obj C.CK_OBJECT_HANDLE
	arena, t, tcount := cAttributeList(temp)
	defer arena.Free()

	e := C.CopyObject(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(o), t, tcount, C.CK_OBJECT_HANDLE_PTR(&obj))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(obj), nil
	}
	return 0, e1
}

// DestroyObject destroys an object.
func (c *Ctx) DestroyObject(sh SessionHandle, oh ObjectHandle) error {
	e := C.DestroyObject(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(oh))
	return toError(e)
}

// GetObjectSize gets the size of an object in bytes.
func (c *Ctx) GetObjectSize(sh SessionHandle, oh ObjectHandle) (uint, error) {
	var size C.CK_ULONG
	e := C.GetObjectSize(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(oh), &size)
	return uint(size), toError(e)
}

// GetAttributeValue obtains the value of one or more object attributes.
func (c *Ctx) GetAttributeValue(sh SessionHandle, o ObjectHandle, a []*Attribute) ([]*Attribute, error) {
	// copy the attribute list and make all the values nil, so that
	// the C function can (allocate) fill them in
	pa := make([]C.CK_ATTRIBUTE, len(a))
	for i := 0; i < len(a); i++ {
		pa[i]._type = C.CK_ATTRIBUTE_TYPE(a[i].Type)
	}
	e := C.GetAttributeValue(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(o), &pa[0], C.CK_ULONG(len(a)))
	if err := toError(e); err != nil {
		return nil, err
	}
	a1 := make([]*Attribute, len(a))
	for i, c := range pa {
		x := new(Attribute)
		x.Type = uint(c._type)
		if int(c.ulValueLen) != -1 {
			buf := unsafe.Pointer(C.getAttributePval(&c))
			x.Value = C.GoBytes(buf, C.int(c.ulValueLen))
			C.free(buf)
		}
		a1[i] = x
	}
	return a1, nil
}

// SetAttributeValue modifies the value of one or more object attributes
func (c *Ctx) SetAttributeValue(sh SessionHandle, o ObjectHandle, a []*Attribute) error {
	arena, pa, palen := cAttributeList(a)
	defer arena.Free()
	e := C.SetAttributeValue(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(o), pa, palen)
	return toError(e)
}

// FindObjectsInit initializes a search for token and session
// objects that match a template.
func (c *Ctx) FindObjectsInit(sh SessionHandle, temp []*Attribute) error {
	arena, t, tcount := cAttributeList(temp)
	defer arena.Free()
	e := C.FindObjectsInit(c.ctx, C.CK_SESSION_HANDLE(sh), t, tcount)
	return toError(e)
}

// FindObjects continues a search for token and session
// objects that match a template, obtaining additional object
// handles. Calling the function repeatedly may yield additional results until
// an empty slice is returned.
//
// The returned boolean value is deprecated and should be ignored.
func (c *Ctx) FindObjects(sh SessionHandle, max int) ([]ObjectHandle, bool, error) {
	var (
		objectList C.CK_OBJECT_HANDLE_PTR
		ulCount    C.CK_ULONG
	)
	e := C.FindObjects(c.ctx, C.CK_SESSION_HANDLE(sh), &objectList, C.CK_ULONG(max), &ulCount)
	if toError(e) != nil {
		return nil, false, toError(e)
	}
	l := toList(C.CK_ULONG_PTR(unsafe.Pointer(objectList)), ulCount)
	// Make again a new list of the correct type.
	// This is copying data, but this is not an often used function.
	o := make([]ObjectHandle, len(l))
	for i, v := range l {
		o[i] = ObjectHandle(v)
	}
	return o, ulCount > C.CK_ULONG(max), nil
}

// FindObjectsFinal finishes a search for token and session objects.
func (c *Ctx) FindObjectsFinal(sh SessionHandle) error {
	e := C.FindObjectsFinal(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

// EncryptInit initializes an encryption operation.
func (c *Ctx) EncryptInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.EncryptInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

// Encrypt encrypts single-part data.
func (c *Ctx) Encrypt(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		enc    C.CK_BYTE_PTR
		enclen C.CK_ULONG
	)
	e := C.Encrypt(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)), &enc, &enclen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(enc), C.int(enclen))
	C.free(unsafe.Pointer(enc))
	return s, nil
}

// EncryptUpdate continues a multiple-part encryption operation.
func (c *Ctx) EncryptUpdate(sh SessionHandle, plain []byte) ([]byte, error) {
	var (
		part    C.CK_BYTE_PTR
		partlen C.CK_ULONG
	)
	e := C.EncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(plain), C.CK_ULONG(len(plain)), &part, &partlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(part), C.int(partlen))
	C.free(unsafe.Pointer(part))
	return h, nil
}

// EncryptFinal finishes a multiple-part encryption operation.
func (c *Ctx) EncryptFinal(sh SessionHandle) ([]byte, error) {
	var (
		enc    C.CK_BYTE_PTR
		enclen C.CK_ULONG
	)
	e := C.EncryptFinal(c.ctx, C.CK_SESSION_HANDLE(sh), &enc, &enclen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(enc), C.int(enclen))
	C.free(unsafe.Pointer(enc))
	return h, nil
}

// DecryptInit initializes a decryption operation.
func (c *Ctx) DecryptInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.DecryptInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

// Decrypt decrypts encrypted data in a single part.
func (c *Ctx) Decrypt(sh SessionHandle, cipher []byte) ([]byte, error) {
	var (
		plain    C.CK_BYTE_PTR
		plainlen C.CK_ULONG
	)
	e := C.Decrypt(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &plain, &plainlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(plain), C.int(plainlen))
	C.free(unsafe.Pointer(plain))
	return s, nil
}

// DecryptUpdate continues a multiple-part decryption operation.
func (c *Ctx) DecryptUpdate(sh SessionHandle, cipher []byte) ([]byte, error) {
	var (
		part    C.CK_BYTE_PTR
		partlen C.CK_ULONG
	)
	e := C.DecryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &part, &partlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(part), C.int(partlen))
	C.free(unsafe.Pointer(part))
	return h, nil
}

// DecryptFinal finishes a multiple-part decryption operation.
func (c *Ctx) DecryptFinal(sh SessionHandle) ([]byte, error) {
	var (
		plain    C.CK_BYTE_PTR
		plainlen C.CK_ULONG
	)
	e := C.DecryptFinal(c.ctx, C.CK_SESSION_HANDLE(sh), &plain, &plainlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(plain), C.int(plainlen))
	C.free(unsafe.Pointer(plain))
	return h, nil
}

// DigestInit initializes a message-digesting operation.
func (c *Ctx) DigestInit(sh SessionHandle, m []*Mechanism) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.DigestInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech)
	return toError(e)
}

// Digest digests message in a single part.
func (c *Ctx) Digest(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		hash    C.CK_BYTE_PTR
		hashlen C.CK_ULONG
	)
	e := C.Digest(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)), &hash, &hashlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(hash), C.int(hashlen))
	C.free(unsafe.Pointer(hash))
	return h, nil
}

// DigestUpdate continues a multiple-part message-digesting operation.
func (c *Ctx) DigestUpdate(sh SessionHandle, message []byte) error {
	e := C.DigestUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)))
	if toError(e) != nil {
		return toError(e)
	}
	return nil
}

// DigestKey continues a multi-part message-digesting
// operation, by digesting the value of a secret key as part of
// the data already digested.
func (c *Ctx) DigestKey(sh SessionHandle, key ObjectHandle) error {
	e := C.DigestKey(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_OBJECT_HANDLE(key))
	if toError(e) != nil {
		return toError(e)
	}
	return nil
}

// DigestFinal finishes a multiple-part message-digesting operation.
func (c *Ctx) DigestFinal(sh SessionHandle) ([]byte, error) {
	var (
		hash    C.CK_BYTE_PTR
		hashlen C.CK_ULONG
	)
	e := C.DigestFinal(c.ctx, C.CK_SESSION_HANDLE(sh), &hash, &hashlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(hash), C.int(hashlen))
	C.free(unsafe.Pointer(hash))
	return h, nil
}

// SignInit initializes a signature (private key encryption)
// operation, where the signature is (will be) an appendix to
// the data, and plaintext cannot be recovered from the signature.
func (c *Ctx) SignInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.SignInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

// Sign signs (encrypts with private key) data in a single part, where the signature
// is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
func (c *Ctx) Sign(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		sig    C.CK_BYTE_PTR
		siglen C.CK_ULONG
	)
	e := C.Sign(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)), &sig, &siglen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(sig), C.int(siglen))
	C.free(unsafe.Pointer(sig))
	return s, nil
}

// SignUpdate continues a multiple-part signature operation,
// where the signature is (will be) an appendix to the data,
// and plaintext cannot be recovered from the signature.
func (c *Ctx) SignUpdate(sh SessionHandle, message []byte) error {
	e := C.SignUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(message), C.CK_ULONG(len(message)))
	return toError(e)
}

// SignFinal finishes a multiple-part signature operation returning the signature.
func (c *Ctx) SignFinal(sh SessionHandle) ([]byte, error) {
	var (
		sig    C.CK_BYTE_PTR
		siglen C.CK_ULONG
	)
	e := C.SignFinal(c.ctx, C.CK_SESSION_HANDLE(sh), &sig, &siglen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(sig), C.int(siglen))
	C.free(unsafe.Pointer(sig))
	return h, nil
}

// SignRecoverInit initializes a signature operation, where the data can be recovered from the signature.
func (c *Ctx) SignRecoverInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.SignRecoverInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(key))
	return toError(e)
}

// SignRecover signs data in a single operation, where the data can be recovered from the signature.
func (c *Ctx) SignRecover(sh SessionHandle, data []byte) ([]byte, error) {
	var (
		sig    C.CK_BYTE_PTR
		siglen C.CK_ULONG
	)
	e := C.SignRecover(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(data), C.CK_ULONG(len(data)), &sig, &siglen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(sig), C.int(siglen))
	C.free(unsafe.Pointer(sig))
	return h, nil
}

// VerifyInit initializes a verification operation, where the
// signature is an appendix to the data, and plaintext cannot
// be recovered from the signature (e.g. DSA).
func (c *Ctx) VerifyInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.VerifyInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(key))
	return toError(e)
}

// Verify verifies a signature in a single-part operation,
// where the signature is an appendix to the data, and plaintext
// cannot be recovered from the signature.
func (c *Ctx) Verify(sh SessionHandle, data []byte, signature []byte) error {
	e := C.Verify(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(data), C.CK_ULONG(len(data)), cMessage(signature), C.CK_ULONG(len(signature)))
	return toError(e)
}

// VerifyUpdate continues a multiple-part verification
// operation, where the signature is an appendix to the data,
// and plaintext cannot be recovered from the signature.
func (c *Ctx) VerifyUpdate(sh SessionHandle, part []byte) error {
	e := C.VerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(part), C.CK_ULONG(len(part)))
	return toError(e)
}

// VerifyFinal finishes a multiple-part verification
// operation, checking the signature.
func (c *Ctx) VerifyFinal(sh SessionHandle, signature []byte) error {
	e := C.VerifyFinal(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(signature), C.CK_ULONG(len(signature)))
	return toError(e)
}

// VerifyRecoverInit initializes a signature verification
// operation, where the data is recovered from the signature.
func (c *Ctx) VerifyRecoverInit(sh SessionHandle, m []*Mechanism, key ObjectHandle) error {
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.VerifyRecoverInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(key))
	return toError(e)
}

// VerifyRecover verifies a signature in a single-part
// operation, where the data is recovered from the signature.
func (c *Ctx) VerifyRecover(sh SessionHandle, signature []byte) ([]byte, error) {
	var (
		data    C.CK_BYTE_PTR
		datalen C.CK_ULONG
	)
	e := C.DecryptVerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(signature), C.CK_ULONG(len(signature)), &data, &datalen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(data), C.int(datalen))
	C.free(unsafe.Pointer(data))
	return h, nil
}

// DigestEncryptUpdate continues a multiple-part digesting and encryption operation.
func (c *Ctx) DigestEncryptUpdate(sh SessionHandle, part []byte) ([]byte, error) {
	var (
		enc    C.CK_BYTE_PTR
		enclen C.CK_ULONG
	)
	e := C.DigestEncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(part), C.CK_ULONG(len(part)), &enc, &enclen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(enc), C.int(enclen))
	C.free(unsafe.Pointer(enc))
	return h, nil
}

// DecryptDigestUpdate continues a multiple-part decryption and digesting operation.
func (c *Ctx) DecryptDigestUpdate(sh SessionHandle, cipher []byte) ([]byte, error) {
	var (
		part    C.CK_BYTE_PTR
		partlen C.CK_ULONG
	)
	e := C.DecryptDigestUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &part, &partlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(part), C.int(partlen))
	C.free(unsafe.Pointer(part))
	return h, nil
}

// SignEncryptUpdate continues a multiple-part signing and encryption operation.
func (c *Ctx) SignEncryptUpdate(sh SessionHandle, part []byte) ([]byte, error) {
	var (
		enc    C.CK_BYTE_PTR
		enclen C.CK_ULONG
	)
	e := C.SignEncryptUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(part), C.CK_ULONG(len(part)), &enc, &enclen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(enc), C.int(enclen))
	C.free(unsafe.Pointer(enc))
	return h, nil
}

// DecryptVerifyUpdate continues a multiple-part decryption and verify operation.
func (c *Ctx) DecryptVerifyUpdate(sh SessionHandle, cipher []byte) ([]byte, error) {
	var (
		part    C.CK_BYTE_PTR
		partlen C.CK_ULONG
	)
	e := C.DecryptVerifyUpdate(c.ctx, C.CK_SESSION_HANDLE(sh), cMessage(cipher), C.CK_ULONG(len(cipher)), &part, &partlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(part), C.int(partlen))
	C.free(unsafe.Pointer(part))
	return h, nil
}

// GenerateKey generates a secret key, creating a new key object.
func (c *Ctx) GenerateKey(sh SessionHandle, m []*Mechanism, temp []*Attribute) (ObjectHandle, error) {
	var key C.CK_OBJECT_HANDLE
	attrarena, t, tcount := cAttributeList(temp)
	defer attrarena.Free()
	mecharena, mech := cMechanism(m)
	defer mecharena.Free()
	e := C.GenerateKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, t, tcount, C.CK_OBJECT_HANDLE_PTR(&key))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(key), nil
	}
	return 0, e1
}

// GenerateKeyPair generates a public-key/private-key pair creating new key objects.
func (c *Ctx) GenerateKeyPair(sh SessionHandle, m []*Mechanism, public, private []*Attribute) (ObjectHandle, ObjectHandle, error) {
	var (
		pubkey  C.CK_OBJECT_HANDLE
		privkey C.CK_OBJECT_HANDLE
	)
	pubarena, pub, pubcount := cAttributeList(public)
	defer pubarena.Free()
	privarena, priv, privcount := cAttributeList(private)
	defer privarena.Free()
	mecharena, mech := cMechanism(m)
	defer mecharena.Free()
	e := C.GenerateKeyPair(c.ctx, C.CK_SESSION_HANDLE(sh), mech, pub, pubcount, priv, privcount, C.CK_OBJECT_HANDLE_PTR(&pubkey), C.CK_OBJECT_HANDLE_PTR(&privkey))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(pubkey), ObjectHandle(privkey), nil
	}
	return 0, 0, e1
}

// WrapKey wraps (i.e., encrypts) a key.
func (c *Ctx) WrapKey(sh SessionHandle, m []*Mechanism, wrappingkey, key ObjectHandle) ([]byte, error) {
	var (
		wrappedkey    C.CK_BYTE_PTR
		wrappedkeylen C.CK_ULONG
	)
	arena, mech := cMechanism(m)
	defer arena.Free()
	e := C.WrapKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(wrappingkey), C.CK_OBJECT_HANDLE(key), &wrappedkey, &wrappedkeylen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(wrappedkey), C.int(wrappedkeylen))
	C.free(unsafe.Pointer(wrappedkey))
	return h, nil
}

// UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object.
func (c *Ctx) UnwrapKey(sh SessionHandle, m []*Mechanism, unwrappingkey ObjectHandle, wrappedkey []byte, a []*Attribute) (ObjectHandle, error) {
	var key C.CK_OBJECT_HANDLE
	attrarena, ac, aclen := cAttributeList(a)
	defer attrarena.Free()
	mecharena, mech := cMechanism(m)
	defer mecharena.Free()
	e := C.UnwrapKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(unwrappingkey), C.CK_BYTE_PTR(unsafe.Pointer(&wrappedkey[0])), C.CK_ULONG(len(wrappedkey)), ac, aclen, &key)
	return ObjectHandle(key), toError(e)
}

// DeriveKey derives a key from a base key, creating a new key object.
func (c *Ctx) DeriveKey(sh SessionHandle, m []*Mechanism, basekey ObjectHandle, a []*Attribute) (ObjectHandle, error) {
	var key C.CK_OBJECT_HANDLE
	attrarena, ac, aclen := cAttributeList(a)
	defer attrarena.Free()
	mecharena, mech := cMechanism(m)
	defer mecharena.Free()
	e := C.DeriveKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(basekey), ac, aclen, &key)
	return ObjectHandle(key), toError(e)
}

// SeedRandom mixes additional seed material into the token's
// random number generator.
func (c *Ctx) SeedRandom(sh SessionHandle, seed []byte) error {
	e := C.SeedRandom(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&seed[0])), C.CK_ULONG(len(seed)))
	return toError(e)
}

// GenerateRandom generates random data.
func (c *Ctx) GenerateRandom(sh SessionHandle, length int) ([]byte, error) {
	var rand C.CK_BYTE_PTR
	e := C.GenerateRandom(c.ctx, C.CK_SESSION_HANDLE(sh), &rand, C.CK_ULONG(length))
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(rand), C.int(length))
	C.free(unsafe.Pointer(rand))
	return h, nil
}

// WaitForSlotEvent returns a channel which returns a slot event
// (token insertion, removal, etc.) when it occurs.
func (c *Ctx) WaitForSlotEvent(flags uint) chan SlotEvent {
	sl := make(chan SlotEvent, 1) // hold one element
	go c.waitForSlotEventHelper(flags, sl)
	return sl
}

func (c *Ctx) waitForSlotEventHelper(f uint, sl chan SlotEvent) {
	var slotID C.CK_ULONG
	C.WaitForSlotEvent(c.ctx, C.CK_FLAGS(f), &slotID)
	sl <- SlotEvent{uint(slotID)}
	close(sl) // TODO(miek): Sending and then closing ...?
}
