1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-06 19:53:59 +00:00

Implement secure memory kv store

This commit is contained in:
Bernd Schoolmann
2025-06-14 02:09:34 +02:00
parent 50051e57a7
commit 662bbc79be
9 changed files with 514 additions and 125 deletions

View File

@@ -162,7 +162,7 @@ dependencies = [
"serde_repr",
"tokio",
"url",
"zbus 5.6.0",
"zbus",
]
[[package]]
@@ -879,6 +879,7 @@ dependencies = [
"keytar",
"libc",
"log",
"memsec",
"oo7",
"pin-project",
"pkcs8",
@@ -900,7 +901,7 @@ dependencies = [
"widestring",
"windows 0.61.1",
"windows-future",
"zbus 4.4.0",
"zbus",
"zbus_polkit",
"zeroizing-alloc",
]
@@ -1701,6 +1702,17 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memsec"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c797b9d6bb23aab2fc369c65f871be49214f5c759af65bde26ffaaa2b646b492"
dependencies = [
"getrandom 0.2.16",
"libc",
"windows-sys 0.45.0",
]
[[package]]
name = "mime"
version = "0.3.17"
@@ -2063,10 +2075,10 @@ dependencies = [
"sha2",
"subtle",
"tokio",
"zbus 5.6.0",
"zbus_macros 5.6.0",
"zbus",
"zbus_macros",
"zeroize",
"zvariant 5.5.1",
"zvariant",
]
[[package]]
@@ -2715,17 +2727,6 @@ dependencies = [
"syn",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
@@ -3677,6 +3678,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -3695,6 +3705,21 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@@ -3726,6 +3751,12 @@ dependencies = [
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@@ -3738,6 +3769,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -3750,6 +3787,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -3768,6 +3811,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -3789,6 +3838,12 @@ dependencies = [
"windows-core 0.61.0",
]
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -3801,6 +3856,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -3813,6 +3874,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
@@ -3921,9 +3988,9 @@ dependencies = [
[[package]]
name = "zbus"
version = "4.4.0"
version = "5.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236"
dependencies = [
"async-broadcast",
"async-executor",
@@ -3938,90 +4005,37 @@ dependencies = [
"enumflags2",
"event-listener",
"futures-core",
"futures-sink",
"futures-util",
"hex",
"nix",
"ordered-stream",
"rand 0.8.5",
"serde",
"serde_repr",
"sha1",
"static_assertions",
"tracing",
"uds_windows",
"windows-sys 0.52.0",
"xdg-home",
"zbus_macros 4.4.0",
"zbus_names 3.0.0",
"zvariant 4.2.0",
]
[[package]]
name = "zbus"
version = "5.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58"
dependencies = [
"async-broadcast",
"async-recursion",
"async-trait",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"nix",
"ordered-stream",
"serde",
"serde_repr",
"static_assertions",
"tokio",
"tracing",
"uds_windows",
"windows-sys 0.59.0",
"winnow",
"zbus_macros 5.6.0",
"zbus_names 4.2.0",
"zvariant 5.5.1",
"xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "4.4.0"
version = "5.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils 2.1.0",
]
[[package]]
name = "zbus_macros"
version = "5.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d2e12843c75108c00c618c2e8ef9675b50b6ec095b36dc965f2e5aed463c15"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zbus_names 4.2.0",
"zvariant 5.5.1",
"zvariant_utils 3.2.0",
]
[[package]]
name = "zbus_names"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
dependencies = [
"serde",
"static_assertions",
"zvariant 4.2.0",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
@@ -4033,20 +4047,20 @@ dependencies = [
"serde",
"static_assertions",
"winnow",
"zvariant 5.5.1",
"zvariant",
]
[[package]]
name = "zbus_polkit"
version = "4.0.0"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00a29bfa927b29f91b7feb4e1990f2dd1b4604072f493dc2f074cf59e4e0ba90"
checksum = "ad23d5c4d198c7e2641b33e6e0d1f866f117408ba66fe80bbe52e289eeb77c52"
dependencies = [
"enumflags2",
"serde",
"serde_repr",
"static_assertions",
"zbus 4.4.0",
"zbus",
]
[[package]]
@@ -4149,19 +4163,6 @@ dependencies = [
"syn",
]
[[package]]
name = "zvariant"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
dependencies = [
"endi",
"enumflags2",
"serde",
"static_assertions",
"zvariant_derive 4.2.0",
]
[[package]]
name = "zvariant"
version = "5.5.1"
@@ -4173,21 +4174,8 @@ dependencies = [
"serde",
"url",
"winnow",
"zvariant_derive 5.5.1",
"zvariant_utils 3.2.0",
]
[[package]]
name = "zvariant_derive"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils 2.1.0",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
@@ -4200,18 +4188,7 @@ dependencies = [
"proc-macro2",
"quote",
"syn",
"zvariant_utils 3.2.0",
]
[[package]]
name = "zvariant_utils"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [
"proc-macro2",
"quote",
"syn",
"zvariant_utils",
]
[[package]]

View File

@@ -56,6 +56,7 @@ ed25519 = { workspace = true, features = ["pkcs8"] }
bytes = { workspace = true }
sysinfo = { workspace = true, features = ["windows"] }
zeroizing-alloc = { workspace = true }
memsec = { version = "0.7.0", features = ["alloc_ext"] }
[target.'cfg(windows)'.dependencies]
widestring = { workspace = true, optional = true }

View File

@@ -8,6 +8,7 @@ pub mod ipc;
pub mod password;
pub mod powermonitor;
pub mod process_isolation;
pub mod secure_memory;
pub mod ssh_agent;
use zeroizing_alloc::ZeroAlloc;

View File

@@ -0,0 +1,125 @@
use std::collections::HashMap;
use pkcs8::der::zeroize::Zeroize;
use windows::Win32::Security::Cryptography::{
CryptProtectMemory, CryptUnprotectMemory, CRYPTPROTECTMEMORY_BLOCK_SIZE,
CRYPTPROTECTMEMORY_SAME_PROCESS,
};
use crate::secure_memory::SecureMemoryStore;
/// https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata
/// The DPAPI store encrypts data using the Windows Data Protection API (DPAPI). The key is bound
/// to the current process, and cannot be decrypted by other user-mode processes.
///
/// Note: Admin processes can still decrypt this memory:
/// https://blog.slowerzs.net/posts/cryptdecryptmemory/
pub(super) struct DpapiSecretKVStore {
map: HashMap<String, Vec<u8>>,
}
impl DpapiSecretKVStore {
pub(super) fn new() -> Self {
DpapiSecretKVStore {
map: HashMap::new(),
}
}
}
impl SecureMemoryStore for DpapiSecretKVStore {
fn put(&mut self, key: String, value: &mut [u8]) {
let length_header_len = std::mem::size_of::<usize>();
// The allocated data has to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE, so we pad it and write the length in front
// We are storing LENGTH|DATA|00..00, where LENGTH is the length of DATA, the total length is a multiple
// of CRYPTPROTECTMEMORY_BLOCK_SIZE, and the padding is filled with zeros.
let data_len = value.len();
let len_with_header = data_len + length_header_len;
let padded_length = len_with_header + CRYPTPROTECTMEMORY_BLOCK_SIZE as usize
- (len_with_header % CRYPTPROTECTMEMORY_BLOCK_SIZE as usize);
let mut padded_data = vec![0u8; padded_length];
padded_data[..length_header_len].copy_from_slice(&data_len.to_le_bytes());
padded_data[length_header_len..][..data_len].copy_from_slice(value);
// Protect the memory using DPAPI
unsafe {
CryptProtectMemory(
padded_data.as_mut_ptr() as *mut core::ffi::c_void,
padded_length as u32,
CRYPTPROTECTMEMORY_SAME_PROCESS,
)
}
.expect("crypt_protect_memory should work");
self.map.insert(key, padded_data);
}
fn get(&self, key: &str) -> Option<Vec<u8>> {
self.map.get(key).map(|data| {
// A copy is created, that is then mutated by the DPAPI unprotect function.
let mut data = data.clone();
unsafe {
CryptUnprotectMemory(
data.as_mut_ptr() as *mut core::ffi::c_void,
data.len() as u32,
CRYPTPROTECTMEMORY_SAME_PROCESS,
)
}
.expect("crypt_unprotect_memory should work");
// Unpad the data to retrieve the original value
let length_header_size = std::mem::size_of::<usize>();
let length_bytes = &data[..length_header_size];
let data_length = usize::from_le_bytes(
length_bytes
.try_into()
.expect("length header should be usize"),
);
data[length_header_size..length_header_size + data_length].to_vec()
})
}
fn has(&self, key: &str) -> bool {
self.map.contains_key(key)
}
fn remove(&mut self, key: &str) {
if let Some(value) = self.map.remove(key) {
unsafe {
std::ptr::write_bytes(value.as_mut_ptr(), 0, value.len());
}
}
}
fn clear(&mut self) {
for (_, value) in self.map.drain() {
unsafe {
std::ptr::write_bytes(value.as_mut_ptr(), 0, value.len());
}
}
}
}
impl Drop for DpapiSecretKVStore {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dpapi_secret_kv_store() {
let mut store = DpapiSecretKVStore::new();
let key = "test_key".to_string();
let mut value = vec![1, 2, 3, 4, 5];
store.put(key.clone(), &mut value);
assert!(store.has(&key));
assert_eq!(store.get(&key), Some(value));
}
}

View File

@@ -0,0 +1,106 @@
use std::{collections::HashMap, ptr::NonNull, sync::LazyLock};
use crate::secure_memory::SecureMemoryStore;
/// https://man.archlinux.org/man/memfd_secret.2.en
/// The memfd_secret store protects the data using the `memfd_secret` syscall. The
/// data is inaccessible to other user-mode processes, and even to root in most cases.
/// If arbitrary data can be executed in the kernel, the data can still be retrieved:
/// https://github.com/JonathonReinhart/nosecmem
pub(super) struct MemfdSecretKVStore {
map: HashMap<String, std::ptr::NonNull<[u8]>>,
}
impl MemfdSecretKVStore {
pub(super) fn new() -> Self {
MemfdSecretKVStore {
map: HashMap::new(),
}
}
}
impl SecureMemoryStore for MemfdSecretKVStore {
fn put(&mut self, key: String, value: &[u8]) {
let mut ptr: std::ptr::NonNull<[u8]> = unsafe {
memsec::memfd_secret_sized(value.len()).expect("memfd_secret_sized should work")
};
unsafe {
std::ptr::copy_nonoverlapping(value.as_ptr(), ptr.as_mut().as_mut_ptr(), value.len());
}
self.map.insert(key, ptr);
}
fn get(&self, key: &str) -> Option<Vec<u8>> {
let ptr = self.map.get(key)?;
let value = unsafe { ptr.as_ref() };
Some(value.to_vec())
}
fn has(&self, key: &str) -> bool {
self.map.contains_key(key)
}
fn remove(&mut self, key: &str) {
if let Some(value) = self.map.remove(key) {
unsafe {
memsec::free_memfd_secret(value);
}
}
}
fn clear(&mut self) {
for (_, value) in self.map.drain() {
unsafe {
memsec::free_memfd_secret(value);
}
}
}
}
impl Drop for MemfdSecretKVStore {
fn drop(&mut self) {
self.clear();
}
}
pub(super) fn is_supported() -> bool {
// To test if memfd_secret is supported, we try to allocate a 1 byte and see if that
// succeeds.
static IS_SUPPORTED: LazyLock<bool> = LazyLock::new(|| {
let Some(ptr): Option<NonNull<[u8]>> = (unsafe { memsec::memfd_secret_sized(1) }) else {
return false;
};
// Check that the pointer is readable and writable
let result = unsafe {
let ptr = ptr.as_ptr() as *mut u8;
*ptr = 30;
*ptr += 107;
*ptr == 137
};
unsafe { memsec::free_memfd_secret(ptr) };
result
});
*IS_SUPPORTED
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memfd_secret_kv_store() {
let mut store = MemfdSecretKVStore::new();
let key = "test_key".to_string();
let value = vec![1, 2, 3, 4, 5];
store.put(key.clone(), &value);
assert!(store.has(&key));
assert_eq!(store.get(&key), Some(value.clone()));
store.remove(&key);
assert!(!store.has(&key));
assert_eq!(store.get(&key), None);
}
}

View File

@@ -0,0 +1,80 @@
use std::collections::HashMap;
use crate::secure_memory::SecureMemoryStore;
/// The mlock store protects the data using the `mlock`. This prevents swapping to disk
/// but does not provide protection against user-mode memory dumps or debugger access.
pub(super) struct MlockSecretKVStore {
map: HashMap<String, std::ptr::NonNull<[u8]>>,
}
impl MlockSecretKVStore {
pub(super) fn new() -> Self {
MlockSecretKVStore {
map: HashMap::new(),
}
}
}
impl SecureMemoryStore for MlockSecretKVStore {
fn put(&mut self, key: String, value: &[u8]) {
let mut ptr: std::ptr::NonNull<[u8]> =
unsafe { memsec::malloc_sized(value.len()).expect("malloc_sized should work") };
unsafe {
std::ptr::copy_nonoverlapping(value.as_ptr(), ptr.as_mut().as_mut_ptr(), value.len());
}
self.map.insert(key, ptr);
}
fn get(&self, key: &str) -> Option<Vec<u8>> {
let ptr = self.map.get(key)?;
let value = unsafe { ptr.as_ref() };
Some(value.to_vec())
}
fn has(&self, key: &str) -> bool {
self.map.contains_key(key)
}
fn remove(&mut self, key: &str) {
if let Some(value) = self.map.remove(key) {
unsafe {
memsec::free(value);
}
}
}
fn clear(&mut self) {
for (_, value) in self.map.drain() {
unsafe {
memsec::free(value);
}
}
}
}
impl Drop for MlockSecretKVStore {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mlock_secret_kv_store() {
let mut store = MlockSecretKVStore::new();
let key = "test_key".to_string();
let value = vec![1, 2, 3, 4, 5];
store.put(key.clone(), &value);
assert!(store.has(&key));
assert_eq!(store.get(&key), Some(value.clone()));
store.remove(&key);
assert!(!store.has(&key));
assert_eq!(store.get(&key), None);
}
}

View File

@@ -0,0 +1,44 @@
#[cfg(target_os = "windows")]
mod dpapi;
#[cfg(target_os = "linux")]
mod memfd_secret;
mod mlock;
/// The secure memory store provides an ephemeral key-value store for sensitive data.
/// Data stored in this store is prevented from being swapped to disk and zeroed out. Additionally,
/// platform-specific protections are applied to prevent memory dumps or debugger access from
/// reading the stored values.
pub trait SecureMemoryStore {
/// Stores a copy of the provided value in secure memory.
fn put(&mut self, key: String, value: &[u8]);
/// Retrieves a copy of the value associated with the given key from secure memory.
/// This copy does not have additional memory protections applied, and should be zeroed when no
/// longer needed.
fn get(&self, key: &str) -> Option<Vec<u8>>;
/// Checks if a value is stored under the given key.
fn has(&self, key: &str) -> bool;
/// Removes the value associated with the given key from secure memory.
fn remove(&mut self, key: &str);
/// Clears all values stored in secure memory.
fn clear(&mut self);
}
/// Creates a new secure memory store based on the platform.
pub fn create_secure_memory_store() -> Box<dyn SecureMemoryStore> {
#[cfg(target_os = "linux")]
{
if memfd_secret::is_supported() {
Box::new(memfd_secret::MemfdSecretKVStore::new())
} else {
Box::new(mlock::MlockSecretKVStore::new())
}
}
#[cfg(target_os = "windows")]
{
Box::new(dpapi::DpapiSecretKVStore::new())
}
#[cfg(target_os = "macos")]
{
Box::new(mlock::MlockSecretKVStore::new())
}
}

View File

@@ -198,3 +198,13 @@ export declare namespace logging {
}
export function initNapiLog(jsLogFn: (err: Error | null, arg0: LogLevel, arg1: string) => any): void
}
export declare namespace secure_memory {
export class SecureMemoryStoreWrapper {
constructor()
set(key: string, value: Uint8Array): void
get(key: string): Buffer | null
has(key: string): boolean
remove(key: string): void
clear(): void
}
}

View File

@@ -875,3 +875,48 @@ pub mod logging {
fn flush(&self) {}
}
}
#[napi]
pub mod secure_memory {
use desktop_core::secure_memory::SecureMemoryStore;
use napi::bindgen_prelude::Buffer;
#[napi]
pub struct SecureMemoryStoreWrapper(Box<dyn SecureMemoryStore>);
#[napi]
impl SecureMemoryStoreWrapper {
#[napi(constructor)]
pub fn new() -> Self {
let memory_store = desktop_core::secure_memory::create_secure_memory_store();
SecureMemoryStoreWrapper(memory_store)
}
#[napi]
pub fn set(&mut self, key: String, value: &[u8]) {
self.0.put(key, value);
}
#[napi]
pub fn get(&self, key: String) -> Option<Buffer> {
self.0.get(&key).map(Buffer::from)
}
#[napi]
pub fn has(&self, key: String) -> bool {
self.0.has(&key)
}
#[napi]
pub fn remove(&mut self, key: String) {
self.0.remove(&key);
}
#[napi]
pub fn clear(&mut self) {
self.0.clear();
}
}
impl Default for SecureMemoryStoreWrapper {
fn default() -> Self {
Self::new()
}
}
}