implemented basic authentication
This commit is contained in:
parent
0d4d348979
commit
568b8d1443
@ -48,7 +48,7 @@ function base64ToUint8(base64: string) {
|
|||||||
|
|
||||||
export async function deriveKeyVault(secret: string) {
|
export async function deriveKeyVault(secret: string) {
|
||||||
console.log(crypto);
|
console.log(crypto);
|
||||||
|
|
||||||
let hash = strToUint8(secret), last = hash;
|
let hash = strToUint8(secret), last = hash;
|
||||||
for (let i = 0; i < 10000; i++) {
|
for (let i = 0; i < 10000; i++) {
|
||||||
const hashInput = Uint8Array.from([...strToUint8(SALT), ...hash]);
|
const hashInput = Uint8Array.from([...strToUint8(SALT), ...hash]);
|
||||||
@ -57,14 +57,14 @@ export async function deriveKeyVault(secret: string) {
|
|||||||
hash = new Uint8Array(hashBuffer);
|
hash = new Uint8Array(hashBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const symmetricKey = last.slice(0, 32);
|
const symmKey = last.slice(0, 32);
|
||||||
const privateKey = hash.slice(0, 32);
|
const privKey = hash.slice(0, 32);
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const pubKey = ed.getPublicKey(privKey);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
publicKey,
|
pubKey,
|
||||||
privateKey,
|
privKey,
|
||||||
symmetricKey,
|
symmKey,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,10 +85,10 @@ export function decodeKeyVault(encoded: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function encrypt(vault: KeyVault, data: Uint8Array) {
|
export async function encrypt(symmKey: Uint8Array, data: Uint8Array) {
|
||||||
const key = await crypto.subtle.importKey(
|
const key = await crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
vault.symmetricKey,
|
symmKey,
|
||||||
{ name: 'AES-GCM' },
|
{ name: 'AES-GCM' },
|
||||||
false,
|
false,
|
||||||
['encrypt', 'decrypt'],
|
['encrypt', 'decrypt'],
|
||||||
@ -105,10 +105,10 @@ export async function encrypt(vault: KeyVault, data: Uint8Array) {
|
|||||||
return Uint8Array.from([...iv, ...new Uint8Array(encryptedBuffer)]);
|
return Uint8Array.from([...iv, ...new Uint8Array(encryptedBuffer)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function decrypt(vault: KeyVault, cipher: Uint8Array) {
|
export async function decrypt(symmKey: Uint8Array, cipher: Uint8Array) {
|
||||||
const key = await crypto.subtle.importKey(
|
const key = await crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
vault.symmetricKey,
|
symmKey,
|
||||||
{ name: 'AES-GCM' },
|
{ name: 'AES-GCM' },
|
||||||
false,
|
false,
|
||||||
['encrypt', 'decrypt'],
|
['encrypt', 'decrypt'],
|
||||||
@ -125,3 +125,15 @@ export async function decrypt(vault: KeyVault, cipher: Uint8Array) {
|
|||||||
|
|
||||||
return new Uint8Array(dataBuffer);
|
return new Uint8Array(dataBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function sign(privKey: Uint8Array, data: Uint8Array): Promise<Uint8Array> {
|
||||||
|
return await ed.signAsync(data, privKey) as Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verify(pubKey: Uint8Array, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return await ed.verifyAsync(signature, data, pubKey);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const vault = getKeyVault();
|
const vault = getKeyVault();
|
||||||
fetch(`/api/log?pubkey=${encodeURIComponent(uint8ToHex(vault!.publicKey))}`).then(async res => {
|
fetch(`/api/log?pubkey=${encodeURIComponent(uint8ToHex(vault!.pubKey))}`).then(async res => {
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
|
|
||||||
entryUrls = (await res.json() as any[]).sort((a, b) => b.date.localeCompare(a.date));
|
entryUrls = (await res.json() as any[]).sort((a, b) => b.date.localeCompare(a.date));
|
||||||
@ -35,7 +35,7 @@
|
|||||||
console.log(cipherBuffer);
|
console.log(cipherBuffer);
|
||||||
|
|
||||||
const vault = getKeyVault()!;
|
const vault = getKeyVault()!;
|
||||||
const videoData = await decrypt(vault, new Uint8Array(cipherBuffer));
|
const videoData = await decrypt(vault.symmKey, new Uint8Array(cipherBuffer));
|
||||||
|
|
||||||
const blob = new Blob([videoData], { type: 'video/webm' });
|
const blob = new Blob([videoData], { type: 'video/webm' });
|
||||||
videoSrc = URL.createObjectURL(blob);
|
videoSrc = URL.createObjectURL(blob);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { hashBuffer } from "$lib/hash";
|
import { hashBuffer } from "$lib/hash";
|
||||||
import { dateDiff } from "$lib/date";
|
import { dateDiff } from "$lib/date";
|
||||||
import { getKeyVault, loggedIn } from "$lib/auth";
|
import { getKeyVault, loggedIn } from "$lib/auth";
|
||||||
import { encrypt, uint8ToHex } from "$lib/crypto";
|
import { encrypt, sign, uint8ToHex } from "$lib/crypto";
|
||||||
import { lang } from "$lib/lang";
|
import { lang } from "$lib/lang";
|
||||||
|
|
||||||
type StatusT = "pending" | "active" | "error";
|
type StatusT = "pending" | "active" | "error";
|
||||||
@ -59,7 +59,10 @@
|
|||||||
recorder.addEventListener('dataavailable', async (e) => {
|
recorder.addEventListener('dataavailable', async (e) => {
|
||||||
const inputBlob = new Blob([e.data], { type: 'video/mp4' });
|
const inputBlob = new Blob([e.data], { type: 'video/mp4' });
|
||||||
|
|
||||||
const convertRes = await fetch('/api/convert', {
|
const vault = getKeyVault()!;
|
||||||
|
const signature = await sign(vault.privKey, new Uint8Array(await inputBlob.arrayBuffer()));
|
||||||
|
|
||||||
|
const convertRes = await fetch(`/api/convert?pubkey=${encodeURIComponent(uint8ToHex(vault.pubKey))}&signature=${encodeURIComponent(uint8ToHex(signature))}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: inputBlob,
|
body: inputBlob,
|
||||||
});
|
});
|
||||||
@ -87,13 +90,12 @@
|
|||||||
|
|
||||||
const blob = await (await fetch(recordingURL)).blob();
|
const blob = await (await fetch(recordingURL)).blob();
|
||||||
const buffer = await blob.arrayBuffer();
|
const buffer = await blob.arrayBuffer();
|
||||||
console.log('buffer', buffer);
|
|
||||||
|
|
||||||
const vault = getKeyVault()!;
|
const vault = getKeyVault()!;
|
||||||
const encrypted = await encrypt(vault, new Uint8Array(buffer));
|
const encrypted = await encrypt(vault.symmKey, new Uint8Array(buffer));
|
||||||
console.log('encrypted', encrypted);
|
const signature = await sign(vault.privKey, encrypted);
|
||||||
|
|
||||||
const res = await fetch(`/api/today?pubkey=${uint8ToHex(vault.publicKey)}`, {
|
const res = await fetch(`/api/today?pubkey=${uint8ToHex(vault.pubKey)}&signature=${uint8ToHex(signature)}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: encrypted,
|
body: encrypted,
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
if (typeof window !== 'object') return;
|
if (typeof window !== 'object') return;
|
||||||
|
|
||||||
document.body.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
|
document.body.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
|
||||||
window.addEventListener('resize', () => {
|
// window.addEventListener('resize', () => {
|
||||||
document.body.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
|
// document.body.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (typeof window !== 'object') return;
|
if (typeof window !== 'object') return;
|
||||||
|
|
||||||
console.log('test', loggedIn());
|
|
||||||
|
|
||||||
if (loggedIn()) {
|
if (loggedIn()) {
|
||||||
window.location.href = '/today';
|
window.location.href = '/today';
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,22 @@
|
|||||||
import { convertVideo } from "$lib/ffmpeg.server";
|
import { convertVideo } from "$lib/ffmpeg.server";
|
||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
import type { RequestHandler } from "./$types";
|
import type { RequestHandler } from "./$types";
|
||||||
|
import { hexToUint8, verify } from "$lib/crypto";
|
||||||
|
|
||||||
|
export const POST: RequestHandler = async ({ request, url }) => {
|
||||||
|
const pubkeyHex = url.searchParams.get('pubkey')?.trim()?.toLowerCase();
|
||||||
|
if (!pubkeyHex || !/^[0-9a-f]{64}$/.test(pubkeyHex)) error(400);
|
||||||
|
const pubKey = hexToUint8(pubkeyHex);
|
||||||
|
|
||||||
|
const signatureHex = url.searchParams.get('signature')?.trim()?.toLowerCase();
|
||||||
|
if (!signatureHex || !/^[0-9a-f]+$/.test(signatureHex)) error(400);
|
||||||
|
const signature = hexToUint8(signatureHex);
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ request }) => {
|
|
||||||
const bodyBuffer = await request.arrayBuffer();
|
const bodyBuffer = await request.arrayBuffer();
|
||||||
const bodyUint8 = new Uint8Array(bodyBuffer);
|
const bodyUint8 = new Uint8Array(bodyBuffer);
|
||||||
|
|
||||||
|
const valid = await verify(pubKey, signature, bodyUint8);
|
||||||
|
if (!valid) error(401);
|
||||||
|
|
||||||
const convertedUint8 = await convertVideo(bodyUint8);
|
const convertedUint8 = await convertVideo(bodyUint8);
|
||||||
if (!convertedUint8) error(500);
|
if (!convertedUint8) error(500);
|
||||||
|
@ -4,17 +4,26 @@ import type { RequestHandler } from "./$types";
|
|||||||
import { PutObjectCommand } from "@aws-sdk/client-s3";
|
import { PutObjectCommand } from "@aws-sdk/client-s3";
|
||||||
import { env } from "$env/dynamic/private";
|
import { env } from "$env/dynamic/private";
|
||||||
import { s3 } from "$lib/storage.server";
|
import { s3 } from "$lib/storage.server";
|
||||||
|
import { hexToUint8, verify } from "$lib/crypto";
|
||||||
|
|
||||||
|
// This route is currently vulnerable against replay attacks across multiple days
|
||||||
|
// -- possible fix: prepend date to signature verification payload
|
||||||
export const POST: RequestHandler = async ({ request, url }) => {
|
export const POST: RequestHandler = async ({ request, url }) => {
|
||||||
const pubkeyHex = url.searchParams.get('pubkey')?.trim()?.toLowerCase();
|
const pubkeyHex = url.searchParams.get('pubkey')?.trim()?.toLowerCase();
|
||||||
if (!pubkeyHex || !/^[0-9a-f]{64}$/.test(pubkeyHex)) error(400);
|
if (!pubkeyHex || !/^[0-9a-f]{64}$/.test(pubkeyHex)) error(400);
|
||||||
|
const pubKey = hexToUint8(pubkeyHex);
|
||||||
|
|
||||||
|
const signatureHex = url.searchParams.get('signature')?.trim()?.toLowerCase();
|
||||||
|
if (!signatureHex || !/^[0-9a-f]+$/.test(signatureHex)) error(400);
|
||||||
|
const signature = hexToUint8(signatureHex);
|
||||||
|
|
||||||
const today = new Date().toISOString().slice(0, 10);
|
const today = new Date().toISOString().slice(0, 10);
|
||||||
|
|
||||||
const bodyBuffer = await request.arrayBuffer();
|
const bodyBuffer = await request.arrayBuffer();
|
||||||
const bodyUint8 = new Uint8Array(bodyBuffer);
|
const bodyUint8 = new Uint8Array(bodyBuffer);
|
||||||
|
|
||||||
console.log(today, bodyUint8.byteLength);
|
const valid = await verify(pubKey, signature, bodyUint8);
|
||||||
|
if (!valid) error(401);
|
||||||
|
|
||||||
const command = new PutObjectCommand({
|
const command = new PutObjectCommand({
|
||||||
Bucket: env.S3_BUCKET,
|
Bucket: env.S3_BUCKET,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user