Skip to content

Commit 3692a12

Browse files
authored
fix: copy buffers to JS memory and free (#55)
Signed-off-by: jamshale <[email protected]>
1 parent 058f7ad commit 3692a12

File tree

5 files changed

+114
-50
lines changed

5 files changed

+114
-50
lines changed

.changeset/red-deers-notice.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@openwallet-foundation/askar-react-native": patch
3+
"@openwallet-foundation/askar-nodejs": patch
4+
---
5+
6+
fix: memory leak from not freeing secret and encrypted buffers

packages/askar-nodejs/src/NodeJSAskar.ts

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,9 @@ import {
9191
StoreHandle,
9292
handleInvalidNullResponse,
9393
} from '@openwallet-foundation/askar-shared'
94-
import type {
95-
ByteBufferType,
96-
EncryptedBufferType,
97-
NativeCallback,
98-
NativeCallbackWithResponse,
99-
SecretBufferType,
100-
} from './ffi'
10194
import {
95+
type ByteBufferType,
96+
type EncryptedBufferType,
10297
FFI_ENTRY_LIST_HANDLE,
10398
FFI_INT8,
10499
FFI_INT64,
@@ -108,6 +103,8 @@ import {
108103
FFI_STORE_HANDLE,
109104
FFI_STRING,
110105
FFI_STRING_LIST_HANDLE,
106+
type NativeCallback,
107+
type NativeCallbackWithResponse,
111108
allocateAeadParams,
112109
allocateEncryptedBuffer,
113110
allocateInt8Buffer,
@@ -319,9 +316,11 @@ export class NodeJSAskar implements Askar {
319316

320317
const errorCode = this.nativeAskar.askar_entry_list_get_value(entryListHandle, index, ret)
321318
this.handleError(errorCode)
322-
323319
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
324-
return new Uint8Array(secretBufferToBuffer(byteBuffer))
320+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
321+
this.nativeAskar.askar_buffer_free(byteBuffer)
322+
323+
return bufferArray
325324
}
326325

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

331330
const errorCode = this.nativeAskar.askar_key_aead_decrypt(localKeyHandle, ciphertext, nonce, tag, aad, ret)
332331
this.handleError(errorCode)
333-
334332
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
335-
return new Uint8Array(secretBufferToBuffer(byteBuffer))
333+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
334+
this.nativeAskar.askar_buffer_free(byteBuffer)
335+
336+
return bufferArray
336337
}
337338

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

342343
const errorCode = this.nativeAskar.askar_key_aead_encrypt(localKeyHandle, message, nonce, aad, ret)
343344
this.handleError(errorCode)
344-
345345
const encryptedBuffer = handleReturnPointer<EncryptedBufferType>(ret)
346-
return encryptedBufferStructToClass(encryptedBuffer)
346+
const encryptedBufferClass = encryptedBufferStructToClass(encryptedBuffer)
347+
this.nativeAskar.askar_buffer_free(encryptedBuffer.secretBuffer)
348+
349+
return encryptedBufferClass
347350
}
348351

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

373376
const errorCode = this.nativeAskar.askar_key_aead_random_nonce(localKeyHandle, ret)
374377
this.handleError(errorCode)
378+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
379+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
380+
this.nativeAskar.askar_buffer_free(byteBuffer)
375381

376-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
377-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
382+
return bufferArray
378383
}
379384

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

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

398-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
399-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
406+
return bufferArray
400407
}
401408

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

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

409-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
410-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
419+
return bufferArray
411420
}
412421

413422
public keyCryptoBoxRandomNonce(): Uint8Array {
414423
const ret = allocateSecretBuffer()
415424

416425
const errorCode = this.nativeAskar.askar_key_crypto_box_random_nonce(ret)
417426
this.handleError(errorCode)
427+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
428+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
429+
this.nativeAskar.askar_buffer_free(byteBuffer)
418430

419-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
420-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
431+
return bufferArray
421432
}
422433

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

427438
const errorCode = this.nativeAskar.askar_key_crypto_box_seal(localKeyHandle, message, ret)
428439
this.handleError(errorCode)
440+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
441+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
442+
this.nativeAskar.askar_buffer_free(byteBuffer)
429443

430-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
431-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
444+
return bufferArray
432445
}
433446

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

438451
const errorCode = this.nativeAskar.askar_key_crypto_box_seal_open(localKeyHandle, ciphertext, ret)
439452
this.handleError(errorCode)
453+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
454+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
455+
this.nativeAskar.askar_buffer_free(byteBuffer)
440456

441-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
442-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
457+
return bufferArray
443458
}
444459

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

662677
const errorCode = this.nativeAskar.askar_key_get_jwk_secret(localKeyHandle, ret)
663678
this.handleError(errorCode)
679+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
680+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
681+
this.nativeAskar.askar_buffer_free(byteBuffer)
664682

665-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
666-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
683+
return bufferArray
667684
}
668685

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

683700
const errorCode = this.nativeAskar.askar_key_get_public_bytes(localKeyHandle, ret)
684701
this.handleError(errorCode)
702+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
703+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
704+
this.nativeAskar.askar_buffer_free(byteBuffer)
685705

686-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
687-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
706+
return bufferArray
688707
}
689708

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

694713
const errorCode = this.nativeAskar.askar_key_get_secret_bytes(localKeyHandle, ret)
695714
this.handleError(errorCode)
715+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
716+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
717+
this.nativeAskar.askar_buffer_free(byteBuffer)
696718

697-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
698-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
719+
return bufferArray
699720
}
700721

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

705726
const errorCode = this.nativeAskar.askar_key_sign_message(localKeyHandle, message, sigType, ret)
706727
this.handleError(errorCode)
728+
const byteBuffer = handleReturnPointer<ByteBufferType>(ret)
729+
const bufferArray = new Uint8Array(Buffer.from(secretBufferToBuffer(byteBuffer)))
730+
this.nativeAskar.askar_buffer_free(byteBuffer)
707731

708-
const secretBuffer = handleReturnPointer<SecretBufferType>(ret)
709-
return new Uint8Array(secretBufferToBuffer(secretBuffer))
732+
return bufferArray
710733
}
711734

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

737760
const errorCode = this.nativeAskar.askar_key_wrap_key(localKeyHandle, other, nonce, ret)
738761
this.handleError(errorCode)
739-
740762
const encryptedBuffer = handleReturnPointer<EncryptedBufferType>(ret)
741-
return encryptedBufferStructToClass(encryptedBuffer)
763+
const encryptedBufferClass = encryptedBufferStructToClass(encryptedBuffer)
764+
this.nativeAskar.askar_buffer_free(encryptedBuffer.secretBuffer)
765+
766+
return encryptedBufferClass
742767
}
743768

744769
public keyGetSupportedBackends(): string[] {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { nativeBindings } from './bindings'
22

3+
import type { ByteBufferType, SecretBufferStruct } from '../ffi'
34
// We need a mapping from string type value => type (property 'string' maps to type string)
45
interface StringTypeMapping {
56
pointer: Buffer
@@ -17,7 +18,11 @@ type ShapeOf<T> = {
1718
[Property in keyof T]: T[Property]
1819
}
1920
type StringTypeArrayToTypes<List extends (keyof StringTypeMapping)[]> = {
20-
[Item in keyof List]: List[Item] extends keyof StringTypeMapping ? StringTypeMapping[List[Item]] : Buffer
21+
[Item in keyof List]: List[Item] extends keyof StringTypeMapping
22+
? StringTypeMapping[List[Item]]
23+
: List[Item] extends Mutable<typeof SecretBufferStruct>
24+
? ByteBufferType | Buffer
25+
: Buffer
2126
}
2227

2328
// biome-ignore lint/suspicious/noExplicitAny:
0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)