You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
612 lines
14 KiB
612 lines
14 KiB
// Copyright 2015 Joyent, Inc. |
|
|
|
module.exports = { |
|
read: read, |
|
readPkcs8: readPkcs8, |
|
write: write, |
|
writePkcs8: writePkcs8, |
|
|
|
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); |
|
der.readSequence(0xa1); |
|
|
|
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 }, |
|
{ 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 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(); |
|
}
|
|
|