Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7909f63
add opaque
TamaraFinogina Oct 14, 2025
496bc7a
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Oct 14, 2025
1839209
add disble 2FA
TamaraFinogina Oct 15, 2025
9489a85
add password change, add login after signup
TamaraFinogina Oct 17, 2025
6a1dcf1
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Oct 17, 2025
628aa90
remove extra changes
TamaraFinogina Oct 17, 2025
04077cc
add tests for opaque versions of signUp, login, disable2FA, and chan…
TamaraFinogina Oct 23, 2025
44db502
add new files
TamaraFinogina Oct 23, 2025
1bc9d59
reduce changes in yarn.lock
TamaraFinogina Oct 23, 2025
bf329f2
reduce changes in types
TamaraFinogina Oct 23, 2025
916fc9c
upgrade internxt-crypto
TamaraFinogina Oct 24, 2025
74868ca
put is2FAorOpaqueNeeded separately
TamaraFinogina Oct 24, 2025
76922ee
remove 2FA example
TamaraFinogina Oct 24, 2025
10cefd0
fix PR comments
TamaraFinogina Oct 24, 2025
c65e17c
fix test description
TamaraFinogina Oct 27, 2025
080b94e
don't use localStorage for storing values between tests
TamaraFinogina Oct 27, 2025
872dd0c
add more tests
TamaraFinogina Oct 27, 2025
c974d3f
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Oct 27, 2025
945e306
fix sonar errors
TamaraFinogina Oct 27, 2025
5d32592
fix _ in crypto test
TamaraFinogina Oct 27, 2025
3fee78b
fix comments
TamaraFinogina Oct 28, 2025
e1223b0
Merge branch 'master' into opaque_poc
CandelR Nov 3, 2025
0ab63be
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Nov 4, 2025
4fba4dd
Merge branch 'master' into opaque_poc
CandelR Nov 5, 2025
ee02788
Merge branch 'master' into opaque_poc
CandelR Nov 7, 2025
739f848
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Nov 14, 2025
994b85e
update sdk, update internext-crypto
TamaraFinogina Nov 21, 2025
5d0350e
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Nov 21, 2025
8bcdaa8
yarn.lock changes
TamaraFinogina Nov 21, 2025
b9be060
increase node version
TamaraFinogina Nov 21, 2025
b97e446
use node 20
TamaraFinogina Nov 26, 2025
e54ff76
fix crypto package link
TamaraFinogina Nov 26, 2025
c1dda59
fix pathes
TamaraFinogina Nov 26, 2025
cac4356
merge master
TamaraFinogina Nov 26, 2025
baf3538
address PR comments
TamaraFinogina Dec 5, 2025
7b8bca5
merge master
TamaraFinogina Dec 5, 2025
8a70388
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Dec 15, 2025
435b604
remove diasable 2FA
TamaraFinogina Dec 15, 2025
19f663a
Merge remote-tracking branch 'origin/master' into opaque_poc
TamaraFinogina Dec 18, 2025
6a35e91
update sdk and api
TamaraFinogina Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cd-cloudflare.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
packages: read
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- name: Checkout repo
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.11.1'
node-version: '20.19.0'
- run: echo "registry=https://registry.yarnpkg.com/" > .npmrc
- run: echo "@internxt:registry=https://npm.pkg.github.com" >> .npmrc
- run: echo //npm.pkg.github.com/:_authToken=${{ secrets.PERSONAL_ACCESS_TOKEN }} >> .npmrc
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
"@iconscout/react-unicons": "^1.1.6",
"@internxt/css-config": "1.1.0",
"@internxt/lib": "1.4.1",
"@internxt/sdk": "=1.11.17",
"@internxt/sdk": "=1.11.25",
"@internxt/ui": "0.1.1",
"@phosphor-icons/react": "^2.1.7",
"@popperjs/core": "^2.11.6",
"@reduxjs/toolkit": "^1.6.0",
"@serenity-kit/opaque": "^1.0.0",
"@stripe/react-stripe-js": "^2.7.1",
"@stripe/stripe-js": "^3.5.0",
"@typeform/embed-react": "^1.19.0",
Expand All @@ -36,6 +37,7 @@
"i18next": "^22.4.9",
"i18next-browser-languagedetector": "^7.2.0",
"idb": "^6.1.5",
"internxt-crypto": "https://github.com/internxt/crypto/releases/download/v.0.0.10-alpha/internxt-crypto-0.0.10-alpha.tgz",
"js-file-download": "^0.4.12",
"lint-staged": "^13.1.0",
"lodash": "^4.17.21",
Expand Down
2 changes: 1 addition & 1 deletion src/app/analytics/ga.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,4 +513,4 @@ describe('Testing GA Service', () => {
});
});
});
});
});
2 changes: 1 addition & 1 deletion src/app/analytics/ga.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ function trackBeginCheckout(params: TrackBeginCheckoutParams): void {
}
}

function trackPurchase(): void {
function trackPurchase(): void {
try {
const userSettings = localStorageService.getUser() as UserSettings;
if (!userSettings) {
Expand Down
4 changes: 4 additions & 0 deletions src/services/auth.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const ECC_KEY_AUX = 'user-private-key';
export const KYBER_KEY_AUX = 'user-private-kyber-key';
export const MNEMONIC_AUX = 'user-mnemonic';
export const SESSION_KEY_AUX = 'User Session Key';
16 changes: 16 additions & 0 deletions src/services/auth.crypto.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, it } from 'vitest';
import { generateUserSecrets, encryptUserKeysAndMnemonic, decryptUserKeysAndMnemonic } from './auth.crypto';

describe('Test auth crypto functions', () => {
it('should sucessfully encrypt and decrypt user keys and mnemonic', async () => {
const { keys, mnemonic } = await generateUserSecrets();
const exportKey = 'Srp6AzybbyludWuaVwGoHa1C2H0Qtv7JR0sKGLSWe8Ho8_q9hezfYD2RYb9IUrW999pH4VlABgDLse484zAapg';

const { encMnemonic, encKeys } = await encryptUserKeysAndMnemonic(keys, mnemonic, exportKey);

const { keys: decKeys, mnemonic: decMnemonic } = await decryptUserKeysAndMnemonic(encMnemonic, encKeys, exportKey);

expect(keys).toStrictEqual(decKeys);
expect(mnemonic).toEqual(decMnemonic);
});
});
115 changes: 115 additions & 0 deletions src/services/auth.crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
encryptSymmetrically,
deriveSymmetricCryptoKey,
decryptSymmetrically,
importSymmetricCryptoKey,
} from 'internxt-crypto/symmetric-crypto';
import { getKeyFromPasswordAndSalt, getKeyFromPassword } from 'internxt-crypto/derive-key';
import {
UTF8ToUint8,
base64ToUint8Array,
uint8ArrayToBase64,
genMnemonic,
mnemonicToBytes,
bytesToMnemonic,
uint8ToUTF8,
} from 'internxt-crypto/utils';
import { UserKeys } from '@internxt/sdk';
import { generateNewKeys } from 'app/crypto/services/pgp.service';
import { ECC_KEY_AUX, KYBER_KEY_AUX, MNEMONIC_AUX, SESSION_KEY_AUX } from './auth.constants';

export async function encryptUserKeysAndMnemonic(
userKeys: UserKeys,
mnemonic: string,
exportKey: string,
): Promise<{ encMnemonic: string; encKeys: UserKeys }> {
const exportKeyBytes = safeBase64ToBytes(exportKey);
const cryptoKey = await deriveSymmetricCryptoKey(exportKeyBytes);
const key = UTF8ToUint8(userKeys.ecc.privateKey);
const encPrivateKey = await encryptSymmetrically(cryptoKey, key, UTF8ToUint8(ECC_KEY_AUX));
const keyKyber = base64ToUint8Array(userKeys.kyber.privateKey);
const encPrivateKyberKey = await encryptSymmetrically(cryptoKey, keyKyber, UTF8ToUint8(KYBER_KEY_AUX));
const mnemonicArray = mnemonicToBytes(mnemonic);
const mnemonicCipher = await encryptSymmetrically(cryptoKey, mnemonicArray, UTF8ToUint8(MNEMONIC_AUX));
const encMnemonic = uint8ArrayToBase64(mnemonicCipher);

const encKeys: UserKeys = {
ecc: {
privateKey: uint8ArrayToBase64(encPrivateKey),
publicKey: userKeys.ecc.publicKey,
},
kyber: {
privateKey: uint8ArrayToBase64(encPrivateKyberKey),
publicKey: userKeys.kyber.publicKey,
},
};
return { encMnemonic, encKeys };
}

export async function decryptUserKeysAndMnemonic(
encMnemonic: string,
encKeys: UserKeys,
exportKey: string,
): Promise<{ keys: UserKeys; mnemonic: string }> {
const exportKeyBytes = safeBase64ToBytes(exportKey);
const cryptoKey = await deriveSymmetricCryptoKey(exportKeyBytes);
const encKey = base64ToUint8Array(encKeys.ecc.privateKey);
const privateKey = await decryptSymmetrically(cryptoKey, encKey, UTF8ToUint8(ECC_KEY_AUX));
const encKyberKey = base64ToUint8Array(encKeys.kyber.privateKey);
const privateKyberKey = await decryptSymmetrically(cryptoKey, encKyberKey, UTF8ToUint8(KYBER_KEY_AUX));
const encMnemonicArray = base64ToUint8Array(encMnemonic);
const mnemonicArray = await decryptSymmetrically(cryptoKey, encMnemonicArray, UTF8ToUint8(MNEMONIC_AUX));
const mnemonic = bytesToMnemonic(mnemonicArray);

const keys: UserKeys = {
ecc: {
privateKey: uint8ToUTF8(privateKey),
publicKey: encKeys.ecc.publicKey,
},
kyber: {
privateKey: uint8ArrayToBase64(privateKyberKey),
publicKey: encKeys.kyber.publicKey,
},
};
return { keys, mnemonic };
}

export const encryptSessionKey = async (
password: string,
sessionKey: string,
): Promise<{ sessionKeyEnc: string; salt: string }> => {
const { key, salt } = await getKeyFromPassword(password);
const cryptoKey = await importSymmetricCryptoKey(key);
const sessionKeyArray = safeBase64ToBytes(sessionKey);
const sessionKeyEncCipher = await encryptSymmetrically(cryptoKey, sessionKeyArray, UTF8ToUint8(SESSION_KEY_AUX));
const sessionKeyEnc = uint8ArrayToBase64(sessionKeyEncCipher);
return { sessionKeyEnc, salt: uint8ArrayToBase64(salt) };
};

export const safeBase64ToBytes = (urlSafeBase64: string): Uint8Array => {
const base64 = urlSafeBase64.replaceAll('-', '+').replaceAll('_', '/');
const padding = (4 - (base64.length % 4)) % 4;
return base64ToUint8Array(base64 + '='.repeat(padding));
};

export const decryptSessionKey = async (password: string, sessionKeyEnc: string, salt: string): Promise<Uint8Array> => {
const keyBytes = await getKeyFromPasswordAndSalt(password, base64ToUint8Array(salt));
const key = await importSymmetricCryptoKey(keyBytes);
const sessionKeyCipher = base64ToUint8Array(sessionKeyEnc);
const sessionKeyArray = await decryptSymmetrically(key, sessionKeyCipher, UTF8ToUint8(SESSION_KEY_AUX));
return sessionKeyArray;
};

export const generateUserSecrets = async (): Promise<{ keys: UserKeys; mnemonic: string }> => {
const mnemonic = genMnemonic(256);

const { privateKeyArmored, publicKeyArmored, publicKyberKeyBase64, privateKyberKeyBase64 } = await generateNewKeys();
const keys: UserKeys = {
ecc: { privateKey: privateKeyArmored, publicKey: publicKeyArmored },
kyber: {
privateKey: privateKyberKeyBase64,
publicKey: publicKyberKeyBase64,
},
};
return { keys, mnemonic };
};
Loading
Loading