@microsoft/dev-tunnels-ssh
Version:
SSH library for Dev Tunnels
181 lines • 8.17 kB
JavaScript
"use strict";
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sec1KeyFormatter = exports.Pkcs1KeyFormatter = exports.parsePem = exports.formatPem = void 0;
const bigInt_1 = require("../../io/bigInt");
const derData_1 = require("../../io/derData");
const ecdsaCurves_1 = require("../ecdsaCurves");
function formatPem(keyBytes, name) {
const key = `-----BEGIN ${name}-----\n` +
keyBytes
.toString('base64')
.match(/.{1,64}/g)
.join('\n') +
'\n' +
`-----END ${name}-----\n`;
return key;
}
exports.formatPem = formatPem;
function parsePem(key) {
const keyBase64 = key.replace(/-+[^-\n]+KEY-+/g, '').replace(/\s/g, '');
const keyBytes = Buffer.from(keyBase64, 'base64');
return keyBytes;
}
exports.parsePem = parsePem;
/**
* Provides *minimal* PKCS#1 import/export support for Node.js keys.
*
* This code is redundant with some of the PKCS#1 import/export code in the separate
* `ssh-keys` library; that is intentional, and necessary to support a consistent
* interface for importing/exporting key parameters in the core `ssh` library.
*/
class Pkcs1KeyFormatter {
static formatRsaPublic(rsa) {
const writer = new derData_1.DerWriter(Buffer.alloc(1024));
writer.writeInteger(rsa.modulus);
writer.writeInteger(rsa.exponent);
return writer.toBuffer();
}
static formatRsaPrivate(rsa) {
if (!(rsa.d && rsa.p && rsa.q && rsa.dp && rsa.dq && rsa.qi)) {
throw new Error('Missing private key parameters.');
}
const writer = new derData_1.DerWriter(Buffer.alloc(2048));
writer.writeInteger(bigInt_1.BigInt.fromInt32(0));
writer.writeInteger(rsa.modulus);
writer.writeInteger(rsa.exponent);
writer.writeInteger(rsa.d);
writer.writeInteger(rsa.p);
writer.writeInteger(rsa.q);
writer.writeInteger(rsa.dp);
writer.writeInteger(rsa.dq);
writer.writeInteger(rsa.qi);
return writer.toBuffer();
}
static parseRsaPublic(keyBytes) {
const reader = new derData_1.DerReader(keyBytes);
const modulus = reader.readInteger();
const exponent = reader.readInteger();
return { modulus, exponent };
}
static parseRsaPrivate(keyBytes) {
const reader = new derData_1.DerReader(keyBytes);
const version = reader.readInteger();
const modulus = reader.readInteger();
const exponent = reader.readInteger();
const d = reader.readInteger();
const p = reader.readInteger();
const q = reader.readInteger();
const dp = reader.readInteger();
const dq = reader.readInteger();
const qi = reader.readInteger();
return { modulus, exponent, d, p, q, dp, dq, qi };
}
}
exports.Pkcs1KeyFormatter = Pkcs1KeyFormatter;
/**
* Provides *minimal* SEC1 import/export support for Node.js keys.
*
* This code is redundant with some of the SEC1 import/export code in the separate
* `ssh-keys` library; that is intentional, and necessary to support a consistent
* interface for importing/exporting key parameters in the core `ssh` library.
*/
class Sec1KeyFormatter {
static formatECPublic(ec) {
const curve = ecdsaCurves_1.curves.find((c) => c.oid === ec.curve.oid);
const keySizeInBytes = Math.ceil(curve.keySize / 8);
const writer = new derData_1.DerWriter(Buffer.alloc(512));
const oidsWriter = new derData_1.DerWriter(Buffer.alloc(100));
oidsWriter.writeObjectIdentifier(Sec1KeyFormatter.ecPublicKeyOid);
oidsWriter.writeObjectIdentifier(ec.curve.oid);
writer.writeSequence(oidsWriter);
const x = ec.x.toBytes({ unsigned: true, length: keySizeInBytes });
const y = ec.y.toBytes({ unsigned: true, length: keySizeInBytes });
const publicKeyData = Buffer.alloc(1 + x.length + y.length);
publicKeyData[0] = 4; // Indicates uncompressed curve format
x.copy(publicKeyData, 1);
y.copy(publicKeyData, 1 + x.length);
writer.writeBitString(publicKeyData);
return writer.toBuffer();
}
static formatECPrivate(ec) {
const curve = ecdsaCurves_1.curves.find((c) => c.oid === ec.curve.oid);
const keySizeInBytes = Math.ceil(curve.keySize / 8);
const writer = new derData_1.DerWriter(Buffer.alloc(512));
writer.writeInteger(bigInt_1.BigInt.fromInt32(1)); // version
writer.writeOctetString(ec.d.toBytes({ unsigned: true, length: keySizeInBytes }));
const curveWriter = new derData_1.DerWriter(Buffer.alloc(100));
curveWriter.writeObjectIdentifier(ec.curve.oid);
writer.writeTagged(0, curveWriter);
const x = ec.x.toBytes({ unsigned: true, length: keySizeInBytes });
const y = ec.y.toBytes({ unsigned: true, length: keySizeInBytes });
const publicKeyData = Buffer.alloc(1 + x.length + y.length);
publicKeyData[0] = 4; // Indicates uncompressed curve format
x.copy(publicKeyData, 1);
y.copy(publicKeyData, 1 + x.length);
const keyWriter = new derData_1.DerWriter(Buffer.alloc(512));
keyWriter.writeBitString(publicKeyData);
writer.writeTagged(1, keyWriter);
return writer.toBuffer();
}
static parseECPublic(keyBytes) {
var _a;
const reader = new derData_1.DerReader(keyBytes);
const oidsReader = reader.readSequence();
const keyTypeOid = oidsReader.readObjectIdentifier();
if (keyTypeOid !== Sec1KeyFormatter.ecPublicKeyOid) {
throw new Error(`Unexpected key type OID: ${keyTypeOid}`);
}
const curveOid = oidsReader.readObjectIdentifier();
const curveName = (_a = ecdsaCurves_1.curves.find((c) => c.oid === curveOid)) === null || _a === void 0 ? void 0 : _a.name;
const xy = reader.readBitString();
if (xy.length % 2 !== 1) {
throw new Error(`Unexpected key data length: ${xy.length}`);
}
const x = bigInt_1.BigInt.fromBytes(xy.slice(1, 1 + (xy.length - 1) / 2), { unsigned: true });
const y = bigInt_1.BigInt.fromBytes(xy.slice(1 + (xy.length - 1) / 2), { unsigned: true });
const ec = {
curve: { name: curveName, oid: curveOid },
x,
y,
};
return ec;
}
static parseECPrivate(keyBytes) {
var _a;
const reader = new derData_1.DerReader(keyBytes);
const version = reader.readInteger().toInt32();
if (version !== 1) {
throw new Error(`Unsupported SEC1 format version: ${version}`);
}
const d = bigInt_1.BigInt.fromBytes(reader.readOctetString(), { unsigned: true });
const curveReader = reader.tryReadTagged(0);
if (!curveReader) {
throw new Error('SEC1 curve info not found.');
}
const curveOid = curveReader.readObjectIdentifier();
const curveName = (_a = ecdsaCurves_1.curves.find((c) => c.oid === curveOid)) === null || _a === void 0 ? void 0 : _a.name;
const publicKeyReader = reader.tryReadTagged(1);
if (!publicKeyReader) {
throw new Error('SEC1 public key data not found.');
}
const xy = publicKeyReader.readBitString();
if (xy.length % 2 !== 1) {
throw new Error(`Unexpected key data length: ${xy.length}`);
}
const x = bigInt_1.BigInt.fromBytes(xy.slice(1, 1 + (xy.length - 1) / 2), { unsigned: true });
const y = bigInt_1.BigInt.fromBytes(xy.slice(1 + (xy.length - 1) / 2), { unsigned: true });
const ec = {
curve: { name: curveName, oid: curveOid },
x,
y,
d,
};
return ec;
}
}
exports.Sec1KeyFormatter = Sec1KeyFormatter;
Sec1KeyFormatter.ecPublicKeyOid = '1.2.840.10045.2.1';
//# sourceMappingURL=keyFormatters.js.map