Fileverse Cryptography
Fileverse is a privacy-first and decentralized workspace, with collaborative apps for note-taking, spreadsheets, file-sharing, and presentation slides. This repo contains a comprehensive, type-safe cryptographic library for the end-to-end encrypted Fileverse applications, including dsheets.new and ddocs.new. The library provides a set of easy-to-use cryptographic primitives with a focus on security, type safety, and flexibility, which can be used for any JavaScript/TypeScript applications.
Features
- ECIES (Elliptic Curve Integrated Encryption Scheme) for asymmetric encryption
- RSA encryption with envelope encryption support for large messages
- NaCl Secret Box for fast, secure symmetric encryption using TweetNaCl
- HKDF (HMAC-based Key Derivation Function) for secure key derivation
- Argon2id password hashing for secure password storage and key derivation
- Encoding Utilities for consistent data format handling
- Type-Safe API with TypeScript generics for encoding types
- Cross-platform compatible with Node.js and modern browsers
Installation
npm install @fileverse/crypto
Usage
ECIES
Elliptic Curve Integrated Encryption Scheme for secure asymmetric encryption.
import {
generateECKeyPair,
eciesEncrypt,
eciesDecrypt,
} from "@fileverse/crypto/ecies";
const keyPair = generateECKeyPair();
const keyPairB64 = generateECKeyPair("base64");
const message = new TextEncoder().encode("Secret message");
const encrypted = eciesEncrypt(keyPair.publicKey, message);
const decrypted = eciesDecrypt(keyPair.privateKey, encrypted);
console.log(new TextDecoder().decode(decrypted));
const encryptedRaw = eciesEncrypt(keyPair.publicKey, message, "raw");
Shared Secret Derivation
import { generateECKeyPair, deriveSharedSecret } from "@fileverse/crypto/ecies";
const aliceKeyPair = generateECKeyPair();
const bobKeyPair = generateECKeyPair();
const aliceSharedSecret = deriveSharedSecret(
aliceKeyPair.privateKey,
bobKeyPair.publicKey
);
const bobSharedSecret = deriveSharedSecret(
bobKeyPair.privateKey,
aliceKeyPair.publicKey
);
NaCl Secret Box
Fast, secure symmetric encryption using TweetNaCl's secretbox implementation. Perfect for encrypting data when you have a shared secret key.
import {
generateSecretBoxKey,
secretBoxEncrypt,
secretBoxDecrypt,
} from "@fileverse/crypto/nacl";
const secretKey = generateSecretBoxKey();
const message = new TextEncoder().encode("Secret message");
const encrypted = secretBoxEncrypt(secretKey, message);
const decrypted = secretBoxDecrypt(secretKey, encrypted);
console.log(new TextDecoder().decode(decrypted));
const encryptedUrlSafe = secretBoxEncrypt(secretKey, message, true);
Key Features
- Authenticated encryption: Built-in authentication prevents tampering
- Fast performance: Optimized for speed using TweetNaCl
- Secure nonces: Automatically generates cryptographically secure 24-byte nonces
- URL-safe encoding: Optional URL-safe base64 encoding for web applications
- Type safety: Full TypeScript support with proper error handling
RSA
RSA encryption with support for envelope encryption to handle messages of any size.
import {
generateRSAKeyPair,
rsaEncrypt,
rsaDecrypt,
encryptEnvelope,
decryptEnvelope,
} from "@fileverse/crypto/webcrypto";
import { toBytes } from "@fileverse/crypto/utils";
const keyPair = await generateRSAKeyPair();
const keyPairBytes = await generateRSAKeyPair(4096, "bytes");
const message = new TextEncoder().encode("Hello, RSA encryption!");
const encrypted = await rsaEncrypt(keyPairBytes.publicKey, message);
const decrypted = await rsaDecrypt(keyPairBytes.privateKey, toBytes(encrypted));
console.log(new TextDecoder().decode(decrypted));
const largeMessage = new Uint8Array(1024 * 500);
const envelope = await encryptEnvelope(keyPairBytes.publicKey, largeMessage);
const decryptedLarge = await decryptEnvelope(keyPairBytes.privateKey, envelope);
HKDF
HMAC-based Key Derivation Function for deriving secure cryptographic keys.
import { deriveHKDFKey } from "@fileverse/crypto/hkdf";
const keyMaterial = new TextEncoder().encode("some-secure-key-material");
const salt = new TextEncoder().encode("salt-value");
const info = new TextEncoder().encode("context-info");
const key = deriveHKDFKey(keyMaterial, salt, info);
const keyBase64 = deriveHKDFKey(keyMaterial, salt, info, "base64");
Argon2id
Argon2id password hashing for secure password storage and key derivation.
import { getArgon2idHash } from "@fileverse/crypto/argon";
const password = "mySecurePassword123";
const salt = crypto.getRandomValues(new Uint8Array(16));
const hash = await getArgon2idHash(password, salt);
const hashBase64 = await getArgon2idHash(password, salt, "base64");
const customOptions = {
t: 3,
m: 65536,
p: 4,
dkLen: 32,
};
const customHash = await getArgon2idHash(
password,
salt,
"base64",
customOptions
);
Encoding Utilities
Utilities for handling encoding conversions consistently.
import { toBytes, bytesToBase64, encodeData } from "@fileverse/crypto/utils";
const bytes = toBytes("SGVsbG8gV29ybGQ=");
const base64 = bytesToBase64(new TextEncoder().encode("Hello World"));
const data = new TextEncoder().encode("Test data");
const bytesResult = encodeData(data, "bytes");
const base64Result = encodeData(data, "base64");
API Reference
ECIES Module
generateECKeyPair<E extends EncodingType = "bytes">(encoding?: E): EciesKeyPairType<E>
Generates an ECIES key pair with the specified encoding.
- Parameters:
encoding: Optional. The encoding type to use ("base64" or "bytes"). Default: "bytes".
- Returns: An object containing
publicKey and privateKey in the specified encoding.
deriveSharedSecret<E extends EncodingType = "bytes">(privateKey: Uint8Array | string, publicKey: Uint8Array | string, encoding?: E): EncodedReturnType<E>
Derives a shared secret from a private key and a public key.
- Parameters:
privateKey: The private key (base64 string or Uint8Array).
publicKey: The public key (base64 string or Uint8Array).
encoding: Optional. The encoding type for the result. Default: "bytes".
- Returns: The shared secret in the specified encoding.
eciesEncrypt(publicKey: string | Uint8Array, data: Uint8Array, format?: "base64" | "raw"): string | EciesCipher
Encrypts data using ECIES.
- Parameters:
publicKey: The public key (base64 string or Uint8Array).
data: The data to encrypt.
format: Optional. The format of the result ("base64" or "raw"). Default: "base64".
- Returns: The encrypted data as a string or EciesCipher object.
eciesDecrypt(privateKey: string | Uint8Array, encryptedData: string | EciesCipher): Uint8Array
Decrypts data using ECIES.
- Parameters:
privateKey: The private key (base64 string or Uint8Array).
encryptedData: The encrypted data (string or EciesCipher object).
- Returns: The decrypted data as a Uint8Array.
NaCl Secret Box Module
generateSecretBoxKey(): Uint8Array
Generates a cryptographically secure 32-byte secret key for use with secret box encryption.
- Returns: A 32-byte Uint8Array suitable for secret box operations.
secretBoxEncrypt(key: Uint8Array, message: Uint8Array, urlSafe?: boolean): string
Encrypts data using NaCl's secretbox authenticated encryption.
- Parameters:
key: The 32-byte secret key as a Uint8Array.
message: The data to encrypt as a Uint8Array.
urlSafe: Optional. Whether to use URL-safe base64 encoding. Default: false.
- Returns: The encrypted data as a base64-encoded string containing nonce and ciphertext.
- Throws: Error if the key length is invalid (not 32 bytes).
secretBoxDecrypt(key: Uint8Array, encrypted: string): Uint8Array
Decrypts data that was encrypted with secretBoxEncrypt.
- Parameters:
key: The 32-byte secret key as a Uint8Array.
encrypted: The encrypted data string returned by secretBoxEncrypt.
- Returns: The decrypted data as a Uint8Array.
- Throws:
- Error if the key length is invalid (not 32 bytes).
- Error if the encrypted message format is invalid.
- Error if the nonce length is invalid (not 24 bytes).
- Error if decryption fails (authentication failure or corrupted data).
RSA Module
generateRSAKeyPair<E extends EncodingType = "base64">(keySize?: number, encoding?: E): Promise<RsaKeyPairType<E>>
Generates an RSA key pair with the specified key size and encoding.
- Parameters:
keySize: Optional. The key size in bits. Default: 4096.
encoding: Optional. The encoding type to use ("base64" or "bytes"). Default: "base64".
- Returns: A promise resolving to an object containing
publicKey and privateKey in the specified encoding.
rsaEncrypt<E extends "base64" | "bytes" = "base64">(publicKey: Uint8Array, message: Uint8Array, returnFormat?: E): Promise<E extends "base64" ? string : Uint8Array>
Encrypts data using RSA-OAEP.
- Parameters:
publicKey: The public key as a Uint8Array.
message: The data to encrypt.
returnFormat: Optional. The format of the result ("base64" or "bytes"). Default: "base64".
- Returns: A promise resolving to the encrypted data in the specified format.
rsaDecrypt(privateKey: Uint8Array, cipherText: Uint8Array): Promise<Uint8Array>
Decrypts data using RSA-OAEP.
- Parameters:
privateKey: The private key as a Uint8Array.
cipherText: The encrypted data.
- Returns: A promise resolving to the decrypted data as a Uint8Array.
encryptEnvelope(publicKey: Uint8Array, message: Uint8Array): Promise<string>
Encrypts a message of any size using envelope encryption (hybrid RSA/AES).
- Parameters:
publicKey: The public key as a Uint8Array.
message: The data to encrypt.
- Returns: A promise resolving to the enveloped ciphertext as a string.
decryptEnvelope(privateKey: Uint8Array, envelopedCipher: string): Promise<Uint8Array>
Decrypts an envelope-encrypted message.
- Parameters:
privateKey: The private key as a Uint8Array.
envelopedCipher: The enveloped ciphertext.
- Returns: A promise resolving to the decrypted data as a Uint8Array.
HKDF Module
deriveHKDFKey<E extends EncodingType = "bytes">(keyMaterial: Uint8Array, salt: Uint8Array, info: Uint8Array, encoding?: E): EncodedReturnType<E>
Derives a key using HKDF.
- Parameters:
keyMaterial: The key material.
salt: The salt.
info: The context info.
encoding: Optional. The encoding type for the result. Default: "bytes".
- Returns: The derived key in the specified encoding.
Argon2id Module
getArgon2idHash<E extends EncodingType = "bytes">(password: string, salt: Uint8Array, returnFormat?: E, opts?: ArgonOpts): Promise<EncodedReturnType<E>>
Generates an Argon2id hash from a password and salt.
- Parameters:
password: The password to hash.
salt: The salt as a Uint8Array (recommended: 16 bytes minimum).
returnFormat: Optional. The encoding type for the result ("base64" or "bytes"). Default: "bytes".
opts: Optional. Argon2 configuration options. Default uses secure defaults.
- Returns: A promise resolving to the hash in the specified encoding.
Default Argon2 Options:
t: 2 (time cost/iterations)
m: 102400 (memory cost in KiB, ~100MB)
p: 8 (parallelism)
dkLen: 32 (derived key length in bytes)
Encoding Utilities
toBytes(base64: string): Uint8Array
Converts a base64 string to a Uint8Array.
bytesToBase64(bytes: Uint8Array, urlSafe = false): string
Converts a Uint8Array to a base64 string.
- Parameters:
bytes: The bytes to convert.
urlSafe: Optional. Whether to use URL-safe base64 encoding. Default: false.
- Returns: The base64 encoded string.
encodeData<E extends EncodingType>(data: Uint8Array, encoding: E): EncodedReturnType<E>
Encodes data in the specified format.
- Parameters:
data: The data to encode.
encoding: The encoding type ("base64" or "bytes").
- Returns: The encoded data in the specified format.
Technical Implementation
This library leverages the following dependencies for cryptographic operations:
Security Considerations
- This library uses well-established cryptographic primitives but has not undergone a formal security audit.
- Always keep private keys secure and never expose them in client-side code.
- For production use cases with high-security requirements, consider a formal security review.
- The library doesn't handle key storage - use appropriate secure storage mechanisms for your platform.
Development
Prerequisites
- Node.js (v14+)
- npm or yarn
Setup
git clone https://github.com/fileverse/fileverse-cryptography.git
cd crypto
npm install
npm test
Building
npm run build
Running Tests
npm test
npx vitest run src/ecies/__tests__/core.test.ts
Browser Compatibility
This library supports all modern browsers that implement the Web Crypto API and TextEncoder/TextDecoder APIs:
- Chrome/Edge 60+
- Firefox 55+
- Safari 11+
- iOS Safari 11+
License
GNU GPL