import sodium from 'sodium-native'
import b4a from 'b4a'
const NONCE_BYTES = sodium.crypto_secretbox_NONCEBYTES
const KEY_BYTES = sodium.crypto_secretbox_KEYBYTES
const MAC_BYTES = sodium.crypto_secretbox_MACBYTES
function generateKey () {
const key = b4a.allocUnsafe(KEY_BYTES)
sodium.randombytes_buf(key)
return key
}
function deriveKey (password, salt) {
if (!salt) {
salt = b4a.allocUnsafe(sodium.crypto_pwhash_SALTBYTES)
sodium.randombytes_buf(salt)
}
const key = b4a.allocUnsafe(KEY_BYTES)
sodium.crypto_pwhash(
key,
b4a.from(password),
salt,
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
sodium.crypto_pwhash_ALG_DEFAULT
)
return { key, salt }
}
function encrypt (plaintext, key) {
const nonce = b4a.allocUnsafe(NONCE_BYTES)
sodium.randombytes_buf(nonce)
const input = b4a.isBuffer(plaintext) ? plaintext : b4a.from(plaintext)
const cipher = b4a.allocUnsafe(input.length + MAC_BYTES)
sodium.crypto_secretbox_easy(cipher, input, nonce, key)
// Return nonce + ciphertext
const result = b4a.allocUnsafe(NONCE_BYTES + cipher.length)
b4a.copy(nonce, result, 0)
b4a.copy(cipher, result, NONCE_BYTES)
return result
}
function decrypt (encrypted, key) {
const nonce = encrypted.subarray(0, NONCE_BYTES)
const cipher = encrypted.subarray(NONCE_BYTES)
const plaintext = b4a.allocUnsafe(cipher.length - MAC_BYTES)
const ok = sodium.crypto_secretbox_open_easy(plaintext, cipher, nonce, key)
if (!ok) throw new Error('Decryption failed — wrong key or corrupted data')
return plaintext
}
export { generateKey, deriveKey, encrypt, decrypt, KEY_BYTES }