# Mail cryptographic library

[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=coverage)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)

# 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`)

- Runs unit tests with [Vitest](https://vitest.dev/)


### `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

```typescript
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: Email = {
    text: 'email text',
    preview: 'email preview',
    attachmentsSessionKey: new Uint8Array([1, 2, 3, 4]),
};
const { secretKey: bobPrivateKeys, publicKey: bobPublicKeys } = await generateEmailKeys();
const bobWithPublicKeys = {
    email: 'bob email',
    publicHybridKey: bobPublicKeys,
};
const {encryptedKeys, encEmail} = await encryptEmailHybridForMultipleRecipients(email, [bobWithPublicKeys]);
const decryptedEmail = await decryptEmailHybrid(encEmail, encryptedKeys[0], bobPrivateKeys);

expect(decryptedEmail).toStrictEqual(email);

// Hybrid email and subject encryption
const emailAndSubject: EmailAndSubject = {
    text: 'email text',
    subject: 'email subject',
    preview: 'email preview,',
    attachmentsSessionKey: new Uint8Array([1, 2, 3, 4]),
};
const {encryptedKeys, encEmail} = await encryptEmailAndSubjectHybridForMultipleRecipients(emailAndSubject, [bobWithPublicKeys]);
const decryptedEmailAndSubject = await decryptEmailAndSubjectHybrid(encEmail, encryptedKeys[0], bobPrivateKeys);

expect(encryptedEmailAndSubject.encEmail.encSubject).not.toBe(emailAndSubject.subject);
expect(decryptedEmailAndSubject).toStrictEqual(emailAndSubject);


// 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 mnemonic = 'user mnemonic';
const { encryptionKeystore, recoveryKeystore, recoveryCodes } = await createEncryptionAndRecoveryKeystores(
      userEmail,
      mnemonic
    );
const resultEnc = await openEncryptionKeystore(encryptionKeystore, mnemonic);
const resultRec = await openRecoveryKeystore(recoveryCodes, recoveryKeystore);

expect(resultEnc).toStrictEqual(resultRec);

```