Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit 7bf46ba

Browse files
jas-trevnorris
authored andcommitted
crypto: add SPKAC support
Implements new class 'Certificate' within crypto object for working with SPKAC's (signed public key & challenge) natively.
1 parent a555992 commit 7bf46ba

File tree

7 files changed

+291
-0
lines changed

7 files changed

+291
-0
lines changed

lib/crypto.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,30 @@ function pbkdf2(password, salt, iterations, keylen, callback) {
573573
}
574574

575575

576+
exports.Certificate = Certificate;
577+
578+
function Certificate() {
579+
if (!(this instanceof Certificate))
580+
return new Certificate();
581+
582+
this._binding = new binding.Certificate();
583+
}
584+
585+
586+
Certificate.prototype.verifySpkac = function(object) {
587+
return this._binding.verifySpkac(object);
588+
};
589+
590+
591+
Certificate.prototype.exportPublicKey = function(object, encoding) {
592+
return this._binding.exportPublicKey(toBuf(object, encoding));
593+
};
594+
595+
596+
Certificate.prototype.exportChallenge = function(object, encoding) {
597+
return this._binding.exportChallenge(toBuf(object, encoding));
598+
};
599+
576600

577601
exports.randomBytes = randomBytes;
578602
exports.pseudoRandomBytes = pseudoRandomBytes;

src/node_crypto.cc

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3581,6 +3581,190 @@ void GetHashes(const FunctionCallbackInfo<Value>& args) {
35813581
}
35823582

35833583

3584+
void Certificate::Initialize(Handle<Object> target) {
3585+
HandleScope scope(node_isolate);
3586+
3587+
Local<FunctionTemplate> t = FunctionTemplate::New(New);
3588+
3589+
t->InstanceTemplate()->SetInternalFieldCount(1);
3590+
3591+
NODE_SET_PROTOTYPE_METHOD(t, "verifySpkac", VerifySpkac);
3592+
NODE_SET_PROTOTYPE_METHOD(t, "exportPublicKey", ExportPublicKey);
3593+
NODE_SET_PROTOTYPE_METHOD(t, "exportChallenge", ExportChallenge);
3594+
3595+
target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Certificate"),
3596+
t->GetFunction());
3597+
}
3598+
3599+
3600+
void Certificate::New(const FunctionCallbackInfo<Value>& args) {
3601+
new Certificate(args.GetIsolate(), args.This());
3602+
}
3603+
3604+
3605+
bool Certificate::VerifySpkac(const char* data, unsigned int len) {
3606+
bool i = 0;
3607+
EVP_PKEY* pkey = NULL;
3608+
NETSCAPE_SPKI* spki = NULL;
3609+
3610+
spki = NETSCAPE_SPKI_b64_decode(data, len);
3611+
if (spki == NULL)
3612+
goto exit;
3613+
3614+
pkey = X509_PUBKEY_get(spki->spkac->pubkey);
3615+
if (pkey == NULL)
3616+
goto exit;
3617+
3618+
i = NETSCAPE_SPKI_verify(spki, pkey) > 0;
3619+
3620+
exit:
3621+
if (pkey != NULL)
3622+
EVP_PKEY_free(pkey);
3623+
3624+
if (spki != NULL)
3625+
NETSCAPE_SPKI_free(spki);
3626+
3627+
return i;
3628+
}
3629+
3630+
3631+
void Certificate::VerifySpkac(const FunctionCallbackInfo<Value>& args) {
3632+
HandleScope scope(node_isolate);
3633+
3634+
Certificate* certificate = WeakObject::Unwrap<Certificate>(args.This());
3635+
bool i = false;
3636+
3637+
if (args.Length() < 1)
3638+
return ThrowTypeError("Missing argument");
3639+
3640+
ASSERT_IS_BUFFER(args[0]);
3641+
3642+
size_t length = Buffer::Length(args[0]);
3643+
if (length == 0)
3644+
return args.GetReturnValue().Set(i);
3645+
3646+
char* data = Buffer::Data(args[0]);
3647+
assert(data != NULL);
3648+
3649+
i = certificate->VerifySpkac(data, length) > 0;
3650+
3651+
args.GetReturnValue().Set(i);
3652+
}
3653+
3654+
3655+
const char* Certificate::ExportPublicKey(const char* data, int len) {
3656+
char* buf = NULL;
3657+
EVP_PKEY* pkey = NULL;
3658+
NETSCAPE_SPKI* spki = NULL;
3659+
3660+
BIO* bio = BIO_new(BIO_s_mem());
3661+
if (bio == NULL)
3662+
goto exit;
3663+
3664+
spki = NETSCAPE_SPKI_b64_decode(data, len);
3665+
if (spki == NULL)
3666+
goto exit;
3667+
3668+
pkey = NETSCAPE_SPKI_get_pubkey(spki);
3669+
if (pkey == NULL)
3670+
goto exit;
3671+
3672+
if (PEM_write_bio_PUBKEY(bio, pkey) <= 0)
3673+
goto exit;
3674+
3675+
BIO_write(bio, "\0", 1);
3676+
BUF_MEM* ptr;
3677+
BIO_get_mem_ptr(bio, &ptr);
3678+
3679+
buf = new char[ptr->length];
3680+
memcpy(buf, ptr->data, ptr->length);
3681+
3682+
exit:
3683+
if (pkey != NULL)
3684+
EVP_PKEY_free(pkey);
3685+
3686+
if (spki != NULL)
3687+
NETSCAPE_SPKI_free(spki);
3688+
3689+
if (bio != NULL)
3690+
BIO_free_all(bio);
3691+
3692+
return buf;
3693+
}
3694+
3695+
3696+
void Certificate::ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
3697+
HandleScope scope(node_isolate);
3698+
3699+
Certificate* certificate = WeakObject::Unwrap<Certificate>(args.This());
3700+
3701+
if (args.Length() < 1)
3702+
return ThrowTypeError("Missing argument");
3703+
3704+
ASSERT_IS_BUFFER(args[0]);
3705+
3706+
size_t length = Buffer::Length(args[0]);
3707+
if (length == 0)
3708+
return args.GetReturnValue().SetEmptyString();
3709+
3710+
char* data = Buffer::Data(args[0]);
3711+
assert(data != NULL);
3712+
3713+
const char* pkey = certificate->ExportPublicKey(data, length);
3714+
if (pkey == NULL)
3715+
return args.GetReturnValue().SetEmptyString();
3716+
3717+
Local<Value> out = Encode(pkey, strlen(pkey), BUFFER);
3718+
3719+
delete[] pkey;
3720+
3721+
args.GetReturnValue().Set(out);
3722+
}
3723+
3724+
3725+
const char* Certificate::ExportChallenge(const char* data, int len) {
3726+
NETSCAPE_SPKI* sp = NULL;
3727+
3728+
sp = NETSCAPE_SPKI_b64_decode(data, len);
3729+
if (sp == NULL)
3730+
return NULL;
3731+
3732+
const char* buf = NULL;
3733+
buf = reinterpret_cast<const char*>(ASN1_STRING_data(sp->spkac->challenge));
3734+
3735+
return buf;
3736+
}
3737+
3738+
3739+
void Certificate::ExportChallenge(const FunctionCallbackInfo<Value>& args) {
3740+
HandleScope scope(node_isolate);
3741+
3742+
Certificate* crt = WeakObject::Unwrap<Certificate>(args.This());
3743+
3744+
if (args.Length() < 1)
3745+
return ThrowTypeError("Missing argument");
3746+
3747+
ASSERT_IS_BUFFER(args[0]);
3748+
3749+
size_t len = Buffer::Length(args[0]);
3750+
if (len == 0)
3751+
return args.GetReturnValue().SetEmptyString();
3752+
3753+
char* data = Buffer::Data(args[0]);
3754+
assert(data != NULL);
3755+
3756+
const char* cert = crt->ExportChallenge(data, len);
3757+
if (cert == NULL)
3758+
return args.GetReturnValue().SetEmptyString();
3759+
3760+
Local<Value> outString = Encode(cert, strlen(cert), BUFFER);
3761+
3762+
delete[] cert;
3763+
3764+
args.GetReturnValue().Set(outString);
3765+
}
3766+
3767+
35843768
void InitCryptoOnce() {
35853769
SSL_library_init();
35863770
OpenSSL_add_all_algorithms();
@@ -3621,6 +3805,7 @@ void InitCrypto(Handle<Object> target,
36213805
Hash::Initialize(env, target);
36223806
Sign::Initialize(env, target);
36233807
Verify::Initialize(env, target);
3808+
Certificate::Initialize(target);
36243809

36253810
NODE_SET_METHOD(target, "PBKDF2", PBKDF2);
36263811
NODE_SET_METHOD(target, "randomBytes", RandomBytes<false>);

src/node_crypto.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,26 @@ class DiffieHellman : public WeakObject {
533533
DH* dh;
534534
};
535535

536+
class Certificate : public WeakObject {
537+
public:
538+
static void Initialize(v8::Handle<v8::Object> target);
539+
540+
v8::Handle<v8::Value> CertificateInit(const char* sign_type);
541+
bool VerifySpkac(const char* data, unsigned int len);
542+
const char* ExportPublicKey(const char* data, int len);
543+
const char* ExportChallenge(const char* data, int len);
544+
545+
protected:
546+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
547+
static void VerifySpkac(const v8::FunctionCallbackInfo<v8::Value>& args);
548+
static void ExportPublicKey(const v8::FunctionCallbackInfo<v8::Value>& args);
549+
static void ExportChallenge(const v8::FunctionCallbackInfo<v8::Value>& args);
550+
551+
Certificate(v8::Isolate* isolate, v8::Local<v8::Object> wrap)
552+
: WeakObject(isolate, wrap) {
553+
}
554+
};
555+
536556
bool EntropySource(unsigned char* buffer, size_t length);
537557
void InitCrypto(v8::Handle<v8::Object> target);
538558

test/fixtures/spkac.fail

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
MIIB/FAILDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkZmI5YWI4MTQtNjY3Ny00MmE0LWE2MGMtZjkwNWQxYTY5MjRkMA0GCSqGSIb3DQEBBQUAA4GBADu1U9t3eY9O3WOofp1RHX2rkh0TPs1CeS+sNdWUSDmdV5ifaGdeXpDikEnh4QIUIeZehxwgy2EjiZqMjMJHF++KPTzfAnHuuEtpDmIGzBnodZa1qt322iGZwgREvacv78GxJBJvPP3KLe+EDDsERG1aWLJoSZRusadacdzNmdV4

test/fixtures/spkac.pem

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcvQh9SKOPv4DwI8LwSaFx02h7
3+
l9QCiDs6sF2GfsSTEUG61SnjQ/v4uJiLKBgbVOagj9rkSCwtTez23ATPeGaBj2Zg
4+
ipv+tv5IXyqUP8ropXJQ5ELtaXPUN/gvw7cO5EbPHr/7eMhbpw8Gl+AfWxW5hLW8
5+
MGw/+AwwjHBOwong/QIDAQAB
6+
-----END PUBLIC KEY-----

test/fixtures/spkac.valid

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkZmI5YWI4MTQtNjY3Ny00MmE0LWE2MGMtZjkwNWQxYTY5MjRkMA0GCSqGSIb3DQEBBQUAA4GBADu1U9t3eY9O3WOofp1RHX2rkh0TPs1CeS+sNdWUSDmdV5ifaGdeXpDikEnh4QIUIeZehxwgy2EjiZqMjMJHF++KPTzfAnHuuEtpDmIGzBnodZa1qt322iGZwgREvacv78GxJBJvPP3KLe+EDDsERG1aWLJoSZRusadacdzNmdV4
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var common = require('../common');
23+
var assert = require('assert');
24+
25+
var crypto = require('crypto');
26+
27+
crypto.DEFAULT_ENCODING = 'buffer';
28+
29+
var fs = require('fs');
30+
var path = require('path');
31+
32+
// Test Certificates
33+
var spkacValid = fs.readFileSync(common.fixturesDir + '/spkac.valid');
34+
var spkacFail = fs.readFileSync(common.fixturesDir + '/spkac.fail');
35+
var spkacPem = fs.readFileSync(common.fixturesDir + '/spkac.pem');
36+
37+
var certificate = new crypto.Certificate();
38+
39+
assert.equal(certificate.verifySpkac(spkacValid), true);
40+
assert.equal(certificate.verifySpkac(spkacFail), false);
41+
42+
assert.equal(stripLineEndings(certificate.exportPublicKey(spkacValid)
43+
.toString('utf8')),
44+
stripLineEndings(spkacPem.toString('utf8')));
45+
assert.equal(certificate.exportPublicKey(spkacFail), '');
46+
47+
assert.equal(certificate.exportChallenge(spkacValid)
48+
.toString('utf8'),
49+
'fb9ab814-6677-42a4-a60c-f905d1a6924d');
50+
assert.equal(certificate.exportChallenge(spkacFail), '');
51+
52+
function stripLineEndings(obj) {
53+
return obj.replace(/\n/g, '');
54+
}

0 commit comments

Comments
 (0)