Skip to content

Commit

Permalink
fix: Delete secrets DB when deleting current device (#16491)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomrc authored Jan 8, 2024
1 parent 5281e0a commit ad67b9f
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 46 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@peculiar/x509": "1.9.6",
"@wireapp/avs": "9.5.13",
"@wireapp/commons": "5.2.4",
"@wireapp/core": "43.5.0",
"@wireapp/core": "43.5.1",
"@wireapp/react-ui-kit": "9.12.5",
"@wireapp/store-engine-dexie": "2.1.7",
"@wireapp/webapp-events": "0.18.3",
Expand Down
43 changes: 10 additions & 33 deletions src/script/service/CoreSingleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*
*/

import {generateSecretKey as generateKey} from '@wireapp/core/lib/secretStore/secretKeyGenerator';
import {container, singleton} from 'tsyringe';

import {Account} from '@wireapp/core';
Expand All @@ -26,54 +25,34 @@ import {supportsMLS} from 'Util/util';

import {APIClient} from './APIClientSingleton';
import {createStorageEngine, DatabaseTypes} from './StoreEngineProvider';
import {SystemCrypto, wrapSystemCrypto} from './utils/systemCryptoWrapper';

import {Config} from '../Config';
import {isE2EIEnabled} from '../E2EIdentity';

declare global {
interface Window {
systemCrypto?:
| {
encrypt: (value: Uint8Array) => Promise<Uint8Array>;
decrypt: (payload: Uint8Array) => Promise<Uint8Array>;
version: undefined;
}
| {
encrypt: (value: string) => Promise<Uint8Array>;
decrypt: (payload: Uint8Array) => Promise<string>;
version: 1;
};
systemCrypto?: SystemCrypto;
}
}

const generateSecretKey = async (storeName: string, keyId: string, keySize: number) => {
return generateKey({
keyId,
keySize,
dbName: `secrets-${storeName}`,
/*
* When in an electron context, the window.systemCrypto will be populated by the renderer process.
* We then give those crypto primitives to the key generator that will use them to encrypt secrets.
* When in a browser context, then this systemCrypto will be undefined and the key generator will then use it's internal encryption system
*/
systemCrypto: window.systemCrypto,
});
};

@singleton()
export class Core extends Account {
constructor(apiClient = container.resolve(APIClient)) {
const enableCoreCrypto = supportsMLS() || Config.getConfig().FEATURE.USE_CORE_CRYPTO;
super(apiClient, {
createStore: async storeName => {
const key = Config.getConfig().FEATURE.ENABLE_ENCRYPTION_AT_REST
? await generateSecretKey(storeName, 'db-key', 32)
: undefined;
createStore: async (storeName, key) => {
return createStorageEngine(storeName, DatabaseTypes.PERMANENT, {
key: key?.key,
key: Config.getConfig().FEATURE.ENABLE_ENCRYPTION_AT_REST ? key : undefined,
});
},

/*
* When in an electron context, the window.systemCrypto will be populated by the renderer process.
* We then give those crypto primitives to the key generator that will use them to encrypt secrets.
* When in a browser context, then this systemCrypto will be undefined and the key generator will then use it's internal encryption system
*/
systemCrypto: window.systemCrypto ? wrapSystemCrypto(window.systemCrypto) : undefined,
coreCryptoConfig: enableCoreCrypto
? {
wasmFilePath: '/min/core-crypto.wasm',
Expand All @@ -84,8 +63,6 @@ export class Core extends Account {
useE2EI: isE2EIEnabled(),
}
: undefined,

generateSecretKey,
}
: undefined,

Expand Down
85 changes: 85 additions & 0 deletions src/script/service/utils/systemCryptoWrapper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {Encoder, Decoder} from 'bazinga64';

import {SystemCrypto, wrapSystemCrypto} from './systemCryptoWrapper';

const systemCryptos = {
v0: {
encrypt: async (value: Uint8Array) => {
return value;
},
decrypt: async (value: Uint8Array) => {
return value;
},
version: undefined,
} as SystemCrypto,

v01: {
encrypt: async (value: Uint8Array) => {
return Encoder.toBase64(value).asBytes;
},
decrypt: async (value: Uint8Array) => {
return Decoder.fromBase64(Array.from(value.values())).asBytes;
},
version: undefined,
} as SystemCrypto,

v1: {
encrypt: async (value: string) => {
const encoder = new TextEncoder();
return encoder.encode(value);
},
decrypt: async (value: Uint8Array) => {
const decoder = new TextDecoder();
return decoder.decode(value);
},
version: 1,
} as SystemCrypto,
} as const;

describe('systemCryptoWrapper', () => {
it.each(Object.entries(systemCryptos))(
'generates and store a secret key encrypted using system crypto (%s)',
async (_name, systemCrypto) => {
const {encrypt, decrypt} = wrapSystemCrypto(systemCrypto);

const value = new Uint8Array([1, 2, 3, 4]);
const encrypted = await encrypt(value);
const decrypted = await decrypt(encrypted);

expect(value).toEqual(decrypted);
},
);

it.each([['v01 > v1', systemCryptos.v01, systemCryptos.v1]])(
'handles migration from old system crypto (%s)',
async (_name, crypto1, crypto2) => {
const wrap1 = wrapSystemCrypto(crypto1);
const wrap2 = wrapSystemCrypto(crypto2);

const value = new Uint8Array([1, 2, 3, 4]);
const encrypted = await wrap1.encrypt(value);
const decrypted = await wrap2.decrypt(encrypted);

expect(value).toEqual(decrypted);
},
);
});
62 changes: 62 additions & 0 deletions src/script/service/utils/systemCryptoWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {Encoder, Decoder} from 'bazinga64';

type SystemCryptoV0 = {
encrypt: (value: Uint8Array) => Promise<Uint8Array>;
decrypt: (payload: Uint8Array) => Promise<Uint8Array>;
version: undefined;
};
type SystemCryptoV1 = {
encrypt: (value: string) => Promise<Uint8Array>;
decrypt: (payload: Uint8Array) => Promise<string>;
version: 1;
};

export type SystemCrypto = SystemCryptoV0 | SystemCryptoV1;

export function wrapSystemCrypto(baseCrypto: SystemCrypto) {
const isBase64 = /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$/;
return {
encrypt: (value: Uint8Array) => {
if (baseCrypto.version === 1) {
const strValue = Encoder.toBase64(value).asString;
return (baseCrypto as SystemCryptoV1).encrypt(strValue);
}
// In previous versions of the systemCrypto (prior to February 2023), encrypt took a uint8Array
return (baseCrypto as SystemCryptoV0).encrypt(value);
},

decrypt: async (value: Uint8Array) => {
if (typeof baseCrypto.version === 'undefined') {
// In previous versions of the systemCrypto (prior to February 2023), the decrypt function returned a Uint8Array
return (baseCrypto as SystemCryptoV0).decrypt(value);
}
const decrypted = await (baseCrypto as SystemCryptoV1).decrypt(value);
if (isBase64.test(decrypted)) {
return Decoder.fromBase64(decrypted).asBytes;
}
// Between June 2022 and October 2022, the systemCrypto returned a string encoded in UTF-8
const encoder = new TextEncoder();

return encoder.encode(decrypted);
},
};
}
45 changes: 33 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4966,14 +4966,14 @@ __metadata:
languageName: node
linkType: hard

"@wireapp/api-client@npm:^26.8.1":
version: 26.8.1
resolution: "@wireapp/api-client@npm:26.8.1"
"@wireapp/api-client@npm:^26.8.2":
version: 26.8.2
resolution: "@wireapp/api-client@npm:26.8.2"
dependencies:
"@wireapp/commons": ^5.2.4
"@wireapp/priority-queue": ^2.1.4
"@wireapp/protocol-messaging": 1.44.0
axios: 1.6.3
axios: 1.6.5
axios-retry: 4.0.0
exponential-backoff: 3.1.1
http-status-codes: 2.3.0
Expand All @@ -4984,7 +4984,7 @@ __metadata:
tough-cookie: 4.1.3
ws: 8.16.0
zod: 3.22.4
checksum: dc2b560c9cdb229b0a7ed8fad1126cb5949c5e100aa217d0e25f75c3bea745f81fc774e89fa5138bc46a5464b8cf80d14509829ca10e938b6421b33549500487
checksum: c65b2de7758f4dbe720d170b0d979ac98a9d795e11273a3be553ff147b393e2593c9d420fc1d705f3f0fbce5809cac90d747a94406f5aea1b6912f01bff08567
languageName: node
linkType: hard

Expand Down Expand Up @@ -5037,19 +5037,19 @@ __metadata:
languageName: node
linkType: hard

"@wireapp/core@npm:43.5.0":
version: 43.5.0
resolution: "@wireapp/core@npm:43.5.0"
"@wireapp/core@npm:43.5.1":
version: 43.5.1
resolution: "@wireapp/core@npm:43.5.1"
dependencies:
"@wireapp/api-client": ^26.8.1
"@wireapp/api-client": ^26.8.2
"@wireapp/commons": ^5.2.4
"@wireapp/core-crypto": 1.0.0-rc.21
"@wireapp/cryptobox": 12.8.0
"@wireapp/promise-queue": ^2.2.9
"@wireapp/protocol-messaging": 1.44.0
"@wireapp/store-engine": 5.1.5
"@wireapp/store-engine-dexie": ^2.1.7
axios: 1.6.3
axios: 1.6.5
bazinga64: ^6.3.4
deepmerge-ts: 5.1.0
hash.js: 1.1.7
Expand All @@ -5059,7 +5059,7 @@ __metadata:
long: ^5.2.0
uuidjs: 4.2.13
zod: 3.22.4
checksum: af9ec4d14ca2df4bb5ced1cee5ec7efd6cf855c6ec0eb05615c7f7ca55725bcc61b65a6ccc8ae916a73818d60fd48de51418f9e25444b38041f19b5ea1aea4c4
checksum: c0c80ac0d9014453067c90338101c5492abfda058a848ff7203448fc3d0c0cc489cc66588642acf9be2e478ad2672b0c593286e24d414ca04c7d452dac83bcff
languageName: node
linkType: hard

Expand Down Expand Up @@ -5892,6 +5892,17 @@ __metadata:
languageName: node
linkType: hard

"axios@npm:1.6.5":
version: 1.6.5
resolution: "axios@npm:1.6.5"
dependencies:
follow-redirects: ^1.15.4
form-data: ^4.0.0
proxy-from-env: ^1.1.0
checksum: e28d67b2d9134cb4608c44d8068b0678cfdccc652742e619006f27264a30c7aba13b2cd19c6f1f52ae195b5232734925928fb192d5c85feea7edd2f273df206d
languageName: node
linkType: hard

"axobject-query@npm:^3.1.1":
version: 3.2.1
resolution: "axobject-query@npm:3.2.1"
Expand Down Expand Up @@ -8981,6 +8992,16 @@ __metadata:
languageName: node
linkType: hard

"follow-redirects@npm:^1.15.4":
version: 1.15.4
resolution: "follow-redirects@npm:1.15.4"
peerDependenciesMeta:
debug:
optional: true
checksum: e178d1deff8b23d5d24ec3f7a94cde6e47d74d0dc649c35fc9857041267c12ec5d44650a0c5597ef83056ada9ea6ca0c30e7c4f97dbf07d035086be9e6a5b7b6
languageName: node
linkType: hard

"for-each@npm:^0.3.3":
version: 0.3.3
resolution: "for-each@npm:0.3.3"
Expand Down Expand Up @@ -17748,7 +17769,7 @@ __metadata:
"@wireapp/avs": 9.5.13
"@wireapp/commons": 5.2.4
"@wireapp/copy-config": 2.1.13
"@wireapp/core": 43.5.0
"@wireapp/core": 43.5.1
"@wireapp/eslint-config": 3.0.4
"@wireapp/prettier-config": 0.6.3
"@wireapp/react-ui-kit": 9.12.5
Expand Down

0 comments on commit ad67b9f

Please sign in to comment.