{"id":40370737,"url":"https://github.com/internxt/crypto","last_synced_at":"2026-04-24T09:03:03.259Z","repository":{"id":309572371,"uuid":"833084973","full_name":"internxt/crypto","owner":"internxt","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-17T17:24:30.000Z","size":1197,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-02-17T22:30:26.359Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/internxt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-07-24T10:19:19.000Z","updated_at":"2026-02-17T16:29:25.000Z","dependencies_parsed_at":"2025-08-28T19:10:22.767Z","dependency_job_id":"c1df42af-4293-4ac8-ad82-7e8a2319e5f5","html_url":"https://github.com/internxt/crypto","commit_stats":null,"previous_names":["internxt/crypto"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/internxt/crypto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/internxt%2Fcrypto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/internxt%2Fcrypto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/internxt%2Fcrypto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/internxt%2Fcrypto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/internxt","download_url":"https://codeload.github.com/internxt/crypto/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/internxt%2Fcrypto/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29578138,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T08:38:15.585Z","status":"ssl_error","status_checked_at":"2026-02-18T08:38:14.917Z","response_time":162,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-20T11:06:34.348Z","updated_at":"2026-02-18T12:01:05.084Z","avatar_url":"https://github.com/internxt.png","language":"TypeScript","readme":"# Mail cryptographic library\n\n[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=ncloc)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=code_smells)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)\n\n# Project Manteinance\n\nWe aim to have:\n\n- An 'A' score on Maintainability Rating\n- An 'A' score on Security Rating\n- Less than 3% duplicated lines\n- A 90% tests coverage\n\n## Scripts\n\n### `yarn run lint` (`yarn run lint:ts`)\n\n- Runs .ts linter\n\n### `yarn test` (`vitest run`)\n\n- Runs unit tests with [Vitest](https://vitest.dev/)\n\n\n### `yarn build`\n\nBuilds the app for production to the `build` folder.\n\n## Project Structure\n\n### Core Cryptography Modules\n\n- **`asymmetric-crypto`** - Asymmetric elliptic curves cryptography (curve P-521) for generating keys and deriving a shared secret between two users\n- **`symmetric-crypto`** - Symmetric encryption operations (AES-GCM) for data encryption and decryption\n- **`post-quantum-crypto`** - Post-quantum cryptographic algorithms (MLKEMs) for generating keys and deriving a shared secret between two users\n- **`hash`** - Cryptographic hashing functions (BLAKE3) for data integrity, commitments and secret extensions\n\n### Key Management\n\n- **`derive-key`** - Key derivation functions for deriving cryptographic keys from passwords (ARGON2) and base key (BLAKE3 in KDF mode)\n- **`key-wrapper`** - Key wrapping and unwrapping functions for secure symmetric key storage and transport\n- **`keystore-crypto`** - Keystore cryptographic operations for securing user's keys\n- **`keystore-service`** - Keystore management service for communicating with the server\n\n### Email Security\n\n- **`email-crypto`** - End-to-end email encryption and decryption using hybrid cryptography and password-protection\n- **`email-search`** - Email indexing on the client side to enable search while preserving privacy\n- **`email-service`** - Email management service for communicating with the server\n\n### Infrastructure\n\n- **`storage-service`** - Abstraction layer for accessing Local Storage and Session Storage\n- **`utils`** - Type converter functions and access to enviromental variables\n- **`types`** - TypeScript type definitions for all library interfaces and data structures\n- **`constants`** - Cryptographic constants, algorithm identifiers, and configuration values\n\n## Usage Example\n\n```typescript\nimport {\n  asymmetric,\n  symmetric,\n  utils,\n  emailCrypto,\n  pq,\n  keystoreService,\n  deriveKey,\n  hash,\n} from 'internxt-crypto';\n\n// Asymmetric encryption\nconst keysAlice = await asymmetric.generateEccKeys();\nconst keysBob = await asymmetric.generateEccKeys();\nconst resultAlice = await asymmetric.deriveSecretKey(keysBob.publicKey, keysAlice.privateKey);\nconst resultBob = await asymmetric.deriveSecretKey(keysAlice.publicKey, keysBob.privateKey);\nexpect(resultAlice).toStrictEqual(resultBob);\n\n// Symmetric encryption\nconst data = utils.UTF8ToUint8('Sensitive information to encrypt'); // convert to Uint8Array \nconst additionalData = 'Additional non-secret data';\nconst key = await symmetric.genSymmetricCryptoKey(); // CryptoKey \nconst ciphertext: Uint8Array = await symmetric.encryptSymmetrically(key, data, additionalData);\nconst plainText = await symmetric.decryptSymmetrically(encryptionKey, ciphertext, additionalData);\nexpect(data).toStrictEqual(plainText);\n\n// Post qunatum cryptography\nconst keys = pq.generateKyberKeys();\nconst { cipherText, sharedSecret } = pq.encapsulateKyber(keys.publicKey);\nconst result = pq.decapsulateKyber(cipherText, keys.secretKey);\nexpect(result).toStrictEqual(sharedSecret);\n\n// Hash\nconst result = await hash.hashData(['']);\nconst expectedResult = 'af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262';\nexpect(result).toStrictEqual(expectedResult);\n\n// Key derivation\nconst context = 'BLAKE3 2019-12-27 16:29:52 test vectors context';\nconst baseKey = symmetric.genSymmetricKey();  // Uint8Array \nconst key = await deriveKey.deriveSymmetricCryptoKeyFromContext(context, baseKey);\nexpect(key).instanceOf(CryptoKey);\n\nconst password = 'your password';\nconst { keyHex, saltHex } = await deriveKey.getKeyFromPasswordHex(password);\nconst result = await deriveKey.verifyKeyFromPasswordHex(password, saltHex, keyHex);\nexpect(result).toBe(true);\n\n// Hybrid email encryption\n\nconst emailBody: EmailBody = {\n    text: 'email text',\n    createdAt: '2025-06-14T08:11:22.000Z',\n    labels: ['label 1', 'label2'],\n};\n\nconst userAlice = {\n    email: 'alice email',\n    name: 'alice',\n};\n\nconst userBob = {\n    email: 'bob email',\n    name: 'bob',\n};\nconst { privateKeys: alicePrivateKeys, publicKeys: alicePublicKeys } = await emailCrypto.generateEmailKeys();\nconst { privateKeys: bobPrivateKeys, publicKeys: bobPublicKeys } = await emailCrypto.generateEmailKeys();\n\nconst emailBody: EmailBody = {\n  text: 'email body',\n};\n\nconst emailParams: EmailPublicParameters = {\n  labels: ['label 1', 'label2'],\n  createdAt: '2025-06-14T08:11:22.000Z',\n  subject: 'email subject',\n  sender: userAlice,\n  recipient: userBob,\n  replyToEmailID: generateUuid(),\n};\n\nconst email: Email = {\n  id:  generateUuid(),\n  params: emailParams,\n  body: emailBody,\n};\nconst encryptedEmail = await emailCrypto.encryptEmailHybrid(email, bobPublicKeys, alicePrivateKeys);\nconst decryptedEmail = await emailCrypto.decryptEmailHybrid(encryptedEmail, alicePublicKeys, bobPrivateKeys);\nexpect(decryptedEmail).toStrictEqual(email);\n\n\n// password-protected email\nconst sharedSecret = 'secret shared between Alice and Bob';\nconst encryptedEmail = await emailCrypto.createPwdProtectedEmail(email, sharedSecret);\nconst decryptedEmail = await emailCrypto.decryptPwdProtectedEmail(encryptedEmail, sharedSecret);\nexpect(decryptedEmail).toStrictEqual(email);\n\n// keystore\n\n// For this to work, session storage must have UserID and baseKey\nconst { encryptionKeystore, recoveryKeystore, recoveryCodes } = await createEncryptionAndRecoveryKeystores();\nconst result_enc = await keystoreService.openEncryptionKeystore(encryptionKeystore);\nconst result_rec = await keystoreService.openRecoveryKeystore(recoveryCodes, recoveryKeystore); \nexpect(result_enc).toStrictEqual(result_rec);\n\n// Email storage and search\n\n// Between sessions emails are stored encrypted in IndexedDB. The encryption key is derived from user's baseKey\n// During the session, all emails are decrypted and stored in the cache (up to 600 MB, if excides - we delete oldests emails)\n// For search, we build a search index from cache, then use Flexsearch for the search. \n// The search is doen separately for email content, subject, sender and recivers. \n\n// Open IndexedDB database\nconst userID = 'user ID';\nconst db = await openDatabase(userID);\n\n// Derive database key\nconst key = await deriveIndexKey(baseKey);\n\n// Encrypt and store one or several emails\nawait encryptAndStoreEmail(email, key, db);\nawait encryptAndStoreManyEmail(emails, key, db);\n\n// Delete given email by its ID\nawait deleteEmail(emailID, db);\n\n// Delete oldests emails\nconst number = 5;\nawait deleteOldestEmails(number, db);\n\n// Get all emails with or without sorting\nconst allEmails = await getAndDecryptAllEmails(key, db);\nconst newestFirst = await getAllEmailsSortedNewestFirst(db, key);\nconst oldestFirst = await getAllEmailsSortedOldestFirst(db, key);\n\n// Get the number of stored emails\nconst count = await getEmailCount(db);\n\n// Close IndexedDB database\ncloseDatabase(db);\n\n// Delete IndexedDB database\nawait deleteDatabase(userID);\n\n// Create email cache \nconst esCache = await createCacheFromDB(key, db);\n\n// Add one or multiple emails to cache\nconst result = addEmailToCache(email, esCache);\nexpect(result.success).toBe(true);\n\nconst result = addEmailsToCache(emails, esCache);\nexpect(result.success).toBe(true);\n\n// Get email from cache by its ID\nconst email = await getEmailFromCache(emailID, esCache);\n\n// Delete email from cache by its ID\nawait deleteEmailFromCache(emailID, esCache);\n\n// Create search index and search by query\n const searchIndex = await buildSearchIndexFromCache(esCache);\n const query = 'keywords to search';\n const options = {\n    fields: ['subject'], // in which fields to search, all by deafult (subject, body, from, to)\n    limit: 5,  // result limit, 50 by default\n    boost: { subject: 3, body: 1, from: 2, to: 2 }, // custom waights for matches in different email parts\n  };\n const result: EmailSearchResult = await searchEmails(query, esCache, searchIndex);\n\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finternxt%2Fcrypto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finternxt%2Fcrypto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finternxt%2Fcrypto/lists"}