Upload/Download certs zip from Flipper

Summary:
This diff adds upload and download logic for certs. It makes changes on both Flipper Client and Desktop side. With this we enable cert exchange through WWW.

Next Diffs:

1) Add Flipper state in cert provider for more debug data
2) Tests

Reviewed By: jknoxville

Differential Revision: D23092706

fbshipit-source-id: e576253606b64b62848b70203db7e09a3bd77fd9
This commit is contained in:
Pritesh Nandgaonkar
2020-08-17 06:50:15 -07:00
committed by Facebook GitHub Bot
parent 6e0b407063
commit f626925443
9 changed files with 400 additions and 46 deletions

View File

@@ -14,10 +14,13 @@
"@emotion/core": "^10.0.22", "@emotion/core": "^10.0.22",
"@emotion/styled": "^10.0.23", "@emotion/styled": "^10.0.23",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@types/archiver": "^3.1.0",
"@types/uuid": "^8.0.1",
"JSONStream": "^1.3.1", "JSONStream": "^1.3.1",
"adbkit": "^2.11.1", "adbkit": "^2.11.1",
"adbkit-logcat": "^2.0.1", "adbkit-logcat": "^2.0.1",
"algoliasearch": "^4.0.0", "algoliasearch": "^4.0.0",
"archiver": "^5.0.0",
"async-mutex": "^0.1.3", "async-mutex": "^0.1.3",
"axios": "^0.19.2", "axios": "^0.19.2",
"deep-equal": "^2.0.1", "deep-equal": "^2.0.1",
@@ -61,7 +64,7 @@
"semver": "^7.3.2", "semver": "^7.3.2",
"string-natural-compare": "^3.0.0", "string-natural-compare": "^3.0.0",
"tmp": "^0.2.1", "tmp": "^0.2.1",
"uuid": "^8.1.0", "uuid": "^8.3.0",
"which": "^2.0.1", "which": "^2.0.1",
"ws": "^7.3.0", "ws": "^7.3.0",
"xdg-basedir": "^4.0.0" "xdg-basedir": "^4.0.0"

View File

@@ -72,7 +72,12 @@ function getStore(selectedPlugins: Array<string>) {
const client = new Client( const client = new Client(
clientId, clientId,
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial',
},
null, null,
logger, logger,
// @ts-ignore // @ts-ignore

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import BaseDevice, {OS} from './BaseDevice';
export default class ClientDevice extends BaseDevice {
constructor(serial: string, title: string, os: OS) {
super(serial, 'emulator', title, os);
this.devicePlugins = [];
}
}

View File

@@ -37,6 +37,7 @@ import querystring from 'querystring';
import {IncomingMessage} from 'http'; import {IncomingMessage} from 'http';
import ws from 'ws'; import ws from 'ws';
import {initSelfInpector} from './utils/self-inspection/selfInspectionUtils'; import {initSelfInpector} from './utils/self-inspection/selfInspectionUtils';
import ClientDevice from './devices/ClientDevice';
type ClientInfo = { type ClientInfo = {
connection: FlipperClientConnection<any, any> | null | undefined; connection: FlipperClientConnection<any, any> | null | undefined;
@@ -51,9 +52,9 @@ type ClientCsrQuery = {
function transformCertificateExchangeMediumToType( function transformCertificateExchangeMediumToType(
medium: number | undefined, medium: number | undefined,
): CertificateExchangeMedium { ): CertificateExchangeMedium {
if (medium === 1) { if (medium == 1) {
return 'FS_ACCESS'; return 'FS_ACCESS';
} else if (medium === 2) { } else if (medium == 2) {
return 'WWW'; return 'WWW';
} else { } else {
return 'FS_ACCESS'; return 'FS_ACCESS';
@@ -226,6 +227,7 @@ class Server extends EventEmitter {
device: 'device', device: 'device',
device_id: deviceId, device_id: deviceId,
sdk_version: 1, sdk_version: 1,
medium: 'FS_ACCESS',
}, },
{}, {},
).then((c) => (resolvedClient = c)); ).then((c) => (resolvedClient = c));
@@ -277,14 +279,43 @@ class Server extends EventEmitter {
if (!payload.data) { if (!payload.data) {
return {}; return {};
} }
const clientData: ClientQuery & ClientCsrQuery = JSON.parse(payload.data); const clientData: ClientQuery &
ClientCsrQuery & {medium: number | undefined} = JSON.parse(payload.data);
this.connectionTracker.logConnectionAttempt(clientData); this.connectionTracker.logConnectionAttempt(clientData);
const {app, os, device, device_id, sdk_version, csr, csr_path} = clientData; const {
app,
os,
device,
device_id,
sdk_version,
csr,
csr_path,
medium,
} = clientData;
const transformedMedium = transformCertificateExchangeMediumToType(medium);
const duplicateDevices = this.store
.getState()
.connections.devices.filter((device) => device.serial === device_id);
// When user switches from WWW to FS_ACCESS, we reset the certs folder, but we don't do it other way around. Thus when user switches to WWW from FS_ACCESS and if certs arepresent then the device id sent by Flipper SDK is the original one and in that case we need not create a device.
if (transformedMedium === 'WWW' && duplicateDevices.length == 0) {
// TODO unregister previous ClientDevice's for a particular device
this.store.dispatch({
type: 'REGISTER_DEVICE',
payload: new ClientDevice(device_id, app, os),
});
}
const client: Promise<Client> = this.addConnection( const client: Promise<Client> = this.addConnection(
socket, socket,
{app, os, device, device_id, sdk_version}, {
app,
os,
device,
device_id,
sdk_version,
medium: transformedMedium,
},
{csr, csr_path}, {csr, csr_path},
).then((client) => { ).then((client) => {
return (resolvedClient = client); return (resolvedClient = client);
@@ -364,6 +395,7 @@ class Server extends EventEmitter {
destination: string; destination: string;
medium: number | undefined; // OSS's older Client SDK might not send medium information. This is not an issue for internal FB users, as Flipper release is insync with client SDK through launcher. medium: number | undefined; // OSS's older Client SDK might not send medium information. This is not an issue for internal FB users, as Flipper release is insync with client SDK through launcher.
} = rawData; } = rawData;
if (json.method === 'signCertificate') { if (json.method === 'signCertificate') {
console.debug('CSR received from device', 'server'); console.debug('CSR received from device', 'server');
@@ -461,7 +493,7 @@ class Server extends EventEmitter {
async addConnection( async addConnection(
conn: FlipperClientConnection<any, any>, conn: FlipperClientConnection<any, any>,
query: ClientQuery, query: ClientQuery & {medium: CertificateExchangeMedium},
csrQuery: ClientCsrQuery, csrQuery: ClientCsrQuery,
): Promise<Client> { ): Promise<Client> {
invariant(query, 'expected query'); invariant(query, 'expected query');
@@ -469,7 +501,7 @@ class Server extends EventEmitter {
// try to get id by comparing giving `csr` to file from `csr_path` // try to get id by comparing giving `csr` to file from `csr_path`
// otherwise, use given device_id // otherwise, use given device_id
const {csr_path, csr} = csrQuery; const {csr_path, csr} = csrQuery;
return (csr_path && csr return (csr_path && csr && query.medium === 'FS_ACCESS'
? this.certificateProvider.extractAppNameFromCSR(csr).then((appName) => { ? this.certificateProvider.extractAppNameFromCSR(csr).then((appName) => {
return this.certificateProvider.getTargetDeviceId( return this.certificateProvider.getTargetDeviceId(
query.os, query.os,

View File

@@ -8,9 +8,12 @@
*/ */
import {Logger} from '../fb-interfaces/Logger'; import {Logger} from '../fb-interfaces/Logger';
import {internGraphPOSTAPIRequest} from '../fb-stubs/user';
import Server from '../server'; import Server from '../server';
import {promisify} from 'util'; import {promisify} from 'util';
import fs from 'fs'; import fs from 'fs';
import fsExtra from 'fs-extra';
import { import {
openssl, openssl,
isInstalled as opensslInstalled, isInstalled as opensslInstalled,
@@ -24,6 +27,9 @@ import * as androidUtil from './androidContainerUtility';
import os from 'os'; import os from 'os';
import {Client as ADBClient} from 'adbkit'; import {Client as ADBClient} from 'adbkit';
import {Store} from '../reducers/index'; import {Store} from '../reducers/index';
import archiver from 'archiver';
import promiseTimeout from '../utils/promiseTimeout';
import {v4 as uuid} from 'uuid';
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW'; export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW';
@@ -94,47 +100,90 @@ export default class CertificateProvider {
this.server = server; this.server = server;
} }
processCertificateSigningRequest( uploadFiles = async (zipPath: string, deviceID: string): Promise<void> => {
const buff = await fsExtra.readFile(zipPath);
const file = new File([buff], 'certs.zip');
return reportPlatformFailures(
promiseTimeout(
5 * 60 * 1000,
internGraphPOSTAPIRequest('flipper/certificates', {
certificate_zip: file,
device_id: deviceID,
}),
'Timed out uploading Flipper export.',
),
'uploadCertificates',
).catch((e) => console.error(`Failed to upload certificates due to ${e}`));
};
async processCertificateSigningRequest(
unsanitizedCsr: string, unsanitizedCsr: string,
os: string, os: string,
appDirectory: string, appDirectory: string,
medium: CertificateExchangeMedium, medium: CertificateExchangeMedium,
): Promise<{deviceId: string}> { ): Promise<{deviceId: string}> {
// TODO: Add implementations for each of these conditions
if (medium === 'FS_ACCESS') {
// Use IDB for cert exchange
} else if (medium === 'WWW') {
// Use WWWW
}
const csr = this.santitizeString(unsanitizedCsr); const csr = this.santitizeString(unsanitizedCsr);
if (csr === '') { if (csr === '') {
return Promise.reject(new Error(`Received empty CSR from ${os} device`)); return Promise.reject(new Error(`Received empty CSR from ${os} device`));
} }
this.ensureOpenSSLIsAvailable(); this.ensureOpenSSLIsAvailable();
const rootFolder = await promisify(tmp.dir)();
const certFolder = rootFolder + '/FlipperCerts/';
const certsZipPath = rootFolder + '/certs.zip';
return this.certificateSetup return this.certificateSetup
.then((_) => this.getCACertificate()) .then((_) => this.getCACertificate())
.then((caCert) => .then((caCert) =>
this.deployFileToMobileApp( this.deployOrStageFileForMobileApp(
appDirectory, appDirectory,
deviceCAcertFile, deviceCAcertFile,
caCert, caCert,
csr, csr,
os, os,
medium,
certFolder,
), ),
) )
.then((_) => this.generateClientCertificate(csr)) .then((_) => this.generateClientCertificate(csr))
.then((clientCert) => .then((clientCert) =>
this.deployFileToMobileApp( this.deployOrStageFileForMobileApp(
appDirectory, appDirectory,
deviceClientCertFile, deviceClientCertFile,
clientCert, clientCert,
csr, csr,
os, os,
medium,
certFolder,
), ),
) )
.then((_) => this.extractAppNameFromCSR(csr)) .then((_) => {
.then((appName) => this.getTargetDeviceId(os, appName, appDirectory, csr)) return this.extractAppNameFromCSR(csr);
.then((deviceId) => { })
.then((appName) => {
if (medium === 'FS_ACCESS') {
return this.getTargetDeviceId(os, appName, appDirectory, csr);
} else {
return uuid();
}
})
.then(async (deviceId) => {
if (medium === 'WWW') {
const zipPromise = new Promise((resolve, reject) => {
const output = fs.createWriteStream(certsZipPath);
const archive = archiver('zip', {
zlib: {level: 9}, // Sets the compression level.
});
archive.directory(certFolder, false);
output.on('close', function () {
resolve(certsZipPath);
});
archive.on('warning', reject);
archive.on('error', reject);
archive.pipe(output);
archive.finalize();
});
await zipPromise;
await this.uploadFiles(certsZipPath, deviceId);
}
return { return {
deviceId, deviceId,
}; };
@@ -201,15 +250,31 @@ export default class CertificateProvider {
throw new Error("Path didn't match expected pattern: " + absolutePath); throw new Error("Path didn't match expected pattern: " + absolutePath);
} }
deployFileToMobileApp( async deployOrStageFileForMobileApp(
destination: string, destination: string,
filename: string, filename: string,
contents: string, contents: string,
csr: string, csr: string,
os: string, os: string,
medium: CertificateExchangeMedium,
certFolder: string,
): Promise<void> { ): Promise<void> {
const appNamePromise = this.extractAppNameFromCSR(csr); const appNamePromise = this.extractAppNameFromCSR(csr);
if (medium === 'WWW') {
const certPathExists = await fsExtra.pathExists(certFolder);
if (!certPathExists) {
await fsExtra.mkdir(certFolder);
}
return promisify(fs.writeFile)(certFolder + filename, contents).catch(
(e) => {
throw new Error(
`Failed to write ${filename} to temporary folder. Error: ${e}`,
);
},
);
}
if (os === 'Android') { if (os === 'Android') {
const deviceIdPromise = appNamePromise.then((app) => const deviceIdPromise = appNamePromise.then((app) =>
this.getTargetAndroidDeviceId(app, destination, csr), this.getTargetAndroidDeviceId(app, destination, csr),
@@ -237,9 +302,9 @@ export default class CertificateProvider {
destination, destination,
); );
return appNamePromise return appNamePromise
.then((appName) => .then((appName) => {
this.getTargetiOSDeviceId(appName, destination, csr), return this.getTargetiOSDeviceId(appName, destination, csr);
) })
.then((udid) => { .then((udid) => {
return appNamePromise.then((appName) => return appNamePromise.then((appName) =>
this.pushFileToiOSDevice( this.pushFileToiOSDevice(

View File

@@ -716,7 +716,12 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS'); const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
const client1 = new Client( const client1 = new Client(
generateClientIdentifier(device1, 'app'), generateClientIdentifier(device1, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -724,7 +729,12 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
); );
const client2 = new Client( const client2 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
{app: 'app2', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app2',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -732,7 +742,12 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
); );
const client3 = new Client( const client3 = new Client(
generateClientIdentifier(device1, 'app3'), generateClientIdentifier(device1, 'app3'),
{app: 'app3', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app3',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -777,7 +792,12 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS'); const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
const client1 = new Client( const client1 = new Client(
generateClientIdentifier(device1, 'app'), generateClientIdentifier(device1, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -785,7 +805,12 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
); );
const client2 = new Client( const client2 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
{app: 'app2', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app2',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -811,7 +836,12 @@ test('test determinePluginsToProcess for multiple clients on same device', async
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS'); const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
const client1 = new Client( const client1 = new Client(
generateClientIdentifier(device1, 'app'), generateClientIdentifier(device1, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -819,7 +849,12 @@ test('test determinePluginsToProcess for multiple clients on same device', async
); );
const client2 = new Client( const client2 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
{app: 'app2', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app2',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -852,7 +887,12 @@ test('test determinePluginsToProcess for multiple clients on different device',
const device2 = new BaseDevice('serial2', 'emulator', 'TestiPhone', 'iOS'); const device2 = new BaseDevice('serial2', 'emulator', 'TestiPhone', 'iOS');
const client1Device1 = new Client( const client1Device1 = new Client(
generateClientIdentifier(device1, 'app'), generateClientIdentifier(device1, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -860,7 +900,12 @@ test('test determinePluginsToProcess for multiple clients on different device',
); );
const client2Device1 = new Client( const client2Device1 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
{app: 'app1', os: 'iOS', device: 'TestiPhone', device_id: 'serial1'}, {
app: 'app1',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial1',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -868,7 +913,12 @@ test('test determinePluginsToProcess for multiple clients on different device',
); );
const client1Device2 = new Client( const client1Device2 = new Client(
generateClientIdentifier(device2, 'app'), generateClientIdentifier(device2, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial2'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial2',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -876,7 +926,12 @@ test('test determinePluginsToProcess for multiple clients on different device',
); );
const client2Device2 = new Client( const client2Device2 = new Client(
generateClientIdentifier(device2, 'app2'), generateClientIdentifier(device2, 'app2'),
{app: 'app1', os: 'iOS', device: 'TestiPhone', device_id: 'serial2'}, {
app: 'app1',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial2',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -934,7 +989,12 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
const mockStore = configureStore<State, {}>([])(); const mockStore = configureStore<State, {}>([])();
const client = new Client( const client = new Client(
generateClientIdentifier(selectedDevice, 'app'), generateClientIdentifier(selectedDevice, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial',
},
null, null,
logger, logger,
mockStore, mockStore,
@@ -942,7 +1002,12 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
); );
const archivedClient = new Client( const archivedClient = new Client(
generateClientIdentifier(archivedDevice, 'app'), generateClientIdentifier(archivedDevice, 'app'),
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial-archived'}, {
app: 'app',
os: 'iOS',
device: 'TestiPhone',
device_id: 'serial-archived',
},
null, null,
logger, logger,
mockStore, mockStore,

View File

@@ -1882,6 +1882,13 @@
resolved "https://registry.yarnpkg.com/@types/algoliasearch/-/algoliasearch-3.34.5.tgz#c40e346a6c5526f9b27af7863117d1200456e7d2" resolved "https://registry.yarnpkg.com/@types/algoliasearch/-/algoliasearch-3.34.5.tgz#c40e346a6c5526f9b27af7863117d1200456e7d2"
integrity sha512-JS+5KT9SfwzGIkoCsj7EyYHzhrsagj321BEPH3oroHYnuKUfPVoei58qLsBPo9RRa27Vb79WJssSSPBicnaAng== integrity sha512-JS+5KT9SfwzGIkoCsj7EyYHzhrsagj321BEPH3oroHYnuKUfPVoei58qLsBPo9RRa27Vb79WJssSSPBicnaAng==
"@types/archiver@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-3.1.0.tgz#0d5bd922ba5cf06e137cd6793db7942439b1805e"
integrity sha512-nTvHwgWONL+iXG+9CX+gnQ/tTOV+qucAjwpXqeUn4OCRMxP42T29FFP/7XaOo0EqqO3TlENhObeZEe7RUJAriw==
dependencies:
"@types/glob" "*"
"@types/aria-query@^4.2.0": "@types/aria-query@^4.2.0":
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0"
@@ -2049,6 +2056,14 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/glob@*":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
dependencies:
"@types/minimatch" "*"
"@types/node" "*"
"@types/glob@^7.1.1": "@types/glob@^7.1.1":
version "7.1.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
@@ -2467,6 +2482,11 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
"@types/uuid@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.1.tgz#42958a1a880640b139eea97a1640e1a3f61016d2"
integrity sha512-2kE8rEFgJpbBAPw5JghccEevQb0XVU0tewF/8h7wPQTeCtoJ6h8qmBIwuzUVm2MutmzC/cpCkwxudixoNYDp1A==
"@types/webdriverio@^4.8.0": "@types/webdriverio@^4.8.0":
version "4.13.3" version "4.13.3"
resolved "https://registry.yarnpkg.com/@types/webdriverio/-/webdriverio-4.13.3.tgz#c1571c4e62724135c0b11e7d7e36b07af5168856" resolved "https://registry.yarnpkg.com/@types/webdriverio/-/webdriverio-4.13.3.tgz#c1571c4e62724135c0b11e7d7e36b07af5168856"
@@ -2919,6 +2939,35 @@ archiver-utils@^1.3.0:
normalize-path "^2.0.0" normalize-path "^2.0.0"
readable-stream "^2.0.0" readable-stream "^2.0.0"
archiver-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
dependencies:
glob "^7.1.4"
graceful-fs "^4.2.0"
lazystream "^1.0.0"
lodash.defaults "^4.2.0"
lodash.difference "^4.5.0"
lodash.flatten "^4.4.0"
lodash.isplainobject "^4.0.6"
lodash.union "^4.6.0"
normalize-path "^3.0.0"
readable-stream "^2.0.0"
archiver@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.0.0.tgz#b1e7dc075a4e18e0aa59afdd7c3e5f3d3321cbeb"
integrity sha512-AEWhJz6Yi6hWtN1Sqy/H4sZo/lLMJ/NftXxGaDy/TnOMmmjsRaZc/Ts+U4BsPoBQkuunTN6t8hk7iU9A+HBxLw==
dependencies:
archiver-utils "^2.1.0"
async "^3.2.0"
buffer-crc32 "^0.2.1"
readable-stream "^3.6.0"
readdir-glob "^1.0.0"
tar-stream "^2.1.2"
zip-stream "^4.0.0"
archiver@~2.1.0: archiver@~2.1.0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-2.1.1.tgz#ff662b4a78201494a3ee544d3a33fe7496509ebc" resolved "https://registry.yarnpkg.com/archiver/-/archiver-2.1.1.tgz#ff662b4a78201494a3ee544d3a33fe7496509ebc"
@@ -3074,6 +3123,11 @@ async@^2.0.0, async@^2.4.0:
dependencies: dependencies:
lodash "^4.17.14" lodash "^4.17.14"
async@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
async@~0.2.9: async@~0.2.9:
version "0.2.10" version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
@@ -3540,7 +3594,7 @@ buffer-alloc@^1.2.0:
buffer-alloc-unsafe "^1.1.0" buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0" buffer-fill "^1.0.0"
buffer-crc32@^0.2.1, buffer-crc32@~0.2.3: buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
version "0.2.13" version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
@@ -4025,6 +4079,16 @@ compress-commons@^1.2.0:
normalize-path "^2.0.0" normalize-path "^2.0.0"
readable-stream "^2.0.0" readable-stream "^2.0.0"
compress-commons@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.0.1.tgz#c5fa908a791a0c71329fba211d73cd2a32005ea8"
integrity sha512-xZm9o6iikekkI0GnXCmAl3LQGZj5TBDj0zLowsqi7tJtEa3FMGSEcHcqrSJIrOAk1UG/NBbDn/F1q+MG/p/EsA==
dependencies:
buffer-crc32 "^0.2.13"
crc32-stream "^4.0.0"
normalize-path "^3.0.0"
readable-stream "^3.6.0"
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -4171,6 +4235,14 @@ crc32-stream@^2.0.0:
crc "^3.4.4" crc "^3.4.4"
readable-stream "^2.0.0" readable-stream "^2.0.0"
crc32-stream@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.0.tgz#05b7ca047d831e98c215538666f372b756d91893"
integrity sha512-tyMw2IeUX6t9jhgXI6um0eKfWq4EIDpfv5m7GX4Jzp7eVelQ360xd8EPXJhp2mHwLQIkqlnMLjzqSZI3a+0wRw==
dependencies:
crc "^3.4.4"
readable-stream "^3.4.0"
crc@^3.4.4: crc@^3.4.4:
version "3.8.0" version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
@@ -8212,6 +8284,21 @@ lodash.debounce@4.0.8, lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
lodash.difference@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=
lodash.flatten@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
lodash.isplainobject@^4.0.6: lodash.isplainobject@^4.0.6:
version "4.0.6" version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
@@ -8247,6 +8334,11 @@ lodash.throttle@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lodash.union@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
lodash@^4.0.1, lodash@^4.16.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.12, lodash@~4.17.4: lodash@^4.0.1, lodash@^4.16.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.12, lodash@~4.17.4:
version "4.17.19" version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
@@ -10107,7 +10199,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.1.4, readable
string_decoder "~1.1.1" string_decoder "~1.1.1"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-stream@^3.1.1, readable-stream@^3.4.0: readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -10116,6 +10208,13 @@ readable-stream@^3.1.1, readable-stream@^3.4.0:
string_decoder "^1.1.1" string_decoder "^1.1.1"
util-deprecate "^1.0.1" util-deprecate "^1.0.1"
readdir-glob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.0.0.tgz#a495436934bbe57be6a68039d16e8946621eb8c5"
integrity sha512-km0DIcwQVZ1ZUhXhMWpF74/Wm5aFEd5/jDiVWF1Hkw2myPQovG8vCQ8+FQO2KXE9npQQvCnAMZhhWuUee4WcCQ==
dependencies:
minimatch "^3.0.4"
realpath-native@^2.0.0: realpath-native@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866"
@@ -11485,6 +11584,17 @@ tar-stream@^2.0.0:
inherits "^2.0.3" inherits "^2.0.3"
readable-stream "^3.1.1" readable-stream "^3.1.1"
tar-stream@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41"
integrity sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==
dependencies:
bl "^4.0.1"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@^6.0.1: tar@^6.0.1:
version "6.0.2" version "6.0.2"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39"
@@ -12141,6 +12251,11 @@ uuid@^8.1.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
uuid@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea"
integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==
v8-compile-cache@^2.0.3: v8-compile-cache@^2.0.3:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
@@ -12613,3 +12728,12 @@ zip-stream@^1.2.0:
compress-commons "^1.2.0" compress-commons "^1.2.0"
lodash "^4.8.0" lodash "^4.8.0"
readable-stream "^2.0.0" readable-stream "^2.0.0"
zip-stream@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.0.2.tgz#3a20f1bd7729c2b59fd4efa04df5eb7a5a217d2e"
integrity sha512-TGxB2g+1ur6MHkvM644DuZr8Uzyz0k0OYWtS3YlpfWBEmK4woaC2t3+pozEL3dBfIPmpgmClR5B2QRcMgGt22g==
dependencies:
archiver-utils "^2.1.0"
compress-commons "^4.0.0"
readable-stream "^3.6.0"

View File

@@ -28,10 +28,22 @@ class FlipperCertificateProvider {
const std::string& path, const std::string& path,
const std::string& deviceID) = 0; const std::string& deviceID) = 0;
/**
* Sets certificate exchange medium
*/
virtual void setCertificateExchangeMedium( virtual void setCertificateExchangeMedium(
const FlipperCertificateExchangeMedium medium) = 0; const FlipperCertificateExchangeMedium medium) = 0;
/**
* Gets certificate exchange medium
*/
virtual FlipperCertificateExchangeMedium getCertificateExchangeMedium() = 0; virtual FlipperCertificateExchangeMedium getCertificateExchangeMedium() = 0;
/**
* This lets the Client know if it should reset the connection folder when
* `stop` is called.
*/
virtual bool shouldResetCertificateFolder() = 0;
}; };
} // namespace flipper } // namespace flipper

View File

@@ -235,12 +235,15 @@ bool FlipperConnectionManagerImpl::connectSecurely() {
if (deviceId.compare("unknown")) { if (deviceId.compare("unknown")) {
loadingDeviceId->complete(); loadingDeviceId->complete();
} }
int medium = certProvider_ != nullptr
? certProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS;
parameters.payload = rsocket::Payload(folly::toJson(folly::dynamic::object( parameters.payload = rsocket::Payload(folly::toJson(folly::dynamic::object(
"csr", contextStore_->getCertificateSigningRequest().c_str())( "csr", contextStore_->getCertificateSigningRequest().c_str())(
"csr_path", contextStore_->getCertificateDirectoryPath().c_str())( "csr_path", contextStore_->getCertificateDirectoryPath().c_str())(
"os", deviceData_.os)("device", deviceData_.device)( "os", deviceData_.os)("device", deviceData_.device)(
"device_id", deviceId)("app", deviceData_.app)( "device_id", deviceId)("app", deviceData_.app)("medium", medium)(
"sdk_version", sdkVersion))); "sdk_version", sdkVersion)));
address.setFromHostPort(deviceData_.host, securePort); address.setFromHostPort(deviceData_.host, securePort);
@@ -293,6 +296,9 @@ void FlipperConnectionManagerImpl::reconnect() {
} }
void FlipperConnectionManagerImpl::stop() { void FlipperConnectionManagerImpl::stop() {
if (certProvider_ && certProvider_->shouldResetCertificateFolder()) {
contextStore_->resetState();
}
if (!isStarted_) { if (!isStarted_) {
log("Not started"); log("Not started");
return; return;
@@ -353,10 +359,13 @@ void FlipperConnectionManagerImpl::requestSignedCertFromFlipper() {
auto generatingCSR = flipperState_->start("Generate CSR"); auto generatingCSR = flipperState_->start("Generate CSR");
std::string csr = contextStore_->getCertificateSigningRequest(); std::string csr = contextStore_->getCertificateSigningRequest();
generatingCSR->complete(); generatingCSR->complete();
int medium = certProvider_ != nullptr
? certProvider_->getCertificateExchangeMedium()
: FlipperCertificateExchangeMedium::FS_ACCESS;
folly::dynamic message = folly::dynamic message =
folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())( folly::dynamic::object("method", "signCertificate")("csr", csr.c_str())(
"destination", contextStore_->getCertificateDirectoryPath().c_str()); "destination", contextStore_->getCertificateDirectoryPath().c_str())(
"medium", medium);
auto gettingCert = flipperState_->start("Getting cert from desktop"); auto gettingCert = flipperState_->start("Getting cert from desktop");
flipperEventBase_->add([this, message, gettingCert]() { flipperEventBase_->add([this, message, gettingCert]() {
@@ -369,10 +378,32 @@ void FlipperConnectionManagerImpl::requestSignedCertFromFlipper() {
folly::dynamic config = folly::parseJson(response); folly::dynamic config = folly::parseJson(response);
contextStore_->storeConnectionConfig(config); contextStore_->storeConnectionConfig(config);
} }
gettingCert->complete(); if (certProvider_) {
auto gettingCertFromProvider =
flipperState_->start("Getting cert from Cert Provider");
try {
// Certificates should be present in app's sandbox after it is
// returned. The reason we can't have a completion block here
// is because if the certs are not present after it returns
// then the flipper tries to reconnect on insecured channel
// and recreates the app.csr. By the time completion block is
// called the DeviceCA cert doesn't match app's csr and it
// throws an SSL error.
certProvider_->getCertificates(
contextStore_->getCertificateDirectoryPath(),
contextStore_->getDeviceId());
gettingCertFromProvider->complete();
} catch (std::exception& e) {
gettingCertFromProvider->fail(e.what());
gettingCert->fail(e.what());
} catch (...) {
gettingCertFromProvider->fail("Exception from certProvider");
gettingCert->fail("Exception from certProvider");
}
}
log("Certificate exchange complete."); log("Certificate exchange complete.");
// TODO: Use Certificate provider get Certificates gettingCert->complete();
// `certProvider_->getCertificates("path", "device");`
// Disconnect after message sending is complete. // Disconnect after message sending is complete.
// This will trigger a reconnect which should use the secure // This will trigger a reconnect which should use the secure