x509.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. // Copyright 2017 Joyent, Inc.
  2. module.exports = {
  3. read: read,
  4. verify: verify,
  5. sign: sign,
  6. signAsync: signAsync,
  7. write: write
  8. };
  9. var assert = require('assert-plus');
  10. var asn1 = require('asn1');
  11. var algs = require('../algs');
  12. var utils = require('../utils');
  13. var Key = require('../key');
  14. var PrivateKey = require('../private-key');
  15. var pem = require('./pem');
  16. var Identity = require('../identity');
  17. var Signature = require('../signature');
  18. var Certificate = require('../certificate');
  19. var pkcs8 = require('./pkcs8');
  20. /*
  21. * This file is based on RFC5280 (X.509).
  22. */
  23. /* Helper to read in a single mpint */
  24. function readMPInt(der, nm) {
  25. assert.strictEqual(der.peek(), asn1.Ber.Integer,
  26. nm + ' is not an Integer');
  27. return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true)));
  28. }
  29. function verify(cert, key) {
  30. var sig = cert.signatures.x509;
  31. assert.object(sig, 'x509 signature');
  32. var algParts = sig.algo.split('-');
  33. if (algParts[0] !== key.type)
  34. return (false);
  35. var blob = sig.cache;
  36. if (blob === undefined) {
  37. var der = new asn1.BerWriter();
  38. writeTBSCert(cert, der);
  39. blob = der.buffer;
  40. }
  41. var verifier = key.createVerify(algParts[1]);
  42. verifier.write(blob);
  43. return (verifier.verify(sig.signature));
  44. }
  45. function Local(i) {
  46. return (asn1.Ber.Context | asn1.Ber.Constructor | i);
  47. }
  48. function Context(i) {
  49. return (asn1.Ber.Context | i);
  50. }
  51. var SIGN_ALGS = {
  52. 'rsa-md5': '1.2.840.113549.1.1.4',
  53. 'rsa-sha1': '1.2.840.113549.1.1.5',
  54. 'rsa-sha256': '1.2.840.113549.1.1.11',
  55. 'rsa-sha384': '1.2.840.113549.1.1.12',
  56. 'rsa-sha512': '1.2.840.113549.1.1.13',
  57. 'dsa-sha1': '1.2.840.10040.4.3',
  58. 'dsa-sha256': '2.16.840.1.101.3.4.3.2',
  59. 'ecdsa-sha1': '1.2.840.10045.4.1',
  60. 'ecdsa-sha256': '1.2.840.10045.4.3.2',
  61. 'ecdsa-sha384': '1.2.840.10045.4.3.3',
  62. 'ecdsa-sha512': '1.2.840.10045.4.3.4',
  63. 'ed25519-sha512': '1.3.101.112'
  64. };
  65. Object.keys(SIGN_ALGS).forEach(function (k) {
  66. SIGN_ALGS[SIGN_ALGS[k]] = k;
  67. });
  68. SIGN_ALGS['1.3.14.3.2.3'] = 'rsa-md5';
  69. SIGN_ALGS['1.3.14.3.2.29'] = 'rsa-sha1';
  70. var EXTS = {
  71. 'issuerKeyId': '2.5.29.35',
  72. 'altName': '2.5.29.17',
  73. 'basicConstraints': '2.5.29.19',
  74. 'keyUsage': '2.5.29.15',
  75. 'extKeyUsage': '2.5.29.37'
  76. };
  77. function read(buf, options) {
  78. if (typeof (buf) === 'string') {
  79. buf = new Buffer(buf, 'binary');
  80. }
  81. assert.buffer(buf, 'buf');
  82. var der = new asn1.BerReader(buf);
  83. der.readSequence();
  84. if (Math.abs(der.length - der.remain) > 1) {
  85. throw (new Error('DER sequence does not contain whole byte ' +
  86. 'stream'));
  87. }
  88. var tbsStart = der.offset;
  89. der.readSequence();
  90. var sigOffset = der.offset + der.length;
  91. var tbsEnd = sigOffset;
  92. if (der.peek() === Local(0)) {
  93. der.readSequence(Local(0));
  94. var version = der.readInt();
  95. assert.ok(version <= 3,
  96. 'only x.509 versions up to v3 supported');
  97. }
  98. var cert = {};
  99. cert.signatures = {};
  100. var sig = (cert.signatures.x509 = {});
  101. sig.extras = {};
  102. cert.serial = readMPInt(der, 'serial');
  103. der.readSequence();
  104. var after = der.offset + der.length;
  105. var certAlgOid = der.readOID();
  106. var certAlg = SIGN_ALGS[certAlgOid];
  107. if (certAlg === undefined)
  108. throw (new Error('unknown signature algorithm ' + certAlgOid));
  109. der._offset = after;
  110. cert.issuer = Identity.parseAsn1(der);
  111. der.readSequence();
  112. cert.validFrom = readDate(der);
  113. cert.validUntil = readDate(der);
  114. cert.subjects = [Identity.parseAsn1(der)];
  115. der.readSequence();
  116. after = der.offset + der.length;
  117. cert.subjectKey = pkcs8.readPkcs8(undefined, 'public', der);
  118. der._offset = after;
  119. /* issuerUniqueID */
  120. if (der.peek() === Local(1)) {
  121. der.readSequence(Local(1));
  122. sig.extras.issuerUniqueID =
  123. buf.slice(der.offset, der.offset + der.length);
  124. der._offset += der.length;
  125. }
  126. /* subjectUniqueID */
  127. if (der.peek() === Local(2)) {
  128. der.readSequence(Local(2));
  129. sig.extras.subjectUniqueID =
  130. buf.slice(der.offset, der.offset + der.length);
  131. der._offset += der.length;
  132. }
  133. /* extensions */
  134. if (der.peek() === Local(3)) {
  135. der.readSequence(Local(3));
  136. var extEnd = der.offset + der.length;
  137. der.readSequence();
  138. while (der.offset < extEnd)
  139. readExtension(cert, buf, der);
  140. assert.strictEqual(der.offset, extEnd);
  141. }
  142. assert.strictEqual(der.offset, sigOffset);
  143. der.readSequence();
  144. after = der.offset + der.length;
  145. var sigAlgOid = der.readOID();
  146. var sigAlg = SIGN_ALGS[sigAlgOid];
  147. if (sigAlg === undefined)
  148. throw (new Error('unknown signature algorithm ' + sigAlgOid));
  149. der._offset = after;
  150. var sigData = der.readString(asn1.Ber.BitString, true);
  151. if (sigData[0] === 0)
  152. sigData = sigData.slice(1);
  153. var algParts = sigAlg.split('-');
  154. sig.signature = Signature.parse(sigData, algParts[0], 'asn1');
  155. sig.signature.hashAlgorithm = algParts[1];
  156. sig.algo = sigAlg;
  157. sig.cache = buf.slice(tbsStart, tbsEnd);
  158. return (new Certificate(cert));
  159. }
  160. function readDate(der) {
  161. if (der.peek() === asn1.Ber.UTCTime) {
  162. return (utcTimeToDate(der.readString(asn1.Ber.UTCTime)));
  163. } else if (der.peek() === asn1.Ber.GeneralizedTime) {
  164. return (gTimeToDate(der.readString(asn1.Ber.GeneralizedTime)));
  165. } else {
  166. throw (new Error('Unsupported date format'));
  167. }
  168. }
  169. /* RFC5280, section 4.2.1.6 (GeneralName type) */
  170. var ALTNAME = {
  171. OtherName: Local(0),
  172. RFC822Name: Context(1),
  173. DNSName: Context(2),
  174. X400Address: Local(3),
  175. DirectoryName: Local(4),
  176. EDIPartyName: Local(5),
  177. URI: Context(6),
  178. IPAddress: Context(7),
  179. OID: Context(8)
  180. };
  181. /* RFC5280, section 4.2.1.12 (KeyPurposeId) */
  182. var EXTPURPOSE = {
  183. 'serverAuth': '1.3.6.1.5.5.7.3.1',
  184. 'clientAuth': '1.3.6.1.5.5.7.3.2',
  185. 'codeSigning': '1.3.6.1.5.5.7.3.3',
  186. /* See https://github.com/joyent/oid-docs/blob/master/root.md */
  187. 'joyentDocker': '1.3.6.1.4.1.38678.1.4.1',
  188. 'joyentCmon': '1.3.6.1.4.1.38678.1.4.2'
  189. };
  190. var EXTPURPOSE_REV = {};
  191. Object.keys(EXTPURPOSE).forEach(function (k) {
  192. EXTPURPOSE_REV[EXTPURPOSE[k]] = k;
  193. });
  194. var KEYUSEBITS = [
  195. 'signature', 'identity', 'keyEncryption',
  196. 'encryption', 'keyAgreement', 'ca', 'crl'
  197. ];
  198. function readExtension(cert, buf, der) {
  199. der.readSequence();
  200. var after = der.offset + der.length;
  201. var extId = der.readOID();
  202. var id;
  203. var sig = cert.signatures.x509;
  204. sig.extras.exts = [];
  205. var critical;
  206. if (der.peek() === asn1.Ber.Boolean)
  207. critical = der.readBoolean();
  208. switch (extId) {
  209. case (EXTS.basicConstraints):
  210. der.readSequence(asn1.Ber.OctetString);
  211. der.readSequence();
  212. var bcEnd = der.offset + der.length;
  213. var ca = false;
  214. if (der.peek() === asn1.Ber.Boolean)
  215. ca = der.readBoolean();
  216. if (cert.purposes === undefined)
  217. cert.purposes = [];
  218. if (ca === true)
  219. cert.purposes.push('ca');
  220. var bc = { oid: extId, critical: critical };
  221. if (der.offset < bcEnd && der.peek() === asn1.Ber.Integer)
  222. bc.pathLen = der.readInt();
  223. sig.extras.exts.push(bc);
  224. break;
  225. case (EXTS.extKeyUsage):
  226. der.readSequence(asn1.Ber.OctetString);
  227. der.readSequence();
  228. if (cert.purposes === undefined)
  229. cert.purposes = [];
  230. var ekEnd = der.offset + der.length;
  231. while (der.offset < ekEnd) {
  232. var oid = der.readOID();
  233. cert.purposes.push(EXTPURPOSE_REV[oid] || oid);
  234. }
  235. /*
  236. * This is a bit of a hack: in the case where we have a cert
  237. * that's only allowed to do serverAuth or clientAuth (and not
  238. * the other), we want to make sure all our Subjects are of
  239. * the right type. But we already parsed our Subjects and
  240. * decided if they were hosts or users earlier (since it appears
  241. * first in the cert).
  242. *
  243. * So we go through and mutate them into the right kind here if
  244. * it doesn't match. This might not be hugely beneficial, as it
  245. * seems that single-purpose certs are not often seen in the
  246. * wild.
  247. */
  248. if (cert.purposes.indexOf('serverAuth') !== -1 &&
  249. cert.purposes.indexOf('clientAuth') === -1) {
  250. cert.subjects.forEach(function (ide) {
  251. if (ide.type !== 'host') {
  252. ide.type = 'host';
  253. ide.hostname = ide.uid ||
  254. ide.email ||
  255. ide.components[0].value;
  256. }
  257. });
  258. } else if (cert.purposes.indexOf('clientAuth') !== -1 &&
  259. cert.purposes.indexOf('serverAuth') === -1) {
  260. cert.subjects.forEach(function (ide) {
  261. if (ide.type !== 'user') {
  262. ide.type = 'user';
  263. ide.uid = ide.hostname ||
  264. ide.email ||
  265. ide.components[0].value;
  266. }
  267. });
  268. }
  269. sig.extras.exts.push({ oid: extId, critical: critical });
  270. break;
  271. case (EXTS.keyUsage):
  272. der.readSequence(asn1.Ber.OctetString);
  273. var bits = der.readString(asn1.Ber.BitString, true);
  274. var setBits = readBitField(bits, KEYUSEBITS);
  275. setBits.forEach(function (bit) {
  276. if (cert.purposes === undefined)
  277. cert.purposes = [];
  278. if (cert.purposes.indexOf(bit) === -1)
  279. cert.purposes.push(bit);
  280. });
  281. sig.extras.exts.push({ oid: extId, critical: critical,
  282. bits: bits });
  283. break;
  284. case (EXTS.altName):
  285. der.readSequence(asn1.Ber.OctetString);
  286. der.readSequence();
  287. var aeEnd = der.offset + der.length;
  288. while (der.offset < aeEnd) {
  289. switch (der.peek()) {
  290. case ALTNAME.OtherName:
  291. case ALTNAME.EDIPartyName:
  292. der.readSequence();
  293. der._offset += der.length;
  294. break;
  295. case ALTNAME.OID:
  296. der.readOID(ALTNAME.OID);
  297. break;
  298. case ALTNAME.RFC822Name:
  299. /* RFC822 specifies email addresses */
  300. var email = der.readString(ALTNAME.RFC822Name);
  301. id = Identity.forEmail(email);
  302. if (!cert.subjects[0].equals(id))
  303. cert.subjects.push(id);
  304. break;
  305. case ALTNAME.DirectoryName:
  306. der.readSequence(ALTNAME.DirectoryName);
  307. id = Identity.parseAsn1(der);
  308. if (!cert.subjects[0].equals(id))
  309. cert.subjects.push(id);
  310. break;
  311. case ALTNAME.DNSName:
  312. var host = der.readString(
  313. ALTNAME.DNSName);
  314. id = Identity.forHost(host);
  315. if (!cert.subjects[0].equals(id))
  316. cert.subjects.push(id);
  317. break;
  318. default:
  319. der.readString(der.peek());
  320. break;
  321. }
  322. }
  323. sig.extras.exts.push({ oid: extId, critical: critical });
  324. break;
  325. default:
  326. sig.extras.exts.push({
  327. oid: extId,
  328. critical: critical,
  329. data: der.readString(asn1.Ber.OctetString, true)
  330. });
  331. break;
  332. }
  333. der._offset = after;
  334. }
  335. var UTCTIME_RE =
  336. /^([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
  337. function utcTimeToDate(t) {
  338. var m = t.match(UTCTIME_RE);
  339. assert.ok(m, 'timestamps must be in UTC');
  340. var d = new Date();
  341. var thisYear = d.getUTCFullYear();
  342. var century = Math.floor(thisYear / 100) * 100;
  343. var year = parseInt(m[1], 10);
  344. if (thisYear % 100 < 50 && year >= 60)
  345. year += (century - 1);
  346. else
  347. year += century;
  348. d.setUTCFullYear(year, parseInt(m[2], 10) - 1, parseInt(m[3], 10));
  349. d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
  350. if (m[6] && m[6].length > 0)
  351. d.setUTCSeconds(parseInt(m[6], 10));
  352. return (d);
  353. }
  354. var GTIME_RE =
  355. /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?Z$/;
  356. function gTimeToDate(t) {
  357. var m = t.match(GTIME_RE);
  358. assert.ok(m);
  359. var d = new Date();
  360. d.setUTCFullYear(parseInt(m[1], 10), parseInt(m[2], 10) - 1,
  361. parseInt(m[3], 10));
  362. d.setUTCHours(parseInt(m[4], 10), parseInt(m[5], 10));
  363. if (m[6] && m[6].length > 0)
  364. d.setUTCSeconds(parseInt(m[6], 10));
  365. return (d);
  366. }
  367. function zeroPad(n) {
  368. var s = '' + n;
  369. while (s.length < 2)
  370. s = '0' + s;
  371. return (s);
  372. }
  373. function dateToUTCTime(d) {
  374. var s = '';
  375. s += zeroPad(d.getUTCFullYear() % 100);
  376. s += zeroPad(d.getUTCMonth() + 1);
  377. s += zeroPad(d.getUTCDate());
  378. s += zeroPad(d.getUTCHours());
  379. s += zeroPad(d.getUTCMinutes());
  380. s += zeroPad(d.getUTCSeconds());
  381. s += 'Z';
  382. return (s);
  383. }
  384. function sign(cert, key) {
  385. if (cert.signatures.x509 === undefined)
  386. cert.signatures.x509 = {};
  387. var sig = cert.signatures.x509;
  388. sig.algo = key.type + '-' + key.defaultHashAlgorithm();
  389. if (SIGN_ALGS[sig.algo] === undefined)
  390. return (false);
  391. var der = new asn1.BerWriter();
  392. writeTBSCert(cert, der);
  393. var blob = der.buffer;
  394. sig.cache = blob;
  395. var signer = key.createSign();
  396. signer.write(blob);
  397. cert.signatures.x509.signature = signer.sign();
  398. return (true);
  399. }
  400. function signAsync(cert, signer, done) {
  401. if (cert.signatures.x509 === undefined)
  402. cert.signatures.x509 = {};
  403. var sig = cert.signatures.x509;
  404. var der = new asn1.BerWriter();
  405. writeTBSCert(cert, der);
  406. var blob = der.buffer;
  407. sig.cache = blob;
  408. signer(blob, function (err, signature) {
  409. if (err) {
  410. done(err);
  411. return;
  412. }
  413. sig.algo = signature.type + '-' + signature.hashAlgorithm;
  414. if (SIGN_ALGS[sig.algo] === undefined) {
  415. done(new Error('Invalid signing algorithm "' +
  416. sig.algo + '"'));
  417. return;
  418. }
  419. sig.signature = signature;
  420. done();
  421. });
  422. }
  423. function write(cert, options) {
  424. var sig = cert.signatures.x509;
  425. assert.object(sig, 'x509 signature');
  426. var der = new asn1.BerWriter();
  427. der.startSequence();
  428. if (sig.cache) {
  429. der._ensure(sig.cache.length);
  430. sig.cache.copy(der._buf, der._offset);
  431. der._offset += sig.cache.length;
  432. } else {
  433. writeTBSCert(cert, der);
  434. }
  435. der.startSequence();
  436. der.writeOID(SIGN_ALGS[sig.algo]);
  437. if (sig.algo.match(/^rsa-/))
  438. der.writeNull();
  439. der.endSequence();
  440. var sigData = sig.signature.toBuffer('asn1');
  441. var data = new Buffer(sigData.length + 1);
  442. data[0] = 0;
  443. sigData.copy(data, 1);
  444. der.writeBuffer(data, asn1.Ber.BitString);
  445. der.endSequence();
  446. return (der.buffer);
  447. }
  448. function writeTBSCert(cert, der) {
  449. var sig = cert.signatures.x509;
  450. assert.object(sig, 'x509 signature');
  451. der.startSequence();
  452. der.startSequence(Local(0));
  453. der.writeInt(2);
  454. der.endSequence();
  455. der.writeBuffer(utils.mpNormalize(cert.serial), asn1.Ber.Integer);
  456. der.startSequence();
  457. der.writeOID(SIGN_ALGS[sig.algo]);
  458. if (sig.algo.match(/^rsa-/))
  459. der.writeNull();
  460. der.endSequence();
  461. cert.issuer.toAsn1(der);
  462. der.startSequence();
  463. der.writeString(dateToUTCTime(cert.validFrom), asn1.Ber.UTCTime);
  464. der.writeString(dateToUTCTime(cert.validUntil), asn1.Ber.UTCTime);
  465. der.endSequence();
  466. var subject = cert.subjects[0];
  467. var altNames = cert.subjects.slice(1);
  468. subject.toAsn1(der);
  469. pkcs8.writePkcs8(der, cert.subjectKey);
  470. if (sig.extras && sig.extras.issuerUniqueID) {
  471. der.writeBuffer(sig.extras.issuerUniqueID, Local(1));
  472. }
  473. if (sig.extras && sig.extras.subjectUniqueID) {
  474. der.writeBuffer(sig.extras.subjectUniqueID, Local(2));
  475. }
  476. if (altNames.length > 0 || subject.type === 'host' ||
  477. (cert.purposes !== undefined && cert.purposes.length > 0) ||
  478. (sig.extras && sig.extras.exts)) {
  479. der.startSequence(Local(3));
  480. der.startSequence();
  481. var exts = [];
  482. if (cert.purposes !== undefined && cert.purposes.length > 0) {
  483. exts.push({
  484. oid: EXTS.basicConstraints,
  485. critical: true
  486. });
  487. exts.push({
  488. oid: EXTS.keyUsage,
  489. critical: true
  490. });
  491. exts.push({
  492. oid: EXTS.extKeyUsage,
  493. critical: true
  494. });
  495. }
  496. exts.push({ oid: EXTS.altName });
  497. if (sig.extras && sig.extras.exts)
  498. exts = sig.extras.exts;
  499. for (var i = 0; i < exts.length; ++i) {
  500. der.startSequence();
  501. der.writeOID(exts[i].oid);
  502. if (exts[i].critical !== undefined)
  503. der.writeBoolean(exts[i].critical);
  504. if (exts[i].oid === EXTS.altName) {
  505. der.startSequence(asn1.Ber.OctetString);
  506. der.startSequence();
  507. if (subject.type === 'host') {
  508. der.writeString(subject.hostname,
  509. Context(2));
  510. }
  511. for (var j = 0; j < altNames.length; ++j) {
  512. if (altNames[j].type === 'host') {
  513. der.writeString(
  514. altNames[j].hostname,
  515. ALTNAME.DNSName);
  516. } else if (altNames[j].type ===
  517. 'email') {
  518. der.writeString(
  519. altNames[j].email,
  520. ALTNAME.RFC822Name);
  521. } else {
  522. /*
  523. * Encode anything else as a
  524. * DN style name for now.
  525. */
  526. der.startSequence(
  527. ALTNAME.DirectoryName);
  528. altNames[j].toAsn1(der);
  529. der.endSequence();
  530. }
  531. }
  532. der.endSequence();
  533. der.endSequence();
  534. } else if (exts[i].oid === EXTS.basicConstraints) {
  535. der.startSequence(asn1.Ber.OctetString);
  536. der.startSequence();
  537. var ca = (cert.purposes.indexOf('ca') !== -1);
  538. var pathLen = exts[i].pathLen;
  539. der.writeBoolean(ca);
  540. if (pathLen !== undefined)
  541. der.writeInt(pathLen);
  542. der.endSequence();
  543. der.endSequence();
  544. } else if (exts[i].oid === EXTS.extKeyUsage) {
  545. der.startSequence(asn1.Ber.OctetString);
  546. der.startSequence();
  547. cert.purposes.forEach(function (purpose) {
  548. if (purpose === 'ca')
  549. return;
  550. if (KEYUSEBITS.indexOf(purpose) !== -1)
  551. return;
  552. var oid = purpose;
  553. if (EXTPURPOSE[purpose] !== undefined)
  554. oid = EXTPURPOSE[purpose];
  555. der.writeOID(oid);
  556. });
  557. der.endSequence();
  558. der.endSequence();
  559. } else if (exts[i].oid === EXTS.keyUsage) {
  560. der.startSequence(asn1.Ber.OctetString);
  561. /*
  562. * If we parsed this certificate from a byte
  563. * stream (i.e. we didn't generate it in sshpk)
  564. * then we'll have a ".bits" property on the
  565. * ext with the original raw byte contents.
  566. *
  567. * If we have this, use it here instead of
  568. * regenerating it. This guarantees we output
  569. * the same data we parsed, so signatures still
  570. * validate.
  571. */
  572. if (exts[i].bits !== undefined) {
  573. der.writeBuffer(exts[i].bits,
  574. asn1.Ber.BitString);
  575. } else {
  576. var bits = writeBitField(cert.purposes,
  577. KEYUSEBITS);
  578. der.writeBuffer(bits,
  579. asn1.Ber.BitString);
  580. }
  581. der.endSequence();
  582. } else {
  583. der.writeBuffer(exts[i].data,
  584. asn1.Ber.OctetString);
  585. }
  586. der.endSequence();
  587. }
  588. der.endSequence();
  589. der.endSequence();
  590. }
  591. der.endSequence();
  592. }
  593. /*
  594. * Reads an ASN.1 BER bitfield out of the Buffer produced by doing
  595. * `BerReader#readString(asn1.Ber.BitString)`. That function gives us the raw
  596. * contents of the BitString tag, which is a count of unused bits followed by
  597. * the bits as a right-padded byte string.
  598. *
  599. * `bits` is the Buffer, `bitIndex` should contain an array of string names
  600. * for the bits in the string, ordered starting with bit #0 in the ASN.1 spec.
  601. *
  602. * Returns an array of Strings, the names of the bits that were set to 1.
  603. */
  604. function readBitField(bits, bitIndex) {
  605. var bitLen = 8 * (bits.length - 1) - bits[0];
  606. var setBits = {};
  607. for (var i = 0; i < bitLen; ++i) {
  608. var byteN = 1 + Math.floor(i / 8);
  609. var bit = 7 - (i % 8);
  610. var mask = 1 << bit;
  611. var bitVal = ((bits[byteN] & mask) !== 0);
  612. var name = bitIndex[i];
  613. if (bitVal && typeof (name) === 'string') {
  614. setBits[name] = true;
  615. }
  616. }
  617. return (Object.keys(setBits));
  618. }
  619. /*
  620. * `setBits` is an array of strings, containing the names for each bit that
  621. * sould be set to 1. `bitIndex` is same as in `readBitField()`.
  622. *
  623. * Returns a Buffer, ready to be written out with `BerWriter#writeString()`.
  624. */
  625. function writeBitField(setBits, bitIndex) {
  626. var bitLen = bitIndex.length;
  627. var blen = Math.ceil(bitLen / 8);
  628. var unused = blen * 8 - bitLen;
  629. var bits = new Buffer(1 + blen);
  630. bits.fill(0);
  631. bits[0] = unused;
  632. for (var i = 0; i < bitLen; ++i) {
  633. var byteN = 1 + Math.floor(i / 8);
  634. var bit = 7 - (i % 8);
  635. var mask = 1 << bit;
  636. var name = bitIndex[i];
  637. if (name === undefined)
  638. continue;
  639. var bitVal = (setBits.indexOf(name) !== -1);
  640. if (bitVal) {
  641. bits[byteN] |= mask;
  642. }
  643. }
  644. return (bits);
  645. }