Browse Source

Protect public key history by encryption with AES

Carsten Porth 5 years ago
parent
commit
7aeea562fb
1 changed files with 123 additions and 6 deletions
  1. 123 6
      app/src/providers/crypto/crypto.ts

+ 123 - 6
app/src/providers/crypto/crypto.ts

@@ -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 };
+  }
 }