diff --git a/install.js b/install.js new file mode 100644 index 0000000..f715501 --- /dev/null +++ b/install.js @@ -0,0 +1,11 @@ +var child_process = require('child_process'); +child_process.exec('npm -v', function (error, stdout, stderr) { + var version = stdout.toString(); + var pure_js = version[0] === '2'; + console.log(pure_js); + if (pure_js) { + console.log('PURE JS'); + } else { + child_process.exec('npm install git+https://github.com/rynomad/ecc.git'); + } +}); diff --git a/install.sh b/install.sh deleted file mode 100755 index 01f548a..0000000 --- a/install.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -export VERSION=`npm -v` - -if [ "${VERSION:0:1}" == "2" ] -then - export PURE_JS=true -fi - -echo $PURE_JS - -if [ "$PURE_JS" == "true" ] -then - echo "PURE JS" -else - npm install git+https://github.com/rynomad/ecc.git -fi diff --git a/package.json b/package.json index 76b9c45..6124e2a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "crypto.subtle for node.js", "main": "src/main.js", "scripts": { - "install": "./install.sh", + "install": "node install.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ @@ -22,6 +22,7 @@ "license": "MIT", "dependencies": { "ecc-jsbn": "0.0.1", + "ecc-qj": "git+https://github.com/mitar/ecc.git#4087e38646d2a375c3c2ce0a3d1881b3c35d9dab", "node-forge": "^0.6.20", "polyfill-promise": "^4.0.1", "sjcl": "^1.0.3" diff --git a/src/bufferize.js b/src/bufferize.js new file mode 100644 index 0000000..a263d37 --- /dev/null +++ b/src/bufferize.js @@ -0,0 +1,10 @@ +function Bufferize (result){ + if (result instanceof ArrayBuffer) + result = new Uint8Array(result); + if (result instanceof Uint8Array) + result = new Buffer(result); + + return result; +} + +module.exports = Bufferize; diff --git a/src/main.js b/src/main.js index 0e6c4e7..b40d583 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,7 @@ var Browser = require("./use_node.js") var OPS = ["generateKey", "importKey", "exportKey", "sign", "verify", "encrypt", "decrypt", "digest", "deriveKey", "deriveBits"] var nonce = require("crypto").randomBytes(64).toString("hex") +var Bufferize = require('./bufferize') global.FORGE = require("./forgeless.js") global.Promise = require("polyfill-promise") var Subtle = {} @@ -25,14 +26,6 @@ function makeArgArray (args){ return ar; } -function Bufferize (result){ - if (result instanceof ArrayBuffer) - result = new Uint8Array(result); - if (result instanceof Uint8Array) - result = new Buffer(result); - - return result; -} function makeRoutine(routine){ return function(){ var routineArgs = makeArgArray(arguments) diff --git a/src/node/algorithms/AES-GCM.js b/src/node/algorithms/AES-GCM.js index 7b84eea..d29b729 100644 --- a/src/node/algorithms/AES-GCM.js +++ b/src/node/algorithms/AES-GCM.js @@ -1,5 +1,6 @@ var sjcl = require("sjcl") , Algorithm = require("./abstract")("AES-GCM") + , Bufferize = require('./../../bufferize') , AES = require("./shared/AES") , secret = Algorithm.types.secret.usage @@ -11,8 +12,13 @@ secret.decrypt = createDecrypt; module.exports = Algorithm; function getParams(alg, data){ - return { iv : sjcl.codec.hex.toBits(alg.iv.toString('hex')) - , add : sjcl.codec.hex.toBits(alg.additionalData.toString('hex')) + var iv = Bufferize(alg.iv); + var additionalData = alg.additionalData ? Bufferize(alg.additionalData) : new Buffer([]); + + data = data ? Bufferize(data) : new Buffer([]); + + return { iv : sjcl.codec.hex.toBits(iv.toString('hex')) + , add : sjcl.codec.hex.toBits(additionalData.toString('hex')) , data : sjcl.codec.hex.toBits(data.toString('hex')) }; } diff --git a/src/node/algorithms/ECDSA.js b/src/node/algorithms/ECDSA.js new file mode 100644 index 0000000..307d930 --- /dev/null +++ b/src/node/algorithms/ECDSA.js @@ -0,0 +1,54 @@ +var Algorithm = require("./abstract")("ECDSA") + , ECC = require("./shared/ECC") + , forge = require("node-forge") + , types = Algorithm.types + , public = types.public.usage + , private = types.private.usage; + +ECC(Algorithm); + +Algorithm.checkParams = checkParams; + +public.verify = createVerify; +private.sign = createSign; + +module.exports = Algorithm; + +function checkParams(format, algorithm, usages) { +} + +function createSign(key, alg1) { + return function ECDSA_SIGN(alg, buf) { + var forgehashKey = alg.hash.name.replace(/-/g, '').toLowerCase(); + var md = forge.md[forgehashKey].create(); + md.update(buf.toString("binary")); + var digest = md.digest().toHex(); + var signature = key.sign(new Buffer(digest, 'hex')); + // Signature is DER encoded, remove it. + var decodedSignature = forge.asn1.fromDer(signature.toString("binary")); + // We take only the last 32 bytes because values could be padded with 0 at the beginning. + return Buffer.concat([ + new Buffer(decodedSignature.value[0].value, 'binary').slice(-32), + new Buffer(decodedSignature.value[1].value, 'binary').slice(-32) + ]); + }; +} + +function createVerify(key, alg1){ + return function ECDSA_VERIFY(alg, buf, sig) { + var forgehashKey = alg.hash.name.replace(/-/g, '').toLowerCase(); + var md = forge.md[forgehashKey].create(); + md.update(buf.toString("binary")); + var digest = md.digest().getBytes(); + var sigPart1 = sig.slice(0, 32); + var sigPart2 = sig.slice(32, 64); + var asn1 = forge.asn1; + var derSignature = asn1.toDer(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, sigPart1.toString("binary")), + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, sigPart2.toString("binary")) + ])).getBytes(); + return key.verifySignature(new Buffer(digest, 'binary'), new Buffer(derSignature, 'binary')); + } +} + +module.exports = Algorithm; diff --git a/src/node/algorithms/index.js b/src/node/algorithms/index.js index 677529d..03cdcee 100644 --- a/src/node/algorithms/index.js +++ b/src/node/algorithms/index.js @@ -5,3 +5,4 @@ exports["RSASSA-PKCS1-v1_5"] = require("./RSASSA-PKCS1-v1_5"); exports["HMAC"] = require("./HMAC") exports["AES-GCM"] = require("./AES-GCM") exports["ECDH"] = require("./ECDH") +exports["ECDSA"] = require("./ECDSA") diff --git a/src/node/algorithms/shared/AES.js b/src/node/algorithms/shared/AES.js index d1f58f3..c962aef 100644 --- a/src/node/algorithms/shared/AES.js +++ b/src/node/algorithms/shared/AES.js @@ -1,20 +1,24 @@ -var sjcl = require("sjcl"); +var sjcl = require("sjcl"), + crypto = require("crypto"); -function AES_generateKey(noop){ - return; +function AES_generateKey(algorithm){ + return raw_import(crypto.randomBytes(algorithm.length / 8)); } function raw_import(buf){ - return new sjcl.cipher.aes(sjcl.codec.hex.toBits(buf.toString("hex"))); + var key = new sjcl.cipher.aes(sjcl.codec.hex.toBits(buf.toString("hex"))); + key._raw = buf; + return key; } function raw_export(key){ - return key; + return key._raw; } function AES(Algorithm){ Algorithm.formats.raw.import = raw_import; Algorithm.formats.raw.export = raw_export; + Algorithm.generate = AES_generateKey; return; } diff --git a/src/node/algorithms/shared/ECC.js b/src/node/algorithms/shared/ECC.js index 60dbd5a..2f79ec0 100644 --- a/src/node/algorithms/shared/ECC.js +++ b/src/node/algorithms/shared/ECC.js @@ -1,5 +1,7 @@ var ecc = require("./ecc.node.js") - , spkiECCPad = new Buffer("3056301006042b81047006082a8648ce3d030107034200","hex"); + , spkiECCPad = new Buffer("3059301306072a8648ce3d020106082a8648ce3d030107034200","hex") + , pkcsPad1 = new Buffer("308187020100301306072A8648CE3D020106082A8648CE3D030107046D306B0201010420", "hex") + , pkcsPad2 = new Buffer("A144034200", "hex"); function getCurveKey(namedCurve){ return ("sec" + namedCurve.replace(/-/g, '').toLowerCase() + "r1"); @@ -16,7 +18,6 @@ function spki_export(Key){ } function raw_export(Key){ - console.log("raw ecc export") return new Buffer(Key.PublicKey); } @@ -29,6 +30,19 @@ function raw_import(publicBytes, alg){ return new ecc.ECKey(curve, publicBytes, true); } +function pkcs8_import(privateBytes, algorithm) { + var curvekey = getCurveKey(algorithm.namedCurve) + , curve = ecc.ECCurves[curvekey]; + + curve.legacy = true; + + return new ecc.ECKey(curve, privateBytes.slice(pkcsPad1.length, pkcsPad1.length + 32), false); +} + +function pkcs8_export(Key) { + return Buffer.concat([pkcsPad1, Key.PrivateKey, pkcsPad2, raw_export(Key)]); +} + function generate(alg){ var curvekey = getCurveKey(alg.namedCurve) , curve = ecc.ECCurves[curvekey]; @@ -41,7 +55,8 @@ function generate(alg){ function ECC(Algorithm){ var formats = Algorithm.formats , raw = formats.raw - , spki = formats.spki; + , spki = formats.spki + , pkcs8 = formats.pkcs8; // attach common generator Algorithm.generate = generate; @@ -53,6 +68,9 @@ function ECC(Algorithm){ spki.import = spki_import; spki.export = spki_export; + pkcs8.import = pkcs8_import; + pkcs8.export = pkcs8_export; + return; } diff --git a/src/node/algorithms/shared/RSA.js b/src/node/algorithms/shared/RSA.js index bd444f4..0be7771 100644 --- a/src/node/algorithms/shared/RSA.js +++ b/src/node/algorithms/shared/RSA.js @@ -35,7 +35,7 @@ function generate(algorithm){ pos++; } - return forge.rsa.generateKeyPair({bits: algorithm.modulousLength , e: exp}); + return forge.rsa.generateKeyPair({bits: algorithm.modulusLength , e: exp}); } diff --git a/src/node/generateKey.js b/src/node/generateKey.js index 838ae27..dc5d545 100644 --- a/src/node/generateKey.js +++ b/src/node/generateKey.js @@ -58,7 +58,12 @@ function generateKey(algorithm, exportable, usages, nonce){ //construct the return object from the scaffold Object.keys(_scaf).forEach(function(type){ - _res[_alg.types[type].returnLabel] = new CryptoKey(_key, type, _scaf[type]._exp, _scaf[type]._uses, nonce); + if (_alg.types[type].returnLabel) { + _res[_alg.types[type].returnLabel] = new CryptoKey(_key, type, _scaf[type]._exp, _scaf[type]._uses, nonce); + } + else { + _res = new CryptoKey(_key, type, _scaf[type]._exp, _scaf[type]._uses, nonce); + } }) //special case... ECDH public keys don't have they're own usage, but are still needed as params in deriveKey/Bits