| // Copyright 2018 Joyent, Inc. | 
|   | 
| module.exports = { | 
|     read: read, | 
|     readPkcs8: readPkcs8, | 
|     write: write, | 
|     writePkcs8: writePkcs8, | 
|     pkcs8ToBuffer: pkcs8ToBuffer, | 
|   | 
|     readECDSACurve: readECDSACurve, | 
|     writeECDSACurve: writeECDSACurve | 
| }; | 
|   | 
| var assert = require('assert-plus'); | 
| var asn1 = require('asn1'); | 
| var Buffer = require('safer-buffer').Buffer; | 
| var algs = require('../algs'); | 
| var utils = require('../utils'); | 
| var Key = require('../key'); | 
| var PrivateKey = require('../private-key'); | 
| var pem = require('./pem'); | 
|   | 
| function read(buf, options) { | 
|     return (pem.read(buf, options, 'pkcs8')); | 
| } | 
|   | 
| function write(key, options) { | 
|     return (pem.write(key, options, 'pkcs8')); | 
| } | 
|   | 
| /* Helper to read in a single mpint */ | 
| function readMPInt(der, nm) { | 
|     assert.strictEqual(der.peek(), asn1.Ber.Integer, | 
|         nm + ' is not an Integer'); | 
|     return (utils.mpNormalize(der.readString(asn1.Ber.Integer, true))); | 
| } | 
|   | 
| function readPkcs8(alg, type, der) { | 
|     /* Private keys in pkcs#8 format have a weird extra int */ | 
|     if (der.peek() === asn1.Ber.Integer) { | 
|         assert.strictEqual(type, 'private', | 
|             'unexpected Integer at start of public key'); | 
|         der.readString(asn1.Ber.Integer, true); | 
|     } | 
|   | 
|     der.readSequence(); | 
|     var next = der.offset + der.length; | 
|   | 
|     var oid = der.readOID(); | 
|     switch (oid) { | 
|     case '1.2.840.113549.1.1.1': | 
|         der._offset = next; | 
|         if (type === 'public') | 
|             return (readPkcs8RSAPublic(der)); | 
|         else | 
|             return (readPkcs8RSAPrivate(der)); | 
|     case '1.2.840.10040.4.1': | 
|         if (type === 'public') | 
|             return (readPkcs8DSAPublic(der)); | 
|         else | 
|             return (readPkcs8DSAPrivate(der)); | 
|     case '1.2.840.10045.2.1': | 
|         if (type === 'public') | 
|             return (readPkcs8ECDSAPublic(der)); | 
|         else | 
|             return (readPkcs8ECDSAPrivate(der)); | 
|     case '1.3.101.112': | 
|         if (type === 'public') { | 
|             return (readPkcs8EdDSAPublic(der)); | 
|         } else { | 
|             return (readPkcs8EdDSAPrivate(der)); | 
|         } | 
|     case '1.3.101.110': | 
|         if (type === 'public') { | 
|             return (readPkcs8X25519Public(der)); | 
|         } else { | 
|             return (readPkcs8X25519Private(der)); | 
|         } | 
|     default: | 
|         throw (new Error('Unknown key type OID ' + oid)); | 
|     } | 
| } | 
|   | 
| function readPkcs8RSAPublic(der) { | 
|     // bit string sequence | 
|     der.readSequence(asn1.Ber.BitString); | 
|     der.readByte(); | 
|     der.readSequence(); | 
|   | 
|     // modulus | 
|     var n = readMPInt(der, 'modulus'); | 
|     var e = readMPInt(der, 'exponent'); | 
|   | 
|     // now, make the key | 
|     var key = { | 
|         type: 'rsa', | 
|         source: der.originalInput, | 
|         parts: [ | 
|             { name: 'e', data: e }, | 
|             { name: 'n', data: n } | 
|         ] | 
|     }; | 
|   | 
|     return (new Key(key)); | 
| } | 
|   | 
| function readPkcs8RSAPrivate(der) { | 
|     der.readSequence(asn1.Ber.OctetString); | 
|     der.readSequence(); | 
|   | 
|     var ver = readMPInt(der, 'version'); | 
|     assert.equal(ver[0], 0x0, 'unknown RSA private key version'); | 
|   | 
|     // modulus then public exponent | 
|     var n = readMPInt(der, 'modulus'); | 
|     var e = readMPInt(der, 'public exponent'); | 
|     var d = readMPInt(der, 'private exponent'); | 
|     var p = readMPInt(der, 'prime1'); | 
|     var q = readMPInt(der, 'prime2'); | 
|     var dmodp = readMPInt(der, 'exponent1'); | 
|     var dmodq = readMPInt(der, 'exponent2'); | 
|     var iqmp = readMPInt(der, 'iqmp'); | 
|   | 
|     // now, make the key | 
|     var key = { | 
|         type: 'rsa', | 
|         parts: [ | 
|             { name: 'n', data: n }, | 
|             { name: 'e', data: e }, | 
|             { name: 'd', data: d }, | 
|             { name: 'iqmp', data: iqmp }, | 
|             { name: 'p', data: p }, | 
|             { name: 'q', data: q }, | 
|             { name: 'dmodp', data: dmodp }, | 
|             { name: 'dmodq', data: dmodq } | 
|         ] | 
|     }; | 
|   | 
|     return (new PrivateKey(key)); | 
| } | 
|   | 
| function readPkcs8DSAPublic(der) { | 
|     der.readSequence(); | 
|   | 
|     var p = readMPInt(der, 'p'); | 
|     var q = readMPInt(der, 'q'); | 
|     var g = readMPInt(der, 'g'); | 
|   | 
|     // bit string sequence | 
|     der.readSequence(asn1.Ber.BitString); | 
|     der.readByte(); | 
|   | 
|     var y = readMPInt(der, 'y'); | 
|   | 
|     // now, make the key | 
|     var key = { | 
|         type: 'dsa', | 
|         parts: [ | 
|             { name: 'p', data: p }, | 
|             { name: 'q', data: q }, | 
|             { name: 'g', data: g }, | 
|             { name: 'y', data: y } | 
|         ] | 
|     }; | 
|   | 
|     return (new Key(key)); | 
| } | 
|   | 
| function readPkcs8DSAPrivate(der) { | 
|     der.readSequence(); | 
|   | 
|     var p = readMPInt(der, 'p'); | 
|     var q = readMPInt(der, 'q'); | 
|     var g = readMPInt(der, 'g'); | 
|   | 
|     der.readSequence(asn1.Ber.OctetString); | 
|     var x = readMPInt(der, 'x'); | 
|   | 
|     /* The pkcs#8 format does not include the public key */ | 
|     var y = utils.calculateDSAPublic(g, p, x); | 
|   | 
|     var key = { | 
|         type: 'dsa', | 
|         parts: [ | 
|             { name: 'p', data: p }, | 
|             { name: 'q', data: q }, | 
|             { name: 'g', data: g }, | 
|             { name: 'y', data: y }, | 
|             { name: 'x', data: x } | 
|         ] | 
|     }; | 
|   | 
|     return (new PrivateKey(key)); | 
| } | 
|   | 
| function readECDSACurve(der) { | 
|     var curveName, curveNames; | 
|     var j, c, cd; | 
|   | 
|     if (der.peek() === asn1.Ber.OID) { | 
|         var oid = der.readOID(); | 
|   | 
|         curveNames = Object.keys(algs.curves); | 
|         for (j = 0; j < curveNames.length; ++j) { | 
|             c = curveNames[j]; | 
|             cd = algs.curves[c]; | 
|             if (cd.pkcs8oid === oid) { | 
|                 curveName = c; | 
|                 break; | 
|             } | 
|         } | 
|   | 
|     } else { | 
|         // ECParameters sequence | 
|         der.readSequence(); | 
|         var version = der.readString(asn1.Ber.Integer, true); | 
|         assert.strictEqual(version[0], 1, 'ECDSA key not version 1'); | 
|   | 
|         var curve = {}; | 
|   | 
|         // FieldID sequence | 
|         der.readSequence(); | 
|         var fieldTypeOid = der.readOID(); | 
|         assert.strictEqual(fieldTypeOid, '1.2.840.10045.1.1', | 
|             'ECDSA key is not from a prime-field'); | 
|         var p = curve.p = utils.mpNormalize( | 
|             der.readString(asn1.Ber.Integer, true)); | 
|         /* | 
|          * p always starts with a 1 bit, so count the zeros to get its | 
|          * real size. | 
|          */ | 
|         curve.size = p.length * 8 - utils.countZeros(p); | 
|   | 
|         // Curve sequence | 
|         der.readSequence(); | 
|         curve.a = utils.mpNormalize( | 
|             der.readString(asn1.Ber.OctetString, true)); | 
|         curve.b = utils.mpNormalize( | 
|             der.readString(asn1.Ber.OctetString, true)); | 
|         if (der.peek() === asn1.Ber.BitString) | 
|             curve.s = der.readString(asn1.Ber.BitString, true); | 
|   | 
|         // Combined Gx and Gy | 
|         curve.G = der.readString(asn1.Ber.OctetString, true); | 
|         assert.strictEqual(curve.G[0], 0x4, | 
|             'uncompressed G is required'); | 
|   | 
|         curve.n = utils.mpNormalize( | 
|             der.readString(asn1.Ber.Integer, true)); | 
|         curve.h = utils.mpNormalize( | 
|             der.readString(asn1.Ber.Integer, true)); | 
|         assert.strictEqual(curve.h[0], 0x1, 'a cofactor=1 curve is ' + | 
|             'required'); | 
|   | 
|         curveNames = Object.keys(algs.curves); | 
|         var ks = Object.keys(curve); | 
|         for (j = 0; j < curveNames.length; ++j) { | 
|             c = curveNames[j]; | 
|             cd = algs.curves[c]; | 
|             var equal = true; | 
|             for (var i = 0; i < ks.length; ++i) { | 
|                 var k = ks[i]; | 
|                 if (cd[k] === undefined) | 
|                     continue; | 
|                 if (typeof (cd[k]) === 'object' && | 
|                     cd[k].equals !== undefined) { | 
|                     if (!cd[k].equals(curve[k])) { | 
|                         equal = false; | 
|                         break; | 
|                     } | 
|                 } else if (Buffer.isBuffer(cd[k])) { | 
|                     if (cd[k].toString('binary') | 
|                         !== curve[k].toString('binary')) { | 
|                         equal = false; | 
|                         break; | 
|                     } | 
|                 } else { | 
|                     if (cd[k] !== curve[k]) { | 
|                         equal = false; | 
|                         break; | 
|                     } | 
|                 } | 
|             } | 
|             if (equal) { | 
|                 curveName = c; | 
|                 break; | 
|             } | 
|         } | 
|     } | 
|     return (curveName); | 
| } | 
|   | 
| function readPkcs8ECDSAPrivate(der) { | 
|     var curveName = readECDSACurve(der); | 
|     assert.string(curveName, 'a known elliptic curve'); | 
|   | 
|     der.readSequence(asn1.Ber.OctetString); | 
|     der.readSequence(); | 
|   | 
|     var version = readMPInt(der, 'version'); | 
|     assert.equal(version[0], 1, 'unknown version of ECDSA key'); | 
|   | 
|     var d = der.readString(asn1.Ber.OctetString, true); | 
|     var Q; | 
|   | 
|     if (der.peek() == 0xa0) { | 
|         der.readSequence(0xa0); | 
|         der._offset += der.length; | 
|     } | 
|     if (der.peek() == 0xa1) { | 
|         der.readSequence(0xa1); | 
|         Q = der.readString(asn1.Ber.BitString, true); | 
|         Q = utils.ecNormalize(Q); | 
|     } | 
|   | 
|     if (Q === undefined) { | 
|         var pub = utils.publicFromPrivateECDSA(curveName, d); | 
|         Q = pub.part.Q.data; | 
|     } | 
|   | 
|     var key = { | 
|         type: 'ecdsa', | 
|         parts: [ | 
|             { name: 'curve', data: Buffer.from(curveName) }, | 
|             { name: 'Q', data: Q }, | 
|             { name: 'd', data: d } | 
|         ] | 
|     }; | 
|   | 
|     return (new PrivateKey(key)); | 
| } | 
|   | 
| function readPkcs8ECDSAPublic(der) { | 
|     var curveName = readECDSACurve(der); | 
|     assert.string(curveName, 'a known elliptic curve'); | 
|   | 
|     var Q = der.readString(asn1.Ber.BitString, true); | 
|     Q = utils.ecNormalize(Q); | 
|   | 
|     var key = { | 
|         type: 'ecdsa', | 
|         parts: [ | 
|             { name: 'curve', data: Buffer.from(curveName) }, | 
|             { name: 'Q', data: Q } | 
|         ] | 
|     }; | 
|   | 
|     return (new Key(key)); | 
| } | 
|   | 
| function readPkcs8EdDSAPublic(der) { | 
|     if (der.peek() === 0x00) | 
|         der.readByte(); | 
|   | 
|     var A = utils.readBitString(der); | 
|   | 
|     var key = { | 
|         type: 'ed25519', | 
|         parts: [ | 
|             { name: 'A', data: utils.zeroPadToLength(A, 32) } | 
|         ] | 
|     }; | 
|   | 
|     return (new Key(key)); | 
| } | 
|   | 
| function readPkcs8X25519Public(der) { | 
|     var A = utils.readBitString(der); | 
|   | 
|     var key = { | 
|         type: 'curve25519', | 
|         parts: [ | 
|             { name: 'A', data: utils.zeroPadToLength(A, 32) } | 
|         ] | 
|     }; | 
|   | 
|     return (new Key(key)); | 
| } | 
|   | 
| function readPkcs8EdDSAPrivate(der) { | 
|     if (der.peek() === 0x00) | 
|         der.readByte(); | 
|   | 
|     der.readSequence(asn1.Ber.OctetString); | 
|     var k = der.readString(asn1.Ber.OctetString, true); | 
|     k = utils.zeroPadToLength(k, 32); | 
|   | 
|     var A; | 
|     if (der.peek() === asn1.Ber.BitString) { | 
|         A = utils.readBitString(der); | 
|         A = utils.zeroPadToLength(A, 32); | 
|     } else { | 
|         A = utils.calculateED25519Public(k); | 
|     } | 
|   | 
|     var key = { | 
|         type: 'ed25519', | 
|         parts: [ | 
|             { name: 'A', data: utils.zeroPadToLength(A, 32) }, | 
|             { name: 'k', data: utils.zeroPadToLength(k, 32) } | 
|         ] | 
|     }; | 
|   | 
|     return (new PrivateKey(key)); | 
| } | 
|   | 
| function readPkcs8X25519Private(der) { | 
|     if (der.peek() === 0x00) | 
|         der.readByte(); | 
|   | 
|     der.readSequence(asn1.Ber.OctetString); | 
|     var k = der.readString(asn1.Ber.OctetString, true); | 
|     k = utils.zeroPadToLength(k, 32); | 
|   | 
|     var A = utils.calculateX25519Public(k); | 
|   | 
|     var key = { | 
|         type: 'curve25519', | 
|         parts: [ | 
|             { name: 'A', data: utils.zeroPadToLength(A, 32) }, | 
|             { name: 'k', data: utils.zeroPadToLength(k, 32) } | 
|         ] | 
|     }; | 
|   | 
|     return (new PrivateKey(key)); | 
| } | 
|   | 
| function pkcs8ToBuffer(key) { | 
|     var der = new asn1.BerWriter(); | 
|     writePkcs8(der, key); | 
|     return (der.buffer); | 
| } | 
|   | 
| function writePkcs8(der, key) { | 
|     der.startSequence(); | 
|   | 
|     if (PrivateKey.isPrivateKey(key)) { | 
|         var sillyInt = Buffer.from([0]); | 
|         der.writeBuffer(sillyInt, asn1.Ber.Integer); | 
|     } | 
|   | 
|     der.startSequence(); | 
|     switch (key.type) { | 
|     case 'rsa': | 
|         der.writeOID('1.2.840.113549.1.1.1'); | 
|         if (PrivateKey.isPrivateKey(key)) | 
|             writePkcs8RSAPrivate(key, der); | 
|         else | 
|             writePkcs8RSAPublic(key, der); | 
|         break; | 
|     case 'dsa': | 
|         der.writeOID('1.2.840.10040.4.1'); | 
|         if (PrivateKey.isPrivateKey(key)) | 
|             writePkcs8DSAPrivate(key, der); | 
|         else | 
|             writePkcs8DSAPublic(key, der); | 
|         break; | 
|     case 'ecdsa': | 
|         der.writeOID('1.2.840.10045.2.1'); | 
|         if (PrivateKey.isPrivateKey(key)) | 
|             writePkcs8ECDSAPrivate(key, der); | 
|         else | 
|             writePkcs8ECDSAPublic(key, der); | 
|         break; | 
|     case 'ed25519': | 
|         der.writeOID('1.3.101.112'); | 
|         if (PrivateKey.isPrivateKey(key)) | 
|             throw (new Error('Ed25519 private keys in pkcs8 ' + | 
|                 'format are not supported')); | 
|         writePkcs8EdDSAPublic(key, der); | 
|         break; | 
|     default: | 
|         throw (new Error('Unsupported key type: ' + key.type)); | 
|     } | 
|   | 
|     der.endSequence(); | 
| } | 
|   | 
| function writePkcs8RSAPrivate(key, der) { | 
|     der.writeNull(); | 
|     der.endSequence(); | 
|   | 
|     der.startSequence(asn1.Ber.OctetString); | 
|     der.startSequence(); | 
|   | 
|     var version = Buffer.from([0]); | 
|     der.writeBuffer(version, asn1.Ber.Integer); | 
|   | 
|     der.writeBuffer(key.part.n.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.e.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.d.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.p.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.q.data, asn1.Ber.Integer); | 
|     if (!key.part.dmodp || !key.part.dmodq) | 
|         utils.addRSAMissing(key); | 
|     der.writeBuffer(key.part.dmodp.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.dmodq.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.iqmp.data, asn1.Ber.Integer); | 
|   | 
|     der.endSequence(); | 
|     der.endSequence(); | 
| } | 
|   | 
| function writePkcs8RSAPublic(key, der) { | 
|     der.writeNull(); | 
|     der.endSequence(); | 
|   | 
|     der.startSequence(asn1.Ber.BitString); | 
|     der.writeByte(0x00); | 
|   | 
|     der.startSequence(); | 
|     der.writeBuffer(key.part.n.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.e.data, asn1.Ber.Integer); | 
|     der.endSequence(); | 
|   | 
|     der.endSequence(); | 
| } | 
|   | 
| function writePkcs8DSAPrivate(key, der) { | 
|     der.startSequence(); | 
|     der.writeBuffer(key.part.p.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.q.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.g.data, asn1.Ber.Integer); | 
|     der.endSequence(); | 
|   | 
|     der.endSequence(); | 
|   | 
|     der.startSequence(asn1.Ber.OctetString); | 
|     der.writeBuffer(key.part.x.data, asn1.Ber.Integer); | 
|     der.endSequence(); | 
| } | 
|   | 
| function writePkcs8DSAPublic(key, der) { | 
|     der.startSequence(); | 
|     der.writeBuffer(key.part.p.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.q.data, asn1.Ber.Integer); | 
|     der.writeBuffer(key.part.g.data, asn1.Ber.Integer); | 
|     der.endSequence(); | 
|     der.endSequence(); | 
|   | 
|     der.startSequence(asn1.Ber.BitString); | 
|     der.writeByte(0x00); | 
|     der.writeBuffer(key.part.y.data, asn1.Ber.Integer); | 
|     der.endSequence(); | 
| } | 
|   | 
| function writeECDSACurve(key, der) { | 
|     var curve = algs.curves[key.curve]; | 
|     if (curve.pkcs8oid) { | 
|         /* This one has a name in pkcs#8, so just write the oid */ | 
|         der.writeOID(curve.pkcs8oid); | 
|   | 
|     } else { | 
|         // ECParameters sequence | 
|         der.startSequence(); | 
|   | 
|         var version = Buffer.from([1]); | 
|         der.writeBuffer(version, asn1.Ber.Integer); | 
|   | 
|         // FieldID sequence | 
|         der.startSequence(); | 
|         der.writeOID('1.2.840.10045.1.1'); // prime-field | 
|         der.writeBuffer(curve.p, asn1.Ber.Integer); | 
|         der.endSequence(); | 
|   | 
|         // Curve sequence | 
|         der.startSequence(); | 
|         var a = curve.p; | 
|         if (a[0] === 0x0) | 
|             a = a.slice(1); | 
|         der.writeBuffer(a, asn1.Ber.OctetString); | 
|         der.writeBuffer(curve.b, asn1.Ber.OctetString); | 
|         der.writeBuffer(curve.s, asn1.Ber.BitString); | 
|         der.endSequence(); | 
|   | 
|         der.writeBuffer(curve.G, asn1.Ber.OctetString); | 
|         der.writeBuffer(curve.n, asn1.Ber.Integer); | 
|         var h = curve.h; | 
|         if (!h) { | 
|             h = Buffer.from([1]); | 
|         } | 
|         der.writeBuffer(h, asn1.Ber.Integer); | 
|   | 
|         // ECParameters | 
|         der.endSequence(); | 
|     } | 
| } | 
|   | 
| function writePkcs8ECDSAPublic(key, der) { | 
|     writeECDSACurve(key, der); | 
|     der.endSequence(); | 
|   | 
|     var Q = utils.ecNormalize(key.part.Q.data, true); | 
|     der.writeBuffer(Q, asn1.Ber.BitString); | 
| } | 
|   | 
| function writePkcs8ECDSAPrivate(key, der) { | 
|     writeECDSACurve(key, der); | 
|     der.endSequence(); | 
|   | 
|     der.startSequence(asn1.Ber.OctetString); | 
|     der.startSequence(); | 
|   | 
|     var version = Buffer.from([1]); | 
|     der.writeBuffer(version, asn1.Ber.Integer); | 
|   | 
|     der.writeBuffer(key.part.d.data, asn1.Ber.OctetString); | 
|   | 
|     der.startSequence(0xa1); | 
|     var Q = utils.ecNormalize(key.part.Q.data, true); | 
|     der.writeBuffer(Q, asn1.Ber.BitString); | 
|     der.endSequence(); | 
|   | 
|     der.endSequence(); | 
|     der.endSequence(); | 
| } | 
|   | 
| function writePkcs8EdDSAPublic(key, der) { | 
|     der.endSequence(); | 
|   | 
|     utils.writeBitString(der, key.part.A.data); | 
| } | 
|   | 
| function writePkcs8EdDSAPrivate(key, der) { | 
|     der.endSequence(); | 
|   | 
|     var k = utils.mpNormalize(key.part.k.data, true); | 
|     der.startSequence(asn1.Ber.OctetString); | 
|     der.writeBuffer(k, asn1.Ber.OctetString); | 
|     der.endSequence(); | 
| } |