UNPKG

lemon-core

Version:
220 lines 9.84 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AWSKMSService = exports.fromBase64 = exports.isBase64 = void 0; /** * file: `cores/aws-kms-service.ts` * - kms service for AWS KMS. * * * **TODO** * - [x] in VPC lambda, will timeout due to NAT => so, need to support via engine. => use `VPC Endpoints` with kms service. * - [x] error of `not authorized` => use one of below. * 1. add this Role (lemon-todaq-api-prod-ap-northeast-2-lambdaRole) to `Key users`. * 2. add `kms:Decrypt` `kms:Encrypt` in resource of `iamRoleStatements`. * * * @author Steve Jung <steve@lemoncloud.io> * @date 2019-10-30 initial version. * * @copyright (C) lemoncloud.io 2019 - All Rights Reserved. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars const engine_1 = require("../../engine/"); const client_kms_1 = require("@aws-sdk/client-kms"); const client_kms_2 = require("@aws-sdk/client-kms"); const tools_1 = require("../../tools"); const NS = engine_1.$U.NS('KMSS', 'blue'); // NAMESPACE TO BE PRINTED. const ALIAS = `lemon-hello-api`; //NOTE - use env[KMS_KEY_ID] to overide. const region = () => engine_1.$engine.environ('REGION', 'ap-northeast-2'); const instance = () => { const cfg = (0, tools_1.awsConfig)(engine_1.$engine, region()); return new client_kms_1.KMSClient(cfg); }; /** * check if base64 string. */ const isBase64 = (text) => /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/.test(text); exports.isBase64 = isBase64; /** * normal base64 to url encoded. */ const fromBase64 = (base64) => base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); exports.fromBase64 = fromBase64; /** * class: `AWSKMSService` * - shared Key Management Service to encrypt/decrypt message. */ class AWSKMSService { constructor(keyId, options) { /** * get name of this */ this.name = () => `KMS`; /** * hello */ this.hello = () => `aws-kms-service:${this._keyId}`; /** * get key-id to encrypt. */ this.keyId = () => { if (!this._keyId || typeof this._keyId !== 'string') throw new Error(`.keyId<${typeof this._keyId}> (string) is required!`); return this._keyId; }; /** * Encrypt message * * @param {*} message */ this.encrypt = (message) => __awaiter(this, void 0, void 0, function* () { const keyId = this.keyId(); (0, engine_1._inf)(NS, `encrypt(${keyId}, ${message.substring(0, 10)}...)..`); const KeyId = keyId; const params = { KeyId, Plaintext: Buffer.from(message), }; const result = yield this.instance().send(new client_kms_2.EncryptCommand(params)); (0, engine_1._log)(NS, '> result =', result); const ciphertext = result.CiphertextBlob ? Buffer.from(result.CiphertextBlob).toString('base64') : message; (0, engine_1._log)(NS, '> ciphertext =', ciphertext.substring(0, 32), '...'); return ciphertext; }); /** * Decrypt message * * @param {*} encryptedSecret */ this.decrypt = (encryptedSecret) => __awaiter(this, void 0, void 0, function* () { (0, engine_1._inf)(NS, `decrypt(${encryptedSecret.substring(0, 12)}...)..`); const CiphertextBlob = Buffer.from(encryptedSecret, 'base64'); //* api param. const params = { CiphertextBlob: CiphertextBlob }; const data = yield this.instance().send(new client_kms_2.DecryptCommand(params)); // _log(NS, '> data.type =', typeof data); return (data === null || data === void 0 ? void 0 : data.Plaintext) ? Buffer.from(data.Plaintext).toString('utf-8') : ''; }); /** * make signature by message * * @param {*} message any string * @param forJwtSignature (option) flag to get JWT signature format. */ this.sign = (message, forJwtSignature = true) => __awaiter(this, void 0, void 0, function* () { var _a, _b; if (!message || typeof message !== 'string') throw new Error(`@message[${message}] is invalid - kms.sign()`); const KeyId = this.keyId(); (0, engine_1._inf)(NS, `sign(${KeyId}, ${message.substring(0, 10)}...)..`); const params = { KeyId, Message: Buffer.from(message), SigningAlgorithm: (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.algorithm) !== null && _b !== void 0 ? _b : 'RSASSA_PKCS1_V1_5_SHA_256', MessageType: 'RAW', }; const result = yield this.instance().send(new client_kms_1.SignCommand(params)); const signature = result.Signature ? Buffer.from(result.Signature).toString('base64') : ''; if (forJwtSignature) return (0, exports.fromBase64)(signature); return signature; }); /** * verify signature in asymetric way * - it tooks around `30ms` * * @param {*} message any string * @param {*} signature signature of Buffer or string(in base64) */ this.verify = (message, signature) => __awaiter(this, void 0, void 0, function* () { var _c, _d; if (!message || typeof message !== 'string') throw new Error(`@message[${message}] is invalid - kms.verify()`); if (!signature) throw new Error(`@signature (string|Buffer) is required - kms.verify()`); const KeyId = this.keyId(); (0, engine_1._inf)(NS, `verify(${KeyId}, ${message.substring(0, 10)}...)..`); const params = { KeyId, Message: Buffer.from(message), SigningAlgorithm: (_d = (_c = this._options) === null || _c === void 0 ? void 0 : _c.algorithm) !== null && _d !== void 0 ? _d : 'RSASSA_PKCS1_V1_5_SHA_256', MessageType: 'RAW', Signature: typeof signature === 'string' ? Buffer.from(signature, 'base64') : signature, }; const result = yield this.instance() .send(new client_kms_2.VerifyCommand(params)) .catch(e => { (0, engine_1._err)(NS, `! err=`, e); return null; }); return result === null || result === void 0 ? void 0 : result.SignatureValid; }); /** * retrieve public-key for asymetric verification. * - used to verify signature with JWT library w/o making request to AWS KMS. * - in general, cache this `public-key` to verify locally. * * @param encoding (optional) encoding type */ this.getPublicKey = (encoding = 'base64') => __awaiter(this, void 0, void 0, function* () { const KeyId = this.keyId(); (0, engine_1._inf)(NS, `getPublicKey(${KeyId})..`); const params = { KeyId, }; const result = yield this.instance().send(new client_kms_2.GetPublicKeyCommand(params)); return (result === null || result === void 0 ? void 0 : result.PublicKey) ? Buffer.from(result.PublicKey).toString(encoding) : ''; }); keyId = keyId !== null && keyId !== void 0 ? keyId : `${engine_1.$engine.environ(AWSKMSService.ENV_KMS_KEY_ID, AWSKMSService.DEF_KMS_TARGET)}`; this._keyId = keyId; this._options = options; } /** * get KMS instance in stock */ instance() { if (!this._instance) this._instance = instance(); return this._instance; } /** * it should be 'hello lemon' * * # Example * ```sh * # encrypt text * $ aws kms encrypt --profile <profile> --key-id <kms-key-id> --plaintext "hello lemon" --query CiphertextBlob --output text * ``` */ sample() { return __awaiter(this, void 0, void 0, function* () { (0, engine_1._inf)(NS, 'sample()..'); //* check key-id. const message = `hello lemon!`; const KMS_KEY_ID = engine_1.$engine.environ(AWSKMSService.ENV_KMS_KEY_ID, `alias/${ALIAS}`); const keyId = this.keyId(); (0, engine_1._log)(NS, '> key-id =', keyId); const encrypted = yield this.encrypt(message); (0, engine_1._log)(NS, '> encrypted =', encrypted); const decrypted = yield this.decrypt(encrypted); (0, engine_1._log)(NS, '> decrypted =', decrypted, '->', message == decrypted ? 'ok' : 'error'); return { KMS_KEY_ID, keyId, message, encrypted, decrypted }; }); } } exports.AWSKMSService = AWSKMSService; /** * environ name of KMS KEY */ AWSKMSService.ENV_KMS_KEY_ID = 'KMS_KEY_ID'; AWSKMSService.DEF_KMS_TARGET = `alias/${ALIAS}`; //# sourceMappingURL=aws-kms-service.js.map