Inital commit

This commit is contained in:
Chris Josten 2020-07-28 12:01:30 +02:00
commit d76392f99b
7 changed files with 453 additions and 0 deletions

BIN
favicoon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

186
index.html Normal file
View file

@ -0,0 +1,186 @@
<!DOCTYPE html>
<html lang="nl">
<head>
<title>Betaalverzoekjes via elke chatdienst (en zelfs briefpost!) |
Netsoj.nl</title>
<meta name="viewport" content="width=device-width" />
<meta name="og:title" content="Chris Betaalverzoek" />
<meta name="og:description" content="Verstandig betalen zonder tussenpersoon." />
<meta name="og:image" content="/favicoon.png" />
<link rel="stylesheet" href="stijl/standaard.css" />
<link rel="shortcut icon" href="/favicoon.png" />
<script src="programmaatjes/veelvul.urlsearchparams.js"></script>
<script src="programmaatjes/openpgp.js"></script>
<script src="programmaatjes/ondertekening.js"></script>
</head>
<body>
<main>
<header>
<h1>Mede mogelijk gemaakt door Chris betaalverzoeken</h1>
<p>Voor betalen hoef je niet ver te zoeken!</p>
</header>
<noscript>
<section class="doos onjuist">
<header>
<h2>Javascript geblokkeerd</h2>
</header>
<p>
Deze webstek werkt niet zonder JavaScript. Ik wil de
<abbr title="Centrale Verwerkingseenheid">CVE</abbr>-cyclussen
op mijn centrale rekentuig liever gebruiken om een
virtuele blokjeswereld te simuleren.
</p>
</section>
</noscript>
<section class="doos onjuist" id="betaal-fout" style="display: none;">
<header>
<h2>Betaalverzoek onjuist</h2>
</header>
<p>
Het doorgestuurde betaalverzoek is ongeldig.
</p>
<h3>Foutdetails</h3>
<p><span data-veld="foutmelding"></span></p>
<pre id="fout-engels" class="onjuist" data-veld="fout-engels" style="display: none;"> </pre>
<dl id="fout-handtekening" style="display: none;">
<dt>Vingerafdruk</dt>
<dd>
<span data-veld="handtekening-vingerafdruk"></span>
</dd>
<dt>Datum ondertekend</dt>
<dd><span data-veld="onderteken-datum"></span></dd>
<dt>Handtekening</dt>
<dd><pre data-veld="handtekening" class="onjuist"></pre>
Ik hoop dat deze rode handtekening je een gevoel van
"oei, hier is iets flinks mis" geeft, ook al heb je geen
snars verstand van PGP. </dd>
</dl>
</section>
<section class="doos" id="betaal-info" style="display: none;">
<header>
<h2><span id="betaal-onderwerp" data-veld="betreft"></span></h2>
<p class="geen-marge">Aan <span id="betaal-ontvanger" data-veld="aan">
</span></p>
</header>
<p><span id="betaal-bedrag" data-veld="bedrag"></span></p>
<h3>Betaalmogelijkheden</h3>
<ol>
<li>
<h3>Contant</h3>
<p>Je geeft het bedrag in contant geld aan
<span data-veld="aan"></span>. Spreekt voor zichzelf,
toch?</p>
</li>
<li>
<h3>Overmaken</h3>
<p>Je maakt het bedrag over naar mijn bankrekening,
met het IBAN <span id="betaal-iban" data-veld="iban"></span> en met als omschrijving <span class="veld" data-veld="betreft"></span>.
</p>
</li>
</ol>
<details>
<summary>Veiligheidsinformatie</summary>
<dl>
<dt>Vingerafdruk</dt>
<dd><span data-veld="handtekening-vingerafdruk"></span></dd>
<dt>Datum ondertekend</dt>
<dd><span data-veld="onderteken-datum"></span></dd>
<dt>Handtekening</dt>
<dd><pre data-veld="handtekening" class="juist"></pre>
Ik hoop dat deze groene kleur je een gevoel van veiligheid
geeft, ook al heb je geen snars verstand van PGP.
</dd>
</dl>
</details>
</section>
<section class="betaal-vgv doos">
<header>
<h2>Heb je vragen?</h2>
</header>
<details>
<summary>Is Chris betaalverzoeken veilig?</summary>
<p>Chris Betaalverzoeken is gemaakt door een Chris die jij
wellicht wel of niet kent en voldoet aan strenge
veiligheidseisen. De link begint met https. Dit betekent
dat jouw gegevens versleuteld zijn. Een Chris-betaalverzoekje
ziet er altijd zo uit: https://betalen.netsoj.nl/&hellip;
Als dit niet zo is, betaal dan niet op en
<a href="mailto:betaalverzoek@netsoj.nl?subject=Melding%20misbruik">
meld het ons </a></p>
</details>
<details>
<summary>Is Chris betaalverzoeken voor iedereen?</summary>
<p>Ja, maar alleen voor degenen die het Nederlands
beheersen. Dus eigenlijk nee, maar dat zou geen goede
reclame zijn.</p>
<p>Het aanmaken van betaalverzoeken kan om technische
redenen alleen door de Chris die achter deze webstek zit.</p>
</details>
<details>
<summary>Is het gratis?</summary>
<p>Ja, zelfs je data wordt niet verkocht!</p>
</details>
<details>
<summary>Waarvoor is dit webstekkie gemaakt?</summary>
<p>Om het mogelijk te maken om net zulke 'hippe'
betaalverzoeken te sturen zonder dat nutteloze tussenpartijen
meesnoepen aan betaalgegevens. </p>
<p>Je zult misschien al opgemerkt hebben dat dit webstekkie
eigenlijk een parodie is op een bepaalde populaire
betaalverzoekjesdienst. Het was de bedoeling om het een gele
achtergrondkleur te geven, omdat het de tegenovergestelde
kleur van paars is. Dat bleek echter een nachtmerrie voor
je ogen zijn, vandaar dat ik de kleur oranje heb gekozen. </p>
</details>
<details>
<summary>Huh? Waarom gebruikt deze webstek webassembleertaal?</summary>
<p>
De parameters van deze webpagina worden ondertekend
met PGP. De bibliotheek die deze ondertekening
nakijkt, gebruikt webassembleertaal om deze
berekening sneller te kunnen uitvoeren.
</p>
</details>
</section>
<section class="doos">
<header>
<h2>Dingetjes die moeten van de wet</h2>
</header>
<details>
<summary>Omgang persoonlijke gegevens</summary>
<h3>Welke gegevens verzamelen wij?</h3>
<p>Uw IP-adres, pad en tijd van toegang wordt vastgelegd wordt
vastgelegd in een logboek als u dit subdomein bezoekt.
</p>
<h3>Hoelang blijven deze gegevens bewaard?</h3>
<p>4 weken, daarna worden ze uit het logboek gegooid.</p>
<h3>Waarvoor worden deze gegevens gebruikt?</h3>
<p>Om mogelijk misbruik van dit subdomein tegen te gaan via
geautomatiseerde system en om mogelijke configuratefouten
op te sporen. Minder deftig: fail2ban wordt gebruikt.</p>
<h3>Ik wil mijn persoonlijke gegevens opvragen</h3>
<p>Dat is op dit moment helaas niet mogelijk zonder meer
problemen te veroorzaken rondom de toegang tot jouw persoonlijke
gegevens.</p>
<h3>Ik wil mijn persoonlijke gegevens verwijderen</h3>
<p>Wacht 4 weken. En bezoek dit webstekkie niet opnieuw.</p>
</details>
<details>
<summary>Programmatuurlicenties</summary>
<p>Wij maken gebruik van:</p>
<ul>
<li><a href="https://openpgpjs.org">openpgpjs</a>
onder de <a href="https://github.com/openpgpjs/openpgpjs/blob/master/LICENSE">LGPLv3.0</a></li>
</ul>
</details>
</section>
</main>
</body>
</html>

View file

@ -0,0 +1,156 @@
console.info("ondertekening.js geladen");
let publiekeSleutel = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
"mQENBF3FuR0BCACxaQrLc53zFD1ch9lY9tsCcQpmBPRT1x9yPphMcJl+0K3Lu8AB\n" +
"F2Nktvol3oV8LT3JCOT9WXwV2srN3CbTAI2h9P6Bu5tHztCfhotNtgFSeX31r2fc\n" +
"raDaDgWS8iKOHk2SCRaOk4UqkZkjSnKGs56MWCqDjtKYOl3pCvTPzCsEiduJGPtj\n" +
"83xI9WyF3mkGvh9EVcbfGDywfDpZ6zmRfWiAniteZuXLgDtQ4aDC9rfjg6vVTNZz\n" +
"Q6ntVzC5bVLHcIpk4fSEXL0M/0yFwycGkbf2mZbbAMJCnXPKo8EEoZwh4d4SusSy\n" +
"coJzTUV9Azo57eO25iUdvWeDXtukqvMttapJABEBAAG0IENocmlzIEpvc3RlbiA8\n" +
"YmV0YWxlbkBuZXRzb2oubmw+iQFUBBMBCAA+FiEE9y0e0M5kgwkiGmLb2y0MgIMO\n" +
"E30FAl3FuR0CGwMFCQPDShMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ2y0M\n" +
"gIMOE32Cnwf/aFTKv5vxQTDkvTyKPLh/i/AfA4PEDyRF+oX0INTnZ3BwfUZxFoCs\n" +
"XBcxolqtjZdOUPmGEIj0GbDCQs/NyHuqESdiUYdAlodACepqALnIrT2DdlLL5OMx\n" +
"NgyakbrsLSWAKVayDbljuEQ0pU+NfkYFgZFDDRM9dVMRw3CEfnEnjiGmU+S6dx6b\n" +
"011UCtICWS50d0hXxyjCOM+0BEpYD6mH7ZEx+8CIXkAAZgVwGdmRzvbe2KAtgxGo\n" +
"vF+3DzsPfP/GguaUwpFblz6IUnjVKvdc7FYv+1TlJRvCJgKCXWHHtq6eOthIoQgv\n" +
"eJGglnQla0Sys+druwpYPAF/08aWXSBW1bkBDQRdxbkdAQgAtySuGbe3+CDetv5A\n" +
"vodDVSZu9MzreT1zVUce2EDKWYzukEOuqJLU/49sB11Nh59Q+mV7TRle8+0KXsLq\n" +
"3DxZejuN7JBGWKF/6HU8huIunDXkbixK6bPOcXTBKxUJJkW6gRPtwtP03uJqDblq\n" +
"U4huA1NNKTJXdz3kiHRyNIZYVFMDaWiX+btRFr3Vcplo9Gyn+uklzArZZjrdIzwv\n" +
"xBIRDu+cy8uvREy1KRx+AFWUAidKUldrUZ2kMpaOWG663/4Byvv7HcOTKhdiqVLU\n" +
"N9gkeZnr+JQGwme5/+2aZJVuYgjIf89lsYF2y+C4ByNhkaaYkilv7RGhBw+srAdj\n" +
"WDHWvwARAQABiQE8BBgBCAAmFiEE9y0e0M5kgwkiGmLb2y0MgIMOE30FAl3FuR0C\n" +
"GwwFCQPDShMACgkQ2y0MgIMOE32+dAf/doJBIiNn1WqS7jUmek4Yz71AzzsLVo+z\n" +
"5/Q4hDoSaQAXxYZcNt2SUqtmwdZ4vctEWp5xXn9g5ysd82LIT2QR8PRCWdWbiK9Z\n" +
"kGYDjabMsOt/+aACfvxbqrL48oXMdTgAET2t6LSKJHoyNlXPvgwtHxKk5O4tANJR\n" +
"8DmL1H1UlI9mSuipHYPPRTMxMdknnVaiKwVIXdkTm514XxLWLJB3Hkjrb2AHhwxb\n" +
"Lih5kdsxL/DoZ1Ss73SZWVATbpUAtkXbOAcePjGdpUS91krHGFacYSH4plnHGhcT\n" +
"2HfZWiN/fi2hfE9gXhRGYXiS8pRd0eMemt+gUIUkAyRy33BHvzkW2Q==\n" +
" =hGsM\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
let TEKST_FOUT_ENGELS = 0;
let TEKST_FOUT_MISSENDE_PARAMETERS = 1;
let TEKST_FOUT_ONJUISTE_HANDTEKENING = 2;
let TEKST_FOUT_CORRUPTE_HANDTEKENING = 3;
let TEKST_FOUT_VERLOPEN = 4;
let teksten = [];
teksten[TEKST_FOUT_ENGELS] = "Helaas hebben we alleen een Engelse foutmelding voor deze fout. Als u dit aankunt, lees dan de onderstaande tekst:";
teksten[TEKST_FOUT_MISSENDE_PARAMETERS] = "Ongeldige URL: missende parameters.";
teksten[TEKST_FOUT_ONJUISTE_HANDTEKENING] = "Ongeldige URL: ongeldige handtekening.";
teksten[TEKST_FOUT_CORRUPTE_HANDTEKENING] = "Ongeldige URL: handtekening corrupt.";
teksten[TEKST_FOUT_VERLOPEN] = "Betaalverzoek is verlopen.";
let vertaaldeFouten = {};
vertaaldeFouten["Error during parsing. This message / key probably does not conform to a valid OpenPGP format."] = TEKST_FOUT_CORRUPTE_HANDTEKENING;
function toonFout(tekstId, opties) {
vulVeldIn("foutmelding", teksten[tekstId]);
document.getElementById("betaal-fout").style.display = "block";
if (opties) {
if (opties["toonHandtekening"]) {
document.getElementById("fout-handtekening").style.display = "block";
}
if (opties["engelseFout"]) {
vulVeldIn("fout-engels", opties["engelseFout"]);
document.getElementById("fout-engels").style.display = "block";
}
}
}
function vulVeldIn(veld, data) {
let velden = document.querySelectorAll("*[data-veld='" + veld + "']");
for (let i = 0; i < velden.length; i++) {
velden[i].innerText = data;
}
}
function goeieOuweOntkink(object) {
return;
//alert(JSON.stringify(object));
}
document.addEventListener("DOMContentLoaded", function() {
try {
//goeieOuweOntkink("Verkrijgen parameters")
// Verkrijg parameters
let aanvraagParameters = new URLSearchParams(window.location.search);
let aan = aanvraagParameters.get("aan");
let betreft = aanvraagParameters.get("betreft");
let bedrag = aanvraagParameters.get("bedrag");
let iban = aanvraagParameters.get("iban");
let datum = aanvraagParameters.get("datum");
let handtekening = aanvraagParameters.get("handtekening");
goeieOuweOntkink(aanvraagParameters);
//Controleer of ze allemaal zijn ingevuld.
if (aan === null || betreft === null || bedrag === null || iban === null
|| datum === null || handtekening === null) {
toonFout(TEKST_FOUT_MISSENDE_PARAMETERS);
return;
}
// Maak een tekenreeks van alle parameters
let nakijkParameters = new URLSearchParams();
nakijkParameters.append("aan", aan);
nakijkParameters.append("betreft", betreft);
nakijkParameters.append("bedrag", bedrag);
nakijkParameters.append("iban", iban);
nakijkParameters.append("datum", datum);
handtekening = "-----BEGIN PGP SIGNATURE-----\n\n" + handtekening + "\n-----END PGP SIGNATURE-----";
belofte1 = openpgp.cleartext.fromText(nakijkParameters.toString())
belofte2 = openpgp.signature.readArmored(handtekening)
belofte3 = openpgp.key.readArmored(publiekeSleutel)
Promise.all([belofte1, belofte2, belofte3])
.then(function(waardes) {
console.log(waardes);
opties = {
message: waardes[0],
signature: waardes[1],
publicKeys: waardes[2].keys
}
return openpgp.verify(opties).then(function(geverifieerd) {
let handtekening2 = geverifieerd.signatures[0];
vulVeldIn("handtekening-vingerafdruk", handtekening2.keyid.toHex());
vulVeldIn("onderteken-datum", handtekening2.signature.packets[0].created.toLocaleString());
vulVeldIn("handtekening", handtekening);
if (geverifieerd.signatures[0].valid) {
console.info("Handtekekning juist");
return Promise.resolve(geverifieerd.signatures[0]);
} else {
console.info("Handtekening onjuist");
toonFout(TEKST_FOUT_ONJUISTE_HANDTEKENING, {"toonHandtekening": true});
return Promise.reject();
}
});
}, function(fout) {
console.error(fout.message);
fout2 = vertaaldeFouten[fout.message];
if (fout2) {
toonFout(fout2);
} else {
toonFout(TEKST_FOUT_ENGELS, {"engelseFout": fout.name + ": " + fout.message});
}
return Promise.reject();
}).then(function(handtekening2) {
console.log(handtekening2);
vulVeldIn("aan", aan);
vulVeldIn("betreft", betreft);
vulVeldIn("bedrag", bedrag);
vulVeldIn("iban", iban);
document.getElementById("betaal-info").style.display = "block";
});
let ondertekendeTekst = nakijkParameters.toString();
console.log(ondertekendeTekst);
} catch(e) {
console.error(e);
toonFout(TEKST_FOUT_ENGELS, {"engelseFout": e.name + ": " + e.message});
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
/*! (c) Andrea Giammarchi - ISC */
var self=this||{};try{!function(t,e){if(new t("q=%2B").get("q")!==e||new t({q:e}).get("q")!==e||new t([["q",e]]).get("q")!==e||"q=%0A"!==new t("q=\n").toString()||"q=+%26"!==new t({q:" &"}).toString()||"q=%25zx"!==new t({q:"%zx"}).toString())throw t;self.URLSearchParams=t}(URLSearchParams,"+")}catch(t){!function(t,a,o){"use strict";var u=t.create,h=t.defineProperty,e=/[!'\(\)~]|%20|%00/g,n=/%(?![0-9a-fA-F]{2})/g,r=/\+/g,i={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"},s={append:function(t,e){p(this._ungap,t,e)},delete:function(t){delete this._ungap[t]},get:function(t){return this.has(t)?this._ungap[t][0]:null},getAll:function(t){return this.has(t)?this._ungap[t].slice(0):[]},has:function(t){return t in this._ungap},set:function(t,e){this._ungap[t]=[a(e)]},forEach:function(e,n){var r=this;for(var i in r._ungap)r._ungap[i].forEach(t,i);function t(t){e.call(n,t,a(i),r)}},toJSON:function(){return{}},toString:function(){var t=[];for(var e in this._ungap)for(var n=v(e),r=0,i=this._ungap[e];r<i.length;r++)t.push(n+"="+v(i[r]));return t.join("&")}};for(var c in s)h(f.prototype,c,{configurable:!0,writable:!0,value:s[c]});function f(t){var e=u(null);switch(h(this,"_ungap",{value:e}),!0){case!t:break;case"string"==typeof t:"?"===t.charAt(0)&&(t=t.slice(1));for(var n=t.split("&"),r=0,i=n.length;r<i;r++){var a=(s=n[r]).indexOf("=");-1<a?p(e,g(s.slice(0,a)),g(s.slice(a+1))):s.length&&p(e,g(s),"")}break;case o(t):for(r=0,i=t.length;r<i;r++){var s;p(e,(s=t[r])[0],s[1])}break;case"forEach"in t:t.forEach(l,e);break;default:for(var c in t)p(e,c,t[c])}}function l(t,e){p(this,e,t)}function p(t,e,n){var r=o(n)?n.join(","):n;e in t?t[e].push(r):t[e]=[r]}function g(t){return decodeURIComponent(t.replace(n,"%25").replace(r," "))}function v(t){return encodeURIComponent(t).replace(e,d)}function d(t){return i[t]}self.URLSearchParams=f}(Object,String,Array.isArray)}!function(d){var r=!1;try{r=!!Symbol.iterator}catch(t){}function t(t,e){var n=[];return t.forEach(e,n),r?n[Symbol.iterator]():{next:function(){var t=n.shift();return{done:void 0===t,value:t}}}}"forEach"in d||(d.forEach=function(n,r){var i=this,t=Object.create(null);this.toString().replace(/=[\s\S]*?(?:&|$)/g,"=").split("=").forEach(function(e){!e.length||e in t||(t[e]=i.getAll(e)).forEach(function(t){n.call(r,t,e,i)})})}),"keys"in d||(d.keys=function(){return t(this,function(t,e){this.push(e)})}),"values"in d||(d.values=function(){return t(this,function(t,e){this.push(t)})}),"entries"in d||(d.entries=function(){return t(this,function(t,e){this.push([e,t])})}),!r||Symbol.iterator in d||(d[Symbol.iterator]=d.entries),"sort"in d||(d.sort=function(){for(var t,e,n,r=this.entries(),i=r.next(),a=i.done,s=[],c=Object.create(null);!a;)e=(n=i.value)[0],s.push(e),e in c||(c[e]=[]),c[e].push(n[1]),a=(i=r.next()).done;for(s.sort(),t=0;t<s.length;t++)this.delete(s[t]);for(t=0;t<s.length;t++)e=s[t],this.append(e,c[e].shift())}),function(f){function l(t){var e=t.append;t.append=d.append,URLSearchParams.call(t,t._usp.search.slice(1)),t.append=e}function p(t,e){if(!(t instanceof e))throw new TypeError("'searchParams' accessed on an object that does not implement interface "+e.name)}function t(e){var n,r,i,t=e.prototype,a=v(t,"searchParams"),s=v(t,"href"),c=v(t,"search");function o(t,e){d.append.call(this,t,e),t=this.toString(),i.set.call(this._usp,t?"?"+t:"")}function u(t){d.delete.call(this,t),t=this.toString(),i.set.call(this._usp,t?"?"+t:"")}function h(t,e){d.set.call(this,t,e),t=this.toString(),i.set.call(this._usp,t?"?"+t:"")}!a&&c&&c.set&&(i=c,r=function(t,e){return t.append=o,t.delete=u,t.set=h,g(t,"_usp",{configurable:!0,writable:!0,value:e})},n=function(t,e){return g(t,"_searchParams",{configurable:!0,writable:!0,value:r(e,t)}),e},f.defineProperties(t,{href:{get:function(){return s.get.call(this)},set:function(t){var e=this._searchParams;s.set.call(this,t),e&&l(e)}},search:{get:function(){return c.get.call(this)},set:function(t){var e=this._searchParams;c.set.call(this,t),e&&l(e)}},searchParams:{get:function(){return p(this,e),this._searchParams||n(this,new URLSearchParams(this.search.slice(1)))},set:function(t){p(this,e),n(this,t)}}}))}var g=f.defineProperty,v=f.getOwnPropertyDescriptor;try{t(HTMLAnchorElement),/^function|object$/.test(typeof URL)&&URL.prototype&&t(URL)}catch(t){}}(Object)}(self.URLSearchParams.prototype,Object);

105
stijl/standaard.css Normal file
View file

@ -0,0 +1,105 @@
:root {
background-color: #ffa500;
color: white;
font-family: sans-serif;
}
h1, h2, h3, h4, h5, h6 {
/*margin: 0;*/
}
h2 {
font-size: 1.2em;
}
dt {
font-weight: bold;
}
dt:after {
content: ":";
}
a {
color: white;
text-decoration: underline;
font-weight: bold;
}
header {
margin-bottom: 0.5em;
}
header > * {
margin: 0;
}
body {
margin: 1em auto;
max-width: 600px;
}
main {
max-width: 600px;
}
main > header {
/*max-width: 20em;*/
text-align: center;
}
main > header h1 {
margin: 0;
font-size: 0.75em;
}
main > header p {
margin: 0;
font-size: 0.75em;
}
.doos {
background-color: #ff8500;
padding: 1em;
border-radius: 1em;
margin: 1em;
}
.doos.onjuist {
background-color: #ff0000;
}
pre {
border-left: 0.5em solid;
padding-left: 0.5em;
position: relative;
left: -1em;
width: calc(100% + 0.5em);
overflow-x: auto;
}
pre.juist {
border-color: #00ff15;
background-color: #dfa500;
}
pre.onjuist {
border-color: #aa0000;
background-color: #dd0000;
}
::marker, .veld, #betaal-ontvanger, #betaal-iban, #betaal-bedrag {
font-weight: bold;
}
::marker {
font-size: 1.2em;
}
#betaal-bedrag {
font-size: 2em;
}
summary {
font-weight: bold;
padding: 0.25em;
}