ssh-to-jwk.js/lib/ssh-parser.js

102 lines
2.4 KiB
JavaScript

'use strict';
var SSH = module.exports;
var Enc = require('./encoding.js');
SSH.parse = function (ssh) {
ssh = ssh.split(/\s+/g);
var result = { type: ssh[0], jwk: null, comment: ssh[2] || '' };
var buf = Enc.base64ToBuf(ssh[1]);
var els = SSH.parseElements(buf);
var typ = Enc.bufToBin(els[0]);
var len;
// RSA keys are all the same
if (SSH.types.rsa === typ) {
result.jwk = {
kty: 'RSA'
, n: Enc.bufToUrlBase64(els[2])
, e: Enc.bufToUrlBase64(els[1])
};
return result;
}
// EC keys are each different
if (SSH.types.p256 === typ) {
len = 32;
result.jwk = { kty: 'EC', crv: 'P-256' };
} else if (SSH.types.p384 === typ) {
len = 48;
result.jwk = { kty: 'EC', crv: 'P-384' };
} else {
throw new Error("Unsupported ssh public key type: "
+ Enc.bufToBin(els[0]));
}
// els[1] is just a repeat of a subset of els[0]
var x = els[2].slice(1, 1 + len);
var y = els[2].slice(1 + len, 1 + len + len);
// I don't think EC keys use 0x00 padding, but just in case
if (0x00 === x[0]) { x = x.slice(1); }
if (0x00 === y[0]) { y = y.slice(1); }
result.jwk.x = Enc.bufToUrlBase64(x);
result.jwk.y = Enc.bufToUrlBase64(y);
return result;
};
SSH.parseElements = function (buf) {
var fulllen = buf.byteLength || buf.length;
var offset = (buf.byteOffset || 0);
var i = 0;
var index = 0;
// using dataview to be browser-compatible (I do want _some_ code reuse)
var dv = new DataView(buf.buffer.slice(offset, offset + fulllen));
var els = [];
var el;
var len;
while (index < fulllen) {
i += 1;
if (i > 15) { throw new Error("15+ elements, probably not a public ssh key"); }
len = dv.getUint32(index, false);
index += 4;
el = buf.slice(index, index + len);
// remove BigUInt '00' prefix
if (0x00 === el[0]) {
el = el.slice(1);
}
els.push(el);
index += len;
}
if (fulllen !== index) {
throw new Error(els.map(function (b) {
return Enc.bufToHex(b);
}).join('\n') + "invalid ssh public key length");
}
return els;
};
SSH.types = {
// 19 '00000013'
// e c d s a - s h a 2 - n i s t p 2 5 6
// 65636473612d736861322d6e69737470323536
// 6e69737470323536
p256: 'ecdsa-sha2-nistp256'
// 19 '00000013'
// e c d s a - s h a 2 - n i s t p 3 8 4
// 65636473612d736861322d6e69737470333834
// 6e69737470323536
, p384: 'ecdsa-sha2-nistp384'
// 7 '00000007'
// s s h - r s a
// 7373682d727361
, rsa: 'ssh-rsa'
};