Add content verification

Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
This commit is contained in:
Jacob Kiers 2021-08-21 11:21:55 +02:00
parent 4b117a4743
commit 2d477bb77e
3 changed files with 58 additions and 14 deletions

View File

@ -73,7 +73,7 @@ In short: just because I could. It was one of those ideas I was wondering idly a
### Has it any practical use? ### Has it any practical use?
It is not intended to have any. Since DNS records are fairly small, serving images or something would quickly start It is not intended to have any. Since DNS records are fairly small, serving images or something would quickly start
consuming 100s of requests per second. I wouldn't want to do that to Cloudflare 😉 consuming 100s of requests per second. I wouldn't want to do that to Cloudflare.
It would be an interesting experiment to see how feasible that is. It would be an interesting experiment to see how feasible that is.

View File

@ -0,0 +1,21 @@
class Verifier {
/***
* @param content Content
*/
async verify(content) {
if (!window.isSecureContext) return;
if (typeof TextEncoder === "undefined") return;
if (typeof crypto === "undefined") return;
const encoder = new TextEncoder();
const digestBuffer = await crypto.subtle.digest(content.index.hashAlgorithm, encoder.encode(content.content));
const hashArray = Array.from(new Uint8Array(digestBuffer)); // convert buffer to byte array
const digest = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
if (content.index.hash === digest) {
document.getElementById("verification").innerHTML =
"The hash of the content is verified to correspond with the hash" +
" in the metadata. You are now reading exactly what was intended.";
}
}
}

View File

@ -10,13 +10,20 @@
class Content { class Content {
content = ""; content = "";
type = ""; type = "";
metaData = {} metaData = {};
index;
constructor(content, type, metaData = {}) /***
* @param content string
* @param type string
* @param index Index
*/
constructor(content, type, index)
{ {
this.content = content; this.content = content;
this.type = type; this.type = type;
this.metaData = metaData; this.index = index;
this.metaData = index.metaData;
} }
} }
@ -24,13 +31,15 @@ class Index
{ {
mimeType = ""; mimeType = "";
chunks = 0; chunks = 0;
hashAlgorithm = "";
hash = ""; hash = "";
metaData = {}; metaData = {};
constructor(mimeType, chunks, hash, metaData = {}) constructor(mimeType, chunks, hash, hashAlgorithm, metaData = {})
{ {
this.mimeType = mimeType; this.mimeType = mimeType;
this.chunks = chunks; this.chunks = chunks;
this.hashAlgorithm = hashAlgorithm;
this.hash = hash; this.hash = hash;
this.metaData = metaData this.metaData = metaData
} }
@ -51,7 +60,8 @@ async function readUrl(domain) {
const chunks = await Promise.all(chunk_promises); const chunks = await Promise.all(chunk_promises);
const base64 = chunks.reduce((built, current) => built += current); const base64 = chunks.reduce((built, current) => built += current);
return handleContent(new Content(atob(content), index.mimeType, index.metaData)); const content = atob(base64);
return handleContent(new Content(content, index.mimeType, index));
} }
async function fetchChunk(id, hash) async function fetchChunk(id, hash)
@ -84,7 +94,7 @@ async function fetchIndex(domain)
}); });
const metadata = JSON.parse(atob(ret["m"])); const metadata = JSON.parse(atob(ret["m"]));
return new Index(ret["t"], ret["c"], ret["h"], metadata); return new Index(ret["t"], ret["c"], ret["h"], ret["ha"], metadata);
} }
function handleContent(content) function handleContent(content)
@ -110,7 +120,12 @@ function handleContent(content)
function handleJavascript(content) function handleJavascript(content)
{ {
console.log("Got some javascript!", content.content); console.log("Got some javascript!");
const scripts = document.getElementById("scripts");
const new_script = document.createElement("script");
new_script.text = content.content;
scripts.appendChild(new_script);
} }
async function handleMarkdown(content) async function handleMarkdown(content)
@ -128,20 +143,28 @@ async function handleMarkdown(content)
let title = document.createElement("h1"); let title = document.createElement("h1");
title.innerHTML = content.metaData.title; title.innerHTML = content.metaData.title;
document.getElementById("post").prepend(title) document.getElementById("post").prepend(title)
document.getElementById("nojs").style.visibility = "hidden"; document.getElementById("nojs").remove();
if (typeof Verifier !== "undefined") {
await (new Verifier()).verify(content);
}
} }
</script> </script>
</head> </head>
<body> <body>
<script> <div id="post"></div>
readUrl("posts-2021-08-17-serving-blog-content-over-dns-md"); <div id="verification"></div>
</script>
<div id="post"></div>
<div id="nojs"> <div id="nojs">
<h1>HTML over DNS</h1> <h1>HTML over DNS</h1>
<p>The content of this page is fetched using DNS over HTTP. Since that requires Javascript, please enable that to see the content.</p> <p>The content of this page is fetched using DNS over HTTP. Since that requires Javascript, please enable that to see the content.</p>
</div> </div>
<div id="scripts">
<script>
readUrl("scripts-verifier-js").then(
() => readUrl("posts-2021-08-17-serving-blog-content-over-dns-md")
);
</script>
</div>
</body> </body>
</html> </html>