v4 Not working with react-native
LucasBerger opened this issue · 7 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
I was worried this would happen.
This is the same issue reported in #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 #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? brix/crypto-js#259 (comment)
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
);
};
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 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
brix/crypto-js#259 (comment)
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.
Ah, that makes more sense. Thanks for the clarification!
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);