Browse files

moved everything around

  • Loading branch information...
1 parent 6b2d68c commit 0a89d7ee10dfc94358b2a34da683557d609f3a51 @substack committed Mar 1, 2011
Showing with 152 additions and 126 deletions.
  1. +85 −27 dss.js
  2. +30 −0 format.js
  3. +35 −66 index.js
  4. +2 −33 test/keys.js
View
112 dss.js
@@ -1,45 +1,103 @@
var assert = require('assert');
+var crypto = require('crypto');
-var bigint = require('bigint');
-var put = require('put');
var Hash = require('hashish');
+var bigint = require('bigint');
+
var Buffers = require('buffers');
+var Binary = require('binary');
-// Generate two primes p and q to the Digital Signature Standard (DSS)
-// http://www.itl.nist.gov/fipspubs/fip186.htm appendix 2.2
+var Put = require('put');
+function pack (buf) {
+ return Put().word32be(buf.length).put(buf).buffer();
+}
+
+var Format = require('./format');
var exports = module.exports = DSS;
-function DSS (fields) {
- if (!(this instanceof DSS)) return new DSS(fields);
+function DSS (keys) {
+ if (!(this instanceof DSS)) return new DSS(keys);
+
+ if (!keys.public) throw new Error('Public key not specified');
+ if (!keys.private) throw new Error('Private key not specified');
- this.fields = Hash.merge(fields, {
- k : function (e) { return e.powm(ref.y, ref.p) },
- f : fields.g.powm(fields.y, fields.p),
+ this.algorithm = 'dss';
+ this.keys = keys;
+ this.fields = { x : bigint.fromBuffer(keys.private) };
+
+ var buffers = Binary.parse(keys.public)
+ .word32be('length.p').buffer('buffers.p', 'length.p')
+ .word32be('length.q').buffer('buffers.q', 'length.q')
+ .word32be('length.g').buffer('buffers.g', 'length.g')
+ .word32be('length.y').buffer('buffers.y', 'length.y')
+ .vars.buffers
+ ;
+
+ 'pqgy'.split('').forEach(function (name) {
+ this.fields[name] = bigint.fromBuffer(buffers[name]);
});
+
+ if (!this.valid()) throw new Error('Public and private keys disagree');
}
-DSS.prototype.data = function (privPub) {
- var p = (privPub || '').toUpperCase();
-
- if (p === 'PRIVATE') {
- return this.fields.x.toBuffer().toString('base64');
- }
- else if (p === 'PUBLIC') {
- var fields = this.fields;
- return Buffers(exports.fields.public.map(function (name) {
- return fields[name].toBuffer('mpint')
- })).slice();
- }
- else throw new Error('Specify "private" or "public"')
+DSS.fromFields = function (fields) {
+ var dss = Object.create(DSS.prototype);
+
+ dss.fields = fields;
+
+ var bufs = 'pqgy'.split('').map(function (name) {
+ return fields[name].toBuffer('mpint')
+ });
+
+ var id = pack(new Buffer('ssh-dss'));
+ bufs.unshift(id);
+
+ dss.keys = {
+ private : fields.x.toBuffer(),
+ public : Buffers(bufs).slice(),
+ };
+
+ if (!dss.valid()) throw new Error('Invalid fields');
+
+ return dss;
+}
+
+DSS.prototype.valid = function () {
+ var y = this.fields.g.powm(this.fields.x, this.fields.p);
+ return y.toString() === this.fields.y.toString();
};
-exports.fields = {
- public : [ 'p', 'q', 'g', 'y' ],
- private : [ 'x' ],
+DSS.prototype.challenge = function (ebuf, params) {
+ var e = bigint.fromBuffer(ebuf);
+ var K = e.powm(this.fields.y, this.fields.p).toBuffer('mpint');
+ var f = this.fields.g.powm(this.fields.y, this.fields.p).toBuffer('mpint');
+
+ var K_S = pack(this.keys.public);
+
+ var V_C = pack(params.client.ident);
+ var V_S = pack(params.server.ident);
+ var I_C = pack(params.client.kexinit);
+ var I_S = pack(params.server.kexinit);
+
+ var sign = crypto.createSign('DSA');
+
+ [ V_C, V_S, I_C, I_S, K_S, ebuf, f, K ]
+ .forEach(function (buf) { sign.update(buf) });
+
+ var signed = new Buffer(sign.sign(this.keys.private, 'base64'), 'base64');
+
+ return Buffers([ K_S, f.toBuffer('mpint'), signed ]).slice();
+};
+
+DSS.prototype.format = function (format, kt, email) {
+ return Format(format, this.keys, kt, email);
};
-exports.generate = function () {
+// Generate two primes p and q to the Digital Signature Standard (DSS)
+// http://www.itl.nist.gov/fipspubs/fip186.htm appendix 2.2
+
+DSS.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);
@@ -64,5 +122,5 @@ exports.generate = function () {
var x = q.sub(1).rand().add(1); // private key
var y = g.powm(x, p); // public key
- return module.exports({ p : p, q : q, g : g, y : y, x : x });
+ return DSS({ p : p, q : q, g : g, y : y, x : x });
};
View
30 format.js
@@ -0,0 +1,30 @@
+module.exports = function (format, keypair, kt, email) {
+ if (kt !== 'private' && kt !== 'public') {
+ throw new Error('Must specify private or public');
+ }
+ var data = keypair.keys[kt].toString('base64');
+
+ if (format === 'ssh2') {
+
+ var algo = keypair.algorithm.toUpperCase();
+ if (algo === 'DSS') algo = 'DSA';
+
+ var wrapped = [];
+ for (var i = 0; i < data.length; i += 64) {
+ wrapped.push(data.slice(i, i + 64));
+ }
+
+ var KT = kt.toUpperCase();
+ return [
+ [ '-----BEGIN', algo, KT, 'KEY-----' ].join(' '),
+ wrapped.join('\n'),
+ [ '-----END', algo, KT, 'KEY-----' ].join(' '),
+ ''
+ ].join('\n');
+ }
+ else if (format === 'openssh') {
+ var id = 'ssh-' + keypair.algorithm;
+ return [ id, data, kt || '' ].join(' ') + '\r\n';
+ }
+ else throw new Error('Unrecognized format ' + format.toString());
+};
View
101 index.js
@@ -1,86 +1,55 @@
-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];
+var exports = module.exports = function (keys) {
+ if (!keys.public) throw new Error('Public key not specified');
+ if (!keys.private) throw new Error('Private key not specified');
- 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');
+ var pub = exports.parse(keys.public);
+ if (!pub) throw new Error('Failed to parse public key');
+
+ var priv = exports.parse(keys.private);
+ if (!priv) throw new Error('Failed to parse private key');
+
+ if (pub.keyType != priv.keyType) throw new Error('key types disagree');
+
+ if (!algos[pub.keyType]) {
+ throw new Error('Unsupported key type ' + pub.keyType.toString());
}
-}
+
+ return algos[pub.keyType]({ public : pub, private : priv });
+};
-Key.pack = function (algorithm, vars) {
- var bufs = vars.map(function (bigi) { return bigi.toBuffer('mpint') });
- var packed = Buffers(bufs).slice();
- return new Key(packed, algorithm);
+var algos = exports.algorithms = {
+ dss : require('./dss'),
};
-Key.parse = function (body) {
+exports.generate = function (algo) {
+ if (!algos[algo]) throw new Error('Unsupported key type ' + algo);
+ return algos[algo].generate();
+};
+
+exports.parse = function (body) {
var ssh2 = body.match(/^-----BEGIN (\S+) (PRIVATE|PUBLIC) KEY-----\n/);
if (ssh2) {
- return new Key(
- body
- .toString()
- .split('\n')
+ return {
+ algorithm : ssh2[1].toLowerCase(),
+ keyType : ssh2[2].toLowerCase(),
+ data : body.toString().split('\n')
.filter(function (line) {
return line.match(/^\S+\s*$/)
})
.join('')
.replace(/\s+/g,'')
- , ssh2[1]
- );
+ ,
+ };
}
var openssh = body.match(/^ssh-(\S+)\s+(\S+)/);
if (openssh) {
- return new Key(openssh[2], openssh[1]);
+ return {
+ algorithm : openssh[1],
+ keyType : undefined,
+ data : openssh[2],
+ };
}
return undefined;
};
-
-Key.prototype.toString = function (encoding) {
- return this.data.toString(encoding || 'base64')
-};
-
-Key.prototype.format = function (format, aux) {
- var fmt = (format || 'ssh2').toLowerCase();
- if (fmt === 'ssh2') {
- var p = (aux || '').toUpperCase();
- if (p !== 'PRIVATE' && p !== 'PUBLIC') {
- throw new Error('Must specify private or public for ssh2');
- }
-
- var algo = this.algorithm.toUpperCase();
- if (algo === 'DSS') algo = 'DSA';
-
- var wrapped = [];
- var data = this.data.toString('base64');
- for (var i = 0; i < data.length; i += 64) {
- wrapped.push(data.slice(i, 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';
- var data = this.data.toString('base64');
- return [ 'ssh-' + id, data, aux || '' ].join(' ') + '\r\n';
- }
- else throw new Error('Unrecognized format ' + format.toString());
-};
View
35 test/keys.js
@@ -5,42 +5,11 @@ var assert = require('assert');
var bigint = require('bigint');
var Buffers = require('buffers');
-exports.createKey = function () {
- assert.eql(
- new keyx('test', 'dss').data,
- new Buffer('test', 'base64')
- );
-
- assert.eql(
- new keyx('test', 'dss-base64').data,
- new Buffer('test', 'base64')
- );
-
- assert.eql(
- new keyx('abcdef', 'dss-hex').data,
- new Buffer('abcdef', 'hex')
- );
-};
-
exports.packParseKey = function () {
- var keypair = dss.generate();
- var pubkey = keypair.data('public');
- var privkey = keypair.data('private');
-
- var fields = keypair.fields;
-
- var vars = [ fields.p, fields.q, fields.g, fields.y ];
- var pubdata = Buffers(vars.map(function (x) {
- return x.toBuffer('mpint')
- })).slice();
-
- var key = keyx.pack('dss', vars);
- assert.eql(key.data, pubdata);
-
- assert.eql(key.toString(), pubdata.toString('base64'));
+ var keypair = keyx.generate('dss');
assert.throws(function () {
- key.format('ssh2');
+ keypair.format('ssh2');
});
var ssh2 = key.format('ssh2', 'private');

0 comments on commit 0a89d7e

Please sign in to comment.