Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

redesigned the framework to make it less vulnerable to native exceptions. #951

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
300e0fa
improve the code with the help of new try-catch
radetsky Sep 2, 2022
8d2eb52
integrate isBase64 into code
radetsky Sep 15, 2022
a47fad8
review Obj-C code to make all try-catch and error checks
radetsky Sep 16, 2022
9a27632
fix an errors
radetsky Sep 16, 2022
241e0ac
review Java code
radetsky Sep 21, 2022
9907cb5
review Obj-C code
radetsky Sep 21, 2022
ecb3457
review Javascript code
radetsky Sep 21, 2022
0d9de20
moved old example and add ThemisTest for RN app
radetsky Sep 22, 2022
9c8377f
new Android Studio support file
radetsky Sep 22, 2022
90a0401
failed/passed fixes in order
radetsky Sep 23, 2022
c4eecd6
comparator can return nil without an error
radetsky Sep 23, 2022
6ecbec4
update example app
radetsky Sep 23, 2022
19496ce
Merge branch 'release/0.14' into rad-dev
radetsky Sep 23, 2022
e3a0322
changelog update
radetsky Sep 23, 2022
84218b2
check int value before casting to unsigned char
radetsky Sep 26, 2022
cdd443f
fix the compare procedure; var naming due the suggestion;
radetsky Sep 26, 2022
480702a
remove privateKey from secureMessageVerify functions
radetsky Sep 26, 2022
99342d1
remove publicKey from secureMessageSign64
radetsky Sep 26, 2022
297c93d
rewrite Obj-C code to use NSError instead of try/catch
radetsky Sep 29, 2022
cc5c3bb
fix the tests. The issue was in incorrect display of error
radetsky Sep 29, 2022
aa72af2
updated the code according to the comments
radetsky Sep 29, 2022
35ec1e9
updated the code according to the comments
radetsky Oct 2, 2022
913fec3
move examples to the previous state
radetsky Oct 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/wrappers/themis/react-native-themis/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export declare const COMPARATOR_NOT_READY: any, COMPARATOR_NOT_MATCH: any, COMPARATOR_MATCH: any, COMPARATOR_ERROR: any, KEYTYPE_RSA: any, KEYTYPE_EC: any;
export declare function isBase64(str: String): boolean;
export declare function string64(input: String): String;
export declare function keyPair64(typeOfKey: any): Promise<Object>;
export declare function symmetricKey64(): Promise<string>;
export declare function secureCellSealWithSymmetricKeyEncrypt64(symmetricKey64: String, plaintext: String, context?: String): Promise<string>;
Expand All @@ -13,7 +15,6 @@ export declare function secureMessageSign64(plaintext: String, privateKey64: Str
export declare function secureMessageVerify64(signed64: String, privateKey64: String, publicKey64: String): Promise<string>;
export declare function secureMessageEncrypt64(plaintext: String, privateKey64: String, publicKey64: String): Promise<string>;
export declare function secureMessageDecrypt64(encrypted64: String, privateKey64: String, publicKey64: String): Promise<string>;
export declare function string64(input: String): String;
export declare function comparatorInit64(data64: String): Promise<string>;
export declare function comparatorBegin(uuidStr: String): Promise<string>;
export declare function comparatorProceed64(uuidStr: String, data64: String): Promise<Object>;
79 changes: 76 additions & 3 deletions src/wrappers/themis/react-native-themis/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ const Themis = NativeModules.Themis
},
});
export const { COMPARATOR_NOT_READY, COMPARATOR_NOT_MATCH, COMPARATOR_MATCH, COMPARATOR_ERROR, KEYTYPE_RSA, KEYTYPE_EC } = Themis.getConstants();
export function isBase64(str) {
const regex64 = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
return regex64.test(str);
}
export function string64(input) {
return Buffer.from(input).toString('base64');
}
export function keyPair64(typeOfKey) {
if (typeOfKey !== KEYTYPE_RSA && typeOfKey !== KEYTYPE_EC) {
throw new Error('Invalid key type');
Expand Down Expand Up @@ -43,6 +50,9 @@ export function secureCellSealWithSymmetricKeyEncrypt64(symmetricKey64, plaintex
if (symmetricKey64 === "" || symmetricKey64 === undefined || symmetricKey64 === null) {
throw new Error("Parameter symmetricKey64 can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're wondering why this check is necessary, Buffer.from('💩💩💩', 'base64') – in typical Node.js & JS fashion – simply skips all characters that it can't recognize.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe if we announced that parameters should be base64 encoded, we have to validate them. Of course, I know about it behavior of Javascipt.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That comment was for other reviewers, who might not be aware why exactly you have to validate before calling the base64 conversion.

return new Promise((resolve, reject) => {
Themis.secureCellSealWithSymmetricKeyEncrypt(symmetricKey, plaintext, context, (encrypted) => {
Expand All @@ -60,6 +70,12 @@ export function secureCellSealWithSymmetricKeyDecrypt64(symmetricKey64, encrypte
if (encrypted64 === "" || encrypted64 === undefined || encrypted64 === null) {
throw new Error("Parameter encrypted64 can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -92,6 +108,9 @@ export function secureCellSealWithPassphraseDecrypt64(passphrase, encrypted64, c
if (encrypted64 === "" || encrypted64 === undefined || encrypted64 === null) {
throw new Error("Parameter encrypted64 can not be empty");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
return new Promise((resolve, reject) => {
Themis.secureCellSealWithPassphraseDecrypt(passphrase, encrypted, context, (decrypted) => {
Expand All @@ -108,6 +127,9 @@ export function secureCellTokenProtectEncrypt64(symmetricKey64, plaintext, conte
if (plaintext === "" || plaintext === undefined || plaintext === null) {
throw new Error("Parameter plaintext can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
return new Promise((resolve, reject) => {
Themis.secureCellTokenProtectEncrypt(symmetricKey, plaintext, context, (encrypted) => {
Expand All @@ -132,6 +154,15 @@ export function secureCellTokenProtectDecrypt64(symmetricKey64, encrypted64, tok
if (token64 === "" || token64 === undefined || token64 === null) {
throw new Error("Parameter token64 can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
if (!isBase64(token64)) {
throw new Error("Parameter token64 is not base64 encoded");
}
const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
const token = Array.from(Buffer.from(token64, 'base64'));
Expand All @@ -154,6 +185,9 @@ export function secureCellContextImprintEncrypt64(symmetricKey64, plaintext, con
if (context === "" || context === undefined || context === null) {
throw new Error("Parameter context can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
return new Promise((resolve, reject) => {
Themis.secureCellContextImprintEncrypt(symmetricKey, plaintext, context, (encrypted) => {
Expand All @@ -173,6 +207,12 @@ export function secureCellContextImprintDecrypt64(symmetricKey64, encrypted64, c
if (context === "" || context === undefined || context === null) {
throw new Error("Parameter context can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
return new Promise((resolve, reject) => {
Expand All @@ -191,6 +231,12 @@ export function secureMessageSign64(plaintext, privateKey64, publicKey64) {
if (privateKey64 === "" || privateKey64 === undefined || privateKey64 === null) {
throw new Error("Parameter privateKey64 can not be empty");
}
if (!isBase64(privateKey64)) {
throw new Error("Parameter privateKey64 is not base64 encoded");
}
if (publicKey64 && !isBase64(publicKey64)) {
throw new Error("Optional parameter publicKey64 is not base64 encoded");
}
const privateKey = Array.from(Buffer.from(privateKey64, 'base64'));
const publicKey = publicKey64 !== null && publicKey64 !== "" ?
Array.from(Buffer.from(publicKey64, 'base64')) : null;
Expand All @@ -209,6 +255,15 @@ export function secureMessageVerify64(signed64, privateKey64, publicKey64) {
if (publicKey64 === "" || publicKey64 === undefined || publicKey64 === null) {
throw new Error("Parameter publicKey64 can not be empty");
}
if (!isBase64(signed64)) {
throw new Error("Parameter signed64 is not base64 encoded");
}
if (!isBase64(publicKey64)) {
throw new Error("Parameter publicKey64 is not base64 encoded");
}
if (privateKey64 && !isBase64(privateKey64)) {
throw new Error("Optional parameter privateKey64 is not base64 encoded");
}
const signed = Array.from(Buffer.from(signed64, 'base64'));
const privateKey = privateKey64 !== null && privateKey64 !== "" ?
Array.from(Buffer.from(privateKey64, 'base64')) : null;
Expand All @@ -232,6 +287,12 @@ export function secureMessageEncrypt64(plaintext, privateKey64, publicKey64) {
if (publicKey64 === "" || publicKey64 === undefined || publicKey64 === null) {
throw new Error("Parameter publicKey64 can not be empty");
}
if (!isBase64(privateKey64)) {
throw new Error("Parameter privateKey64 is not base64 encoded");
}
if (!isBase64(publicKey64)) {
throw new Error("Parameter publicKey64 is not base64 encoded");
}
const privateKey = Array.from(Buffer.from(privateKey64, 'base64'));
const publicKey = Array.from(Buffer.from(publicKey64, 'base64'));
return new Promise((resolve, reject) => {
Expand All @@ -252,6 +313,15 @@ export function secureMessageDecrypt64(encrypted64, privateKey64, publicKey64) {
if (publicKey64 === "" || publicKey64 === undefined || publicKey64 === null) {
throw new Error("Parameter publicKey64 can not be empty");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
if (!isBase64(privateKey64)) {
throw new Error("Parameter privateKey64 is not base64 encoded");
}
if (!isBase64(publicKey64)) {
throw new Error("Parameter publicKey64 is not base64 encoded");
}
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
const privateKey = Array.from(Buffer.from(privateKey64, 'base64'));
const publicKey = Array.from(Buffer.from(publicKey64, 'base64'));
Expand All @@ -263,14 +333,14 @@ export function secureMessageDecrypt64(encrypted64, privateKey64, publicKey64) {
});
});
}
export function string64(input) {
return Buffer.from(input).toString('base64');
}
/* Returns UUID in string value that corresponds to new comparator */
export function comparatorInit64(data64) {
if (data64 === "" || data64 === undefined || data64 === null) {
throw new Error("Parameter data64 can not be empty");
}
if (!isBase64(data64)) {
throw new Error("Parameter data64 is not base64 encoded");
}
ilammy marked this conversation as resolved.
Show resolved Hide resolved
const data = Array.from(Buffer.from(data64, 'base64'));
return new Promise((resolve, reject) => {
Themis.initComparator(data, (comparator) => {
Expand Down Expand Up @@ -300,6 +370,9 @@ export function comparatorProceed64(uuidStr, data64) {
if (data64 === "" || data64 === undefined || data64 === null) {
throw new Error("Parameter data64 can not be empty");
}
if (!isBase64(data64)) {
throw new Error("Parameter data64 is not base64 encoded");
}
const data = Array.from(Buffer.from(data64, 'base64'));
return new Promise((resolve, reject) => {
Themis.proceedCompare(uuidStr, data, (nextData, status) => {
Expand Down
89 changes: 82 additions & 7 deletions src/wrappers/themis/react-native-themis/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ export const {
KEYTYPE_EC } = Themis.getConstants()



export function isBase64(str: String): boolean {
const regex64 = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
return regex64.test(str as string);
}

export function string64(input: String): String {
return Buffer.from(input).toString('base64')
}

export function keyPair64(typeOfKey: any): Promise<Object> {
if (typeOfKey !== KEYTYPE_RSA && typeOfKey !== KEYTYPE_EC) {
throw new Error('Invalid key type');
Expand Down Expand Up @@ -64,6 +74,9 @@ export function secureCellSealWithSymmetricKeyEncrypt64(
if (symmetricKey64 === "" || symmetricKey64 === undefined || symmetricKey64 === null) {
throw new Error("Parameter symmetricKey64 can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}

const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));

Expand All @@ -89,6 +102,13 @@ export function secureCellSealWithSymmetricKeyDecrypt64(
throw new Error("Parameter encrypted64 can not be empty");
}

if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}

const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));

Expand Down Expand Up @@ -128,10 +148,12 @@ export function secureCellSealWithPassphraseDecrypt64(
if (passphrase === "" || passphrase === undefined || passphrase === null) {
throw new Error("Parameter passphrase can not be empty");
}

if (encrypted64 === "" || encrypted64 === undefined || encrypted64 === null) {
throw new Error("Parameter encrypted64 can not be empty");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}

const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));

Expand All @@ -155,6 +177,9 @@ export function secureCellTokenProtectEncrypt64(
if (plaintext === "" || plaintext === undefined || plaintext === null) {
throw new Error("Parameter plaintext can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}

const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));

Expand Down Expand Up @@ -188,6 +213,16 @@ export function secureCellTokenProtectDecrypt64(
throw new Error("Parameter token64 can not be empty");
}

if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
if (!isBase64(token64)) {
throw new Error("Parameter token64 is not base64 encoded");
}

const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
const token = Array.from(Buffer.from(token64, 'base64'));
Expand Down Expand Up @@ -216,6 +251,9 @@ export function secureCellContextImprintEncrypt64(
if (context === "" || context === undefined || context === null) {
throw new Error("Parameter context can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}

const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
return new Promise((resolve, reject) => {
Expand All @@ -241,6 +279,13 @@ export function secureCellContextImprintDecrypt64(
if (context === "" || context === undefined || context === null) {
throw new Error("Parameter context can not be empty");
}
if (!isBase64(symmetricKey64)) {
throw new Error("Parameter symmetricKey64 is not base64 encoded");
}
if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}


const symmetricKey = Array.from(Buffer.from(symmetricKey64, 'base64'));
const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
Expand All @@ -266,6 +311,12 @@ export function secureMessageSign64(
if (privateKey64 === "" || privateKey64 === undefined || privateKey64 === null) {
throw new Error("Parameter privateKey64 can not be empty");
}
if (!isBase64(privateKey64)) {
throw new Error("Parameter privateKey64 is not base64 encoded");
}
if (publicKey64 && !isBase64(publicKey64)) {
throw new Error("Optional parameter publicKey64 is not base64 encoded");
}

const privateKey = Array.from(Buffer.from(privateKey64, 'base64'));
const publicKey = publicKey64 !== null && publicKey64 !== "" ?
Expand All @@ -291,6 +342,15 @@ export function secureMessageVerify64(
if (publicKey64 === "" || publicKey64 === undefined || publicKey64 === null) {
throw new Error("Parameter publicKey64 can not be empty");
}
if (!isBase64(signed64)) {
throw new Error("Parameter signed64 is not base64 encoded");
}
if (!isBase64(publicKey64)) {
throw new Error("Parameter publicKey64 is not base64 encoded");
}
if (privateKey64 && !isBase64(privateKey64)) {
throw new Error("Optional parameter privateKey64 is not base64 encoded");
}

const signed = Array.from(Buffer.from(signed64, 'base64'));
const privateKey = privateKey64 !== null && privateKey64 !== "" ?
Expand Down Expand Up @@ -321,7 +381,12 @@ export function secureMessageEncrypt64(
if (publicKey64 === "" || publicKey64 === undefined || publicKey64 === null) {
throw new Error("Parameter publicKey64 can not be empty");
}

if (!isBase64(privateKey64)) {
throw new Error("Parameter privateKey64 is not base64 encoded");
}
if (!isBase64(publicKey64)) {
throw new Error("Parameter publicKey64 is not base64 encoded");
}

const privateKey = Array.from(Buffer.from(privateKey64, 'base64'));
const publicKey = Array.from(Buffer.from(publicKey64, 'base64'));
Expand Down Expand Up @@ -349,7 +414,15 @@ export function secureMessageDecrypt64(
if (publicKey64 === "" || publicKey64 === undefined || publicKey64 === null) {
throw new Error("Parameter publicKey64 can not be empty");
}

if (!isBase64(encrypted64)) {
throw new Error("Parameter encrypted64 is not base64 encoded");
}
if (!isBase64(privateKey64)) {
throw new Error("Parameter privateKey64 is not base64 encoded");
}
if (!isBase64(publicKey64)) {
throw new Error("Parameter publicKey64 is not base64 encoded");
}

const encrypted = Array.from(Buffer.from(encrypted64, 'base64'));
const privateKey = Array.from(Buffer.from(privateKey64, 'base64'));
Expand All @@ -364,16 +437,16 @@ export function secureMessageDecrypt64(
})
}

export function string64(input: String): String {
return Buffer.from(input).toString('base64')
}

/* Returns UUID in string value that corresponds to new comparator */
export function comparatorInit64(data64: String): Promise<string> {

if (data64 === "" || data64 === undefined || data64 === null) {
throw new Error("Parameter data64 can not be empty");
}
if (!isBase64(data64)) {
throw new Error("Parameter data64 is not base64 encoded");
}

const data = Array.from(Buffer.from(data64, 'base64'))
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -411,7 +484,9 @@ export function comparatorProceed64(
if (data64 === "" || data64 === undefined || data64 === null) {
throw new Error("Parameter data64 can not be empty");
}

if (!isBase64(data64)) {
throw new Error("Parameter data64 is not base64 encoded");
}

const data = Array.from(Buffer.from(data64, 'base64'))
return new Promise((resolve, reject) => {
Expand Down