import { browser } from "$app/environment"; import { validateSignature } from "./keystore"; export const canaries: Canary[] = []; export interface CanaryInterface { name: string, description: string, signer: string, url: string, keyIdentifier: string, contentType: string, upstream?: string } export interface Canary extends CanaryInterface { } type _PreCanaryInterfaceEntries = { [T in keyof CanaryInterface]: [T, CanaryInterface[T]] } type CanaryInterfaceEntry = _PreCanaryInterfaceEntries[keyof _PreCanaryInterfaceEntries] export class Canary { public constructor( canary: CanaryInterface ) { const entries = Object.entries(canary) as CanaryInterfaceEntry[]; for (const a of entries) { // @ts-ignore we know the value will match in type this[a[0]] = a[1]; } 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( { name: 'estrogen.zone', description: 'Services hosted around estrogen.zone and yuridick.gay', signer: 'memdmp', url: '/canaries/memdmp:estrogen.zone', keyIdentifier: 'memdmp', contentType: 'text/plain; charset=utf-8' } ); new Canary( { name: 'kyun.host', description: 'The VPS provider "kyun.host"', signer: 'napatha', url: '/canaries/napatha:kyun.host', keyIdentifier: 'napatha', contentType: 'text/plain; charset=utf-8', upstream: 'https://files.kyun.host/canary.txt' } );