Skip to content

Commit 6e2f44b

Browse files
feat: argon2 in node.js and react native wrapper
Signed-off-by: Berend Sliedrecht <[email protected]>
1 parent 69547ce commit 6e2f44b

File tree

12 files changed

+126
-20
lines changed

12 files changed

+126
-20
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"scripts": {
1313
"style:check": "biome check --unsafe .",
1414
"style:fix": "pnpm style:check --write",
15-
"types:check": "pnpm -r check-types",
15+
"types:check": "pnpm -r types:check",
1616
"build": "pnpm -r build",
1717
"clean": "pnpm -r clean",
1818
"test": "node --import tsx --test packages/**/tests/*.test.ts",

packages/askar-nodejs/src/NodeJSAskar.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
AeadParamsOptions,
3+
Argon2DerivePasswordOptions,
34
Askar,
45
AskarErrorObject,
56
EncryptedBuffer,
@@ -263,6 +264,21 @@ export class NodeJSAskar implements Askar {
263264
this.handleError(errorCode)
264265
}
265266

267+
public argon2DerivePassword(options: Argon2DerivePasswordOptions) {
268+
const { parameters, password, salt } = serializeArguments(options)
269+
270+
const ret = allocateSecretBuffer()
271+
272+
const errorCode = this.nativeAskar.askar_argon2_derive_password(parameters, password, salt, ret)
273+
274+
this.handleError(errorCode)
275+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
276+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
277+
this.nativeAskar.askar_buffer_free(byteBuffer)
278+
279+
return bufferArray
280+
}
281+
266282
public entryListCount(options: EntryListCountOptions): number {
267283
const { entryListHandle } = serializeArguments(options)
268284
const ret = allocateInt32Buffer()

packages/askar-nodejs/src/library/bindings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const nativeBindings = {
3636
askar_set_default_logger: [FFI_ERROR_CODE, []],
3737
askar_set_max_log_level: [FFI_ERROR_CODE, [FFI_INT32]],
3838

39+
askar_argon2_derive_password: [FFI_ERROR_CODE, [FFI_INT8, ByteBufferStruct, ByteBufferStruct, SecretBufferStructPtr]],
40+
3941
askar_entry_list_count: [FFI_ERROR_CODE, [FFI_ENTRY_LIST_HANDLE, FFI_INT32_PTR]],
4042
askar_entry_list_free: [FFI_VOID, [FFI_ENTRY_LIST_HANDLE]],
4143
askar_entry_list_get_category: [FFI_ERROR_CODE, [FFI_ENTRY_LIST_HANDLE, FFI_INT32, FFI_STRING_PTR]],
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ok } from 'node:assert'
2+
import { before, describe, test } from 'node:test'
3+
import { Argon2, Argon2Parameters } from '@openwallet-foundation/askar-shared'
4+
import { setup } from './utils'
5+
6+
describe('Argon2', () => {
7+
before(setup)
8+
9+
test('derive password', () => {
10+
const password = 'my password'
11+
const salt = 'long enough salt'
12+
13+
const passwordBytes = Uint8Array.from(Buffer.from(password))
14+
const saltBytes = Uint8Array.from(Buffer.from(salt))
15+
16+
const derivedPassword = Argon2.derivePassword(Argon2Parameters.Interactive, passwordBytes, saltBytes)
17+
18+
ok(
19+
Buffer.from(derivedPassword).toString('hex') ===
20+
'9ef87bcf828c46c0136a0d1d9e391d713f75b327c6dc190455bd36c1bae33259'
21+
)
22+
})
23+
})

packages/askar-react-native/cpp/HostObject.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ FunctionMap AskarTurboModuleHostObject::functionMapping(jsi::Runtime &rt) {
1515
fMap.insert(
1616
std::make_tuple("setDefaultLogger", &askar::setDefaultLogger));
1717

18+
fMap.insert(
19+
std::make_tuple("argon2DerivePassword", &askar::argon2DerivePassword));
20+
1821
fMap.insert(std::make_tuple("storeCopyTo", &askar::storeCopyTo));
1922
fMap.insert(std::make_tuple("storeOpen", &askar::storeOpen));
2023
fMap.insert(

packages/askar-react-native/cpp/askar.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,23 @@ jsi::Value setDefaultLogger(jsi::Runtime &rt, jsi::Object options) {
2222
return createReturnValue(rt, code, nullptr);
2323
}
2424

25+
jsi::Value argon2DerivePassword(jsi::Runtime &rt, jsi::Object options) {
26+
auto parameters =
27+
jsiToValue<int8_t>(rt, options, "parameters");
28+
auto password =
29+
jsiToValue<ByterBuffer>(rt, options, "password");
30+
auto salt =
31+
jsiToValue<ByteBuffer>(rt, options, "salt");
32+
33+
SecretBuffer out;
34+
35+
ErrorCode code = askar_argon2_derive_password(parameters, password, salt, &out);
36+
37+
auto ret = createReturnValue(rt, code, &out);
38+
askar_buffer_free(out);
39+
return ret;
40+
}
41+
2542
jsi::Value entryListCount(jsi::Runtime &rt, jsi::Object options) {
2643
auto entryListHandle =
2744
jsiToValue<EntryListHandle>(rt, options, "entryListHandle");

packages/askar-react-native/cpp/include/libaries_askar.h

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,6 @@ typedef struct Option_EnabledCallback Option_EnabledCallback;
4848

4949
typedef struct Option_FlushCallback Option_FlushCallback;
5050

51-
typedef struct SecretBuffer {
52-
int64_t len;
53-
uint8_t *data;
54-
} SecretBuffer;
55-
56-
typedef struct FfiResultList_Entry FfiEntryList;
57-
58-
typedef struct ArcHandle_FfiEntryList {
59-
const FfiEntryList *_0;
60-
} ArcHandle_FfiEntryList;
61-
62-
typedef struct ArcHandle_FfiEntryList EntryListHandle;
63-
64-
typedef struct ArcHandle_LocalKey {
65-
const struct LocalKey *_0;
66-
} ArcHandle_LocalKey;
67-
68-
typedef struct ArcHandle_LocalKey LocalKeyHandle;
69-
7051
/**
7152
* ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
7253
* There are several cases when you might want to use this, but the primary one for us
@@ -153,6 +134,25 @@ typedef struct ByteBuffer {
153134
uint8_t *data;
154135
} ByteBuffer;
155136

137+
typedef struct SecretBuffer {
138+
int64_t len;
139+
uint8_t *data;
140+
} SecretBuffer;
141+
142+
typedef struct FfiResultList_Entry FfiEntryList;
143+
144+
typedef struct ArcHandle_FfiEntryList {
145+
const FfiEntryList *_0;
146+
} ArcHandle_FfiEntryList;
147+
148+
typedef struct ArcHandle_FfiEntryList EntryListHandle;
149+
150+
typedef struct ArcHandle_LocalKey {
151+
const struct LocalKey *_0;
152+
} ArcHandle_LocalKey;
153+
154+
typedef struct ArcHandle_LocalKey LocalKeyHandle;
155+
156156
typedef struct EncryptedBuffer {
157157
struct SecretBuffer buffer;
158158
int64_t tag_pos;
@@ -241,6 +241,18 @@ typedef void (*LogCallback)(const void *context,
241241
extern "C" {
242242
#endif // __cplusplus
243243

244+
/**
245+
* ## Derive password using Argon2
246+
*
247+
* If the first provided argument is 1, it will use `PARAMS_INTERACTTIVE` and otherwise it will
248+
* fallback to `PARAMS_MODERATE`.
249+
*
250+
*/
251+
ErrorCode askar_argon2_derive_password(int8_t parameters,
252+
struct ByteBuffer password,
253+
struct ByteBuffer salt,
254+
struct SecretBuffer *out);
255+
244256
void askar_buffer_free(struct SecretBuffer buffer);
245257

246258
void askar_clear_custom_logger(void);

packages/askar-react-native/src/NativeBindings.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ export interface NativeBindings {
77
version(options: Record<string, never>): string
88
getCurrentError(options: Record<string, never>): string
99

10+
argon2DerivePassword(options: {
11+
parameters: number
12+
password: ArrayBuffer
13+
salt: ArrayBuffer
14+
}): ReturnObject<Uint8Array>
15+
1016
entryListCount(options: { entryListHandle: string }): ReturnObject<number>
1117
entryListFree(options: { entryListHandle: string }): ReturnObject<never>
1218
entryListGetCategory(options: { entryListHandle: string; index: number }): ReturnObject<string>

packages/askar-react-native/src/ReactNativeAskar.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {
2+
Argon2DerivePasswordOptions,
23
Askar,
34
AskarErrorObject,
45
EntryListCountOptions,
@@ -187,6 +188,11 @@ export class ReactNativeAskar implements Askar {
187188
throw new Error('Method not implemented. setMaxLogLevel')
188189
}
189190

191+
public argon2DerivePassword(options: Argon2DerivePasswordOptions): Uint8Array {
192+
const serializedOptions = serializeArguments(options)
193+
return handleInvalidNullResponse(this.handleError(this.askar.argon2DerivePassword(serializedOptions)))
194+
}
195+
190196
public entryListCount(options: EntryListCountOptions): number {
191197
const serializedOptions = serializeArguments(options)
192198
return handleInvalidNullResponse(this.handleError(this.askar.entryListCount(serializedOptions)))

packages/askar-shared/src/askar/Askar.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ export type SetCustomLoggerOptions = {
3333
}
3434
export type SetMaxLogLevelOptions = { logLevel: number }
3535

36+
export type Argon2DerivePasswordOptions = {
37+
parameters: number
38+
password: Uint8Array
39+
salt: Uint8Array
40+
}
41+
3642
export type EntryListCountOptions = { entryListHandle: EntryListHandle }
3743
export type EntryListFreeOptions = { entryListHandle: EntryListHandle }
3844
export type EntryListGetCategoryOptions = {
@@ -363,6 +369,8 @@ export type Askar = {
363369
setDefaultLogger(): void
364370
setMaxLogLevel(options: SetMaxLogLevelOptions): void
365371

372+
argon2DerivePassword(options: Argon2DerivePasswordOptions): Uint8Array
373+
366374
entryListCount(options: EntryListCountOptions): number
367375
entryListFree(options: EntryListFreeOptions): void
368376
entryListGetCategory(options: EntryListGetCategoryOptions): string

0 commit comments

Comments
 (0)