diff --git a/app/api.js b/app/api.js index 45fc00ef..52b78775 100644 --- a/app/api.js +++ b/app/api.js @@ -371,9 +371,9 @@ export function downloadFile(id, keychain, onprogress) { }; } -export async function getFileList(bearerToken) { +export async function getFileList(bearerToken, kid) { const headers = new Headers({ Authorization: `Bearer ${bearerToken}` }); - const response = await fetch(getApiUrl('/api/filelist'), { headers }); + const response = await fetch(getApiUrl(`/api/filelist/${kid}`), { headers }); if (response.ok) { const encrypted = await response.blob(); return encrypted; @@ -381,9 +381,9 @@ export async function getFileList(bearerToken) { throw new Error(response.status); } -export async function setFileList(bearerToken, data) { +export async function setFileList(bearerToken, kid, data) { const headers = new Headers({ Authorization: `Bearer ${bearerToken}` }); - const response = await fetch(getApiUrl('/api/filelist'), { + const response = await fetch(getApiUrl(`/api/filelist/${kid}`), { headers, method: 'POST', body: data diff --git a/app/user.js b/app/user.js index 569fd856..f3df940d 100644 --- a/app/user.js +++ b/app/user.js @@ -159,10 +159,13 @@ export default class User { return this.storage.merge(); } let list = []; + const key = b64ToArray(this.info.fileListKey); + const sha = await crypto.subtle.digest('SHA-256', key); + const kid = arrayToB64(new Uint8Array(sha)).substring(0, 16); try { - const encrypted = await getFileList(this.bearerToken); + const encrypted = await getFileList(this.bearerToken, kid); const decrypted = await streamToArrayBuffer( - decryptStream(blobStream(encrypted), b64ToArray(this.info.fileListKey)) + decryptStream(blobStream(encrypted), key) ); list = JSON.parse(textDecoder.decode(decrypted)); } catch (e) { @@ -180,9 +183,9 @@ export default class User { textEncoder.encode(JSON.stringify(this.storage.files)) ]); const encrypted = await streamToArrayBuffer( - encryptStream(blobStream(blob), b64ToArray(this.info.fileListKey)) + encryptStream(blobStream(blob), key) ); - await setFileList(this.bearerToken, encrypted); + await setFileList(this.bearerToken, kid, encrypted); } catch (e) { // } diff --git a/server/routes/filelist.js b/server/routes/filelist.js index 44c30f73..700fe745 100644 --- a/server/routes/filelist.js +++ b/server/routes/filelist.js @@ -1,9 +1,14 @@ +const crypto = require('crypto'); const config = require('../config'); const storage = require('../storage'); const Limiter = require('../limiter'); -function id(user) { - return `filelist-${user}`; +function id(user, kid) { + const sha = crypto.createHash('sha256'); + sha.update(user); + sha.update(kid); + const hash = sha.digest('hex'); + return `filelist-${hash}`; } module.exports = { @@ -11,8 +16,9 @@ module.exports = { if (!req.user) { return res.sendStatus(401); } + const kid = req.params.id; try { - const fileId = id(req.user); + const fileId = id(req.user, kid); const contentLength = await storage.length(fileId); const fileStream = await storage.get(fileId); res.writeHead(200, { @@ -29,11 +35,12 @@ module.exports = { if (!req.user) { return res.sendStatus(401); } + const kid = req.params.id; try { const limiter = new Limiter(1024 * 1024 * 10); const fileStream = req.pipe(limiter); await storage.set( - id(req.user), + id(req.user, kid), fileStream, null, config.max_expire_seconds diff --git a/server/routes/index.js b/server/routes/index.js index 44f61e29..6ddd5d8b 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -88,8 +88,8 @@ module.exports = function(app) { ); app.get(`/api/exists/:id${ID_REGEX}`, require('./exists')); app.get(`/api/metadata/:id${ID_REGEX}`, auth.hmac, require('./metadata')); - app.get('/api/filelist', auth.fxa, filelist.get); - app.post('/api/filelist', auth.fxa, filelist.post); + app.get('/api/filelist/:id(\\w{16})', auth.fxa, filelist.get); + app.post('/api/filelist/:id(\\w{16})', auth.fxa, filelist.post); app.post('/api/upload', auth.fxa, require('./upload')); app.post(`/api/delete/:id${ID_REGEX}`, auth.owner, require('./delete')); app.post(`/api/password/:id${ID_REGEX}`, auth.owner, require('./password'));