implemented basic authentication
This commit is contained in:
		
							parent
							
								
									0d4d348979
								
							
						
					
					
						commit
						568b8d1443
					
				| @ -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,11 +1,23 @@ | |||||||
| 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