Skip to content

internxt/crypto

Repository files navigation

Mail cryptographic library

Lines of Code Maintainability Rating Security Rating Vulnerabilities Code Smells Duplicated Lines (%) Coverage

Project Manteinance

We aim to have:

  • An 'A' score on Maintainability Rating
  • An 'A' score on Security Rating
  • Less than 3% duplicated lines
  • A 90% tests coverage

Scripts

yarn run lint (yarn run lint:ts)

  • Runs .ts linter

yarn test (vitest run)

yarn build

Builds the app for production to the build folder.

Project Structure

Core Cryptography Modules

  • asymmetric-crypto - Asymmetric elliptic curves cryptography (curve P-521) for generating keys and deriving a shared secret between two users
  • symmetric-crypto - Symmetric encryption operations (AES-GCM) for data encryption and decryption
  • post-quantum-crypto - Post-quantum cryptographic algorithms (MLKEMs) for generating keys and deriving a shared secret between two users
  • hash - Cryptographic hashing functions (BLAKE3) for data integrity, commitments and secret extensions

Key Management

  • derive-key - Key derivation functions for deriving cryptographic keys from base key (BLAKE3 in KDF mode)
  • derive-password - Key derivation functions for deriving cryptographic keys from passwords (ARGON2)
  • key-wrapper - Key wrapping and unwrapping functions for secure symmetric key storage and transport
  • keystore-crypto - Keystore cryptographic operations for securing user's keys

Email Security

  • email-crypto - End-to-end email encryption and decryption using hybrid cryptography and password-protection
  • email-search - Email indexing on the client side to enable search while preserving privacy

Infrastructure

  • storage-service - Abstraction layer for accessing Local Storage and Session Storage
  • utils - Type converter functions and access to enviromental variables
  • types - TypeScript type definitions for all library interfaces and data structures
  • constants - Cryptographic constants, algorithm identifiers, and configuration values

Usage Example

import {
  generateEccKeys,
  deriveSecretKey,
  UTF8ToUint8,
  genSymmetricKey,
  encryptSymmetrically,
} from 'internxt-crypto';

// Asymmetric encryption
const keysAlice = await generateEccKeys();
const keysBob = await generateEccKeys();
const resultAlice = await deriveSecretKey(keysBob.publicKey, keysAlice.privateKey);
const resultBob = await deriveSecretKey(keysAlice.publicKey, keysBob.privateKey);
expect(resultAlice).toStrictEqual(resultBob);

// Symmetric encryption
const data = UTF8ToUint8('Sensitive information to encrypt'); // convert to Uint8Array 
const additionalData = 'Additional non-secret data';
const key = genSymmetricKey(); 
const ciphertext: Uint8Array = await encryptSymmetrically(key, data, additionalData);
const plainText = await decryptSymmetrically(encryptionKey, ciphertext, additionalData);
expect(data).toStrictEqual(plainText);

// Post qunatum cryptography
const keys = generateKyberKeys();
const { cipherText, sharedSecret } = encapsulateKyber(keys.publicKey);
const result = decapsulateKyber(cipherText, keys.secretKey);
expect(result).toStrictEqual(sharedSecret);

// Hash
const result = hashData(['']);
const expectedResult = 'af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262';
expect(result).toStrictEqual(expectedResult);

// Key derivation
const context = 'BLAKE3 2019-12-27 16:29:52 test vectors context';
const baseKey = genSymmetricKey(); 
const key = deriveSymmetricKeyFromContext(context, baseKey);

// Key derivation from password
const password = 'your password';
const { key, salt } = await getKeyFromPassword(password);

// Hybrid email encryption
const email: EmailBody = {
    text: 'email text',
    subject: 'email subject',
};
const { secretKey: bobPrivateKeys, publicKey: bobPublicKeys } = await generateEmailKeys();
const bobWithPublicKeys = {
    email: 'bob email',
    publicHybridKey: bobPublicKeys,
};
const encryptedEmail = await encryptEmailHybrid(email, bobWithPublicKeys);
const decryptedEmail = await decryptEmailHybrid(encryptedEmail, bobPrivateKeys);

expect(encryptedEmail.encEmailBody.encSubject).not.toBe(email.subject);
expect(decryptedEmailBody).toStrictEqual(email);


// password-protected email
const sharedSecret = 'secret shared between Alice and Bob';
const encryptedEmail = await createPwdProtectedEmail(email, sharedSecret);
const decryptedEmail = await decryptPwdProtectedEmail(encryptedEmail, sharedSecret);
expect(decryptedEmail).toStrictEqual(email);

// keystore
const userEmail = 'user email';
const secretKey = genSymmetricKey();
const { encryptionKeystore, recoveryKeystore, recoveryCodes } = await createEncryptionAndRecoveryKeystores(
      userEmail,
      secretKey,
    );
const resultEnc = await openEncryptionKeystore(encryptionKeystore, secretKey);
const resultRec = await openRecoveryKeystore(recoveryCodes, recoveryKeystore);

expect(resultEnc).toStrictEqual(resultRec);

// Email storage and search

// Between sessions emails are stored encrypted in IndexedDB. The encryption key is derived from user's baseKey
// During the session, all emails are decrypted and stored in the cache (up to 600 MB, if excides - we delete oldests emails)
// For search, we build a search index from cache, then use Flexsearch for the search. 
// The search is doen separately for email content, subject, sender and recivers. 

// Open IndexedDB database
const userID = 'user ID';
const db = await openDatabase(userID);

// Derive database key
const key = await deriveIndexKey(baseKey);

// Encrypt and store one or several emails
await encryptAndStoreEmail(email, key, db);
await encryptAndStoreManyEmail(emails, key, db);

// Delete given email by its ID
await deleteEmail(emailID, db);

// Delete oldests emails
const number = 5;
await deleteOldestEmails(number, db);

// Get all emails with or without sorting
const allEmails = await getAndDecryptAllEmails(key, db);
const newestFirst = await getAllEmailsSortedNewestFirst(db, key);
const oldestFirst = await getAllEmailsSortedOldestFirst(db, key);

// Get the number of stored emails
const count = await getEmailCount(db);

// Close IndexedDB database
closeDatabase(db);

// Delete IndexedDB database
await deleteDatabase(userID);

// Create email cache 
const esCache = await createCacheFromDB(key, db);

// Add one or multiple emails to cache
const result = addEmailToCache(email, esCache);
expect(result.success).toBe(true);

const result = addEmailsToCache(emails, esCache);
expect(result.success).toBe(true);

// Get email from cache by its ID
const email = await getEmailFromCache(emailID, esCache);

// Delete email from cache by its ID
await deleteEmailFromCache(emailID, esCache);

// Create search index and search by query
 const searchIndex = await buildSearchIndexFromCache(esCache);
 const query = 'keywords to search';
 const options = {
    fields: ['subject'], // in which fields to search, all by deafult (subject, body, from, to)
    limit: 5,  // result limit, 50 by default
    boost: { subject: 3, body: 1, from: 2, to: 2 }, // custom waights for matches in different email parts
  };
 const result: EmailSearchResult = await searchEmails(query, esCache, searchIndex);

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages