forro

Forro is a end-to-end encrypted contract form based on PGP.
git clone git://git.defalsify.org/forro.git
Log | Files | Refs | LICENSE

commit e9c140a2d7353ec74b8e1f7a82adcd2b53508cde
parent b52b0a5c7c49d9e658f72f5c6b94c8c3ed3fc68a
Author: lash <dev@holbrook.no>
Date:   Sun, 18 Sep 2022 12:28:18 +0000

Add wala submit, state feedback

Diffstat:
Mindex.html | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mkey.js | 23++++++++++++++---------
Asettings.json.sample | 4++++
3 files changed, 146 insertions(+), 37 deletions(-)

diff --git a/index.html b/index.html @@ -3,38 +3,92 @@ <script> let g_remote_key = undefined; let g_local_key = undefined; +let g_remote_key_id = '(none)'; +let g_local_key_id = '(none)'; +let g_data_endpoint = window.location.href; +let g_counter = undefined; </script> <script src="node_modules/openpgp/dist/openpgp.min.js"></script> <script defer src="/node_modules/alpinejs/dist/cdn.min.js"></script> <script src="/key.js"></script> <script> + + // Thanks to: // https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex function buf2hex(buffer) { // buffer is an ArrayBuffer - console.log('buf ' + buffer); return [...new Uint8Array(buffer)] .map(x => x.toString(16).padStart(2, '0')) .join(''); } - async function setUp(o) { - let r = await fetch('louis.asc'); - let remote_key_src = await r.text(); - let remote_key = await openpgp.readKey({ - armoredKey: remote_key_src, + + async function loadSettings() { + let rs = await fetch(window.location.href + '/settings.json', { + method: 'GET', }); - g_remote_key = remote_key; + if (!rs.ok) { + stateChange('could not load settings'); + throw('could not load settings'); + } + return await rs.json(); + } + + async function setUp(o) { let k = undefined; try { + stateChange('attempt load local signing key'); k = await getKey('deadbeef'); } catch { - k = await generatePGPKey(); + stateChange('generate local signing key'); + k = await generatePGPKey('deadbeef'); }; + g_local_key = k; - return [k.getKeyID().toHex(), g_remote_key.getKeyID().toHex()]; + g_local_key_id = k.getKeyID().toHex(); + + stateChange('load settings'); + let settings = await loadSettings(); + if (settings.data_endpoint !== undefined) { + stateChange('update data endpoint'); + g_data_endpoint = settings.data_endpoint; + } + + stateChange('load counter'); + let c = localStorage.getItem('msg_count'); + if (c == null) { + g_counter = 0; + } else { + g_counter = parseInt(c); + } + + stateChange('load remote encryption key'); + let r = await fetch(settings.remote_pubkey_url); + let remote_key_src = await r.text(); + let remote_key = await openpgp.readKey({ + armoredKey: remote_key_src, + }); + g_remote_key = remote_key; + g_remote_key_id = g_remote_key.getKeyID().toHex(); + + stateChange('ready to send'); } - function foo() { - alert(g_local_key.getKeyID().toHex()); + + async function stateChange(s) { + const ev = new CustomEvent('messagestatechange', { + bubbles: true, + cancelable: false, + composed: true, + detail: { + s: s, + c: g_counter, + kr: g_remote_key_id, + kl: g_local_key_id, + }, + }); + window.dispatchEvent(ev); } + async function dispatch(s) { + stateChange('encrypt message ' + g_counter); let sb = new TextEncoder("utf-8").encode(s); let digest = await crypto.subtle.digest('SHA-256', sb); let msg = await openpgp.createMessage({ @@ -45,41 +99,87 @@ let g_local_key = undefined; encryptionKeys: enc, format: 'binary', message: msg, - config: { rejectCurves: new Set() }, + // config: { rejectCurves: new Set() }, + }); + let em = await openpgp.createMessage({ + binary: m, //_str, }); + //let emb = new TextEncoder("utf-8").encode(m); + let digest_enc = await crypto.subtle.digest('SHA-256', m); + stateChange('sign message ' + g_counter); let sig = await openpgp.sign({ signingKeys: g_local_key, - message: msg, + message: em, format: 'binary', detached: true, - config: { rejectCurves: new Set() }, + // config: { rejectCurves: new Set() }, }); + + stateChange('encode request for message ' + g_counter); let pubkey = g_local_key.toPublic().write(); let pubkey_str = String.fromCharCode.apply(null, pubkey); - let sig_str = String.fromCharCode.apply(null, sig); - let rcpt = buf2hex(digest); + let rcpt_unencrypted = buf2hex(digest); + let rcpt = buf2hex(digest_enc); + console.debug('digest for unencrypted message:', rcpt_unencrypted); + console.debug('digest for encrypted message:', rcpt); + + sig_b = btoa(sig_str); + pub_b = btoa(pubkey_str); - console.log({sig: btoa(sig_str), pubkey: btoa(pubkey_str), rcpt: rcpt, m: s}); + stateChange('send message ' + g_counter); + let req = await fetch(g_data_endpoint + '/msg' + g_counter, { + method: 'PUT', + body: m, + headers: { + 'Content-Type': 'application/octet-stream', + 'Authorization': 'PUBSIG pgp:' + pub_b + ':' + sig_b, + } + }); + + stateChange('update local state, next message is: ' + g_counter); + g_counter += 1; + localStorage.setItem('msg_count', g_counter); + + stateChange('ready to send next message'); + return rcpt; }; + window.addEventListener('messagestatechange', (v) => { + console.debug('message state change:', v.detail.s); + }); </script> </head> <body> - <div x-init="[key, rkey] = await setUp(this);" x-data="{key: '(none)', rkey: '(none)', rcpt: '', content: ''}"> - <h1 >Your identity: - <span x-text="key"></span> - <br/>Their identity: - <span x-text="rkey"></span> - <br/>Your receipt: - <span x-text="rcpt"></span> - </h1> - <textarea x-model="content"> + <div x-init="await setUp(this);" x-data="{ + key: '(none)', + rkey: '(none)', + message_status: '(none)', + message_count: g_counter, + rcpt: ' ', + content: '', + + }"> + <dl> + <dt>Application version:</dt> + <dd>0.0.1</dt> + <dt>Status:</dt> + <dd x-text="message_status" x-on:messagestatechange.window="message_status = $event.detail.s; message_count = $event.detail.c; key = $event.detail.kl, rkey = $event.detail.kr;"></dd> + <dt>Your identity:</dt> + <dd x-text="key"></dd> + <dt>Their identity:</dt> + <dd x-text="rkey"></dd> + <dt>Message number:</dt> + <dd x-text="message_count"></dd> + <dt>Your receipt:<dt> + <dd x-text="rcpt"></dd> + </dl> + <textarea cols=72 rows=10 x-model="content" > </textarea> - <button @click="rcpt = await dispatch(content)">sign, encrypt and send</button> + <br/> + <button @click="r = await dispatch(content); rcpt = 'message ' + (message_count-1) + ': ' + r">sign, encrypt and send</button> </div> </body> - </html> diff --git a/key.js b/key.js @@ -1,20 +1,24 @@ -async function generatePGPKey() { +async function generatePGPKey(pwd) { return new Promise(async (whohoo, doh) => { - console.log(openpgp.generateKey); let v = await openpgp.generateKey({ - type: 'ecc', - curve: 'secp256k1', + //type: 'ecc', + //curve: 'secp256k1', + type: 'rsa', userIDs: [{name: "Ola Nordmann", email: "ola@nordmann.no" }], - passphrase: 'deadbeef', + passphrase: pwd, format: 'armored', config: { rejectCurves: new Set() }, }); - console.log('pk ' + v.privateKey ); - console.log('pubk ' + v.publicKey ); + //console.debug('pk ' + v.privateKey ); + //console.debug('pubk ' + v.publicKey ); localStorage.setItem('pgp-key', v.privateKey); - let k = openpgp.readKey({ + let pk = await openpgp.readKey({ armoredKey: v.privateKey, }); + let k = await openpgp.decryptKey({ + privateKey: pk, + passphrase: pwd, + }); whohoo(k); }); } @@ -22,7 +26,6 @@ async function generatePGPKey() { async function getKey(pwd) { return new Promise(async (whohoo, doh) => { let pk_armor = localStorage.getItem('pgp-key'); - console.log('pk ' + pk_armor); if (pk_armor === null) { doh('no key'); return; @@ -34,6 +37,8 @@ async function getKey(pwd) { privateKey: pk, passphrase: pwd, }); + //console.debug('pk ' + k.armor()); + //console.debug('pubk ' + k.toPublic().armor()); whohoo(k); }); } diff --git a/settings.json.sample b/settings.json.sample @@ -0,0 +1,4 @@ +{ + "remote_pubkey_url": "publickey.asc", + "data_endpoint": "http://localhost:8001" +}