Skip to content

Commit

Permalink
feat: reintroduce crypto_pwhash
Browse files Browse the repository at this point in the history
  • Loading branch information
maximkott authored and nikgraf committed Nov 27, 2023
1 parent e951836 commit 73e3e65
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 0 deletions.
5 changes: 5 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,
crypto_pwhash_ALG_DEFAULT,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_SALTBYTES,
crypto_generichash,
crypto_generichash_BYTES,
crypto_generichash_BYTES_MIN,
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
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
105 changes: 105 additions & 0 deletions example/src/tests/crypto_pwhash_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
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';

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);
});
73 changes: 73 additions & 0 deletions src/lib.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ declare global {
var jsi_crypto_generichash_KEYBYTES_MIN: number;
var jsi_crypto_generichash_KEYBYTES_MAX: number;
var jsi_crypto_sign_SEEDBYTES: number;
var jsi_crypto_pwhash_SALTBYTES: number;
var jsi_crypto_pwhash_ALG_DEFAULT: number;
var jsi_crypto_pwhash_OPSLIMIT_INTERACTIVE: number;
var jsi_crypto_pwhash_MEMLIMIT_INTERACTIVE: number;
var jsi_crypto_pwhash_BYTES_MIN: number;
var jsi_crypto_pwhash_BYTES_MAX: number;

function jsi_crypto_auth(
message: string | ArrayBuffer,
Expand Down Expand Up @@ -114,6 +120,14 @@ declare global {
message: string | ArrayBuffer,
key?: ArrayBuffer | null | undefined
): ArrayBuffer;
function jsi_crypto_pwhash(
keyLength: number,
password: string | ArrayBuffer,
salt: ArrayBuffer,
opsLimit: number,
memLimit: number,
algorithm: number
): ArrayBuffer;
function jsi_crypto_kdf_derive_from_key(
subkeyLength: number,
subkeyId: number,
Expand Down Expand Up @@ -160,6 +174,14 @@ export const crypto_generichash_KEYBYTES_MIN =
export const crypto_generichash_KEYBYTES_MAX =
global.jsi_crypto_generichash_KEYBYTES_MAX;
export const crypto_sign_SEEDBYTES = global.jsi_crypto_sign_SEEDBYTES;
export const crypto_pwhash_SALTBYTES = global.jsi_crypto_pwhash_SALTBYTES;
export const crypto_pwhash_ALG_DEFAULT = global.jsi_crypto_pwhash_ALG_DEFAULT;
export const crypto_pwhash_OPSLIMIT_INTERACTIVE =
global.jsi_crypto_pwhash_OPSLIMIT_INTERACTIVE;
export const crypto_pwhash_MEMLIMIT_INTERACTIVE =
global.jsi_crypto_pwhash_MEMLIMIT_INTERACTIVE;
export const crypto_pwhash_BYTES_MIN = global.jsi_crypto_pwhash_BYTES_MIN;
export const crypto_pwhash_BYTES_MAX = global.jsi_crypto_pwhash_BYTES_MAX;

export const from_base64 = (
input: string,
Expand Down Expand Up @@ -510,6 +532,50 @@ export function crypto_generichash(
return convertToOutputFormat(result, outputFormat);
}

export function crypto_pwhash(
keyLength: number,
password: string | Uint8Array,
salt: Uint8Array,
opsLimit: number,
memLimit: number,
algorithm: number,
outputFormat?: Uint8ArrayOutputFormat | null
): Uint8Array;
export function crypto_pwhash(
keyLength: number,
password: string | Uint8Array,
salt: Uint8Array,
opsLimit: number,
memLimit: number,
algorithm: number,
outputFormat: StringOutputFormat
): string;
export function crypto_pwhash(
keyLength: number,
password: string | Uint8Array,
salt: Uint8Array,
opsLimit: number,
memLimit: number,
algorithm: number,
outputFormat: OutputFormat
) {
if (salt.length !== crypto_pwhash_SALTBYTES) {
throw new Error('invalid salt length');
}
let result: ArrayBuffer;
const passwordParam =
typeof password === 'string' ? password : password.buffer;
result = global.jsi_crypto_pwhash(
keyLength,
passwordParam,
salt.buffer,
opsLimit,
memLimit,
algorithm
);
return convertToOutputFormat(result, outputFormat);
}

export function crypto_kdf_derive_from_key(
subkey_len: number,
subkey_id: number,
Expand Down Expand Up @@ -657,6 +723,13 @@ export default {
crypto_kdf_CONTEXTBYTES,
crypto_kdf_KEYBYTES,
crypto_kdf_keygen,
crypto_pwhash,
crypto_pwhash_ALG_DEFAULT,
crypto_pwhash_BYTES_MAX,
crypto_pwhash_BYTES_MIN,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_SALTBYTES,
crypto_secretbox_easy,
crypto_secretbox_KEYBYTES,
crypto_secretbox_keygen,
Expand Down

0 comments on commit 73e3e65

Please sign in to comment.