From 47f22a6bd0bb098ba4dfa846334a3e14a4a910f2 Mon Sep 17 00:00:00 2001 From: deuce <> Date: Thu, 22 Feb 2018 02:12:18 +0000 Subject: [PATCH] Add new private_key object to CryptContext (not in JSDOCS). Values are in the format required by ACMEv2/Let's Encrypt. Yes, this seems to be the easiest way to exfiltrate a private key from a cryptlib context. --- src/sbbs3/js_cryptcon.c | 231 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 1 deletion(-) diff --git a/src/sbbs3/js_cryptcon.c b/src/sbbs3/js_cryptcon.c index ffaa35473d..674b4e7403 100644 --- a/src/sbbs3/js_cryptcon.c +++ b/src/sbbs3/js_cryptcon.c @@ -6,6 +6,7 @@ #include <cryptlib.h> #include "js_request.h" #include "ssl.h" +#include "base64.h" struct private_data { CRYPT_CONTEXT ctx; @@ -39,6 +40,226 @@ js_cryptcon_error(JSContext *cx, CRYPT_CONTEXT ctx, int error) free(errstr); } +static size_t js_asn1_len(unsigned char *data, size_t len, size_t *off) +{ + size_t sz = 0; + size_t bytes; + size_t i; + + if (*off >= len) + return 0; + + if ((data[*off] & 0x80) == 0) { + + sz = data[*off]; + (*off)++; + } + else if(data[*off] == 0x80) { + // We can't actually handle this when we're this simple. + (*off)++; + } + else { + bytes = data[(*off)++] & 0x7f; + for (i = 0; i < bytes && *off < len; i++) { + sz <<= 8; + sz |= data[(*off)++]; + } + } + + return sz; +} + +static unsigned char js_asn1_type(unsigned char *data, size_t len, size_t *off) +{ + unsigned char t = 0; + + if ((data[*off] & 0x1f) == 0x1f) { + for ((*off)++; *off < len && data[*off]; (*off)++) { + if ((data[*off] & 0x80) == 0) + break; + } + } + else { + t = data[*off]; + (*off)++; + } + + return t; +} + +static void js_simple_asn1(unsigned char *data, size_t len, JSContext *cx, JSObject *parent) +{ + unsigned char t; + size_t off=0; + size_t sz; + char *e; + char *n; + char *e64; + char *n64; + JSObject *obj; + JSString* estr; + JSString* nstr; + + while(off < len) { + /* Parse identifier */ + t = js_asn1_type(data, len, &off); + + /* Parse length */ + sz = js_asn1_len(data, len, &off); + + switch(t) { + case 48: + // Sequence, descend into it. + break; + case 6: + // OID.... check if it's PKCS #1 + if (strncmp((char *)data+off, "\x2a\x86\x48\x86\xF7\x0D\x01\x01\x01", 9)==0) { + // YEP! + off += sz; + off += 9; + if (js_asn1_type(data, len, &off) == 2) { + sz = js_asn1_len(data,len,&off); + n = malloc(sz); + if (n == NULL) + return; + memcpy(n, data+off, sz); + n64 = malloc(sz*4/3+3); + if (n64 == NULL) { + free(n); + return; + } + b64_encode(n64, sz*4/3+3, n, sz); + free(n); + for (n=n64; *n; n++) { + if (*n == '+') + *n = '-'; + else if (*n == '/') + *n = '_'; + else if (*n == '=') + *n = 0; + } + off += sz; + if (js_asn1_type(data, len, &off) != 2) { + free(n64); + return; + } + sz = js_asn1_len(data,len,&off); + e = malloc(sz); + if (e == NULL) { + free(n64); + return; + } + memcpy(e, data+off, sz); + e64 = malloc(sz*4/3+3); + if (e64 == NULL) { + free(e); + free(n64); + return; + } + b64_encode(e64, sz*4/3+3, e, sz); + for (e=e64; *e; e++) { + if (*e == '+') + *e = '-'; + else if (*e == '/') + *e = '_'; + else if (*e == '=') + *e = 0; + } + free(e); + obj=JS_NewObject(cx, NULL, NULL, parent); + JS_DefineProperty(cx, parent, "public_key", OBJECT_TO_JSVAL(obj), NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY); + nstr=JS_NewStringCopyZ(cx, n64); + if (nstr != NULL) + JS_DefineProperty(cx, obj, "n", STRING_TO_JSVAL(nstr), NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY); + estr=JS_NewStringCopyZ(cx, e64); + if (estr != NULL) + JS_DefineProperty(cx, obj, "e", STRING_TO_JSVAL(estr), NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY); + JS_DeepFreezeObject(cx, obj); + } + off = len; + } + off += sz; + break; + default: + off += sz; + break; + } + } +} + +static void js_create_key_object(JSContext *cx, JSObject *parent) +{ + struct private_data* p; + jsrefcount rc; + int status; + int val; + int sz; + CRYPT_CERTIFICATE cert; // Just to hold the public key... + unsigned char *certbuf; + + if ((p=(struct private_data *)JS_GetPrivate(cx,parent))==NULL) + return; + + rc = JS_SUSPENDREQUEST(cx); + + status = cryptGetAttribute(p->ctx, CRYPT_CTXINFO_ALGO, &val); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptGetAttribute(ALGO) returned %d\n", status); + goto resume; + } + if (val != CRYPT_ALGO_RSA) + goto resume; + + status = cryptCreateCert(&cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptCreateCert() returned %d\n", status); + goto resume; + } + status = cryptSetAttribute(cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, p->ctx); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptSetAttribute(PKI) returned %d\n", status); + goto resume; + } + status = cryptSetAttribute(cert, CRYPT_CERTINFO_XYZZY, 1); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptSetAttribute(XYZZY) returned %d\n", status); + goto resume; + } + status = cryptSetAttributeString(cert, CRYPT_CERTINFO_COMMONNAME, "Key", 3); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptSetAttributeString(CN) returned %d\n", status); + goto resume; + } + status = cryptSignCert(cert, p->ctx); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptSignCert() returned %d\n", status); + goto resume; + } + status = cryptExportCert(NULL, 0, &sz, CRYPT_CERTFORMAT_CERTIFICATE, cert); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptExportCert(NULL) returned %d\n", status); + goto resume; + } + certbuf = malloc(sz); + if (certbuf == NULL) { + lprintf(LOG_ERR, "Unable to allocate %d bytes\n", sz); + goto resume; + } + status = cryptExportCert(certbuf, sz, &sz, CRYPT_CERTFORMAT_CERTIFICATE, cert); + if (status != CRYPT_OK) { + lprintf(LOG_ERR, "cryptExportCert(certbuf) returned %d\n", status); + goto resume; + } + cryptDestroyCert(cert); + + js_simple_asn1(certbuf, sz, cx, parent); + free(certbuf); + +resume: + JS_RESUMEREQUEST(cx, rc); + return; +} + // Destructor static void @@ -64,6 +285,7 @@ js_generate_key(JSContext *cx, uintN argc, jsval *arglist) JSObject *obj=JS_THIS_OBJECT(cx, arglist); jsrefcount rc; int status; + char kname[32]; if ((p=(struct private_data *)JS_GetPrivate(cx,obj))==NULL) { JS_ReportError(cx, getprivate_failure, WHERE); @@ -71,8 +293,13 @@ js_generate_key(JSContext *cx, uintN argc, jsval *arglist) } rc = JS_SUSPENDREQUEST(cx); - status = cryptGenerateKey(p->ctx); + sprintf(kname, "Key-%d", p->ctx); + status = cryptSetAttributeString(p->ctx, CRYPT_CTXINFO_LABEL, kname, strlen(kname)); + if (status == CRYPT_OK) + status = cryptGenerateKey(p->ctx); JS_RESUMEREQUEST(cx, rc); + if (status == CRYPT_OK) + js_create_key_object(cx, obj); if (cryptStatusError(status)) { js_cryptcon_error(cx, p->ctx, status); return JS_FALSE; @@ -117,6 +344,7 @@ js_set_key(JSContext *cx, uintN argc, jsval *arglist) js_cryptcon_error(cx, p->ctx, status); return JS_FALSE; } + js_create_key_object(cx, obj); JS_SET_RVAL(cx, arglist, JSVAL_VOID); return JS_TRUE; @@ -164,6 +392,7 @@ js_derive_key(JSContext *cx, uintN argc, jsval *arglist) js_cryptcon_error(cx, p->ctx, status); return JS_FALSE; } + js_create_key_object(cx, obj); JS_SET_RVAL(cx, arglist, JSVAL_VOID); return JS_TRUE; -- GitLab