diff --git a/setup-wizard/src/app/pages/product-key/product-key.page.ts b/setup-wizard/src/app/pages/product-key/product-key.page.ts index 1f9c69dc8..6c7de9ef2 100644 --- a/setup-wizard/src/app/pages/product-key/product-key.page.ts +++ b/setup-wizard/src/app/pages/product-key/product-key.page.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core' import { iosTransitionAnimation, LoadingController, NavController } from '@ionic/angular' import { ApiService } from 'src/app/services/api/api.service' -import { AES_CTR, decodeUtf8, encodeUtf8 } from 'src/app/services/api/http.service' +import { HttpService } from 'src/app/services/api/http.service' import { StateService } from 'src/app/services/state.service' @Component({ @@ -18,15 +18,10 @@ export class ProductKeyPage { private stateService: StateService, private apiService: ApiService, private loadingCtrl: LoadingController, + private httpService: HttpService ) {} async submit () { - - const ret = await AES_CTR.encryptPbkdf2(this.productKey, encodeUtf8('hello world')) - const arr = await AES_CTR.decryptPbkdf2(this.productKey, ret) - - console.log(decodeUtf8(arr)) - if(!this.productKey) return this.error = "Must enter product key" const loader = await this.loadingCtrl.create({ @@ -36,7 +31,7 @@ export class ProductKeyPage { try { const state = await this.apiService.verifyProductKey(this.productKey) - this.stateService.productKey = this.productKey + this.httpService.productKey = this.productKey if(state['is-recovering']) { await this.navCtrl.navigateForward(`/loading`, { animationDirection: 'forward', animation: iosTransitionAnimation }) } else if (!!state['tor-address']) { @@ -47,6 +42,7 @@ export class ProductKeyPage { } } catch (e) { this.error = e.message + this.httpService.productKey = undefined } finally { loader.dismiss() } diff --git a/setup-wizard/src/app/services/api/api.service.ts b/setup-wizard/src/app/services/api/api.service.ts index fff7247fd..29a22002d 100644 --- a/setup-wizard/src/app/services/api/api.service.ts +++ b/setup-wizard/src/app/services/api/api.service.ts @@ -3,24 +3,33 @@ import { Subject } from 'rxjs' export abstract class ApiService { protected error$: Subject = new Subject(); watchError$ = this.error$.asObservable(); - abstract verifyProductKey (key: string): Promise<{ "is-recovering": boolean, "tor-address": string }>; + abstract verifyProductKey (key: string): Promise; abstract getEmbassyDrives (): Promise; abstract getRecoveryDrives (): Promise; - abstract getDataTransferProgress (): Promise; + abstract getDataTransferProgress (): Promise; abstract verifyRecoveryPassword (logicalname: string, password: string): Promise; abstract setupEmbassy (setupInfo: { embassyLogicalname: string, embassyPassword: string recoveryLogicalname?: string, recoveryPassword?: string - }): Promise<{ "tor-address": string }> + }): Promise } -export interface TransferProgress { +export interface VerifyProductKeyRes { + "is-recovering": boolean + "tor-address": string +} + +export interface TransferProgressRes { 'bytes-transfered': number; 'total-bytes': number; } +export interface SetupEmbassyRes { + "tor-address": string +} + export interface EmbassyDrive { logicalname: string; labels: string[]; diff --git a/setup-wizard/src/app/services/api/http.service.ts b/setup-wizard/src/app/services/api/http.service.ts index 01ae7bb27..acafea7fe 100644 --- a/setup-wizard/src/app/services/api/http.service.ts +++ b/setup-wizard/src/app/services/api/http.service.ts @@ -9,6 +9,7 @@ import { map, take } from 'rxjs/operators' export class HttpService { private unauthorizedApiResponse$ = new Subject() fullUrl: string + productKey: string constructor ( private readonly http: HttpClient, @@ -21,11 +22,11 @@ export class HttpService { return this.unauthorizedApiResponse$.asObservable() } - async rpcRequest (rpcOpts: RPCOptions): Promise { - rpcOpts.params = rpcOpts.params || { } + async rpcRequest (body: RPCOptions): Promise { + const httpOpts = { method: Method.POST, - body: rpcOpts, + body, url: `this.fullUrl`, } @@ -39,45 +40,35 @@ export class HttpService { if (isRpcSuccess(res)) return res.result } - async httpRequest (httpOpts: HttpOptions): Promise { - if (httpOpts.withCredentials !== false) { - httpOpts.withCredentials = true - } + async httpRequest (httpOpts: { + body: RPCOptions; + url: string; + }): Promise { const urlIsRelative = httpOpts.url.startsWith('/') const url = urlIsRelative ? this.fullUrl + httpOpts.url : httpOpts.url - Object.keys(httpOpts.params || { }).forEach(key => { - if (httpOpts.params[key] === undefined) { - delete httpOpts.params[key] - } - }) - const options = { - responseType: httpOpts.responseType || 'json', - body: httpOpts.body, + responseType: 'arraybuffer', + body: await AES_CTR.encryptPbkdf2( 'asdf', encodeUtf8( JSON.stringify(httpOpts.body))), observe: 'events', reportProgress: false, - withCredentials: httpOpts.withCredentials, - headers: httpOpts.headers, - params: httpOpts.params, - timeout: httpOpts.timeout, + headers: { + 'Content-Encoding': 'aesctr256', + 'Content-Type': 'application/json' + }, + // one minute + timeout: 60000, } as any - let req: Observable<{ body: T }> - switch (httpOpts.method) { - case Method.GET: req = this.http.get(url, options) as any; break - case Method.POST: req = this.http.post(url, httpOpts.body, options) as any; break - case Method.PUT: req = this.http.put(url, httpOpts.body, options) as any; break - case Method.PATCH: req = this.http.patch(url, httpOpts.body, options) as any; break - case Method.DELETE: req = this.http.delete(url, options) as any; break - } + const req = this.http.post(url, httpOpts.body, options) - return (httpOpts.timeout ? withTimeout(req, httpOpts.timeout) : req) + return (withTimeout(req, 60000)) .toPromise() - .then(res => res.body) + .then(res => AES_CTR.decryptPbkdf2('asdf', new Uint8Array(res))) + .then(res => JSON.parse(decodeUtf8(res))) .catch(e => { throw new HttpError(e) }) } } @@ -178,8 +169,8 @@ function withTimeout (req: Observable, timeout: number): Observable { } type AES_CTR = { - encryptPbkdf2: (secretKey: string, messageBuffer: Uint8Array) => Promise<{ cipher: Uint8Array, counter: Uint8Array, salt: Uint8Array }> - decryptPbkdf2: (secretKey, a: { cipher: Uint8Array, counter: Uint8Array, salt: Uint8Array }) => Promise + encryptPbkdf2: (secretKey: string, messageBuffer: Uint8Array) => Promise + decryptPbkdf2: (secretKey, arr: Uint8Array) => Promise } export const AES_CTR: AES_CTR = { @@ -191,10 +182,12 @@ export const AES_CTR: AES_CTR = { return window.crypto.subtle.encrypt(algorithm, key, messageBuffer) .then(encrypted => new Uint8Array(encrypted)) - .then(cipher => ({ cipher, counter, salt })) + .then(cipher => new Uint8Array([...counter,...salt,...cipher])) }, - decryptPbkdf2: async (secretKey: string, a: { cipher: Uint8Array, counter: Uint8Array, salt: Uint8Array }) => { - const { cipher, counter, salt } = a + decryptPbkdf2: async (secretKey: string, arr: Uint8Array) => { + const counter = arr.slice(0, 16) + const salt = arr.slice(16, 32) + const cipher = arr.slice(32) const { key } = await pbkdf2(secretKey, { name: 'AES-CTR', length: 256 }, salt) const algorithm = { name: 'AES-CTR', counter, length: 64 }; diff --git a/setup-wizard/src/app/services/api/live-api.service.ts b/setup-wizard/src/app/services/api/live-api.service.ts index 0008344d7..f735d3117 100644 --- a/setup-wizard/src/app/services/api/live-api.service.ts +++ b/setup-wizard/src/app/services/api/live-api.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core' -import { pauseFor } from '../state.service' -import { ApiService } from './api.service' +import { ApiService, EmbassyDrive, RecoveryDrive, SetupEmbassyRes, TransferProgressRes, VerifyProductKeyRes } from './api.service' import { HttpService } from './http.service' @Injectable({ @@ -12,64 +11,45 @@ export class LiveApiService extends ApiService { private readonly http: HttpService ) { super() } - async verifyProductKey(key) { - await pauseFor(2000) - return { - "is-recovering": false, - "tor-address": null - } + async verifyProductKey() { + return this.http.rpcRequest({ + method: 'verifyProductKey', + params: {} + }) } async getDataTransferProgress() { - tries = Math.min(tries + 1, 4) - return { - 'bytes-transfered': tries, - 'total-bytes': 4 - } + return this.http.rpcRequest({ + method: 'getDataTransferProgress', + params: {} + }) } async getEmbassyDrives() { - return [ - { - logicalname: 'Name1', - labels: ['label 1', 'label 2'], - capacity: 1600.66666, - used: 200.1255312, - }, - { - logicalname: 'Name2', - labels: [], - capacity: 1600.01234, - used: 0.00, - } - ] + return this.http.rpcRequest({ + method: 'getEmbassyDrives', + params: {} + }) } async getRecoveryDrives() { - await pauseFor(2000) - return [ - { - logicalname: 'Name1', - version: '0.3.3', - name: 'My Embassy' - }, - { - logicalname: 'Name2', - version: '0.2.7', - name: 'My Embassy' - } - ] + return this.http.rpcRequest({ + method: 'getRecoveryDrives', + params: {} + }) } async verifyRecoveryPassword(logicalname, password) { - await pauseFor(2000) - return password.length > 8 + return this.http.rpcRequest({ + method: 'verifyRecoveryPassword', + params: {logicalname, password} + }) } async setupEmbassy (setupInfo) { - await pauseFor(2000) - return { "tor-address": 'asdfasdfasdf.onion' } + return this.http.rpcRequest({ + method: 'setupEmbassy', + params: setupInfo + }) } } - -let tries = 0 diff --git a/setup-wizard/src/app/services/state.service.ts b/setup-wizard/src/app/services/state.service.ts index 526d96a1c..f38d7f6bb 100644 --- a/setup-wizard/src/app/services/state.service.ts +++ b/setup-wizard/src/app/services/state.service.ts @@ -8,8 +8,6 @@ import { ApiService, EmbassyDrive, RecoveryDrive } from './api/api.service' export class StateService { polling = false - productKey: string - embassyDrive: EmbassyDrive; embassyPassword: string recoveryDrive: RecoveryDrive;