From 63133c72e8a2eec06be242e4ceb255e2c0ea9b40 Mon Sep 17 00:00:00 2001 From: Patrick Roumanoff Date: Tue, 13 Feb 2018 17:41:00 +0100 Subject: [PATCH] ES6 --- .eslintrc.json | 32 ++++++------------- base64url.js | 29 +++++++++-------- index.html | 1 + js-keygen.js | 34 ++++++++++---------- ssh-util.js | 75 ++++++++++++++++++++++--------------------- test/tests.js | 86 +++++++++++++++++++++----------------------------- 6 files changed, 115 insertions(+), 142 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7db5754..c49565b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,23 +1,11 @@ { - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "node": true - }, - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "module" - }, - "rules": { - "no-const-assign": "warn", - "no-this-before-super": "warn", - "no-undef": "warn", - "no-unreachable": "warn", - "no-unused-vars": "warn", - "constructor-super": "warn", - "valid-typeof": "warn" - } -} \ No newline at end of file + "extends": ["airbnb-base", "prettier"], + "rules": { + "quotes": ["warn", "double"], + "no-param-reassign": ["error", { "props": false }] + }, + "env": { + "browser": true, + "node": true + } +} diff --git a/base64url.js b/base64url.js index 0c90a7f..0428a83 100644 --- a/base64url.js +++ b/base64url.js @@ -1,30 +1,31 @@ -//adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-C +// adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-C function base64urlEncode(arg) { - var s = window.btoa(arg); // Regular base64 encoder - s = s.split("=")[0]; // Remove any trailing '='s - s = s.replace(/\+/g, "-"); // 62nd char of encoding - s = s.replace(/\//g, "_"); // 63rd char of encoding - return s; + const step1 = window.btoa(arg); // Regular base64 encoder + const step2 = step1.split("=")[0]; // Remove any trailing '='s + const step3 = step2.replace(/\+/g, "-"); // 62nd char of encoding + const step4 = step3.replace(/\//g, "_"); // 63rd char of encoding + return step4; } function base64urlDecode(s) { - s = s.replace(/-/g, "+"); // 62nd char of encoding - s = s.replace(/_/g, "/"); // 63rd char of encoding - switch (s.length % 4) { // Pad with trailing '='s + const step1 = s.replace(/-/g, "+"); // 62nd char of encoding + const step2 = step1.replace(/_/g, "/"); // 63rd char of encoding + let step3 = step2; + switch (step2.length % 4) { // Pad with trailing '='s case 0: // No pad chars in this case break; case 2: // Two pad chars - s += "=="; + step3 += "=="; break; case 3: // One pad char - s += "="; + step3 += "="; break; default: - throw "Illegal base64url string!"; + throw new Error("Illegal base64url string!"); } - return window.atob(s); // Regular base64 decoder + return window.atob(step3); // Regular base64 decoder } -module = window.module || {}; +const module = window.module || {}; module.exports = { base64urlDecode, base64urlEncode }; diff --git a/index.html b/index.html index 107c1aa..e9dcd70 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ js-keygen + module = {}; diff --git a/js-keygen.js b/js-keygen.js index b9f3e54..97c2149 100644 --- a/js-keygen.js +++ b/js-keygen.js @@ -1,26 +1,25 @@ -var extractable = true; -var encodePrivateKey, encodePublicKey; +/* global encodePrivateKey, encodePublicKey */ +const extractable = true; function wrap(text, len) { - var length = len || 72, - i, - result = ""; - for (i = 0; i < text.length; i += length) { - result += text.slice(i, i + length) + "\n"; + const length = len || 72; + let result = ""; + for (let i = 0; i < text.length; i += length) { + result += text.slice(i, i + length); + result += "\n"; } return result; } function rsaPrivateKey(key) { - return "-----BEGIN RSA PRIVATE KEY-----\n" + key + "-----END RSA PRIVATE KEY-----"; + return `-----BEGIN RSA PRIVATE KEY-----\n${key}-----END RSA PRIVATE KEY-----`; } function arrayBufferToBase64(buffer) { - var binary = "", - i; - var bytes = new Uint8Array(buffer); - var len = bytes.byteLength; - for (i = 0; i < len; i += 1) { + let binary = ""; + const bytes = new Uint8Array(buffer); + const len = bytes.byteLength; + for (let i = 0; i < len; i += 1) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); @@ -31,24 +30,23 @@ function generateKeyPair(alg, size, name) { .generateKey( { name: "RSASSA-PKCS1-v1_5", - modulusLength: 2048, //can be 1024, 2048, or 4096 + modulusLength: 2048, // can be 1024, 2048, or 4096 publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - hash: { name: "SHA-1" }, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" + hash: { name: "SHA-1" }, // can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" }, extractable, ["sign", "verify"] ) .then(key => { - var privateKey = window.crypto.subtle + const privateKey = window.crypto.subtle .exportKey("jwk", key.privateKey) .then(encodePrivateKey) .then(wrap) .then(rsaPrivateKey); - var publicKey = window.crypto.subtle.exportKey("jwk", key.publicKey).then(jwk => encodePublicKey(jwk, name)); + const publicKey = window.crypto.subtle.exportKey("jwk", key.publicKey).then(jwk => encodePublicKey(jwk, name)); return Promise.all([privateKey, publicKey]); }); } -module = window.module || {}; module.exportKey = { arrayBufferToBase64, generateKeyPair }; diff --git a/ssh-util.js b/ssh-util.js index 477aeb3..e919e12 100644 --- a/ssh-util.js +++ b/ssh-util.js @@ -1,4 +1,5 @@ -var base64urlDecode; +/* eslint no-bitwise: 0 */ +/* global base64urlDecode */ function arrayToString(a) { return String.fromCharCode.apply(null, a); @@ -21,25 +22,24 @@ function arrayToPem(a) { } function arrayToLen(a) { - var result = 0, - i; - for (i = 0; i < a.length; i += 1) { + let result = 0; + for (let i = 0; i < a.length; i += 1) { result = result * 256 + a[i]; } return result; } function integerToOctet(n) { - var result = []; - for (true; n > 0; n = n >> 8) { - result.push(n & 0xff); + const result = []; + for (let i = n; i > 0; i >>= 8) { + result.push(i & 0xff); } return result.reverse(); } function lenToArray(n) { - var oct = integerToOctet(n), - i; + const oct = integerToOctet(n); + let i; for (i = oct.length; i < 4; i += 1) { oct.unshift(0); } @@ -47,22 +47,22 @@ function lenToArray(n) { } function decodePublicKey(s) { - var split = s.split(" "); - var prefix = split[0]; + const split = s.split(" "); + const prefix = split[0]; if (prefix !== "ssh-rsa") { - throw "Unknown prefix:" + prefix; + throw new Error(`Unknown prefix: ${prefix}`); } - var buffer = pemToArray(split[1]); - var nameLen = arrayToLen(buffer.splice(0, 4)); - var type = arrayToString(buffer.splice(0, nameLen)); + const buffer = pemToArray(split[1]); + const nameLen = arrayToLen(buffer.splice(0, 4)); + const type = arrayToString(buffer.splice(0, nameLen)); if (type !== "ssh-rsa") { - throw "Unknown key type:" + type; + throw new Error(`Unknown key type: ${type}`); } - var exponentLen = arrayToLen(buffer.splice(0, 4)); - var exponent = buffer.splice(0, exponentLen); - var keyLen = arrayToLen(buffer.splice(0, 4)); - var key = buffer.splice(0, keyLen); - return { type: type, exponent: exponent, key: key, name: split[2] }; + const exponentLen = arrayToLen(buffer.splice(0, 4)); + const exponent = buffer.splice(0, exponentLen); + const keyLen = arrayToLen(buffer.splice(0, 4)); + const key = buffer.splice(0, keyLen); + return { type, exponent, key, name: split[2] }; } function checkHighestBit(v) { @@ -83,18 +83,18 @@ function jwkToInternal(jwk) { } function encodePublicKey(jwk, name) { - var k = jwkToInternal(jwk); + const k = jwkToInternal(jwk); k.name = name; - var keyLenA = lenToArray(k.key.length); - var exponentLenA = lenToArray(k.exponent.length); - var typeLenA = lenToArray(k.type.length); - var array = [].concat(typeLenA, stringToArray(k.type), exponentLenA, k.exponent, keyLenA, k.key); - var encoding = arrayToPem(array); - return k.type + " " + encoding + " " + k.name; + const keyLenA = lenToArray(k.key.length); + const exponentLenA = lenToArray(k.exponent.length); + const typeLenA = lenToArray(k.type.length); + const array = [].concat(typeLenA, stringToArray(k.type), exponentLenA, k.exponent, keyLenA, k.key); + const encoding = arrayToPem(array); + return `${k.type} ${encoding} ${k.name}`; } function asnEncodeLen(n) { - var result = []; + let result = []; if (n >> 7) { result = integerToOctet(n); result.unshift(0x80 + result.length); @@ -105,18 +105,17 @@ function asnEncodeLen(n) { } function encodePrivateKey(jwk) { - var order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"]; - var list = order.map(prop => { - var v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop]))); - var len = asnEncodeLen(v.length); + const order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"]; + const list = order.map(prop => { + const v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop]))); + const len = asnEncodeLen(v.length); return [0x02].concat(len, v); // int tag is 0x02 }); - var seq = [0x02, 0x01, 0x00]; // extra seq for SSH - seq = seq.concat.apply(seq, list); - var len = asnEncodeLen(seq.length); - var a = [0x30].concat(len, seq); // seq is 0x30 + let seq = [0x02, 0x01, 0x00]; // extra seq for SSH + seq = seq.concat(...list); + const len = asnEncodeLen(seq.length); + const a = [0x30].concat(len, seq); // seq is 0x30 return arrayToPem(a); } -module = window.module || {}; module.exports = { base64urlToArray, decodePublicKey, encodePublicKey, encodePrivateKey }; diff --git a/test/tests.js b/test/tests.js index 6d66dd6..bb4b9fc 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1,9 +1,11 @@ -var public_ssh = [ +/* global QUnit,arrayToString,pemToArray,arrayToPem,encodePrivateKey,lenToArray,arrayToLen,asnEncodeLen,decodePublicKey,decodePublicKey,encodePublicKey,stringToArray,base64urlDecode,checkHighestBit,integerToOctet */ + +const publicSsh = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwi36YMW0eDS3NXSAM/Gcs0txeLOcZE0LQmGPYmHX09Fm1FC9AdzvDWQIfwVylqNy8G6X8+pE0TMuWav4rQjtWRls3j43LdrXkfaTZV2PNJH0ki2zaCND3cz46hBR1bSwi3O4LoN0ZHXoC4ZXoMBXKtYEOg+9jS+pE3vu2QSPruiRROTOYYvrjWx0Bwi8DJc90TmNVeqvPjewPAm4qaTdmh96jIgJQq+vAdhDHu90i31Kl3JUF94x6pzFmg8ZyXOv0Py2GtK9c5To3C33FXI8yTm/sf2Bp7fwd3MEGNcdVNqa7Tt0z2u5Jcmsws93SZuj4iVjbR6xqme9EmIa3BTB7 name", "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuew50MaphQgiuM6H7zxMspNojI2Ujf77MuWlAjmw1JxcTkfE7JKzV+9fqmESJNtnZSr3+I2dxQhJ72jttrz+2dFt9ol91muTPWzKrA8XXIBH2o7sEJ+QB8/q7S03d+Zgw6tlo+qdXLOWcKqL5MJhYwzTFEdGTSMF00cBFadcpDq1xFPygGTHRa7m3pK723nGz7TMGWmtBK2bHx+Zlp7geLK/7hl+NRG1lTyIbtdkP2T4Y81Z0bhz9kNHroUei3MFD6HvN93qMJWl3/LZZzTb++1BedNeybGKqbtsB3xp0v3c6bQy49wR3RwrAwL03AKbCwTawAufSeoXyRI+rtgZ/ name", "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpNNtjZeldPuJ5ZgcjO4i6eSJb6kiuf1sULOoWaW9acwSxAfqrmN6Hn7VGg3GK3kSKJUmBKMsF2u+ECajVBec+OTMlbL7oZrYNl2neUYsI7O0G/8lpozZjADYu8CaMqVSAeTa3ORga9Ht/qgCpqXIyEcTsFSbZ45hhaZF0fXQ0GHDCkV/ylBduQHxheCe1SPBSWIO2BwqSlGx/Q76lkL/BnGdcx7xVi3h2yNbEGxqzFuPK75VADZfWria4x09rTqvu41GWIyqzFcbB7BxNImVNh6WVk/qKTcXbfWwH8ck9Cd5bX9g36QaImZ6tW8i/bl3o75bGgP2hSWpsNx8CMVn9 name", ]; -var public_ssh_decoded = [ +const publicSshDecoded = [ { type: "ssh-rsa", exponent: [1, 0, 1], @@ -270,7 +272,7 @@ var public_ssh_decoded = [ }, ]; -var jwk_public = { +const jwkPublic = { alg: "RS1", e: "AQAB", ext: true, @@ -280,7 +282,7 @@ var jwk_public = { "3PWJ6uDsFPgQo67of3IYw0Svyq95SNh9GS-2gorv68GxWIYYeAShaG_UtTf8mvf6u-VIUr54Re2FoLc78ICR3nRhFH5D1_fNaP9hkMAHBqaJ8ATiq4d7-PfeXTCi0yY0qfWkGjuPtOC3IK7WmnEkiA5qUVpy0oHFPiqoAyNynWJRDFJka00JEpM1QFyF1Tz3PEGp0XlFnClY48iJG9UqXlDgaysnG3ro2sDm8ftva0IjX1Sp7Z9FyWQci-yOYfST00wKHQd7z5-Eo3cTd5M0BhcVXeR0gprdK1TTDLZLznFJQ36HYwrUFEXvTyme6vkZfNPRb0z8KPq5Gs7dujE_cw", }; -var jwk_private = { +const jwkPrivate = { kty: "RSA", kid: "juliet@capulet.lit", use: "enc", @@ -301,76 +303,60 @@ var jwk_private = { "lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8", }; -var tags = { - SEQUENCE: 0x30, - INTEGER: 0x02, - BIT_STRING: 0x03, - OCTET_STRING: 0x04, - NULL: 0x05, - OBJECT_IDENTIFIER: 0x06, - SET: 0x11, - PrintableString: 0x13, - T61String: 0x14, - IA5String: 0x16, - UTCTime: 0x17, -}; - -QUnit.test("array to PEM", function(assert) { - var a = [1, 2, 3]; - var p = arrayToPem(a); - var a2 = pemToArray(p); +QUnit.test("array to PEM", assert => { + const a = [1, 2, 3]; + const p = arrayToPem(a); + const a2 = pemToArray(p); assert.deepEqual(a2, a, "can you count?"); }); -QUnit.test("array to String", function(assert) { - var a = "ssh-rsa".split("").map(function(c) { - return c.charCodeAt(); - }); +QUnit.test("array to String", assert => { + const a = "ssh-rsa".split("").map(c => c.charCodeAt()); assert.equal(arrayToString(pemToArray(arrayToPem(a))), "ssh-rsa"); }); -QUnit.test("lenToArray", function(assert) { - var a = 66051; +QUnit.test("lenToArray", assert => { + const a = 66051; assert.deepEqual(lenToArray(a), [0, 1, 2, 3]); }); -QUnit.test("arrayToLen", function(assert) { - var a = [0, 1, 2, 3]; +QUnit.test("arrayToLen", assert => { + const a = [0, 1, 2, 3]; assert.deepEqual(arrayToLen(a), 66051); }); -public_ssh.forEach(function(public, index) { - QUnit.test("decoding ssh public key " + index, function(assert) { - var key = decodePublicKey(public); +publicSsh.forEach((pub, index) => { + QUnit.test(`decoding ssh public key ${index}`, assert => { + const key = decodePublicKey(pub); assert.equal(key.type, "ssh-rsa", "type"); assert.equal(key.name, "name", "name"); if (index === 0) { - assert.deepEqual(key.key, public_ssh_decoded[0].key, "key"); + assert.deepEqual(key.key, publicSshDecoded[0].key, "key"); } }); }); -QUnit.test("Encoding ssh public key", function(assert) { - var result = encodePublicKey(jwk_public, "name"); +QUnit.test("Encoding ssh public key", assert => { + const result = encodePublicKey(jwkPublic, "name"); assert.equal( result, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDc9Ynq4OwU+BCjruh/chjDRK/Kr3lI2H0ZL7aCiu/rwbFYhhh4BKFob9S1N/ya9/q75UhSvnhF7YWgtzvwgJHedGEUfkPX981o/2GQwAcGponwBOKrh3v4995dMKLTJjSp9aQaO4+04LcgrtaacSSIDmpRWnLSgcU+KqgDI3KdYlEMUmRrTQkSkzVAXIXVPPc8QanReUWcKVjjyIkb1SpeUOBrKycbeujawObx+29rQiNfVKntn0XJZByL7I5h9JPTTAodB3vPn4SjdxN3kzQGFxVd5HSCmt0rVNMMtkvOcUlDfodjCtQURe9PKZ7q+Rl809FvTPwo+rkazt26MT9z name" ); }); -QUnit.test("base64url", function(assert) { - var result = stringToArray(base64urlDecode(jwk_public.n)); +QUnit.test("base64url", assert => { + const result = stringToArray(base64urlDecode(jwkPublic.n)); assert.equal(result.length, 256); }); -QUnit.test("high bit", function(assert) { +QUnit.test("high bit", assert => { assert.deepEqual(checkHighestBit([0x80]), [0x00, 0x80]); assert.deepEqual(checkHighestBit([0x0f]), [0x0f]); }); -QUnit.test("jwk", function(assert) { - var sshkey = encodePublicKey(jwk_public, "name"); - assert.deepEqual(stringToArray(base64urlDecode(jwk_public.e)), [1, 0, 1]); +QUnit.test("jwk", assert => { + const sshkey = encodePublicKey(jwkPublic, "name"); + assert.deepEqual(stringToArray(base64urlDecode(jwkPublic.e)), [1, 0, 1]); assert.equal( sshkey, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDc9Ynq4OwU+BCjruh/chjDRK/Kr3lI2H0ZL7aCiu/rwbFYhhh4BKFob9S1N/ya9/q75UhSvnhF7YWgtzvwgJHedGEUfkPX981o/2GQwAcGponwBOKrh3v4995dMKLTJjSp9aQaO4+04LcgrtaacSSIDmpRWnLSgcU+KqgDI3KdYlEMUmRrTQkSkzVAXIXVPPc8QanReUWcKVjjyIkb1SpeUOBrKycbeujawObx+29rQiNfVKntn0XJZByL7I5h9JPTTAodB3vPn4SjdxN3kzQGFxVd5HSCmt0rVNMMtkvOcUlDfodjCtQURe9PKZ7q+Rl809FvTPwo+rkazt26MT9z name" @@ -382,9 +368,9 @@ QUnit.test("jwk", function(assert) { { len: 0x104, octet: [0x01, 0x04] }, { len: 0xff32, octet: [0xff, 0x32] }, { len: 0x1000000, octet: [1, 0, 0, 0] }, - { len: 0x7fffffff, octet: [0x7f, 0xff, 0xff, 0xff] }, //biggest one -].forEach(function(t) { - QUnit.test("Integer to Octet:" + t.len, function(assert) { + { len: 0x7fffffff, octet: [0x7f, 0xff, 0xff, 0xff] }, // biggest one +].forEach(t => { + QUnit.test(`Integer to Octet: ${t.len}`, assert => { assert.deepEqual(integerToOctet(t.len), t.octet, t.len); }); }); @@ -395,14 +381,14 @@ QUnit.test("jwk", function(assert) { { len: 0x134, asn: [0x80 + 2, 0x01, 0x34] }, { len: 0x12345, asn: [0x80 + 3, 0x01, 0x23, 0x45] }, { len: 0x123456, asn: [0x80 + 3, 0x12, 0x34, 0x56] }, -].forEach(function(t) { - QUnit.test("ASN.1 Len Writing:" + t.len, function(assert) { +].forEach(t => { + QUnit.test(`ASN.1 Len Writing: ${t.len}`, assert => { assert.deepEqual(asnEncodeLen(t.len), t.asn, t.len); }); }); -QUnit.test("encodePrivateKey", function(assert) { - var encoded = encodePrivateKey(jwk_private); - console.log(encoded); +QUnit.test("encodePrivateKey", assert => { + encodePrivateKey(jwkPrivate); + // console.log(encoded); assert.ok(true); });