Name: js-handler/node_modules/restify/node_modules/http-signature/lib/util.js
| 1: | // Copyright 2012 Joyent, Inc. All rights reserved. |
| 2: | |
| 3: | var assert = require('assert-plus'); |
| 4: | var crypto = require('crypto'); |
| 5: | |
| 6: | var asn1 = require('asn1'); |
| 7: | var ctype = require('ctype'); |
| 8: | |
| 9: | |
| 10: | |
| 11: | ///--- Helpers |
| 12: | |
| 13: | function readNext(buffer, offset) { |
| 14: | var len = ctype.ruint32(buffer, 'big', offset); |
| 15: | offset += 4; |
| 16: | |
| 17: | var newOffset = offset + len; |
| 18: | |
| 19: | return { |
| 20: | data: buffer.slice(offset, newOffset), |
| 21: | offset: newOffset |
| 22: | }; |
| 23: | } |
| 24: | |
| 25: | |
| 26: | function writeInt(writer, buffer) { |
| 27: | writer.writeByte(0x02); // ASN1.Integer |
| 28: | writer.writeLength(buffer.length); |
| 29: | |
| 30: | for (var i = 0; i < buffer.length; i++) |
| 31: | writer.writeByte(buffer[i]); |
| 32: | |
| 33: | return writer; |
| 34: | } |
| 35: | |
| 36: | |
| 37: | function rsaToPEM(key) { |
| 38: | var buffer; |
| 39: | var der; |
| 40: | var exponent; |
| 41: | var i; |
| 42: | var modulus; |
| 43: | var newKey = ''; |
| 44: | var offset = 0; |
| 45: | var type; |
| 46: | var tmp; |
| 47: | |
| 48: | try { |
| 49: | buffer = new Buffer(key.split(' ')[1], 'base64'); |
| 50: | |
| 51: | tmp = readNext(buffer, offset); |
| 52: | type = tmp.data.toString(); |
| 53: | offset = tmp.offset; |
| 54: | |
| 55: | if (type !== 'ssh-rsa') |
| 56: | throw new Error('Invalid ssh key type: ' + type); |
| 57: | |
| 58: | tmp = readNext(buffer, offset); |
| 59: | exponent = tmp.data; |
| 60: | offset = tmp.offset; |
| 61: | |
| 62: | tmp = readNext(buffer, offset); |
| 63: | modulus = tmp.data; |
| 64: | } catch (e) { |
| 65: | throw new Error('Invalid ssh key: ' + key); |
| 66: | } |
| 67: | |
| 68: | // DER is a subset of BER |
| 69: | der = new asn1.BerWriter(); |
| 70: | |
| 71: | der.startSequence(); |
| 72: | |
| 73: | der.startSequence(); |
| 74: | der.writeOID('1.2.840.113549.1.1.1'); |
| 75: | der.writeNull(); |
| 76: | der.endSequence(); |
| 77: | |
| 78: | der.startSequence(0x03); // bit string |
| 79: | der.writeByte(0x00); |
| 80: | |
| 81: | // Actual key |
| 82: | der.startSequence(); |
| 83: | writeInt(der, modulus); |
| 84: | writeInt(der, exponent); |
| 85: | der.endSequence(); |
| 86: | |
| 87: | // bit string |
| 88: | der.endSequence(); |
| 89: | |
| 90: | der.endSequence(); |
| 91: | |
| 92: | tmp = der.buffer.toString('base64'); |
| 93: | for (i = 0; i < tmp.length; i++) { |
| 94: | if ((i % 64) === 0) |
| 95: | newKey += '\n'; |
| 96: | newKey += tmp.charAt(i); |
| 97: | } |
| 98: | |
| 99: | if (!/\\n$/.test(newKey)) |
| 100: | newKey += '\n'; |
| 101: | |
| 102: | return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n'; |
| 103: | } |
| 104: | |
| 105: | |
| 106: | function dsaToPEM(key) { |
| 107: | var buffer; |
| 108: | var offset = 0; |
| 109: | var tmp; |
| 110: | var der; |
| 111: | var newKey = ''; |
| 112: | |
| 113: | var type; |
| 114: | var p; |
| 115: | var q; |
| 116: | var g; |
| 117: | var y; |
| 118: | |
| 119: | try { |
| 120: | buffer = new Buffer(key.split(' ')[1], 'base64'); |
| 121: | |
| 122: | tmp = readNext(buffer, offset); |
| 123: | type = tmp.data.toString(); |
| 124: | offset = tmp.offset; |
| 125: | |
| 126: | /* JSSTYLED */ |
| 127: | if (!/^ssh-ds[as].*/.test(type)) |
| 128: | throw new Error('Invalid ssh key type: ' + type); |
| 129: | |
| 130: | tmp = readNext(buffer, offset); |
| 131: | p = tmp.data; |
| 132: | offset = tmp.offset; |
| 133: | |
| 134: | tmp = readNext(buffer, offset); |
| 135: | q = tmp.data; |
| 136: | offset = tmp.offset; |
| 137: | |
| 138: | tmp = readNext(buffer, offset); |
| 139: | g = tmp.data; |
| 140: | offset = tmp.offset; |
| 141: | |
| 142: | tmp = readNext(buffer, offset); |
| 143: | y = tmp.data; |
| 144: | } catch (e) { |
| 145: | console.log(e.stack); |
| 146: | throw new Error('Invalid ssh key: ' + key); |
| 147: | } |
| 148: | |
| 149: | // DER is a subset of BER |
| 150: | der = new asn1.BerWriter(); |
| 151: | |
| 152: | der.startSequence(); |
| 153: | |
| 154: | der.startSequence(); |
| 155: | der.writeOID('1.2.840.10040.4.1'); |
| 156: | |
| 157: | der.startSequence(); |
| 158: | writeInt(der, p); |
| 159: | writeInt(der, q); |
| 160: | writeInt(der, g); |
| 161: | der.endSequence(); |
| 162: | |
| 163: | der.endSequence(); |
| 164: | |
| 165: | der.startSequence(0x03); // bit string |
| 166: | der.writeByte(0x00); |
| 167: | writeInt(der, y); |
| 168: | der.endSequence(); |
| 169: | |
| 170: | der.endSequence(); |
| 171: | |
| 172: | tmp = der.buffer.toString('base64'); |
| 173: | for (var i = 0; i < tmp.length; i++) { |
| 174: | if ((i % 64) === 0) |
| 175: | newKey += '\n'; |
| 176: | newKey += tmp.charAt(i); |
| 177: | } |
| 178: | |
| 179: | if (!/\\n$/.test(newKey)) |
| 180: | newKey += '\n'; |
| 181: | |
| 182: | return '-----BEGIN PUBLIC KEY-----' + newKey + '-----END PUBLIC KEY-----\n'; |
| 183: | } |
| 184: | |
| 185: | |
| 186: | ///--- API |
| 187: | |
| 188: | module.exports = { |
| 189: | |
| 190: | /** |
| 191: | * Converts an OpenSSH public key (rsa only) to a PKCS#8 PEM file. |
| 192: | * |
| 193: | * The intent of this module is to interoperate with OpenSSL only, |
| 194: | * specifically the node crypto module's `verify` method. |
| 195: | * |
| 196: | * @param {String} key an OpenSSH public key. |
| 197: | * @return {String} PEM encoded form of the RSA public key. |
| 198: | * @throws {TypeError} on bad input. |
| 199: | * @throws {Error} on invalid ssh key formatted data. |
| 200: | */ |
| 201: | sshKeyToPEM: function sshKeyToPEM(key) { |
| 202: | assert.string(key, 'ssh_key'); |
| 203: | |
| 204: | /* JSSTYLED */ |
| 205: | if (/^ssh-rsa.*/.test(key)) |
| 206: | return rsaToPEM(key); |
| 207: | |
| 208: | /* JSSTYLED */ |
| 209: | if (/^ssh-ds[as].*/.test(key)) |
| 210: | return dsaToPEM(key); |
| 211: | |
| 212: | throw new Error('Only RSA and DSA public keys are allowed'); |
| 213: | }, |
| 214: | |
| 215: | |
| 216: | /** |
| 217: | * Generates an OpenSSH fingerprint from an ssh public key. |
| 218: | * |
| 219: | * @param {String} key an OpenSSH public key. |
| 220: | * @return {String} key fingerprint. |
| 221: | * @throws {TypeError} on bad input. |
| 222: | * @throws {Error} if what you passed doesn't look like an ssh public key. |
| 223: | */ |
| 224: | fingerprint: function fingerprint(key) { |
| 225: | assert.string(key, 'ssh_key'); |
| 226: | |
| 227: | var pieces = key.split(' '); |
| 228: | if (!pieces || !pieces.length || pieces.length < 2) |
| 229: | throw new Error('invalid ssh key'); |
| 230: | |
| 231: | var data = new Buffer(pieces[1], 'base64'); |
| 232: | |
| 233: | var hash = crypto.createHash('md5'); |
| 234: | hash.update(data); |
| 235: | var digest = hash.digest('hex'); |
| 236: | |
| 237: | var fp = ''; |
| 238: | for (var i = 0; i < digest.length; i++) { |
| 239: | if (i && i % 2 === 0) |
| 240: | fp += ':'; |
| 241: | |
| 242: | fp += digest[i]; |
| 243: | } |
| 244: | |
| 245: | return fp; |
| 246: | } |
| 247: | |
| 248: | |
| 249: | }; |
