redux-persist-transform-encrypt icon indicating copy to clipboard operation
redux-persist-transform-encrypt copied to clipboard

v4 Not working with react-native

Open LucasBerger opened this issue 3 years ago • 1 comments

When using version 4.0.0 of this library there are errors when starting the app:

Error: redux-persist-transform-encrypt: Expected outbound state to be a string. and Error: Native crypto module could not be used to get secure random number.

Downgrading to 3.x solves these issues

LucasBerger avatar Jul 31 '22 14:07 LucasBerger

I was worried this would happen.

This is the same issue reported in https://github.com/maxdeviant/redux-persist-transform-encrypt/issues/49. The "solution" at the time was to downgrade crypto-js to a version that did not depend on Node's built-in crypto.

However, in v4 we upgraded crypto-js to fix some security issues in the previous version (requested in https://github.com/maxdeviant/redux-persist-transform-encrypt/issues/57).

I anticipated that upgrading crypto-js would break React Native usage, which is why I opted for the major version bump.

Could you see if this workaround listed on the crypto-js repo solves the issue? https://github.com/brix/crypto-js/pull/259#issuecomment-588201011

maxdeviant avatar Jul 31 '22 19:07 maxdeviant

I've encountered the same issue. Downgrading is not an options as I was looking to encrypt only partially the redux store, and this is available only on v4.0.0.

What I have done on the other hand is reimplemented the encrypt logic in another file. The only thing I've changed is removed the onError from interface of EncryptTransformConfig.

These are the packages

"react-native": "0.68.0",
"crypto-js": "^4.1.1",
"json-stringify-safe": "^5.0.1",
"redux-persist": "^6.0.0",

This is the implementation - which is pretty much copy paste.

import * as Aes from "crypto-js/aes";
import * as CryptoJsCore from "crypto-js/core";
import stringify from "json-stringify-safe";
import { createTransform } from "redux-persist";
import type { TransformConfig } from "redux-persist/lib/createTransform";

export interface EncryptTransformConfig {
    secretKey: string;
}

const makeError = (message: string) => new Error(`redux-encryption-err: ${message}`);

export const encryptStore = <HSS, S = any, RS = any>(
    config: EncryptTransformConfig,
    transformConfig?: TransformConfig
) => {
    if (typeof config === "undefined") {
        throw makeError("No configuration provided.");
    }

    const { secretKey } = config;
    if (!secretKey) {
        throw makeError("No secret key provided.");
    }

    const onError = console.warn;

    return createTransform<HSS, string, S, RS>(
        (inboundState, _key) => Aes.encrypt(stringify(inboundState), secretKey).toString(),
        (outboundState, _key) => {
            if (typeof outboundState !== "string") {
                return onError(makeError("Expected outbound state to be a string."));
            }

            try {
                const decryptedString = Aes.decrypt(outboundState, secretKey).toString(CryptoJsCore.enc.Utf8);
                if (!decryptedString) {
                    throw new Error("Decrypted string is empty.");
                }

                try {
                    return JSON.parse(decryptedString);
                } catch {
                    return onError(makeError("Failed to parse state as JSON."));
                }
            } catch {
                return onError(
                    makeError("Could not decrypt state. Please verify that you are using the correct secret key.")
                );
            }
        },
        transformConfig
    );
};

langarus avatar Dec 13 '22 17:12 langarus

Apologies, I'm not sure I'm following how removing the onError from the EncryptTransformConfig resolved the issue.

The error Error: Native crypto module could not be used to get secure random number. seems to stem from React Native not supporting the native crypto package.

maxdeviant avatar Dec 13 '22 17:12 maxdeviant

@maxdeviant maybe it's not clear. That is just a customisation I've done. It has nothing to do fixing the issue.

I've replicated the logic locally so I have more control over it. Removing the onError was just for me.

Also I've done this as well https://github.com/brix/crypto-js/pull/259#issuecomment-799973769

Basically you have to install react-native-get-random-values run pod install and add this import before the crypto-js import (I put in the root index.tsx file), and it seems to work. Now if I comment that import it throws the error, but leaving it uncommented fixes it.

langarus avatar Dec 15 '22 09:12 langarus

Ah, that makes more sense. Thanks for the clarification!

maxdeviant avatar Dec 19 '22 15:12 maxdeviant

Downgrading is not an options as I was looking to encrypt only partially the redux store, and this is available only on v4.0.0.

Why do you say this @langarus ? I'm using 3.0.1 and am able to encrypt only part of the store. Maybe I'm missing something. I'm using a nested persist like this:

const rootPersistConfig = {
  key: 'root',
  storage: AsyncStorage,
  blacklist: ['encryptedFields'],
};

const encryptedPersistConfig = {
  key: 'encryptedFields',
  storage: AsyncStorage,
  transforms: [
    encryptTransform({
      secretKey: 'my-super-secret-key',
      onError(error) {},
    }),
  ],
};

const rootReducer = combineReducers({
  offices: officesReducer,
  user: userReducer,
  encryptedFields: persistReducer(
    encryptedPersistConfig,
    encryptedFieldsReducer,
  ),
});

const persistedReducer = persistReducer(rootPersistConfig, rootReducer);

jackkrone avatar Jun 16 '23 17:06 jackkrone

Hi Team

I am also facing the same issue.

I installed the library react-native-get-random-values and import in store.js and sync.js file but still facing the same issue.

simulator_screenshot_E48E912A-77E0-4E04-9ACA-821E73EE047A

dipanshujindaladt avatar Nov 29 '23 00:11 dipanshujindaladt