import * as pgp from 'openpgp'; import * as crypto from 'crypto'; interface Signable { digest(): string; } type KeyGetter = () => any; type Signature = { engine: string algo: string data: string digest: string }; interface Signer { prepare(Signable): boolean; onsign(Signature): void; onverify(bool): void; sign(digest: string): void; verify(digest: string, signature: Signature): void; fingerprint(): string; } interface Authoritative { } interface KeyStore { getPrivateKey: KeyGetter; getFingerprint: () => string; getTrustedKeys: () => Array; getTrustedActiveKeys: () => Array; getEncryptKeys: () => Array; } class PGPKeyStore implements KeyStore { fingerprint: string; pk: any; pubk = { active: [], trusted: [], encrypt: [], }; loads = 0x00; loadsTarget = 0x0f; onload: (k: KeyStore) => void; constructor(passphrase: string, pkArmor: string, pubkActiveArmor: string, pubkTrustedArmor: string, pubkEncryptArmor: string, onload = (ks: KeyStore) => { }) { this._readKey(pkArmor, undefined, 1, passphrase); this._readKey(pubkActiveArmor, 'active', 2); this._readKey(pubkTrustedArmor, 'trusted', 4); this._readKey(pubkEncryptArmor, 'encrypt', 8); this.onload = onload; } private _readKey(a: string, x: any, n: number, pass?: string): void { pgp.key.readArmored(a).then((k) => { if (pass !== undefined) { this.pk = k.keys[0]; this.pk.decrypt(pass).then(() => { this.fingerprint = this.pk.getFingerprint(); console.log('private key (sign)', this.fingerprint); this._registerLoad(n); }); } else { this.pubk[x] = k.keys; k.keys.forEach((pubk) => { console.log('public key (' + x + ')', pubk.getFingerprint()); }); this._registerLoad(n); } }); } private _registerLoad(b: number): void { this.loads |= b; if (this.loads == this.loadsTarget) { this.onload(this); } } public getTrustedKeys(): Array { return this.pubk.trusted; } public getTrustedActiveKeys(): Array { return this.pubk.active; } public getEncryptKeys(): Array { return this.pubk.encrypt; } public getPrivateKey(): any { return this.pk; } public getFingerprint(): string { return this.fingerprint; } } class PGPSigner implements Signer { engine = 'pgp'; algo = 'sha256'; dgst: string; signature: Signature; keyStore: KeyStore; onsign: (Signature) => void; onverify: (bool) => void; constructor(keyStore: KeyStore) { this.keyStore = keyStore; this.onsign = (string) => { }; this.onverify = (boolean) => { }; } public fingerprint(): string { return this.keyStore.getFingerprint(); } public prepare(material: Signable): boolean { this.dgst = material.digest(); return true; } public verify(digest: string, signature: Signature): void { pgp.signature.readArmored(signature.data).then((s) => { const opts = { message: pgp.cleartext.fromText(digest), publicKeys: this.keyStore.getTrustedKeys(), signature: s, }; pgp.verify(opts).then((v) => { let i = 0; for (i = 0; i < v.signatures.length; i++) { const s = v.signatures[i]; if (s.valid) { this.onverify(s); return; } } console.error('checked ' + i + ' signature(s) but none valid'); this.onverify(false); }); }).catch((e) => { console.error(e); this.onverify(false); }); } public sign(digest: string): void { const m = pgp.cleartext.fromText(digest); const pk = this.keyStore.getPrivateKey(); const opts = { message: m, privateKeys: [pk], detached: true, }; pgp.sign(opts).then((s) => { this.signature = { engine: this.engine, algo: this.algo, data: s.signature, // TODO: fix for browser later digest: digest, }; this.onsign(this.signature); }).catch((e) => { console.error(e); this.onsign(undefined); }); } } export { Signature, Authoritative, Signer, KeyGetter, Signable, KeyStore, PGPSigner, PGPKeyStore, };