utils.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. var crypto = require('crypto');
  2. var Ber = require('asn1').Ber;
  3. var BigInteger = require('./jsbn'); // only for converting PPK -> OpenSSL format
  4. var SSH_TO_OPENSSL = require('./constants').SSH_TO_OPENSSL;
  5. var RE_STREAM = /^arcfour/i;
  6. var RE_KEY_LEN = /(.{64})/g;
  7. // XXX the value of 2400 from dropbear is only for certain strings, not all
  8. // strings. for example the list strings used during handshakes
  9. var MAX_STRING_LEN = Infinity;//2400; // taken from dropbear
  10. var PPK_IV = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
  11. module.exports = {
  12. iv_inc: iv_inc,
  13. isStreamCipher: isStreamCipher,
  14. readInt: readInt,
  15. readString: readString,
  16. parseKey: require('./keyParser'),
  17. genPublicKey: genPublicKey,
  18. convertPPKPrivate: convertPPKPrivate,
  19. verifyPPKMAC: verifyPPKMAC,
  20. decryptKey: decryptKey,
  21. DSASigBERToBare: DSASigBERToBare,
  22. DSASigBareToBER: DSASigBareToBER,
  23. ECDSASigASN1ToSSH: ECDSASigASN1ToSSH,
  24. ECDSASigSSHToASN1: ECDSASigSSHToASN1,
  25. RSAKeySSHToASN1: RSAKeySSHToASN1,
  26. DSAKeySSHToASN1: DSAKeySSHToASN1,
  27. ECDSAKeySSHToASN1: ECDSAKeySSHToASN1
  28. };
  29. function iv_inc(iv) {
  30. var n = 12;
  31. var c = 0;
  32. do {
  33. --n;
  34. c = iv[n];
  35. if (c === 255)
  36. iv[n] = 0;
  37. else {
  38. iv[n] = ++c;
  39. return;
  40. }
  41. } while (n > 4);
  42. }
  43. function isStreamCipher(name) {
  44. return RE_STREAM.test(name);
  45. }
  46. function readInt(buffer, start, stream, cb) {
  47. var bufferLen = buffer.length;
  48. if (start < 0 || start >= bufferLen || (bufferLen - start) < 4) {
  49. stream && stream._cleanup(cb);
  50. return false;
  51. }
  52. return buffer.readUInt32BE(start, true);
  53. }
  54. function DSASigBERToBare(signature) {
  55. if (signature.length <= 40)
  56. return signature;
  57. // This is a quick and dirty way to get from BER encoded r and s that
  58. // OpenSSL gives us, to just the bare values back to back (40 bytes
  59. // total) like OpenSSH (and possibly others) are expecting
  60. var asnReader = new Ber.Reader(signature);
  61. asnReader.readSequence();
  62. var r = asnReader.readString(Ber.Integer, true);
  63. var s = asnReader.readString(Ber.Integer, true);
  64. var rOffset = 0;
  65. var sOffset = 0;
  66. if (r.length < 20) {
  67. var rNew = new Buffer(20);
  68. r.copy(rNew, 1);
  69. r = rNew;
  70. r[0] = 0;
  71. }
  72. if (s.length < 20) {
  73. var sNew = new Buffer(20);
  74. s.copy(sNew, 1);
  75. s = sNew;
  76. s[0] = 0;
  77. }
  78. if (r.length > 20 && r[0] === 0x00)
  79. rOffset = 1;
  80. if (s.length > 20 && s[0] === 0x00)
  81. sOffset = 1;
  82. var newSig = new Buffer((r.length - rOffset) + (s.length - sOffset));
  83. r.copy(newSig, 0, rOffset);
  84. s.copy(newSig, r.length - rOffset, sOffset);
  85. return newSig;
  86. }
  87. function DSASigBareToBER(signature) {
  88. if (signature.length > 40)
  89. return signature;
  90. // Change bare signature r and s values to ASN.1 BER values for OpenSSL
  91. var asnWriter = new Ber.Writer();
  92. asnWriter.startSequence();
  93. var r = signature.slice(0, 20);
  94. var s = signature.slice(20);
  95. if (r[0] & 0x80) {
  96. var rNew = new Buffer(21);
  97. rNew[0] = 0x00;
  98. r.copy(rNew, 1);
  99. r = rNew;
  100. } else if (r[0] === 0x00 && !(r[1] & 0x80)) {
  101. r = r.slice(1);
  102. }
  103. if (s[0] & 0x80) {
  104. var sNew = new Buffer(21);
  105. sNew[0] = 0x00;
  106. s.copy(sNew, 1);
  107. s = sNew;
  108. } else if (s[0] === 0x00 && !(s[1] & 0x80)) {
  109. s = s.slice(1);
  110. }
  111. asnWriter.writeBuffer(r, Ber.Integer);
  112. asnWriter.writeBuffer(s, Ber.Integer);
  113. asnWriter.endSequence();
  114. return asnWriter.buffer;
  115. }
  116. function ECDSASigASN1ToSSH(signature) {
  117. if (signature[0] === 0x00)
  118. return signature;
  119. // Convert SSH signature parameters to ASN.1 BER values for OpenSSL
  120. var asnReader = new Ber.Reader(signature);
  121. asnReader.readSequence();
  122. var r = asnReader.readString(Ber.Integer, true);
  123. var s = asnReader.readString(Ber.Integer, true);
  124. if (r === null || s === null)
  125. throw new Error('Invalid signature');
  126. var newSig = new Buffer(4 + r.length + 4 + s.length);
  127. newSig.writeUInt32BE(r.length, 0, true);
  128. r.copy(newSig, 4);
  129. newSig.writeUInt32BE(s.length, 4 + r.length, true);
  130. s.copy(newSig, 4 + 4 + r.length);
  131. return newSig;
  132. }
  133. function ECDSASigSSHToASN1(signature, self, callback) {
  134. // Convert SSH signature parameters to ASN.1 BER values for OpenSSL
  135. var r = readString(signature, 0, self, callback);
  136. if (r === false)
  137. return false;
  138. var s = readString(signature, signature._pos, self, callback);
  139. if (s === false)
  140. return false;
  141. var asnWriter = new Ber.Writer();
  142. asnWriter.startSequence();
  143. asnWriter.writeBuffer(r, Ber.Integer);
  144. asnWriter.writeBuffer(s, Ber.Integer);
  145. asnWriter.endSequence();
  146. return asnWriter.buffer;
  147. }
  148. function RSAKeySSHToASN1(key, self, callback) {
  149. // Convert SSH key parameters to ASN.1 BER values for OpenSSL
  150. var e = readString(key, key._pos, self, callback);
  151. if (e === false)
  152. return false;
  153. var n = readString(key, key._pos, self, callback);
  154. if (n === false)
  155. return false;
  156. var asnWriter = new Ber.Writer();
  157. asnWriter.startSequence();
  158. // algorithm
  159. asnWriter.startSequence();
  160. asnWriter.writeOID('1.2.840.113549.1.1.1'); // rsaEncryption
  161. // algorithm parameters (RSA has none)
  162. asnWriter.writeNull();
  163. asnWriter.endSequence();
  164. // subjectPublicKey
  165. asnWriter.startSequence(Ber.BitString);
  166. asnWriter.writeByte(0x00);
  167. asnWriter.startSequence();
  168. asnWriter.writeBuffer(n, Ber.Integer);
  169. asnWriter.writeBuffer(e, Ber.Integer);
  170. asnWriter.endSequence();
  171. asnWriter.endSequence();
  172. asnWriter.endSequence();
  173. return asnWriter.buffer;
  174. }
  175. function DSAKeySSHToASN1(key, self, callback) {
  176. // Convert SSH key parameters to ASN.1 BER values for OpenSSL
  177. var p = readString(key, key._pos, self, callback);
  178. if (p === false)
  179. return false;
  180. var q = readString(key, key._pos, self, callback);
  181. if (q === false)
  182. return false;
  183. var g = readString(key, key._pos, self, callback);
  184. if (g === false)
  185. return false;
  186. var y = readString(key, key._pos, self, callback);
  187. if (y === false)
  188. return false;
  189. var asnWriter = new Ber.Writer();
  190. asnWriter.startSequence();
  191. // algorithm
  192. asnWriter.startSequence();
  193. asnWriter.writeOID('1.2.840.10040.4.1'); // id-dsa
  194. // algorithm parameters
  195. asnWriter.startSequence();
  196. asnWriter.writeBuffer(p, Ber.Integer);
  197. asnWriter.writeBuffer(q, Ber.Integer);
  198. asnWriter.writeBuffer(g, Ber.Integer);
  199. asnWriter.endSequence();
  200. asnWriter.endSequence();
  201. // subjectPublicKey
  202. asnWriter.startSequence(Ber.BitString);
  203. asnWriter.writeByte(0x00);
  204. asnWriter.writeBuffer(y, Ber.Integer);
  205. asnWriter.endSequence();
  206. asnWriter.endSequence();
  207. return asnWriter.buffer;
  208. }
  209. function ECDSAKeySSHToASN1(key, self, callback) {
  210. // Convert SSH key parameters to ASN.1 BER values for OpenSSL
  211. var curve = readString(key, key._pos, self, callback);
  212. if (curve === false)
  213. return false;
  214. var Q = readString(key, key._pos, self, callback);
  215. if (Q === false)
  216. return false;
  217. var ecCurveOID;
  218. switch (curve.toString('ascii')) {
  219. case 'nistp256':
  220. // prime256v1/secp256r1
  221. ecCurveOID = '1.2.840.10045.3.1.7';
  222. break;
  223. case 'nistp384':
  224. // secp384r1
  225. ecCurveOID = '1.3.132.0.34';
  226. break;
  227. case 'nistp521':
  228. // secp521r1
  229. ecCurveOID = '1.3.132.0.35';
  230. break;
  231. default:
  232. return false;
  233. }
  234. var asnWriter = new Ber.Writer();
  235. asnWriter.startSequence();
  236. // algorithm
  237. asnWriter.startSequence();
  238. asnWriter.writeOID('1.2.840.10045.2.1'); // id-ecPublicKey
  239. // algorithm parameters (namedCurve)
  240. asnWriter.writeOID(ecCurveOID);
  241. asnWriter.endSequence();
  242. // subjectPublicKey
  243. asnWriter.startSequence(Ber.BitString);
  244. asnWriter.writeByte(0x00);
  245. // XXX: hack to write a raw buffer without a tag -- yuck
  246. asnWriter._ensure(Q.length);
  247. Q.copy(asnWriter._buf, asnWriter._offset, 0, Q.length);
  248. asnWriter._offset += Q.length;
  249. // end hack
  250. asnWriter.endSequence();
  251. asnWriter.endSequence();
  252. return asnWriter.buffer;
  253. }
  254. function decryptKey(keyInfo, passphrase) {
  255. if (keyInfo._decrypted || !keyInfo.encryption)
  256. return;
  257. var keylen = 0;
  258. var key;
  259. var iv;
  260. var dc;
  261. keyInfo.encryption = (SSH_TO_OPENSSL[keyInfo.encryption]
  262. || keyInfo.encryption);
  263. switch (keyInfo.encryption) {
  264. case 'aes-256-cbc':
  265. case 'aes-256-ctr':
  266. keylen = 32;
  267. break;
  268. case 'des-ede3-cbc':
  269. case 'des-ede3':
  270. case 'aes-192-cbc':
  271. case 'aes-192-ctr':
  272. keylen = 24;
  273. break;
  274. case 'aes-128-cbc':
  275. case 'aes-128-ctr':
  276. case 'cast-cbc':
  277. case 'bf-cbc':
  278. keylen = 16;
  279. break;
  280. default:
  281. throw new Error('Unsupported cipher for encrypted key: '
  282. + keyInfo.encryption);
  283. }
  284. if (keyInfo.ppk) {
  285. iv = PPK_IV;
  286. key = Buffer.concat([
  287. crypto.createHash('sha1')
  288. .update('\x00\x00\x00\x00' + passphrase, 'utf8')
  289. .digest(),
  290. crypto.createHash('sha1')
  291. .update('\x00\x00\x00\x01' + passphrase, 'utf8')
  292. .digest()
  293. ]);
  294. key = key.slice(0, keylen);
  295. } else {
  296. iv = new Buffer(keyInfo.extra[0], 'hex');
  297. key = crypto.createHash('md5')
  298. .update(passphrase, 'utf8')
  299. .update(iv.slice(0, 8))
  300. .digest();
  301. while (keylen > key.length) {
  302. key = Buffer.concat([
  303. key,
  304. (crypto.createHash('md5')
  305. .update(key)
  306. .update(passphrase, 'utf8')
  307. .update(iv)
  308. .digest()).slice(0, 8)
  309. ]);
  310. }
  311. if (key.length > keylen)
  312. key = key.slice(0, keylen);
  313. }
  314. dc = crypto.createDecipheriv(keyInfo.encryption, key, iv);
  315. dc.setAutoPadding(false);
  316. keyInfo.private = Buffer.concat([ dc.update(keyInfo.private), dc.final() ]);
  317. keyInfo._decrypted = true;
  318. if (keyInfo.privateOrig) {
  319. // Update our original base64-encoded version of the private key
  320. var orig = keyInfo.privateOrig.toString('utf8');
  321. var newOrig = /^(.+(?:\r\n|\n))/.exec(orig)[1];
  322. var b64key = keyInfo.private.toString('base64');
  323. newOrig += b64key.match(/.{1,70}/g).join('\n');
  324. newOrig += /((?:\r\n|\n).+)$/.exec(orig)[1];
  325. keyInfo.privateOrig = newOrig;
  326. } else if (keyInfo.ppk) {
  327. var valid = verifyPPKMAC(keyInfo, passphrase, keyInfo.private);
  328. if (!valid)
  329. throw new Error('PPK MAC mismatch');
  330. // Automatically convert private key data to OpenSSL format
  331. // (including PEM)
  332. convertPPKPrivate(keyInfo);
  333. }
  334. // Fill in full key type
  335. // TODO: make DRY, we do this also in keyParser
  336. if (keyInfo.type !== 'ec') {
  337. keyInfo.fulltype = 'ssh-' + keyInfo.type;
  338. } else {
  339. // ECDSA
  340. var asnReader = new Ber.Reader(keyInfo.private);
  341. asnReader.readSequence();
  342. asnReader.readInt();
  343. asnReader.readString(Ber.OctetString, true);
  344. asnReader.readByte(); // Skip "complex" context type byte
  345. var offset = asnReader.readLength(); // Skip context length
  346. if (offset !== null) {
  347. asnReader._offset = offset;
  348. switch (asnReader.readOID()) {
  349. case '1.2.840.10045.3.1.7':
  350. // prime256v1/secp256r1
  351. keyInfo.fulltype = 'ecdsa-sha2-nistp256';
  352. break;
  353. case '1.3.132.0.34':
  354. // secp384r1
  355. keyInfo.fulltype = 'ecdsa-sha2-nistp384';
  356. break;
  357. case '1.3.132.0.35':
  358. // secp521r1
  359. keyInfo.fulltype = 'ecdsa-sha2-nistp521';
  360. break;
  361. }
  362. }
  363. if (keyInfo.fulltype === undefined)
  364. return new Error('Unsupported EC private key type');
  365. }
  366. }
  367. function genPublicKey(keyInfo) {
  368. var publicKey;
  369. var i;
  370. // RSA
  371. var n;
  372. var e;
  373. // DSA
  374. var p;
  375. var q;
  376. var g;
  377. var y;
  378. // ECDSA
  379. var d;
  380. var Q;
  381. var ecCurveOID;
  382. var ecCurveName;
  383. if (keyInfo.private) {
  384. // parsing private key in ASN.1 format in order to generate a public key
  385. var privKey = keyInfo.private;
  386. var asnReader = new Ber.Reader(privKey);
  387. var errMsg;
  388. if (asnReader.readSequence() === null) {
  389. errMsg = 'Malformed private key (expected sequence)';
  390. if (keyInfo._decrypted)
  391. errMsg += '. Bad passphrase?';
  392. throw new Error(errMsg);
  393. }
  394. // version (ignored)
  395. if (asnReader.readInt() === null) {
  396. errMsg = 'Malformed private key (expected version)';
  397. if (keyInfo._decrypted)
  398. errMsg += '. Bad passphrase?';
  399. throw new Error(errMsg);
  400. }
  401. if (keyInfo.type === 'rsa') {
  402. // modulus (n) -- integer
  403. n = asnReader.readString(Ber.Integer, true);
  404. if (n === null) {
  405. errMsg = 'Malformed private key (expected RSA n value)';
  406. if (keyInfo._decrypted)
  407. errMsg += '. Bad passphrase?';
  408. throw new Error(errMsg);
  409. }
  410. // public exponent (e) -- integer
  411. e = asnReader.readString(Ber.Integer, true);
  412. if (e === null) {
  413. errMsg = 'Malformed private key (expected RSA e value)';
  414. if (keyInfo._decrypted)
  415. errMsg += '. Bad passphrase?';
  416. throw new Error(errMsg);
  417. }
  418. publicKey = new Buffer(4 + 7 // ssh-rsa
  419. + 4 + n.length
  420. + 4 + e.length);
  421. publicKey.writeUInt32BE(7, 0, true);
  422. publicKey.write('ssh-rsa', 4, 7, 'ascii');
  423. i = 4 + 7;
  424. publicKey.writeUInt32BE(e.length, i, true);
  425. e.copy(publicKey, i += 4);
  426. publicKey.writeUInt32BE(n.length, i += e.length, true);
  427. n.copy(publicKey, i += 4);
  428. } else if (keyInfo.type === 'dss') { // DSA
  429. // prime (p) -- integer
  430. p = asnReader.readString(Ber.Integer, true);
  431. if (p === null) {
  432. errMsg = 'Malformed private key (expected DSA p value)';
  433. if (keyInfo._decrypted)
  434. errMsg += '. Bad passphrase?';
  435. throw new Error(errMsg);
  436. }
  437. // group order (q) -- integer
  438. q = asnReader.readString(Ber.Integer, true);
  439. if (q === null) {
  440. errMsg = 'Malformed private key (expected DSA q value)';
  441. if (keyInfo._decrypted)
  442. errMsg += '. Bad passphrase?';
  443. throw new Error(errMsg);
  444. }
  445. // group generator (g) -- integer
  446. g = asnReader.readString(Ber.Integer, true);
  447. if (g === null) {
  448. errMsg = 'Malformed private key (expected DSA g value)';
  449. if (keyInfo._decrypted)
  450. errMsg += '. Bad passphrase?';
  451. throw new Error(errMsg);
  452. }
  453. // public key value (y) -- integer
  454. y = asnReader.readString(Ber.Integer, true);
  455. if (y === null) {
  456. errMsg = 'Malformed private key (expected DSA y value)';
  457. if (keyInfo._decrypted)
  458. errMsg += '. Bad passphrase?';
  459. throw new Error(errMsg);
  460. }
  461. publicKey = new Buffer(4 + 7 // ssh-dss
  462. + 4 + p.length
  463. + 4 + q.length
  464. + 4 + g.length
  465. + 4 + y.length);
  466. publicKey.writeUInt32BE(7, 0, true);
  467. publicKey.write('ssh-dss', 4, 7, 'ascii');
  468. i = 4 + 7;
  469. publicKey.writeUInt32BE(p.length, i, true);
  470. p.copy(publicKey, i += 4);
  471. publicKey.writeUInt32BE(q.length, i += p.length, true);
  472. q.copy(publicKey, i += 4);
  473. publicKey.writeUInt32BE(g.length, i += q.length, true);
  474. g.copy(publicKey, i += 4);
  475. publicKey.writeUInt32BE(y.length, i += g.length, true);
  476. y.copy(publicKey, i += 4);
  477. } else { // ECDSA
  478. d = asnReader.readString(Ber.OctetString, true);
  479. if (d === null)
  480. throw new Error('Malformed private key (expected ECDSA private key)');
  481. asnReader.readByte(); // Skip "complex" context type byte
  482. var offset = asnReader.readLength(); // Skip context length
  483. if (offset === null)
  484. throw new Error('Malformed private key (expected ECDSA context value)');
  485. asnReader._offset = offset;
  486. ecCurveOID = asnReader.readOID();
  487. if (ecCurveOID === null)
  488. throw new Error('Malformed private key (expected ECDSA curve)');
  489. var tempECDH;
  490. switch (ecCurveOID) {
  491. case '1.2.840.10045.3.1.7':
  492. // prime256v1/secp256r1
  493. keyInfo.curve = ecCurveName = 'nistp256';
  494. tempECDH = crypto.createECDH('prime256v1');
  495. break;
  496. case '1.3.132.0.34':
  497. // secp384r1
  498. keyInfo.curve = ecCurveName = 'nistp384';
  499. tempECDH = crypto.createECDH('secp384r1');
  500. break;
  501. case '1.3.132.0.35':
  502. // secp521r1
  503. keyInfo.curve = ecCurveName = 'nistp521';
  504. tempECDH = crypto.createECDH('secp521r1');
  505. break;
  506. default:
  507. throw new Error('Malformed private key (unsupported EC curve)');
  508. }
  509. tempECDH.setPrivateKey(d);
  510. Q = tempECDH.getPublicKey();
  511. publicKey = new Buffer(4 + 19 // ecdsa-sha2-<curve name>
  512. + 4 + 8 // <curve name>
  513. + 4 + Q.length);
  514. publicKey.writeUInt32BE(19, 0, true);
  515. publicKey.write('ecdsa-sha2-' + ecCurveName, 4, 19, 'ascii');
  516. publicKey.writeUInt32BE(8, 23, true);
  517. publicKey.write(ecCurveName, 27, 8, 'ascii');
  518. publicKey.writeUInt32BE(Q.length, 35, true);
  519. Q.copy(publicKey, 39);
  520. }
  521. } else if (keyInfo.public) {
  522. publicKey = keyInfo.public;
  523. if (keyInfo.type === 'ec') {
  524. // TODO: support adding ecdsa-* prefix
  525. ecCurveName = keyInfo.curve;
  526. } else if (publicKey[0] !== 0
  527. // check for missing ssh-{dsa,rsa} prefix
  528. || publicKey[1] !== 0
  529. || publicKey[2] !== 0
  530. || publicKey[3] !== 7
  531. || publicKey[4] !== 115
  532. || publicKey[5] !== 115
  533. || publicKey[6] !== 104
  534. || publicKey[7] !== 45
  535. || ((publicKey[8] !== 114
  536. || publicKey[9] !== 115
  537. || publicKey[10] !== 97)
  538. &&
  539. ((publicKey[8] !== 100
  540. || publicKey[9] !== 115
  541. || publicKey[10] !== 115)))) {
  542. var newPK = new Buffer(4 + 7 + publicKey.length);
  543. publicKey.copy(newPK, 11);
  544. newPK.writeUInt32BE(7, 0, true);
  545. if (keyInfo.type === 'rsa')
  546. newPK.write('ssh-rsa', 4, 7, 'ascii');
  547. else
  548. newPK.write('ssh-dss', 4, 7, 'ascii');
  549. publicKey = newPK;
  550. }
  551. } else
  552. throw new Error('Missing data generated by parseKey()');
  553. // generate a public key format for use with OpenSSL
  554. i = 4 + 7;
  555. var fulltype;
  556. var asn1KeyBuf;
  557. if (keyInfo.type === 'rsa') {
  558. fulltype = 'ssh-rsa';
  559. asn1KeyBuf = RSAKeySSHToASN1(publicKey.slice(4 + 7));
  560. } else if (keyInfo.type === 'dss') {
  561. fulltype = 'ssh-dss';
  562. asn1KeyBuf = DSAKeySSHToASN1(publicKey.slice(4 + 7));
  563. } else { // ECDSA
  564. fulltype = 'ecdsa-sha2-' + ecCurveName;
  565. asn1KeyBuf = ECDSAKeySSHToASN1(publicKey.slice(4 + 19));
  566. }
  567. if (!asn1KeyBuf)
  568. throw new Error('Invalid SSH-formatted public key');
  569. var b64key = asn1KeyBuf.toString('base64').replace(RE_KEY_LEN, '$1\n');
  570. var fullkey = '-----BEGIN PUBLIC KEY-----\n'
  571. + b64key
  572. + (b64key[b64key.length - 1] === '\n' ? '' : '\n')
  573. + '-----END PUBLIC KEY-----';
  574. return {
  575. type: keyInfo.type,
  576. fulltype: fulltype,
  577. curve: ecCurveName,
  578. public: publicKey,
  579. publicOrig: new Buffer(fullkey)
  580. };
  581. }
  582. function verifyPPKMAC(keyInfo, passphrase, privateKey) {
  583. if (keyInfo._macresult !== undefined)
  584. return keyInfo._macresult;
  585. else if (!keyInfo.ppk)
  586. throw new Error("Key isn't a PPK");
  587. else if (!keyInfo.privateMAC)
  588. throw new Error('Missing MAC');
  589. else if (!privateKey)
  590. throw new Error('Missing raw private key data');
  591. else if (keyInfo.encryption && typeof passphrase !== 'string')
  592. throw new Error('Missing passphrase for encrypted PPK');
  593. else if (keyInfo.encryption && !keyInfo._decrypted)
  594. throw new Error('PPK must be decrypted before verifying MAC');
  595. var mac = keyInfo.privateMAC;
  596. var typelen = keyInfo.fulltype.length;
  597. // encryption algorithm is converted at this point for use with OpenSSL,
  598. // so we need to use the original value so that the MAC is calculated
  599. // correctly
  600. var enc = (keyInfo.encryption ? 'aes256-cbc' : 'none');
  601. var enclen = enc.length;
  602. var commlen = Buffer.byteLength(keyInfo.comment);
  603. var pub = keyInfo.public;
  604. var publen = pub.length;
  605. var privlen = privateKey.length;
  606. var macdata = new Buffer(4 + typelen
  607. + 4 + enclen
  608. + 4 + commlen
  609. + 4 + publen
  610. + 4 + privlen);
  611. var p = 0;
  612. macdata.writeUInt32BE(typelen, p, true);
  613. macdata.write(keyInfo.fulltype, p += 4, typelen, 'ascii');
  614. macdata.writeUInt32BE(enclen, p += typelen, true);
  615. macdata.write(enc, p += 4, enclen, 'ascii');
  616. macdata.writeUInt32BE(commlen, p += enclen, true);
  617. macdata.write(keyInfo.comment, p += 4, commlen, 'utf8');
  618. macdata.writeUInt32BE(publen, p += commlen, true);
  619. pub.copy(macdata, p += 4);
  620. macdata.writeUInt32BE(privlen, p += publen, true);
  621. privateKey.copy(macdata, p += 4);
  622. if (typeof passphrase !== 'string')
  623. passphrase = '';
  624. var mackey = crypto.createHash('sha1')
  625. .update('putty-private-key-file-mac-key', 'ascii')
  626. .update(passphrase, 'utf8')
  627. .digest();
  628. var calcMAC = crypto.createHmac('sha1', mackey)
  629. .update(macdata)
  630. .digest('hex');
  631. return (keyInfo._macresult = (calcMAC === mac));
  632. }
  633. function convertPPKPrivate(keyInfo) {
  634. if (!keyInfo.ppk || !keyInfo.public || !keyInfo.private)
  635. throw new Error("Key isn't a PPK");
  636. else if (keyInfo._converted)
  637. return false;
  638. var pub = keyInfo.public;
  639. var priv = keyInfo.private;
  640. var asnWriter = new Ber.Writer();
  641. var p;
  642. var q;
  643. if (keyInfo.type === 'rsa') {
  644. var e = readString(pub, 4 + 7);
  645. var n = readString(pub, pub._pos);
  646. var d = readString(priv, 0);
  647. p = readString(priv, priv._pos);
  648. q = readString(priv, priv._pos);
  649. var iqmp = readString(priv, priv._pos);
  650. var p1 = new BigInteger(p, 256);
  651. var q1 = new BigInteger(q, 256);
  652. var dmp1 = new BigInteger(d, 256);
  653. var dmq1 = new BigInteger(d, 256);
  654. dmp1 = new Buffer(dmp1.mod(p1.subtract(BigInteger.ONE)).toByteArray());
  655. dmq1 = new Buffer(dmq1.mod(q1.subtract(BigInteger.ONE)).toByteArray());
  656. asnWriter.startSequence();
  657. asnWriter.writeInt(0x00, Ber.Integer);
  658. asnWriter.writeBuffer(n, Ber.Integer);
  659. asnWriter.writeBuffer(e, Ber.Integer);
  660. asnWriter.writeBuffer(d, Ber.Integer);
  661. asnWriter.writeBuffer(p, Ber.Integer);
  662. asnWriter.writeBuffer(q, Ber.Integer);
  663. asnWriter.writeBuffer(dmp1, Ber.Integer);
  664. asnWriter.writeBuffer(dmq1, Ber.Integer);
  665. asnWriter.writeBuffer(iqmp, Ber.Integer);
  666. asnWriter.endSequence();
  667. } else {
  668. p = readString(pub, 4 + 7);
  669. q = readString(pub, pub._pos);
  670. var g = readString(pub, pub._pos);
  671. var y = readString(pub, pub._pos);
  672. var x = readString(priv, 0);
  673. asnWriter.startSequence();
  674. asnWriter.writeInt(0x00, Ber.Integer);
  675. asnWriter.writeBuffer(p, Ber.Integer);
  676. asnWriter.writeBuffer(q, Ber.Integer);
  677. asnWriter.writeBuffer(g, Ber.Integer);
  678. asnWriter.writeBuffer(y, Ber.Integer);
  679. asnWriter.writeBuffer(x, Ber.Integer);
  680. asnWriter.endSequence();
  681. }
  682. var b64key = asnWriter.buffer.toString('base64').replace(RE_KEY_LEN, '$1\n');
  683. var fullkey = '-----BEGIN '
  684. + (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
  685. + ' PRIVATE KEY-----\n'
  686. + b64key
  687. + (b64key[b64key.length - 1] === '\n' ? '' : '\n')
  688. + '-----END '
  689. + (keyInfo.type === 'rsa' ? 'RSA' : 'DSA')
  690. + ' PRIVATE KEY-----';
  691. keyInfo.private = asnWriter.buffer;
  692. keyInfo.privateOrig = new Buffer(fullkey);
  693. keyInfo._converted = true;
  694. return true;
  695. }
  696. function readString(buffer, start, encoding, stream, cb, maxLen) {
  697. if (encoding && !Buffer.isBuffer(encoding) && typeof encoding !== 'string') {
  698. if (typeof cb === 'number')
  699. maxLen = cb;
  700. cb = stream;
  701. stream = encoding;
  702. encoding = undefined;
  703. }
  704. start || (start = 0);
  705. var bufferLen = buffer.length;
  706. var left = (bufferLen - start);
  707. var len;
  708. var end;
  709. if (start < 0 || start >= bufferLen || left < 4) {
  710. stream && stream._cleanup(cb);
  711. return false;
  712. }
  713. len = buffer.readUInt32BE(start, true);
  714. if (len > (maxLen || MAX_STRING_LEN) || left < (4 + len)) {
  715. stream && stream._cleanup(cb);
  716. return false;
  717. }
  718. start += 4;
  719. end = start + len;
  720. buffer._pos = end;
  721. if (encoding) {
  722. if (Buffer.isBuffer(encoding)) {
  723. buffer.copy(encoding, 0, start, end);
  724. return encoding;
  725. } else
  726. return buffer.toString(encoding, start, end);
  727. } else
  728. return buffer.slice(start, end);
  729. }