# substack/node-keyx

### Subversion checkout URL

You can clone with HTTPS or Subversion.

completely untested key thing

commit 61c67b33b25aab70b78fc45ab0ccfd757da03c53 0 parents
authored
Showing with 134 additions and 0 deletions.
1. +44 −0 dss.js
2. +1 −0  index.js
3. +89 −0 key.js
44 dss.js
 @@ -0,0 +1,44 @@ +var bigint = require('bigint'); +var put = require('put'); +var assert = require('assert'); + +// Generate two primes p and q to the Digital Signature Standard (DSS) +// http://www.itl.nist.gov/fipspubs/fip186.htm appendix 2.2 + +exports.generate = function () { + var q = bigint(2).pow(159).add(1).rand(bigint(2).pow(160)).nextPrime(); + var L = 512 + 64 * Math.floor(Math.random() * 8); + + do { + var X = bigint(2).pow(L-1).add(1).rand(bigint(2).pow(L)); + var c = X.mod(q.mul(2)); + var p = X.sub(c.sub(1)); // p is congruent to 1 % 2q somehow! + } while (p.lt(bigint.pow(2, L - 1)) || p.probPrime(50) === false) + + assert.ok(q.gt(bigint.pow(2,159)), 'q > 2**159'); + assert.ok(q.lt(bigint.pow(2,160)), 'q < 2**160'); + assert.ok(p.gt(bigint.pow(2,L-1)), 'p > 2**(L-1)'); + assert.ok(q.lt(bigint.pow(2,L)), 'p < 2**L'); + assert.ok(q.mul(p.sub(1).div(q)).add(1).eq(p), 'q divides p - 1'); + + do { + var e = p.sub(1).div(q); + var h = p.sub(2).rand().add(1); + var g = h.powm(e, p); + } while (g.eq(1)) + + var x = q.sub(1).rand().add(1); // private key + var y = g.powm(x, p); // public key + var f = g.powm(y, p); + + return { + // p, q, and g can be shared and re-used + p : p, q : q, g : g, y : y, + + session : function () { + return { + k : function (e) { return e.powm(y, p) }, + }; + }, + }; +};
1  index.js
 @@ -0,0 +1 @@ +exports.dss = require('./dss');
89 key.js
 @@ -0,0 +1,89 @@ +var Buffers = require('buffers'); + +module.exports = Key; + +function Key (keyData, format) { + if (!(this instanceof Key)) return new Key(keyData, format); + this.algorithm = format.split('-')[0]; + + if (typeof keyData === 'string') { + var encoding = format && format.split('-')[1] || 'base64'; + this.data = new Buffer(keyData, encoding); + } + else if (Buffer.isBuffer(keyData)) { + this.data = keyData; + } + else { + throw new Error('keyData must be a string or Buffer'); + } +} + +Key.pack = function (algorithm) { + var bufs = [].slice.call(arguments, 1) + .map(function (bigi) { return bigi.toBuffer('mpint') }); + var packed = Buffers(bufs).slice(); + return new Key(packed, algorithm); +}; + +Key.parse = function (body) { + var ssh2 = body.match(/^-----BEGIN (\S+) (PRIVATE|PUBLIC) KEY-----\n/); + if (ssh2) { + return new Key( + body + .toString() + .split('\n') + .slice(1,-2) + .filter(function (line) { return !line.match(/:/) }) + .join('') + .replace(/\s+/g,'') + , ssh2[1] + ); + } + + var openssh = body.match(/^ssh-(\S+)\s+(\S+)/); + if (openssh) { + return new Key(openssh[2], openssh[1]); + } + + return undefined; +}; + +Key.prototype.toString = function (encoding) { + return this.fields + .reduce(function (put, x) { + return put.put(x.toBuffer('mpint')) + }, Put().word32be(id.length).put(new Buffer(id))) + .buffer().toString(encoding || 'base64') + ; +}; + +Key.prototype.format = function (format, privOrPub, cols) { + var fmt = (format || 'ssh2').toLowerCase(); + if (fmt === 'ssh2') { + var p = (privOrPub || '').toUpperCase(); + if (p !== 'PRIVATE' && p !== 'PUBLIC') { + throw new Error('Must specify private or public for ssh2'); + } + + var algo = this.algorithm.toUpperCase(); + + var wrapped = []; + for (var i = 0; i < this.data.length; i += 64) { + wrapped.push(this.data.slice( + i, Math.min(this.data.length, i + 64) + )); + } + + return [ + [ '-----BEGIN', algo, p, 'KEY-----' ].join(' '), + wrapped.join('\n'), + [ '-----END', algo, p, 'KEY-----' ].join(' '), + ].join('\n'); + } + else if (fmt === 'openssh') { + var id = this.algorithm; + if (id === 'dsa') id = 'dss'; + return [ 'ssh-' + id, this.data, email || '' ].join(' ') + '\r\n'; + } + else throw new Error('Unrecognized format ' + format.toString()); +};