Merge pull request #811 from ente-io/add-encrypted-collection-type

Add encrypted collection type
This commit is contained in:
Abhinav Kumar 2022-12-25 10:29:07 +05:30 committed by GitHub
commit 05c1af8ec0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 52 deletions

View file

@ -28,6 +28,8 @@ import {
CollectionSummaries,
CollectionSummary,
CollectionFilesCount,
EncryptedCollection,
CollectionMagicMetadata,
} from 'types/collection';
import {
COLLECTION_SORT_BY,
@ -50,14 +52,14 @@ const COLLECTION_TABLE = 'collections';
const COLLECTION_UPDATION_TIME = 'collection-updation-time';
const getCollectionWithSecrets = async (
collection: Collection,
collection: EncryptedCollection,
masterKey: string
) => {
): Promise<Collection> => {
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 +71,36 @@ 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
));
let collectionMagicMetadata: CollectionMagicMetadata;
if (collection.magicMetadata?.data) {
collection.magicMetadata.data = await worker.decryptMetadata(
collection.magicMetadata.data,
collection.magicMetadata.header,
decryptedKey
);
collectionMagicMetadata = {
...collection.magicMetadata,
data: await worker.decryptMetadata(
collection.magicMetadata.data,
collection.magicMetadata.header,
collectionKey
),
};
}
return {
...collection,
key: decryptedKey,
name: collectionName,
key: collectionKey,
magicMetadata: collectionMagicMetadata,
};
};
@ -109,27 +117,25 @@ const getCollections = async (
},
{ 'X-Auth-Token': token }
);
const promises: Promise<Collection>[] = 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 collection;
}
try {
return await getCollectionWithSecrets(collection, key);
} catch (e) {
logError(e, `decryption failed for collection`, {
collectionID: collection.id,
});
return collection;
}
}
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(
const collections = decryptedCollections.filter(
(collection) => collection.isDeleted || collection.key
);
return collections;
@ -292,7 +298,7 @@ export const createCollection = async (
collectionName,
collectionKey
);
const newCollection: Collection = {
const newCollection: EncryptedCollection = {
id: null,
owner: null,
encryptedKey,
@ -306,15 +312,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 +325,9 @@ export const createCollection = async (
};
const postCollection = async (
collectionData: Collection,
collectionData: EncryptedCollection,
token: string
): Promise<Collection> => {
): Promise<EncryptedCollection> => {
try {
const response = await HTTPService.post(
`${ENDPOINT}/collections`,

View file

@ -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';
@ -340,7 +340,7 @@ export const verifyPublicCollectionPassword = async (
};
const decryptCollectionName = async (
collection: Collection,
collection: EncryptedCollection,
collectionKey: string
) => {
const worker = await new CryptoWorker();

View file

@ -2,28 +2,43 @@ import { User } from 'types/user';
import { EnteFile } from 'types/file';
import { CollectionSummaryType, CollectionType } from 'constants/collection';
import {
EncryptedMagicMetadata,
MagicMetadataCore,
SUB_TYPE,
VISIBILITY_STATE,
} from 'types/magicMetadata';
export interface Collection {
export interface EncryptedCollection {
id: number;
owner: User;
key?: string;
// collection name was unencrypted in the past, so we need to keep it as optional
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: EncryptedMagicMetadata;
}
export interface Collection
extends Omit<
EncryptedCollection,
| 'encryptedKey'
| 'keyDecryptionNonce'
| 'encryptedName'
| 'nameDecryptionNonce'
| 'magicMetadata'
> {
key: string;
name: string;
isSharedCollection?: boolean;
magicMetadata: CollectionMagicMetadata;
}
export interface PublicURL {