auth-server-helper/lib/KeyStore.ts
Timo Hocker fd4f891b3e
Some checks failed
continuous-integration/drone/push Build is failing
asymmetric keys import/export
2021-01-06 22:43:03 +01:00

124 lines
3.0 KiB
TypeScript

/*
* 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 <timo@scode.ovh>, 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<string, KeyPair>;
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<string> {
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 };