UNPKG

@microsoft/dev-tunnels-ssh-keys

Version:

SSH key import/export library for Dev Tunnels

130 lines 6.02 kB
"use strict"; // // Copyright (c) Microsoft Corporation. All rights reserved. // Object.defineProperty(exports, "__esModule", { value: true }); exports.Sec1KeyFormatter = void 0; const dev_tunnels_ssh_1 = require("@microsoft/dev-tunnels-ssh"); const keyData_1 = require("./keyData"); const pkcs1KeyFormatter_1 = require("./pkcs1KeyFormatter"); /** Provides import/export of the SEC1 key format. */ class Sec1KeyFormatter { async import(keyData) { if (!keyData) throw new TypeError('KeyData object expected.'); if (keyData.keyType === Sec1KeyFormatter.privateKeyType || !keyData.keyType) { const parameters = Sec1KeyFormatter.importECPrivate(keyData.data); const keyPair = new dev_tunnels_ssh_1.ECDsa.KeyPair(); await keyPair.importParameters(parameters); return keyPair; } return null; } async export(keyPair, includePrivate) { if (!keyPair) throw new TypeError('KeyPair object expected.'); if (!includePrivate) { throw new Error('Public-only export is not supported by this format.'); } if (keyPair instanceof dev_tunnels_ssh_1.ECDsa.KeyPair) { if (!keyPair.hasPublicKey) { throw new Error('KeyPair object does not have a public key.'); } else if (!keyPair.hasPrivateKey) { throw new Error('KeyPair object does not have a private key.'); } const keyData = new keyData_1.KeyData(); const parameters = await keyPair.exportParameters(); keyData.keyType = Sec1KeyFormatter.privateKeyType; keyData.data = Sec1KeyFormatter.exportECPrivate(parameters); return keyData; } else { throw new Error('KeyPair class not supported.'); } } async decrypt(keyData, passphrase) { if (!keyData) throw new TypeError('KeyData object expected.'); if (keyData.keyType === Sec1KeyFormatter.privateKeyType || !keyData.keyType) { const procTypeHeader = keyData.headers.get('Proc-Type'); if (procTypeHeader === '4,ENCRYPTED') { if (!passphrase) { throw new Error('A passphrase is required to decrypt the key.'); } return pkcs1KeyFormatter_1.Pkcs1KeyFormatter.decryptPrivate(keyData, passphrase); } else { return keyData; } } return null; } async encrypt(keyData, passphrase) { if (!keyData) throw new TypeError('KeyData object expected.'); if (keyData.keyType === Sec1KeyFormatter.privateKeyType) { throw new Error('SEC1 export with passphrase is not supported because the format uses ' + 'a weak key derivation algorithm. Use PKCS#8 to export a ' + 'passphrase-protected private key.'); } else { throw new Error(`Unsupported key type: ${keyData.keyType}`); } } static exportECPrivate(ec) { const curve = dev_tunnels_ssh_1.ECDsa.curves.find((c) => c.oid === ec.curve.oid); const keySizeInBytes = Math.ceil(curve.keySize / 8); const writer = new dev_tunnels_ssh_1.DerWriter(Buffer.alloc(1024)); writer.writeInteger(dev_tunnels_ssh_1.BigInt.fromInt32(1)); // version writer.writeOctetString(ec.d.toBytes({ unsigned: true, length: keySizeInBytes })); const curveWriter = new dev_tunnels_ssh_1.DerWriter(Buffer.alloc(100)); curveWriter.writeObjectIdentifier(ec.curve.oid); writer.writeTagged(0, curveWriter); const publicKeyWriter = new dev_tunnels_ssh_1.DerWriter(Buffer.alloc(512)); 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); publicKeyWriter.writeBitString(publicKeyData); writer.writeTagged(1, publicKeyWriter); return writer.toBuffer(); } static importECPrivate(keyBytes) { var _a; const reader = new dev_tunnels_ssh_1.DerReader(keyBytes); const version = reader.readInteger().toInt32(); if (version !== 1) { throw new Error(`Unsupported SEC1 format version: ${version}`); } const d = dev_tunnels_ssh_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 = dev_tunnels_ssh_1.ECDsa.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 = dev_tunnels_ssh_1.BigInt.fromBytes(xy.slice(1, 1 + (xy.length - 1) / 2), { unsigned: true }); const y = dev_tunnels_ssh_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.privateKeyType = 'EC PRIVATE KEY'; //# sourceMappingURL=sec1KeyFormatter.js.map