Commit bfcdc66a authored by deuce's avatar deuce
Browse files

Add CryptKeyset class.

This class allows saving and loading private keys.  It doesn't currently
allow saving or loading *public* keys, since they require certificate support
which we don't yet have in our JavaScript Object Model.

I'll fix the JSDocs build in a minute.
parent 4daafc43
......@@ -5,14 +5,11 @@
#include "sbbs.h"
#include <cryptlib.h>
#include "js_request.h"
#include "js_cryptcon.h"
#include "ssl.h"
#include "base64.h"
struct private_data {
CRYPT_CONTEXT ctx;
};
static JSClass js_cryptcon_class;
JSClass js_cryptcon_class;
static const char* getprivate_failure = "line %d %s %s JS_GetPrivate failed";
// Helpers
......@@ -204,7 +201,7 @@ static void js_simple_asn1(unsigned char *data, size_t len, JSContext *cx, JSObj
static void js_create_key_object(JSContext *cx, JSObject *parent)
{
struct private_data* p;
struct js_cryptcon_private_data* p;
jsrefcount rc;
int status;
int val;
......@@ -212,7 +209,7 @@ static void js_create_key_object(JSContext *cx, JSObject *parent)
CRYPT_CERTIFICATE cert; // Just to hold the public key...
unsigned char *certbuf;
if ((p=(struct private_data *)JS_GetPrivate(cx,parent))==NULL)
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,parent))==NULL)
return;
rc = JS_SUSPENDREQUEST(cx);
......@@ -280,9 +277,9 @@ resume:
static void
js_finalize_cryptcon(JSContext *cx, JSObject *obj)
{
struct private_data* p;
struct js_cryptcon_private_data* p;
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL)
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL)
return;
cryptDestroyContext(p->ctx);
......@@ -296,12 +293,12 @@ js_finalize_cryptcon(JSContext *cx, JSObject *obj)
static JSBool
js_generate_key(JSContext *cx, uintN argc, jsval *arglist)
{
struct private_data* p;
struct js_cryptcon_private_data* p;
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsrefcount rc;
int status;
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
}
......@@ -322,7 +319,7 @@ js_generate_key(JSContext *cx, uintN argc, jsval *arglist)
static JSBool
js_set_key(JSContext *cx, uintN argc, jsval *arglist)
{
struct private_data* p;
struct js_cryptcon_private_data* p;
JSObject *obj;
jsval *argv;
size_t len;
......@@ -341,7 +338,7 @@ js_set_key(JSContext *cx, uintN argc, jsval *arglist)
return JS_FALSE;
obj = JS_THIS_OBJECT(cx, arglist);
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
free(key);
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
......@@ -364,7 +361,7 @@ js_set_key(JSContext *cx, uintN argc, jsval *arglist)
static JSBool
js_derive_key(JSContext *cx, uintN argc, jsval *arglist)
{
struct private_data* p;
struct js_cryptcon_private_data* p;
JSObject *obj;
jsval *argv;
size_t len;
......@@ -389,7 +386,7 @@ js_derive_key(JSContext *cx, uintN argc, jsval *arglist)
}
obj = JS_THIS_OBJECT(cx, arglist);
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
free(key);
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
......@@ -412,7 +409,7 @@ js_derive_key(JSContext *cx, uintN argc, jsval *arglist)
static JSBool
js_do_encrption(JSContext *cx, uintN argc, jsval *arglist, int encrypt)
{
struct private_data* p;
struct js_cryptcon_private_data* p;
JSObject *obj;
jsval *argv;
size_t len;
......@@ -427,7 +424,7 @@ js_do_encrption(JSContext *cx, uintN argc, jsval *arglist, int encrypt)
argv = JS_ARGV(cx, arglist);
obj = JS_THIS_OBJECT(cx, arglist);
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
}
......@@ -472,8 +469,8 @@ js_decrypt(JSContext *cx, uintN argc, jsval *arglist)
static JSBool
js_create_signature(JSContext *cx, uintN argc, jsval *arglist)
{
struct private_data* p;
struct private_data* scp;
struct js_cryptcon_private_data* p;
struct js_cryptcon_private_data* scp;
JSObject *sigCtx;
JSObject *obj;
jsval *argv;
......@@ -489,7 +486,7 @@ js_create_signature(JSContext *cx, uintN argc, jsval *arglist)
argv = JS_ARGV(cx, arglist);
obj = JS_THIS_OBJECT(cx, arglist);
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
}
......@@ -497,7 +494,7 @@ js_create_signature(JSContext *cx, uintN argc, jsval *arglist)
if (!JS_InstanceOf(cx, sigCtx, &js_cryptcon_class, NULL))
return JS_FALSE;
HANDLE_PENDING(cx, NULL);
if ((scp=(struct private_data *)JS_GetPrivate(cx,sigCtx))==NULL) {
if ((scp=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,sigCtx))==NULL) {
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
}
......@@ -634,9 +631,9 @@ js_cryptcon_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
{
jsval idval;
jsint tiny;
struct private_data* p;
struct js_cryptcon_private_data* p;
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
}
......@@ -731,9 +728,9 @@ js_cryptcon_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jsval idval;
jsint tiny;
struct private_data* p;
struct js_cryptcon_private_data* p;
if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) {
if ((p=(struct js_cryptcon_private_data *)JS_GetPrivate(cx,obj))==NULL) {
return JS_TRUE;
JS_ReportError(cx, getprivate_failure, WHERE);
return JS_FALSE;
......@@ -846,7 +843,7 @@ static JSBool js_cryptcon_enumerate(JSContext *cx, JSObject *obj)
return(js_cryptcon_resolve(cx, obj, JSID_VOID));
}
static JSClass js_cryptcon_class = {
JSClass js_cryptcon_class = {
"CryptContext" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
......@@ -859,6 +856,29 @@ static JSClass js_cryptcon_class = {
,js_finalize_cryptcon /* finalize */
};
JSObject* DLLCALL js_CreateCryptconObject(JSContext* cx, CRYPT_CONTEXT ctx)
{
JSObject *obj;
struct js_cryptcon_private_data *p;
obj=JS_NewObject(cx, &js_cryptcon_class, NULL, NULL);
if((p=(struct js_cryptcon_private_data *)malloc(sizeof(struct js_cryptcon_private_data)))==NULL) {
JS_ReportError(cx,"malloc failed");
return NULL;
}
memset(p,0,sizeof(struct js_cryptcon_private_data));
p->ctx = ctx;
if(!JS_SetPrivate(cx, obj, p)) {
JS_ReportError(cx,"JS_SetPrivate failed");
return NULL;
}
js_create_key_object(cx, obj);
return obj;
}
// Constructor
static JSBool
......@@ -866,7 +886,7 @@ js_cryptcon_constructor(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj;
jsval *argv=JS_ARGV(cx, arglist);
struct private_data *p;
struct js_cryptcon_private_data *p;
jsrefcount rc;
int status;
int algo;
......@@ -881,11 +901,11 @@ js_cryptcon_constructor(JSContext *cx, uintN argc, jsval *arglist)
if (!JS_ValueToInt32(cx,argv[0],&algo))
return JS_FALSE;
if((p=(struct private_data *)malloc(sizeof(struct private_data)))==NULL) {
if((p=(struct js_cryptcon_private_data *)malloc(sizeof(struct js_cryptcon_private_data)))==NULL) {
JS_ReportError(cx,"malloc failed");
return(JS_FALSE);
}
memset(p,0,sizeof(struct private_data));
memset(p,0,sizeof(struct js_cryptcon_private_data));
if(!JS_SetPrivate(cx, obj, p)) {
JS_ReportError(cx,"JS_SetPrivate failed");
......
#ifndef _JS_CRYPTCON_H_
#define _JS_CRYPTCON_H_
struct js_cryptcon_private_data {
CRYPT_CONTEXT ctx;
};
extern JSClass js_cryptcon_class;
JSObject* DLLCALL js_CreateCryptconObject(JSContext* cx, CRYPT_CONTEXT ctx);
#endif
/* $Id$ */
// Cyrptlib Keyset...
#include "sbbs.h"
#include <cryptlib.h>
#include "js_request.h"
#include "js_cryptcon.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;
}
argv = JS_ARGV(cx, arglist);
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_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);
fprintf(stderr, "cryptDeleteKey(%s) = %d\n", label, status);
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;
}
// 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
},
{"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
},
{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");