diff options
feat: initial commit
Diffstat (limited to 'src/routes/canaries')
-rw-r--r-- | src/routes/canaries/+page.svelte | 5 | ||||
-rw-r--r-- | src/routes/canaries/canaries.ts | 25 | ||||
-rw-r--r-- | src/routes/canaries/keystore.ts | 120 | ||||
-rw-r--r-- | src/routes/canaries/napatha:kyun.host/+server.ts | 5 |
4 files changed, 155 insertions, 0 deletions
diff --git a/src/routes/canaries/+page.svelte b/src/routes/canaries/+page.svelte new file mode 100644 index 0000000..adbbdcc --- /dev/null +++ b/src/routes/canaries/+page.svelte @@ -0,0 +1,5 @@ +<script lang="ts"> + import keyStore from "./keystore"; +</script> + +{@debug keyStore} diff --git a/src/routes/canaries/canaries.ts b/src/routes/canaries/canaries.ts new file mode 100644 index 0000000..de5c44f --- /dev/null +++ b/src/routes/canaries/canaries.ts @@ -0,0 +1,25 @@ +export const canaries: Canary[] = []; +export class Canary { + public constructor( + public name: string, + public description: string, + public url: string, + public keyIdentifier: string, + ) { + canaries.push(this); + } +} +new Canary( + 'estrogen.zone, memdmp', + 'This canary is responsible for services hosted around estrogen.zone and yuridick.gay', + '/canaries/memdmp:estrogen.zone', + 'memdmp', +); + +new Canary( + 'kyun.host', + 'This canary is responsible for the VPS provider "kyun.host"', + // '/canaries/napatha:kyun.host', + 'https://files.kyun.host/canary.txt', + 'napatha', +); diff --git a/src/routes/canaries/keystore.ts b/src/routes/canaries/keystore.ts new file mode 100644 index 0000000..788428d --- /dev/null +++ b/src/routes/canaries/keystore.ts @@ -0,0 +1,120 @@ +import { PublicKey, readCleartextMessage, readKey, verify } from 'openpgp'; +export const keyStore = new Map<string, PublicKey>(); +export const validateSignature = async ( + message: string, + id: string, +) => { + id = id.toUpperCase(); + const key = keyStore.get(id) ?? keyStore.get(id.replace(/ /g, '')); + if (!key) throw new Error('Could not find key from keystore'); + const signedMessage = await readCleartextMessage({ + cleartextMessage: message, + }); + const verificationResult = await verify({ + message: signedMessage, + verificationKeys: key, + expectSigned: true, + }); + return verificationResult.data; +}; +const pushKey = async ( + { ids, key, is_url, expectUserIds, signed_by }: { + ids?: string[]; + expectUserIds?: string[]; + key: string; + is_url?: boolean; + signed_by?: string; + }, +) => { + ids = ids ?? []; + if (is_url) { + key = await fetch( + new URL(key, 'https://keys.openpgp.org/vks/v1/by-fingerprint/'), + {}, + ).then((v) => v.text()); + } + if (signed_by) { + key = await validateSignature(key, signed_by); + } + const parsedKey = await readKey({ + armoredKey: key, + }).then((v) => v.toPublic()); + { + const missingUserIds = + expectUserIds?.filter((v) => !expectUserIds.includes(v)) ?? []; + if (missingUserIds.length) { + throw new Error( + `Key ${parsedKey.getFingerprint()} is missing User IDs: ${ + missingUserIds.join(', ') + }`, + ); + } + } + ids.push( + parsedKey.getKeyID().toHex().replace(/ /g, ''), + parsedKey.getFingerprint().replace(/ /g, ''), + ...(expectUserIds ?? []), + ); + ids = ids.filter((v, i, a) => a.indexOf(v) === i).map((v) => v.toUpperCase()); + for (const id of ids) { + keyStore.set(id, parsedKey); + } +}; +await pushKey({ + key: 'B546778F06BBCC8EC167DB3CD919706487B8B6DE', + ids: ['memdmp'], + expectUserIds: [ + 'memdmp <memdmp@estrogen.zone>', + 'memdmp <memdmp@memeware.net>', + ], + is_url: true, +}); +await pushKey({ + // TODO: when primary memdmp key rotates, or when this key expires, replace this inline string with a new one + key: `-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +- -----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: User ID: memdmp canary keysig <memdmp-key-for-signing-canary-related-keys-for-external-services@fakemail.uwu> +Comment: Valid from: 22 Nov 2024 18:31:11 +Comment: Valid until: 22 Nov 2027 12:00:00 +Comment: Type: 255-bit EdDSA (secret key available) +Comment: Usage: Signing, Encryption, Certifying User IDs +Comment: Fingerprint: 55D3582CAE78601990A8CA1DBFD0F9E61CB7D84E + +mDMEZ0C/3xYJKwYBBAHaRw8BAQdA5w4ET7V3FmasUc3h9sb0O0/y38LXp+IUV8Wf +La95jm20ZG1lbWRtcCBjYW5hcnkga2V5c2lnIDxtZW1kbXAta2V5LWZvci1zaWdu +aW5nLWNhbmFyeS1yZWxhdGVkLWtleXMtZm9yLWV4dGVybmFsLXNlcnZpY2VzQGZh +a2VtYWlsLnV3dT6ImQQTFgoAQRYhBFXTWCyueGAZkKjKHb/Q+eYct9hOBQJnQL/f +AhsDBQkFoz7RBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEL/Q+eYct9hO +X68BAPPBy76J7EWb25+fj/QUD0rYyi/E2kLfGbW+PLhrB/AdAQDl5icCilAI/2xv +X4jpGCH9KdJoClIV4g2AyKoEITKBDbg4BGdAv98SCisGAQQBl1UBBQEBB0CcYmml +AWFCXVjIerJJrs/GA65EZDwoZowiVVTS99FvaQMBCAeIfgQYFgoAJhYhBFXTWCyu +eGAZkKjKHb/Q+eYct9hOBQJnQL/fAhsMBQkFoz7RAAoJEL/Q+eYct9hOr2IA/22U +2rOPevvUoiObv/DeeQlP2mvaQcOCFHp1HVF+4oHrAQDWZiihBvdIESbqm5MH0zLe +EkEE03+lW4Zbe25P6MHsBg== +=5NPo +- -----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP SIGNATURE----- + +iIoEARYKADIWIQS1RnePBrvMjsFn2zzZGXBkh7i23gUCZ0DABRQcbWVtZG1wQG1l +bWV3YXJlLm5ldAAKCRDZGXBkh7i23vV5AP9K2Q6j6cOGovTVqsWlThK7qxA2Faz+ +ZQ4KTbprMz8J4AD/bG33f9Kqg3AqehEyU2TldJs9U9Oni5AXGSGfKLJhmQc= +=945T +-----END PGP SIGNATURE----- +`, + signed_by: 'memdmp <memdmp@memeware.net>', + ids: ['canary-sigkey-signing'], +}); +await pushKey({ + // TODO: adapt to the relevant url on current domain when up + key: 'https://files.catbox.moe/yf4x40.sig', + ids: ['napatha'], + expectUserIds: [ + 'chef naphtha <naphtha@kyun.host>', + ], + is_url: true, + signed_by: 'canary-sigkey-signing', +}); + +export default keyStore; diff --git a/src/routes/canaries/napatha:kyun.host/+server.ts b/src/routes/canaries/napatha:kyun.host/+server.ts new file mode 100644 index 0000000..260d15e --- /dev/null +++ b/src/routes/canaries/napatha:kyun.host/+server.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export const GET = () => { + throw redirect(307, 'https://files.kyun.host/canary.txt'); +}; |