aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/canaries
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/canaries')
-rw-r--r--src/routes/canaries/canaries.ts107
-rw-r--r--src/routes/canaries/keystore.ts137
2 files changed, 133 insertions, 111 deletions
diff --git a/src/routes/canaries/canaries.ts b/src/routes/canaries/canaries.ts
index 692abc2..dab2e68 100644
--- a/src/routes/canaries/canaries.ts
+++ b/src/routes/canaries/canaries.ts
@@ -1,25 +1,25 @@
-import { browser } from "$app/environment";
-import { validateSignature } from "./keystore";
+import { browser } from '$app/environment';
+import { base } from '$app/paths';
+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
+ name: string;
+ description: string;
+ signer: string;
+ url: string;
+ keyIdentifier: string;
+ contentType: string;
+ upstream?: string;
}
-export interface Canary extends CanaryInterface { }
+export interface Canary extends CanaryInterface {}
type _PreCanaryInterfaceEntries = {
- [T in keyof CanaryInterface]: [T, CanaryInterface[T]]
-}
-type CanaryInterfaceEntry = _PreCanaryInterfaceEntries[keyof _PreCanaryInterfaceEntries]
+ [T in keyof CanaryInterface]: [T, CanaryInterface[T]];
+};
+type CanaryInterfaceEntry =
+ _PreCanaryInterfaceEntries[keyof _PreCanaryInterfaceEntries];
export class Canary {
- public constructor(
- canary: CanaryInterface
- ) {
+ 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
@@ -29,19 +29,22 @@ export class Canary {
}
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 (!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
+ 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)})`)}`)
+${await res.text().catch((e) => `Unknown (Unable to get text, ${JSON.stringify(e)})`)}`);
}
}
- public async getValidatedText(rawText: string | Promise<string> = this.getRawText()) {
- return await validateSignature(await rawText, this.keyIdentifier)
+ public async getValidatedText(
+ rawText: string | Promise<string> = 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<string> = this.getRawText()) {
@@ -54,34 +57,34 @@ ${await res.text().catch(e => `Unknown (Unable to get text, ${JSON.stringify(e)}
return null;
}
return {
- stripped: URL.createObjectURL(new Blob([stripped], {
- type: this.contentType,
- })),
- signed: URL.createObjectURL(new Blob([t], {
- type: this.contentType,
- })),
- }
+ 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: 'estrogen.zone',
+ description: 'Services hosted around estrogen.zone and yuridick.gay',
+ signer: 'memdmp',
+ url: base + '/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'
- }
-);
+new Canary({
+ name: 'kyun.host',
+ description: 'The VPS provider "kyun.host"',
+ signer: 'napatha',
+ url: base + '/canaries/napatha:kyun.host',
+ keyIdentifier: 'napatha',
+ contentType: 'text/plain; charset=utf-8',
+ upstream: 'https://files.kyun.host/canary.txt',
+});
diff --git a/src/routes/canaries/keystore.ts b/src/routes/canaries/keystore.ts
index 5b85201..ca8cb22 100644
--- a/src/routes/canaries/keystore.ts
+++ b/src/routes/canaries/keystore.ts
@@ -1,34 +1,49 @@
-import { dev } from "$app/environment";
-import { PublicKey, readCleartextMessage, readKey, verify } from "openpgp";
-import { fallbackKeys } from "./fallback-keys";
+import { dev } from '$app/environment';
+import { PublicKey, readCleartextMessage, readKey, verify } from 'openpgp';
+import { fallbackKeys } from './fallback-keys';
export const keyStore = new Map<string, PublicKey>();
const will_debug = true;
-const debug = dev && will_debug ? (z: any, ...a: any[]) => console.debug(...[
- ...(typeof z === 'string' ? [`[keystore] ${z}`] : ['[keystore]', z]),
- ...a
-]) : () => void 0;
+const debug =
+ dev && will_debug
+ ? (z: any, ...a: any[]) =>
+ console.debug(
+ ...[
+ ...(typeof z === 'string'
+ ? [`[keystore] ${z}`]
+ : ['[keystore]', z]),
+ ...a,
+ ],
+ )
+ : () => void 0;
const _validateSignature = async (message: string, id: string) => {
id = id.toUpperCase();
- debug(`[validateSignature][status=lookup] looking up key by ID ${JSON.stringify(id)}`)
- const key = keyStore.get(id) ?? keyStore.get(id.replace(/ /g, ""));
- if (!key) throw new Error("Could not find key from keystore");
- debug(`[validateSignature][status=parse] parsing message`)
+ debug(
+ `[validateSignature][status=lookup] looking up key by ID ${JSON.stringify(id)}`,
+ );
+ const key = keyStore.get(id) ?? keyStore.get(id.replace(/ /g, ''));
+ if (!key) throw new Error('Could not find key from keystore');
+ debug(`[validateSignature][status=parse] parsing message`);
const signedMessage = await readCleartextMessage({
cleartextMessage: message,
});
- debug(`[validateSignature][status=verify] verifying message signature`)
+ debug(`[validateSignature][status=verify] verifying message signature`);
const verificationResult = await verify({
message: signedMessage,
verificationKeys: key,
expectSigned: true,
});
- debug(`[validateSignature][success] successfully validated message signature`)
+ debug(
+ `[validateSignature][success] successfully validated message signature`,
+ );
return verificationResult.data;
-}
-export const validateSignature: typeof _validateSignature = async (message, id) => {
- debug(`[validateSignature][globalState] waiting on initKeystore`)
+};
+export const validateSignature: typeof _validateSignature = async (
+ message,
+ id,
+) => {
+ debug(`[validateSignature][globalState] waiting on initKeystore`);
await initKeystore;
- return _validateSignature(message, id)
+ return _validateSignature(message, id);
};
const pushKey = async ({
ids,
@@ -47,34 +62,35 @@ const pushKey = async ({
}) => {
ids = ids ?? [];
if (is_url) {
- const url = new URL(key, "https://keys.openpgp.org/vks/v1/by-fingerprint/");
- debug('[pushKey][info] Getting key with url\n\n', url)
- key = await fetch(
- url,
- ).then((v) => v.text()).catch(e => {
- if (fallbackKeys.has(key)) {
- debug('[pushKey][warn] Failed with error', e, 'but found fallback key')
- return fallbackKeys.get(key)!
- }
- else {
- debug('[pushKey][error] Failed to fetch key, cannot find fallback')
- throw e
- }
- });
- debug('[pushKey][success] Fetched key\n\n', JSON.stringify(key))
+ const url = new URL(key, 'https://keys.openpgp.org/vks/v1/by-fingerprint/');
+ debug('[pushKey][info] Getting key with url\n\n', url);
+ key = await fetch(url)
+ .then((v) => v.text())
+ .catch((e) => {
+ if (fallbackKeys.has(key)) {
+ debug(
+ '[pushKey][warn] Failed with error',
+ e,
+ 'but found fallback key',
+ );
+ return fallbackKeys.get(key)!;
+ } else {
+ debug('[pushKey][error] Failed to fetch key, cannot find fallback');
+ throw e;
+ }
+ });
+ debug('[pushKey][success] Fetched key\n\n', JSON.stringify(key));
} else {
- debug('[pushKey][success] Found key\n\n', JSON.stringify(key))
+ debug('[pushKey][success] Found key\n\n', JSON.stringify(key));
}
- if (key === null)
- throw new Error('Key is null.')
- if (key === '')
- throw new Error('Key is empty string.')
+ if (key === null) throw new Error('Key is null.');
+ if (key === '') throw new Error('Key is empty string.');
if (typeof key !== 'string')
- throw new Error(`Expected key with type string, got key of type ${key}`)
+ throw new Error(`Expected key with type string, got key of type ${key}`);
if (signed_by) {
- debug('[pushKey][validation][info] Key must be signed by', signed_by)
+ debug('[pushKey][validation][info] Key must be signed by', signed_by);
key = await _validateSignature(key, signed_by);
- debug('[pushKey][success] Validated signature')
+ debug('[pushKey][success] Validated signature');
}
const parsedKey = await readKey({
armoredKey: key,
@@ -86,22 +102,25 @@ const pushKey = async ({
if (missingUserIds.length) {
throw new Error(
`Key ${parsedKey.getFingerprint()} is missing User IDs: ${missingUserIds.join(
- ", ",
+ ', ',
)}`,
);
}
}
- if (expect_fingerprint && parsedKey.getFingerprint().toUpperCase() !== expect_fingerprint.toUpperCase())
+ if (
+ expect_fingerprint &&
+ parsedKey.getFingerprint().toUpperCase() !==
+ expect_fingerprint.toUpperCase()
+ )
throw new Error(
`Key ${parsedKey.getFingerprint()} is not ${expect_fingerprint}`,
);
else if (expect_fingerprint)
- debug('[success] Fingerprint matches expected fingerprint')
- else
- debug('[warn] No expected fingerprint passed')
+ debug('[success] Fingerprint matches expected fingerprint');
+ else debug('[warn] No expected fingerprint passed');
ids.push(
- parsedKey.getKeyID().toHex().replace(/ /g, ""),
- parsedKey.getFingerprint().replace(/ /g, ""),
+ parsedKey.getKeyID().toHex().replace(/ /g, ''),
+ parsedKey.getFingerprint().replace(/ /g, ''),
...(expect_user_ids ?? []),
);
ids = ids.filter((v, i, a) => a.indexOf(v) === i).map((v) => v.toUpperCase());
@@ -111,15 +130,15 @@ const pushKey = async ({
debug('[success] Added key\n\n', {
key: parsedKey,
ids,
- })
+ });
};
export const initKeystore = (async () => {
await pushKey({
key: 'B546778F06BBCC8EC167DB3CD919706487B8B6DE',
- ids: ["memdmp"],
+ ids: ['memdmp'],
expect_user_ids: [
- "memdmp <memdmp@estrogen.zone>",
- "memdmp <memdmp@memeware.net>",
+ 'memdmp <memdmp@estrogen.zone>',
+ 'memdmp <memdmp@memeware.net>',
],
expect_fingerprint: 'B546778F06BBCC8EC167DB3CD919706487B8B6DE',
is_url: true,
@@ -158,18 +177,18 @@ ZQ4KTbprMz8J4AD/bG33f9Kqg3AqehEyU2TldJs9U9Oni5AXGSGfKLJhmQc=
=945T
-----END PGP SIGNATURE-----
`,
- signed_by: "memdmp <memdmp@memeware.net>",
- ids: ["canary-sigkey-signing"],
- expect_fingerprint: '55D3582CAE78601990A8CA1DBFD0F9E61CB7D84E'
+ signed_by: 'memdmp <memdmp@memeware.net>',
+ ids: ['canary-sigkey-signing'],
+ expect_fingerprint: '55D3582CAE78601990A8CA1DBFD0F9E61CB7D84E',
});
await pushKey({
- key: "https://git.estrogen.zone/mem-estrogen-zone.git/plain/static/keys/external/napatha.pgp.sig",
- ids: ["napatha"],
- expect_user_ids: ["chef naphtha <naphtha@kyun.host>"],
+ key: 'https://git.estrogen.zone/mem-estrogen-zone.git/plain/static/keys/external/napatha.pgp.sig',
+ ids: ['napatha'],
+ expect_user_ids: ['chef naphtha <naphtha@kyun.host>'],
is_url: true,
- signed_by: "canary-sigkey-signing",
+ signed_by: 'canary-sigkey-signing',
});
- debug('[success] keystore initialization completed')
+ debug('[success] keystore initialization completed');
})();
export default keyStore;