-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c5ff9cf
commit 7f8f869
Showing
6 changed files
with
207 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { CryptoProvider } from '@internxt/sdk'; | ||
import { Keys, Password } from '@internxt/sdk/dist/auth'; | ||
import { KeysService } from './keys.service'; | ||
import { ConfigService } from '../services/config.service'; | ||
|
||
interface PassObjectInterface { | ||
salt?: string | null; | ||
password: string; | ||
} | ||
|
||
export class CryptoService { | ||
public static readonly instance: CryptoService = new CryptoService(); | ||
|
||
public static readonly cryptoProvider: CryptoProvider = { | ||
encryptPasswordHash(password: Password, encryptedSalt: string): string { | ||
const salt = CryptoService.instance.decryptText(encryptedSalt); | ||
const hashObj = CryptoService.instance.passToHash({ password, salt }); | ||
return CryptoService.instance.encryptText(hashObj.hash); | ||
}, | ||
async generateKeys(password: Password): Promise<Keys> { | ||
const { privateKeyArmoredEncrypted, publicKeyArmored, revocationCertificate } = | ||
await KeysService.instance.generateNewKeysWithEncrypted(password); | ||
const keys: Keys = { | ||
privateKeyEncrypted: privateKeyArmoredEncrypted, | ||
publicKey: publicKeyArmored, | ||
revocationCertificate: revocationCertificate, | ||
}; | ||
return keys; | ||
}, | ||
}; | ||
|
||
// Method to hash password. If salt is passed, use it, in other case use crypto lib for generate salt | ||
public passToHash = (passObject: PassObjectInterface): { salt: string; hash: string } => { | ||
const salt = passObject.salt ? CryptoJS.enc.Hex.parse(passObject.salt) : CryptoJS.lib.WordArray.random(128 / 8); | ||
const hash = CryptoJS.PBKDF2(passObject.password, salt, { keySize: 256 / 32, iterations: 10000 }); | ||
const hashedObjetc = { | ||
salt: salt.toString(), | ||
hash: hash.toString(), | ||
}; | ||
|
||
return hashedObjetc; | ||
}; | ||
|
||
// AES Plain text encryption method | ||
public encryptText = (textToEncrypt: string): string => { | ||
const APP_CRYPTO_SECRET = ConfigService.instance.get('APP_CRYPTO_SECRET'); | ||
return this.encryptTextWithKey(textToEncrypt, APP_CRYPTO_SECRET); | ||
}; | ||
|
||
// AES Plain text decryption method | ||
public decryptText = (encryptedText: string): string => { | ||
const APP_CRYPTO_SECRET = ConfigService.instance.get('APP_CRYPTO_SECRET'); | ||
return this.decryptTextWithKey(encryptedText, APP_CRYPTO_SECRET); | ||
}; | ||
|
||
// AES Plain text encryption method with enc. key | ||
public encryptTextWithKey = (textToEncrypt: string, keyToEncrypt: string): string => { | ||
const bytes = CryptoJS.AES.encrypt(textToEncrypt, keyToEncrypt).toString(); | ||
const text64 = CryptoJS.enc.Base64.parse(bytes); | ||
|
||
return text64.toString(CryptoJS.enc.Hex); | ||
}; | ||
|
||
// AES Plain text decryption method with enc. key | ||
public decryptTextWithKey = (encryptedText: string, keyToDecrypt: string): string => { | ||
const reb = CryptoJS.enc.Hex.parse(encryptedText); | ||
const bytes = CryptoJS.AES.decrypt(reb.toString(CryptoJS.enc.Base64), keyToDecrypt); | ||
|
||
return bytes.toString(CryptoJS.enc.Utf8); | ||
}; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import crypto from 'crypto'; | ||
|
||
export const UserFixture = { | ||
userId: crypto.randomBytes(16).toString('hex'), | ||
uuid: crypto.randomBytes(16).toString('hex'), | ||
email: crypto.randomBytes(16).toString('hex'), | ||
name: crypto.randomBytes(16).toString('hex'), | ||
lastname: crypto.randomBytes(16).toString('hex'), | ||
username: crypto.randomBytes(16).toString('hex'), | ||
bridgeUser: crypto.randomBytes(16).toString('hex'), | ||
bucket: crypto.randomBytes(16).toString('hex'), | ||
backupsBucket: crypto.randomBytes(16).toString('hex'), | ||
root_folder_id: crypto.randomInt(1, 9999), | ||
sharedWorkspace: false, | ||
credit: crypto.randomInt(1, 9999), | ||
mnemonic: crypto.randomBytes(16).toString('hex'), | ||
privateKey: crypto.randomBytes(16).toString('hex'), | ||
publicKey: crypto.randomBytes(16).toString('hex'), | ||
revocationKey: crypto.randomBytes(16).toString('hex'), | ||
teams: false, | ||
appSumoDetails: null, | ||
registerCompleted: true, | ||
hasReferralsProgram: false, | ||
createdAt: new Date(), | ||
avatar: crypto.randomBytes(16).toString('hex'), | ||
emailVerified: true, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { expect } from 'chai'; | ||
import sinon, { SinonSandbox } from 'sinon'; | ||
import crypto from 'crypto'; | ||
import { AuthService } from '../../src/services/auth.service'; | ||
import { KeysService } from '../../src/services/keys.service'; | ||
import { CryptoService } from '../../src/services/crypto.service'; | ||
import { SdkManager } from '../../src/services/SDKManager.service'; | ||
import { UserFixture } from '../fixtures/auth.fixture'; | ||
import { Auth, LoginDetails, SecurityDetails } from '@internxt/sdk'; | ||
|
||
describe('Auth service', () => { | ||
let authServiceSandbox: SinonSandbox; | ||
|
||
beforeEach(() => { | ||
authServiceSandbox = sinon.createSandbox(); | ||
}); | ||
|
||
afterEach(function () { | ||
authServiceSandbox.restore(); | ||
}); | ||
|
||
it('When user logs in, then login user credentials are generated', async () => { | ||
const loginResponse = { | ||
token: crypto.randomBytes(16).toString('hex'), | ||
newToken: crypto.randomBytes(16).toString('hex'), | ||
user: UserFixture, | ||
userTeam: null, | ||
}; | ||
|
||
authServiceSandbox.stub(Auth.prototype, 'login').returns(Promise.resolve(loginResponse)); | ||
authServiceSandbox.stub(SdkManager.instance, 'getAuth').returns(Auth.prototype); | ||
authServiceSandbox.stub(KeysService.instance, 'decryptPrivateKey').returns(loginResponse.user.privateKey); | ||
authServiceSandbox.stub(KeysService.instance, 'assertPrivateKeyIsValid').resolves(); | ||
authServiceSandbox.stub(KeysService.instance, 'assertValidateKeys').resolves(); | ||
authServiceSandbox.stub(CryptoService.instance, 'decryptTextWithKey').returns(loginResponse.user.mnemonic); | ||
|
||
const responseLogin = await AuthService.instance.doLogin( | ||
loginResponse.user.email, | ||
crypto.randomBytes(16).toString('hex'), | ||
'', | ||
); | ||
const expectedResponseLogin = { | ||
user: { ...loginResponse.user, privateKey: Buffer.from(loginResponse.user.privateKey).toString('base64') }, | ||
token: loginResponse.token, | ||
newToken: loginResponse.newToken, | ||
mnemonic: loginResponse.user.mnemonic, | ||
}; | ||
expect(responseLogin).to.eql(expectedResponseLogin); | ||
}); | ||
|
||
it('When user logs in and credentials are not correct, then an error is thrown', async () => { | ||
const loginDetails: LoginDetails = { | ||
email: crypto.randomBytes(16).toString('hex'), | ||
password: crypto.randomBytes(8).toString('hex'), | ||
tfaCode: crypto.randomInt(1, 999999).toString().padStart(6, '0'), | ||
}; | ||
|
||
authServiceSandbox.stub(Auth.prototype, 'login').withArgs(loginDetails, CryptoService.cryptoProvider).rejects(); | ||
authServiceSandbox.stub(SdkManager.instance, 'getAuth').returns(Auth.prototype); | ||
|
||
try { | ||
await AuthService.instance.doLogin(loginDetails.email, loginDetails.password, loginDetails.tfaCode || ''); | ||
expect(false).to.be.true; //should throw error | ||
} catch { | ||
/* no op */ | ||
} | ||
}); | ||
|
||
it('When two factor authentication property is enabled at securityDetails endpoint, then it is returned from is2FANeeded functionality', async () => { | ||
const email = crypto.randomBytes(16).toString('hex'); | ||
const securityDetails: SecurityDetails = { | ||
encryptedSalt: crypto.randomBytes(16).toString('hex'), | ||
tfaEnabled: true, | ||
}; | ||
|
||
authServiceSandbox | ||
.stub(Auth.prototype, 'securityDetails') | ||
.withArgs(email) | ||
.returns(Promise.resolve(securityDetails)); | ||
authServiceSandbox.stub(SdkManager.instance, 'getAuth').returns(Auth.prototype); | ||
|
||
const responseLogin = await AuthService.instance.is2FANeeded(email); | ||
|
||
expect(responseLogin).to.equal(securityDetails.tfaEnabled); | ||
}); | ||
|
||
it('When email is not correct when checking two factor authentication property, then an error is thrown', async () => { | ||
const email = crypto.randomBytes(16).toString('hex'); | ||
|
||
authServiceSandbox.stub(Auth.prototype, 'securityDetails').withArgs(email).rejects(); | ||
authServiceSandbox.stub(SdkManager.instance, 'getAuth').returns(Auth.prototype); | ||
|
||
try { | ||
await AuthService.instance.is2FANeeded(email); | ||
expect(false).to.be.true; //should throw error | ||
} catch { | ||
/* no op */ | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters