diff --git a/packages/encryption/package.json b/packages/encryption/package.json index 4d35d498f2c3..ab0835efd498 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -5,6 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "dependencies": { + "@dimensiondev/stego-js": "0.11.1-20201027083223-8ab41be", "@masknet/shared-base": "workspace:*", "@msgpack/msgpack": "^2.7.0" } diff --git a/packages/mask/src/extension/background-script/CryptoServices/Steganography.ts b/packages/encryption/src/image-steganography/index.ts similarity index 53% rename from packages/mask/src/extension/background-script/CryptoServices/Steganography.ts rename to packages/encryption/src/image-steganography/index.ts index 792c19166c44..24d796f19516 100644 --- a/packages/mask/src/extension/background-script/CryptoServices/Steganography.ts +++ b/packages/encryption/src/image-steganography/index.ts @@ -1,58 +1,56 @@ +import { GrayscaleAlgorithm } from '@dimensiondev/stego-js/cjs/grayscale.js' +import { TransformAlgorithm } from '@dimensiondev/stego-js/cjs/transform.js' +import { encode, decode } from '@dimensiondev/stego-js/cjs/dom.js' +import type { EncodeOptions } from '@dimensiondev/stego-js/cjs/stego.js' import { omit } from 'lodash-unified' -import { blobToArrayBuffer } from '@dimensiondev/kit' -import { encode, decode } from '@dimensiondev/stego-js/cjs/dom' -import { GrayscaleAlgorithm } from '@dimensiondev/stego-js/cjs/grayscale' -import { TransformAlgorithm } from '@dimensiondev/stego-js/cjs/transform' -import type { EncodeOptions, DecodeOptions } from '@dimensiondev/stego-js/cjs/stego' -import { downloadUrl } from '../../../utils/utils' -import { memoizePromise } from '../../../../utils-pure' -import { getDimension } from '../../../utils/image' -import type { ImageTemplateTypes } from '../../../resources/image-payload' +import { getDimension } from './utils' -type Dimension = { +export { GrayscaleAlgorithm } from '@dimensiondev/stego-js/cjs/grayscale.js' +export type ImageTemplateTypes = 'v2' | 'eth' + +interface Dimension { width: number height: number } - -const dimensionPreset: (Dimension & { +interface Preset extends Dimension { mask: string + deprecated?: string template?: ImageTemplateTypes options?: Partial -})[] = [ - // @deprecated legacy post +} +const dimensionPreset: Preset[] = [ { + deprecated: 'legacy post', width: 1024, height: 1240, - mask: new URL('./SteganographyResources/mask-v1.png', import.meta.url).toString(), + mask: new URL('./masks/mask-v1.png', import.meta.url).toString(), }, - // text { width: 1200, height: 681, template: 'v2', - mask: new URL('./SteganographyResources/mask-v2.png', import.meta.url).toString(), + mask: new URL('./masks/mask-v2.png', import.meta.url).toString(), }, - // lucky drop { width: 1200, height: 680, template: 'eth', - mask: new URL('./SteganographyResources/mask-transparent.png', import.meta.url).toString(), + mask: new URL('./masks/mask-transparent.png', import.meta.url).toString(), options: { cropEdgePixels: true, }, }, - // @deprecated election 2020 { + deprecated: 'event election 2020', width: 1000, height: 558, - mask: new URL('./SteganographyResources/mask-transparent.png', import.meta.url).toString(), + mask: new URL('./masks/mask-transparent.png', import.meta.url).toString(), }, - // @deprecated NFT { + deprecated: 'old NFT', width: 1000, height: 560, - mask: new URL('./SteganographyResources/mask-v4.png', import.meta.url).toString(), + mask: new URL('./masks/mask-v4.png', import.meta.url).toString(), }, ] @@ -71,18 +69,21 @@ const defaultOptions = { const isSameDimension = (dimension: Dimension, otherDimension: Dimension) => dimension.width === otherDimension.width && dimension.height === otherDimension.height -const getMaskBuf = memoizePromise(async (url: string) => blobToArrayBuffer(await downloadUrl(url)), void 0) - -export type EncodeImageOptions = { - template?: ImageTemplateTypes -} & PartialRequired, 'text' | 'pass'> +export interface SteganographyIO { + downloadImage: (url: string) => Promise +} +export type EncodeImageOptions = SteganographyIO & + Partial & + Pick & { + template?: ImageTemplateTypes + } export async function steganographyEncodeImage(buf: ArrayBuffer, options: EncodeImageOptions) { - const { template } = options + const { template, downloadImage } = options const preset = dimensionPreset.find((d) => d.template && d.template === template) if (!preset) throw new Error('Failed to find preset.') return new Uint8Array( - await encode(buf, await getMaskBuf(preset.mask), { + await encode(buf, await downloadImage(preset.mask), { ...defaultOptions, ...preset?.options, ...omit(options, 'template'), @@ -90,13 +91,13 @@ export async function steganographyEncodeImage(buf: ArrayBuffer, options: Encode ) } -export type DecodeImageOptions = PartialRequired, 'pass'> +export type DecodeImageOptions = SteganographyIO & Partial & Pick async function steganographyDecodeImage(buf: ArrayBuffer, options: DecodeImageOptions) { const dimension = getDimension(buf) const preset = dimensionPreset.find((d) => isSameDimension(d, dimension)) if (!preset) return '' - return decode(buf, await getMaskBuf(preset.mask), { + return decode(buf, await options.downloadImage(preset.mask), { ...defaultOptions, transformAlgorithm: TransformAlgorithm.FFT1D, ...options, @@ -104,5 +105,5 @@ async function steganographyDecodeImage(buf: ArrayBuffer, options: DecodeImageOp } export async function steganographyDecodeImageUrl(url: string, options: DecodeImageOptions) { - return steganographyDecodeImage(await blobToArrayBuffer(await downloadUrl(url)), options) + return steganographyDecodeImage(await options.downloadImage(url), options) } diff --git a/packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-transparent.png b/packages/encryption/src/image-steganography/masks/mask-transparent.png similarity index 100% rename from packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-transparent.png rename to packages/encryption/src/image-steganography/masks/mask-transparent.png diff --git a/packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-v1.png b/packages/encryption/src/image-steganography/masks/mask-v1.png similarity index 100% rename from packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-v1.png rename to packages/encryption/src/image-steganography/masks/mask-v1.png diff --git a/packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-v2.png b/packages/encryption/src/image-steganography/masks/mask-v2.png similarity index 100% rename from packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-v2.png rename to packages/encryption/src/image-steganography/masks/mask-v2.png diff --git a/packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-v4.png b/packages/encryption/src/image-steganography/masks/mask-v4.png similarity index 100% rename from packages/mask/src/extension/background-script/CryptoServices/SteganographyResources/mask-v4.png rename to packages/encryption/src/image-steganography/masks/mask-v4.png diff --git a/packages/mask/src/utils/image.ts b/packages/encryption/src/image-steganography/utils.ts similarity index 96% rename from packages/mask/src/utils/image.ts rename to packages/encryption/src/image-steganography/utils.ts index 6a3a3099acc4..e056d0589795 100644 --- a/packages/mask/src/utils/image.ts +++ b/packages/encryption/src/image-steganography/utils.ts @@ -1,6 +1,7 @@ /* eslint-disable no-bitwise */ -import { imgType } from '@dimensiondev/stego-js/cjs/helper' +import { imgType } from '@dimensiondev/stego-js/cjs/helper.js' +/** @internal */ export function getDimension(buf: ArrayBuffer) { const fallback = { width: 0, diff --git a/packages/encryption/src/index.ts b/packages/encryption/src/index.ts index d424133cb760..9b7e28480bb8 100644 --- a/packages/encryption/src/index.ts +++ b/packages/encryption/src/index.ts @@ -22,3 +22,13 @@ export { type DecryptProgress, type DecryptSuccess, } from './encryption' + +export { + type DecodeImageOptions, + type EncodeImageOptions, + type ImageTemplateTypes, + type SteganographyIO, + steganographyDecodeImageUrl, + steganographyEncodeImage, + GrayscaleAlgorithm, +} from './image-steganography' diff --git a/packages/mask/package.json b/packages/mask/package.json index 0a02af264527..d49f6010dc94 100644 --- a/packages/mask/package.json +++ b/packages/mask/package.json @@ -9,7 +9,6 @@ "@dimensiondev/mask-wallet-core": "0.1.0-20211013082857-eb62e5f", "@dimensiondev/metamask-extension-provider": "3.0.6-20210519045409-1835d4d", "@dimensiondev/snapshot.js": "0.2.0", - "@dimensiondev/stego-js": "=0.11.1-20201027083223-8ab41be", "@ethersproject/abi": "^5.4.0", "@ethersproject/address": "^5.0.4", "@ethersproject/providers": "^5.0.9", diff --git a/packages/mask/src/components/CompositionDialog/useSubmit.ts b/packages/mask/src/components/CompositionDialog/useSubmit.ts index b6fb958abd91..6f6c7ee6b9ad 100644 --- a/packages/mask/src/components/CompositionDialog/useSubmit.ts +++ b/packages/mask/src/components/CompositionDialog/useSubmit.ts @@ -2,7 +2,7 @@ import { ProfileIdentifier } from '@masknet/shared-base' import { useCallback } from 'react' import Services from '../../extension/service' import { RedPacketMetadataReader } from '../../plugins/RedPacket/SNSAdaptor/helpers' -import type { ImageTemplateTypes } from '../../resources/image-payload' +import type { ImageTemplateTypes } from '@masknet/encryption' import { activatedSocialNetworkUI, globalUIState } from '../../social-network' import { isTwitter } from '../../social-network-adaptor/twitter.com/base' import { useI18N } from '../../utils' diff --git a/packages/mask/src/components/InjectedComponents/SteganographyTextPayload.ts b/packages/mask/src/components/InjectedComponents/SteganographyTextPayload.ts index 0f8885a7343a..b509473db0c8 100644 --- a/packages/mask/src/components/InjectedComponents/SteganographyTextPayload.ts +++ b/packages/mask/src/components/InjectedComponents/SteganographyTextPayload.ts @@ -1,7 +1,7 @@ import { blobToArrayBuffer } from '@dimensiondev/kit' -import { GrayscaleAlgorithm } from '@dimensiondev/stego-js/esm/grayscale' +import { GrayscaleAlgorithm, type ImageTemplateTypes } from '@masknet/encryption' import Services from '../../extension/service' -import { ImageTemplateTypes, ImagePayloadURLs } from '../../resources/image-payload' +import { ImagePayloadURLs } from '../../resources/image-payload' import { activatedSocialNetworkUI } from '../../social-network' import { downloadUrl } from '../../utils/utils' diff --git a/packages/mask/src/extension/background-script/CryptoService.ts b/packages/mask/src/extension/background-script/CryptoService.ts index a8f57605a767..5dbe116ec82d 100644 --- a/packages/mask/src/extension/background-script/CryptoService.ts +++ b/packages/mask/src/extension/background-script/CryptoService.ts @@ -1,4 +1,6 @@ import { assertEnvironment, Environment } from '@dimensiondev/holoflows-kit' +import { steganographyEncodeImage as __steganographyEncodeImage, EncodeImageOptions } from '@masknet/encryption' +import { steganographyDownloadImage } from './CryptoServices/utils' assertEnvironment(Environment.ManifestBackground) export { encryptComment, decryptComment } from '../../crypto/crypto-alpha-40' @@ -6,7 +8,10 @@ export { encryptTo, publishPostAESKey } from './CryptoServices/encryptTo' export { appendShareTarget } from './CryptoServices/appendShareTarget' export { getPartialSharedListOfPost } from './CryptoServices/getPartialSharedListOfPost' export { verifyOthersProve } from './CryptoServices/verifyOthersProve' -export { steganographyEncodeImage } from './CryptoServices/Steganography' + +export function steganographyEncodeImage(buf: ArrayBuffer, options: Omit) { + return __steganographyEncodeImage(buf, { ...options, downloadImage: steganographyDownloadImage }) +} import type { debugShowAllPossibleHashForPost as orig } from './CryptoServices/debugShowAllPossibleHashForPost' // This module requires lazy loading otherwise it will load gun diff --git a/packages/mask/src/extension/background-script/CryptoServices/decryptFrom.ts b/packages/mask/src/extension/background-script/CryptoServices/decryptFrom.ts index 2209e58010c1..a8c2e97d5e4e 100644 --- a/packages/mask/src/extension/background-script/CryptoServices/decryptFrom.ts +++ b/packages/mask/src/extension/background-script/CryptoServices/decryptFrom.ts @@ -14,7 +14,7 @@ import { verifyOthersProve } from './verifyOthersProve' import { publicSharedAESKey } from '../../../crypto/crypto-alpha-38' import { DecryptFailedReason } from '../../../utils/constants' import { asyncIteratorWithResult, memorizeAsyncGenerator } from '../../../utils/type-transform/asyncIteratorHelpers' -import { steganographyDecodeImageUrl } from './Steganography' +import { steganographyDecodeImageUrl } from '@masknet/encryption' import type { TypedMessage, AESJsonWebKey, Payload } from '@masknet/shared-base' import stringify from 'json-stable-stringify' import type { SharedAESKeyGun2 } from '../../../network/gun/version.2' @@ -22,6 +22,7 @@ import { MaskMessages } from '../../../utils/messages' import { GunAPI } from '../../../network/gun' import { Err, Ok, Result } from 'ts-results' import { decodeTextPayloadWorker } from '../../../social-network/utils/text-payload-worker' +import { steganographyDownloadImage } from './utils' type Progress = ( | { progress: 'finding_person_public_key' | 'finding_post_key' | 'init' | 'decode_post' } @@ -316,6 +317,7 @@ async function* decryptFromImageUrlWithProgress_raw( yield makeProgress('decode_post', true) const post = await steganographyDecodeImageUrl(url, { pass: author.toText(), + downloadImage: steganographyDownloadImage, }) if (!post.startsWith('🎼') && !/https:\/\/.+\..+\/(\?PostData_v\d=)?%20(.+)%40/.test(post)) return makeError(i18n.t('service_decode_image_payload_failed'), true) diff --git a/packages/mask/src/extension/background-script/CryptoServices/utils.ts b/packages/mask/src/extension/background-script/CryptoServices/utils.ts new file mode 100644 index 000000000000..6d690e5f4b88 --- /dev/null +++ b/packages/mask/src/extension/background-script/CryptoServices/utils.ts @@ -0,0 +1,7 @@ +import { blobToArrayBuffer, memoizePromise } from '@dimensiondev/kit' +import { downloadUrl } from '../../../utils/utils' + +export const steganographyDownloadImage = memoizePromise( + async (url: string) => blobToArrayBuffer(await downloadUrl(url)), + void 0, +) diff --git a/packages/mask/src/resources/image-payload/index.ts b/packages/mask/src/resources/image-payload/index.ts index e9387b091c57..5657f8a46107 100644 --- a/packages/mask/src/resources/image-payload/index.ts +++ b/packages/mask/src/resources/image-payload/index.ts @@ -1,4 +1,5 @@ -export type ImageTemplateTypes = 'v2' | 'eth' +import type { ImageTemplateTypes } from '@masknet/encryption' + export const ImagePayloadURLs: Readonly> = { v2: new URL('./normal/payload-v2.png', import.meta.url).toString(), eth: new URL('./wallet/payload-eth.png', import.meta.url).toString(), diff --git a/packages/mask/src/social-network-adaptor/facebook.com/ui-provider.ts b/packages/mask/src/social-network-adaptor/facebook.com/ui-provider.ts index fa7904a01bb8..b821b387c29e 100644 --- a/packages/mask/src/social-network-adaptor/facebook.com/ui-provider.ts +++ b/packages/mask/src/social-network-adaptor/facebook.com/ui-provider.ts @@ -18,7 +18,7 @@ import { PostProviderFacebook } from './collecting/posts' import { pasteImageToCompositionDefault } from '../../social-network/defaults/automation/AttachImageToComposition' import { injectPageInspectorDefault } from '../../social-network/defaults/inject/PageInspector' import { createTaskStartSetupGuideDefault } from '../../social-network/defaults/inject/StartSetupGuide' -import { GrayscaleAlgorithm } from '@dimensiondev/stego-js/esm/grayscale' +import { GrayscaleAlgorithm } from '@masknet/encryption' import { PaletteModeProviderFacebook, useThemeFacebookVariant } from './customization/custom' import { unreachable } from '@dimensiondev/kit' import { makeStyles } from '@masknet/theme' diff --git a/packages/mask/src/social-network/types.ts b/packages/mask/src/social-network/types.ts index aee452c846e7..8b0b81f954b9 100644 --- a/packages/mask/src/social-network/types.ts +++ b/packages/mask/src/social-network/types.ts @@ -12,7 +12,7 @@ import type { PaletteMode, Theme } from '@mui/material' import type { InjectedDialogClassKey, InjectedDialogProps } from '../components/shared/InjectedDialog' import type { Profile } from '../database' import type { PostInfo } from './PostInfo' -import type { GrayscaleAlgorithm } from '@dimensiondev/stego-js/umd/grayscale' +import type { GrayscaleAlgorithm } from '@masknet/encryption' import type { createSNSAdaptorSpecializedPostContext } from './utils/create-post-context' import type { Subscription } from 'use-subscription' diff --git a/packages/mask/src/utils/index.ts b/packages/mask/src/utils/index.ts index 780d335d5686..c7f0facb60e6 100644 --- a/packages/mask/src/utils/index.ts +++ b/packages/mask/src/utils/index.ts @@ -10,7 +10,6 @@ export * from './constants' export * from './dom' export * from './getTextUILength' export * from './i18n-next-ui' -export * from './image' export * from './messages' export * from './permissions' export * from './createNormalReactRoot' diff --git a/packages/scripts/src/codegen/resource-files.ts b/packages/scripts/src/codegen/resource-files.ts index 0ba7a058e2b6..b387890d2105 100644 --- a/packages/scripts/src/codegen/resource-files.ts +++ b/packages/scripts/src/codegen/resource-files.ts @@ -4,16 +4,19 @@ import { src, dest, watch, parallel, TaskFunction } from 'gulp' const Shared = join(__dirname, '../../../shared/') const Flow = join(__dirname, '../../../plugins/Flow') +const Encryption = join(__dirname, '../../../encryption') const FileService = join(__dirname, '../../../plugins/FileService') const CopyFileService = () => src('./src/**/*.png', { cwd: FileService }).pipe(dest('./dist', { cwd: FileService })) const CopyShared = () => src('./src/**/*.png', { cwd: Shared }).pipe(dest('./dist', { cwd: Shared })) const CopyFlow = () => src('./src/**/*.png', { cwd: Flow }).pipe(dest('./dist', { cwd: Flow })) +const CopyEncryption = () => src('./src/**/*.png', { cwd: Encryption }).pipe(dest('./dist', { cwd: Encryption })) -export const resourceCopy: TaskFunction = parallel(CopyShared, CopyFlow, CopyFileService) +export const resourceCopy: TaskFunction = parallel(CopyShared, CopyFlow, CopyFileService, CopyEncryption) export function resourceCopyWatch() { watch('./src/**/*.png', { cwd: FileService }, CopyFileService) watch('./src/**/*.png', { cwd: Flow }, CopyFlow) + watch('./src/**/*.png', { cwd: Encryption }, CopyEncryption) return watch('./src/**/*.png', { cwd: Shared }, CopyShared) } watchTask(resourceCopy, resourceCopyWatch, 'resource-copy', 'Copy resources') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8c04a0a0856..3de41c5ae0ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -237,9 +237,11 @@ importers: packages/encryption: specifiers: + '@dimensiondev/stego-js': 0.11.1-20201027083223-8ab41be '@masknet/shared-base': workspace:* '@msgpack/msgpack': ^2.7.0 dependencies: + '@dimensiondev/stego-js': 0.11.1-20201027083223-8ab41be '@masknet/shared-base': link:../shared-base '@msgpack/msgpack': 2.7.1 @@ -277,7 +279,6 @@ importers: '@dimensiondev/mask-wallet-core': 0.1.0-20211013082857-eb62e5f '@dimensiondev/metamask-extension-provider': 3.0.6-20210519045409-1835d4d '@dimensiondev/snapshot.js': 0.2.0 - '@dimensiondev/stego-js': '=0.11.1-20201027083223-8ab41be' '@dimensiondev/webextension-shim': 0.0.3-20210823035705-4257e12 '@ethersproject/abi': ^5.4.0 '@ethersproject/address': ^5.0.4 @@ -414,7 +415,6 @@ importers: '@dimensiondev/mask-wallet-core': 0.1.0-20211013082857-eb62e5f_protobufjs@6.11.2 '@dimensiondev/metamask-extension-provider': 3.0.6-20210519045409-1835d4d '@dimensiondev/snapshot.js': 0.2.0 - '@dimensiondev/stego-js': 0.11.1-20201027083223-8ab41be '@ethersproject/abi': 5.4.0 '@ethersproject/address': 5.4.0 '@ethersproject/providers': 5.4.2 @@ -12878,7 +12878,7 @@ packages: is-shared-array-buffer: 1.0.1 is-string: 1.0.7 is-weakref: 1.0.2 - object-inspect: 1.11.1 + object-inspect: 1.12.0 object-keys: 1.1.1 object.assign: 4.1.2 string.prototype.trimend: 1.0.4 @@ -18676,10 +18676,10 @@ packages: /object-inspect/1.11.1: resolution: {integrity: sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==} + dev: false /object-inspect/1.12.0: resolution: {integrity: sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==} - dev: true /object-is/1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} @@ -21448,7 +21448,7 @@ packages: dependencies: call-bind: 1.0.2 get-intrinsic: 1.1.1 - object-inspect: 1.11.1 + object-inspect: 1.12.0 /signal-exit/3.0.3: resolution: {integrity: sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==}