| // 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(); |
| } |