JSPM

@ondemos/core

0.2.7
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q54926F
  • License AGPL-3.0

A cryptographic commitment scheme with implied order of ownership wasm module for nodejs and the browser.

Package Exports

  • @ondemos/core
  • @ondemos/core/package.json

Readme

@ondemos/core

codecov Known Vulnerabilities
NPM Version NPM License code-style-prettier
NPM Downloads

This repository contains cryptographic functions that may be useful for digital democracy.

It does not have any native dependencies and can be used in both Nodejs and the browser.

The API is not completely stable and the code has not undergone external security audits. Use at your own risk.

Introduction

This library uses the WASM output from the core C library and wraps it in Typescript.

Files

The commitment directory contains the utility functions for private liquid votes.

The utils directory contains sha512 and argon2 hashing, mnemonic generation and validation and symmetric and asymmetric cryptography operations with Ed25519 and ChaCha20Poly1305.

The merkle directory contains a Merkle root getter function, a Merkle proof artifacts getter, a root from proof getter and a proof verification function.

The shamir directory contains a WASM implementation of a cryptographic technique called Shamir's secret sharing, which allows one to split a secret into random shares that can only recreate it if a threshold of them is combined. Under the hood it uses the libsodium randombytes js method to generate random coefficients for the polynomial.

Getting Started

To get started you have to install the package with

npm install @ondemos/core

You can include as ES module

import ondemos from "@ondemos/core";

as CommonJS module

const ondemos = require("@ondemos/core");

or as UMD in the browser with

<script src="https://cdn.jsdelivr.net/npm/@ondemos/core@latest/lib/index.min.js"></script>

Examples

You can visit the tests folder to find usage examples.

For Curve25519 public key cryptography we have the following methods

import ondemos from "@ondemos/core";

// Words from dictionary create random seed for Ed25519 private key.
// Default entropy is 128bits, which results in 12 words.
const mnemonic = await ondemos.generateMnemonic();
console.log(`Mnemonic with 128 bits of entropy => 12 words: ${mnemonic}`);
// Max entropy is 256bit, where generateMnemonic(256) results in 24 words.

// Keypair is an object representing an Ed25519 keypair with { publicKey: Uint8Array(32), secretKey: Uint8Array(64) }
const keypair = await ondemos.keyPairFromMnemonic(mnemonic);
console.log(
  `Keypair from mnemonic: {\n\
  secretKey: ${Buffer.from(keypair.secretKey).toString("hex")}\n\
  publicKey: ${Buffer.from(keypair.publicKey).toString("hex")}\n}\
`,
);

// Generates a Uint8Array(128) full of random bytes
const message = await ondemos.randomBytes(128);

// EdDSA
const signature = await ondemos.sign(message, keypair.secretKey);

const verify = await ondemos.verify(message, signature, keypair.publicKey);
console.log(verify); // true

const hash = await ondemos.sha512(message);

const keypair2 = await ondemos.keyPair();

// Forward secrecy box.
// Encryptor generates a random keypair. The public key is contained in the
// "encrypted" box and the secret key is used for the key exchange with
// "keypair2.publicKey" and then it is removed from memory.
const encrypted = await ondemos.encryptAsymmetric(
  message,
  keypair2.publicKey,
  keypair.secretKey,
  hash,
);

const decrypted = await ondemos.decryptAsymmetric(
  encrypted,
  keypair.publicKey,
  keypair2.secretKey,
  hash,
);

// To test equality for two Uint8Arrays in js you need to check if each of their elements are equal
// The === operator does not work
for (let i = 0; i < message.length; i++) {
  if (message[i] !== decrypted[i]) console.error("Arrays unequal");
}

const symmetricKey = await ondemos.randomBytes(
  ondemos.interfaces.crypto_kx_SESSIONKEYBYTES,
);
const encrypted1 = await ondemos.encryptSymmetric(message, symmetricKey, hash);
const decrypted1 = await ondemos.decryptSymmetric(encrypted1, key, hash);

For Shamir secret sharing you can test the following

import ondemos from "@ondemos/core";

const keypair = await ondemos.keyPair();

// 100 splitted shares, you need 60 to recreate keypair.secretKey
// Note that you can have max 255 shares and threshold <= shares
const shares = await ondemos.splitSecret(keypair.secretKey, 100, 60);

// Should be equal to keypair.secretKey
const sk1 = await ondemos.restoreSecret(shares);

console.log("sk1 and kaypair.secretKey are equal");

// Remove 40 shares to see if it will still work
const lessShares = shares.slice(0, shares.length - 40);

// Should be equal to sk1 and keypair.secretKey
const sk2 = await ondemos.restoreSecret(lessShares);

console.log("sk2 and kaypair.secretKey are equal");

const evenLessShares = lessShares.slice(0, lessShares.length - 1);

// Should not be equal to sk1 and sk2.
const sk3 = await ondemos.restoreSecret(evenLessShares);

console.log("sk3 and kaypair.secretKey are NOT equal");

In order to find the Merkle root, proof and to verify the proof you can do the following:

import ondemos from "@ondemos/core";

const randomArrays: Uint8Array[] = [];
for (let i = 0; i < 50; i++) {
  randomArrays.push(await ondemos.randomBytes(32));
}

// ondemos.constants.crypto_hash_sha512_BYTES
// Function also accepts any type of data but it then requires a serializer function.
const randomArraysMerkleRoot = await ondemos.getMerkleRoot(randomArrays);

// Multiple of ondemos.constants.crypto_hash_sha512_BYTES
const randomArrayMerkleProof = await ondemos.getMerkleProof(
  randomArrays,
  randomArrays[43],
);

const elementHash = await ondemos.sha512(randomArrays[43]);

const verify = await ondemos.verifyMerkleProof(
  elementHash,
  randomArraysMerkleRoot,
  randomArrayMerkleProof,
);

console.log(verify); // should be true

For more examples you can see the tests directory.

Development

If you want to bundle the library yourselves, you need to have Emscripten installed on your machine in order to compile the C code into WebAssembly. We have the -s SINGLE_FILE=1 option for the emcc compiler, which converts the wasm file to a base64 string that will be compiled by the glue js code into a WebAssembly module. This was done for the purpose of interoperability and modularity.

Once you have all the dependencies installed, you can clone the core library, compile it, copy the wasm output and build:

git clone https://github.com/ondemos/core
cd core
git submodule update --init --recursive
git clone https://github.com/emscripten-core/emsdk
cd emsdk
./emsdk install latest
./emsdk activate latest
cd ..
source ./build.sh
cd ..
git clone https://github.com/ondemos/core
cd core
npm i 
npm run copy:wasm
npm run build

and Rollup will generate the UMD, ESM and CJS bundles.

Releases

Releases are available on Github and npmjs.com

License

The source code is licensed under the terms of the Affero General Public License version 3.0 (see LICENSE).

Copyright (C) 2024 Deliberative Technologies P.C.