[web] Remove node buffer dependency from base 58 conversion and cleanup (#4438)

This commit is contained in:
Manav Rathi
2024-12-18 18:54:27 +05:30
committed by GitHub
12 changed files with 158 additions and 100 deletions

View File

@@ -11,7 +11,6 @@
"@ente/shared": "*",
"@stripe/stripe-js": "^1.13.2",
"bip39": "^3.0.4",
"bs58": "^5.0.0",
"chrono-node": "^2.7.6",
"debounce": "^2.1.1",
"exifreader": "^4",
@@ -36,8 +35,8 @@
"devDependencies": {
"@/build-config": "*",
"@next/bundle-analyzer": "^14.1",
"@types/bs58": "^4.0.1",
"@types/leaflet": "^1.9",
"@types/node": "^20",
"@types/photoswipe": "^4.1.1",
"@types/react-virtualized-auto-sizer": "^1.0",
"@types/react-window": "^1.8.8"

View File

@@ -10,6 +10,7 @@ import { Titlebar } from "@/base/components/Titlebar";
import { useModalVisibility } from "@/base/components/utils/modal";
import { sharedCryptoWorker } from "@/base/crypto";
import log from "@/base/log";
import { appendCollectionKeyToShareURL } from "@/gallery/services/share";
import type {
Collection,
PublicURL,
@@ -65,10 +66,7 @@ import {
unshareCollection,
updateShareableURL,
} from "services/collectionService";
import {
appendCollectionKeyToShareURL,
getDeviceLimitOptions,
} from "utils/collection";
import { getDeviceLimitOptions } from "utils/collection";
import * as Yup from "yup";
interface CollectionShareProps {
@@ -1224,12 +1222,11 @@ const PublicShare: React.FC<PublicShareProps> = ({
}, [collection]);
useEffect(() => {
if (publicShareProp) {
const url = appendCollectionKeyToShareURL(
if (publicShareProp?.url) {
appendCollectionKeyToShareURL(
publicShareProp.url,
collection.key,
);
setPublicShareUrl(url);
).then((url) => setPublicShareUrl(url));
} else {
setPublicShareUrl(null);
}

View File

@@ -7,10 +7,10 @@ import {
useIsSmallWidth,
useIsTouchscreen,
} from "@/base/components/utils/hooks";
import { sharedCryptoWorker } from "@/base/crypto";
import { isHTTP401Error, PublicAlbumsCredentials } from "@/base/http";
import log from "@/base/log";
import { downloadManager } from "@/gallery/services/download";
import { extractCollectionKeyFromShareURL } from "@/gallery/services/share";
import { updateShouldDisableCFUploadProxy } from "@/gallery/services/upload";
import type { Collection } from "@/media/collection";
import { type EnteFile, mergeMetadata } from "@/media/file";
@@ -47,7 +47,6 @@ import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import type { ButtonProps, IconButtonProps } from "@mui/material";
import { Box, Button, IconButton, Stack, styled, Tooltip } from "@mui/material";
import Typography from "@mui/material/Typography";
import bs58 from "bs58";
import {
FilesDownloadProgress,
FilesDownloadProgressAttributes,
@@ -222,12 +221,10 @@ export default function PublicCollectionGallery() {
const main = async () => {
let redirectingToWebsite = false;
try {
const cryptoWorker = await sharedCryptoWorker();
url.current = window.location.href;
const currentURL = new URL(url.current);
const t = currentURL.searchParams.get("t");
const ck = currentURL.hash.slice(1);
const ck = await extractCollectionKeyFromShareURL(currentURL);
if (!t && !ck) {
window.location.href = "https://ente.io";
redirectingToWebsite = true;
@@ -235,11 +232,7 @@ export default function PublicCollectionGallery() {
if (!t || !ck) {
return;
}
const dck =
ck.length < 50
? await cryptoWorker.toB64(bs58.decode(ck))
: await cryptoWorker.fromHex(ck);
collectionKey.current = dck;
collectionKey.current = ck;
url.current = window.location.href;
const localCollection = await getLocalPublicCollection(
collectionKey.current,

View File

@@ -22,7 +22,6 @@ import { updateMagicMetadata } from "@/new/photos/services/magic-metadata";
import { safeDirectoryName } from "@/new/photos/utils/native-fs";
import { LS_KEYS, getData } from "@ente/shared/storage/localStorage";
import type { User } from "@ente/shared/user/types";
import bs58 from "bs58";
import { t } from "i18next";
import {
addToCollection,
@@ -177,21 +176,6 @@ async function createCollectionDownloadFolder(
return collectionDownloadPath;
}
export function appendCollectionKeyToShareURL(
url: string,
collectionKey: string,
) {
if (!url) {
return null;
}
const sharableURL = new URL(url);
const bytes = Buffer.from(collectionKey, "base64");
sharableURL.hash = bs58.encode(bytes);
return sharableURL.href;
}
const _intSelectOption = (i: number) => {
const label = i === 0 ? t("NO_DEVICE_LIMIT") : i.toString();
return { label, value: i };

View File

@@ -160,6 +160,9 @@ For more details, see [translations.md](translations.md).
identifiers. For one particular use case, we also need
[uuid](https://github.com/uuidjs/uuid) for UUID v4 generation.
- [bs58](https://github.com/cryptocoinjs/bs58) is used for base-58 conversion
(used for encoding the collection key to use as the hash in the share URL).
- [debounce](https://github.com/sindresorhus/debounce) and its
promise-supporting sibling
[pDebounce](https://github.com/sindresorhus/p-debounce) are used for

View File

@@ -2,6 +2,16 @@
import * as libsodium from "./libsodium";
import type { BytesOrB64, EncryptedBlob, EncryptedFile } from "./types";
export const _toB64 = libsodium.toB64;
export const _toB64URLSafe = libsodium.toB64URLSafe;
export const _fromB64 = libsodium.fromB64;
export const _toHex = libsodium.toHex;
export const _fromHex = libsodium.fromHex;
export const _generateBoxKey = libsodium.generateBoxKey;
export const _generateBlobOrStreamKey = libsodium.generateBlobOrStreamKey;

View File

@@ -98,6 +98,44 @@ const assertInWorker = <T>(x: T): T => {
return x;
};
/**
* Convert bytes ({@link Uint8Array}) to a base64 string.
*/
export const toB64 = (bytes: Uint8Array) =>
inWorker() ? ei._toB64(bytes) : sharedWorker().then((w) => w.toB64(bytes));
/**
* URL safe variant of {@link toB64}.
*/
export const toB64URLSafe = (bytes: Uint8Array) =>
inWorker()
? ei._toB64URLSafe(bytes)
: sharedWorker().then((w) => w.toB64URLSafe(bytes));
/**
* Convert a base64 string to bytes ({@link Uint8Array}).
*/
export const fromB64 = (b64String: string) =>
inWorker()
? ei._fromB64(b64String)
: sharedWorker().then((w) => w.fromB64(b64String));
/**
* Convert a base64 string to the hex representation of the underlying bytes.
*/
export const toHex = (b64String: string) =>
inWorker()
? ei._toHex(b64String)
: sharedWorker().then((w) => w.toHex(b64String));
/**
* Convert a hex string to the base64 representation of the underlying bytes.
*/
export const fromHex = (hexString: string) =>
inWorker()
? ei._fromHex(hexString)
: sharedWorker().then((w) => w.fromHex(hexString));
/**
* Return a new randomly generated 256-bit key (as a base64 string) suitable for
* use with the *Box encryption functions.

View File

@@ -100,15 +100,27 @@ export const fromB64URLSafeNoPaddingString = async (input: string) => {
return sodium.to_string(await fromB64URLSafeNoPadding(input));
};
export async function toHex(input: string) {
/**
* Convert a base64 string to the hex representation of the bytes that the base
* 64 string encodes.
*
* Use {@link fromHex} to go back.
*/
export const toHex = async (input: string) => {
await sodium.ready;
return sodium.to_hex(await fromB64(input));
}
};
export async function fromHex(input: string) {
/**
* Convert a hex string to the base 64 representation of the bytes that the hex
* string encodes.
*
* This is the inverse of {@link toHex}.
*/
export const fromHex = async (input: string) => {
await sodium.ready;
return await toB64(sodium.from_hex(input));
}
};
/**
* If the provided {@link bob} ("Bytes or B64 string") is already a

View File

@@ -13,6 +13,11 @@ import * as libsodium from "./libsodium";
* Note: Keep these methods logic free. They are meant to be trivial proxies.
*/
export class CryptoWorker {
toB64 = ei._toB64;
toB64URLSafe = ei._toB64URLSafe;
fromB64 = ei._fromB64;
toHex = ei._toHex;
fromHex = ei._fromHex;
generateBoxKey = ei._generateBoxKey;
generateBlobOrStreamKey = ei._generateBlobOrStreamKey;
encryptBoxB64 = ei._encryptBoxB64;
@@ -90,26 +95,6 @@ export class CryptoWorker {
) {
return libsodium.generateSubKey(key, subKeyLength, subKeyID, context);
}
async toB64(data: Uint8Array) {
return libsodium.toB64(data);
}
async toB64URLSafe(data: Uint8Array) {
return libsodium.toB64URLSafe(data);
}
async fromB64(string: string) {
return libsodium.fromB64(string);
}
async toHex(string: string) {
return libsodium.toHex(string);
}
async fromHex(string: string) {
return libsodium.fromHex(string);
}
}
expose(CryptoWorker);

View File

@@ -3,7 +3,8 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@ffmpeg/ffmpeg": "^0.12.10"
"@ffmpeg/ffmpeg": "^0.12.10",
"bs58": "^6.0.0"
},
"devDependencies": {
"@/build-config": "*"

View File

@@ -0,0 +1,46 @@
import { fromB64, fromHex, toB64 } from "@/base/crypto";
import bs58 from "bs58";
/**
* Add the collection key as the hash to the given URL.
*
* The hash fragment is a client side component and is not accessible to remote
* servers. The collection key is base-58 encoded and set as the hash in public
* URLs of shared albums.
*
* Use {@link extractCollectionKeyFromShareURL} to get back the collection key.
* @param url The base URL for the public album.
* @param collectionKey The base-64 encoded string representation of the
* collection key for the public album.
*
* @returns A URL that includes the base-58 encoded collection key as the hash
* fragment.
*/
export const appendCollectionKeyToShareURL = async (
url: string,
collectionKey: string,
) => {
const sharableURL = new URL(url);
const bytes = await fromB64(collectionKey);
sharableURL.hash = bs58.encode(bytes);
return sharableURL.href;
};
/**
* Extract the collection key from a public URL.
*
* This is the inverse of {@link appendCollectionKeyToShareURL}, returning the
* base 64 string representation of the collection key.
*
* collection key (bytes)
* => appendCollectionKeytoShareURL (base 64 => base 58)
* => URL hash => extractCollectionKeyBytesFromShareURL (base 58 => base 64)
* => collection key (bytes).
*/
export const extractCollectionKeyFromShareURL = async (url: URL) => {
const ck = url.hash.slice(1);
return ck.length < 50 ? await toB64(bs58.decode(ck)) : await fromHex(ck);
};

View File

@@ -234,7 +234,7 @@
source-map "^0.5.7"
stylis "4.2.0"
"@emotion/cache@11.13.1", "@emotion/cache@^11.11.0", "@emotion/cache@^11.13.5", "@emotion/cache@^11.4.0":
"@emotion/cache@11.13.1", "@emotion/cache@^11.11.0", "@emotion/cache@^11.14.0", "@emotion/cache@^11.4.0":
version "11.13.1"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7"
integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==
@@ -263,15 +263,15 @@
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
"@emotion/react@11.13.3", "@emotion/react@^11.13.3", "@emotion/react@^11.8.1":
version "11.13.5"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.5.tgz#fc818ff5b13424f86501ba4d0740f343ae20b8d9"
integrity sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==
version "11.14.0"
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d"
integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==
dependencies:
"@babel/runtime" "^7.18.3"
"@emotion/babel-plugin" "^11.13.5"
"@emotion/cache" "^11.13.5"
"@emotion/cache" "^11.14.0"
"@emotion/serialize" "^1.3.3"
"@emotion/use-insertion-effect-with-fallbacks" "^1.1.0"
"@emotion/use-insertion-effect-with-fallbacks" "^1.2.0"
"@emotion/utils" "^1.4.2"
"@emotion/weak-memoize" "^0.4.0"
hoist-non-react-statics "^3.3.1"
@@ -325,6 +325,11 @@
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf"
integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==
"@emotion/use-insertion-effect-with-fallbacks@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz#8a8cb77b590e09affb960f4ff1e9a89e532738bf"
integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==
"@emotion/utils@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.0.tgz#262f1d02aaedb2ec91c83a0955dd47822ad5fbdd"
@@ -947,14 +952,6 @@
dependencies:
"@babel/types" "^7.20.7"
"@types/bs58@^4.0.1":
version "4.0.4"
resolved "https://registry.yarnpkg.com/@types/bs58/-/bs58-4.0.4.tgz#49fbcb0c7db5f7cea26f0e0f61dc4a41a2445aab"
integrity sha512-0IEpMFXXQi2zXaXl9GJ3sRwQo0uEkD+yFOv+FnAU5lkPtcu6h61xb7jc2CFPEZ5BUOaiP13ThuGc9HD4R8lR5g==
dependencies:
"@types/node" "*"
base-x "^3.0.6"
"@types/chromecast-caf-receiver@^6.0.17":
version "6.0.17"
resolved "https://registry.yarnpkg.com/@types/chromecast-caf-receiver/-/chromecast-caf-receiver-6.0.17.tgz#c54bb6416ed07b78694db6e953be7ca966d09f06"
@@ -1012,10 +1009,10 @@
resolved "https://registry.yarnpkg.com/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.14.tgz#f688f8d44e46ed61c401f82ff757581655fbcc42"
integrity sha512-5Kv68fXuXK0iDuUir1WPGw2R9fOZUlYlSAa0ztMcL0s0BfIDTqg9GXz8K30VJpPP3sxWhbolnQma2x+/TfkzDQ==
"@types/node@*":
version "22.5.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8"
integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==
"@types/node@^20":
version "20.17.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.10.tgz#3f7166190aece19a0d1d364d75c8b0b5778c1e18"
integrity sha512-/jrvh5h6NXhEauFFexRin69nA0uHJ5gwk4iDivp/DeoEua3uwCUto6PC86IpRITBOs4+6i2I56K5x5b6WYGXHA==
dependencies:
undici-types "~6.19.2"
@@ -1345,17 +1342,10 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base-x@^3.0.6:
version "3.0.10"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75"
integrity sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==
dependencies:
safe-buffer "^5.0.1"
base-x@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a"
integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==
base-x@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b"
integrity sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==
bip39@^3.0.4:
version "3.1.0"
@@ -1396,12 +1386,12 @@ browserslist@^4.23.1:
node-releases "^2.0.18"
update-browserslist-db "^1.1.0"
bs58@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279"
integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==
bs58@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8"
integrity sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==
dependencies:
base-x "^4.0.0"
base-x "^5.0.0"
busboy@1.6.0:
version "1.6.0"
@@ -3470,16 +3460,16 @@ safe-array-concat@^1.1.2:
has-symbols "^1.0.3"
isarray "^2.0.5"
safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-regex-test@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377"