( password: string, encodedSalt: string, )
| 315 | } |
| 316 | |
| 317 | async function aesKeyForPassword( |
| 318 | password: string, |
| 319 | encodedSalt: string, |
| 320 | ): Promise<CryptoKey> { |
| 321 | const salt = base64UrlBytes(encodedSalt); |
| 322 | const passwordBytes = new TextEncoder().encode(password); |
| 323 | const material = new Uint8Array(passwordBytes.length + salt.length + 1); |
| 324 | material.set(passwordBytes); |
| 325 | material[passwordBytes.length] = 0; |
| 326 | material.set(salt, passwordBytes.length + 1); |
| 327 | const digest = await crypto.subtle.digest("SHA-256", material); |
| 328 | return crypto.subtle.importKey( |
| 329 | "raw", |
| 330 | digest, |
| 331 | { name: "AES-GCM", length: 256 }, |
| 332 | false, |
| 333 | ["decrypt"], |
| 334 | ); |
| 335 | } |
| 336 | |
| 337 | function sessionFromCookie(request: Request): SessionCookie | null { |
| 338 | const raw = cookieValue(request.headers.get("Cookie") ?? "", SESSION_COOKIE); |
no test coverage detected