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

@brilliant-ember/secure-channel

v1.0.5

Published

Cross-platform (Node & browser) authenticated, encrypted channel: X25519 key-exchange + HKDF + AES-GCM **and** Ed25519 sign/verify in one zero-dependency TypeScript package

Readme

Cross-Platform Key Exchange and Signature

A TypeScript library for secure cross-platform key exchange and encryption, as well as signature and verification. Works in both Node.js (15+) and browsers.

Features

  • X25519 for elliptic curve key exchange
  • HKDF for key derivation
  • AES-GCM for authenticated encryption
  • Ed25519 signature / verification
  • Cross-platform - works in Node.js and browsers
  • TypeScript - fully typed
  • Zero dependencies

Installation


npm install @brilliant-ember/secure-channel --save

Use examples

NodeJS


// completeNodeExample.js
const sc = require("@brilliant-ember/secure-channel");
const { webcrypto } = require("crypto");
const { subtle } = webcrypto;

async function main() {

    // signature related
    const signer = await sc.Signature.getInstance();
    await signer.initializeServerKey(theOtherPartySignaturePublicKey);
    const ourPublicSignatureKey = await signer.getPublicKey();
    // sign our own response
    const ourSignature = await signer.sign(ourDataBytes); // this has to be base64
    // verify the response we got from the other server
    const isOtherServerSignatureValid = await signer.verify(otherServerSignature, dataBytesFromOtherServer);


    // encrypte and decrypt
    const kx = await sc.KeyExchange.getInstance();
    const otherServerPublicKxKey = getOtherServerKxKey(); // we need this from the other server we are talking to first to generate our own kx keys
    const otherServerKxCryptoKey = await subtle.importKey(
      "raw",
      otherServerPublicKxKeyBytes,
      { name: "X25519" },
      false,
      []
    );
    // we need to send our kxKey to the other server to finish the dh key exchange
    const ourKxPublicKey = await kx.generateKey(otherServerKxCryptoKey);
    sendKxToOtherServer(ourKxPublicKey);

    const { ciphertext, nonce } = await kx.encrypt("Hello other server!");
    const response = await sendDataToOtherServer(ciphertext, nonce); // we must send the nonce too
    const decryptedData = await kx.decrypt(response.ciphertext, response.nonce);
    // this is a helper function
    const plaintext = sc.byteArrToString(decryptedData);
    console.log(plaintext) // will print "Hello client!"


    // helper functions provided
    sc.byteArrToString(Uint8Array);
    sc.base64ToUint32(b64);
    sc.uint32ToBase64(number);
    sc.uint32ToBytes(num);
    sc.copyToBuffer(Uint8Array, number, Uint8Array);
    sc.base64StringtoByteArr(uint8array); 
    sc.byteArrayToBase64(ArrayBuffer | Uint8Array); 
    sc.numberToBase64(num); 
    sc.base64ToNumber(b64); 
    sc.generateRandomBytes(32); 
    sc.bigIntToBase64(1n); 
    sc.base64ToBigInt(b64); 
    sc.convertToJsonSerializable(obj);
}

React client

// SecureComponent.jsx
import { useState, useEffect } from 'react';
import { KeyExchange, Signature } from '@brilliant-ember/secure-channel';

export function SecureComponent() {
  const [crypto, setCrypto] = useState(null);
  const [message, setMessage] = useState('');

  useEffect(() => {
    KeyExchange.getInstance().then(setCrypto);
  }, []);

  const encryptMessage = async () => {
    const { ciphertext, nonce } = await crypto.encrypt(message);
    console.log('Encrypted:', { ciphertext, nonce });
  };

  return (
    <div>
      <input value={message} onChange={e => setMessage(e.target.value)} />
      <button onClick={encryptMessage}>Encrypt</button>
    </div>
  );
}

Raw browser javascript


<script type="module">
  import { KeyExchange, Signature } from './@brilliant-ember/secure-channel.js';

  // Initialize
  const crypto = await KeyExchange.getInstance();
  const sig = await Signature.getInstance();

  // Generate keys with server's public key
  const serverKey = await importServerKey();
  await crypto.generateKey(serverKey);

  // Encrypt
  const { ciphertext, nonce } = await crypto.encrypt('Hello World!');

  // Decrypt  
  const decrypted = await crypto.decrypt(ciphertext, nonce);
  console.log('Decrypted:', new TextDecoder().decode(decrypted));
</script>

Helpers

Basic Base64 Conversion

import { byteArrayToBase64, base64StringToByteArr } from '@brilliant-ember/secure-channel';

// Convert text to base64 and back
const text = "Hello World";
const bytes = new TextEncoder().encode(text);
const base64 = byteArrayToBase64(bytes);
console.log('Base64:', base64); // "SGVsbG8gV29ybGQ="

const decodedBytes = base64StringToByteArr(base64);
const decodedText = new TextDecoder().decode(decodedBytes);
console.log('Decoded:', decodedText); // "Hello World"

BigInt Helpers for Timestamps/IDs

import { bigIntToBase64, base64ToBigInt } from '@brilliant-ember/secure-channel';

// Store timestamps as compact base64
const timestamp = BigInt(Date.now());
const timestampB64 = bigIntToBase64(timestamp);
console.log('Timestamp base64:', timestampB64);

const recovered = base64ToBigInt(timestampB64);
console.log('Recovered timestamp:', Number(recovered));

// For message IDs
const messageId = 1234567890123456789n;
const idB64 = bigIntToBase64(messageId);
console.log('Message ID:', idB64);

Number Helpers (Safe Integer Range)


import { numberToBase64, base64ToNumber } from '@brilliant-ember/secure-channel';

// For smaller numbers
const count = 1234567890;
const countB64 = numberToBase64(count);
console.log('Count base64:', countB64);

const recoveredCount = base64ToNumber(countB64);
console.log('Recovered count:', recoveredCount);

Random Bytes


import { generateRandomBytes } from '@brilliant-ember/secure-channel';

// Generate secure random data
const randomData = await generateRandomBytes(32);
console.log('Random bytes:', randomData);

Copy To Buffer


function serializeData(data) {
  const { id, type, payload, timestamp } = data;
  
  // Calculate total size
  const idBytes = new TextEncoder().encode(id);
  const typeBytes = new TextEncoder().encode(type);
  const totalSize = idBytes.length + typeBytes.length + payload.length + 8;
  
  const buffer = new Uint8Array(totalSize);
  let offset = 0;
  
  // Serialize structure
  offset = copyToBuffer(buffer, offset, id);                    // String ID
  offset = copyToBuffer(buffer, offset, "|");                   // Separator
  offset = copyToBuffer(buffer, offset, type);                  // String type
  offset = copyToBuffer(buffer, offset, "|");                   // Separator
  
  const timestampBytes = uint32ToBytes(timestamp);
  offset = copyToBuffer(buffer, offset, timestampBytes);        // 4-byte timestamp
  
  offset = copyToBuffer(buffer, offset, payload);               // Binary payload
  
  return buffer;
}

// Usage
const message = {
  id: "msg-123",
  type: "user_message", 
  timestamp: Math.floor(Date.now() / 1000),
  payload: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])
};

const serialized = serializeData(message);
console.log('Serialized size:', serialized.length);

Convert to JSON serializable


// Storing binary data in JSON-based storage (LocalStorage, Redis, etc.)
function saveToStorage(key: string, data: any) {
  const serializableData = convertToJsonSerializable(data);
  localStorage.setItem(key, JSON.stringify(serializableData));
}

// Usage with binary user data
const userData = {
  id: 123,
  profileImage: new Uint8Array([255, 255, 255, 0]), // Binary image data
  preferences: {
    theme: "dark",
    customData: new Uint8Array([1, 2, 3])
  }
};

saveToStorage("userData", userData);