Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/red-deers-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@openwallet-foundation/askar-react-native": patch
"@openwallet-foundation/askar-nodejs": patch
---

fix: memory leak from not freeing secret and encrypted buffers
95 changes: 60 additions & 35 deletions packages/askar-nodejs/src/NodeJSAskar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,9 @@ import {
StoreHandle,
handleInvalidNullResponse,
} from '@openwallet-foundation/askar-shared'
import type {
ByteBufferType,
EncryptedBufferType,
NativeCallback,
NativeCallbackWithResponse,
SecretBufferType,
} from './ffi'
import {
type ByteBufferType,
type EncryptedBufferType,
FFI_ENTRY_LIST_HANDLE,
FFI_INT8,
FFI_INT64,
Expand All @@ -108,6 +103,8 @@ import {
FFI_STORE_HANDLE,
FFI_STRING,
FFI_STRING_LIST_HANDLE,
type NativeCallback,
type NativeCallbackWithResponse,
allocateAeadParams,
allocateEncryptedBuffer,
allocateInt8Buffer,
Expand Down Expand Up @@ -319,9 +316,11 @@ export class NodeJSAskar implements Askar {

const errorCode = this.nativeAskar.askar_entry_list_get_value(entryListHandle, index, ret)
this.handleError(errorCode)

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

return bufferArray
}

public keyAeadDecrypt(options: KeyAeadDecryptOptions): Uint8Array {
Expand All @@ -330,9 +329,11 @@ export class NodeJSAskar implements Askar {

const errorCode = this.nativeAskar.askar_key_aead_decrypt(localKeyHandle, ciphertext, nonce, tag, aad, ret)
this.handleError(errorCode)

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

return bufferArray
}

public keyAeadEncrypt(options: KeyAeadEncryptOptions): EncryptedBuffer {
Expand All @@ -341,9 +342,11 @@ export class NodeJSAskar implements Askar {

const errorCode = this.nativeAskar.askar_key_aead_encrypt(localKeyHandle, message, nonce, aad, ret)
this.handleError(errorCode)

const encryptedBuffer = handleReturnPointer<EncryptedBufferType>(ret)
return encryptedBufferStructToClass(encryptedBuffer)
const encryptedBufferClass = encryptedBufferStructToClass(encryptedBuffer)
this.nativeAskar.askar_buffer_free(encryptedBuffer.secretBuffer)

return encryptedBufferClass
}

public keyAeadGetPadding(options: KeyAeadGetPaddingOptions): number {
Expand Down Expand Up @@ -372,9 +375,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyConvert(options: KeyConvertOptions): LocalKeyHandle {
Expand All @@ -394,9 +399,11 @@ export class NodeJSAskar implements Askar {

const errorCode = this.nativeAskar.askar_key_crypto_box(recipientKey, senderKey, message, nonce, ret)
this.handleError(errorCode)
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
this.nativeAskar.askar_buffer_free(byteBuffer)

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyCryptoBoxOpen(options: KeyCryptoBoxOpenOptions): Uint8Array {
Expand All @@ -405,19 +412,23 @@ export class NodeJSAskar implements Askar {

const errorCode = this.nativeAskar.askar_key_crypto_box_open(recipientKey, senderKey, message, nonce, ret)
this.handleError(errorCode)
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
this.nativeAskar.askar_buffer_free(byteBuffer)

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyCryptoBoxRandomNonce(): Uint8Array {
const ret = allocateSecretBuffer()

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyCryptoBoxSeal(options: KeyCryptoBoxSealOptions): Uint8Array {
Expand All @@ -426,9 +437,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyCryptoBoxSealOpen(options: KeyCryptoBoxSealOpenOptions): Uint8Array {
Expand All @@ -437,9 +450,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyDeriveEcdh1pu(options: KeyDeriveEcdh1puOptions): LocalKeyHandle {
Expand Down Expand Up @@ -661,9 +676,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyGetJwkThumbprint(options: KeyGetJwkThumbprintOptions): string {
Expand All @@ -682,9 +699,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyGetSecretBytes(options: KeyGetSecretBytesOptions): Uint8Array {
Expand All @@ -693,9 +712,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keySignMessage(options: KeySignMessageOptions): Uint8Array {
Expand All @@ -704,9 +725,11 @@ export class NodeJSAskar implements Askar {

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

const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
return new Uint8Array(secretBufferToBuffer(secretBuffer))
return bufferArray
}

public keyUnwrapKey(options: KeyUnwrapKeyOptions): LocalKeyHandle {
Expand Down Expand Up @@ -736,9 +759,11 @@ export class NodeJSAskar implements Askar {

const errorCode = this.nativeAskar.askar_key_wrap_key(localKeyHandle, other, nonce, ret)
this.handleError(errorCode)

const encryptedBuffer = handleReturnPointer<EncryptedBufferType>(ret)
return encryptedBufferStructToClass(encryptedBuffer)
const encryptedBufferClass = encryptedBufferStructToClass(encryptedBuffer)
this.nativeAskar.askar_buffer_free(encryptedBuffer.secretBuffer)

return encryptedBufferClass
}

public keyGetSupportedBackends(): string[] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { nativeBindings } from './bindings'

import type { ByteBufferType, SecretBufferStruct } from '../ffi'
// We need a mapping from string type value => type (property 'string' maps to type string)
interface StringTypeMapping {
pointer: Buffer
Expand All @@ -17,7 +18,11 @@ type ShapeOf<T> = {
[Property in keyof T]: T[Property]
}
type StringTypeArrayToTypes<List extends (keyof StringTypeMapping)[]> = {
[Item in keyof List]: List[Item] extends keyof StringTypeMapping ? StringTypeMapping[List[Item]] : Buffer
[Item in keyof List]: List[Item] extends keyof StringTypeMapping
? StringTypeMapping[List[Item]]
: List[Item] extends Mutable<typeof SecretBufferStruct>
? ByteBufferType | Buffer
: Buffer
}

// biome-ignore lint/suspicious/noExplicitAny:
Expand Down
Binary file modified packages/askar-nodejs/tests/indy_wallet_sqlite_upgraded.db
Binary file not shown.
Loading