diff --git a/package.json b/package.json index cec8e9a..251f89c 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "ecc-jsbn": "0.0.1", + "ecc-qj": "git+https://github.com/rynomad/ecc.git", "node-forge": "^0.6.20", "polyfill-promise": "^4.0.1", "sjcl": "^1.0.3" diff --git a/src/node/algorithms/ECDSA.js b/src/node/algorithms/ECDSA.js new file mode 100644 index 0000000..f65a412 --- /dev/null +++ b/src/node/algorithms/ECDSA.js @@ -0,0 +1,66 @@ +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 pad(buffer) { + if (buffer[0] & 0x80) { + // If the value starts with 1, we prepend a 0 to assure it is interpreted as a positive number. + return Buffer.concat([new Buffer([0]), buffer]) + } + else { + return buffer; + } +} + +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 = pad(sig.slice(0, 32)); + var sigPart2 = pad(sig.slice(32, 64)); + var asn1 = forge.asn1; + var integer1 = sigPart1.toString("binary"); + var integer2 = sigPart2.toString("binary"); + var derSignature = asn1.toDer(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, integer1), + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, integer2) + ])).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/ECC.js b/src/node/algorithms/shared/ECC.js index 69f047e..dd23ee7 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("3059301306072a8648ce3d020106082a8648ce3d030107034200","hex"); + , pkcsPad1 = new Buffer("308187020100301306072A8648CE3D020106082A8648CE3D030107046D306B0201010420", "hex") + , pkcsPad2 = new Buffer("A144034200", "hex"); function getCurveKey(namedCurve){ return ("sec" + namedCurve.replace(/-/g, '').toLowerCase() + "r1"); @@ -28,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]; @@ -40,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; @@ -52,6 +68,9 @@ function ECC(Algorithm){ spki.import = spki_import; spki.export = spki_export; + pkcs8.import = pkcs8_import; + pkcs8.export = pkcs8_export; + return; }