Skip to content

Commit

Permalink
Implement lock mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikescops committed Mar 29, 2024
1 parent f0c18e8 commit 96f8b98
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/command-handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './configure';
export * from './devices';
export * from './exec';
export * from './inject';
export * from './lock';
export * from './logout';
export * from './passwords';
export * from './read';
Expand Down
29 changes: 29 additions & 0 deletions src/command-handlers/lock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as winston from 'winston';
import { deleteLocalKey } from '../modules/crypto/keychainManager';
import { connectAndPrepare } from '../modules/database';

export const runLock = async () => {
const { db, localConfiguration } = await connectAndPrepare({
autoSync: false,
shouldNotSaveMasterPasswordIfNoDeviceKeys: true,
});

// Forget the local key stored in the OS keychain because the master password and the DB are enough to retrieve the
// local key
try {
deleteLocalKey(localConfiguration.login);
} catch (error) {
// Errors are ignored because the OS keychain may be unreachable
let errorMessage = 'unknown error';
if (error instanceof Error) {
errorMessage = error.message;
}
winston.warn(`Unable to lock the vault: ${errorMessage}`);
}

db.prepare('UPDATE device SET masterPasswordEncrypted = ? WHERE login = ?')
.bind(null, localConfiguration.login)
.run();

db.close();
};
6 changes: 6 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
runSync,
runPassword,
runSecureNote,
runLock,
runLogout,
runRead,
runInject,
Expand Down Expand Up @@ -112,6 +113,11 @@ export const rootCommands = (params: { program: Command }) => {
.description('Backup your local vault (will use the current directory by default)')
.action(runBackup);

program
.command('lock')
.description('Lock the vault, next commands will request the master password to unlock it)')
.action(runLock);

program
.command('logout')
.option('--ignore-revocation', "Device credentials won't be revoked on Dashlane's servers")
Expand Down
12 changes: 11 additions & 1 deletion src/modules/crypto/keychainManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ const getLocalConfigurationWithoutDB = async (
};

const getLocalConfigurationWithoutKeychain = async (
db: Database,
login: string,
deviceConfiguration: DeviceConfiguration
): Promise<LocalConfiguration> => {
Expand Down Expand Up @@ -204,6 +205,15 @@ const getLocalConfigurationWithoutKeychain = async (
winston.warn(`Unable to reach OS keychain because of error: "${errorMessage}". \
Install it or disable its usage via \`dcli configure save-master-password false\`.`);
});

if (!deviceConfiguration.masterPasswordEncrypted) {
// Set encrypted master password in the DB
const masterPasswordEncrypted = encryptAesCbcHmac256(localKey, Buffer.from(masterPassword));

db.prepare('UPDATE device SET masterPasswordEncrypted = ? WHERE login = ?')
.bind(masterPasswordEncrypted, login)
.run();
}
}

return {
Expand Down Expand Up @@ -296,7 +306,7 @@ export const getLocalConfiguration = async (
!deviceConfiguration.masterPasswordEncrypted ||
!(localKey = getLocalKey(login))
) {
return getLocalConfigurationWithoutKeychain(login, deviceConfiguration);
return getLocalConfigurationWithoutKeychain(db, login, deviceConfiguration);
}

// Otherwise, the local key can be used to decrypt the device secret key and the master password in the DB
Expand Down

0 comments on commit 96f8b98

Please sign in to comment.