Skip to main content

skiff-crypto

skiff-crypto is a versatile, open-source JavaScript cryptography library specifically designed to provide strong and efficient cryptographic functions for use in NodeJS or in the browser. This package powers cryptography inside Skiff’s end-to-end encrypted, privacy-first product suite, including Mail, Pages, Drive, and Calendar applications.

Features

  • Symmetric Key Cryptography: ChaCha20Poly1305-based symmetric encryption functions
  • Asymmetric Key Cryptography: TweetNaCl-based asymmetric encryption
  • Datagram Library: Manage object versions and metadata in an efficient way
  • Integrity: Support for checksum and data integrity verification
  • Utilities: Collection of useful utilities for working with cryptographic data

Installation

npm install @skiff-org/skiff-crypto

Quick Start

const skiffCrypto = require('@skiff-org/skiff-crypto');
// or
import * as skiffCrypto from '@skiff-org/skiff-crypto';

// Asymmetric encryption example
const plaintext = "Hello, skiff-crypto!";
const keypair = skiffCrypto.generatePublicPrivateKeyPair();
const encrypted = skiffCrypto.stringEncryptAsymmetric(
  keypair.privateKey,
  { key: keypair.publicKey },
  plaintext
);
const decrypted = skiffCrypto.stringDecryptAsymmetric(
  keypair.privateKey,
  { key: keypair.publicKey },
  encrypted
);

console.log('Plaintext:', plaintext);
console.log('Ciphertext:', encrypted);
console.log('Expected to be true:', plaintext === decrypted);

Key Management

generateSymmetricKey

Generate a symmetric key for encryption.
function generateSymmetricKey(): string
Returns: Base64-encoded symmetric key Example:
const symmetricKey = skiffCrypto.generateSymmetricKey();
console.log('Symmetric key:', symmetricKey);
Source: libs/skiff-crypto/src/keys.ts:14

generatePublicPrivateKeyPair

Generate public and private key pairs for signing and encryption.
function generatePublicPrivateKeyPair(): SigningAndEncryptionKeypairs
Returns:
publicKey
string
required
Base64-encoded public encryption key
privateKey
string
required
Base64-encoded private encryption key
signingPublicKey
string
required
Base64-encoded public signing key
signingPrivateKey
string
required
Base64-encoded private signing key
Example:
const keypair = skiffCrypto.generatePublicPrivateKeyPair();
console.log('Public key:', keypair.publicKey);
console.log('Private key:', keypair.privateKey);
console.log('Signing public key:', keypair.signingPublicKey);
console.log('Signing private key:', keypair.signingPrivateKey);
Source: libs/skiff-crypto/src/keys.ts:82

createKeyFromSecret

Deterministically generate a key from a secret value and salt using Argon2id.
async function createKeyFromSecret(
  secret: string,
  argonSalt: string
): Promise<string>
secret
string
required
Secret value to derive key from
argonSalt
string
required
Salt for Argon2 hashing
Returns: Base64-encoded derived key Source: libs/skiff-crypto/src/keys.ts:27

createSRPKey

Generate a key for SRP authentication from master secret using HKDF.
function createSRPKey(masterSecret: string, salt: string): string
masterSecret
string
required
Master secret used for HKDF
salt
string
required
Salt for SRP key
Returns: SRP private key in hexadecimal format Source: libs/skiff-crypto/src/keys.ts:59

createPasswordDerivedSecret

Generate the password-derived secret (a symmetric key that encrypts user’s private keys).
function createPasswordDerivedSecret(
  masterSecret: string,
  salt: string
): string
masterSecret
string
required
User’s master secret for HKDF input
salt
string
required
Salt to use in HKDF
Returns: Base64-encoded password derived secret Source: libs/skiff-crypto/src/keys.ts:102

generateVerificationPhraseFromSigningKey

Generate verification mnemonic using BIP39-like methodology.
function generateVerificationPhraseFromSigningKey(
  publicSigningKey: string
): string
publicSigningKey
string
required
Public signing key of user to be verified
Returns: Mnemonic sentence for verification Source: libs/skiff-crypto/src/keys.ts:120

Asymmetric Encryption

stringEncryptAsymmetric

Encrypt a string using asymmetric encryption (NaCl box).
function stringEncryptAsymmetric(
  myPrivateKey: string,
  theirPublicKey: { key: string },
  plaintext: string
): string
myPrivateKey
string
required
User’s private encryption key
theirPublicKey
object
required
Recipient’s public key object
plaintext
string
required
Plaintext to encrypt
Returns: Base64-encoded encrypted ciphertext Example:
const keypair = skiffCrypto.generatePublicPrivateKeyPair();
const encrypted = skiffCrypto.stringEncryptAsymmetric(
  keypair.privateKey,
  { key: keypair.publicKey },
  "Hello, World!"
);
Source: libs/skiff-crypto/src/asymmetricEncryption.ts:57

stringDecryptAsymmetric

Decrypt a string using asymmetric encryption (NaCl box).
function stringDecryptAsymmetric(
  myPrivateKey: string,
  theirPublicKey: { key: string },
  encryptedText: string
): string
myPrivateKey
string
required
User’s private encryption key
theirPublicKey
object
required
Recipient’s public key object
encryptedText
string
required
Encrypted data to decrypt
Returns: Decrypted plaintext string
This function is memoized for performance optimization.
Source: libs/skiff-crypto/src/asymmetricEncryption.ts:74

Symmetric Encryption

encryptSymmetric

Symmetric encryption using NaCl secretbox (secret-key authenticated encryption).
function encryptSymmetric<T>(
  content: T,
  symmetricKey: string,
  datagram: Datagram<T>
): string
content
T
required
The object being serialized and encrypted
symmetricKey
string
required
Base64-encoded symmetric key
datagram
Datagram<T>
required
Mechanism to convert instances of T to a Datagram
Returns: Base64-encoded encrypted payload Example:
const symmetricKey = skiffCrypto.generateSymmetricKey();
const TestDatagram = skiffCrypto.createJSONWrapperDatagram('ddl://test');
const plainText = "Hello, skiff-crypto (symmetric encryption)!";
const encrypted = skiffCrypto.encryptSymmetric(
  plainText,
  symmetricKey,
  TestDatagram
);
Source: libs/skiff-crypto/src/symmetricEncryption.ts:31

decryptSymmetric

Symmetric decryption using NaCl secretbox (secret-key authenticated encryption).
function decryptSymmetric<T>(
  message: string,
  symmetricKey: string,
  DatagramType: Datagram<T>
): T
message
string
required
Base64-encoded encrypted payload
symmetricKey
string
required
Base64-encoded key used for decryption
DatagramType
Datagram<T>
required
The type of object being decrypted
Returns: Decrypted message contents Example:
const decrypted = skiffCrypto.decryptSymmetric(
  encrypted,
  symmetricKey,
  TestDatagram
);
console.log('Decrypted:', decrypted);
Source: libs/skiff-crypto/src/symmetricEncryption.ts:61

rawEncryptSymmetric

Symmetric encryption returning raw data without base64 encoding.
function rawEncryptSymmetric<T>(
  content: T,
  symmetricKey: string,
  datagram: Datagram<T>
): Uint8Array
content
T
required
The object being serialized
symmetricKey
string
required
Base64-encoded symmetric key
datagram
Datagram<T>
required
Mechanism to convert instances of T to a Datagram
Returns: Uint8Array encrypted payload Source: libs/skiff-crypto/src/symmetricEncryption.ts:16

rawDecryptSymmetric

Symmetric decryption without base64 encoding.
function rawDecryptSymmetric<T>(
  message: Uint8Array,
  symmetricKey: string,
  DatagramType: Datagram<T>
): T
message
Uint8Array
required
Encrypted payload as raw bytes
symmetricKey
string
required
Base64-encoded key used for decryption
DatagramType
Datagram<T>
required
The type of object being decrypted
Returns: Decrypted message contents Source: libs/skiff-crypto/src/symmetricEncryption.ts:45

Datagram System

Datagrams provide a versioned, typed way to serialize and deserialize encrypted data with metadata support.

createJSONWrapperDatagram

Create a datagram that encodes and decodes any JSON.stringify-able data in an object containing {version, type, data}.
function createJSONWrapperDatagram<T>(
  type: string,
  version?: string,
  versionConstraint?: Range
): Datagram<T>
type
string
required
Datagram type identifier (e.g., ‘ddl://test’)
version
string
default:"0.1.0"
Datagram version
versionConstraint
Range
default:"0.1.*"
Datagram version constraint
Returns: Datagram instance for encrypting/decrypting typed data Example:
const TestDatagram = skiffCrypto.createJSONWrapperDatagram('ddl://test');
const symmetricKey = skiffCrypto.generateSymmetricKey();
const data = { message: "Hello, World!" };
const encrypted = skiffCrypto.encryptSymmetric(data, symmetricKey, TestDatagram);
const decrypted = skiffCrypto.decryptSymmetric(encrypted, symmetricKey, TestDatagram);
Source: libs/skiff-crypto/src/datagramBuilders.ts:15

createRawJSONDatagram

Create a datagram that encodes and decodes any JSON.stringify-able data without wrapping in version/type metadata.
function createRawJSONDatagram<T>(
  type: string,
  version?: string,
  versionConstraint?: Range
): Datagram<T>
type
string
required
Datagram type identifier
version
string
default:"0.1.0"
Datagram version
versionConstraint
Range
default:"0.1.*"
Datagram version constraint
Returns: Datagram instance without metadata wrapper
This is recommended for new code as version and type info are already contained in the encryption header.
Source: libs/skiff-crypto/src/datagramBuilders.ts:44

createRawCompressedJSONDatagram

Create a datagram that encodes and decodes JSON data with gzip compression.
function createRawCompressedJSONDatagram<T>(
  type: string,
  version?: string,
  versionConstraint?: Range
): Datagram<T>
type
string
required
Datagram type identifier
version
string
default:"0.1.0"
Datagram version
versionConstraint
Range
default:"0.1.*"
Datagram version constraint
Returns: Datagram instance with compression support Source: libs/skiff-crypto/src/datagramBuilders.ts:68

createUint8ArrayDatagram

Create a datagram that encodes and decodes raw Uint8Array objects for optimal size.
function createUint8ArrayDatagram(
  type: string,
  version?: string,
  versionConstraint?: Range
): Datagram<Uint8Array>
type
string
required
Datagram type identifier
version
string
default:"0.1.0"
Datagram version
versionConstraint
Range
default:"0.1.*"
Datagram version constraint
Returns: Datagram instance for raw binary data Source: libs/skiff-crypto/src/datagramBuilders.ts:96

Hashing

generateHash

Generate a SHA-512 hash of the given value.
function generateHash(value: string): string
value
string
required
String to hash
Returns: Base64-encoded SHA-512 hash Example:
const hash = skiffCrypto.generateHash('Hello, skiff-crypto!');
console.log('SHA-512 hash:', hash);
Source: libs/skiff-crypto/src/hash.ts:10

Digital Signatures

createDetachedSignatureAsymmetric

Create a detached signature from a message with context.
function createDetachedSignatureAsymmetric(
  message: string,
  signingPrivateKey: string,
  context: SignatureContext,
  additionalContext?: AdditionalContext
): string
message
string
required
Message to sign
signingPrivateKey
string
required
User’s signing private key
context
SignatureContext
required
Signature context to prevent re-interpretation
additionalContext
AdditionalContext
Optional additional context
Returns: Base64-encoded signature Source: libs/skiff-crypto/src/signature.ts:96

verifyDetachedSignatureAsymmetric

Verify a signature on provided message given context.
function verifyDetachedSignatureAsymmetric(
  message: string,
  signature: string,
  signingPublicKey: string,
  context: SignatureContext,
  additionalContext?: AdditionalContext
): boolean
message
string
required
Message to verify
signature
string
required
Signature to check
signingPublicKey
string
required
User’s signing public key
context
SignatureContext
required
Signature context
additionalContext
AdditionalContext
Optional additional context
Returns: Boolean indicating whether signature is valid Source: libs/skiff-crypto/src/signature.ts:73

SignatureContext

Enumeration of signature contexts to prevent signature re-interpretation:
enum SignatureContext {
  DeleteAccount = 'DELETE_ACCOUNT',
  DeleteDoc = 'DELETE_DOC',
  DeleteRecoveryData = 'DELETE_RECOVERY_DATA',
  DisableMfa = 'DISABLE_MFA',
  DocumentChunk = 'DOCUMENT_CHUNK',
  DocumentData = 'DOCUMENT_DATA',
  DocumentMetadata = 'DOCUMENT_METADATA',
  DocumentParent = 'DOCUMENT_PARENT',
  EnrollMfa = 'ENROLL_MFA',
  LinksLinkKey = 'LINKS_LINK_KEY',
  LinksSessionKey = 'LINKS_SESSION_KEY',
  RecoveryData = 'RECOVERY_DATA',
  RegenerateMfaBackupCodes = 'REGENERATE_MFA_BACKUP_CODES',
  SessionKey = 'SESSION_KEY',
  SrpSalt = 'SRP_SALT',
  SrpVerifier = 'SRP_VERIFIER',
  UnshareDoc = 'UNSHARE_DOC',
  UpdateUserData = 'UPDATE_USER_DATA',
  UserData = 'USER_DATA',
  UserPublicKey = 'USER_PUBLIC_KEY',
  EmailContent = 'EMAIL_CONTENT',
  MobileLogin = 'MOBILE_LOGIN'
}
Source: libs/skiff-crypto/src/signature.ts:12

Complete Example

import * as skiffCrypto from '@skiff-org/skiff-crypto';

// Generate keys
const keypair = skiffCrypto.generatePublicPrivateKeyPair();
const symmetricKey = skiffCrypto.generateSymmetricKey();

// Asymmetric encryption
const plaintext = "Hello, skiff-crypto!";
const encrypted = skiffCrypto.stringEncryptAsymmetric(
  keypair.privateKey,
  { key: keypair.publicKey },
  plaintext
);
const decrypted = skiffCrypto.stringDecryptAsymmetric(
  keypair.privateKey,
  { key: keypair.publicKey },
  encrypted
);

console.log('Asymmetric encryption works:', plaintext === decrypted);

// Symmetric encryption with datagram
const TestDatagram = skiffCrypto.createJSONWrapperDatagram('ddl://test');
const symmetricPlainText = "Hello, skiff-crypto (symmetric encryption)!";
const symmetricEncrypted = skiffCrypto.encryptSymmetric(
  symmetricPlainText,
  symmetricKey,
  TestDatagram
);
const symmetricDecrypted = skiffCrypto.decryptSymmetric(
  symmetricEncrypted,
  symmetricKey,
  TestDatagram
);

console.log('Symmetric encryption works:', symmetricPlainText === symmetricDecrypted);

// Hash example
const hash = skiffCrypto.generateHash('Hello, skiff-crypto!');
console.log('SHA-512 hash:', hash);

Security

Cryptography is a sensitive domain. While skiff-crypto has been audited and built using widely-used, audited libraries (TweetNaCl, ChaCha20Poly1305, Argon2), we urge you to use this library responsibly. Please have a sound understanding of the underlying principles of cryptography before implementation. Security issues or questions can be sent to [email protected].

License

skiff-crypto is MIT licensed. See the LICENSE file for details.

Build docs developers (and LLMs) love