diff --git a/assets/scripts/libs/google.client.js b/assets/scripts/libs/google.client.js index c6b0baa..e5c18fe 100644 --- a/assets/scripts/libs/google.client.js +++ b/assets/scripts/libs/google.client.js @@ -2,31 +2,31 @@ const scripts = ['https://apis.google.com/js/api.js', 'https://accounts.google.c const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'; const CLIENT_ID = '53206975661-ih3r0ubph3rqejdq97b029difbrk2bqj.apps.googleusercontent.com'; -// Authorization scopes required by the API; multiple scopes can be -// included, separated by spaces. -const SCOPES = 'openid email profile https://www.googleapis.com/auth/drive.appdata'; +// Authorization scopes required by the API; multiple scopes can be included, separated by spaces. +const SCOPES = 'openid email profile'; +const SCOPES_DRIVE = 'https://www.googleapis.com/auth/drive.appdata'; export class GoogleClient { async initGApi() { return new Promise((resolve, reject) => { gapi.load('client', async () => { - await gapi.client.init({ - discoveryDocs: [DISCOVERY_DOC], - }); + await gapi.client.init({ discoveryDocs: [DISCOVERY_DOC] }); resolve(); }); }); } + initTokenClient(scopes, cb) { + this.tokenClient = google.accounts.oauth2.initTokenClient({ + client_id: CLIENT_ID, + scope: scopes, + callback: cb // defined later + }); + console.log('google client init success'); + } async initGoolgeClient() { await Promise.all([loadSingleScript([scripts[0]]), loadSingleScript([scripts[1]])]); await this.initGApi(); - - this.tokenClient = google.accounts.oauth2.initTokenClient({ - client_id: CLIENT_ID, - scope: SCOPES, - callback: '', // defined later - }); - console.log('google client init success'); + this.initTokenClient(SCOPES); } login(funid) { this.tokenClient.callback = async (resp) => { @@ -51,4 +51,82 @@ export class GoogleClient { this.tokenClient.requestAccessToken({ prompt: '' }); } } + generateCfgName(key) { + return `wallet_${key}.json`; + } + hasDriveScope() { + return google.accounts.oauth2.hasGrantedAnyScope(gapi.client.getToken(), SCOPES_DRIVE); + } + async requestDriveScope() { + return new Promise((resolve, reject) => { + this.initTokenClient(SCOPES + " " + SCOPES_DRIVE, (resp) => { + if (resp.error !== undefined) { + throw resp; + } + resolve(); + }); + this.tokenClient.requestAccessToken({ prompt: 'consent' }); + }); + } + async listFiles() { + return gapi.client.drive.files.list({ + spaces: 'appDataFolder', + fields: 'files(id, name)', + }); + } + async downloadCfg(key) { + if (!this.hasDriveScope()) { + await this.requestDriveScope(); + } + let fileName = this.generateCfgName(key) + let files = await this.listFiles(); + let file = files.result.files.find((file) => file.name.toLowerCase() === fileName.toLowerCase()); + if (file) { + let fileId = file.id; + let info = await gapi.client.drive.files.get({ + fileId: fileId, + alt: 'media', + }); + return info.result; + } + } + async uploadOneFile(content, fileName) { + return new Promise((resolve, reject) => { + var file = new Blob([JSON.stringify(content)], {type: 'application/json'}); + var metadata = { + 'name': fileName, // Filename at Google Drive + 'mimeType': 'application/json', // mimeType at Google Drive + 'parents': ['appDataFolder'], // Folder ID at Google Drive + }; + + var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token. + var form = new FormData(); + form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'})); + form.append('file', file); + + var xhr = new XMLHttpRequest(); + xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id'); + xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken); + xhr.responseType = 'json'; + xhr.onload = () => { + console.log(xhr.response.id); // Retrieve uploaded file ID. + resolve && resolve(xhr.response); + }; + xhr.send(form); + }); + } + async uploadCfg(key, pass) { + if (!this.hasDriveScope()) { + await this.requestDriveScope(); + } + let fileName = this.generateCfgName(key); + let files = await this.listFiles(); + let file = files.result.files.find((file) => file.name.toLowerCase() === fileName.toLowerCase()); + if (!file) { + let res = await this.uploadOneFile({pass}, fileName); + return res; + } else { + return file; + } + } } diff --git a/assets/scripts/libs/native_bridge.js b/assets/scripts/libs/native_bridge.js index 72bc072..cb9ecbe 100644 --- a/assets/scripts/libs/native_bridge.js +++ b/assets/scripts/libs/native_bridge.js @@ -6,6 +6,8 @@ import init, { generate_sec_key, generate_scrypt_hash, generate_qr, + encrypt_content, + decrypt_content, } from '../../wasm/rustwallet.js'; import { QrScanner, showQr } from './qr_reading.js'; import { GoogleClient } from './google.client.js'; @@ -131,13 +133,30 @@ window.jsb = { callNative({ action: 'finishTransaction', funid, transactionId }); }, authGetStoragePass: function (funid, key) { - callNative({ action: 'authGetStoragePass', funid, key }); + if (jc.wallet.platform === 'web') { + googleClient.downloadCfg(key).then((data) => { + console.log(data); + let r = decrypt_content(data.pass, key); + let result = {errcode: 0, data: r}; + nativeCall(funid, JSON.stringify(result)); + }); + } else { + callNative({ action: 'authGetStoragePass', funid, key }); + } }, passStorageState: function (funid, key) { callNative({ action: 'passStorageState', funid, key }); }, storagePass: function (funid, key, pass) { - callNative({ action: 'storagePass', funid, key, pass }); + if (jc.wallet.platform === 'web') { + pass = encrypt_content(pass, key); + googleClient.uploadCfg(key, pass).then((data) => { + let result = {errcode: 0, data: data.id}; + nativeCall(funid, JSON.stringify(result)); + }); + } else { + callNative({ action: 'storagePass', funid, key, pass }); + } }, // END:: native method }; diff --git a/assets/scripts/run_sample.js b/assets/scripts/run_sample.js index ce9d55b..e3e25c5 100644 --- a/assets/scripts/run_sample.js +++ b/assets/scripts/run_sample.js @@ -184,6 +184,14 @@ const pages = { let res = await callMethod('emailInfo'); console.log(res); }, + getPass: async function() { + let res = await callMethod('restorePassLocal', '0x1c2d7640d9510569ef2687b7729be8e1583c2781'); + console.log(res); + }, + savePassCloud: async function() { + let res = await callMethod('storePassLocal', '0x1c2d7640d9510569ef2687b7729be8e1583c2781', '111111'); + console.log(res); + } }; // 根据上面的事件列表, 在页面上显示按钮 diff --git a/assets/wasm/rustwallet.d.ts b/assets/wasm/rustwallet.d.ts index 0e8d430..413db86 100644 --- a/assets/wasm/rustwallet.d.ts +++ b/assets/wasm/rustwallet.d.ts @@ -87,6 +87,18 @@ export function wdecrypt(id: string, openid: string, master_key: string, salt: s * @param {string} key * @returns {string} */ +export function encrypt_content(content: string, key: string): string; +/** +* @param {string} content +* @param {string} key +* @returns {string} +*/ +export function decrypt_content(content: string, key: string): string; +/** +* @param {string} content +* @param {string} key +* @returns {string} +*/ export function aes_encrypt(content: string, key: string): string; /** * @param {string} content @@ -152,6 +164,8 @@ export interface InitOutput { readonly wget_address: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => void; readonly wencrypt: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void; readonly wdecrypt: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void; + readonly encrypt_content: (a: number, b: number, c: number, d: number, e: number) => void; + readonly decrypt_content: (a: number, b: number, c: number, d: number, e: number) => void; readonly aes_encrypt: (a: number, b: number, c: number, d: number, e: number) => void; readonly aes_decrypt: (a: number, b: number, c: number, d: number, e: number) => void; readonly rencrypt: (a: number, b: number, c: number, d: number, e: number) => void; diff --git a/assets/wasm/rustwallet.js b/assets/wasm/rustwallet.js index ac7b7ad..eafc283 100644 --- a/assets/wasm/rustwallet.js +++ b/assets/wasm/rustwallet.js @@ -384,6 +384,50 @@ export function wdecrypt(id, openid, master_key, salt, pass, msg) { } } +/** +* @param {string} content +* @param {string} key +* @returns {string} +*/ +export function encrypt_content(content, key) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(content, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + wasm.encrypt_content(retptr, ptr0, len0, ptr1, len1); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(r0, r1); + } +} + +/** +* @param {string} content +* @param {string} key +* @returns {string} +*/ +export function decrypt_content(content, key) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(content, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + wasm.decrypt_content(retptr, ptr0, len0, ptr1, len1); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(r0, r1); + } +} + /** * @param {string} content * @param {string} key diff --git a/assets/wasm/rustwallet_bg.wasm b/assets/wasm/rustwallet_bg.wasm index d467a94..843e2e0 100644 Binary files a/assets/wasm/rustwallet_bg.wasm and b/assets/wasm/rustwallet_bg.wasm differ diff --git a/assets/wasm/rustwallet_bg.wasm.d.ts b/assets/wasm/rustwallet_bg.wasm.d.ts index 55de190..66b9b74 100644 --- a/assets/wasm/rustwallet_bg.wasm.d.ts +++ b/assets/wasm/rustwallet_bg.wasm.d.ts @@ -10,6 +10,8 @@ export function generate_scrypt_hash(a: number, b: number, c: number, d: number, export function wget_address(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number): void; export function wencrypt(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number): void; export function wdecrypt(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number): void; +export function encrypt_content(a: number, b: number, c: number, d: number, e: number): void; +export function decrypt_content(a: number, b: number, c: number, d: number, e: number): void; export function aes_encrypt(a: number, b: number, c: number, d: number, e: number): void; export function aes_decrypt(a: number, b: number, c: number, d: number, e: number): void; export function rencrypt(a: number, b: number, c: number, d: number, e: number): void; diff --git a/index.html b/index.html index 8e0b458..caba460 100644 --- a/index.html +++ b/index.html @@ -63,7 +63,6 @@ }, false); document.body.appendChild(s); }) - }; var scripts = [ ['assets/scripts/libs/jcwallet.js'],