Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

keyParser.js 47KB

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