diff --git a/.changeset/heavy-wasps-grow.md b/.changeset/heavy-wasps-grow.md new file mode 100644 index 0000000..c62c86d --- /dev/null +++ b/.changeset/heavy-wasps-grow.md @@ -0,0 +1,10 @@ +--- +"@openwallet-foundation/askar-react-native": minor +"@openwallet-foundation/askar-nodejs": minor +"@openwallet-foundation/askar-shared": minor +--- + +feat: update to Askar 0.5.0 release. + +- This adds support for providing a custom Argon2 config. +- In addition it adds methods to list the open sessions and scans on a store. Note however that these methods are only implemented in the Node.JS wrapper for now, they will be added to the React Native wrapper at a later stage. diff --git a/README.md b/README.md index 8911e77..d0f64c0 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,11 @@ const key = Key.fromSeed({ algorithm: KeyAlgs.Bls12381G1, seed }); ## Version Compatibility -The JavaScript wrapper is versioned independently from the native bindings. The following table shows the compatibility between the different versions. This library has been tested with specific Node.JS and React Native versions. Newer versions might also work, but they have not been tested. +The JavaScript wrapper is versioned independently from the native bindings. The following table shows the compatibility between the different versions. This library has been tested with specific Node.JS and React Native versions. Newer or older versions might also work, but they have not been tested. | JavaScript Wrapper | Askar | React Native | Node.JS | | ------------------ | ------ | ------------ | ---------- | | v0.3.0 - v0.3.1 | v0.4.1 | - | - | | v0.4.0 - v0.4.2 | v0.4.5 | 0.75 - 0.79 | 18, 20, 22 | | v0.4.3 | v0.4.6 | 0.75 - 0.79 | 18, 20, 22 | -| v0.5.0 | v0.4.6 | 0.75 - 0.79 | 20, 22, 24 | +| v0.5.0 | v0.5.0 | 0.76 - 0.81 | 20, 22, 24 | diff --git a/packages/askar-nodejs/package.json b/packages/askar-nodejs/package.json index d5c9de0..d9cc356 100644 --- a/packages/askar-nodejs/package.json +++ b/packages/askar-nodejs/package.json @@ -35,7 +35,7 @@ "typescript": "catalog:" }, "binary": { - "version": "v0.4.6", + "version": "v0.5.0", "host": "https://github.com/openwallet-foundation/askar/releases/download", "packageName": "library-{platform}-{arch}.tar.gz" }, diff --git a/packages/askar-nodejs/src/NodeJSAskar.ts b/packages/askar-nodejs/src/NodeJSAskar.ts index f1e5bbd..a8f115a 100644 --- a/packages/askar-nodejs/src/NodeJSAskar.ts +++ b/packages/askar-nodejs/src/NodeJSAskar.ts @@ -73,6 +73,8 @@ import type { StoreGetDefaultProfileOptions, StoreGetProfileNameOptions, StoreListProfilesOptions, + StoreListScansOptions, + StoreListSessionsOptions, StoreOpenOptions, StoreProvisionOptions, StoreRekeyOptions, @@ -103,6 +105,7 @@ import { allocateStringListHandle, type ByteBufferType, deallocateCallbackBuffer, + decodeHandleList, type EncryptedBufferType, encryptedBufferStructToClass, FFI_ENTRY_LIST_HANDLE, @@ -116,6 +119,7 @@ import { FFI_STRING_LIST_HANDLE, type NativeCallback, type NativeCallbackWithResponse, + type NodeJsHandleList, secretBufferToUint8Array, serializeArguments, toNativeCallback, @@ -278,7 +282,14 @@ export class NodeJSAskar implements Askar { const ret = allocateSecretBuffer() - const errorCode = this.nativeAskar.askar_argon2_derive_password(parameters, password, salt, ret) + const errorCode = this.nativeAskar.askar_argon2_derive_password( + parameters, + password, + salt, + // NOTE: we should not serialize the config, it should be passed as struct + options.config ?? null, + ret + ) this.handleError(errorCode) const byteBuffer = handleReturnPointer(ret) @@ -348,6 +359,10 @@ export class NodeJSAskar implements Askar { return uint8Array } + private handleListFree(options: { handleList: NodeJsHandleList }): void { + this.nativeAskar.askar_handle_list_free(options.handleList) + } + public keyAeadDecrypt(options: KeyAeadDecryptOptions): Uint8Array { const { ciphertext, localKeyHandle, nonce, aad, tag } = serializeArguments({ localKeyHandle: options.localKeyHandle, @@ -1122,6 +1137,37 @@ export class NodeJSAskar implements Askar { return ret } + public async storeListScans(options: StoreListScansOptions): Promise { + const { storeHandle } = serializeArguments(options) + const handleList = await this.promisifyWithResponse( + (cb, cbId) => this.nativeAskar.askar_store_list_scans(storeHandle, cb, cbId), + 'FfiHandleList' + ) + if (!handleList) return [] + + const scanHandleList = decodeHandleList(handleList).map((scanHandle) => ScanHandle.fromHandle(scanHandle)) + this.handleListFree({ handleList }) + + return scanHandleList + } + + public async storeListSessions(options: StoreListSessionsOptions): Promise { + const { storeHandle } = serializeArguments(options) + const handleList = await this.promisifyWithResponse( + (cb, cbId) => this.nativeAskar.askar_store_list_sessions(storeHandle, cb, cbId), + 'FfiHandleList' + ) + + if (!handleList) return [] + + const sessionHandleList = decodeHandleList(handleList).map((sessionHandle) => + SessionHandle.fromHandle(sessionHandle) + ) + this.handleListFree({ handleList }) + + return sessionHandleList + } + public async storeOpen(options: StoreOpenOptions): Promise { const { profile, keyMethod, passKey, specUri } = serializeArguments(options) diff --git a/packages/askar-nodejs/src/ffi/conversion.ts b/packages/askar-nodejs/src/ffi/conversion.ts index 1cf18f4..d55b2f7 100644 --- a/packages/askar-nodejs/src/ffi/conversion.ts +++ b/packages/askar-nodejs/src/ffi/conversion.ts @@ -6,6 +6,20 @@ export const uint8ArrayToByteBufferStruct = (buffer: Uint8Array = new Uint8Array return { data: buffer, len: buffer.length } } +export type NodeJsHandleList = { + len: number + data: unknown +} + +export const decodeHandleList = (handleList: NodeJsHandleList): number[] => { + // With koffi, pointer data needs to be decoded + const { data, len } = handleList + + // If data is a koffi external pointer, decode it as uint8 array + const decoded = koffi.decode(data, 'size_t', len) + return decoded +} + export const byteBufferToUint8Array = (byteBuffer: ByteBufferType): Uint8Array => { // With koffi, pointer data needs to be decoded const { data, len } = byteBuffer diff --git a/packages/askar-nodejs/src/ffi/structures.ts b/packages/askar-nodejs/src/ffi/structures.ts index 94fc24b..a7beee7 100644 --- a/packages/askar-nodejs/src/ffi/structures.ts +++ b/packages/askar-nodejs/src/ffi/structures.ts @@ -34,3 +34,31 @@ export const AeadParamsStruct = koffi.struct('AeadParams', { nonceLength: FFI_INT32, tagLength: FFI_INT32, }) + +// Argon2Config structure +export const Argon2ConfigStruct = koffi.struct('Argon2Config', { + algorithm: FFI_INT32, + version: FFI_INT32, + parallelism: FFI_INT32, + mem_cost: FFI_INT32, + time_cost: FFI_INT32, +}) + +export type Argon2ConfigType = { + algorithm: number + version: number + parallelism: number + mem_cost: number + time_cost: number +} + +// FfiHandleList structure +export const FfiHandleListStruct = koffi.struct('FfiHandleList', { + len: FFI_INT32, + data: koffi.pointer('size_t'), +}) + +export type FfiHandleListType = { + len: number + data: number[] +} diff --git a/packages/askar-nodejs/src/library/bindings.ts b/packages/askar-nodejs/src/library/bindings.ts index 1391de7..39d327c 100644 --- a/packages/askar-nodejs/src/library/bindings.ts +++ b/packages/askar-nodejs/src/library/bindings.ts @@ -23,6 +23,8 @@ const ByteBufferType = 'ByteBuffer' const SecretBufferType = 'ByteBuffer' // SecretBuffer is same as ByteBuffer const EncryptedBufferType = 'EncryptedBuffer' const AeadParamsType = 'AeadParams' +const Argon2ConfigType = 'Argon2Config' +const FfiHandleListType = 'FfiHandleList' // Callback signatures for koffi - must NOT be pointers in signatures // koffi expects callback types without the * pointer syntax @@ -46,7 +48,7 @@ export const nativeBindings = { askar_set_default_logger: `${FFI_ERROR_CODE} askar_set_default_logger()`, askar_set_max_log_level: `${FFI_ERROR_CODE} askar_set_max_log_level(${FFI_INT32} maxLevel)`, - askar_argon2_derive_password: `${FFI_ERROR_CODE} askar_argon2_derive_password(${FFI_INT8} parameters, ${ByteBufferType} password, ${ByteBufferType} salt, _Out_ ${SecretBufferType} *ret)`, + askar_argon2_derive_password: `${FFI_ERROR_CODE} askar_argon2_derive_password(${FFI_INT8} parameters, ${ByteBufferType} password, ${ByteBufferType} salt, ${Argon2ConfigType} *config, _Out_ ${SecretBufferType} *ret)`, askar_entry_list_count: `${FFI_ERROR_CODE} askar_entry_list_count(${FFI_ENTRY_LIST_HANDLE} handle, _Out_ ${FFI_INT32_PTR} count)`, askar_entry_list_free: `${FFI_VOID} askar_entry_list_free(${FFI_ENTRY_LIST_HANDLE} handle)`, @@ -55,6 +57,8 @@ export const nativeBindings = { askar_entry_list_get_tags: `${FFI_ERROR_CODE} askar_entry_list_get_tags(${FFI_ENTRY_LIST_HANDLE} handle, ${FFI_INT32} index, _Out_ ${FFI_STRING_PTR} tags)`, askar_entry_list_get_value: `${FFI_ERROR_CODE} askar_entry_list_get_value(${FFI_ENTRY_LIST_HANDLE} handle, ${FFI_INT32} index, _Out_ ${SecretBufferType} *value)`, + askar_handle_list_free: `${FFI_VOID} askar_handle_list_free(${FfiHandleListType} handle)`, + askar_string_list_count: `${FFI_ERROR_CODE} askar_string_list_count(${FFI_STRING_LIST_HANDLE} handle, _Out_ ${FFI_INT32_PTR} count)`, askar_string_list_free: `${FFI_VOID} askar_string_list_free(${FFI_STRING_LIST_HANDLE} handle)`, askar_string_list_get_item: `${FFI_ERROR_CODE} askar_string_list_get_item(${FFI_STRING_LIST_HANDLE} handle, ${FFI_INT32} index, _Out_ ${FFI_STRING_PTR} item)`, @@ -123,6 +127,8 @@ export const nativeBindings = { askar_store_get_profile_name: `${FFI_ERROR_CODE} askar_store_get_profile_name(${FFI_STORE_HANDLE} handle, ${CallbackString} cb, ${FFI_CALLBACK_ID} cbId)`, askar_store_get_default_profile: `${FFI_ERROR_CODE} askar_store_get_default_profile(${FFI_STORE_HANDLE} handle, ${CallbackString} cb, ${FFI_CALLBACK_ID} cbId)`, askar_store_list_profiles: `${FFI_ERROR_CODE} askar_store_list_profiles(${FFI_STORE_HANDLE} handle, ${CallbackHandle} cb, ${FFI_CALLBACK_ID} cbId)`, + askar_store_list_scans: `${FFI_ERROR_CODE} askar_store_list_scans(${FFI_STORE_HANDLE} handle, ${CallbackHandle} cb, ${FFI_CALLBACK_ID} cbId)`, + askar_store_list_sessions: `${FFI_ERROR_CODE} askar_store_list_sessions(${FFI_STORE_HANDLE} handle, ${CallbackHandle} cb, ${FFI_CALLBACK_ID} cbId)`, askar_store_open: `${FFI_ERROR_CODE} askar_store_open(${FFI_STRING} specUri, ${FFI_STRING} keyMethod, ${FFI_STRING} passKey, ${FFI_STRING} profile, ${CallbackHandle} cb, ${FFI_CALLBACK_ID} cbId)`, askar_store_provision: `${FFI_ERROR_CODE} askar_store_provision(${FFI_STRING} specUri, ${FFI_STRING} keyMethod, ${FFI_STRING} passKey, ${FFI_STRING} profile, ${FFI_INT8} recreate, ${CallbackHandle} cb, ${FFI_CALLBACK_ID} cbId)`, askar_store_rekey: `${FFI_ERROR_CODE} askar_store_rekey(${FFI_STORE_HANDLE} handle, ${FFI_STRING} keyMethod, ${FFI_STRING} passKey, ${CallbackBasic} cb, ${FFI_CALLBACK_ID} cbId)`, diff --git a/packages/askar-nodejs/tests/kdf.test.ts b/packages/askar-nodejs/tests/kdf.test.ts index a1616a2..09c86e3 100644 --- a/packages/askar-nodejs/tests/kdf.test.ts +++ b/packages/askar-nodejs/tests/kdf.test.ts @@ -1,4 +1,4 @@ -import { Argon2, Argon2Parameters } from '@openwallet-foundation/askar-shared' +import { Argon2, Argon2Algorithm, Argon2Parameters, Argon2Version } from '@openwallet-foundation/askar-shared' import { describe, expect, test } from 'vitest' describe('Argon2', () => { @@ -15,4 +15,28 @@ describe('Argon2', () => { '9ef87bcf828c46c0136a0d1d9e391d713f75b327c6dc190455bd36c1bae33259' ) }) + + test('derive password matches react-native-argon2 output', () => { + const password = '000000' + const salt = '13622169116451511306218218219151372412051474242757211221731116372255771137912226' + + const passwordBytes = Uint8Array.from(Buffer.from(password)) + const saltBytes = Uint8Array.from(Buffer.from(salt)) + + const derivedPassword = Argon2.derivePassword( + { + algorithm: Argon2Algorithm.Argon2id, + version: Argon2Version.V0x13, + parallelism: 4, + memCost: 64 * 1024, + timeCost: 8, + }, + passwordBytes, + saltBytes + ) + + expect(Buffer.from(derivedPassword).toString('hex')).toBe( + '1128133bb2b55a35c801f1dfc99a525cb8ff27a519bcd035f1a07f9a1cf6eae9' + ) + }) }) diff --git a/packages/askar-nodejs/tests/store.test.ts b/packages/askar-nodejs/tests/store.test.ts index 1fba211..370fd84 100644 --- a/packages/askar-nodejs/tests/store.test.ts +++ b/packages/askar-nodejs/tests/store.test.ts @@ -180,6 +180,24 @@ describe('Store and Session', () => { await scanStore.close() }) + test('open sessions and scans', async () => { + const store = await setupWallet() + const session = await store.openSession() + + const openSessions = await store.listOpenSessions() + expect(openSessions).toHaveLength(1) + expect(openSessions[0].handle).toEqual(session.handle?.handle) + + await session.close() + await expect(store.listOpenSessions()).resolves.toHaveLength(0) + + await expect(store.listOpenScans()).resolves.toHaveLength(0) + await store.scan({ category: firstEntry.category }).fetchAll() + await expect(store.listOpenScans()).resolves.toHaveLength(0) + + await store.close() + }) + test('Transaction basic', async () => { const txn = await store.openSession(true) diff --git a/packages/askar-react-native/cpp/askar.cpp b/packages/askar-react-native/cpp/askar.cpp index d71c151..08a917b 100644 --- a/packages/askar-react-native/cpp/askar.cpp +++ b/packages/askar-react-native/cpp/askar.cpp @@ -31,11 +31,27 @@ jsi::Value argon2DerivePassword(jsi::Runtime &rt, jsi::Object options) { jsiToValue(rt, options, "salt"); SecretBuffer out; - - ErrorCode code = askar_argon2_derive_password(parameters, password, salt, &out); + ErrorCode code; + + // Check if config parameter is provided + if (options.hasProperty(rt, "config")) { + auto configObj = options.getPropertyAsObject(rt, "config"); + Argon2Config config; + config.algorithm = jsiToValue(rt, configObj, "algorithm"); + config.version = jsiToValue(rt, configObj, "version"); + config.parallelism = jsiToValue(rt, configObj, "parallelism"); + config.mem_cost = jsiToValue(rt, configObj, "mem_cost"); + config.time_cost = jsiToValue(rt, configObj, "time_cost"); + + code = askar_argon2_derive_password(parameters, password, salt, &config, &out); + } else { + code = askar_argon2_derive_password(parameters, password, salt, nullptr, &out); + } auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -93,7 +109,9 @@ jsi::Value entryListGetValue(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_entry_list_get_value(entryListHandle, index, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -741,7 +759,9 @@ jsi::Value keyGetJwkSecret(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_get_jwk_secret(localKeyHandle, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -767,7 +787,9 @@ jsi::Value keyGetPublicBytes(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_get_public_bytes(localKeyHandle, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -780,7 +802,9 @@ jsi::Value keyGetSecretBytes(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_get_secret_bytes(localKeyHandle, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -798,7 +822,9 @@ jsi::Value keySignMessage(jsi::Runtime &rt, jsi::Object options) { &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -848,7 +874,9 @@ jsi::Value keyWrapKey(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_wrap_key(localKeyHandle, other, nonce, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out.buffer); + if (code == ErrorCode::Success) { + askar_buffer_free(out.buffer); + } return ret; } @@ -885,7 +913,9 @@ jsi::Value keyCryptoBox(jsi::Runtime &rt, jsi::Object options) { askar_key_crypto_box(recipientKey, senderKey, message, nonce, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -901,7 +931,9 @@ jsi::Value keyCryptoBoxOpen(jsi::Runtime &rt, jsi::Object options) { askar_key_crypto_box_open(recipientKey, senderKey, message, nonce, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -911,7 +943,9 @@ jsi::Value keyCryptoBoxRandomNonce(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_crypto_box_random_nonce(&out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -925,7 +959,9 @@ jsi::Value keyCryptoBoxSeal(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_crypto_box_seal(localKeyHandle, message, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -940,7 +976,9 @@ jsi::Value keyCryptoBoxSealOpen(jsi::Runtime &rt, jsi::Object options) { askar_key_crypto_box_seal_open(localKeyHandle, ciphertext, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -998,7 +1036,9 @@ jsi::Value keyAeadDecrypt(jsi::Runtime &rt, jsi::Object options) { askar_key_aead_decrypt(localKeyHandle, ciphertext, nonce, tag, aad, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } @@ -1016,7 +1056,9 @@ jsi::Value keyAeadEncrypt(jsi::Runtime &rt, jsi::Object options) { askar_key_aead_encrypt(localKeyHandle, message, nonce, aad, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out.buffer); + if (code == ErrorCode::Success) { + askar_buffer_free(out.buffer); + } return ret; } @@ -1053,7 +1095,9 @@ jsi::Value keyAeadRandomNonce(jsi::Runtime &rt, jsi::Object options) { ErrorCode code = askar_key_aead_random_nonce(localKeyHandle, &out); auto ret = createReturnValue(rt, code, &out); - askar_buffer_free(out); + if (code == ErrorCode::Success) { + askar_buffer_free(out); + } return ret; } diff --git a/packages/askar-react-native/cpp/include/libaries_askar.h b/packages/askar-react-native/cpp/include/libaries_askar.h index 3ba8e60..9d155ed 100644 --- a/packages/askar-react-native/cpp/include/libaries_askar.h +++ b/packages/askar-react-native/cpp/include/libaries_askar.h @@ -134,6 +134,34 @@ typedef struct ByteBuffer { uint8_t *data; } ByteBuffer; +typedef struct Argon2Config { + /** + * The algorithm is determined by a numeric code. + * 0: argon2d + * 1: argon2i + * 2: argon2id + */ + int32_t algorithm; + /** + * The version is determined by a numeric code. + * 16: version 0x10 + * 19: version 0x13 + */ + int32_t version; + /** + * The level of parallelism. + */ + int32_t parallelism; + /** + * The memory cost, measured in kibibytes. + */ + int32_t mem_cost; + /** + * The time cost, measured in iterations. + */ + int32_t time_cost; +} Argon2Config; + typedef struct SecretBuffer { int64_t len; uint8_t *data; @@ -147,6 +175,11 @@ typedef struct ArcHandle_FfiEntryList { typedef struct ArcHandle_FfiEntryList EntryListHandle; +typedef struct FfiHandleList { + int32_t len; + size_t *data; +} FfiHandleList; + typedef struct ArcHandle_LocalKey { const struct LocalKey *_0; } ArcHandle_LocalKey; @@ -244,13 +277,16 @@ extern "C" { /** * ## Derive password using Argon2 * - * If the first provided argument is 1, it will use `PARAMS_INTERACTTIVE` and otherwise it will - * fallback to `PARAMS_MODERATE`. + * The `parameters` argument determines the Argon2 derivation parameters: + * `-1`: apply the custom parameters contained in `config`, otherwise `config` must be a null pointer. + * `0`: use the `PARAMS_MODERATE` parameters + * `1`: use the `PARAMS_INTERACTIVE` parameters * */ ErrorCode askar_argon2_derive_password(int8_t parameters, struct ByteBuffer password, struct ByteBuffer salt, + const struct Argon2Config *config, struct SecretBuffer *out); void askar_buffer_free(struct SecretBuffer buffer); @@ -275,6 +311,8 @@ ErrorCode askar_entry_list_get_value(EntryListHandle handle, ErrorCode askar_get_current_error(const char **error_json_p); +void askar_handle_list_free(struct FfiHandleList handle); + ErrorCode askar_key_aead_decrypt(LocalKeyHandle handle, struct ByteBuffer ciphertext, struct ByteBuffer nonce, @@ -604,6 +642,18 @@ ErrorCode askar_store_list_profiles(StoreHandle handle, StringListHandle results), CallbackId cb_id); +ErrorCode askar_store_list_scans(StoreHandle handle, + void (*cb)(CallbackId cb_id, + ErrorCode err, + struct FfiHandleList results), + CallbackId cb_id); + +ErrorCode askar_store_list_sessions(StoreHandle handle, + void (*cb)(CallbackId cb_id, + ErrorCode err, + struct FfiHandleList results), + CallbackId cb_id); + ErrorCode askar_store_open(FfiStr spec_uri, FfiStr key_method, FfiStr pass_key, @@ -657,4 +707,4 @@ char *askar_version(void); #ifdef __cplusplus } // extern "C" -#endif // __cplusplus +#endif // __cplusplus \ No newline at end of file diff --git a/packages/askar-react-native/package.json b/packages/askar-react-native/package.json index 2bd8796..1ce8311 100644 --- a/packages/askar-react-native/package.json +++ b/packages/askar-react-native/package.json @@ -49,7 +49,7 @@ "react-native": ">= 0.71" }, "binary": { - "version": "v0.4.6", + "version": "v0.5.0", "host": "https://github.com/openwallet-foundation/askar/releases/download", "packageName": "library-ios-android.tar.gz" } diff --git a/packages/askar-react-native/src/NativeBindings.ts b/packages/askar-react-native/src/NativeBindings.ts index a2f260e..daae8bc 100644 --- a/packages/askar-react-native/src/NativeBindings.ts +++ b/packages/askar-react-native/src/NativeBindings.ts @@ -11,6 +11,13 @@ export interface NativeBindings { parameters: number password: ArrayBuffer salt: ArrayBuffer + config?: { + algorithm: number + version: number + parallelism: number + mem_cost: number + time_cost: number + } }): ReturnObject entryListCount(options: { entryListHandle: string }): ReturnObject diff --git a/packages/askar-react-native/src/ReactNativeAskar.ts b/packages/askar-react-native/src/ReactNativeAskar.ts index 037b7bb..6689faf 100644 --- a/packages/askar-react-native/src/ReactNativeAskar.ts +++ b/packages/askar-react-native/src/ReactNativeAskar.ts @@ -69,6 +69,8 @@ import type { StoreGetDefaultProfileOptions, StoreGetProfileNameOptions, StoreListProfilesOptions, + StoreListScansOptions, + StoreListSessionsOptions, StoreOpenOptions, StoreProvisionOptions, StoreRekeyOptions, @@ -188,7 +190,9 @@ export class ReactNativeAskar implements Askar { public argon2DerivePassword(options: Argon2DerivePasswordOptions): Uint8Array { const serializedOptions = serializeArguments(options) - return handleInvalidNullResponse(this.handleError(this.askar.argon2DerivePassword(serializedOptions))) + return handleInvalidNullResponse( + this.handleError(this.askar.argon2DerivePassword({ ...serializedOptions, config: options.config })) + ) } public entryListCount(options: EntryListCountOptions): number { @@ -691,6 +695,14 @@ export class ReactNativeAskar implements Askar { return ret } + public async storeListScans(_options: StoreListScansOptions): Promise { + throw new Error('Method askar_store_list_scans is not implemented in React Native') + } + + public async storeListSessions(_options: StoreListSessionsOptions): Promise { + throw new Error('Method askar_store_list_sessions is not implemented in React Native') + } + public async storeOpen(options: StoreOpenOptions): Promise { const serializedOptions = serializeArguments(options) const handle = await this.promisifyWithResponse((cb) => diff --git a/packages/askar-shared/src/askar/Askar.ts b/packages/askar-shared/src/askar/Askar.ts index f803b44..ee631cd 100644 --- a/packages/askar-shared/src/askar/Askar.ts +++ b/packages/askar-shared/src/askar/Askar.ts @@ -37,6 +37,13 @@ export type Argon2DerivePasswordOptions = { parameters: number password: Uint8Array salt: Uint8Array + config?: { + algorithm: number + version: number + parallelism: number + mem_cost: number + time_cost: number + } } export type EntryListCountOptions = { entryListHandle: EntryListHandle } @@ -58,6 +65,11 @@ export type EntryListGetValueOptions = { index: number } +export type HandleList = { + data: number[] + len: number +} + export type KeyAeadDecryptOptions = { localKeyHandle: LocalKeyHandle ciphertext: Uint8Array @@ -314,6 +326,8 @@ export type StoreGenerateRawKeyOptions = { seed?: Uint8Array } export type StoreGetProfileNameOptions = { storeHandle: StoreHandle } export type StoreGetDefaultProfileOptions = { storeHandle: StoreHandle } export type StoreListProfilesOptions = { storeHandle: StoreHandle } +export type StoreListScansOptions = { storeHandle: StoreHandle } +export type StoreListSessionsOptions = { storeHandle: StoreHandle } export type StoreOpenOptions = { specUri: string keyMethod?: string @@ -441,6 +455,8 @@ export type Askar = { storeGetDefaultProfile(options: StoreGetDefaultProfileOptions): Promise storeGetProfileName(options: StoreGetProfileNameOptions): Promise storeListProfiles(options: StoreListProfilesOptions): Promise + storeListScans(options: StoreListScansOptions): Promise + storeListSessions(options: StoreListSessionsOptions): Promise storeOpen(options: StoreOpenOptions): Promise storeProvision(options: StoreProvisionOptions): Promise storeRekey(options: StoreRekeyOptions): Promise diff --git a/packages/askar-shared/src/crypto/Argon2.ts b/packages/askar-shared/src/crypto/Argon2.ts index e876861..d75751f 100644 --- a/packages/askar-shared/src/crypto/Argon2.ts +++ b/packages/askar-shared/src/crypto/Argon2.ts @@ -3,10 +3,51 @@ import { NativeAskar } from '../askar' export enum Argon2Parameters { Moderate = 0, Interactive = 1, + Custom = -1, +} + +export enum Argon2Algorithm { + Argon2d = 0, + Argon2i = 1, + Argon2id = 2, +} + +export enum Argon2Version { + V0x10 = 16, + V0x13 = 19, +} + +export type Argon2Config = { + algorithm: Argon2Algorithm + version: Argon2Version + parallelism: number + memCost: number + timeCost: number } export class Argon2 { - public static derivePassword(parameters: Argon2Parameters, password: Uint8Array, salt: Uint8Array): Uint8Array { + public static derivePassword( + parameters: Argon2Parameters | Argon2Config, + password: Uint8Array, + salt: Uint8Array + ): Uint8Array { + // If parameters is an object (Argon2Config), use Custom (-1) as parameters + if (typeof parameters === 'object') { + return NativeAskar.instance.argon2DerivePassword({ + parameters: Argon2Parameters.Custom, + password, + salt, + config: { + algorithm: parameters.algorithm, + version: parameters.version, + parallelism: parameters.parallelism, + mem_cost: parameters.memCost, + time_cost: parameters.timeCost, + }, + }) + } + + // If parameters is a number, use it directly (no config needed) return NativeAskar.instance.argon2DerivePassword({ parameters, password, salt }) } } diff --git a/packages/askar-shared/src/crypto/EcdhEs.ts b/packages/askar-shared/src/crypto/EcdhEs.ts index 839e1bc..f6069bf 100644 --- a/packages/askar-shared/src/crypto/EcdhEs.ts +++ b/packages/askar-shared/src/crypto/EcdhEs.ts @@ -1,4 +1,4 @@ -import { askar, NativeAskar } from '../askar' +import { NativeAskar } from '../askar' import type { KeyAlgorithm } from '../enums' import { Jwk } from './Jwk' diff --git a/packages/askar-shared/src/crypto/Key.ts b/packages/askar-shared/src/crypto/Key.ts index a20c374..bab9469 100644 --- a/packages/askar-shared/src/crypto/Key.ts +++ b/packages/askar-shared/src/crypto/Key.ts @@ -1,5 +1,5 @@ import { Buffer } from 'buffer' -import { askar, NativeAskar } from '../askar' +import { NativeAskar } from '../askar' import type { KeyAlgorithm, KeyBackend, SignatureAlgorithm } from '../enums' import { KeyMethod, keyAlgorithmFromString } from '../enums' import type { LocalKeyHandle } from './handles' diff --git a/packages/askar-shared/src/store/Store.ts b/packages/askar-shared/src/store/Store.ts index fa04ed9..cf2bf79 100644 --- a/packages/askar-shared/src/store/Store.ts +++ b/packages/askar-shared/src/store/Store.ts @@ -46,6 +46,14 @@ export class Store { return await NativeAskar.instance.storeRenameProfile({ fromProfile, toProfile, storeHandle: this.handle }) } + public async listOpenSessions() { + return await NativeAskar.instance.storeListSessions({ storeHandle: this._handle }) + } + + public async listOpenScans() { + return await NativeAskar.instance.storeListScans({ storeHandle: this._handle }) + } + public async copyProfile({ toStore, fromProfile,