keyParser.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449
  1. // TODO:
  2. // * utilize `crypto.create(Private|Public)Key()` and `keyObject.export()`
  3. // * handle multi-line header values (OpenSSH)?
  4. // * more thorough validation?
  5. var crypto = require('crypto');
  6. var cryptoSign = crypto.sign;
  7. var cryptoVerify = crypto.verify;
  8. var createSign = crypto.createSign;
  9. var createVerify = crypto.createVerify;
  10. var createDecipheriv = crypto.createDecipheriv;
  11. var createHash = crypto.createHash;
  12. var createHmac = crypto.createHmac;
  13. var supportedOpenSSLCiphers = crypto.getCiphers();
  14. var utils;
  15. var Ber = require('asn1').Ber;
  16. var bcrypt_pbkdf = require('bcrypt-pbkdf').pbkdf;
  17. var bufferHelpers = require(__dirname + '/buffer-helpers');
  18. var readUInt32BE = bufferHelpers.readUInt32BE;
  19. var writeUInt32BE = bufferHelpers.writeUInt32BE;
  20. var constants = require(__dirname + '/constants');
  21. var SUPPORTED_CIPHER = constants.ALGORITHMS.SUPPORTED_CIPHER;
  22. var CIPHER_INFO = constants.CIPHER_INFO;
  23. var SSH_TO_OPENSSL = constants.SSH_TO_OPENSSL;
  24. var EDDSA_SUPPORTED = constants.EDDSA_SUPPORTED;
  25. var SYM_HASH_ALGO = Symbol('Hash Algorithm');
  26. var SYM_PRIV_PEM = Symbol('Private key PEM');
  27. var SYM_PUB_PEM = Symbol('Public key PEM');
  28. var SYM_PUB_SSH = Symbol('Public key SSH');
  29. var SYM_DECRYPTED = Symbol('Decrypted Key');
  30. // Create OpenSSL cipher name -> SSH cipher name conversion table
  31. var CIPHER_INFO_OPENSSL = Object.create(null);
  32. (function() {
  33. var keys = Object.keys(CIPHER_INFO);
  34. for (var i = 0; i < keys.length; ++i) {
  35. var cipherName = SSH_TO_OPENSSL[keys[i]];
  36. if (!cipherName || CIPHER_INFO_OPENSSL[cipherName])
  37. continue;
  38. CIPHER_INFO_OPENSSL[cipherName] = CIPHER_INFO[keys[i]];
  39. }
  40. })();
  41. var trimStart = (function() {
  42. if (typeof String.prototype.trimStart === 'function') {
  43. return function trimStart(str) {
  44. return str.trimStart();
  45. };
  46. }
  47. return function trimStart(str) {
  48. var start = 0;
  49. for (var i = 0; i < str.length; ++i) {
  50. switch (str.charCodeAt(i)) {
  51. case 32: // ' '
  52. case 9: // '\t'
  53. case 13: // '\r'
  54. case 10: // '\n'
  55. case 12: // '\f'
  56. ++start;
  57. continue;
  58. }
  59. break;
  60. }
  61. if (start === 0)
  62. return str;
  63. return str.slice(start);
  64. };
  65. })();
  66. function makePEM(type, data) {
  67. data = data.toString('base64');
  68. return '-----BEGIN ' + type + ' KEY-----\n'
  69. + data.replace(/.{64}/g, '$&\n')
  70. + (data.length % 64 ? '\n' : '')
  71. + '-----END ' + type + ' KEY-----';
  72. }
  73. function combineBuffers(buf1, buf2) {
  74. var result = Buffer.allocUnsafe(buf1.length + buf2.length);
  75. buf1.copy(result, 0);
  76. buf2.copy(result, buf1.length);
  77. return result;
  78. }
  79. function skipFields(buf, nfields) {
  80. var bufLen = buf.length;
  81. var pos = (buf._pos || 0);
  82. for (var i = 0; i < nfields; ++i) {
  83. var left = (bufLen - pos);
  84. if (pos >= bufLen || left < 4)
  85. return false;
  86. var len = readUInt32BE(buf, pos);
  87. if (left < 4 + len)
  88. return false;
  89. pos += 4 + len;
  90. }
  91. buf._pos = pos;
  92. return true;
  93. }
  94. function genOpenSSLRSAPub(n, e) {
  95. var asnWriter = new Ber.Writer();
  96. asnWriter.startSequence();
  97. // algorithm
  98. asnWriter.startSequence();
  99. asnWriter.writeOID('1.2.840.113549.1.1.1'); // rsaEncryption
  100. // algorithm parameters (RSA has none)
  101. asnWriter.writeNull();
  102. asnWriter.endSequence();
  103. // subjectPublicKey
  104. asnWriter.startSequence(Ber.BitString);
  105. asnWriter.writeByte(0x00);
  106. asnWriter.startSequence();
  107. asnWriter.writeBuffer(n, Ber.Integer);
  108. asnWriter.writeBuffer(e, Ber.Integer);
  109. asnWriter.endSequence();
  110. asnWriter.endSequence();
  111. asnWriter.endSequence();
  112. return makePEM('PUBLIC', asnWriter.buffer);
  113. }
  114. function genOpenSSHRSAPub(n, e) {
  115. var publicKey = Buffer.allocUnsafe(4 + 7 // "ssh-rsa"
  116. + 4 + n.length
  117. + 4 + e.length);
  118. writeUInt32BE(publicKey, 7, 0);
  119. publicKey.write('ssh-rsa', 4, 7, 'ascii');
  120. var i = 4 + 7;
  121. writeUInt32BE(publicKey, e.length, i);
  122. e.copy(publicKey, i += 4);
  123. writeUInt32BE(publicKey, n.length, i += e.length);
  124. n.copy(publicKey, i + 4);
  125. return publicKey;
  126. }
  127. var genOpenSSLRSAPriv = (function() {
  128. function genRSAASN1Buf(n, e, d, p, q, dmp1, dmq1, iqmp) {
  129. var asnWriter = new Ber.Writer();
  130. asnWriter.startSequence();
  131. asnWriter.writeInt(0x00, Ber.Integer);
  132. asnWriter.writeBuffer(n, Ber.Integer);
  133. asnWriter.writeBuffer(e, Ber.Integer);
  134. asnWriter.writeBuffer(d, Ber.Integer);
  135. asnWriter.writeBuffer(p, Ber.Integer);
  136. asnWriter.writeBuffer(q, Ber.Integer);
  137. asnWriter.writeBuffer(dmp1, Ber.Integer);
  138. asnWriter.writeBuffer(dmq1, Ber.Integer);
  139. asnWriter.writeBuffer(iqmp, Ber.Integer);
  140. asnWriter.endSequence();
  141. return asnWriter.buffer;
  142. }
  143. function bigIntFromBuffer(buf) {
  144. return BigInt('0x' + buf.toString('hex'));
  145. }
  146. function bigIntToBuffer(bn) {
  147. var hex = bn.toString(16);
  148. if ((hex.length & 1) !== 0) {
  149. hex = '0' + hex;
  150. } else {
  151. var sigbit = hex.charCodeAt(0);
  152. // BER/DER integers require leading zero byte to denote a positive value
  153. // when first byte >= 0x80
  154. if (sigbit === 56 || (sigbit >= 97 && sigbit <= 102))
  155. hex = '00' + hex;
  156. }
  157. return Buffer.from(hex, 'hex');
  158. }
  159. // Feature detect native BigInt availability and use it when possible
  160. try {
  161. var code = [
  162. 'return function genOpenSSLRSAPriv(n, e, d, iqmp, p, q) {',
  163. ' var bn_d = bigIntFromBuffer(d);',
  164. ' var dmp1 = bigIntToBuffer(bn_d % (bigIntFromBuffer(p) - 1n));',
  165. ' var dmq1 = bigIntToBuffer(bn_d % (bigIntFromBuffer(q) - 1n));',
  166. ' return makePEM(\'RSA PRIVATE\', '
  167. + 'genRSAASN1Buf(n, e, d, p, q, dmp1, dmq1, iqmp));',
  168. '};'
  169. ].join('\n');
  170. return new Function(
  171. 'bigIntFromBuffer, bigIntToBuffer, makePEM, genRSAASN1Buf',
  172. code
  173. )(bigIntFromBuffer, bigIntToBuffer, makePEM, genRSAASN1Buf);
  174. } catch (ex) {
  175. return (function() {
  176. var BigInteger = require(__dirname + '/jsbn.js');
  177. return function genOpenSSLRSAPriv(n, e, d, iqmp, p, q) {
  178. var pbi = new BigInteger(p, 256);
  179. var qbi = new BigInteger(q, 256);
  180. var dbi = new BigInteger(d, 256);
  181. var dmp1bi = dbi.mod(pbi.subtract(BigInteger.ONE));
  182. var dmq1bi = dbi.mod(qbi.subtract(BigInteger.ONE));
  183. var dmp1 = Buffer.from(dmp1bi.toByteArray());
  184. var dmq1 = Buffer.from(dmq1bi.toByteArray());
  185. return makePEM('RSA PRIVATE',
  186. genRSAASN1Buf(n, e, d, p, q, dmp1, dmq1, iqmp));
  187. };
  188. })();
  189. }
  190. })();
  191. function genOpenSSLDSAPub(p, q, g, y) {
  192. var asnWriter = new Ber.Writer();
  193. asnWriter.startSequence();
  194. // algorithm
  195. asnWriter.startSequence();
  196. asnWriter.writeOID('1.2.840.10040.4.1'); // id-dsa
  197. // algorithm parameters
  198. asnWriter.startSequence();
  199. asnWriter.writeBuffer(p, Ber.Integer);
  200. asnWriter.writeBuffer(q, Ber.Integer);
  201. asnWriter.writeBuffer(g, Ber.Integer);
  202. asnWriter.endSequence();
  203. asnWriter.endSequence();
  204. // subjectPublicKey
  205. asnWriter.startSequence(Ber.BitString);
  206. asnWriter.writeByte(0x00);
  207. asnWriter.writeBuffer(y, Ber.Integer);
  208. asnWriter.endSequence();
  209. asnWriter.endSequence();
  210. return makePEM('PUBLIC', asnWriter.buffer);
  211. }
  212. function genOpenSSHDSAPub(p, q, g, y) {
  213. var publicKey = Buffer.allocUnsafe(4 + 7 // ssh-dss
  214. + 4 + p.length
  215. + 4 + q.length
  216. + 4 + g.length
  217. + 4 + y.length);
  218. writeUInt32BE(publicKey, 7, 0);
  219. publicKey.write('ssh-dss', 4, 7, 'ascii');
  220. var i = 4 + 7;
  221. writeUInt32BE(publicKey, p.length, i);
  222. p.copy(publicKey, i += 4);
  223. writeUInt32BE(publicKey, q.length, i += p.length);
  224. q.copy(publicKey, i += 4);
  225. writeUInt32BE(publicKey, g.length, i += q.length);
  226. g.copy(publicKey, i += 4);
  227. writeUInt32BE(publicKey, y.length, i += g.length);
  228. y.copy(publicKey, i + 4);
  229. return publicKey;
  230. }
  231. function genOpenSSLDSAPriv(p, q, g, y, x) {
  232. var asnWriter = new Ber.Writer();
  233. asnWriter.startSequence();
  234. asnWriter.writeInt(0x00, Ber.Integer);
  235. asnWriter.writeBuffer(p, Ber.Integer);
  236. asnWriter.writeBuffer(q, Ber.Integer);
  237. asnWriter.writeBuffer(g, Ber.Integer);
  238. asnWriter.writeBuffer(y, Ber.Integer);
  239. asnWriter.writeBuffer(x, Ber.Integer);
  240. asnWriter.endSequence();
  241. return makePEM('DSA PRIVATE', asnWriter.buffer);
  242. }
  243. function genOpenSSLEdPub(pub) {
  244. var asnWriter = new Ber.Writer();
  245. asnWriter.startSequence();
  246. // algorithm
  247. asnWriter.startSequence();
  248. asnWriter.writeOID('1.3.101.112'); // id-Ed25519
  249. asnWriter.endSequence();
  250. // PublicKey
  251. asnWriter.writeBuffer(pub, Ber.BitString);
  252. asnWriter.endSequence();
  253. return makePEM('PUBLIC', asnWriter.buffer);
  254. }
  255. function genOpenSSHEdPub(pub) {
  256. var publicKey = Buffer.allocUnsafe(4 + 11 // ssh-ed25519
  257. + 4 + pub.length);
  258. writeUInt32BE(publicKey, 11, 0);
  259. publicKey.write('ssh-ed25519', 4, 11, 'ascii');
  260. writeUInt32BE(publicKey, pub.length, 15);
  261. pub.copy(publicKey, 19);
  262. return publicKey;
  263. }
  264. function genOpenSSLEdPriv(priv) {
  265. var asnWriter = new Ber.Writer();
  266. asnWriter.startSequence();
  267. // version
  268. asnWriter.writeInt(0x00, Ber.Integer);
  269. // algorithm
  270. asnWriter.startSequence();
  271. asnWriter.writeOID('1.3.101.112'); // id-Ed25519
  272. asnWriter.endSequence();
  273. // PrivateKey
  274. asnWriter.startSequence(Ber.OctetString);
  275. asnWriter.writeBuffer(priv, Ber.OctetString);
  276. asnWriter.endSequence();
  277. asnWriter.endSequence();
  278. return makePEM('PRIVATE', asnWriter.buffer);
  279. }
  280. function genOpenSSLECDSAPub(oid, Q) {
  281. var asnWriter = new Ber.Writer();
  282. asnWriter.startSequence();
  283. // algorithm
  284. asnWriter.startSequence();
  285. asnWriter.writeOID('1.2.840.10045.2.1'); // id-ecPublicKey
  286. // algorithm parameters (namedCurve)
  287. asnWriter.writeOID(oid);
  288. asnWriter.endSequence();
  289. // subjectPublicKey
  290. asnWriter.startSequence(Ber.BitString);
  291. asnWriter.writeByte(0x00);
  292. // XXX: hack to write a raw buffer without a tag -- yuck
  293. asnWriter._ensure(Q.length);
  294. Q.copy(asnWriter._buf, asnWriter._offset, 0, Q.length);
  295. asnWriter._offset += Q.length;
  296. // end hack
  297. asnWriter.endSequence();
  298. asnWriter.endSequence();
  299. return makePEM('PUBLIC', asnWriter.buffer);
  300. }
  301. function genOpenSSHECDSAPub(oid, Q) {
  302. var curveName;
  303. switch (oid) {
  304. case '1.2.840.10045.3.1.7':
  305. // prime256v1/secp256r1
  306. curveName = 'nistp256';
  307. break;
  308. case '1.3.132.0.34':
  309. // secp384r1
  310. curveName = 'nistp384';
  311. break;
  312. case '1.3.132.0.35':
  313. // secp521r1
  314. curveName = 'nistp521';
  315. break;
  316. default:
  317. return;
  318. }
  319. var publicKey = Buffer.allocUnsafe(4 + 19 // ecdsa-sha2-<curve name>
  320. + 4 + 8 // <curve name>
  321. + 4 + Q.length);
  322. writeUInt32BE(publicKey, 19, 0);
  323. publicKey.write('ecdsa-sha2-' + curveName, 4, 19, 'ascii');
  324. writeUInt32BE(publicKey, 8, 23);
  325. publicKey.write(curveName, 27, 8, 'ascii');
  326. writeUInt32BE(publicKey, Q.length, 35);
  327. Q.copy(publicKey, 39);
  328. return publicKey;
  329. }
  330. function genOpenSSLECDSAPriv(oid, pub, priv) {
  331. var asnWriter = new Ber.Writer();
  332. asnWriter.startSequence();
  333. // version
  334. asnWriter.writeInt(0x01, Ber.Integer);
  335. // privateKey
  336. asnWriter.writeBuffer(priv, Ber.OctetString);
  337. // parameters (optional)
  338. asnWriter.startSequence(0xA0);
  339. asnWriter.writeOID(oid);
  340. asnWriter.endSequence();
  341. // publicKey (optional)
  342. asnWriter.startSequence(0xA1);
  343. asnWriter.startSequence(Ber.BitString);
  344. asnWriter.writeByte(0x00);
  345. // XXX: hack to write a raw buffer without a tag -- yuck
  346. asnWriter._ensure(pub.length);
  347. pub.copy(asnWriter._buf, asnWriter._offset, 0, pub.length);
  348. asnWriter._offset += pub.length;
  349. // end hack
  350. asnWriter.endSequence();
  351. asnWriter.endSequence();
  352. asnWriter.endSequence();
  353. return makePEM('EC PRIVATE', asnWriter.buffer);
  354. }
  355. function genOpenSSLECDSAPubFromPriv(curveName, priv) {
  356. var tempECDH = crypto.createECDH(curveName);
  357. tempECDH.setPrivateKey(priv);
  358. return tempECDH.getPublicKey();
  359. }
  360. var baseKeySign = (function() {
  361. if (typeof cryptoSign === 'function') {
  362. return function sign(data) {
  363. var pem = this[SYM_PRIV_PEM];
  364. if (pem === null)
  365. return new Error('No private key available');
  366. try {
  367. return cryptoSign(this[SYM_HASH_ALGO], data, pem);
  368. } catch (ex) {
  369. return ex;
  370. }
  371. };
  372. } else {
  373. function trySign(signature, privKey) {
  374. try {
  375. return signature.sign(privKey);
  376. } catch (ex) {
  377. return ex;
  378. }
  379. }
  380. return function sign(data) {
  381. var pem = this[SYM_PRIV_PEM];
  382. if (pem === null)
  383. return new Error('No private key available');
  384. var signature = createSign(this[SYM_HASH_ALGO]);
  385. signature.update(data);
  386. return trySign(signature, pem);
  387. };
  388. }
  389. })();
  390. var baseKeyVerify = (function() {
  391. if (typeof cryptoVerify === 'function') {
  392. return function verify(data, signature) {
  393. var pem = this[SYM_PUB_PEM];
  394. if (pem === null)
  395. return new Error('No public key available');
  396. try {
  397. return cryptoVerify(this[SYM_HASH_ALGO], data, pem, signature);
  398. } catch (ex) {
  399. return ex;
  400. }
  401. };
  402. } else {
  403. function tryVerify(verifier, pubKey, signature) {
  404. try {
  405. return verifier.verify(pubKey, signature);
  406. } catch (ex) {
  407. return ex;
  408. }
  409. }
  410. return function verify(data, signature) {
  411. var pem = this[SYM_PUB_PEM];
  412. if (pem === null)
  413. return new Error('No public key available');
  414. var verifier = createVerify(this[SYM_HASH_ALGO]);
  415. verifier.update(data);
  416. return tryVerify(verifier, pem, signature);
  417. };
  418. }
  419. })();
  420. var BaseKey = {
  421. sign: baseKeySign,
  422. verify: baseKeyVerify,
  423. getPrivatePEM: function getPrivatePEM() {
  424. return this[SYM_PRIV_PEM];
  425. },
  426. getPublicPEM: function getPublicPEM() {
  427. return this[SYM_PUB_PEM];
  428. },
  429. getPublicSSH: function getPublicSSH() {
  430. return this[SYM_PUB_SSH];
  431. },
  432. };
  433. function OpenSSH_Private(type, comment, privPEM, pubPEM, pubSSH, algo, decrypted) {
  434. this.type = type;
  435. this.comment = comment;
  436. this[SYM_PRIV_PEM] = privPEM;
  437. this[SYM_PUB_PEM] = pubPEM;
  438. this[SYM_PUB_SSH] = pubSSH;
  439. this[SYM_HASH_ALGO] = algo;
  440. this[SYM_DECRYPTED] = decrypted;
  441. }
  442. OpenSSH_Private.prototype = BaseKey;
  443. (function() {
  444. var regexp = /^-----BEGIN OPENSSH PRIVATE KEY-----(?:\r\n|\n)([\s\S]+)(?:\r\n|\n)-----END OPENSSH PRIVATE KEY-----$/;
  445. OpenSSH_Private.parse = function(str, passphrase) {
  446. var m = regexp.exec(str);
  447. if (m === null)
  448. return null;
  449. var ret;
  450. var data = Buffer.from(m[1], 'base64');
  451. if (data.length < 31) // magic (+ magic null term.) + minimum field lengths
  452. return new Error('Malformed OpenSSH private key');
  453. var magic = data.toString('ascii', 0, 15);
  454. if (magic !== 'openssh-key-v1\0')
  455. return new Error('Unsupported OpenSSH key magic: ' + magic);
  456. // avoid cyclic require by requiring on first use
  457. if (!utils)
  458. utils = require('./utils');
  459. var cipherName = utils.readString(data, 15, 'ascii');
  460. if (cipherName === false)
  461. return new Error('Malformed OpenSSH private key');
  462. if (cipherName !== 'none' && SUPPORTED_CIPHER.indexOf(cipherName) === -1)
  463. return new Error('Unsupported cipher for OpenSSH key: ' + cipherName);
  464. var kdfName = utils.readString(data, data._pos, 'ascii');
  465. if (kdfName === false)
  466. return new Error('Malformed OpenSSH private key');
  467. if (kdfName !== 'none') {
  468. if (cipherName === 'none')
  469. return new Error('Malformed OpenSSH private key');
  470. if (kdfName !== 'bcrypt')
  471. return new Error('Unsupported kdf name for OpenSSH key: ' + kdfName);
  472. if (!passphrase) {
  473. return new Error(
  474. 'Encrypted private OpenSSH key detected, but no passphrase given'
  475. );
  476. }
  477. } else if (cipherName !== 'none') {
  478. return new Error('Malformed OpenSSH private key');
  479. }
  480. var encInfo;
  481. var cipherKey;
  482. var cipherIV;
  483. if (cipherName !== 'none')
  484. encInfo = CIPHER_INFO[cipherName];
  485. var kdfOptions = utils.readString(data, data._pos);
  486. if (kdfOptions === false)
  487. return new Error('Malformed OpenSSH private key');
  488. if (kdfOptions.length) {
  489. switch (kdfName) {
  490. case 'none':
  491. return new Error('Malformed OpenSSH private key');
  492. case 'bcrypt':
  493. /*
  494. string salt
  495. uint32 rounds
  496. */
  497. var salt = utils.readString(kdfOptions, 0);
  498. if (salt === false || kdfOptions._pos + 4 > kdfOptions.length)
  499. return new Error('Malformed OpenSSH private key');
  500. var rounds = readUInt32BE(kdfOptions, kdfOptions._pos);
  501. var gen = Buffer.allocUnsafe(encInfo.keyLen + encInfo.ivLen);
  502. var r = bcrypt_pbkdf(passphrase,
  503. passphrase.length,
  504. salt,
  505. salt.length,
  506. gen,
  507. gen.length,
  508. rounds);
  509. if (r !== 0)
  510. return new Error('Failed to generate information to decrypt key');
  511. cipherKey = gen.slice(0, encInfo.keyLen);
  512. cipherIV = gen.slice(encInfo.keyLen);
  513. break;
  514. }
  515. } else if (kdfName !== 'none') {
  516. return new Error('Malformed OpenSSH private key');
  517. }
  518. var keyCount = utils.readInt(data, data._pos);
  519. if (keyCount === false)
  520. return new Error('Malformed OpenSSH private key');
  521. data._pos += 4;
  522. if (keyCount > 0) {
  523. // TODO: place sensible limit on max `keyCount`
  524. // Read public keys first
  525. for (var i = 0; i < keyCount; ++i) {
  526. var pubData = utils.readString(data, data._pos);
  527. if (pubData === false)
  528. return new Error('Malformed OpenSSH private key');
  529. var type = utils.readString(pubData, 0, 'ascii');
  530. if (type === false)
  531. return new Error('Malformed OpenSSH private key');
  532. }
  533. var privBlob = utils.readString(data, data._pos);
  534. if (privBlob === false)
  535. return new Error('Malformed OpenSSH private key');
  536. if (cipherKey !== undefined) {
  537. // encrypted private key(s)
  538. if (privBlob.length < encInfo.blockLen
  539. || (privBlob.length % encInfo.blockLen) !== 0) {
  540. return new Error('Malformed OpenSSH private key');
  541. }
  542. try {
  543. var options = { authTagLength: encInfo.authLen };
  544. var decipher = createDecipheriv(SSH_TO_OPENSSL[cipherName],
  545. cipherKey,
  546. cipherIV,
  547. options);
  548. if (encInfo.authLen > 0) {
  549. if (data.length - data._pos < encInfo.authLen)
  550. return new Error('Malformed OpenSSH private key');
  551. decipher.setAuthTag(
  552. data.slice(data._pos, data._pos += encInfo.authLen)
  553. );
  554. }
  555. privBlob = combineBuffers(decipher.update(privBlob),
  556. decipher.final());
  557. } catch (ex) {
  558. return ex;
  559. }
  560. }
  561. // Nothing should we follow the private key(s), except a possible
  562. // authentication tag for relevant ciphers
  563. if (data._pos !== data.length)
  564. return new Error('Malformed OpenSSH private key');
  565. ret = parseOpenSSHPrivKeys(privBlob, keyCount, cipherKey !== undefined);
  566. } else {
  567. ret = [];
  568. }
  569. return ret;
  570. };
  571. function parseOpenSSHPrivKeys(data, nkeys, decrypted) {
  572. var keys = [];
  573. /*
  574. uint32 checkint
  575. uint32 checkint
  576. string privatekey1
  577. string comment1
  578. string privatekey2
  579. string comment2
  580. ...
  581. string privatekeyN
  582. string commentN
  583. char 1
  584. char 2
  585. char 3
  586. ...
  587. char padlen % 255
  588. */
  589. if (data.length < 8)
  590. return new Error('Malformed OpenSSH private key');
  591. var check1 = readUInt32BE(data, 0);
  592. var check2 = readUInt32BE(data, 4);
  593. if (check1 !== check2) {
  594. if (decrypted)
  595. return new Error('OpenSSH key integrity check failed -- bad passphrase?');
  596. return new Error('OpenSSH key integrity check failed');
  597. }
  598. data._pos = 8;
  599. var i;
  600. var oid;
  601. for (i = 0; i < nkeys; ++i) {
  602. var algo = undefined;
  603. var privPEM = undefined;
  604. var pubPEM = undefined;
  605. var pubSSH = undefined;
  606. // The OpenSSH documentation for the key format actually lies, the entirety
  607. // of the private key content is not contained with a string field, it's
  608. // actually the literal contents of the private key, so to be able to find
  609. // the end of the key data you need to know the layout/format of each key
  610. // type ...
  611. var type = utils.readString(data, data._pos, 'ascii');
  612. if (type === false)
  613. return new Error('Malformed OpenSSH private key');
  614. switch (type) {
  615. case 'ssh-rsa':
  616. /*
  617. string n -- public
  618. string e -- public
  619. string d -- private
  620. string iqmp -- private
  621. string p -- private
  622. string q -- private
  623. */
  624. var n = utils.readString(data, data._pos);
  625. if (n === false)
  626. return new Error('Malformed OpenSSH private key');
  627. var e = utils.readString(data, data._pos);
  628. if (e === false)
  629. return new Error('Malformed OpenSSH private key');
  630. var d = utils.readString(data, data._pos);
  631. if (d === false)
  632. return new Error('Malformed OpenSSH private key');
  633. var iqmp = utils.readString(data, data._pos);
  634. if (iqmp === false)
  635. return new Error('Malformed OpenSSH private key');
  636. var p = utils.readString(data, data._pos);
  637. if (p === false)
  638. return new Error('Malformed OpenSSH private key');
  639. var q = utils.readString(data, data._pos);
  640. if (q === false)
  641. return new Error('Malformed OpenSSH private key');
  642. pubPEM = genOpenSSLRSAPub(n, e);
  643. pubSSH = genOpenSSHRSAPub(n, e);
  644. privPEM = genOpenSSLRSAPriv(n, e, d, iqmp, p, q);
  645. algo = 'sha1';
  646. break;
  647. case 'ssh-dss':
  648. /*
  649. string p -- public
  650. string q -- public
  651. string g -- public
  652. string y -- public
  653. string x -- private
  654. */
  655. var p = utils.readString(data, data._pos);
  656. if (p === false)
  657. return new Error('Malformed OpenSSH private key');
  658. var q = utils.readString(data, data._pos);
  659. if (q === false)
  660. return new Error('Malformed OpenSSH private key');
  661. var g = utils.readString(data, data._pos);
  662. if (g === false)
  663. return new Error('Malformed OpenSSH private key');
  664. var y = utils.readString(data, data._pos);
  665. if (y === false)
  666. return new Error('Malformed OpenSSH private key');
  667. var x = utils.readString(data, data._pos);
  668. if (x === false)
  669. return new Error('Malformed OpenSSH private key');
  670. pubPEM = genOpenSSLDSAPub(p, q, g, y);
  671. pubSSH = genOpenSSHDSAPub(p, q, g, y);
  672. privPEM = genOpenSSLDSAPriv(p, q, g, y, x);
  673. algo = 'sha1';
  674. break;
  675. case 'ssh-ed25519':
  676. if (!EDDSA_SUPPORTED)
  677. return new Error('Unsupported OpenSSH private key type: ' + type);
  678. /*
  679. * string public key
  680. * string private key + public key
  681. */
  682. var edpub = utils.readString(data, data._pos);
  683. if (edpub === false || edpub.length !== 32)
  684. return new Error('Malformed OpenSSH private key');
  685. var edpriv = utils.readString(data, data._pos);
  686. if (edpriv === false || edpriv.length !== 64)
  687. return new Error('Malformed OpenSSH private key');
  688. pubPEM = genOpenSSLEdPub(edpub);
  689. pubSSH = genOpenSSHEdPub(edpub);
  690. privPEM = genOpenSSLEdPriv(edpriv.slice(0, 32));
  691. algo = null;
  692. break;
  693. case 'ecdsa-sha2-nistp256':
  694. algo = 'sha256';
  695. oid = '1.2.840.10045.3.1.7';
  696. case 'ecdsa-sha2-nistp384':
  697. if (algo === undefined) {
  698. algo = 'sha384';
  699. oid = '1.3.132.0.34';
  700. }
  701. case 'ecdsa-sha2-nistp521':
  702. if (algo === undefined) {
  703. algo = 'sha512';
  704. oid = '1.3.132.0.35';
  705. }
  706. /*
  707. string curve name
  708. string Q -- public
  709. string d -- private
  710. */
  711. // TODO: validate curve name against type
  712. if (!skipFields(data, 1)) // Skip curve name
  713. return new Error('Malformed OpenSSH private key');
  714. var ecpub = utils.readString(data, data._pos);
  715. if (ecpub === false)
  716. return new Error('Malformed OpenSSH private key');
  717. var ecpriv = utils.readString(data, data._pos);
  718. if (ecpriv === false)
  719. return new Error('Malformed OpenSSH private key');
  720. pubPEM = genOpenSSLECDSAPub(oid, ecpub);
  721. pubSSH = genOpenSSHECDSAPub(oid, ecpub);
  722. privPEM = genOpenSSLECDSAPriv(oid, ecpub, ecpriv);
  723. break;
  724. default:
  725. return new Error('Unsupported OpenSSH private key type: ' + type);
  726. }
  727. var privComment = utils.readString(data, data._pos, 'utf8');
  728. if (privComment === false)
  729. return new Error('Malformed OpenSSH private key');
  730. keys.push(
  731. new OpenSSH_Private(type, privComment, privPEM, pubPEM, pubSSH, algo,
  732. decrypted)
  733. );
  734. }
  735. var cnt = 0;
  736. for (i = data._pos; i < data.length; ++i) {
  737. if (data[i] !== (++cnt % 255))
  738. return new Error('Malformed OpenSSH private key');
  739. }
  740. return keys;
  741. }
  742. })();
  743. function OpenSSH_Old_Private(type, comment, privPEM, pubPEM, pubSSH, algo, decrypted) {
  744. this.type = type;
  745. this.comment = comment;
  746. this[SYM_PRIV_PEM] = privPEM;
  747. this[SYM_PUB_PEM] = pubPEM;
  748. this[SYM_PUB_SSH] = pubSSH;
  749. this[SYM_HASH_ALGO] = algo;
  750. this[SYM_DECRYPTED] = decrypted;
  751. }
  752. OpenSSH_Old_Private.prototype = BaseKey;
  753. (function() {
  754. var regexp = /^-----BEGIN (RSA|DSA|EC) PRIVATE KEY-----(?:\r\n|\n)((?:[^:]+:\s*[\S].*(?:\r\n|\n))*)([\s\S]+)(?:\r\n|\n)-----END (RSA|DSA|EC) PRIVATE KEY-----$/;
  755. OpenSSH_Old_Private.parse = function(str, passphrase) {
  756. var m = regexp.exec(str);
  757. if (m === null)
  758. return null;
  759. var privBlob = Buffer.from(m[3], 'base64');
  760. var headers = m[2];
  761. var decrypted = false;
  762. if (headers !== undefined) {
  763. // encrypted key
  764. headers = headers.split(/\r\n|\n/g);
  765. for (var i = 0; i < headers.length; ++i) {
  766. var header = headers[i];
  767. var sepIdx = header.indexOf(':');
  768. if (header.slice(0, sepIdx) === 'DEK-Info') {
  769. var val = header.slice(sepIdx + 2);
  770. sepIdx = val.indexOf(',');
  771. if (sepIdx === -1)
  772. continue;
  773. var cipherName = val.slice(0, sepIdx).toLowerCase();
  774. if (supportedOpenSSLCiphers.indexOf(cipherName) === -1) {
  775. return new Error(
  776. 'Cipher ('
  777. + cipherName
  778. + ') not supported for encrypted OpenSSH private key'
  779. );
  780. }
  781. var encInfo = CIPHER_INFO_OPENSSL[cipherName];
  782. if (!encInfo) {
  783. return new Error(
  784. 'Cipher ('
  785. + cipherName
  786. + ') not supported for encrypted OpenSSH private key'
  787. );
  788. }
  789. var cipherIV = Buffer.from(val.slice(sepIdx + 1), 'hex');
  790. if (cipherIV.length !== encInfo.ivLen)
  791. return new Error('Malformed encrypted OpenSSH private key');
  792. if (!passphrase) {
  793. return new Error(
  794. 'Encrypted OpenSSH private key detected, but no passphrase given'
  795. );
  796. }
  797. var cipherKey = createHash('md5')
  798. .update(passphrase)
  799. .update(cipherIV.slice(0, 8))
  800. .digest();
  801. while (cipherKey.length < encInfo.keyLen) {
  802. cipherKey = combineBuffers(
  803. cipherKey,
  804. (createHash('md5')
  805. .update(cipherKey)
  806. .update(passphrase)
  807. .update(cipherIV)
  808. .digest()).slice(0, 8)
  809. );
  810. }
  811. if (cipherKey.length > encInfo.keyLen)
  812. cipherKey = cipherKey.slice(0, encInfo.keyLen);
  813. try {
  814. var decipher = createDecipheriv(cipherName, cipherKey, cipherIV);
  815. decipher.setAutoPadding(false);
  816. privBlob = combineBuffers(decipher.update(privBlob),
  817. decipher.final());
  818. decrypted = true;
  819. } catch (ex) {
  820. return ex;
  821. }
  822. }
  823. }
  824. }
  825. var type;
  826. var privPEM;
  827. var pubPEM;
  828. var pubSSH;
  829. var algo;
  830. var reader;
  831. var errMsg = 'Malformed OpenSSH private key';
  832. if (decrypted)
  833. errMsg += '. Bad passphrase?';
  834. switch (m[1]) {
  835. case 'RSA':
  836. type = 'ssh-rsa';
  837. privPEM = makePEM('RSA PRIVATE', privBlob);
  838. try {
  839. reader = new Ber.Reader(privBlob);
  840. reader.readSequence();
  841. reader.readInt(); // skip version
  842. var n = reader.readString(Ber.Integer, true);
  843. if (n === null)
  844. return new Error(errMsg);
  845. var e = reader.readString(Ber.Integer, true);
  846. if (e === null)
  847. return new Error(errMsg);
  848. pubPEM = genOpenSSLRSAPub(n, e);
  849. pubSSH = genOpenSSHRSAPub(n, e);
  850. } catch (ex) {
  851. return new Error(errMsg);
  852. }
  853. algo = 'sha1';
  854. break;
  855. case 'DSA':
  856. type = 'ssh-dss';
  857. privPEM = makePEM('DSA PRIVATE', privBlob);
  858. try {
  859. reader = new Ber.Reader(privBlob);
  860. reader.readSequence();
  861. reader.readInt(); // skip version
  862. var p = reader.readString(Ber.Integer, true);
  863. if (p === null)
  864. return new Error(errMsg);
  865. var q = reader.readString(Ber.Integer, true);
  866. if (q === null)
  867. return new Error(errMsg);
  868. var g = reader.readString(Ber.Integer, true);
  869. if (g === null)
  870. return new Error(errMsg);
  871. var y = reader.readString(Ber.Integer, true);
  872. if (y === null)
  873. return new Error(errMsg);
  874. pubPEM = genOpenSSLDSAPub(p, q, g, y);
  875. pubSSH = genOpenSSHDSAPub(p, q, g, y);
  876. } catch (ex) {
  877. return new Error(errMsg);
  878. }
  879. algo = 'sha1';
  880. break;
  881. case 'EC':
  882. var ecSSLName;
  883. var ecPriv;
  884. try {
  885. reader = new Ber.Reader(privBlob);
  886. reader.readSequence();
  887. reader.readInt(); // skip version
  888. ecPriv = reader.readString(Ber.OctetString, true);
  889. reader.readByte(); // Skip "complex" context type byte
  890. var offset = reader.readLength(); // Skip context length
  891. if (offset !== null) {
  892. reader._offset = offset;
  893. var oid = reader.readOID();
  894. if (oid === null)
  895. return new Error(errMsg);
  896. switch (oid) {
  897. case '1.2.840.10045.3.1.7':
  898. // prime256v1/secp256r1
  899. ecSSLName = 'prime256v1';
  900. type = 'ecdsa-sha2-nistp256';
  901. algo = 'sha256';
  902. break;
  903. case '1.3.132.0.34':
  904. // secp384r1
  905. ecSSLName = 'secp384r1';
  906. type = 'ecdsa-sha2-nistp384';
  907. algo = 'sha384';
  908. break;
  909. case '1.3.132.0.35':
  910. // secp521r1
  911. ecSSLName = 'secp521r1';
  912. type = 'ecdsa-sha2-nistp521';
  913. algo = 'sha512';
  914. break;
  915. default:
  916. return new Error('Unsupported private key EC OID: ' + oid);
  917. }
  918. } else {
  919. return new Error(errMsg);
  920. }
  921. } catch (ex) {
  922. return new Error(errMsg);
  923. }
  924. privPEM = makePEM('EC PRIVATE', privBlob);
  925. var pubBlob = genOpenSSLECDSAPubFromPriv(ecSSLName, ecPriv);
  926. pubPEM = genOpenSSLECDSAPub(oid, pubBlob);
  927. pubSSH = genOpenSSHECDSAPub(oid, pubBlob);
  928. break;
  929. }
  930. return new OpenSSH_Old_Private(type, '', privPEM, pubPEM, pubSSH, algo,
  931. decrypted);
  932. };
  933. })();
  934. function PPK_Private(type, comment, privPEM, pubPEM, pubSSH, algo, decrypted) {
  935. this.type = type;
  936. this.comment = comment;
  937. this[SYM_PRIV_PEM] = privPEM;
  938. this[SYM_PUB_PEM] = pubPEM;
  939. this[SYM_PUB_SSH] = pubSSH;
  940. this[SYM_HASH_ALGO] = algo;
  941. this[SYM_DECRYPTED] = decrypted;
  942. }
  943. PPK_Private.prototype = BaseKey;
  944. (function() {
  945. var EMPTY_PASSPHRASE = Buffer.alloc(0);
  946. var PPK_IV = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
  947. var PPK_PP1 = Buffer.from([0, 0, 0, 0]);
  948. var PPK_PP2 = Buffer.from([0, 0, 0, 1]);
  949. var regexp = /^PuTTY-User-Key-File-2: (ssh-(?:rsa|dss))\r?\nEncryption: (aes256-cbc|none)\r?\nComment: ([^\r\n]*)\r?\nPublic-Lines: \d+\r?\n([\s\S]+?)\r?\nPrivate-Lines: \d+\r?\n([\s\S]+?)\r?\nPrivate-MAC: ([^\r\n]+)/;
  950. PPK_Private.parse = function(str, passphrase) {
  951. var m = regexp.exec(str);
  952. if (m === null)
  953. return null;
  954. // m[1] = key type
  955. // m[2] = encryption type
  956. // m[3] = comment
  957. // m[4] = base64-encoded public key data:
  958. // for "ssh-rsa":
  959. // string "ssh-rsa"
  960. // mpint e (public exponent)
  961. // mpint n (modulus)
  962. // for "ssh-dss":
  963. // string "ssh-dss"
  964. // mpint p (modulus)
  965. // mpint q (prime)
  966. // mpint g (base number)
  967. // mpint y (public key parameter: g^x mod p)
  968. // m[5] = base64-encoded private key data:
  969. // for "ssh-rsa":
  970. // mpint d (private exponent)
  971. // mpint p (prime 1)
  972. // mpint q (prime 2)
  973. // mpint iqmp ([inverse of q] mod p)
  974. // for "ssh-dss":
  975. // mpint x (private key parameter)
  976. // m[6] = SHA1 HMAC over:
  977. // string name of algorithm ("ssh-dss", "ssh-rsa")
  978. // string encryption type
  979. // string comment
  980. // string public key data
  981. // string private-plaintext (including the final padding)
  982. var cipherName = m[2];
  983. var encrypted = (cipherName !== 'none');
  984. if (encrypted && !passphrase) {
  985. return new Error(
  986. 'Encrypted PPK private key detected, but no passphrase given'
  987. );
  988. }
  989. var privBlob = Buffer.from(m[5], 'base64');
  990. if (encrypted) {
  991. var encInfo = CIPHER_INFO[cipherName];
  992. var cipherKey = combineBuffers(
  993. createHash('sha1').update(PPK_PP1).update(passphrase).digest(),
  994. createHash('sha1').update(PPK_PP2).update(passphrase).digest()
  995. );
  996. if (cipherKey.length > encInfo.keyLen)
  997. cipherKey = cipherKey.slice(0, encInfo.keyLen);
  998. try {
  999. var decipher = createDecipheriv(SSH_TO_OPENSSL[cipherName],
  1000. cipherKey,
  1001. PPK_IV);
  1002. decipher.setAutoPadding(false);
  1003. privBlob = combineBuffers(decipher.update(privBlob),
  1004. decipher.final());
  1005. decrypted = true;
  1006. } catch (ex) {
  1007. return ex;
  1008. }
  1009. }
  1010. var type = m[1];
  1011. var comment = m[3];
  1012. var pubBlob = Buffer.from(m[4], 'base64');
  1013. var mac = m[6];
  1014. var typeLen = type.length;
  1015. var cipherNameLen = cipherName.length;
  1016. var commentLen = Buffer.byteLength(comment);
  1017. var pubLen = pubBlob.length;
  1018. var privLen = privBlob.length;
  1019. var macData = Buffer.allocUnsafe(4 + typeLen
  1020. + 4 + cipherNameLen
  1021. + 4 + commentLen
  1022. + 4 + pubLen
  1023. + 4 + privLen);
  1024. var p = 0;
  1025. writeUInt32BE(macData, typeLen, p);
  1026. macData.write(type, p += 4, typeLen, 'ascii');
  1027. writeUInt32BE(macData, cipherNameLen, p += typeLen);
  1028. macData.write(cipherName, p += 4, cipherNameLen, 'ascii');
  1029. writeUInt32BE(macData, commentLen, p += cipherNameLen);
  1030. macData.write(comment, p += 4, commentLen, 'utf8');
  1031. writeUInt32BE(macData, pubLen, p += commentLen);
  1032. pubBlob.copy(macData, p += 4);
  1033. writeUInt32BE(macData, privLen, p += pubLen);
  1034. privBlob.copy(macData, p + 4);
  1035. if (!passphrase)
  1036. passphrase = EMPTY_PASSPHRASE;
  1037. var calcMAC = createHmac('sha1',
  1038. createHash('sha1')
  1039. .update('putty-private-key-file-mac-key')
  1040. .update(passphrase)
  1041. .digest())
  1042. .update(macData)
  1043. .digest('hex');
  1044. if (calcMAC !== mac) {
  1045. if (encrypted) {
  1046. return new Error(
  1047. 'PPK private key integrity check failed -- bad passphrase?'
  1048. );
  1049. } else {
  1050. return new Error('PPK private key integrity check failed');
  1051. }
  1052. }
  1053. // avoid cyclic require by requiring on first use
  1054. if (!utils)
  1055. utils = require('./utils');
  1056. var pubPEM;
  1057. var pubSSH;
  1058. var privPEM;
  1059. pubBlob._pos = 0;
  1060. skipFields(pubBlob, 1); // skip (duplicate) key type
  1061. switch (type) {
  1062. case 'ssh-rsa':
  1063. var e = utils.readString(pubBlob, pubBlob._pos);
  1064. if (e === false)
  1065. return new Error('Malformed PPK public key');
  1066. var n = utils.readString(pubBlob, pubBlob._pos);
  1067. if (n === false)
  1068. return new Error('Malformed PPK public key');
  1069. var d = utils.readString(privBlob, 0);
  1070. if (d === false)
  1071. return new Error('Malformed PPK private key');
  1072. var p = utils.readString(privBlob, privBlob._pos);
  1073. if (p === false)
  1074. return new Error('Malformed PPK private key');
  1075. var q = utils.readString(privBlob, privBlob._pos);
  1076. if (q === false)
  1077. return new Error('Malformed PPK private key');
  1078. var iqmp = utils.readString(privBlob, privBlob._pos);
  1079. if (iqmp === false)
  1080. return new Error('Malformed PPK private key');
  1081. pubPEM = genOpenSSLRSAPub(n, e);
  1082. pubSSH = genOpenSSHRSAPub(n, e);
  1083. privPEM = genOpenSSLRSAPriv(n, e, d, iqmp, p, q);
  1084. break;
  1085. case 'ssh-dss':
  1086. var p = utils.readString(pubBlob, pubBlob._pos);
  1087. if (p === false)
  1088. return new Error('Malformed PPK public key');
  1089. var q = utils.readString(pubBlob, pubBlob._pos);
  1090. if (q === false)
  1091. return new Error('Malformed PPK public key');
  1092. var g = utils.readString(pubBlob, pubBlob._pos);
  1093. if (g === false)
  1094. return new Error('Malformed PPK public key');
  1095. var y = utils.readString(pubBlob, pubBlob._pos);
  1096. if (y === false)
  1097. return new Error('Malformed PPK public key');
  1098. var x = utils.readString(privBlob, 0);
  1099. if (x === false)
  1100. return new Error('Malformed PPK private key');
  1101. pubPEM = genOpenSSLDSAPub(p, q, g, y);
  1102. pubSSH = genOpenSSHDSAPub(p, q, g, y);
  1103. privPEM = genOpenSSLDSAPriv(p, q, g, y, x);
  1104. break;
  1105. }
  1106. return new PPK_Private(type, comment, privPEM, pubPEM, pubSSH, 'sha1',
  1107. encrypted);
  1108. };
  1109. })();
  1110. function parseDER(data, baseType, comment, fullType) {
  1111. // avoid cyclic require by requiring on first use
  1112. if (!utils)
  1113. utils = require('./utils');
  1114. var algo;
  1115. var pubPEM = null;
  1116. var pubSSH = null;
  1117. switch (baseType) {
  1118. case 'ssh-rsa':
  1119. var e = utils.readString(data, data._pos);
  1120. if (e === false)
  1121. return new Error('Malformed OpenSSH public key');
  1122. var n = utils.readString(data, data._pos);
  1123. if (n === false)
  1124. return new Error('Malformed OpenSSH public key');
  1125. pubPEM = genOpenSSLRSAPub(n, e);
  1126. pubSSH = genOpenSSHRSAPub(n, e);
  1127. algo = 'sha1';
  1128. break;
  1129. case 'ssh-dss':
  1130. var p = utils.readString(data, data._pos);
  1131. if (p === false)
  1132. return new Error('Malformed OpenSSH public key');
  1133. var q = utils.readString(data, data._pos);
  1134. if (q === false)
  1135. return new Error('Malformed OpenSSH public key');
  1136. var g = utils.readString(data, data._pos);
  1137. if (g === false)
  1138. return new Error('Malformed OpenSSH public key');
  1139. var y = utils.readString(data, data._pos);
  1140. if (y === false)
  1141. return new Error('Malformed OpenSSH public key');
  1142. pubPEM = genOpenSSLDSAPub(p, q, g, y);
  1143. pubSSH = genOpenSSHDSAPub(p, q, g, y);
  1144. algo = 'sha1';
  1145. break;
  1146. case 'ssh-ed25519':
  1147. var edpub = utils.readString(data, data._pos);
  1148. if (edpub === false || edpub.length !== 32)
  1149. return new Error('Malformed OpenSSH public key');
  1150. pubPEM = genOpenSSLEdPub(edpub);
  1151. pubSSH = genOpenSSHEdPub(edpub);
  1152. algo = null;
  1153. break;
  1154. case 'ecdsa-sha2-nistp256':
  1155. algo = 'sha256';
  1156. oid = '1.2.840.10045.3.1.7';
  1157. case 'ecdsa-sha2-nistp384':
  1158. if (algo === undefined) {
  1159. algo = 'sha384';
  1160. oid = '1.3.132.0.34';
  1161. }
  1162. case 'ecdsa-sha2-nistp521':
  1163. if (algo === undefined) {
  1164. algo = 'sha512';
  1165. oid = '1.3.132.0.35';
  1166. }
  1167. // TODO: validate curve name against type
  1168. if (!skipFields(data, 1)) // Skip curve name
  1169. return new Error('Malformed OpenSSH public key');
  1170. var ecpub = utils.readString(data, data._pos);
  1171. if (ecpub === false)
  1172. return new Error('Malformed OpenSSH public key');
  1173. pubPEM = genOpenSSLECDSAPub(oid, ecpub);
  1174. pubSSH = genOpenSSHECDSAPub(oid, ecpub);
  1175. break;
  1176. default:
  1177. return new Error('Unsupported OpenSSH public key type: ' + baseType);
  1178. }
  1179. return new OpenSSH_Public(fullType, comment, pubPEM, pubSSH, algo);
  1180. }
  1181. function OpenSSH_Public(type, comment, pubPEM, pubSSH, algo) {
  1182. this.type = type;
  1183. this.comment = comment;
  1184. this[SYM_PRIV_PEM] = null;
  1185. this[SYM_PUB_PEM] = pubPEM;
  1186. this[SYM_PUB_SSH] = pubSSH;
  1187. this[SYM_HASH_ALGO] = algo;
  1188. this[SYM_DECRYPTED] = false;
  1189. }
  1190. OpenSSH_Public.prototype = BaseKey;
  1191. (function() {
  1192. var regexp;
  1193. if (EDDSA_SUPPORTED)
  1194. regexp = /^(((?:ssh-(?:rsa|dss|ed25519))|ecdsa-sha2-nistp(?:256|384|521))(?:-cert-v0[01]@openssh.com)?) ([A-Z0-9a-z\/+=]+)(?:$|\s+([\S].*)?)$/;
  1195. else
  1196. regexp = /^(((?:ssh-(?:rsa|dss))|ecdsa-sha2-nistp(?:256|384|521))(?:-cert-v0[01]@openssh.com)?) ([A-Z0-9a-z\/+=]+)(?:$|\s+([\S].*)?)$/;
  1197. OpenSSH_Public.parse = function(str) {
  1198. var m = regexp.exec(str);
  1199. if (m === null)
  1200. return null;
  1201. // m[1] = full type
  1202. // m[2] = base type
  1203. // m[3] = base64-encoded public key
  1204. // m[4] = comment
  1205. // avoid cyclic require by requiring on first use
  1206. if (!utils)
  1207. utils = require('./utils');
  1208. var fullType = m[1];
  1209. var baseType = m[2];
  1210. var data = Buffer.from(m[3], 'base64');
  1211. var comment = (m[4] || '');
  1212. var type = utils.readString(data, data._pos, 'ascii');
  1213. if (type === false || type.indexOf(baseType) !== 0)
  1214. return new Error('Malformed OpenSSH public key');
  1215. return parseDER(data, baseType, comment, fullType);
  1216. };
  1217. })();
  1218. function RFC4716_Public(type, comment, pubPEM, pubSSH, algo) {
  1219. this.type = type;
  1220. this.comment = comment;
  1221. this[SYM_PRIV_PEM] = null;
  1222. this[SYM_PUB_PEM] = pubPEM;
  1223. this[SYM_PUB_SSH] = pubSSH;
  1224. this[SYM_HASH_ALGO] = algo;
  1225. this[SYM_DECRYPTED] = false;
  1226. }
  1227. RFC4716_Public.prototype = BaseKey;
  1228. (function() {
  1229. var regexp = /^---- BEGIN SSH2 PUBLIC KEY ----(?:\r\n|\n)((?:(?:[\x21-\x7E]+?):(?:(?:.*?\\\r?\n)*.*)(?:\r\n|\n))*)((?:[A-Z0-9a-z\/+=]+(?:\r\n|\n))+)---- END SSH2 PUBLIC KEY ----$/;
  1230. var RE_HEADER = /^([\x21-\x7E]+?):((?:.*?\\\r?\n)*.*)$/gm;
  1231. var RE_HEADER_ENDS = /\\\r?\n/g;
  1232. RFC4716_Public.parse = function(str) {
  1233. var m = regexp.exec(str);
  1234. if (m === null)
  1235. return null;
  1236. // m[1] = header(s)
  1237. // m[2] = base64-encoded public key
  1238. var headers = m[1];
  1239. var data = Buffer.from(m[2], 'base64');
  1240. var comment = '';
  1241. if (headers !== undefined) {
  1242. while (m = RE_HEADER.exec(headers)) {
  1243. if (m[1].toLowerCase() === 'comment') {
  1244. comment = trimStart(m[2].replace(RE_HEADER_ENDS, ''));
  1245. if (comment.length > 1
  1246. && comment.charCodeAt(0) === 34/*'"'*/
  1247. && comment.charCodeAt(comment.length - 1) === 34/*'"'*/) {
  1248. comment = comment.slice(1, -1);
  1249. }
  1250. }
  1251. }
  1252. }
  1253. // avoid cyclic require by requiring on first use
  1254. if (!utils)
  1255. utils = require('./utils');
  1256. var type = utils.readString(data, 0, 'ascii');
  1257. if (type === false)
  1258. return new Error('Malformed RFC4716 public key');
  1259. var pubPEM = null;
  1260. var pubSSH = null;
  1261. switch (type) {
  1262. case 'ssh-rsa':
  1263. var e = utils.readString(data, data._pos);
  1264. if (e === false)
  1265. return new Error('Malformed RFC4716 public key');
  1266. var n = utils.readString(data, data._pos);
  1267. if (n === false)
  1268. return new Error('Malformed RFC4716 public key');
  1269. pubPEM = genOpenSSLRSAPub(n, e);
  1270. pubSSH = genOpenSSHRSAPub(n, e);
  1271. break;
  1272. case 'ssh-dss':
  1273. var p = utils.readString(data, data._pos);
  1274. if (p === false)
  1275. return new Error('Malformed RFC4716 public key');
  1276. var q = utils.readString(data, data._pos);
  1277. if (q === false)
  1278. return new Error('Malformed RFC4716 public key');
  1279. var g = utils.readString(data, data._pos);
  1280. if (g === false)
  1281. return new Error('Malformed RFC4716 public key');
  1282. var y = utils.readString(data, data._pos);
  1283. if (y === false)
  1284. return new Error('Malformed RFC4716 public key');
  1285. pubPEM = genOpenSSLDSAPub(p, q, g, y);
  1286. pubSSH = genOpenSSHDSAPub(p, q, g, y);
  1287. break;
  1288. default:
  1289. return new Error('Malformed RFC4716 public key');
  1290. }
  1291. return new RFC4716_Public(type, comment, pubPEM, pubSSH, 'sha1');
  1292. };
  1293. })();
  1294. module.exports = {
  1295. parseDERKey: function parseDERKey(data, type) {
  1296. return parseDER(data, type, '', type);
  1297. },
  1298. parseKey: function parseKey(data, passphrase) {
  1299. if (Buffer.isBuffer(data))
  1300. data = data.toString('utf8').trim();
  1301. else if (typeof data !== 'string')
  1302. return new Error('Key data must be a Buffer or string');
  1303. else
  1304. data = data.trim();
  1305. // intentional !=
  1306. if (passphrase != undefined) {
  1307. if (typeof passphrase === 'string')
  1308. passphrase = Buffer.from(passphrase);
  1309. else if (!Buffer.isBuffer(passphrase))
  1310. return new Error('Passphrase must be a string or Buffer when supplied');
  1311. }
  1312. var ret;
  1313. // Private keys
  1314. if ((ret = OpenSSH_Private.parse(data, passphrase)) !== null)
  1315. return ret;
  1316. if ((ret = OpenSSH_Old_Private.parse(data, passphrase)) !== null)
  1317. return ret;
  1318. if ((ret = PPK_Private.parse(data, passphrase)) !== null)
  1319. return ret;
  1320. // Public keys
  1321. if ((ret = OpenSSH_Public.parse(data)) !== null)
  1322. return ret;
  1323. if ((ret = RFC4716_Public.parse(data)) !== null)
  1324. return ret;
  1325. return new Error('Unsupported key format');
  1326. }
  1327. }