import { Injectable } from "@angular/core"; import { TwitterApiProvider } from "../twitter-api/twitter-api"; import { P2pStorageIpfsProvider } from "../p2p-storage-ipfs/p2p-storage-ipfs"; import { Storage } from "@ionic/storage"; import NodeRSA from "node-rsa"; @Injectable() export class CryptoProvider { ownUserId: string; constructor( private twitter: TwitterApiProvider, private ipfs: P2pStorageIpfsProvider, private storage: Storage ) { this.init(); } private async init() { this.ownUserId = await this.storage.get("userId"); } public async publishPublicKey(key: string) { let publicKeyHistory = await this.getKeyHistory(this.ownUserId); // Todo: avoid publishing the same public key twice - check if new key equals newest key in history // Add new key to history const newKey = { key: key, validFrom: Date.now() }; if (publicKeyHistory) { publicKeyHistory["keys"].push(newKey); } else { publicKeyHistory = { keys: [newKey] }; } // TODO: encrypt key history with AES // Publish updated key history... const res = await this.ipfs.storePublicKey(publicKeyHistory); // tweet ipfs link const tweetResponse = await this.twitter.tweet( "ipfs://" + res["Hash"] + " #hybridOSN" ); // ... and update description in user profile with tweet id this.twitter.updateProfileDescription( "tweet://" + tweetResponse["data"]["id_str"] + " #hybridOSN" ); } private async getKeyHistory(userId: string) { // Get user description const userData = await this.twitter.fetchUser(userId); const profileDescription = userData["description"]; // Get tweet with link to key history const tweetId = this.extractTweetId(profileDescription); const tweetWithKeyHistoryLink = await this.twitter.fetchTweet(tweetId); // Extract link to public key const link = this.extractLinkFromDescription( tweetWithKeyHistoryLink["data"]["full_text"] ); // Fetch public key history if (link.length) { const keyHistory = await this.ipfs.fetchJson(link); // TODO: dedcrypt key history with AES return keyHistory; } else { return null; } } private extractTweetId(text: string) { for (let word of text.split(" ")) { if (this.isTweetId(word)) { return word.substr(8); } } return ""; } private extractLinkFromDescription(text: string): string { for (let word of text.split(" ")) { if (this.isIpfsLink(word)) { return word.substr(7); } } return ""; } private isIpfsLink(word: string): boolean { const hits = word.match(/ipfs:\/\/Qm[a-zA-Z0-9]+/); return hits != null; } private isTweetId(word: string): boolean { const hits = word.match(/tweet:\/\/[0-9]+/); return hits != null; } public async generateRsaKeys() { return await crypto.subtle.generateKey( { name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-256" } }, true, ["encrypt", "decrypt"] ); } public async extractPrivateKey(keys): Promise { return btoa( String.fromCharCode.apply( null, new Uint8Array(await crypto.subtle.exportKey("pkcs8", keys.privateKey)) ) ); } public async extractPublicKey(keys): Promise { return btoa( String.fromCharCode.apply( null, new Uint8Array(await crypto.subtle.exportKey("spki", keys.publicKey)) ) ); } public async isPublicKeyPublished(): Promise { const publicKey = await this.storage.get("publicKey"); const keyHistory = await this.getKeyHistory(this.ownUserId); if (keyHistory && publicKey.length) { const newestPublicKey = keyHistory["keys"].reverse()[0]["key"]; return newestPublicKey === publicKey; } else { return false; } } public async isPrivateKeySet(): Promise { const privateKey = await this.storage.get("privateKey"); return privateKey.length > 0; } public encrypt(plainText: string, privateKey: string) { const key = new NodeRSA( `-----BEGIN PRIVATE KEY-----${privateKey}-----END PRIVATE KEY-----` ); return key.encryptPrivate(plainText, "base64"); } public decrypt(encryptedMessage: string, publicKey: string): string { const key = new NodeRSA( `-----BEGIN PUBLIC KEY-----${publicKey}-----END PUBLIC KEY-----` ); return key.decryptPublic(encryptedMessage).toString(); } public async fetchPublicKeyHistoryForUser(userId: string): Promise { const keyHistory = await this.getKeyHistory(userId); return keyHistory["keys"].reverse(); } }