[web] Further refactoring of the crypto layering (#2755)
\+ tweaks to the indexing process
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { sharedCryptoWorker } from "@/base/crypto";
|
||||
import { encryptMetadataJSON } from "@/base/crypto/ente";
|
||||
import { encryptMetadataJSON, sharedCryptoWorker } from "@/base/crypto";
|
||||
import log from "@/base/log";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import { ItemVisibility } from "@/media/file-metadata";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { sharedCryptoWorker } from "@/base/crypto";
|
||||
import { decryptMetadataJSON } from "@/base/crypto/ente";
|
||||
import { decryptMetadataJSON, sharedCryptoWorker } from "@/base/crypto";
|
||||
import log from "@/base/log";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { encryptMetadataJSON } from "@/base/crypto/ente";
|
||||
import { encryptMetadataJSON } from "@/base/crypto";
|
||||
import log from "@/base/log";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import { getLocalFiles, setLocalFiles } from "@/new/photos/services/files";
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
/**
|
||||
* @file Higher level functions that use the ontology of Ente's types
|
||||
*
|
||||
* [Note: Crypto code hierarchy]
|
||||
*
|
||||
* 1. crypto/ente.ts (Ente specific higher level functions)
|
||||
* 2. crypto/libsodium.ts (More primitive wrappers over libsodium)
|
||||
* 3. libsodium-wrappers (JavaScript bindings to libsodium)
|
||||
*
|
||||
* Our cryptography primitives are provided by libsodium, specifically, its
|
||||
* JavaScript bindings ("libsodium-wrappers"). This is the lowest layer.
|
||||
*
|
||||
* Direct usage of "libsodium-wrappers" is restricted to `crypto/libsodium.ts`.
|
||||
* This is the next higher layer, and the first one that our code should
|
||||
* directly use. Usually the functions in this file are thin wrappers over the
|
||||
* raw libsodium APIs, with a bit of massaging. They also ensure that
|
||||
* sodium.ready has been called before accessing libsodium's APIs, thus all the
|
||||
* functions it exposes are async.
|
||||
*
|
||||
* The highest layer is this file, `crypto/ente.ts`. These are usually simple
|
||||
* compositions of functionality exposed by `crypto/libsodium.ts`, but the
|
||||
* difference is that the functions in ente.ts don't talk in terms of the crypto
|
||||
* algorithms, but rather in terms the higher-level Ente specific goal we are
|
||||
* trying to accomplish.
|
||||
*
|
||||
* There is an additional actor in the play. Cryptographic operations are CPU
|
||||
* intensive and would cause the UI to stutter if used directly on the main
|
||||
* thread. To keep the UI smooth, we instead want to run them in a web worker.
|
||||
* However, sometimes we already _are_ running in a web worker, and delegating
|
||||
* to another worker is wasteful.
|
||||
*
|
||||
* To handle both these scenario, each function in this file is split into the
|
||||
* external API, and the underlying implementation (denoted by an "_" prefix).
|
||||
* The external API functions check to see if we're already in a web worker, and
|
||||
* if so directly invoke the implementation. Otherwise the call the sibling
|
||||
* function in a shared "crypto" web worker (which then invokes the
|
||||
* implementation, but this time in the context of a web worker).
|
||||
*
|
||||
* To avoid a circular dependency during webpack imports, we need to keep the
|
||||
* implementation functions in a separate file (ente-impl.ts). This is a bit
|
||||
* unfortunate, since it makes them harder to read and reason about (since their
|
||||
* documentation and parameter names are all in ente.ts).
|
||||
*
|
||||
* Some older code directly calls the functions in the shared crypto.worker.ts,
|
||||
* but that should be avoided since it makes the code not behave the way we want
|
||||
* when we're already in a web worker. There are exceptions to this
|
||||
* recommendation though (in circumstances where we create more crypto workers
|
||||
* instead of using the shared one).
|
||||
*/
|
||||
import { sharedCryptoWorker } from ".";
|
||||
import { assertionFailed } from "../assert";
|
||||
import { inWorker } from "../env";
|
||||
import * as ei from "./ente-impl";
|
||||
import type { BytesOrB64, EncryptedBlob, EncryptedBox } from "./types";
|
||||
|
||||
/**
|
||||
* Some of these functions have not yet been needed on the main thread, and for
|
||||
* these we don't have a corresponding sharedCryptoWorker method.
|
||||
*
|
||||
* This assertion will let us know when we need to implement them. This will
|
||||
* gracefully degrade in production: the functionality will work, just that the
|
||||
* crypto operations will happen on the main thread itself.
|
||||
*/
|
||||
const assertInWorker = <T>(x: T): T => {
|
||||
if (!inWorker()) assertionFailed("Currently only usable in a web worker");
|
||||
return x;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt the given data, returning a box containing the encrypted data and a
|
||||
* randomly generated nonce that was used during encryption.
|
||||
*
|
||||
* Both the encrypted data and the nonce are returned as base64 strings.
|
||||
*
|
||||
* Use {@link decryptBoxB64} to decrypt the result.
|
||||
*
|
||||
* > The suffix "Box" comes from the fact that it uses the so called secretbox
|
||||
* > APIs provided by libsodium under the hood.
|
||||
* >
|
||||
* > See: [Note: 3 forms of encryption (Box | Blob | Stream)]
|
||||
*/
|
||||
export const encryptBoxB64 = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._encryptBoxB64(data, key)
|
||||
: sharedCryptoWorker().then((w) => w.encryptBoxB64(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the given data, returning a blob containing the encrypted data and a
|
||||
* decryption header.
|
||||
*
|
||||
* This function is usually used to encrypt data associated with an Ente object
|
||||
* (file, collection, entity) using the object's key.
|
||||
*
|
||||
* Use {@link decryptBlob} to decrypt the result.
|
||||
*
|
||||
* > The suffix "Blob" comes from our convention of naming functions that use
|
||||
* > the secretstream APIs in one-shot mode.
|
||||
* >
|
||||
* > See: [Note: 3 forms of encryption (Box | Blob | Stream)]
|
||||
*/
|
||||
export const encryptBlob = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptBlob(data, key));
|
||||
|
||||
/**
|
||||
* A variant of {@link encryptBlob} that returns the result components as base64
|
||||
* strings.
|
||||
*/
|
||||
export const encryptBlobB64 = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptBlobB64(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the thumbnail for a file.
|
||||
*
|
||||
* This is midway variant of {@link encryptBlob} and {@link encryptBlobB64} that
|
||||
* returns the decryption header as a base64 string, but leaves the data
|
||||
* unchanged.
|
||||
*
|
||||
* Use {@link decryptThumbnail} to decrypt the result.
|
||||
*/
|
||||
export const encryptThumbnail = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptThumbnail(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the JSON metadata associated with an Ente object (file, collection or
|
||||
* entity) using the object's key.
|
||||
*
|
||||
* This is a variant of {@link encryptBlobB64} tailored for encrypting any
|
||||
* arbitrary metadata associated with an Ente object. For example, it is used
|
||||
* for encrypting the various metadata fields associated with a file, using that
|
||||
* file's key.
|
||||
*
|
||||
* Instead of raw bytes, it takes as input an arbitrary JSON object which it
|
||||
* encodes into a string, and encrypts that.
|
||||
*
|
||||
* Use {@link decryptMetadataJSON} to decrypt the result.
|
||||
*
|
||||
* @param jsonValue The JSON value to encrypt. This can be an arbitrary JSON
|
||||
* value, but since TypeScript currently doesn't have a native JSON type, it is
|
||||
* typed as {@link unknown}.
|
||||
*
|
||||
* @param key The encryption key.
|
||||
*/
|
||||
export const encryptMetadataJSON_New = (jsonValue: unknown, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._encryptMetadataJSON_New(jsonValue, key)
|
||||
: sharedCryptoWorker().then((w) =>
|
||||
w.encryptMetadataJSON_New(jsonValue, key),
|
||||
);
|
||||
|
||||
/**
|
||||
* Deprecated, use {@link encryptMetadataJSON_New} instead.
|
||||
*/
|
||||
export const encryptMetadataJSON = async (r: {
|
||||
jsonValue: unknown;
|
||||
keyB64: string;
|
||||
}) =>
|
||||
inWorker()
|
||||
? ei._encryptMetadataJSON(r)
|
||||
: sharedCryptoWorker().then((w) => w.encryptMetadataJSON(r));
|
||||
|
||||
/**
|
||||
* Decrypt a box encrypted using {@link encryptBoxB64}.
|
||||
*/
|
||||
export const decryptBox = (box: EncryptedBox, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBox(box, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBox(box, key));
|
||||
|
||||
/**
|
||||
* Variant of {@link decryptBox} that returns the result as a base64 string.
|
||||
*/
|
||||
export const decryptBoxB64 = (box: EncryptedBox, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBoxB64(box, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBoxB64(box, key));
|
||||
|
||||
/**
|
||||
* Decrypt a blob encrypted using either {@link encryptBlob} or
|
||||
* {@link encryptBlobB64}.
|
||||
*/
|
||||
export const decryptBlob = (blob: EncryptedBlob, key: BytesOrB64) =>
|
||||
assertInWorker(ei._decryptBlob(blob, key));
|
||||
|
||||
/**
|
||||
* A variant of {@link decryptBlob} that returns the result as a base64 string.
|
||||
*/
|
||||
export const decryptBlobB64 = (blob: EncryptedBlob, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBlobB64(blob, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBlobB64(blob, key));
|
||||
|
||||
/**
|
||||
* Decrypt the thumbnail encrypted using {@link encryptThumbnail}.
|
||||
*/
|
||||
export const decryptThumbnail = (blob: EncryptedBlob, key: BytesOrB64) =>
|
||||
assertInWorker(ei._decryptThumbnail(blob, key));
|
||||
|
||||
/**
|
||||
* Decrypt the metadata JSON encrypted using {@link encryptMetadataJSON}.
|
||||
*
|
||||
* @returns The decrypted JSON value. Since TypeScript does not have a native
|
||||
* JSON type, we need to return it as an `unknown`.
|
||||
*/
|
||||
export const decryptMetadataJSON_New = (
|
||||
blob: EncryptedBlob,
|
||||
key: BytesOrB64,
|
||||
) =>
|
||||
inWorker()
|
||||
? ei._decryptMetadataJSON_New(blob, key)
|
||||
: sharedCryptoWorker().then((w) =>
|
||||
w.decryptMetadataJSON_New(blob, key),
|
||||
);
|
||||
|
||||
/**
|
||||
* Deprecated, retains the old API.
|
||||
*/
|
||||
export const decryptMetadataJSON = (r: {
|
||||
encryptedDataB64: string;
|
||||
decryptionHeaderB64: string;
|
||||
keyB64: string;
|
||||
}) =>
|
||||
inWorker()
|
||||
? ei._decryptMetadataJSON(r)
|
||||
: sharedCryptoWorker().then((w) => w.decryptMetadataJSON(r));
|
||||
@@ -1,4 +1,54 @@
|
||||
/**
|
||||
* @file Higher level functions that use the ontology of Ente's requirements.
|
||||
*
|
||||
* [Note: Crypto code hierarchy]
|
||||
*
|
||||
* 1. @/base/crypto (Crypto API for our code)
|
||||
* 2. @/base/crypto/libsodium (Lower level wrappers over libsodium)
|
||||
* 3. libsodium-wrappers (JavaScript bindings to libsodium)
|
||||
*
|
||||
* Our cryptography primitives are provided by libsodium, specifically, its
|
||||
* JavaScript bindings ("libsodium-wrappers"). This is the lowest layer.
|
||||
*
|
||||
* Direct usage of "libsodium-wrappers" is restricted to `crypto/libsodium.ts`.
|
||||
* This is the next higher layer. Usually the functions in this file are thin
|
||||
* wrappers over the raw libsodium APIs, with a bit of massaging. They also
|
||||
* ensure that sodium.ready has been called before accessing libsodium's APIs,
|
||||
* thus all the functions it exposes are async.
|
||||
*
|
||||
* The highest layer is this file, `crypto/index.ts`, and the one that our own
|
||||
* code should use. These are usually simple compositions of functionality
|
||||
* exposed by `crypto/libsodium.ts`, the primary difference being that these
|
||||
* functions try to talk in terms of higher-level Ente specific goal we are
|
||||
* trying to accomplish instead of the specific underlying crypto algorithms.
|
||||
*
|
||||
* There is an additional actor in play. Cryptographic operations like
|
||||
* encryption are CPU intensive and would cause the UI to stutter if used
|
||||
* directly on the main thread. To keep the UI smooth, we instead want to run
|
||||
* them in a web worker. However, sometimes we already _are_ running in a web
|
||||
* worker, and delegating to another worker is wasteful.
|
||||
*
|
||||
* To handle both these scenario, the potentially CPU intensive functions in
|
||||
* this file are split into the external API, and the underlying implementation
|
||||
* (denoted by an "_" prefix). To avoid a circular dependency during webpack
|
||||
* imports, we need to keep the implementation functions in a separate file
|
||||
* (`ente-impl.ts`).
|
||||
*
|
||||
* The external API functions check to see if we're already in a web worker, and
|
||||
* if so directly invoke the implementation. Otherwise the call the sibling
|
||||
* function in a shared "crypto" web worker (which then invokes the
|
||||
* implementation function, but this time in the context of a web worker).
|
||||
*
|
||||
* Also, some code (e.g. the uploader) creates it own crypto worker instances,
|
||||
* and thus directly calls the functions in the web worker (it created) instead
|
||||
* of going through this file.
|
||||
*/
|
||||
import { ComlinkWorker } from "@/base/worker/comlink-worker";
|
||||
import { assertionFailed } from "../assert";
|
||||
import { inWorker } from "../env";
|
||||
import * as ei from "./ente-impl";
|
||||
import * as libsodium from "./libsodium";
|
||||
import type { BytesOrB64, EncryptedBlob, EncryptedBox } from "./types";
|
||||
import type { CryptoWorker } from "./worker";
|
||||
|
||||
/**
|
||||
@@ -21,3 +71,182 @@ export const createComlinkCryptoWorker = () =>
|
||||
"crypto",
|
||||
new Worker(new URL("worker.ts", import.meta.url)),
|
||||
);
|
||||
|
||||
/**
|
||||
* Some of the potentially CPU intensive functions below have not yet been
|
||||
* needed on the main thread, and for these we don't have a corresponding
|
||||
* sharedCryptoWorker method.
|
||||
*
|
||||
* This assertion will let us know when we need to implement them. This will
|
||||
* gracefully degrade in production: the functionality will work, just that the
|
||||
* crypto operations will happen on the main thread itself.
|
||||
*/
|
||||
const assertInWorker = <T>(x: T): T => {
|
||||
if (!inWorker()) assertionFailed("Currently only usable in a web worker");
|
||||
return x;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a new randomly generated 256-bit key suitable for use with the *Box
|
||||
* encryption functions.
|
||||
*/
|
||||
export const generateBoxKey = libsodium.generateBoxKey;
|
||||
|
||||
/**
|
||||
* Encrypt the given data, returning a box containing the encrypted data and a
|
||||
* randomly generated nonce that was used during encryption.
|
||||
*
|
||||
* Both the encrypted data and the nonce are returned as base64 strings.
|
||||
*
|
||||
* Use {@link decryptBoxB64} to decrypt the result.
|
||||
*
|
||||
* > The suffix "Box" comes from the fact that it uses the so called secretbox
|
||||
* > APIs provided by libsodium under the hood.
|
||||
* >
|
||||
* > See: [Note: 3 forms of encryption (Box | Blob | Stream)]
|
||||
*/
|
||||
export const encryptBoxB64 = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._encryptBoxB64(data, key)
|
||||
: sharedCryptoWorker().then((w) => w.encryptBoxB64(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the given data, returning a blob containing the encrypted data and a
|
||||
* decryption header.
|
||||
*
|
||||
* This function is usually used to encrypt data associated with an Ente object
|
||||
* (file, collection, entity) using the object's key.
|
||||
*
|
||||
* Use {@link decryptBlob} to decrypt the result.
|
||||
*
|
||||
* > The suffix "Blob" comes from our convention of naming functions that use
|
||||
* > the secretstream APIs in one-shot mode.
|
||||
* >
|
||||
* > See: [Note: 3 forms of encryption (Box | Blob | Stream)]
|
||||
*/
|
||||
export const encryptBlob = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptBlob(data, key));
|
||||
|
||||
/**
|
||||
* A variant of {@link encryptBlob} that returns the result components as base64
|
||||
* strings.
|
||||
*/
|
||||
export const encryptBlobB64 = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptBlobB64(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the thumbnail for a file.
|
||||
*
|
||||
* This is midway variant of {@link encryptBlob} and {@link encryptBlobB64} that
|
||||
* returns the decryption header as a base64 string, but leaves the data
|
||||
* unchanged.
|
||||
*
|
||||
* Use {@link decryptThumbnail} to decrypt the result.
|
||||
*/
|
||||
export const encryptThumbnail = (data: BytesOrB64, key: BytesOrB64) =>
|
||||
assertInWorker(ei._encryptThumbnail(data, key));
|
||||
|
||||
/**
|
||||
* Encrypt the JSON metadata associated with an Ente object (file, collection or
|
||||
* entity) using the object's key.
|
||||
*
|
||||
* This is a variant of {@link encryptBlobB64} tailored for encrypting any
|
||||
* arbitrary metadata associated with an Ente object. For example, it is used
|
||||
* for encrypting the various metadata fields associated with a file, using that
|
||||
* file's key.
|
||||
*
|
||||
* Instead of raw bytes, it takes as input an arbitrary JSON object which it
|
||||
* encodes into a string, and encrypts that.
|
||||
*
|
||||
* Use {@link decryptMetadataJSON} to decrypt the result.
|
||||
*
|
||||
* @param jsonValue The JSON value to encrypt. This can be an arbitrary JSON
|
||||
* value, but since TypeScript currently doesn't have a native JSON type, it is
|
||||
* typed as {@link unknown}.
|
||||
*
|
||||
* @param key The encryption key.
|
||||
*/
|
||||
export const encryptMetadataJSON_New = (jsonValue: unknown, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._encryptMetadataJSON_New(jsonValue, key)
|
||||
: sharedCryptoWorker().then((w) =>
|
||||
w.encryptMetadataJSON_New(jsonValue, key),
|
||||
);
|
||||
|
||||
/**
|
||||
* Deprecated, use {@link encryptMetadataJSON_New} instead.
|
||||
*/
|
||||
export const encryptMetadataJSON = async (r: {
|
||||
jsonValue: unknown;
|
||||
keyB64: string;
|
||||
}) =>
|
||||
inWorker()
|
||||
? ei._encryptMetadataJSON(r)
|
||||
: sharedCryptoWorker().then((w) => w.encryptMetadataJSON(r));
|
||||
|
||||
/**
|
||||
* Decrypt a box encrypted using {@link encryptBoxB64}.
|
||||
*/
|
||||
export const decryptBox = (box: EncryptedBox, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBox(box, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBox(box, key));
|
||||
|
||||
/**
|
||||
* Variant of {@link decryptBox} that returns the result as a base64 string.
|
||||
*/
|
||||
export const decryptBoxB64 = (box: EncryptedBox, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBoxB64(box, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBoxB64(box, key));
|
||||
|
||||
/**
|
||||
* Decrypt a blob encrypted using either {@link encryptBlob} or
|
||||
* {@link encryptBlobB64}.
|
||||
*/
|
||||
export const decryptBlob = (blob: EncryptedBlob, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBlob(blob, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBlob(blob, key));
|
||||
|
||||
/**
|
||||
* A variant of {@link decryptBlob} that returns the result as a base64 string.
|
||||
*/
|
||||
export const decryptBlobB64 = (blob: EncryptedBlob, key: BytesOrB64) =>
|
||||
inWorker()
|
||||
? ei._decryptBlobB64(blob, key)
|
||||
: sharedCryptoWorker().then((w) => w.decryptBlobB64(blob, key));
|
||||
|
||||
/**
|
||||
* Decrypt the thumbnail encrypted using {@link encryptThumbnail}.
|
||||
*/
|
||||
export const decryptThumbnail = (blob: EncryptedBlob, key: BytesOrB64) =>
|
||||
assertInWorker(ei._decryptThumbnail(blob, key));
|
||||
|
||||
/**
|
||||
* Decrypt the metadata JSON encrypted using {@link encryptMetadataJSON}.
|
||||
*
|
||||
* @returns The decrypted JSON value. Since TypeScript does not have a native
|
||||
* JSON type, we need to return it as an `unknown`.
|
||||
*/
|
||||
export const decryptMetadataJSON_New = (
|
||||
blob: EncryptedBlob,
|
||||
key: BytesOrB64,
|
||||
) =>
|
||||
inWorker()
|
||||
? ei._decryptMetadataJSON_New(blob, key)
|
||||
: sharedCryptoWorker().then((w) =>
|
||||
w.decryptMetadataJSON_New(blob, key),
|
||||
);
|
||||
|
||||
/**
|
||||
* Deprecated, retains the old API.
|
||||
*/
|
||||
export const decryptMetadataJSON = (r: {
|
||||
encryptedDataB64: string;
|
||||
decryptionHeaderB64: string;
|
||||
keyB64: string;
|
||||
}) =>
|
||||
inWorker()
|
||||
? ei._decryptMetadataJSON(r)
|
||||
: sharedCryptoWorker().then((w) => w.decryptMetadataJSON(r));
|
||||
|
||||
@@ -128,6 +128,17 @@ export async function fromHex(input: string) {
|
||||
const bytes = async (bob: BytesOrB64) =>
|
||||
typeof bob == "string" ? fromB64(bob) : bob;
|
||||
|
||||
/**
|
||||
* Generate a key for use with the *Box encryption functions.
|
||||
*
|
||||
* This returns a new randomly generated 256-bit key suitable for being used
|
||||
* with libsodium's secretbox APIs.
|
||||
*/
|
||||
export const generateBoxKey = async () => {
|
||||
await sodium.ready;
|
||||
return toB64(sodium.crypto_secretbox_keygen());
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt the given data using libsodium's secretbox APIs, using a randomly
|
||||
* generated nonce.
|
||||
@@ -231,7 +242,7 @@ export const encryptBoxB64 = async (
|
||||
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
|
||||
const encryptedData = sodium.crypto_secretbox_easy(
|
||||
await bytes(data),
|
||||
await bytes(nonce),
|
||||
nonce,
|
||||
await bytes(key),
|
||||
);
|
||||
return {
|
||||
|
||||
@@ -5,7 +5,8 @@ import * as libsodium from "./libsodium";
|
||||
|
||||
/**
|
||||
* A web worker that exposes some of the functions defined in either the Ente
|
||||
* specific layer (crypto/ente.ts) or the libsodium layer (crypto/libsodium.ts).
|
||||
* specific layer (@/base/crypto) or the libsodium layer
|
||||
* (@/base/crypto/libsodium.ts).
|
||||
*
|
||||
* See: [Note: Crypto code hierarchy].
|
||||
*
|
||||
@@ -18,6 +19,7 @@ export class CryptoWorker {
|
||||
encryptMetadataJSON = ei._encryptMetadataJSON;
|
||||
decryptBox = ei._decryptBox;
|
||||
decryptBoxB64 = ei._decryptBoxB64;
|
||||
decryptBlob = ei._decryptBlob;
|
||||
decryptBlobB64 = ei._decryptBlobB64;
|
||||
decryptThumbnail = ei._decryptThumbnail;
|
||||
decryptMetadataJSON_New = ei._decryptMetadataJSON_New;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { decryptBox } from "./crypto/ente";
|
||||
import { decryptBox } from "./crypto";
|
||||
import { toB64 } from "./crypto/libsodium";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { decryptMetadataJSON, encryptMetadataJSON } from "@/base/crypto/ente";
|
||||
import { decryptMetadataJSON, encryptMetadataJSON } from "@/base/crypto";
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { encryptBlobB64 } from "@/base/crypto/ente";
|
||||
import { encryptBlobB64 } from "@/base/crypto";
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import type { EnteFile } from "@/new/photos/types/file";
|
||||
|
||||
@@ -490,8 +490,10 @@ const getMLStatus = async (): Promise<MLStatus> => {
|
||||
const state = await (await worker()).state;
|
||||
if (state == "indexing" || state == "fetching") {
|
||||
phase = state;
|
||||
} else if (state == "init" || indexableCount > 0) {
|
||||
phase = "scheduled";
|
||||
} else {
|
||||
phase = indexableCount > 0 ? "scheduled" : "done";
|
||||
phase = "done";
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { decryptBlob } from "@/base/crypto/ente";
|
||||
import { decryptBlob } from "@/base/crypto";
|
||||
import log from "@/base/log";
|
||||
import type { EnteFile } from "@/new/photos/types/file";
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
|
||||
@@ -43,6 +43,7 @@ import type { CLIPMatches, MLWorkerDelegate } from "./worker-types";
|
||||
/**
|
||||
* A rough hint at what the worker is up to.
|
||||
*
|
||||
* - "init": Worker has been created but hasn't done anything yet.
|
||||
* - "idle": Not doing anything
|
||||
* - "tick": Transitioning to a new state
|
||||
* - "indexing": Indexing
|
||||
@@ -52,7 +53,7 @@ import type { CLIPMatches, MLWorkerDelegate } from "./worker-types";
|
||||
* data for more than 50% of the files that we requested from it in the last
|
||||
* fetch during indexing.
|
||||
*/
|
||||
export type WorkerState = "idle" | "tick" | "indexing" | "fetching";
|
||||
export type WorkerState = "init" | "idle" | "tick" | "indexing" | "fetching";
|
||||
|
||||
const idleDurationStart = 5; /* 5 seconds */
|
||||
const idleDurationMax = 16 * 60; /* 16 minutes */
|
||||
@@ -92,7 +93,7 @@ interface IndexableItem {
|
||||
*/
|
||||
export class MLWorker {
|
||||
/** The last known state of the worker. */
|
||||
public state: WorkerState = "idle";
|
||||
public state: WorkerState = "init";
|
||||
|
||||
private electron: ElectronMLWorker | undefined;
|
||||
private delegate: MLWorkerDelegate | undefined;
|
||||
@@ -138,7 +139,7 @@ export class MLWorker {
|
||||
|
||||
/** Invoked in response to external events. */
|
||||
private wakeUp() {
|
||||
if (this.state == "idle") {
|
||||
if (this.state == "init" || this.state == "idle") {
|
||||
// We are currently paused. Get back to work.
|
||||
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
||||
this.idleTimeout = undefined;
|
||||
@@ -202,7 +203,12 @@ export class MLWorker {
|
||||
|
||||
const liveQ = this.liveQ;
|
||||
this.liveQ = [];
|
||||
this.state = "indexing";
|
||||
|
||||
// Retain the previous state if it was one of the indexing states. This
|
||||
// prevents jumping between "fetching" and "indexing" being shown in the
|
||||
// UI during the initial load.
|
||||
if (this.state != "fetching" && this.state != "indexing")
|
||||
this.state = "indexing";
|
||||
|
||||
// Use the liveQ if present, otherwise get the next batch to backfill.
|
||||
const items = liveQ.length ? liveQ : await this.backfillQ();
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { decryptBlob, decryptBoxB64 } from "@/base/crypto/ente";
|
||||
import {
|
||||
decryptBlob,
|
||||
decryptBoxB64,
|
||||
encryptBoxB64,
|
||||
generateBoxKey,
|
||||
} from "@/base/crypto";
|
||||
import { authenticatedRequestHeaders, ensureOk, HTTPError } from "@/base/http";
|
||||
import { getKV, getKVN, setKV } from "@/base/kv";
|
||||
import { apiURL } from "@/base/origins";
|
||||
@@ -229,6 +234,14 @@ const saveRemoteUserEntityKey = (
|
||||
entityKey: RemoteUserEntityKey,
|
||||
) => setKV(entityKeyKey(type), JSON.stringify(entityKey));
|
||||
|
||||
/**
|
||||
* Generate a new entity key and return it after encrypting it using the user's
|
||||
* master key.
|
||||
*/
|
||||
// TODO: Temporary export to silence lint
|
||||
export const generateEncryptedEntityKey = async () =>
|
||||
encryptBoxB64(await generateBoxKey(), await masterKeyFromSession());
|
||||
|
||||
/**
|
||||
* Decrypt an encrypted entity key using the user's master key.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user