import { Component } from "@angular/core"; import { IonicPage, NavController, NavParams, LoadingController, AlertController } from "ionic-angular"; import { FormBuilder, Validators, FormGroup, ValidatorFn, AbstractControl } from "@angular/forms"; import { TwitterApiProvider } from "../../providers/twitter-api/twitter-api"; import { Storage } from "@ionic/storage"; import { P2pStorageIpfsProvider } from "../../providers/p2p-storage-ipfs/p2p-storage-ipfs"; import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun"; import twittertext from "twitter-text"; import { CryptoProvider } from "../../providers/crypto/crypto"; import * as openpgp from 'openpgp'; @IonicPage() @Component({ selector: "page-write-tweet", templateUrl: "write-tweet.html" }) export class WriteTweetPage { tweet: FormGroup; retweetId: string; replyToStatusId: string; retweet; replyTweet; openpgp; privateKey; publicKey; pk: any[]=[]; passp = 'super long and hard to guess secret' ; hkp = new openpgp.HKP('https://sks-keyservers.net/'); constructor( public navCtrl: NavController, public navParams: NavParams, private formBuilder: FormBuilder, private twitter: TwitterApiProvider, private loadingCtrl: LoadingController, private storage: Storage, private ipfs: P2pStorageIpfsProvider, private gun: P2pDatabaseGunProvider, private cryptoUtils: CryptoProvider, private alertCtrl: AlertController ) { this.retweetId = this.navParams.get("tweetId"); this.replyToStatusId = this.navParams.get("replyToStatus"); this.tweet = this.formBuilder.group({ text: [""], p2p: [false] }); this.addValidators(); // this.encryptDecryptFunction(); } // public async encryptDecryptFunction () { // await openpgp.initWorker({path:'assets/scripts/openpgp.worker.js'}); // let a = await this.generateKeys(); // console.log('a is:',a.publicKeyArmored); // let b = await this.generateKeys(); // console.log('b is:',b.publicKeyArmored); // let c = await this.generateKeys(); // this.privateKey =c.privateKeyArmored; // this.publicKey = c.publicKeyArmored; // this.pk.push(a.publicKeyArmored); // this.pk.push(b.publicKeyArmored); // // this.pk = [`----BEGIN PGP PUBLIC KEY BLOCK----- // // Version: OpenPGP.js v4.7.1 // // Comment: https://openpgpjs.org // // xjMEXfAn1xYJKwYBBAHaRw8BAQdAAMVNOABw8MBtrtYR8KC3tSro3wITyApT // // TVjKVCppD+DNG0pvbiBTbWl0aCA8am9uQGV4YW1wbGUuY29tPsJ3BBAWCgAf // // BQJd8CfXBgsJBwgDAgQVCAoCAxYCAQIZAQIbAwIeAQAKCRD+efBRXzuMsfA7 // // AQCEgoToFzv2hT9BREdiQp531/AHSyoZWmWvSZSvmga40gD8C+zwbCySnkhQ // // pb4L0DCKtSDa7pLg2g0OcxJlbSZWHQ3OOARd8CfXEgorBgEEAZdVAQUBAQdA // // p4mVY17dPWf6VCBqW10Ybk5JgUO6FK0OsETWw3gG2zcDAQgHwmEEGBYIAAkF // // Al3wJ9cCGwwACgkQ/nnwUV87jLFHbAD9GyoL7dcTDGQoqtrhKozdgnzfugTb // // er0bwU15WNMjefkA/jEqK9YUNcRrFKIuac9PVibGgutL8ak7ukysw6iTcCsM // // =fmhE // // -----END PGP PUBLIC KEY BLOCK-----`, // // `-----BEGIN PGP PUBLIC KEY BLOCK----- // // Version: OpenPGP.js v4.7.1 // // Comment: https://openpgpjs.org // // xjMEXfAn1hYJKwYBBAHaRw8BAQdAsF1ivpd0HU8ogj02LDv6BTOxNMWGZaEc // // OyZBwqoYJPrNG0pvbiBTbWl0aCA8am9uQGV4YW1wbGUuY29tPsJ3BBAWCgAf // // BQJd8CfWBgsJBwgDAgQVCAoCAxYCAQIZAQIbAwIeAQAKCRDEruv77flRJ32B // // AP93GIBcUW2okROoZZhdPVeqjRD72Ft64imXpdZ0jx4ohgEA5Kv9vs2kV73q // // k6fcdf7qD/i5gMExU0+vV05c9VxBYwfOOARd8CfWEgorBgEEAZdVAQUBAQdA // // 1J7E03ZopUnsIeNzeiZvba6qxhhUbpmBZ1aN1HhWUlEDAQgHwmEEGBYIAAkF // // Al3wJ9YCGwwACgkQxK7r++35USdTqQD/ZEg8X5tMx75nQe4mGlyiRjmmtWLw // // n9bslTdjBIszs/EA/R1WIm6ji4Ru1dJWc3ISisz78xTM2H8U7fnP8yjFcWcD // // =hgnW // // -----END PGP PUBLIC KEY BLOCK-----`]; // console.log('array of pub keys is :',this.pk); // this.pk = this.pk.map(async (key) => { // return (await openpgp.key.readArmored(key)).keys[0] // }); // console.log('priv key: ',this.privateKey,'this.pubkey',this.pk); // let encrypted; // const privKeyObj = (await openpgp.key.readArmored(this.privateKey)).keys[0]; // console.log('privKeyObj',privKeyObj); // const bla = await privKeyObj.decrypt(this.passp); // // const options = { // // message: openpgp.message.fromText('Hello, World!'), // input as Message object // // publicKeys: (await openpgp.key.readArmored(this.publicKey)).keys, // for encryption // // privateKeys: [privKeyObj] // for signing (optional) // // } // const options = { // message: openpgp.message.fromText('Hello, World!'), // input as Message object // publicKeys: await Promise.all(this.pk), // for encryption // privateKeys: [privKeyObj] // for signing (optional) // } // const ciphertext = await openpgp.encrypt(options); // encrypted = ciphertext.data; // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----' // console.log('encrypted is:',encrypted); // let aprivKeyObj = (await openpgp.key.readArmored(a.privateKeyArmored)).keys[0]; // await aprivKeyObj.decrypt(this.passp); // const options2 = { // message: await openpgp.message.readArmored(encrypted), // parse armored message // privateKeys: [aprivKeyObj] // for decryption // } // console.log('options2 is: ',options2); // let plaintext = await openpgp.decrypt(options2); // console.log('decrypted text is:',plaintext,plaintext.data); // return plaintext.data // 'Hello, World!' // } // public async generateKeys(){ // let options = { // userIds: [{ name:'Jon Smith', email:'jon@example.com' }], // multiple user IDs // curve: "ed25519", // ECC curve name // passphrase: this.passp // protects the private key // }; // let a = await openpgp.generateKey(options); // return a; // // console.log('resolved a = ',a); // // this.privateKey =a.privateKeyArmored; // // this.publicKey = a.publicKeyArmored; // // this.encryptDecryptFunction(); // } private async addValidators() { const triggerWords = await this.storage.get("keywords"); const validators = [ Validators.maxLength(140), this.containsTriggerWord(triggerWords) ]; this.tweet.controls["text"].setValidators(validators); } private containsTriggerWord(triggerWords: string): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { if (triggerWords) { const regexList = triggerWords .toLowerCase() .split(", ") .join("|"); const regex = new RegExp(regexList); const containsTriggerWord = regex.test(control.value.toLowerCase()); return containsTriggerWord ? { containsTriggerWord: { value: control.value } } : null; } else { return null; } }; } async ionViewDidLoad() { if (this.retweetId) { this.retweet = await this.twitter.fetchTweet(this.retweetId); } if (this.replyToStatusId) { this.replyTweet = await this.twitter.fetchTweet(this.replyToStatusId); } } get tweetCharProgress() { const progress = 1 - this.tweet.value["text"].length / 140; const radius = 8; const circumference = Math.PI * radius * 2; return progress * circumference; } get showTrigger(): boolean { return ( this.tweet && this.tweet.controls && this.tweet.controls.text && this.tweet.controls.text.errors && this.tweet.controls.text.errors["containsTriggerWord"] && !this.tweet.controls.p2p.value ); } showTriggerInfo() { this.alertCtrl .create({ title: "Watch Out!", message: "Your tweet contains words you have previously defined to only share securely via P2P. Currently P2P mode is not selected.", buttons: ["OK"] }) .present(); } async submitTweet() { console.log('Submitting tweet') const loading = this.loadingCtrl.create(); loading.present(); if (this.tweet.value.p2p) { loading.setContent("Validate keys..."); if ( (await this.cryptoUtils.isPrivateKeySet()) && (await this.cryptoUtils.isPublicKeyPublished()) ) { loading.setContent("Publish private tweet..."); await this.tweetPrivate(); } else { loading.dismiss(); const alert = this.alertCtrl.create({ title: "Oooops...", message: "Please verify that you have set a private and public key in the settings and that your latest public key was published." }); alert.present(); return; } } else { loading.setContent("Publish on Twitter..."); await this.twitter.tweet( this.tweet.value["text"], this.retweet, this.replyToStatusId ); } loading.dismiss(); this.navCtrl.pop(); } public async lookupKeys(email:string){ var options = { query: email }; let armoredPubkey = await this.hkp.lookup(options); console.log('armord pubkey',armoredPubkey); let pubkey = (await openpgp.key.readArmored(armoredPubkey)); console.log('array of opubkes returened from server',pubkey); pubkey = (await openpgp.key.readArmored(armoredPubkey)).keys[0]; console.log('latest pubkey is:',pubkey); // console.log('Found public key:',pubkey); this.pk.push(pubkey); } private async tweetPrivate() { const tweet = await this.buildPrivateTweet(); console.log('tweet is:',tweet.full_text); const privateKey = await this.storage.get("privateKey"); //fetch followers and their public keys //assuming the email id of rohit.shiva.gowda await this.lookupKeys("rohit.hosn@gmail.com"); console.log('array of pub keys is :', this.pk); this.pk = this.pk.map(async (key) => { console.log('key is:',key); return (await openpgp.key.readArmored(key)).keys[0] }); console.log("after mapping", this.pk); const options = { message: openpgp.message.fromText(tweet), // input as Message object publicKeys: await Promise.all(this.pk), // for encryption // privateKeys: [privKeyObj] // for signing (optional) } const ciphertext = await openpgp.encrypt(options); const encryptedTweet = ciphertext.data; console.log('encrypted tweet is:',encryptedTweet); // const encryptedTweet = this.cryptoUtils.encrypt( // JSON.stringify(tweet), // privateKey // ); const res = await this.ipfs.storeTweet(encryptedTweet); this.gun.storeLastTweetHashForUser( tweet.user_id, res["Hash"], tweet.created_at ); this.gun.publishHashtags(tweet.entities.hashtags); } private async buildPrivateTweet() { const status = this.tweet.value["text"].trim(); const entities = await this.getEntities(status); return { full_text: status, user_id: await this.storage.get("userId"), created_at: Date.now(), private_tweet: true, in_reply_to_status_id: this.replyToStatusId, quoted_status_id: this.retweetId, display_text_range: [0, status.length], entities: entities }; } private async getEntities(status: string) { return { hashtags: twittertext.extractHashtagsWithIndices(status), urls: twittertext.extractUrlsWithIndices(status), user_mentions: await this.getMentions(status) }; } private async getMentions(status: string) { // extract mentions const entities = twittertext.extractMentionsWithIndices(status); // add user_id const entitiesWithPromises = entities.map(async mention => { try { const user = await this.twitter.fetchUserFromScreenName( mention.screenName ); mention["id_str"] = user[0]["id_str"]; mention["screen_name"] = mention.screenName; delete mention.screenName; } catch (err) { console.error( "There is no user signed up to twitter with username: " + mention.screenName ); } return mention; }); // filter for valid users and return return (await Promise.all(entitiesWithPromises)).filter(el => el.hasOwnProperty("id_str") ); } }