Skip to content

Commit

Permalink
chore: initial preview release
Browse files Browse the repository at this point in the history
  • Loading branch information
TTtie committed Aug 19, 2024
0 parents commit 97eff21
Show file tree
Hide file tree
Showing 16 changed files with 489 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "libsodium"]
path = libsodium
url = https://github.com/jedisct1/libsodium.git
branch = stable
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
libsodium/
/build-libsodium.sh
20 changes: 20 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
The MIT License (MIT)

Copyright (c) 2024- Project Dysnomia Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# @projectdysnomia/libsodium
A minimal unified interface for libsodium-based XChaCha20 Poly1305 encryption/decryption.

This is achieved by using the following libraries:
- [`sodium-native`](https://www.npmjs.com/package/sodium-native) uses a native build of libsodium and is preferred when available.
- A heavily stripped down custom build of [libsodium.js](https://www.npmjs.com/package/libsodium) is used as a fallback.
The differences between libsodium.js and this package are as follows:
- The WASM binary initiates synchronously, making it better suited for CommonJS environments
- Only the WASM binary is provided, there is no fallback to JS-based emulation
- Only `crypto_aead_xchacha20poly1305_ietf_*` (sans `crypto_aead_xchacha20poly1305_ietf_keygen`) and `sodium_init` methods are exposed in the WASM binary

## Installation
Note that
```sh
# install with the WASM backend bundled by default
npm install @projectdysnomia/libsodium
# optionally, you may also install sodium-native for better performance
npm install sodium-native
```

## Usage
```js
// auto-selected backend: native is preferred
const mod = require("@projectdysnomia/libsodium");
// native-only
const mod = require("@projectdysnomia/libsodium/native");
// WASM-only
const mod = require("@projectdysnomia/libsodium/wasm");

```
113 changes: 113 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { Buffer } from "node:buffer";

declare namespace LibSodium {
/**
* Represents a buffer for interfacing with the methods of this library.
*
* It is safe to store references to these objects in your code.
*/
interface BufferPointer {
/**
* A view into the buffer. Accessing this property is always
* guaranteed to return a valid view into the buffer.
*
* It is not recommended to store the returned buffer, as on
* WASM the memory allocation may grow, invalidating the
* returned buffer.
*/
buffer: Buffer;
/**
* Releases the memory of the buffer.
*/
free(): void;
/**
* Returns a buffer pointer to a portion of the backing buffer.
* @param start The start index of the subarray
* @param end The end index of the subarray
*/
subarray(start: number, end: number): BufferPointer;
}

/**
* Allocates a new buffer of the specified length in the appropriate backend.
* @param byteLength The amount of bytes to allocate
* @param zero Whether to zero out the allocated memory. Defaults to `true`
*/
export function alloc(byteLength: number, zero?: boolean): BufferPointer;

/**
* Transfers the buffer over to the appropriate backend.
*
* On the native backend, the buffer is wrapped in a {@link BufferPointer}.
* On WASM, the buffer is copied into the WASM memory and a {@link BufferPointer}
* is returned to the copied memory.
* @param buffer The buffer to transfer
*/
export function transfer(buffer: Buffer): BufferPointer;

/**
* The byte length of the MAC code.
*/
export const crypto_aead_xchacha20poly1305_ietf_ABYTES: number;
/**
* The byte length of the encryption key.
*/
export const crypto_aead_xchacha20poly1305_ietf_KEYBYTES: number;
/**
* The maximum byte length of the message to encrypt/decrypt.
*/
export const crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: number;
/**
* The byte length of the public nonce.
*/
export const crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: number;
/**
* The byte length of the secret nonce. Always 0.
*/
export const crypto_aead_xchacha20poly1305_ietf_NSECBYTES: 0;

/**
* Decrypts a message in combined mode.
* @param m Pointer to the decrypted message memory
* @param nsec Unused, must be null
* @param c The ciphertext to decrypt
* @param ad Additional data
* @param npub The public nonce
* @param k The decryption key
*/
export function crypto_aead_xchacha20poly1305_ietf_decrypt(m: BufferPointer, nsec: null, c: BufferPointer, ad: BufferPointer | null, npub: BufferPointer, k: BufferPointer): number;
/**
* Decrypts a message in detached mode.
* @param m Pointer to the decrypted message memory
* @param nsec Unused, must be null
* @param c Pointer to the ciphertext to decrypt
* @param mac Pointer to the message authentication code
* @param ad Pointer to additional data
* @param npub Pointer to the public nonce
* @param k Pointer to the decryption key
*/
export function crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m: BufferPointer, nsec: null, c: BufferPointer, mac: BufferPointer, ad: BufferPointer | null, npub: BufferPointer, k: BufferPointer): number;
/**
* Encrypts a message in combined mode.
* @param c Pointer to the resulting ciphertext
* @param m Pointer to the message to encrypt
* @param ad Pointer to additional data
* @param nsec Unused, must be null
* @param npub Pointer to the public nonce
* @param k Pointer to the encryption key
*/
export function crypto_aead_xchacha20poly1305_ietf_encrypt(c: BufferPointer, m: BufferPointer, ad: BufferPointer | null, nsec: null, npub: BufferPointer, k: BufferPointer): number;
/**
* Encrypts a message in detached mode.
* @param c Pointer to the resulting ciphertext
* @param mac Pointer to the resulting message authentication code
* @param m Pointer to the message to encrypt
* @param ad Pointer to additional data
* @param nsec Unused, must be null
* @param npub Pointer to the public nonce
* @param k Pointer to the encryption key
*/
export function crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c: BufferPointer, mac: BufferPointer, m: BufferPointer, ad: BufferPointer | null, nsec: null, npub: BufferPointer, k: BufferPointer): number;
}

export = LibSodium;
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
try {
module.exports = require("./native.js");
} catch {
module.exports = require("./wasm.js");
}
1 change: 1 addition & 0 deletions libsodium
Submodule libsodium added at b7b1c0
67 changes: 67 additions & 0 deletions native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const {
// constants
crypto_aead_xchacha20poly1305_ietf_ABYTES,
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX,
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
crypto_aead_xchacha20poly1305_ietf_NSECBYTES,

// functions
crypto_aead_xchacha20poly1305_ietf_decrypt,
crypto_aead_xchacha20poly1305_ietf_decrypt_detached,
crypto_aead_xchacha20poly1305_ietf_encrypt,
crypto_aead_xchacha20poly1305_ietf_encrypt_detached,
} = require("sodium-native");

class NativeBufferPointer {
constructor(buf) {
this.buffer = buf;
}

free() {
// no-op - the buffer will be reclaimed by the GC
}

subarray(start, end) {
if (start > end) throw new RangeError("start must be less than or equal to end");
return new NativeBufferPointer(this.buffer.subarray(start, end));
}
}

/**
* @type {import("./index")}
*/
module.exports = {
alloc(byteLength, zero = true) {
return new NativeBufferPointer(zero ? Buffer.alloc(byteLength) : Buffer.allocUnsafe(byteLength));
},

transfer(buf) {
return new NativeBufferPointer(buf);
},

crypto_aead_xchacha20poly1305_ietf_ABYTES,
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX,
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
crypto_aead_xchacha20poly1305_ietf_NSECBYTES,

crypto_aead_xchacha20poly1305_ietf_decrypt(m, nsec, c, ad, npub, k) {
return crypto_aead_xchacha20poly1305_ietf_decrypt(m.buffer, nsec, c.buffer, ad?.buffer ?? null, npub.buffer, k.buffer);
},

crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, nsec, c, mac, ad, npub, k) {
return crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m.buffer, nsec, c.buffer, mac.buffer, ad?.buffer ?? null, npub.buffer, k.buffer);
},

crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, nsec, npub, k) {
return crypto_aead_xchacha20poly1305_ietf_encrypt(c.buffer, m.buffer, ad?.buffer ?? null, nsec, npub.buffer, k.buffer);
},

crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, nsec, npub, k) {
return crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c.buffer, mac.buffer, m.buffer, ad?.buffer ?? null, nsec, npub.buffer, k.buffer);
},

native: true,
wasm: false,
};
44 changes: 44 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@projectdysnomia/libsodium",
"version": "0.0.1",
"description": "A minimal libsodium wrapper for Project Dysnomia",
"main": "index.js",
"type": "commonjs",
"types": "./index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/projectdysnomia/libsodium.git"
},
"author": "Project Dysnomia Contributors",
"license": "MIT",
"bugs": {
"url": "https://github.com/projectdysnomia/libsodium/issues"
},
"homepage": "https://github.com/projectdysnomia/libsodium#readme",
"peerDependencies": {
"sodium-native": "^4.1.1"
},
"peerDependenciesMeta": {
"sodium-native": {
"optional": true
}
},
"exports": {
".": {
"default": "./index.js",
"types": "./index.d.ts"
},
"./*": "./*",
"./native": {
"default": "./native.js",
"types": "./index.d.ts"
},
"./wasm": {
"default": "./wasm.js",
"types": "./index.d.ts"
}
}
}
30 changes: 30 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 62 additions & 0 deletions scripts/build-libsodium.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#! /bin/sh
# Taken from https://github.com/jedisct1/libsodium/blob/b7b1c08272dc3b8548b189cf558ee8bda63f3491/dist-build/emscripten.sh and heavily stripped down.
cd libsodium
export MAKE_FLAGS='-j4'
export EXPORTED_FUNCTIONS_STANDARD='["_malloc","_free","_crypto_aead_xchacha20poly1305_ietf_abytes","_crypto_aead_xchacha20poly1305_ietf_decrypt","_crypto_aead_xchacha20poly1305_ietf_decrypt_detached","_crypto_aead_xchacha20poly1305_ietf_encrypt","_crypto_aead_xchacha20poly1305_ietf_encrypt_detached","_crypto_aead_xchacha20poly1305_ietf_keybytes","_crypto_aead_xchacha20poly1305_ietf_messagebytes_max","_crypto_aead_xchacha20poly1305_ietf_npubbytes","_crypto_aead_xchacha20poly1305_ietf_nsecbytes","_sodium_init"]'
export EXPORTED_RUNTIME_METHODS='[]'
export JS_RESERVED_MEMORY_STANDARD=16MB
export JS_RESERVED_MEMORY_SUMO=48MB
export JS_RESERVED_MEMORY_TESTS=16MB
export WASM_INITIAL_MEMORY=4MB
export LDFLAGS="-s RESERVED_FUNCTION_POINTERS=8"
export LDFLAGS="${LDFLAGS} -s ALLOW_MEMORY_GROWTH=1"
export LDFLAGS="${LDFLAGS} -s ASSERTIONS=0"
export LDFLAGS="${LDFLAGS} -s AGGRESSIVE_VARIABLE_ELIMINATION=1 -s ALIASING_FUNCTION_POINTERS=1"
export LDFLAGS="${LDFLAGS} -s DISABLE_EXCEPTION_CATCHING=1"
export LDFLAGS="${LDFLAGS} -s ELIMINATE_DUPLICATE_FUNCTIONS=1"
export LDFLAGS="${LDFLAGS} -s NODEJS_CATCH_EXIT=0"
export LDFLAGS="${LDFLAGS} -s NODEJS_CATCH_REJECTION=0"
export LDFLAGS="${LDFLAGS} -s WASM_ASYNC_COMPILATION=0"
export LDFLAGS="${LDFLAGS} -s WASM_BIGINT"

echo

export EXPORTED_FUNCTIONS="$EXPORTED_FUNCTIONS_STANDARD"
export LDFLAGS="${LDFLAGS} ${LDFLAGS_DIST}"
export LDFLAGS_JS="-s TOTAL_MEMORY=${JS_RESERVED_MEMORY_STANDARD}"
export PREFIX="$(pwd)/libsodium-js"
export DONE_FILE="$(pwd)/js.done"
export CONFIG_EXTRA="--enable-minimal"
export DIST='yes'
echo "Building a standard distribution in [${PREFIX}]"
export JS_EXPORTS_FLAGS="-s EXPORTED_FUNCTIONS=${EXPORTED_FUNCTIONS} -s EXPORTED_RUNTIME_METHODS=${EXPORTED_RUNTIME_METHODS}"

rm -f "$DONE_FILE"

echo

emconfigure ./configure $CONFIG_EXTRA --disable-shared --prefix="$PREFIX" \
--without-pthreads \
--disable-ssp --disable-asm --disable-pie &&
emmake make clean
[ $? = 0 ] || exit 1

emccLibsodium() {
outFile="${1}"
shift
emcc "$CFLAGS" --llvm-lto 1 $CPPFLAGS $LDFLAGS $JS_EXPORTS_FLAGS "${@}" \
"${PREFIX}/lib/libsodium.a" -o "${outFile}" || exit 1
}
emmake make $MAKE_FLAGS install || exit 1
emccLibsodium "${PREFIX}/lib/libsodium.js" -O3 -s WASM=1 -s EVAL_CTORS=1 -s INITIAL_MEMORY=${WASM_INITIAL_MEMORY}

touch -r "${PREFIX}/lib/libsodium.js" "$DONE_FILE"
ls -l "${PREFIX}/lib/libsodium.js" "${PREFIX}/lib/libsodium.wasm"
cd ..

WASM_DIR="$(pwd)/wasm"
mkdir -p "$WASM_DIR"
cp -f "$PREFIX/lib/libsodium.js" "$PREFIX/lib/libsodium.wasm" "$WASM_DIR"
cp -f "libsodium/LICENSE" "$WASM_DIR/LICENSE.libsodium"

exit 0
Loading

0 comments on commit 97eff21

Please sign in to comment.