diff --git a/app.js b/app.js index 03c7bf2..9780beb 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,4 @@ +/*global Promise*/ (function () { 'use strict'; @@ -5,6 +6,9 @@ var Rasha = window.Rasha; var Eckles = window.Eckles; var x509 = window.x509; + var CSR = window.CSR; + var ACME = window.ACME; + var accountStuff = {}; function $(sel) { return document.querySelector(sel); @@ -13,6 +17,11 @@ return Array.prototype.slice.call(document.querySelectorAll(sel)); } + function checkTos(tos) { + console.log("TODO checkbox for agree to terms"); + return tos; + } + function run() { console.log('hello'); @@ -49,8 +58,10 @@ , namedCurve: $('input[name="ec-crv"]:checked').value , modulusLength: $('input[name="rsa-len"]:checked').value }; + var then = Date.now(); console.log('opts', opts); Keypairs.generate(opts).then(function (results) { + console.log("Key generation time:", (Date.now() - then) + "ms"); var pubDer; var privDer; if (/EC/i.test(opts.kty)) { @@ -99,12 +110,13 @@ $$('input').map(function ($el) { $el.disabled = false; }); $$('button').map(function ($el) { $el.disabled = false; }); $('.js-toc-jwk').hidden = false; + + $('.js-create-account').hidden = false; + $('.js-create-csr').hidden = false; }); }); - $('.js-generate').hidden = false; - $('.js-create-account').hidden = false; } window.addEventListener('load', run); diff --git a/index.html b/index.html index 4cfdb8a..d4e99de 100644 --- a/index.html +++ b/index.html @@ -34,27 +34,21 @@

EC Options:

- - - - - + + +
@@ -100,7 +94,6 @@ - diff --git a/lib/bluecrypt-encoding.js b/lib/bluecrypt-encoding.js index 7dc1073..c2473a6 100644 --- a/lib/bluecrypt-encoding.js +++ b/lib/bluecrypt-encoding.js @@ -110,6 +110,8 @@ Enc.binToHex = function (bin) { return h; }).join(''); }; +// TODO are there any nuance differences here? +Enc.utf8ToHex = Enc.binToHex; Enc.hexToBase64 = function (hex) { return btoa(Enc.hexToBin(hex)); diff --git a/lib/keypairs.js b/lib/keypairs.js index 9d6cf3d..2e423f9 100644 --- a/lib/keypairs.js +++ b/lib/keypairs.js @@ -180,14 +180,6 @@ Keypairs.signJws = function (opts) { var msg = protected64 + '.' + payload64; return Keypairs._sign(opts, msg).then(function (buf) { - /* - * This will come back into play for CSRs, but not for JOSE - if ('EC' === opts.jwk.kty) { - // ECDSA JWT signatures differ from "normal" ECDSA signatures - // https://tools.ietf.org/html/rfc7518#section-3.4 - binsig = convertIfEcdsa(binsig); - } - */ var signedMsg = { protected: protected64 , payload: payload64 @@ -212,40 +204,6 @@ Keypairs.signJws = function (opts) { } }); }; -Keypairs._convertIfEcdsa = function (binsig) { - // should have asn1 sequence header of 0x30 - if (0x30 !== binsig[0]) { throw new Error("Impossible EC SHA head marker"); } - var index = 2; // first ecdsa "R" header byte - var len = binsig[1]; - var lenlen = 0; - // Seek length of length if length is greater than 127 (i.e. two 512-bit / 64-byte R and S values) - if (0x80 & len) { - lenlen = len - 0x80; // should be exactly 1 - len = binsig[2]; // should be <= 130 (two 64-bit SHA-512s, plus padding) - index += lenlen; - } - // should be of BigInt type - if (0x02 !== binsig[index]) { throw new Error("Impossible EC SHA R marker"); } - index += 1; - - var rlen = binsig[index]; - var bits = 32; - if (rlen > 49) { - bits = 64; - } else if (rlen > 33) { - bits = 48; - } - var r = binsig.slice(index + 1, index + 1 + rlen).toString('hex'); - var slen = binsig[index + 1 + rlen + 1]; // skip header and read length - var s = binsig.slice(index + 1 + rlen + 1 + 1).toString('hex'); - if (2 *slen !== s.length) { throw new Error("Impossible EC SHA S length"); } - // There may be one byte of padding on either - while (r.length < 2*bits) { r = '00' + r; } - while (s.length < 2*bits) { s = '00' + s; } - if (2*(bits+1) === r.length) { r = r.slice(2); } - if (2*(bits+1) === s.length) { s = s.slice(2); } - return Enc.hexToBuf(r + s); -}; Keypairs._sign = function (opts, payload) { return Keypairs._import(opts).then(function (privkey) { @@ -259,9 +217,12 @@ Keypairs._sign = function (opts, payload) { , privkey , payload ).then(function (signature) { - // convert buffer to urlsafe base64 - //return Enc.bufToUrlBase64(new Uint8Array(signature)); - return new Uint8Array(signature); + signature = new Uint8Array(signature); // ArrayBuffer -> u8 + // This will come back into play for CSRs, but not for JOSE + if ('EC' === opts.jwk.kty && /x509/i.test(opts.format)) { + signature = Keypairs._ecdsaJoseSigToAsn1Sig(signature); + } + return signature; }); }); }; @@ -287,7 +248,6 @@ Keypairs._getName = function (opts) { return 'RSASSA-PKCS1-v1_5'; } }; - Keypairs._import = function (opts) { return Promise.resolve().then(function () { var ops; @@ -316,6 +276,30 @@ Keypairs._import = function (opts) { }); }); }; +// ECDSA JOSE / JWS / JWT signatures differ from "normal" ASN1/X509 ECDSA signatures +// https://tools.ietf.org/html/rfc7518#section-3.4 +Keypairs._ecdsaJoseSigToAsn1Sig = function (bufsig) { + // it's easier to do the manipulation in the browser with an array + bufsig = Array.from(bufsig); + var hlen = bufsig.length / 2; // should be even + var r = bufsig.slice(0, hlen); + var s = bufsig.slice(hlen); + // unpad positive ints less than 32 bytes wide + while (!r[0]) { r = r.slice(1); } + while (!s[0]) { s = s.slice(1); } + // pad (or re-pad) ambiguously non-negative BigInts, up to 33 bytes wide + if (0x80 & r[0]) { r.unshift(0); } + if (0x80 & s[0]) { s.unshift(0); } + + var len = 2 + r.length + 2 + s.length; + var head = [0x30]; + // hard code 0x80 + 1 because it won't be longer than + // two SHA512 plus two pad bytes (130 bytes <= 256) + if (len >= 0x80) { head.push(0x81); } + head.push(len); + + return Uint8Array.from(head.concat([0x02, r.length], r, [0x02, s.byteLength], s)); +}; function setTime(time) { if ('number' === typeof time) { return time; } diff --git a/lib/x509.js b/lib/x509.js index 901bb36..575b8c9 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -1,6 +1,6 @@ -'use strict'; (function (exports) { 'use strict'; + var x509 = exports.x509 = {}; var ASN1 = exports.ASN1; var Enc = exports.Enc;