|
@@ -3,10 +3,13 @@ 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";
|
|
|
-
|
|
|
+declare var TextDecoder: any;
|
|
|
+declare var TextEncoder: any;
|
|
|
@Injectable()
|
|
|
export class CryptoProvider {
|
|
|
ownUserId: string;
|
|
|
+ IV_LENGTH = 12;
|
|
|
+ HYBRID_OSN_AES_KEY = "Z1vxAULQnZdoWhJOvv+hWEvVpyUHzNjD/ichEE2c8i4=";
|
|
|
|
|
|
constructor(
|
|
|
private twitter: TwitterApiProvider,
|
|
@@ -39,10 +42,13 @@ export class CryptoProvider {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- // TODO: encrypt key history with AES
|
|
|
+ // Ecnrypt key history
|
|
|
+ const encryptedPublicKeyHistory = await this.aesEncrypt(
|
|
|
+ JSON.stringify(publicKeyHistory)
|
|
|
+ );
|
|
|
|
|
|
// Publish updated key history...
|
|
|
- const res = await this.ipfs.storePublicKey(publicKeyHistory);
|
|
|
+ const res = await this.ipfs.storePublicKey(encryptedPublicKeyHistory);
|
|
|
|
|
|
// tweet ipfs link
|
|
|
const tweetResponse = await this.twitter.tweet(
|
|
@@ -71,9 +77,10 @@ export class CryptoProvider {
|
|
|
|
|
|
// Fetch public key history
|
|
|
if (link.length) {
|
|
|
- const keyHistory = await this.ipfs.fetchJson(link);
|
|
|
- // TODO: dedcrypt key history with AES
|
|
|
- return keyHistory;
|
|
|
+ const encryptedKeyHistory = await this.ipfs.fetchJson(link);
|
|
|
+ // Decrypt key history
|
|
|
+ const keyHistory = await this.aesDecrypt(encryptedKeyHistory.toString());
|
|
|
+ return JSON.parse(keyHistory);
|
|
|
} else {
|
|
|
return null;
|
|
|
}
|
|
@@ -173,4 +180,114 @@ export class CryptoProvider {
|
|
|
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) => {
|
|
|
+ // prepend the iv to the bytes, so that decryption-process can use it, see markdown file for "AES (symm.) encryption" and "AES Initialization vector (iv)"
|
|
|
+ return this.mergeBytes(iv, this.bufferToBytes(encData));
|
|
|
+ })
|
|
|
+ .then(this.bytesToBase64);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 };
|
|
|
+ }
|
|
|
}
|