diff options
Diffstat (limited to 'node_modules/http-signature/lib/util.js')
-rw-r--r-- | node_modules/http-signature/lib/util.js | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/node_modules/http-signature/lib/util.js b/node_modules/http-signature/lib/util.js new file mode 100644 index 000000000..2e1ce4d46 --- /dev/null +++ b/node_modules/http-signature/lib/util.js @@ -0,0 +1,306 @@ +// Copyright 2012 Joyent, Inc. All rights reserved. + +var assert = require('assert-plus'); +var crypto = require('crypto'); + +var asn1 = require('asn1'); +var ctype = require('ctype'); + + + +///--- Helpers + +function readNext(buffer, offset) { + var len = ctype.ruint32(buffer, 'big', offset); + offset += 4; + + var newOffset = offset + len; + + return { + data: buffer.slice(offset, newOffset), + offset: newOffset + }; +} + + +function writeInt(writer, buffer) { + writer.writeByte(0x02); // ASN1.Integer + writer.writeLength(buffer.length); + + for (var i = 0; i < buffer.length; i++) + writer.writeByte(buffer[i]); + + return writer; +} + + +function rsaToPEM(key) { + var buffer; + var der; + var exponent; + var i; + var modulus; + var newKey = ''; + var offset = 0; + var type; + var tmp; + + try { + buffer = new Buffer(key.split(' ')[1], 'base64'); + + tmp = readNext(buffer, offset); + type = tmp.data.toString(); + offset = tmp.offset; + + if (type !== 'ssh-rsa') + throw new Error('Invalid ssh key type: ' + type); + + tmp = readNext(buffer, offset); + exponent = tmp.data; + offset = tmp.offset; + + tmp = readNext(buffer, offset); + modulus = tmp.data; + } catch (e) { + throw new Error('Invalid ssh key: ' + key); + } + + // DER is a subset of BER + der = new asn1.BerWriter(); + + der.startSequence(); + + der.startSequence(); + der.writeOID('1.2.840.113549.1.1.1'); + der.writeNull(); + der.endSequence(); + + der.startSequence(0x03); // bit string + der.writeByte(0x00); + + // Actual key + der.startSequence(); + writeInt(der, modulus); + writeInt(der, exponent); + der.endSequence(); + + // bit string + der.endSequence(); + + der.endSequence(); + + tmp = der.buffer.toString('base64'); + for (i = 0; i < tmp.length; i++) { + if ((i % 64) === 0) + newKey += '\n'; + newKey += tmp.charAt(i); + } + + if (!/\\n$/.test(newKey)) + newKey += '\n'; + + return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n'; +} + + +function dsaToPEM(key) { + var buffer; + var offset = 0; + var tmp; + var der; + var newKey = ''; + + var type; + var p; + var q; + var g; + var y; + + try { + buffer = new Buffer(key.split(' ')[1], 'base64'); + + tmp = readNext(buffer, offset); + type = tmp.data.toString(); + offset = tmp.offset; + + /* JSSTYLED */ + if (!/^ssh-ds[as].*/.test(type)) + throw new Error('Invalid ssh key type: ' + type); + + tmp = readNext(buffer, offset); + p = tmp.data; + offset = tmp.offset; + + tmp = readNext(buffer, offset); + q = tmp.data; + offset = tmp.offset; + + tmp = readNext(buffer, offset); + g = tmp.data; + offset = tmp.offset; + + tmp = readNext(buffer, offset); + y = tmp.data; + } catch (e) { + console.log(e.stack); + throw new Error('Invalid ssh key: ' + key); + } + + // DER is a subset of BER + der = new asn1.BerWriter(); + + der.startSequence(); + + der.startSequence(); + der.writeOID('1.2.840.10040.4.1'); + + der.startSequence(); + writeInt(der, p); + writeInt(der, q); + writeInt(der, g); + der.endSequence(); + + der.endSequence(); + + der.startSequence(0x03); // bit string + der.writeByte(0x00); + writeInt(der, y); + der.endSequence(); + + der.endSequence(); + + tmp = der.buffer.toString('base64'); + for (var i = 0; i < tmp.length; i++) { + if ((i % 64) === 0) + newKey += '\n'; + newKey += tmp.charAt(i); + } + + if (!/\\n$/.test(newKey)) + newKey += '\n'; + + return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n'; +} + + +///--- API + +module.exports = { + + /** + * Converts an OpenSSH public key (rsa only) to a PKCS#8 PEM file. + * + * The intent of this module is to interoperate with OpenSSL only, + * specifically the node crypto module's `verify` method. + * + * @param {String} key an OpenSSH public key. + * @return {String} PEM encoded form of the RSA public key. + * @throws {TypeError} on bad input. + * @throws {Error} on invalid ssh key formatted data. + */ + sshKeyToPEM: function sshKeyToPEM(key) { + assert.string(key, 'ssh_key'); + + /* JSSTYLED */ + if (/^ssh-rsa.*/.test(key)) + return rsaToPEM(key); + + /* JSSTYLED */ + if (/^ssh-ds[as].*/.test(key)) + return dsaToPEM(key); + + throw new Error('Only RSA and DSA public keys are allowed'); + }, + + + /** + * Generates an OpenSSH fingerprint from an ssh public key. + * + * @param {String} key an OpenSSH public key. + * @return {String} key fingerprint. + * @throws {TypeError} on bad input. + * @throws {Error} if what you passed doesn't look like an ssh public key. + */ + fingerprint: function fingerprint(key) { + assert.string(key, 'ssh_key'); + + var pieces = key.split(' '); + if (!pieces || !pieces.length || pieces.length < 2) + throw new Error('invalid ssh key'); + + var data = new Buffer(pieces[1], 'base64'); + + var hash = crypto.createHash('md5'); + hash.update(data); + var digest = hash.digest('hex'); + + var fp = ''; + for (var i = 0; i < digest.length; i++) { + if (i && i % 2 === 0) + fp += ':'; + + fp += digest[i]; + } + + return fp; + }, + + /** + * Converts a PKGCS#8 PEM file to an OpenSSH public key (rsa) + * + * The reverse of the above function. + */ + pemToRsaSSHKey: function pemToRsaSSHKey(pem, comment) { + assert.equal('string', typeof (pem), 'typeof pem'); + + // chop off the BEGIN PUBLIC KEY and END PUBLIC KEY portion + var cleaned = pem.split('\n').slice(1, -2).join(''); + + var buf = new Buffer(cleaned, 'base64'); + + var der = new asn1.BerReader(buf); + + der.readSequence(); + der.readSequence(); + + var oid = der.readOID(); + assert.equal(oid, '1.2.840.113549.1.1.1', 'pem not in RSA format'); + + // Null -- XXX this probably isn't good practice + der.readByte(); + der.readByte(); + + // bit string sequence + der.readSequence(0x03); + der.readByte(); + der.readSequence(); + + // modulus + assert.equal(der.peek(), asn1.Ber.Integer, 'modulus not an integer'); + der._offset = der.readLength(der.offset + 1); + var modulus = der._buf.slice(der.offset, der.offset + der.length); + der._offset += der.length; + + // exponent + assert.equal(der.peek(), asn1.Ber.Integer, 'exponent not an integer'); + der._offset = der.readLength(der.offset + 1); + var exponent = der._buf.slice(der.offset, der.offset + der.length); + der._offset += der.length; + + // now, make the key + var type = new Buffer('ssh-rsa'); + var buffer = new Buffer(4 + type.length + 4 + modulus.length + + 4 + exponent.length); + var i = 0; + buffer.writeUInt32BE(type.length, i); i += 4; + type.copy(buffer, i); i += type.length; + buffer.writeUInt32BE(exponent.length, i); i += 4; + exponent.copy(buffer, i); i += exponent.length; + buffer.writeUInt32BE(modulus.length, i); i += 4; + modulus.copy(buffer, i); i += modulus.length; + + var s = (type.toString() + ' ' + buffer.toString('base64') + ' ' + + (comment || '')); + return s; + } +}; |