Skip to content
Snippets Groups Projects
js_cryptkeyset.c 13.42 KiB
/* $Id$ */

// Cyrptlib Keyset...

#include "sbbs.h"
#include <cryptlib.h>
#include "js_request.h"
#include "js_cryptcon.h"
#include "js_cryptcert.h"
#include "ssl.h"

struct private_data {
	CRYPT_KEYSET	ks;
	char			*name;
};

static JSClass js_cryptkeyset_class;
static const char* getprivate_failure = "line %d %s %s JS_GetPrivate failed";

// Helpers

// Destructor

static void
js_finalize_cryptkeyset(JSContext *cx, JSObject *obj)
{
	jsrefcount rc;
	struct private_data* p;

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL)
		return;

	rc = JS_SUSPENDREQUEST(cx);
	if (p->ks != CRYPT_UNUSED)
		cryptKeysetClose(p->ks);
	FREE_AND_NULL(p->name);
	free(p);
	JS_RESUMEREQUEST(cx, rc);

	JS_SetPrivate(cx, obj, NULL);
}

// Methods

static JSBool
js_add_private_key(JSContext *cx, uintN argc, jsval *arglist)
{
	struct private_data* p;
	struct js_cryptcon_private_data* ctx;
	jsval *argv=JS_ARGV(cx, arglist);
	char* pw = NULL;
	int status;
	jsrefcount rc;
	JSObject *key;
	JSString *jspw;
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);

	if (!js_argc(cx, argc, 2))
		return JS_FALSE;
	if (argc > 2) {
		JS_ReportError(cx, "Too many arguments");
		return JS_FALSE;
	}
	key = JSVAL_TO_OBJECT(argv[0]);
	if (!JS_InstanceOf(cx, key, &js_cryptcon_class, NULL)) {
		JS_ReportError(cx, "Invalid CryptContext");
		return JS_FALSE;
	}

	if ((jspw = JS_ValueToString(cx, argv[1])) == NULL) {
		JS_ReportError(cx, "Invalid password");
		return JS_FALSE;
	}

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	if ((ctx=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,key))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	JSSTRING_TO_MSTRING(cx, jspw, pw, NULL);
	HANDLE_PENDING(cx, pw);
	rc = JS_SUSPENDREQUEST(cx);
	status = cryptAddPrivateKey(p->ks, ctx->ctx, pw);
	free(pw);
	JS_RESUMEREQUEST(cx, rc);

	if (cryptStatusError(status)) {
		JS_ReportError(cx, "Error %d calling cryptAddPrivateKey()\n", status);
		return JS_FALSE;
	}
	return JS_TRUE;
}

static JSBool
js_add_public_key(JSContext *cx, uintN argc, jsval *arglist)
{
	struct private_data* p;
	struct js_cryptcert_private_data* pcert;
	jsval *argv=JS_ARGV(cx, arglist);
	int status;
	jsrefcount rc;
	JSObject *cert;
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);

	if (!js_argc(cx, argc, 1))
		return JS_FALSE;
	if (argc > 1) {
		JS_ReportError(cx, "Too many arguments");
		return JS_FALSE;
	}
	cert = JSVAL_TO_OBJECT(argv[0]);
	if (!JS_InstanceOf(cx, cert, &js_cryptcert_class, NULL)) {
		JS_ReportError(cx, "Invalid CryptCert");
		return JS_FALSE;
	}

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	if ((pcert=(struct js_cryptcert_private_data *)JS_GetPrivate(cx,cert))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	rc = JS_SUSPENDREQUEST(cx);
	status = cryptAddPublicKey(p->ks, pcert->cert);
	JS_RESUMEREQUEST(cx, rc);

	if (cryptStatusError(status)) {
		JS_ReportError(cx, "Error %d calling cryptAddPublicKey()\n", status);
		return JS_FALSE;
	}
	return JS_TRUE;
}

static JSBool
js_close(JSContext *cx, uintN argc, jsval *arglist)
{
	struct private_data* p;
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsrefcount rc;
	int status;

	if (argc) {
		JS_ReportError(cx, "close() takes no arguments");
		return JS_FALSE;
	}
	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}
	if (p->ks == CRYPT_UNUSED) {
		JS_ReportError(cx, "already closed");
		return JS_FALSE;
	}
	rc = JS_SUSPENDREQUEST(cx);
	status = cryptKeysetClose(p->ks);
	JS_RESUMEREQUEST(cx, rc);
	if (cryptStatusError(status)) {
		JS_ReportError(cx, "Error %d calling cryptKeysetClose()\n", status);
		return JS_FALSE;
	}

	return JS_TRUE;
}

static JSBool
js_delete_key(JSContext *cx, uintN argc, jsval *arglist)
{
	struct private_data* p;
	jsval *argv=JS_ARGV(cx, arglist);
	int status;
	jsrefcount rc;
	char* label = NULL;
	JSString *jslabel;
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);

	if (!js_argc(cx, argc, 1))
		return JS_FALSE;
	if (argc > 1) {
		JS_ReportError(cx, "Too many arguments");
		return JS_FALSE;
	}
	if ((jslabel = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_ReportError(cx, "Invalid label");
		return JS_FALSE;
	}

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	JSSTRING_TO_MSTRING(cx, jslabel, label, NULL);
	HANDLE_PENDING(cx, label);
	rc = JS_SUSPENDREQUEST(cx);
	status = cryptDeleteKey(p->ks, CRYPT_KEYID_NAME, label);
	free(label);
	JS_RESUMEREQUEST(cx, rc);
	if (cryptStatusError(status)) {
		JS_ReportError(cx, "Error %d calling cryptDeleteKey()\n", status);
		return JS_FALSE;
	}
	return JS_TRUE;
}

static JSBool
js_get_private_key(JSContext *cx, uintN argc, jsval *arglist)
{
	struct private_data* p;
	jsval *argv=JS_ARGV(cx, arglist);
	int status;
	jsrefcount rc;
	JSObject *key;
	char* pw = NULL;
	char* label = NULL;
	JSString *jspw;
	JSString *jslabel;
	CRYPT_CONTEXT ctx;
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);

	if (!js_argc(cx, argc, 2))
		return JS_FALSE;
	if (argc > 2) {
		JS_ReportError(cx, "Too many arguments");
		return JS_FALSE;
	}
	if ((jslabel = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_ReportError(cx, "Invalid label");
		return JS_FALSE;
	}
	if ((jspw = JS_ValueToString(cx, argv[1])) == NULL) {
		JS_ReportError(cx, "Invalid password");
		return JS_FALSE;
	}

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	JSSTRING_TO_MSTRING(cx, jslabel, label, NULL);
	HANDLE_PENDING(cx, label);
	JSSTRING_TO_MSTRING(cx, jspw, pw, NULL);
	if (JS_IsExceptionPending(cx)) {
		FREE_AND_NULL(label);
		FREE_AND_NULL(pw);
		return JS_FALSE;
	}
	rc = JS_SUSPENDREQUEST(cx);
	status = cryptGetPrivateKey(p->ks, &ctx, CRYPT_KEYID_NAME, label, pw);
	free(label);
	free(pw);
	JS_RESUMEREQUEST(cx, rc);
	if (cryptStatusError(status)) {
		JS_ReportError(cx, "Error %d calling cryptGetPrivateKey()\n", status);
		return JS_FALSE;
	}
	key = js_CreateCryptconObject(cx, ctx);
	if (key == NULL)
		return JS_FALSE;
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(key));

	return JS_TRUE;
}

static JSBool
js_get_public_key(JSContext *cx, uintN argc, jsval *arglist)
{
	struct private_data* p;
	jsval *argv=JS_ARGV(cx, arglist);
	int status;
	jsrefcount rc;
	JSObject *cert;
	char* label = NULL;
	JSString *jslabel;
	CRYPT_CERTIFICATE ncert;
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);

	if (!js_argc(cx, argc, 1))
		return JS_FALSE;
	if (argc > 1) {
		JS_ReportError(cx, "Too many arguments");
		return JS_FALSE;
	}
	if ((jslabel = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_ReportError(cx, "Invalid label");
		return JS_FALSE;
	}

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

	JSSTRING_TO_MSTRING(cx, jslabel, label, NULL);
	HANDLE_PENDING(cx, label);
	rc = JS_SUSPENDREQUEST(cx);
	status = cryptGetPublicKey(p->ks, &ncert, CRYPT_KEYID_NAME, label);
	free(label);
	JS_RESUMEREQUEST(cx, rc);
	if (cryptStatusError(status)) {
		JS_ReportError(cx, "Error %d calling cryptGetPublicKey()\n", status);
		return JS_FALSE;
	}
	cert = js_CreateCryptCertObject(cx, ncert);
	if (cert == NULL)
		return JS_FALSE;
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(cert));

	return JS_TRUE;
}

// Properties

static JSBool
js_cryptkeyset_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
	jsval idval;
    jsint tiny;
	struct private_data* p;

	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		return JS_TRUE;
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);

	switch (tiny) {
	}
	return JS_TRUE;
}

static JSBool
js_cryptkeyset_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
{
	jsval idval;
    jsint tiny;
	struct private_data* p;
	if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
		JS_ReportError(cx, getprivate_failure, WHERE);
		return JS_FALSE;
	}

    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);

	switch (tiny) {
	}
	return JS_TRUE;
}


#ifdef BUILD_JSDOCS
static char* cryptkeyset_prop_desc[] = {
	 "Keyopt constant (CryptKeyset.KEYOPT.XXX):<ul class=\"showList\">\n"
	 "<li>CryptKeyset.KEYOPT.NONE</li>\n"
	 "<li>CryptKeyset.KEYOPT.READONLY</li>\n"
	 "<li>CryptKeyset.KEYOPT.CREATE</li>\n"
	,NULL
};
#endif

static jsSyncPropertySpec js_cryptkeyset_properties[] = {
/*	name				,tinyid						,flags,				ver	*/
	{0}
};

static jsSyncMethodSpec js_cryptkeyset_functions[] = {
	{"add_private_key",	js_add_private_key,	0,	JSTYPE_VOID,	"CryptContext, password"
	,JSDOCSTR("Add a private key to the keyset, encrypting it with <password>.")
	,316
	},
	{"add_public_key",	js_add_public_key,	0,	JSTYPE_VOID,	"CryptCert"
	,JSDOCSTR("Add a public key (certificate) to the keyset.")
	,316
	},
	{"close",			js_close,			0,	JSTYPE_VOID,	""
	,JSDOCSTR("Close the keyset.")
	,316
	},
	{"delete_key",		js_delete_key,		0,	JSTYPE_VOID,	"label"
	,JSDOCSTR("Delete the key with <label> from the keyset.")
	,316
	},
	{"get_private_key",	js_get_private_key,	0,	JSTYPE_OBJECT,	"label, password"
	,JSDOCSTR("Returns a CryptContext from the private key with <label> encrypted with <password>.")
	,316
	},
	{"get_public_key",	js_get_public_key,	0,	JSTYPE_OBJECT,	"label"
	,JSDOCSTR("Returns a CryptCert from the public key with <label>.")
	,316
	},
	{0}
};

static JSBool js_cryptkeyset_resolve(JSContext *cx, JSObject *obj, jsid id)
{
	char*			name=NULL;
	JSBool			ret;

	if(id != JSID_VOID && id != JSID_EMPTY) {
		jsval idval;
		
		JS_IdToValue(cx, id, &idval);
		if(JSVAL_IS_STRING(idval)) {
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
			HANDLE_PENDING(cx, name);
		}
	}

	ret=js_SyncResolve(cx, obj, name, js_cryptkeyset_properties, js_cryptkeyset_functions, NULL, 0);
	if(name)
		free(name);
	return ret;
}

static JSBool js_cryptkeyset_enumerate(JSContext *cx, JSObject *obj)
{
	return(js_cryptkeyset_resolve(cx, obj, JSID_VOID));
}

static JSClass js_cryptkeyset_class = {
     "CryptKeyset"				/* name			*/
    ,JSCLASS_HAS_PRIVATE		/* flags		*/
	,JS_PropertyStub			/* addProperty	*/
	,JS_PropertyStub			/* delProperty	*/
	,js_cryptkeyset_get			/* getProperty	*/
	,js_cryptkeyset_set			/* setProperty	*/
	,js_cryptkeyset_enumerate	/* enumerate	*/
	,js_cryptkeyset_resolve		/* resolve		*/
	,JS_ConvertStub				/* convert		*/
	,js_finalize_cryptkeyset	/* finalize		*/
};

// Constructor

static JSBool
js_cryptkeyset_constructor(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject *obj;
	jsval *argv=JS_ARGV(cx, arglist);
	struct private_data *p;
	jsrefcount rc;
	int status;
	int opts = CRYPT_KEYOPT_NONE;
	JSString *fn;
	size_t fnslen;

	do_cryptInit();
	obj=JS_NewObject(cx, &js_cryptkeyset_class, NULL, NULL);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
	if(argc < 1 || argc > 2) {
		JS_ReportError(cx, "Incorrect number of arguments to CryptKeyset constructor.  Got %d, expected 1 or 2.", argc);
		return JS_FALSE;
	}
	if ((fn = JS_ValueToString(cx, argv[0])) == NULL)
		return JS_FALSE;
	if (argc == 2)
		if (!JS_ValueToInt32(cx,argv[1],&opts))
			return JS_FALSE;

	if((p=(struct private_data *)malloc(sizeof(struct private_data)))==NULL) {
		JS_ReportError(cx,"malloc failed");
		return(JS_FALSE);
	}
	memset(p,0,sizeof(struct private_data));

	if(!JS_SetPrivate(cx, obj, p)) {
		JS_ReportError(cx,"JS_SetPrivate failed");
		return(JS_FALSE);
	}

	JSSTRING_TO_MSTRING(cx, fn, p->name, &fnslen);
	if (p->name == NULL) {
		free(p);
		return JS_FALSE;
	}
	rc = JS_SUSPENDREQUEST(cx);
	status = cryptKeysetOpen(&p->ks, CRYPT_UNUSED, CRYPT_KEYSET_FILE, p->name, opts);
	JS_RESUMEREQUEST(cx, rc);
	if (cryptStatusError(status)) {
		JS_ReportError(cx, "CryptLib error %d", status);
		FREE_AND_NULL(p->name);
		free(p);
		return JS_FALSE;
	}

#ifdef BUILD_JSDOCS
	js_DescribeSyncObject(cx,obj,"Class used for storing CryptContext keys",31601);
	js_DescribeSyncConstructor(cx,obj,"To create a new CryptKeyset object: "
		"var c = new CryptKeyset('<i>filename</i>' [ <i>opts = CryptKeyset.KEYOPT.NONE</i> ])</tt><br> "
		"where <i>filename</i> is the name of the file to create, and <i>opts</i> "
		"is a value from CryptKeyset.KEYOPT"
		);
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", cryptkeyset_prop_desc, JSPROP_READONLY);
#endif

	return(JS_TRUE);
}

JSObject* DLLCALL js_CreateCryptKeysetClass(JSContext* cx, JSObject* parent)
{
	JSObject*	cksobj;
	JSObject*	constructor;
	JSObject*	opts;
	jsval		val;

	cksobj = JS_InitClass(cx, parent, NULL
		,&js_cryptkeyset_class
		,js_cryptkeyset_constructor
		,1	/* number of constructor args */
		,NULL /* props, specified in constructor */
		,NULL /* funcs, specified in constructor */
		,NULL, NULL);

	if(JS_GetProperty(cx, parent, js_cryptkeyset_class.name, &val) && !JSVAL_NULL_OR_VOID(val)) {
		JS_ValueToObject(cx,val,&constructor);
		opts = JS_DefineObject(cx, constructor, "KEYOPT", NULL, NULL, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
		if(opts != NULL) {
			JS_DefineProperty(cx, opts, "NONE", INT_TO_JSVAL(CRYPT_KEYOPT_NONE), NULL, NULL
				, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
			JS_DefineProperty(cx, opts, "READONLY", INT_TO_JSVAL(CRYPT_KEYOPT_READONLY), NULL, NULL
				, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
			JS_DefineProperty(cx, opts, "CREATE", INT_TO_JSVAL(CRYPT_KEYOPT_CREATE), NULL, NULL
				, JSPROP_PERMANENT|JSPROP_ENUMERATE|JSPROP_READONLY);
			JS_DeepFreezeObject(cx, opts);
		}
	}

	return(cksobj);
}