From d9cedda4b44cc5f8e1519d42f9ae6f1034f5a96b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 4 Dec 2022 23:41:52 +0530 Subject: [PATCH 1/4] add encryptedCollection type --- src/services/collectionService.ts | 74 ++++++++++++------------- src/services/publicCollectionService.ts | 4 +- src/types/collection/index.ts | 31 +++++++---- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 418dbb73b..711119a3e 100644 --- a/src/services/collectionService.ts +++ b/src/services/collectionService.ts @@ -28,6 +28,7 @@ import { CollectionSummaries, CollectionSummary, CollectionFilesCount, + EncryptedCollection, } from 'types/collection'; import { COLLECTION_SORT_BY, @@ -50,14 +51,14 @@ const COLLECTION_TABLE = 'collections'; const COLLECTION_UPDATION_TIME = 'collection-updation-time'; const getCollectionWithSecrets = async ( - collection: Collection, + collection: EncryptedCollection, masterKey: string -) => { +): Promise => { const worker = await new CryptoWorker(); const userID = getData(LS_KEYS.USER).id; - let decryptedKey: string; + let collectionKey: string; if (collection.owner.id === userID) { - decryptedKey = await worker.decryptB64( + collectionKey = await worker.decryptB64( collection.encryptedKey, collection.keyDecryptionNonce, masterKey @@ -69,30 +70,31 @@ const getCollectionWithSecrets = async ( keyAttributes.secretKeyDecryptionNonce, masterKey ); - decryptedKey = await worker.boxSealOpen( + collectionKey = await worker.boxSealOpen( collection.encryptedKey, keyAttributes.publicKey, secretKey ); } - collection.name = + const collectionName = collection.name || (await worker.decryptToUTF8( collection.encryptedName, collection.nameDecryptionNonce, - decryptedKey + collectionKey )); if (collection.magicMetadata?.data) { collection.magicMetadata.data = await worker.decryptMetadata( collection.magicMetadata.data, collection.magicMetadata.header, - decryptedKey + collectionKey ); } return { ...collection, - key: decryptedKey, + name: collectionName, + key: collectionKey, }; }; @@ -109,30 +111,27 @@ const getCollections = async ( }, { 'X-Auth-Token': token } ); - const promises: Promise[] = resp.data.collections.map( - async (collection: Collection) => { - if (collection.isDeleted) { - return collection; + const decryptedCollections: Collection[] = await Promise.all( + resp.data.collections.map( + async (collection: EncryptedCollection) => { + if (collection.isDeleted) { + return null; + } + try { + return await getCollectionWithSecrets(collection, key); + } catch (e) { + logError(e, `decryption failed for collection`, { + collectionID: collection.id, + }); + return null; + } } - let collectionWithSecrets = collection; - try { - collectionWithSecrets = await getCollectionWithSecrets( - collection, - key - ); - } catch (e) { - logError(e, `decryption failed for collection`, { - collectionID: collection.id, - }); - } - return collectionWithSecrets; - } + ) ); - // only allow deleted or collection with key, filtering out collection whose decryption failed - const collections = (await Promise.all(promises)).filter( - (collection) => collection.isDeleted || collection.key + const nonNullCollections = decryptedCollections.filter( + (collection) => !!collection ); - return collections; + return nonNullCollections; } catch (e) { logError(e, 'getCollections failed'); throw e; @@ -292,7 +291,7 @@ export const createCollection = async ( collectionName, collectionKey ); - const newCollection: Collection = { + const newCollection: EncryptedCollection = { id: null, owner: null, encryptedKey, @@ -306,15 +305,12 @@ export const createCollection = async ( isDeleted: false, magicMetadata: null, }; - let createdCollection: Collection = await postCollection( - newCollection, - token - ); - createdCollection = await getCollectionWithSecrets( + const createdCollection = await postCollection(newCollection, token); + const decryptedCreatedCollection = await getCollectionWithSecrets( createdCollection, encryptionKey ); - return createdCollection; + return decryptedCreatedCollection; } catch (e) { logError(e, 'create collection failed'); throw e; @@ -322,9 +318,9 @@ export const createCollection = async ( }; const postCollection = async ( - collectionData: Collection, + collectionData: EncryptedCollection, token: string -): Promise => { +): Promise => { try { const response = await HTTPService.post( `${ENDPOINT}/collections`, diff --git a/src/services/publicCollectionService.ts b/src/services/publicCollectionService.ts index a290dbffa..304c83a8f 100644 --- a/src/services/publicCollectionService.ts +++ b/src/services/publicCollectionService.ts @@ -1,6 +1,6 @@ import { getEndpoint } from 'utils/common/apiUtil'; import localForage from 'utils/storage/localForage'; -import { Collection } from 'types/collection'; +import { Collection, EncryptedCollection } from 'types/collection'; import HTTPService from './HTTPService'; import { logError } from 'utils/sentry'; import { decryptFile, mergeMetadata, sortFiles } from 'utils/file'; @@ -322,7 +322,7 @@ export const verifyPublicCollectionPassword = async ( }; const decryptCollectionName = async ( - collection: Collection, + collection: EncryptedCollection, collectionKey: string ) => { const worker = await new CryptoWorker(); diff --git a/src/types/collection/index.ts b/src/types/collection/index.ts index 39980d566..484683946 100644 --- a/src/types/collection/index.ts +++ b/src/types/collection/index.ts @@ -7,23 +7,34 @@ import { VISIBILITY_STATE, } from 'types/magicMetadata'; -export interface Collection { +export interface EncryptedCollection { id: number; owner: User; - key?: string; name?: string; - encryptedName?: string; - nameDecryptionNonce?: string; + encryptedKey: string; + keyDecryptionNonce: string; + encryptedName: string; + nameDecryptionNonce: string; type: CollectionType; attributes: collectionAttributes; sharees: User[]; - updationTime: number; - encryptedKey: string; - keyDecryptionNonce: string; - isDeleted: boolean; - isSharedCollection?: boolean; publicURLs?: PublicURL[]; - magicMetadata?: CollectionMagicMetadata; + updationTime: number; + isDeleted: boolean; + magicMetadata: CollectionMagicMetadata; +} + +export interface Collection + extends Omit< + EncryptedCollection, + | 'encryptedKey' + | 'keyDecryptionNonce' + | 'encryptedName' + | 'nameDecryptionNonce' + > { + key: string; + name: string; + isSharedCollection?: boolean; } export interface PublicURL { From 5928a93748559cf15748441dd734520fa37b37ee Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 4 Dec 2022 23:49:49 +0530 Subject: [PATCH 2/4] fix magicMetadata type --- src/services/collectionService.ts | 16 +++++++++++----- src/types/collection/index.ts | 5 ++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 711119a3e..c1e8d781d 100644 --- a/src/services/collectionService.ts +++ b/src/services/collectionService.ts @@ -29,6 +29,7 @@ import { CollectionSummary, CollectionFilesCount, EncryptedCollection, + CollectionMagicMetadata, } from 'types/collection'; import { COLLECTION_SORT_BY, @@ -84,17 +85,22 @@ const getCollectionWithSecrets = async ( collectionKey )); + let collectionMagicMetadata: CollectionMagicMetadata; if (collection.magicMetadata?.data) { - collection.magicMetadata.data = await worker.decryptMetadata( - collection.magicMetadata.data, - collection.magicMetadata.header, - collectionKey - ); + collectionMagicMetadata = { + ...collection.magicMetadata, + data: await worker.decryptMetadata( + collection.magicMetadata.data, + collection.magicMetadata.header, + collectionKey + ), + }; } return { ...collection, name: collectionName, key: collectionKey, + magicMetadata: collectionMagicMetadata, }; }; diff --git a/src/types/collection/index.ts b/src/types/collection/index.ts index 484683946..4e859dc1a 100644 --- a/src/types/collection/index.ts +++ b/src/types/collection/index.ts @@ -2,6 +2,7 @@ import { User } from 'types/user'; import { EnteFile } from 'types/file'; import { CollectionSummaryType, CollectionType } from 'constants/collection'; import { + EncryptedMagicMetadata, MagicMetadataCore, SUB_TYPE, VISIBILITY_STATE, @@ -21,7 +22,7 @@ export interface EncryptedCollection { publicURLs?: PublicURL[]; updationTime: number; isDeleted: boolean; - magicMetadata: CollectionMagicMetadata; + magicMetadata: EncryptedMagicMetadata; } export interface Collection @@ -31,10 +32,12 @@ export interface Collection | 'keyDecryptionNonce' | 'encryptedName' | 'nameDecryptionNonce' + | 'magicMetadata' > { key: string; name: string; isSharedCollection?: boolean; + magicMetadata: CollectionMagicMetadata; } export interface PublicURL { From 1355b8dc605b531fcb74c1dfe44d46b41c8aed91 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 23 Dec 2022 16:24:09 +0530 Subject: [PATCH 3/4] add comment --- src/types/collection/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/collection/index.ts b/src/types/collection/index.ts index 881425a18..20744b6eb 100644 --- a/src/types/collection/index.ts +++ b/src/types/collection/index.ts @@ -11,6 +11,7 @@ import { export interface EncryptedCollection { id: number; owner: User; + // collection name was unencrypted in the past, so we need to keep it as optional name?: string; encryptedKey: string; keyDecryptionNonce: string; From 929cbc92d92b3e0cb0a6f187292573b91306bd5a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 23 Dec 2022 16:29:30 +0530 Subject: [PATCH 4/4] change back behavior to what it was --- src/services/collectionService.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 47c89494f..0b7469e85 100644 --- a/src/services/collectionService.ts +++ b/src/services/collectionService.ts @@ -121,7 +121,7 @@ const getCollections = async ( resp.data.collections.map( async (collection: EncryptedCollection) => { if (collection.isDeleted) { - return null; + return collection; } try { return await getCollectionWithSecrets(collection, key); @@ -129,15 +129,16 @@ const getCollections = async ( logError(e, `decryption failed for collection`, { collectionID: collection.id, }); - return null; + return collection; } } ) ); - const nonNullCollections = decryptedCollections.filter( - (collection) => !!collection + // only allow deleted or collection with key, filtering out collection whose decryption failed + const collections = decryptedCollections.filter( + (collection) => collection.isDeleted || collection.key ); - return nonNullCollections; + return collections; } catch (e) { logError(e, 'getCollections failed'); throw e;