Skip to content

Commit

Permalink
Merge pull request #52 from serenity-kit/maximkott-feat/reintroduce-c…
Browse files Browse the repository at this point in the history
…rypto-pwhash

feat: reintroduce crypto_pwhash
  • Loading branch information
nikgraf authored Nov 28, 2023
2 parents e951836 + c527c06 commit 8e60f77
Show file tree
Hide file tree
Showing 10 changed files with 795 additions and 203 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ import {
crypto_kdf_derive_from_key,
crypto_kdf_KEYBYTES,
crypto_kdf_keygen,
crypto_pwhash, // only with loadSumoVersion with react-native-web
crypto_pwhash_ALG_DEFAULT, // only with loadSumoVersion with react-native-web
crypto_pwhash_MEMLIMIT_INTERACTIVE, // only with loadSumoVersion with react-native-web
crypto_pwhash_OPSLIMIT_INTERACTIVE, // only with loadSumoVersion with react-native-web
crypto_pwhash_SALTBYTES, // only with loadSumoVersion with react-native-web
crypto_generichash,
crypto_generichash_BYTES,
crypto_generichash_BYTES_MIN,
Expand All @@ -83,11 +88,25 @@ import {
to_base64,
to_hex,
to_string,
ready, // only needed for react-native-web
loadSumoVersion, // only relevant for react-native-web
} from 'react-native-libsodium';

// ...
```

## React Native Web

For the web platform the constants and functions from the `libsodium-wrappers` package is exposed. This also means you need to wait for the `ready` Promise to be resolved before using any constant or function.

Certain constants and functions e.g. `crypto_pwhash` are only available in the `libsodium-wrappers-sumo` package. To load this package instead for web you can call `loadSumoVersion` right after importing the package.

```ts
import { loadSumoVersion, ready } from 'react-native-libsodium';

loadSumoVersion();
```

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
Expand Down
81 changes: 81 additions & 0 deletions cpp/react-native-libsodium.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ namespace ReactNativeLibsodium
{
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_secretbox_KEYBYTES", static_cast<int>(crypto_secretbox_KEYBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_secretbox_NONCEBYTES", static_cast<int>(crypto_secretbox_NONCEBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash_SALTBYTES", static_cast<int>(crypto_pwhash_SALTBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash_ALG_DEFAULT", static_cast<int>(crypto_pwhash_ALG_DEFAULT));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash_OPSLIMIT_INTERACTIVE", static_cast<int>(crypto_pwhash_OPSLIMIT_INTERACTIVE));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash_MEMLIMIT_INTERACTIVE", static_cast<int>(crypto_pwhash_MEMLIMIT_INTERACTIVE));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_PUBLICKEYBYTES", static_cast<int>(crypto_box_PUBLICKEYBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_SECRETKEYBYTES", static_cast<int>(crypto_box_SECRETKEYBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_NONCEBYTES", static_cast<int>(crypto_box_NONCEBYTES));
Expand All @@ -198,6 +202,8 @@ namespace ReactNativeLibsodium
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_generichash_BYTES_MIN", static_cast<int>(crypto_generichash_BYTES_MIN));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_generichash_BYTES_MAX", static_cast<int>(crypto_generichash_BYTES_MAX));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_generichash_KEYBYTES", static_cast<int>(crypto_generichash_KEYBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash_BYTES_MAX", static_cast<int>(crypto_pwhash_BYTES_MAX));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash_BYTES_MIN", static_cast<int>(crypto_pwhash_BYTES_MIN));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_generichash_KEYBYTES_MIN", static_cast<int>(crypto_generichash_KEYBYTES_MIN));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_generichash_KEYBYTES_MAX", static_cast<int>(crypto_generichash_KEYBYTES_MAX));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_sign_SEEDBYTES", static_cast<int>(crypto_sign_SEEDBYTES));
Expand Down Expand Up @@ -959,6 +965,81 @@ namespace ReactNativeLibsodium

jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_open_easy", std::move(jsi_crypto_box_open_easy));

auto jsi_crypto_pwhash = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forUtf8(jsiRuntime, "jsi_crypto_pwhash"),
6,
[](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count) -> jsi::Value
{
const std::string functionName = "crypto_pwhash";

std::string keyLengthArgumentName = "keyLength";
unsigned int keyLengthArgumentPosition = 0;
validateIsNumber(functionName, runtime, arguments[keyLengthArgumentPosition], keyLengthArgumentName, true);

std::string passwordArgumentName = "password";
unsigned int passwordArgumentPosition = 1;
JsiArgType passwordArgType = validateIsStringOrArrayBuffer(functionName, runtime, arguments[passwordArgumentPosition], passwordArgumentName, true);

std::string saltArgumentName = "salt";
unsigned int saltArgumentPosition = 2;
validateIsArrayBuffer(functionName, runtime, arguments[saltArgumentPosition], saltArgumentName, true);

std::string opsLimitArgumentName = "opsLimit";
unsigned int opsLimitArgumentPosition = 3;
validateIsNumber(functionName, runtime, arguments[opsLimitArgumentPosition], opsLimitArgumentName, true);

std::string memLimitArgumentName = "memLimit";
unsigned int memLimitArgumentPosition = 4;
validateIsNumber(functionName, runtime, arguments[memLimitArgumentPosition], memLimitArgumentName, true);

std::string algorithmArgumentName = "algorithm";
unsigned int algorithmArgumentPosition = 5;
validateIsNumber(functionName, runtime, arguments[algorithmArgumentPosition], algorithmArgumentName, true);

int keyLength = arguments[keyLengthArgumentPosition].asNumber();
auto saltDataArrayBuffer =
arguments[saltArgumentPosition].asObject(runtime).getArrayBuffer(runtime);
int opsLimit = arguments[opsLimitArgumentPosition].asNumber();
int memLimit = arguments[memLimitArgumentPosition].asNumber();
int algorithm = arguments[algorithmArgumentPosition].asNumber();
std::vector<uint8_t> key(keyLength);

int result = -1;
if (passwordArgType == JsiArgType::string)
{
std::string passwordString = arguments[passwordArgumentPosition].asString(runtime).utf8(runtime);
result = crypto_pwhash(
key.data(),
keyLength,
reinterpret_cast<const char *>(passwordString.data()),
passwordString.length(),
saltDataArrayBuffer.data(runtime),
opsLimit,
memLimit,
algorithm);
}
else
{
auto passwordArrayBuffer =
arguments[passwordArgumentPosition].asObject(runtime).getArrayBuffer(runtime);
result = crypto_pwhash(
key.data(),
keyLength,
reinterpret_cast<const char *>(passwordArrayBuffer.data(runtime)),
passwordArrayBuffer.length(runtime),
saltDataArrayBuffer.data(runtime),
opsLimit,
memLimit,
algorithm);
}

throwOnBadResult(functionName, runtime, result);
return arrayBufferAsObject(runtime, key);
});

jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_pwhash", std::move(jsi_crypto_pwhash));

auto jsi_crypto_kdf_derive_from_key = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forUtf8(jsiRuntime, "jsi_crypto_kdf_derive_from_key"),
Expand Down
4 changes: 3 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react';
import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native';
import sodium, { ready } from 'react-native-libsodium';
import sodium, { loadSumoVersion, ready } from 'react-native-libsodium';
import { TestResults } from './components/TestResults';
import { VisualImageTest } from './components/VisualImageTest';

loadSumoVersion();

function LibsodiumTests() {
if (sodium.crypto_secretbox_KEYBYTES !== 32) {
throw new Error('export default not working');
Expand Down
1 change: 1 addition & 0 deletions example/src/components/TestResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../tests/crypto_box_open_easy_test';
import '../tests/crypto_generichash_test';
import '../tests/crypto_kdf_derive_from_key_test';
import '../tests/crypto_kdf_keygen_test';
import '../tests/crypto_pwhash_test';
import '../tests/crypto_secretbox_easy_test';
import '../tests/crypto_secretbox_keygen_test';
import '../tests/crypto_secretbox_open_easy_test';
Expand Down
106 changes: 106 additions & 0 deletions example/src/tests/crypto_pwhash_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
crypto_pwhash,
crypto_pwhash_ALG_DEFAULT,
crypto_pwhash_BYTES_MIN,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_SALTBYTES,
randombytes_buf,
} from 'react-native-libsodium';
import { isEqualUint8Array } from '../utils/isEqualUint8Array';
import { expect, test } from '../utils/testRunner';

test('crypto_pwhash', () => {
const password = 'password123';
const salt = new Uint8Array([
149, 177, 121, 247, 17, 38, 67, 49, 150, 68, 118, 228, 16, 98, 110, 175,
]);
const randomSalt = randombytes_buf(crypto_pwhash_SALTBYTES);

expect(() =>
crypto_pwhash(
crypto_pwhash_BYTES_MIN,
password,
salt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
200
)
).toThrow();

expect(() =>
crypto_pwhash(
15,
password,
salt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
)
).toThrow();

expect(() =>
crypto_pwhash(
crypto_pwhash_BYTES_MIN,
password,
new Uint8Array([10]),
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
)
).toThrow();

expect(
isEqualUint8Array(
crypto_pwhash(
crypto_pwhash_BYTES_MIN,
password,
salt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
),
new Uint8Array([
75, 92, 252, 55, 217, 21, 210, 156, 216, 33, 97, 101, 153, 119, 14, 177,
])
)
).toBe(true);

expect(
isEqualUint8Array(
crypto_pwhash(
crypto_pwhash_BYTES_MIN,
new Uint8Array([100, 100, 100]),
salt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
),
new Uint8Array([
136, 232, 104, 134, 32, 193, 138, 249, 192, 236, 243, 239, 248, 70, 117,
160,
])
)
).toBe(true);

expect(
isEqualUint8Array(
crypto_pwhash(
crypto_pwhash_BYTES_MIN,
password,
randomSalt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
),
crypto_pwhash(
crypto_pwhash_BYTES_MIN,
password,
randomSalt,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_ALG_DEFAULT
)
)
).toBe(true);
});
2 changes: 2 additions & 0 deletions example/web/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const babelLoaderConfiguration = {
path.resolve(appDirectory, 'index.js'),
path.resolve(appDirectory, 'src'),
path.resolve(appDirectory, 'node_modules/react-native-uncompiled'),
// make sure the lib src files are also compiled
path.resolve(appDirectory, '../src'),
],
use: {
loader: 'babel-loader',
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@
},
"dependencies": {
"@types/libsodium-wrappers": "^0.7.13",
"libsodium-wrappers": "^0.7.13"
"@types/libsodium-wrappers-sumo": "^0.7.8",
"libsodium-wrappers": "^0.7.13",
"libsodium-wrappers-sumo": "^0.7.13"
}
}
Loading

0 comments on commit 8e60f77

Please sign in to comment.