import { browser } from "$app/environment"; import { validateSignature } from "./keystore"; export const canaries: Canary[] = []; export class Canary { public constructor( public name: string, public description: string, public signer: string, public url: string, public keyIdentifier: string, public contentType: string = 'text/plain', ) { canaries.push(this); } public forceContentType = false; public async getRawText() { if (!browser) throw new Error('This should only be done in a browser.') const res = await fetch(this.url) if (res.ok) { if (!this.forceContentType) this.contentType = res.headers.get('Content-Type') || this.contentType const text = await res.text() return text } else { throw new Error(`Fetching canary failed with code ${res.status} (${res.statusText}): ${await res.text().catch(e => `Unknown (Unable to get text, ${JSON.stringify(e)})`)}`) } } public async getValidatedText(rawText: string | Promise = this.getRawText()) { return await validateSignature(await rawText, this.keyIdentifier) } /** Returns downloadable data url if signature passes, otherwise returns null */ public async getUrl(rawText: string | Promise = this.getRawText()) { const t = await rawText; let stripped: string; try { stripped = await this.getValidatedText(t); } catch (error) { console.warn('Failed to validate signature: ', error); return null; } return { stripped: URL.createObjectURL(new Blob([stripped], { type: this.contentType, })), signed: URL.createObjectURL(new Blob([t], { type: this.contentType, })), } } } new Canary( 'estrogen.zone', 'Services hosted around estrogen.zone and yuridick.gay', 'memdmp', '/canaries/memdmp:estrogen.zone', 'memdmp', 'text/plain; charset=utf-8' ); new Canary( 'kyun.host', 'The VPS provider "kyun.host"', 'napatha', '/canaries/napatha:kyun.host', // 'https://files.kyun.host/canary.txt', 'napatha', 'text/plain; charset=utf-8' );