diff --git a/README.md b/README.md index 6889f80..e7cce1c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ eckles.js ========= -ECDSA tools. Lightweight. Zero Dependencies. Universal compatibility. +ECDSA (elliptic curve) tools. Lightweight. Zero Dependencies. Universal compatibility. * [x] PEM-to-JWK -* [ ] JWK-to-PEM (partial) +* [x] JWK-to-PEM -### PEM-to-JWK +## PEM-to-JWK * [x] SEC1/X9.62, PKCS#8, SPKI/PKIX * [x] P-256 (prime256v1, secp256r1), P-384 (secp384r1) ```js var eckles = require('eckles'); -var pem = require('fs').readFileSync('./fixtures/privkey-ec-p256.sec1.pem', 'ascii') +var pem = require('fs').readFileSync('eckles/fixtures/privkey-ec-p256.sec1.pem', 'ascii'); eckles.import({ pem: pem }).then(function (jwk) { console.log(jwk); @@ -30,15 +30,17 @@ eckles.import({ pem: pem }).then(function (jwk) { } ``` -### JWK-to-PEM +## JWK-to-PEM -* [x] SEC1/X9.62 +* [x] SEC1/X9.62, PKCS#8, SPKI/PKIX * [x] P-256 (prime256v1, secp256r1), P-384 (secp384r1) - ```js +var eckles = require('eckles'); +var jwk = require('eckles/fixtures/privkey-ec-p256.jwk.json'); + eckles.export({ jwk: jwk }).then(function (pem) { - // PEM in sec1 (x9.62) format + // PEM in SEC1 (x9.62) format console.log(pem); }); ``` @@ -51,14 +53,32 @@ yZe7CnFsqeDcpnPbubP6cpYiVcnevNIYyg== -----END EC PRIVATE KEY----- ``` - Goals ----- diff --git a/fixtures/privkey-ec-p256.jwk b/fixtures/privkey-ec-p256.jwk.json similarity index 100% rename from fixtures/privkey-ec-p256.jwk rename to fixtures/privkey-ec-p256.jwk.json diff --git a/fixtures/privkey-ec-p384.jwk b/fixtures/privkey-ec-p384.jwk.json similarity index 100% rename from fixtures/privkey-ec-p384.jwk rename to fixtures/privkey-ec-p384.jwk.json diff --git a/fixtures/pub-ec-p256.jwk b/fixtures/pub-ec-p256.jwk.json similarity index 100% rename from fixtures/pub-ec-p256.jwk rename to fixtures/pub-ec-p256.jwk.json diff --git a/fixtures/pub-ec-p384.jwk b/fixtures/pub-ec-p384.jwk.json similarity index 100% rename from fixtures/pub-ec-p384.jwk rename to fixtures/pub-ec-p384.jwk.json diff --git a/lib/eckles.js b/lib/eckles.js index 115b951..d04d92d 100644 --- a/lib/eckles.js +++ b/lib/eckles.js @@ -14,7 +14,7 @@ var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase(); // 1.2.840.10045.2.1 // ecPublicKey (ANSI X9.62 public key type) -//var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'.replace(/\s+/g, '').toLowerCase(); +var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'.replace(/\s+/g, '').toLowerCase(); // The one good thing that came from the b***kchain hysteria: good EC documentation @@ -325,9 +325,9 @@ EC.pack = function (opts) { if ('sec1' === format) { return PEM.packBlock({ type: "EC PRIVATE KEY", bytes: EC.packSec1(jwk) }); } else if ('pkcs8' === format) { - throw new Error("pkcs8 output not implemented"); + return PEM.packBlock({ type: "EC PRIVATE KEY", bytes: EC.packPkcs8(jwk) }); } else { - throw new Error("spki not implemented"); + return PEM.packBlock({ type: "PUBLIC KEY", bytes: EC.packSpki(jwk) }); } }); }; @@ -350,10 +350,32 @@ EC.packPkcs8 = function (jwk) { var x = toHex(base64ToUint8(urlBase64ToBase64(jwk.x))); var y = toHex(base64ToUint8(urlBase64ToBase64(jwk.y))); var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384; + return Hex.toUint8( + ASN1('30' + , ASN1.UInt('00') + , ASN1('30' + , OBJ_ID_EC_PUB + , objId + ) + , ASN1('04' + , ASN1('30' + , ASN1.UInt('01') + , ASN1('04', d) + , ASN1('A1', ASN1.BitStr('04' + x + y))))) + ); }; EC.packSpki = function (jwk) { var x = toHex(base64ToUint8(urlBase64ToBase64(jwk.x))); var y = toHex(base64ToUint8(urlBase64ToBase64(jwk.y))); + var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384; + return Hex.toUint8( + ASN1('30' + , ASN1('30' + , OBJ_ID_EC_PUB + , objId + ) + , ASN1.BitStr('04' + x + y)) + ); }; EC.packPkix = EC.packSpki; diff --git a/package.json b/package.json index 5a306cd..8b8dd13 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,17 @@ { "name": "eckles", - "version": "0.7.2", + "version": "1.0.0", "description": "PEM-to-JWK and JWK-to-PEM for ECDSA keys in a lightweight, zero-dependency library focused on perfect universal compatibility.", "homepage": "https://git.coolaj86.com/coolaj86/eckles.js", "main": "index.js", "bin": { "eckles": "bin/eckles.js" }, + "files": [ + "bin", + "fixtures", + "lib" + ], "directories": { "lib": "lib" }, diff --git a/test.sh b/test.sh index ef25083..2f2460d 100644 --- a/test.sh +++ b/test.sh @@ -3,26 +3,53 @@ set -e echo "" echo "" -node bin/eckles.js fixtures/privkey-ec-p256.sec1.pem -node bin/eckles.js fixtures/privkey-ec-p256.pkcs8.pem -node bin/eckles.js fixtures/pub-ec-p256.spki.pem +echo "Testing PEM-to-JWK P-256" +echo "" +node bin/eckles.js fixtures/privkey-ec-p256.sec1.pem | tee fixtures/privkey-ec-p256.jwk.2 +diff fixtures/privkey-ec-p256.jwk.json fixtures/privkey-ec-p256.jwk.2 +node bin/eckles.js fixtures/privkey-ec-p256.pkcs8.pem | tee fixtures/privkey-ec-p256.jwk.2 +diff fixtures/privkey-ec-p256.jwk.json fixtures/privkey-ec-p256.jwk.2 +node bin/eckles.js fixtures/pub-ec-p256.spki.pem | tee fixtures/pub-ec-p256.jwk.2 +diff fixtures/pub-ec-p256.jwk.json fixtures/pub-ec-p256.jwk.2 echo "" echo "" -node bin/eckles.js fixtures/privkey-ec-p384.sec1.pem -node bin/eckles.js fixtures/privkey-ec-p384.pkcs8.pem -node bin/eckles.js fixtures/pub-ec-p384.spki.pem +echo "Testing PEM-to-JWK P-384" +echo "" +node bin/eckles.js fixtures/privkey-ec-p384.sec1.pem | tee fixtures/privkey-ec-p384.jwk.2 +diff fixtures/privkey-ec-p384.jwk.json fixtures/privkey-ec-p384.jwk.2 +node bin/eckles.js fixtures/privkey-ec-p384.pkcs8.pem | tee fixtures/privkey-ec-p384.jwk.2.2 +diff fixtures/privkey-ec-p384.jwk.json fixtures/privkey-ec-p384.jwk.2.2 +node bin/eckles.js fixtures/pub-ec-p384.spki.pem | tee fixtures/pub-ec-p384.jwk.2 +diff fixtures/pub-ec-p384.jwk.json fixtures/pub-ec-p384.jwk.2 echo "" echo "" -node bin/eckles.js fixtures/privkey-ec-p256.jwk sec1 -node bin/eckles.js fixtures/privkey-ec-p256.jwk pkcs8 -node bin/eckles.js fixtures/pub-ec-p256.jwk spki +echo "Testing JWK-to-PEM P-256" +echo "" +node bin/eckles.js fixtures/privkey-ec-p256.jwk.json sec1 | tee fixtures/privkey-ec-p256.sec1.pem.2 +diff fixtures/privkey-ec-p256.sec1.pem fixtures/privkey-ec-p256.sec1.pem.2 +node bin/eckles.js fixtures/privkey-ec-p256.jwk.json pkcs8 | tee fixtures/privkey-ec-p256.pkcs8.pem.2 +diff fixtures/privkey-ec-p256.pkcs8.pem fixtures/privkey-ec-p256.pkcs8.pem.2 +node bin/eckles.js fixtures/pub-ec-p256.jwk.json spki | tee fixtures/pub-ec-p256.spki.pem.2 +diff fixtures/pub-ec-p256.spki.pem fixtures/pub-ec-p256.spki.pem.2 echo "" echo "" -node bin/eckles.js fixtures/privkey-ec-p384.jwk sec1 -node bin/eckles.js fixtures/privkey-ec-p384.jwk pkcs8 -node bin/eckles.js fixtures/pub-ec-p384.jwk spki +echo "Testing JWK-to-PEM P-384" +echo "" +node bin/eckles.js fixtures/privkey-ec-p384.jwk.json sec1 | tee fixtures/privkey-ec-p384.sec1.pem.2 +diff fixtures/privkey-ec-p384.sec1.pem fixtures/privkey-ec-p384.sec1.pem.2 +node bin/eckles.js fixtures/privkey-ec-p384.jwk.json pkcs8 | tee fixtures/privkey-ec-p384.pkcs8.pem.2 +diff fixtures/privkey-ec-p384.pkcs8.pem fixtures/privkey-ec-p384.pkcs8.pem.2 +node bin/eckles.js fixtures/pub-ec-p384.jwk.json spki | tee fixtures/pub-ec-p384.spki.pem.2 +diff fixtures/pub-ec-p384.spki.pem fixtures/pub-ec-p384.spki.pem.2 + +rm fixtures/*.2 echo "" +echo "" +echo "PASSED:" +echo "• All inputs produced valid outputs" +echo "• All outputs matched known-good values" +echo ""