Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"scripts": {
"style:check": "biome check --unsafe .",
"style:fix": "pnpm style:check --write",
"types:check": "pnpm -r check-types",
"types:check": "pnpm -r types:check",
"build": "pnpm -r build",
"clean": "pnpm -r clean",
"test": "node --import tsx --test packages/**/tests/*.test.ts",
Expand Down
16 changes: 16 additions & 0 deletions packages/askar-nodejs/src/NodeJSAskar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {
AeadParamsOptions,
Argon2DerivePasswordOptions,
Askar,
AskarErrorObject,
EncryptedBuffer,
Expand Down Expand Up @@ -263,6 +264,21 @@ export class NodeJSAskar implements Askar {
this.handleError(errorCode)
}

public argon2DerivePassword(options: Argon2DerivePasswordOptions) {
const { parameters, password, salt } = serializeArguments(options)

const ret = allocateSecretBuffer()

const errorCode = this.nativeAskar.askar_argon2_derive_password(parameters, password, salt, ret)

this.handleError(errorCode)
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
this.nativeAskar.askar_buffer_free(byteBuffer)

return bufferArray
}

public entryListCount(options: EntryListCountOptions): number {
const { entryListHandle } = serializeArguments(options)
const ret = allocateInt32Buffer()
Expand Down
2 changes: 2 additions & 0 deletions packages/askar-nodejs/src/library/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const nativeBindings = {
askar_set_default_logger: [FFI_ERROR_CODE, []],
askar_set_max_log_level: [FFI_ERROR_CODE, [FFI_INT32]],

askar_argon2_derive_password: [FFI_ERROR_CODE, [FFI_INT8, ByteBufferStruct, ByteBufferStruct, SecretBufferStructPtr]],

askar_entry_list_count: [FFI_ERROR_CODE, [FFI_ENTRY_LIST_HANDLE, FFI_INT32_PTR]],
askar_entry_list_free: [FFI_VOID, [FFI_ENTRY_LIST_HANDLE]],
askar_entry_list_get_category: [FFI_ERROR_CODE, [FFI_ENTRY_LIST_HANDLE, FFI_INT32, FFI_STRING_PTR]],
Expand Down
23 changes: 23 additions & 0 deletions packages/askar-nodejs/tests/kdf.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ok } from 'node:assert'
import { before, describe, test } from 'node:test'
import { Argon2, Argon2Parameters } from '@openwallet-foundation/askar-shared'
import { setup } from './utils'

describe('Argon2', () => {
before(setup)

test('derive password', () => {
const password = 'my password'
const salt = 'long enough salt'

const passwordBytes = Uint8Array.from(Buffer.from(password))
const saltBytes = Uint8Array.from(Buffer.from(salt))

const derivedPassword = Argon2.derivePassword(Argon2Parameters.Interactive, passwordBytes, saltBytes)

ok(
Buffer.from(derivedPassword).toString('hex') ===
'9ef87bcf828c46c0136a0d1d9e391d713f75b327c6dc190455bd36c1bae33259'
)
})
})
3 changes: 3 additions & 0 deletions packages/askar-react-native/cpp/HostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ FunctionMap AskarTurboModuleHostObject::functionMapping(jsi::Runtime &rt) {
fMap.insert(
std::make_tuple("setDefaultLogger", &askar::setDefaultLogger));

fMap.insert(
std::make_tuple("argon2DerivePassword", &askar::argon2DerivePassword));

fMap.insert(std::make_tuple("storeCopyTo", &askar::storeCopyTo));
fMap.insert(std::make_tuple("storeOpen", &askar::storeOpen));
fMap.insert(
Expand Down
17 changes: 17 additions & 0 deletions packages/askar-react-native/cpp/askar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ jsi::Value setDefaultLogger(jsi::Runtime &rt, jsi::Object options) {
return createReturnValue(rt, code, nullptr);
}

jsi::Value argon2DerivePassword(jsi::Runtime &rt, jsi::Object options) {
auto parameters =
jsiToValue<int8_t>(rt, options, "parameters");
auto password =
jsiToValue<ByterBuffer>(rt, options, "password");
auto salt =
jsiToValue<ByteBuffer>(rt, options, "salt");

SecretBuffer out;

ErrorCode code = askar_argon2_derive_password(parameters, password, salt, &out);

auto ret = createReturnValue(rt, code, &out);
askar_buffer_free(out);
return ret;
}

jsi::Value entryListCount(jsi::Runtime &rt, jsi::Object options) {
auto entryListHandle =
jsiToValue<EntryListHandle>(rt, options, "entryListHandle");
Expand Down
50 changes: 31 additions & 19 deletions packages/askar-react-native/cpp/include/libaries_askar.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,6 @@ typedef struct Option_EnabledCallback Option_EnabledCallback;

typedef struct Option_FlushCallback Option_FlushCallback;

typedef struct SecretBuffer {
int64_t len;
uint8_t *data;
} SecretBuffer;

typedef struct FfiResultList_Entry FfiEntryList;

typedef struct ArcHandle_FfiEntryList {
const FfiEntryList *_0;
} ArcHandle_FfiEntryList;

typedef struct ArcHandle_FfiEntryList EntryListHandle;

typedef struct ArcHandle_LocalKey {
const struct LocalKey *_0;
} ArcHandle_LocalKey;

typedef struct ArcHandle_LocalKey LocalKeyHandle;

/**
* ByteBuffer is a struct that represents an array of bytes to be sent over the FFI boundaries.
* There are several cases when you might want to use this, but the primary one for us
Expand Down Expand Up @@ -153,6 +134,25 @@ typedef struct ByteBuffer {
uint8_t *data;
} ByteBuffer;

typedef struct SecretBuffer {
int64_t len;
uint8_t *data;
} SecretBuffer;

typedef struct FfiResultList_Entry FfiEntryList;

typedef struct ArcHandle_FfiEntryList {
const FfiEntryList *_0;
} ArcHandle_FfiEntryList;

typedef struct ArcHandle_FfiEntryList EntryListHandle;

typedef struct ArcHandle_LocalKey {
const struct LocalKey *_0;
} ArcHandle_LocalKey;

typedef struct ArcHandle_LocalKey LocalKeyHandle;

typedef struct EncryptedBuffer {
struct SecretBuffer buffer;
int64_t tag_pos;
Expand Down Expand Up @@ -241,6 +241,18 @@ typedef void (*LogCallback)(const void *context,
extern "C" {
#endif // __cplusplus

/**
* ## Derive password using Argon2
*
* If the first provided argument is 1, it will use `PARAMS_INTERACTTIVE` and otherwise it will
* fallback to `PARAMS_MODERATE`.
*
*/
ErrorCode askar_argon2_derive_password(int8_t parameters,
struct ByteBuffer password,
struct ByteBuffer salt,
struct SecretBuffer *out);

void askar_buffer_free(struct SecretBuffer buffer);

void askar_clear_custom_logger(void);
Expand Down
6 changes: 6 additions & 0 deletions packages/askar-react-native/src/NativeBindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ export interface NativeBindings {
version(options: Record<string, never>): string
getCurrentError(options: Record<string, never>): string

argon2DerivePassword(options: {
parameters: number
password: ArrayBuffer
salt: ArrayBuffer
}): ReturnObject<Uint8Array>

entryListCount(options: { entryListHandle: string }): ReturnObject<number>
entryListFree(options: { entryListHandle: string }): ReturnObject<never>
entryListGetCategory(options: { entryListHandle: string; index: number }): ReturnObject<string>
Expand Down
6 changes: 6 additions & 0 deletions packages/askar-react-native/src/ReactNativeAskar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
Argon2DerivePasswordOptions,
Askar,
AskarErrorObject,
EntryListCountOptions,
Expand Down Expand Up @@ -187,6 +188,11 @@ export class ReactNativeAskar implements Askar {
throw new Error('Method not implemented. setMaxLogLevel')
}

public argon2DerivePassword(options: Argon2DerivePasswordOptions): Uint8Array {
const serializedOptions = serializeArguments(options)
return handleInvalidNullResponse(this.handleError(this.askar.argon2DerivePassword(serializedOptions)))
}

public entryListCount(options: EntryListCountOptions): number {
const serializedOptions = serializeArguments(options)
return handleInvalidNullResponse(this.handleError(this.askar.entryListCount(serializedOptions)))
Expand Down
8 changes: 8 additions & 0 deletions packages/askar-shared/src/askar/Askar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export type SetCustomLoggerOptions = {
}
export type SetMaxLogLevelOptions = { logLevel: number }

export type Argon2DerivePasswordOptions = {
parameters: number
password: Uint8Array
salt: Uint8Array
}

export type EntryListCountOptions = { entryListHandle: EntryListHandle }
export type EntryListFreeOptions = { entryListHandle: EntryListHandle }
export type EntryListGetCategoryOptions = {
Expand Down Expand Up @@ -363,6 +369,8 @@ export type Askar = {
setDefaultLogger(): void
setMaxLogLevel(options: SetMaxLogLevelOptions): void

argon2DerivePassword(options: Argon2DerivePasswordOptions): Uint8Array

entryListCount(options: EntryListCountOptions): number
entryListFree(options: EntryListFreeOptions): void
entryListGetCategory(options: EntryListGetCategoryOptions): string
Expand Down
12 changes: 12 additions & 0 deletions packages/askar-shared/src/crypto/Argon2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { askar } from '../askar'

export enum Argon2Parameters {
Moderate = 0,
Interactive = 1,
}

export class Argon2 {
public static derivePassword(parameters: Argon2Parameters, password: Uint8Array, salt: Uint8Array): Uint8Array {
return askar.argon2DerivePassword({ parameters, password, salt })
}
}
1 change: 1 addition & 0 deletions packages/askar-shared/src/crypto/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './handles'
export * from './Key'
export * from './Argon2'
export * from './EcdhEs'
export * from './Ecdh1PU'
export * from './CryptoBox'
Expand Down
Loading