npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@s77rt/react-native-sodium

v0.4.0

Published

A fast cryptography module for React Native using libsodium.

Readme


@s77rt/react-native-sodium is a high-performance cryptography library written in C++ that uses the JavaScript Interface (JSI) to provide access to the Sodium API. Sodium is a modern, easy-to-use library for encryption, decryption, signatures, password hashing, and more.

Installation

npm install @s77rt/react-native-sodium

Usage

For detailed documentation checkout libsodium Documentation.

Initialization

import sodium from "@s77rt/react-native-sodium";

if (sodium.sodium_init() < 0) {
	throw new Error("Failed to initialize sodium!");
}

Generating random data

Random number

randombytes_random(): number;
const rnd = sodium.randombytes_random();
console.log("Random number:", rnd);

Random number within interval

randombytes_uniform(upperBound: number): number;
const upperBound = 100;
const rnd = sodium.randombytes_uniform(upperBound);
console.log("Random number:", rnd);

Random byte sequence

randombytes_buf(buf: ArrayBuffer, size: number): void;
const buf = new ArrayBuffer(8);
sodium.randombytes_buf(buf, buf.byteLength);
console.log("Random byte sequence:", new Uint8Array(buf));

Deterministic random byte sequence

randombytes_buf_deterministic(buf: ArrayBuffer, size: number, seed: ArrayBuffer): void;
const buf = new ArrayBuffer(8);
const seed = new ArrayBuffer(sodium.randombytes_SEEDBYTES);
new TextEncoder().encodeInto("Couscous", new Uint8Array(seed));
sodium.randombytes_buf_deterministic(buf, buf.byteLength, seed);
console.log("Deterministic random byte sequence:", new Uint8Array(buf));

Generator deallocation

randombytes_close(): number;
randombytes_close();

Generator reseeding

randombytes_stir(): void;
randombytes_stir();

Public-key cryptography

Authenticated encryption

crypto_box_keypair(pk: ArrayBuffer, sk: ArrayBuffer): number;
crypto_box_seed_keypair(pk: ArrayBuffer, sk: ArrayBuffer, seed: ArrayBuffer): number;
crypto_box_easy(c: ArrayBuffer, m: ArrayBuffer, mLen: number, n: ArrayBuffer, pk: ArrayBuffer, sk: ArrayBuffer): number;
crypto_box_open_easy(m: ArrayBuffer, c: ArrayBuffer, cLen: number, n: ArrayBuffer, pk: ArrayBuffer, sk: ArrayBuffer): number;
const alicePK = new ArrayBuffer(sodium.crypto_box_PUBLICKEYBYTES);
const aliceSK = new ArrayBuffer(sodium.crypto_box_SECRETKEYBYTES);
const bobPK = new ArrayBuffer(sodium.crypto_box_PUBLICKEYBYTES);
const bobSK = new ArrayBuffer(sodium.crypto_box_SECRETKEYBYTES);
const nonce = new ArrayBuffer(sodium.crypto_box_NONCEBYTES);
const message = new TextEncoder().encode("Fennec fox").buffer;
const messageL = message.byteLength;
const cipherL = sodium.crypto_box_MACBYTES + messageL;
const cipher = new ArrayBuffer(cipherL);
const decrypted = new ArrayBuffer(messageL);
sodium.crypto_box_keypair(alicePK, aliceSK);
sodium.crypto_box_keypair(bobPK, bobSK);
sodium.randombytes_buf(nonce, nonce.byteLength);
sodium.crypto_box_easy(cipher, message, messageL, nonce, bobPK, aliceSK);
sodium.crypto_box_open_easy(decrypted, cipher, cipherL, nonce, alicePK, bobSK);
console.log("Message:", new Uint8Array(message));
console.log("Cipher:", new Uint8Array(cipher));
console.log("Decrypted:", new Uint8Array(decrypted));

Sealed boxes

crypto_box_seal(c: ArrayBuffer, m: ArrayBuffer, mLen: number, pk: ArrayBuffer): number;
crypto_box_seal_open(m: ArrayBuffer, c: ArrayBuffer, cLen: number, pk: ArrayBuffer, sk: ArrayBuffer): number;
const bobPK = new ArrayBuffer(sodium.crypto_box_PUBLICKEYBYTES);
const bobSK = new ArrayBuffer(sodium.crypto_box_SECRETKEYBYTES);
const message = new TextEncoder().encode("Fennec fox").buffer;
const messageL = message.byteLength;
const cipherL = sodium.crypto_box_SEALBYTES + messageL;
const cipher = new ArrayBuffer(cipherL);
const decrypted = new ArrayBuffer(messageL);
sodium.crypto_box_keypair(bobPK, bobSK);
sodium.crypto_box_seal(cipher, message, messageL, bobPK);
sodium.crypto_box_seal_open(decrypted, cipher, cipherL, bobPK, bobSK);
console.log("Message:", new Uint8Array(message));
console.log("Cipher:", new Uint8Array(cipher));
console.log("Decrypted:", new Uint8Array(decrypted));

Public-key signatures

crypto_sign_keypair(pk: ArrayBuffer, sk: ArrayBuffer): number;
crypto_sign_seed_keypair(pk: ArrayBuffer, sk: ArrayBuffer, seed: ArrayBuffer): number;
crypto_sign(sm: ArrayBuffer, smLenP: ArrayBuffer | null, m: ArrayBuffer, mLen: number, sk: ArrayBuffer): number;
crypto_sign_open(m: ArrayBuffer, mLenP: ArrayBuffer | null, sm: ArrayBuffer, smLen: number, pk: ArrayBuffer): number;
const bobPK = new ArrayBuffer(sodium.crypto_sign_PUBLICKEYBYTES);
const bobSK = new ArrayBuffer(sodium.crypto_sign_SECRETKEYBYTES);
const message = new TextEncoder().encode("I saw a fennec fox").buffer;
const messageL = message.byteLength;
const signedL = sodium.crypto_sign_BYTES + messageL;
const signed = new ArrayBuffer(signedL);
sodium.crypto_sign_keypair(bobPK, bobSK);
if (sodium.crypto_sign(signed, null, message, messageL, bobSK) !== 0) {
	throw new Error("Failed to sign the message!");
}
if (sodium.crypto_sign_open(null, null, signed, signedL, bobPK) !== 0) {
	throw new Error("Invalid signature!");
}
console.log("Message:", new Uint8Array(message));
console.log("Signed:", new Uint8Array(signed));

Hashing

Generic hashing

Single-part
crypto_generichash(output: ArrayBuffer, outputLen: number, input: ArrayBuffer, inputLen: number, key: ArrayBuffer | null, keyLen: number): number;
const output = new ArrayBuffer(sodium.crypto_generichash_BYTES);
const input = new TextEncoder().encode("Fennec fox").buffer;
const key = new ArrayBuffer(sodium.crypto_generichash_KEYBYTES);
sodium.crypto_generichash_keygen(key);
sodium.crypto_generichash(
	output,
	output.byteLength,
	input,
	input.byteLength,
	key,
	key.byteLength
);
console.log(
	"Key:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(key.byteLength * 2 + 1),
		key.byteLength * 2 + 1,
		key,
		key.byteLength
	)
);
console.log(
	"Hash:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(output.byteLength * 2 + 1),
		output.byteLength * 2 + 1,
		output,
		output.byteLength
	)
);
Multi-part
crypto_generichash_init(state: Record<string, never>, key: ArrayBuffer | null, keyLen: number, outputLen: number): number;
crypto_generichash_update(state: Record<string, never>, input: ArrayBuffer, inputLen: number): number;
crypto_generichash_final(state: Record<string, never>, output: ArrayBuffer, outputLen: number): number;
const output = new ArrayBuffer(sodium.crypto_generichash_BYTES);
const input1 = new TextEncoder().encode("Fennec ").buffer;
const input2 = new TextEncoder().encode("fox").buffer;
const key = new ArrayBuffer(sodium.crypto_generichash_KEYBYTES);
const state = {};
sodium.crypto_generichash_keygen(key);
sodium.crypto_generichash_init(state, key, key.byteLength, output.byteLength);
sodium.crypto_generichash_update(state, input1, input1.byteLength);
sodium.crypto_generichash_update(state, input2, input2.byteLength);
sodium.crypto_generichash_final(state, output, output.byteLength);
console.log(
	"Key:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(key.byteLength * 2 + 1),
		key.byteLength * 2 + 1,
		key,
		key.byteLength
	)
);
console.log(
	"Hash:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(output.byteLength * 2 + 1),
		output.byteLength * 2 + 1,
		output,
		output.byteLength
	)
);
Keygen
crypto_generichash_keygen(k: ArrayBuffer): void;
const k = new ArrayBuffer(sodium.crypto_generichash_KEYBYTES);
sodium.crypto_generichash_keygen(k);
console.log(
	"Key:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(k.byteLength * 2 + 1),
		k.byteLength * 2 + 1,
		k,
		k.byteLength
	)
);

Short-input hashing

Short hash
crypto_shorthash(output: ArrayBuffer, input: ArrayBuffer, inputLen: number, k: ArrayBuffer): number;
const output = new ArrayBuffer(sodium.crypto_shorthash_BYTES);
const input = new TextEncoder().encode("Fennec fox").buffer;
const k = new ArrayBuffer(sodium.crypto_shorthash_KEYBYTES);
sodium.crypto_shorthash_keygen(k);
sodium.crypto_shorthash(output, input, input.byteLength, k);
console.log(
	"Key:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(k.byteLength * 2 + 1),
		k.byteLength * 2 + 1,
		k,
		k.byteLength
	)
);
console.log(
	"Hash:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(output.byteLength * 2 + 1),
		output.byteLength * 2 + 1,
		output,
		output.byteLength
	)
);
Keygen
crypto_shorthash_keygen(k: ArrayBuffer): void;
const k = new ArrayBuffer(sodium.crypto_shorthash_KEYBYTES);
sodium.crypto_shorthash_keygen(k);
console.log(
	"Key:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(k.byteLength * 2 + 1),
		k.byteLength * 2 + 1,
		k,
		k.byteLength
	)
);

Padding

Pad

sodium_pad(paddedBufLenP: ArrayBuffer, buf: ArrayBuffer, unpaddedBufLen: number, blockSize: number, maxBufLen: number): number;
const paddedBufLenP = new ArrayBuffer(8); // 8 bytes are needed to store a size_t number
const buf = new ArrayBuffer(64);
const message = new TextEncoder().encode("Fennec fox");
new Uint8Array(buf).set(message);
const unpaddedBufLen = message.byteLength;
const blockSize = 16;
sodium.sodium_pad(
	paddedBufLenP,
	buf,
	unpaddedBufLen,
	blockSize,
	buf.byteLength
);
const paddedBufLen = Number(new DataView(paddedBufLenP).getBigUint64(0, true)); // Safe as long as you are not working with a 9PB data
console.log("Padded buf:", new Uint8Array(buf.slice(0, paddedBufLen)));

Unpad

sodium_unpad(unpaddedBufLenP: ArrayBuffer, buf: ArrayBuffer, paddedBufLen: number, blockSize: number): number;
const unpaddedBufLenP = new ArrayBuffer(8); // 8 bytes are needed to store a size_t number
const buf = new Uint8Array([
	70, 101, 110, 110, 101, 99, 32, 102, 111, 120, 128, 0, 0, 0, 0, 0,
]).buffer;
const blockSize = 16;
sodium.sodium_unpad(unpaddedBufLenP, buf, buf.byteLength, blockSize);
const unpaddedBufLen = Number(
	new DataView(unpaddedBufLenP).getBigUint64(0, true) // Safe as long as you are not working with a 9PB data
);
console.log("Unpadded buf:", new Uint8Array(buf.slice(0, unpaddedBufLen)));

Helpers

Constant-time test for equality

sodium_memcmp(b1_: ArrayBuffer, b2_: ArrayBuffer, len: number): number;
const b1_ = new Uint8Array([7, 7, 1, 2]).buffer;
const b2_ = new Uint8Array([7, 7, 100, 200]).buffer;
console.log("isEqual:", sodium.sodium_memcmp(b1_, b2_, 2) === 0);

Hexadecimal encoding/decoding

sodium_bin2hex(hex: ArrayBuffer, hexMaxLen: number, bin: ArrayBuffer, binLen: number): string;
sodium_hex2bin(bin: ArrayBuffer, binMaxLen: number, hex: string, hexLen: number, ignore: string | null, binLen: ArrayBuffer, hexEnd: ArrayBuffer | null): number;
const bin = new Uint8Array([0, 255, 0, 255]).buffer;
console.log(
	"Hex:",
	sodium.sodium_bin2hex(
		new ArrayBuffer(bin.byteLength * 2 + 1), // Each byte is encoded into two characters, plus one for the null character
		bin.byteLength * 2 + 1,
		bin,
		bin.byteLength
	)
);
const hex = "00ff00ff";
const bin = new ArrayBuffer(hex.length / 2); // Every two characters fit into a single byte
sodium.sodium_hex2bin(
	bin,
	bin.byteLength,
	hex,
	hex.length,
	null,
	new ArrayBuffer(8), // 8 bytes are needed to store a size_t number, not used in this example
	null
);
console.log("Binary:", new Uint8Array(bin));

Base64 encoding/decoding

sodium_base64_encoded_len(binLen: number, variant: number): number;
sodium_bin2base64(b64: ArrayBuffer, b64MaxLen: number, bin: ArrayBuffer, binLen: number, variant: number): string;
sodium_base642bin(bin: ArrayBuffer, binMaxLen: number, b64: string, b64Len: number, ignore: string | null, binLen: ArrayBuffer, b64End: ArrayBuffer | null, variant: number): number;
const variant = sodium.sodium_base64_VARIANT_ORIGINAL;
const bin = new Uint8Array([0, 255, 0, 255]).buffer;
const b64Len = sodium.sodium_base64_encoded_len(bin.byteLength, variant);
console.log(
	"Base64:",
	sodium.sodium_bin2base64(
		new ArrayBuffer(b64Len),
		b64Len,
		bin,
		bin.byteLength,
		variant
	)
);
const variant = sodium.sodium_base64_VARIANT_ORIGINAL;
const b64 = "AP8A/w==";
const bin = new ArrayBuffer(Math.ceil((b64.length / 4) * 3)); // Bin will take at most (b64.length / 4) * 3 bytes. Use binLen to get the exact length
const binLen = new ArrayBuffer(8); // 8 bytes are needed to store a size_t number
sodium.sodium_base642bin(
	bin,
	bin.byteLength,
	b64,
	b64.length,
	null,
	binLen,
	null,
	variant
);
const binLenAsNumber = Number(new DataView(binLen).getBigUint64(0, true)); // Safe as long as you are not working with a 9PB data
console.log("Binary:", new Uint8Array(bin.slice(0, binLenAsNumber)));

Large numbers arithmetic operations

sodium_increment(n: ArrayBuffer, nLen: number): void;
sodium_add(a: ArrayBuffer, b: ArrayBuffer, len: number): void;
sodium_sub(a: ArrayBuffer, b: ArrayBuffer, len: number): void;
sodium_compare(b1_: ArrayBuffer, b2_: ArrayBuffer, len: number): number;
const a = new Uint8Array(16).fill(255, 0, 10).buffer; // a=1208925819614629174706175
const b = new Uint8Array(16).fill(255, 0, 10).buffer; // b=1208925819614629174706175
console.log("Comparison", sodium.sodium_compare(a, b, a.byteLength));
sodium.sodium_increment(a, a.byteLength);
console.log("Comparison", sodium.sodium_compare(a, b, a.byteLength));
sodium.sodium_sub(a, b, a.byteLength);
console.log("Comparison", sodium.sodium_compare(a, b, a.byteLength));

Testing for all zeros

sodium_is_zero(n: ArrayBuffer, nLen: number): number;
const n = new ArrayBuffer(8);
console.log("isZero", sodium.sodium_is_zero(n, n.byteLength) === 1);

Clearing the stack

sodium_stackzero(len: number): void;
sodium.sodium_stackzero(4);

FAQ

Q: Why functions that take char* parameters sometimes use ArrayBuffer and other times use string?

A: If the input is encoding-dependant an ArrayBuffer is used and it's the responsibility of the user to choose the desired encoding. You can use TextEncoder to generate an array buffer with UTF-8 encoding. On the other hand if the input is guaranteed to be representable in ASCII then it will be interpreted as ASCII and a string is used.

If the input is meant to be written into then an ArrayBuffer must be used since strings are immutable.

Q: Why are some libsodium functions not implemented?

A: This library aims to provide a 1:1 libsodium compatibility however functions are implemented progressively and per priority and needs. Feel free to submit an issue for prioritization.

PRs are welcome!

License

MIT