internxt-crypto 0.0.7-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Internxt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # Mail cryptographic library
2
+
3
+ [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
4
+ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
5
+ [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
6
+ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
7
+ [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
8
+ [![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)
9
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=internxt_crypto&metric=coverage)](https://sonarcloud.io/summary/new_code?id=internxt_crypto)
10
+
11
+ # Project Manteinance
12
+
13
+ We aim to have:
14
+
15
+ - An 'A' score on Maintainability Rating
16
+ - An 'A' score on Security Rating
17
+ - Less than 3% duplicated lines
18
+ - A 90% tests coverage
19
+
20
+ ## Scripts
21
+
22
+ ### `yarn run lint` (`yarn run lint:ts`)
23
+
24
+ - Runs .ts linter
25
+
26
+ ### `yarn test` (`vitest run`)
27
+
28
+ - Runs unit tests with [Vitest](https://vitest.dev/)
29
+
30
+
31
+ ### `yarn build`
32
+
33
+ Builds the app for production to the `build` folder.
34
+
35
+ ## Project Structure
36
+
37
+ ### Core Cryptography Modules
38
+
39
+ - **`asymmetric-crypto`** - Asymmetric elliptic curves cryptography (curve P-521) for generating keys and deriving a shared secret between two users
40
+ - **`symmetric-crypto`** - Symmetric encryption operations (AES-GCM) for data encryption and decryption
41
+ - **`post-quantum-crypto`** - Post-quantum cryptographic algorithms (MLKEMs) for generating keys and deriving a shared secret between two users
42
+ - **`hash`** - Cryptographic hashing functions (BLAKE3) for data integrity, commitments and secret extensions
43
+
44
+ ### Key Management
45
+
46
+ - **`derive-key`** - Key derivation functions for deriving cryptographic keys from passwords (ARGON2) and base key (BLAKE3 in KDF mode)
47
+ - **`key-wrapper`** - Key wrapping and unwrapping functions for secure symmetric key storage and transport
48
+ - **`keystore-crypto`** - Keystore cryptographic operations for securing user's keys
49
+ - **`keystore-service`** - Keystore management service for communicating with the server
50
+
51
+ ### Email Security
52
+
53
+ - **`email-crypto`** - End-to-end email encryption and decryption using hybrid cryptography and password-protection
54
+ - **`email-search`** - Email indexing on the client side to enable search while preserving privacy
55
+ - **`email-service`** - Email management service for communicating with the server
56
+
57
+ ### Infrastructure
58
+
59
+ - **`storage-service`** - Abstraction layer for accessing Local Storage and Session Storage
60
+ - **`utils`** - Type converter functions and access to enviromental variables
61
+ - **`types`** - TypeScript type definitions for all library interfaces and data structures
62
+ - **`constants`** - Cryptographic constants, algorithm identifiers, and configuration values
63
+
64
+ ## Usage Example
65
+
66
+ ```typescript
67
+ import {
68
+ asymmetric,
69
+ symmetric,
70
+ utils,
71
+ emailCrypto,
72
+ pq,
73
+ keystoreService,
74
+ deriveKey,
75
+ hash,
76
+ SymmetricCiphertext
77
+ } from 'internxt-crypto';
78
+
79
+ // Asymmetric encryption
80
+ const keysAlice = await asymmetric.generateEccKeys();
81
+ const keysBob = await asymmetric.generateEccKeys();
82
+ const resultAlice = await asymmetric.deriveSecretKey(keysBob.publicKey, keysAlice.privateKey);
83
+ const resultBob = await asymmetric.deriveSecretKey(keysAlice.publicKey, keysBob.privateKey);
84
+ expect(resultAlice).toStrictEqual(resultBob);
85
+
86
+ // Symmetric encryption
87
+ const data = utils.UTF8ToUint8('Sensitive information to encrypt'); // convert to Uint8Array
88
+ const additionalData = 'Additional non-secret data';
89
+ const key = await symmetric.genSymmetricCryptoKey(); // CryptoKey
90
+ const ciphertext: SymmetricCiphertext = await symmetric.encryptSymmetrically(key, data, additionalData);
91
+ const plainText = await symmetric.decryptSymmetrically(encryptionKey, ciphertext, additionalData);
92
+ expect(data).toStrictEqual(plainText);
93
+
94
+ // Post qunatum cryptography
95
+ const keys = pq.generateKyberKeys();
96
+ const { cipherText, sharedSecret } = pq.encapsulateKyber(keys.publicKey);
97
+ const result = pq.decapsulateKyber(cipherText, keys.secretKey);
98
+ expect(result).toStrictEqual(sharedSecret);
99
+
100
+ // Hash
101
+ const result = await hash.hashData(['']);
102
+ const expectedResult = 'af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262';
103
+ expect(result).toStrictEqual(expectedResult);
104
+
105
+ // Key derivation
106
+ const context = 'BLAKE3 2019-12-27 16:29:52 test vectors context';
107
+ const baseKey = symmetric.genSymmetricKey(); // Uint8Array
108
+ const key = await deriveKey.deriveSymmetricCryptoKeyFromContext(context, baseKey);
109
+ expect(key).instanceOf(CryptoKey);
110
+
111
+ const password = 'your password';
112
+ const { keyHex, saltHex } = await deriveKey.getKeyFromPasswordHex(password);
113
+ const result = await deriveKey.verifyKeyFromPasswordHex(password, saltHex, keyHex);
114
+ expect(result).toBe(true);
115
+
116
+ // Hybrid email encryption
117
+
118
+ const emailBody: EmailBody = {
119
+ text: 'email text',
120
+ createdAt: '2025-06-14T08:11:22.000Z',
121
+ labels: ['label 1', 'label2'],
122
+ };
123
+
124
+ const userAlice = {
125
+ email: 'alice email',
126
+ name: 'alice',
127
+ id: '1',
128
+ };
129
+
130
+ const userBob = {
131
+ email: 'bob email',
132
+ name: 'bob',
133
+ id: '2',
134
+ };
135
+ const { privateKeys: alicePrivateKeys, publicKeys: alicePublicKeys } = await emailCrypto.generateEmailKeys();
136
+ const { privateKeys: bobPrivateKeys, publicKeys: bobPublicKeys } = await emailCrypto.generateEmailKeys();
137
+
138
+ const emailBody: EmailBody = {
139
+ text: 'email body',
140
+ };
141
+
142
+ const emailParams: EmailPublicParameters = {
143
+ labels: ['label 1', 'label2'],
144
+ createdAt: '2025-06-14T08:11:22.000Z',
145
+ subject: 'email subject',
146
+ sender: userAlice,
147
+ recipient: userBob,
148
+ replyToEmailID: 2,
149
+ };
150
+
151
+ const email: Email = {
152
+ id: 'email id',
153
+ params: emailParams,
154
+ body: emailBody,
155
+ };
156
+ const encryptedEmail = await emailCrypto.encryptEmailHybrid(email, bobPublicKeys, alicePrivateKeys);
157
+ const decryptedEmail = await emailCrypto.decryptEmailHybrid(encryptedEmail, alicePublicKeys, bobPrivateKeys);
158
+ expect(decryptedEmail).toStrictEqual(email);
159
+
160
+
161
+ // password-protected email
162
+ const sharedSecret = 'secret shared between Alice and Bob';
163
+ const encryptedEmail = await emailCrypto.createPwdProtectedEmail(email, sharedSecret);
164
+ const decryptedEmail = await emailCrypto.decryptPwdProtectedEmail(encryptedEmail, sharedSecret);
165
+ expect(decryptedEmail).toStrictEqual(email);
166
+
167
+ // keystore
168
+
169
+ // For this to work, session storage must have UserID and baseKey
170
+ const { encryptionKeystore, recoveryKeystore, recoveryCodes } = await createEncryptionAndRecoveryKeystores();
171
+ const result_enc = await keystoreService.openEncryptionKeystore(encryptionKeystore);
172
+ const result_rec = await keystoreService.openRecoveryKeystore(recoveryCodes, recoveryKeystore);
173
+ expect(result_enc).toStrictEqual(result_rec);
174
+
175
+ // Email storage and search
176
+
177
+ // Between sessions emails are stored encrypted in IndexedDB. The encryption key is derived from user's baseKey
178
+ // During the session, all emails are decrypted and stored in the cache (up to 600 MB, if excides - we delete oldests emails)
179
+ // For search, we build a search index from cache, then use Flexsearch for the search.
180
+ // The search is doen separately for email content, subject, sender and recivers.
181
+
182
+ // Open IndexedDB database
183
+ const userID = 'user ID';
184
+ const db = await openDatabase(userID);
185
+
186
+ // Derive database key
187
+ const key = await deriveIndexKey(baseKey);
188
+
189
+ // Encrypt and store one or several emails
190
+ await encryptAndStoreEmail(email, key, db);
191
+ await encryptAndStoreManyEmail(emails, key, db);
192
+
193
+ // Delete given email by its ID
194
+ await deleteEmail(emailID, db);
195
+
196
+ // Delete oldests emails
197
+ const number = 5;
198
+ await deleteOldestEmails(number, db);
199
+
200
+ // Get all emails with or without sorting
201
+ const allEmails = await getAndDecryptAllEmails(key, db);
202
+ const newestFirst = await getAllEmailsSortedNewestFirst(db, key);
203
+ const oldestFirst = await getAllEmailsSortedOldestFirst(db, key);
204
+
205
+ // Get the number of stored emails
206
+ const count = await getEmailCount(db);
207
+
208
+ // Close IndexedDB database
209
+ closeDatabase(db);
210
+
211
+ // Delete IndexedDB database
212
+ await deleteDatabase(userID);
213
+
214
+ // Create email cache
215
+ const esCache = await createCacheFromDB(key, db);
216
+
217
+ // Add one or multiple emails to cache
218
+ const result = addEmailToCache(email, esCache);
219
+ expect(result.success).toBe(true);
220
+
221
+ const result = addEmailsToCache(emails, esCache);
222
+ expect(result.success).toBe(true);
223
+
224
+ // Get email from cache by its ID
225
+ const email = await getEmailFromCache(emailID, esCache);
226
+
227
+ // Delete email from cache by its ID
228
+ await deleteEmailFromCache(emailID, esCache);
229
+
230
+ // Create search index and search by query
231
+ const searchIndex = await buildSearchIndexFromCache(esCache);
232
+ const query = 'keywords to search';
233
+ const options = {
234
+ fields: ['subject'], // in which fields to search, all by deafult (subject, body, from, to)
235
+ limit: 5, // result limit, 50 by default
236
+ boost: { subject: 3, body: 1, from: 2, to: 2 }, // custom waights for matches in different email parts
237
+ };
238
+ const result: EmailSearchResult = await searchEmails(query, esCache, searchIndex);
239
+
240
+ ```
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=__vite-browser-external-Dyvby5gX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__vite-browser-external-Dyvby5gX.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=__vite-browser-external-l0sNRNKZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__vite-browser-external-l0sNRNKZ.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}