"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.MessageSigningService = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _crypto = require("crypto"); var _coreSavedObjectsServer = require("@kbn/core-saved-objects-server"); var _errors = require("../../../common/errors"); var _constants = require("../../constants"); var _app_context = require("../app_context"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ class MessageSigningService { constructor(esoClient) { (0, _defineProperty2.default)(this, "_soClient", void 0); this.esoClient = esoClient; } get isEncryptionAvailable() { var _appContextService$ge, _appContextService$ge2; return (_appContextService$ge = (_appContextService$ge2 = _app_context.appContextService.getEncryptedSavedObjectsSetup()) === null || _appContextService$ge2 === void 0 ? void 0 : _appContextService$ge2.canEncrypt) !== null && _appContextService$ge !== void 0 ? _appContextService$ge : false; } async generateKeyPair(providedPassphrase) { const existingKeyPair = await this.checkForExistingKeyPair(); if (existingKeyPair) { return existingKeyPair; } const passphrase = providedPassphrase || this.generatePassphrase(); const keyPair = (0, _crypto.generateKeyPairSync)('ec', { namedCurve: 'prime256v1', privateKeyEncoding: { type: 'pkcs8', format: 'der', cipher: 'aes-256-cbc', passphrase }, publicKeyEncoding: { type: 'spki', format: 'der' } }); const privateKey = keyPair.privateKey.toString('base64'); const publicKey = keyPair.publicKey.toString('base64'); let keypairSoObject = { private_key: privateKey, public_key: publicKey }; keypairSoObject = this.isEncryptionAvailable ? { ...keypairSoObject, passphrase } : { ...keypairSoObject, passphrase_plain: passphrase }; try { await this.soClient.create(_constants.MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, keypairSoObject); return { privateKey, publicKey, passphrase }; } catch (error) { throw new _errors.MessageSigningError(`Error creating key pair: ${error.message}`, error); } } async sign(message) { const { privateKey: serializedPrivateKey, passphrase } = await this.generateKeyPair(); const msgBuffer = Buffer.isBuffer(message) ? message : Buffer.from(JSON.stringify(message), 'utf8'); const signer = (0, _crypto.createSign)('SHA256'); signer.update(msgBuffer); signer.end(); if (!serializedPrivateKey) { throw new Error('unable to find private key'); } if (!passphrase) { throw new Error('unable to find passphrase'); } const privateKey = Buffer.from(serializedPrivateKey, 'base64'); const signature = signer.sign({ key: privateKey, passphrase, format: 'der', type: 'pkcs8' }, 'base64'); return { data: msgBuffer, signature }; } async getPublicKey() { const { publicKey } = await this.generateKeyPair(); if (!publicKey) { throw new Error('unable to find public key'); } return publicKey; } async rotateKeyPair() { try { await this.removeKeyPair(); await this.generateKeyPair(); } catch (error) { throw new _errors.MessageSigningError(`Error rotating key pair: ${error.message}`, error); } } async removeKeyPair() { let currentKeyPair; try { currentKeyPair = await this.getCurrentKeyPairObj(); if (!currentKeyPair) { throw new _errors.MessageSigningError('No current key pair found!'); } } catch (error) { throw new _errors.MessageSigningError(`Error fetching current key pair: ${error.message}`, error); } try { await this.soClient.delete(_constants.MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, currentKeyPair.id); } catch (error) { throw new _errors.MessageSigningError(`Error deleting current key pair: ${error.message}`, error); } } get soClient() { if (this._soClient) { return this._soClient; } const fakeRequest = { headers: {}, getBasePath: () => '', path: '/', route: { settings: {} }, url: { href: {} }, raw: { req: { url: '/' } } }; this._soClient = _app_context.appContextService.getSavedObjects().getScopedClient(fakeRequest, { excludedExtensions: [_coreSavedObjectsServer.SECURITY_EXTENSION_ID], includedHiddenTypes: [_constants.MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE] }); return this._soClient; } async getCurrentKeyPairObj() { const finder = await this.esoClient.createPointInTimeFinderDecryptedAsInternalUser({ type: _constants.MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, perPage: 1, sortField: 'created_at', sortOrder: 'desc' }); let soDoc; for await (const result of finder.find()) { soDoc = result.saved_objects[0]; break; } finder.close(); return soDoc; } async checkForExistingKeyPair() { const currentKeyPair = await this.getCurrentKeyPairObj(); if (!currentKeyPair) { return; } const { attributes } = currentKeyPair; if (!attributes) { return; } const { private_key: privateKey, public_key: publicKey, passphrase: passphraseEncrypted, passphrase_plain: passphrasePlain } = attributes; const passphrase = passphraseEncrypted || passphrasePlain; if (!privateKey || !publicKey || !passphrase) { return; } // newly configured encryption key, encrypt the passphrase if (passphrasePlain && this.isEncryptionAvailable) { await this.soClient.update(_constants.MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE, currentKeyPair === null || currentKeyPair === void 0 ? void 0 : currentKeyPair.id, { ...attributes, passphrase, passphrase_plain: '' }); } return { privateKey, publicKey, passphrase }; } generatePassphrase() { return (0, _crypto.randomBytes)(32).toString('hex'); } } exports.MessageSigningService = MessageSigningService;