/* * Copyright (C) Sapphirecode - All Rights Reserved * This file is part of Auth-Server-Helper which is released under MIT. * See file 'LICENSE' for full license details. * Created by Timo Hocker , December 2020 */ import { generate_keypair } from '@sapphirecode/crypto-helper'; const renew_interval = 60; interface Key { key: string; valid_until: number; } interface KeyPair { private_key?: Key; public_key: Key; } type KeyStoreData = Record; function get_index (iat: number): string { return Math.floor (iat / renew_interval) .toFixed (0); } async function create_key (valid_for: number) { const time = (new Date) .getTime (); const pair = await generate_keypair (); return { private_key: { key: pair.private_key, valid_until: time + (renew_interval * 1000) }, public_key: { key: pair.public_key, valid_until: time + (valid_for * 1000) } }; } function garbage_collect (set: KeyStoreData): void { const time = (new Date) .getTime (); for (const index of Object.keys (set)) { const entry = set[index]; if (typeof entry.private_key !== 'undefined' && entry.private_key.valid_until < time ) delete entry.private_key; if (entry.public_key.valid_until < time) delete set[index]; } } class KeyStore { private _keys: KeyStoreData = {}; private _interval: NodeJS.Timeout; public constructor () { this._interval = setInterval (() => { garbage_collect (this._keys); }, renew_interval); } public async get_sign_key (iat: number, valid_for: number): Promise { if (valid_for <= 0) throw new Error ('cannot create infinitely valid key'); if ((iat + 1) * 1000 < (new Date) .getTime ()) throw new Error ('cannot access already expired keys'); const index = get_index (iat); const valid_until = (new Date) .getTime () + (valid_for * 1000); if (typeof this._keys[index] !== 'undefined') { const key = this._keys[index]; if (key.public_key.valid_until < valid_until) key.public_key.valid_until = valid_until; return key.private_key?.key as string; } this._keys[index] = await create_key (valid_until); return this._keys[index].private_key?.key as string; } public get_key (iat: number): string { const index = get_index (iat); if (typeof this._keys[index] === 'undefined') throw new Error ('key could not be found'); const key = this._keys[index]; return key.public_key.key; } public export_verification_data (): KeyStoreData { garbage_collect (this._keys); const out: KeyStoreData = {}; for (const index of Object.keys (this._keys)) out[index] = { public_key: this._keys[index].public_key }; return out; } public import_verification_data (data: KeyStoreData): void { const import_set = { ...data }; garbage_collect (import_set); // TODO: import } } const ks: KeyStore = (new KeyStore); export default ks; export { KeyStore, KeyStoreData, Key, KeyPair };