|
@@ -1,24 +1,19 @@
|
|
import { Injectable } from "@angular/core";
|
|
import { Injectable } from "@angular/core";
|
|
import { TwitterApiProvider } from "../twitter-api/twitter-api";
|
|
import { TwitterApiProvider } from "../twitter-api/twitter-api";
|
|
import { P2pStorageIpfsProvider } from "../p2p-storage-ipfs/p2p-storage-ipfs";
|
|
import { P2pStorageIpfsProvider } from "../p2p-storage-ipfs/p2p-storage-ipfs";
|
|
-import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun";
|
|
|
|
|
|
+import { P2pDatabaseGunProvider } from "../p2p-database-gun/p2p-database-gun";
|
|
import { Storage } from "@ionic/storage";
|
|
import { Storage } from "@ionic/storage";
|
|
-import NodeRSA from "node-rsa";
|
|
|
|
import * as openpgp from 'openpgp';
|
|
import * as openpgp from 'openpgp';
|
|
-declare var TextDecoder: any;
|
|
|
|
-declare var TextEncoder: any;
|
|
|
|
@Injectable()
|
|
@Injectable()
|
|
export class CryptoProvider {
|
|
export class CryptoProvider {
|
|
ownUserId: string;
|
|
ownUserId: string;
|
|
- IV_LENGTH = 12;
|
|
|
|
- HYBRID_OSN_AES_KEY = "Z1vxAULQnZdoWhJOvv+hWEvVpyUHzNjD/ichEE2c8i4=";
|
|
|
|
email: string;
|
|
email: string;
|
|
|
|
|
|
constructor(
|
|
constructor(
|
|
private twitter: TwitterApiProvider,
|
|
private twitter: TwitterApiProvider,
|
|
private ipfs: P2pStorageIpfsProvider,
|
|
private ipfs: P2pStorageIpfsProvider,
|
|
- private storage: Storage,
|
|
|
|
- private gun: P2pDatabaseGunProvider
|
|
|
|
|
|
+ private gun: P2pDatabaseGunProvider,
|
|
|
|
+ private storage: Storage
|
|
) {
|
|
) {
|
|
this.init();
|
|
this.init();
|
|
}
|
|
}
|
|
@@ -29,59 +24,60 @@ export class CryptoProvider {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Publishs the public key history with the latest key
|
|
|
|
|
|
+ * Publishs the private key history with the latest key
|
|
* @param key key to publish
|
|
* @param key key to publish
|
|
*/
|
|
*/
|
|
|
|
+
|
|
public async publishPrivateKey(key: string) {
|
|
public async publishPrivateKey(key: string) {
|
|
|
|
+ // console.log("this.ownuserid:", this.ownUserId,"key",key);
|
|
let privateKeyHistory = await this.getKeyHistory(this.ownUserId);
|
|
let privateKeyHistory = await this.getKeyHistory(this.ownUserId);
|
|
- console.log("get publishPrivateKey crypto ", privateKeyHistory);
|
|
|
|
// Todo: avoid publishing the same public key twice - check if new key equals newest key in history
|
|
// Todo: avoid publishing the same public key twice - check if new key equals newest key in history
|
|
- if ( key === privateKeyHistory[0])
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ if ( privateKeyHistory && (key === privateKeyHistory.keys[0].key)) return;
|
|
else
|
|
else
|
|
{
|
|
{
|
|
// Add new key to history
|
|
// Add new key to history
|
|
- const newKey = {
|
|
|
|
- key: key,
|
|
|
|
- validFrom: Date.now()
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- if (privateKeyHistory) {
|
|
|
|
- privateKeyHistory["keys"].push(newKey);
|
|
|
|
- } else {
|
|
|
|
- privateKeyHistory = {
|
|
|
|
- keys: [newKey]
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
|
|
+ const newKey = {
|
|
|
|
+ key: key,
|
|
|
|
+ validFrom: Date.now()
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if (privateKeyHistory) {
|
|
|
|
+ privateKeyHistory["keys"].push(newKey);
|
|
|
|
+ } else {
|
|
|
|
+ privateKeyHistory = {
|
|
|
|
+ keys: [newKey]
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ // console.log("new pvtkeyhstry :",privateKeyHistory)
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
- // Ecnrypt key history
|
|
|
|
- const encryptedPrivateKeyHistory = JSON.stringify(privateKeyHistory);
|
|
|
|
|
|
+ if(privateKeyHistory){
|
|
|
|
+ // Ecnrypt key history
|
|
|
|
+ const encryptedPrivateKeyHistory = JSON.stringify(privateKeyHistory);
|
|
|
|
|
|
- // Publish updated key history...
|
|
|
|
- const res = await this.ipfs.storePrivateKey(encryptedPrivateKeyHistory);
|
|
|
|
|
|
+ // Publish updated key history...
|
|
|
|
+ const res = await this.ipfs.storePrivateKey(encryptedPrivateKeyHistory);
|
|
|
|
|
|
- // store ipfs link Of private tweet in gundb
|
|
|
|
- // let ipfsLink = "ipfs://" + res["Hash"];
|
|
|
|
|
|
+ // store ipfs link Of private tweet in gundb
|
|
|
|
+ // let ipfsLink = "ipfs://" + res["Hash"];
|
|
|
|
|
|
- await this.gun.storePrivateKeyHistory(this.ownUserId, this.email, res["Hash"]);
|
|
|
|
|
|
+ await this.gun.storePrivateKeyHistory(this.ownUserId, this.email, res["Hash"]);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- public async getKeyHistory(userId: string) {
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
+ private async getKeyHistory(userId: string) {
|
|
//get private key history from gun
|
|
//get private key history from gun
|
|
let link = await this.gun.getPvtKeyHistory(userId);
|
|
let link = await this.gun.getPvtKeyHistory(userId);
|
|
- console.log("get link crypto ", link);
|
|
|
|
-
|
|
|
|
|
|
+ // console.log("ipfs link = ", link);
|
|
// Fetch public key history
|
|
// Fetch public key history
|
|
- if (link.length) {
|
|
|
|
- const encryptedKeyHistory = await this.ipfs.fetchJson(link);
|
|
|
|
- console.log("get link private key crypto ", encryptedKeyHistory);
|
|
|
|
|
|
+ if (link) {
|
|
|
|
+ const encryptedKeyHistory = await this.ipfs.fetchJson(link.key);
|
|
|
|
+ // Decrypt key history
|
|
|
|
+ // const keyHistory = await this.aesDecrypt(encryptedKeyHistory.toString());
|
|
|
|
+ // console.log("encryptedKeyHistory:",encryptedKeyHistory);
|
|
return JSON.parse(encryptedKeyHistory.toString());
|
|
return JSON.parse(encryptedKeyHistory.toString());
|
|
|
|
+ // return null;
|
|
} else {
|
|
} else {
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
@@ -113,64 +109,12 @@ export class CryptoProvider {
|
|
return /tweet:\/\/[0-9]+/.test(word);
|
|
return /tweet:\/\/[0-9]+/.test(word);
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Generates a RSA key pair object
|
|
|
|
- */
|
|
|
|
- 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 generatePgpKeys(email) {
|
|
|
|
- let options = {
|
|
|
|
- userIds: [{ name: 'Rohithosn', email: email }], // multiple user IDs
|
|
|
|
- curve: "ed25519", // ECC curve name
|
|
|
|
- passphrase: "This is phas" // protects the private key
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- let a = await openpgp.generateKey(options);
|
|
|
|
- return a;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * extracts the private key from the key object and transforms it to readable text
|
|
|
|
- * @param keys key object
|
|
|
|
- */
|
|
|
|
- public async extractPrivateKey(keys): Promise < string > {
|
|
|
|
- return btoa(
|
|
|
|
- String.fromCharCode.apply(
|
|
|
|
- null,
|
|
|
|
- new Uint8Array(await crypto.subtle.exportKey("pkcs8", keys.privateKey))
|
|
|
|
- )
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * extracts the public key from the key object and transforms it to readable text
|
|
|
|
- * @param keys key object
|
|
|
|
- */
|
|
|
|
- public async extractPublicKey(keys): Promise < string > {
|
|
|
|
- return btoa(
|
|
|
|
- String.fromCharCode.apply(
|
|
|
|
- null,
|
|
|
|
- new Uint8Array(await crypto.subtle.exportKey("spki", keys.publicKey))
|
|
|
|
- )
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* checks if the latest published key is the same as the one saved in app settings
|
|
* checks if the latest published key is the same as the one saved in app settings
|
|
*/
|
|
*/
|
|
public async isPublicKeyPublished(): Promise < boolean > {
|
|
public async isPublicKeyPublished(): Promise < boolean > {
|
|
const publicKey = await this.storage.get("publicKey");
|
|
const publicKey = await this.storage.get("publicKey");
|
|
- return publicKey?true:false;
|
|
|
|
|
|
+ return publicKey ? true : false;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -179,148 +123,19 @@ export class CryptoProvider {
|
|
*/
|
|
*/
|
|
public async isPrivateKeySet(): Promise < boolean > {
|
|
public async isPrivateKeySet(): Promise < boolean > {
|
|
const privateKey = await this.storage.get("privateKey");
|
|
const privateKey = await this.storage.get("privateKey");
|
|
- return privateKey?true:false;
|
|
|
|
|
|
+ return privateKey ? true : false;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Encrypt text with RSA
|
|
|
|
- * @param plainText plain text
|
|
|
|
- * @param privateKey private key
|
|
|
|
- */
|
|
|
|
- public encrypt(plainText: string, privateKey: string) {
|
|
|
|
- const key = new NodeRSA(
|
|
|
|
- `-----BEGIN PRIVATE KEY-----${privateKey}-----END PRIVATE KEY-----`
|
|
|
|
- );
|
|
|
|
- return key.encryptPrivate(plainText, "base64");
|
|
|
|
- }
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Decrypt secret with RSA
|
|
|
|
- * @param encryptedMessage encrypted message
|
|
|
|
- * @param publicKey public key
|
|
|
|
- */
|
|
|
|
- public decrypt(encryptedMessage: string, publicKey: string): string {
|
|
|
|
- const key = new NodeRSA(
|
|
|
|
- `-----BEGIN PUBLIC KEY-----${publicKey}-----END PUBLIC KEY-----`
|
|
|
|
- );
|
|
|
|
- return key.decryptPublic(encryptedMessage).toString();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Fetches the public key history for a given user id
|
|
|
|
|
|
+ * Fetches the private key history for a given user id
|
|
* @param userId user id
|
|
* @param userId user id
|
|
*/
|
|
*/
|
|
- public async fetchPublicKeyHistoryForUser(userId: string): Promise < object[] > {
|
|
|
|
|
|
+ public async fetchPrivateKeyHistoryForUser(userId: string): Promise < object[] > {
|
|
const keyHistory = await this.getKeyHistory(userId);
|
|
const keyHistory = await this.getKeyHistory(userId);
|
|
- return keyHistory["keys"].reverse();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private async aesEncrypt(plainText: string) {
|
|
|
|
- const utf8BytesOfPlainText = new TextEncoder().encode(plainText);
|
|
|
|
-
|
|
|
|
- const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
|
|
|
|
- const keyBytes = this.base64ToBytes(this.HYBRID_OSN_AES_KEY);
|
|
|
|
-
|
|
|
|
- const algorithm = { name: "AES-GCM", iv, length: 256 };
|
|
|
|
-
|
|
|
|
- return crypto.subtle
|
|
|
|
- .importKey("raw", keyBytes, algorithm, false, ["encrypt"])
|
|
|
|
- .then((cryptoKey: CryptoKey) => {
|
|
|
|
- return crypto.subtle.encrypt(
|
|
|
|
- algorithm,
|
|
|
|
- cryptoKey,
|
|
|
|
- utf8BytesOfPlainText
|
|
|
|
- );
|
|
|
|
- })
|
|
|
|
- .then((encData: ArrayBuffer) =>
|
|
|
|
- this.mergeBytes(iv, this.bufferToBytes(encData))
|
|
|
|
- )
|
|
|
|
- .then(this.bytesToBase64);
|
|
|
|
|
|
+ if (keyHistory)
|
|
|
|
+ return keyHistory["keys"].reverse();
|
|
|
|
+ else return null;
|
|
}
|
|
}
|
|
|
|
|
|
- private aesDecrypt(encryptedMessage: string) {
|
|
|
|
- try {
|
|
|
|
- const ivWithEncryptedBytes = this.base64ToBytes(encryptedMessage);
|
|
|
|
-
|
|
|
|
- const { encryptedBytes, ivBytes } = this.splitIvAndEncrypted(
|
|
|
|
- ivWithEncryptedBytes
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- const keyBytes = this.base64ToBytes(this.HYBRID_OSN_AES_KEY);
|
|
|
|
-
|
|
|
|
- const algorithm = { name: "AES-GCM", iv: ivBytes, length: 256 };
|
|
|
|
-
|
|
|
|
- return crypto.subtle
|
|
|
|
- .importKey("raw", keyBytes, algorithm, false, ["decrypt"])
|
|
|
|
- .then((cryptoKey: CryptoKey) => {
|
|
|
|
- return crypto.subtle.decrypt(algorithm, cryptoKey, encryptedBytes);
|
|
|
|
- })
|
|
|
|
- .then((decryptedData: ArrayBuffer) => {
|
|
|
|
- return new TextDecoder().decode(decryptedData);
|
|
|
|
- });
|
|
|
|
- } catch (e) {
|
|
|
|
- return Promise.reject(e);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private bufferToBytes(arrayBuffer: ArrayBuffer) {
|
|
|
|
- return new Uint8Array(arrayBuffer);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private bytesToBase64(bytes: Uint8Array): string {
|
|
|
|
- let binary = "";
|
|
|
|
-
|
|
|
|
- const len = bytes.length;
|
|
|
|
- for (let i = 0; i < len; i++) {
|
|
|
|
- binary += String.fromCharCode(bytes[i]);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return btoa(binary);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private base64ToBytes(base64: string): Uint8Array {
|
|
|
|
- const binaryString = atob(base64);
|
|
|
|
-
|
|
|
|
- const len = binaryString.length;
|
|
|
|
- const bytes = new Uint8Array(len);
|
|
|
|
- for (let i = 0; i < len; i++) {
|
|
|
|
- bytes[i] = binaryString.charCodeAt(i);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return bytes;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private mergeBytes(a: Uint8Array, b: Uint8Array) {
|
|
|
|
- const aLen = a.length;
|
|
|
|
- const bLen = b.length;
|
|
|
|
-
|
|
|
|
- const res = new Uint8Array(aLen + bLen);
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < aLen; i++) {
|
|
|
|
- res[i] = a[i];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < bLen; i++) {
|
|
|
|
- res[i + aLen] = b[i];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return res;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private splitIvAndEncrypted(ivWithEncryptedBytes: Uint8Array) {
|
|
|
|
- const dataLen = ivWithEncryptedBytes.length;
|
|
|
|
-
|
|
|
|
- const ivBytes = new Uint8Array(this.IV_LENGTH);
|
|
|
|
- const encryptedBytes = new Uint8Array(dataLen - this.IV_LENGTH);
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < this.IV_LENGTH; i++) {
|
|
|
|
- ivBytes[i] = ivWithEncryptedBytes[i];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (let i = this.IV_LENGTH; i < dataLen; i++) {
|
|
|
|
- encryptedBytes[i - this.IV_LENGTH] = ivWithEncryptedBytes[i];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return { ivBytes, encryptedBytes };
|
|
|
|
- }
|
|
|
|
}
|
|
}
|