From a05b96ac237f7fc7f0c680f6d6d325635b2ceb76 Mon Sep 17 00:00:00 2001
From: Damyan Mitev <damyan.mitev@vereign.com>
Date: Tue, 10 Dec 2019 01:13:49 +0200
Subject: [PATCH] add validateVMime, minor changes to signVCard

---
 javascript/src/iframe/viamapi-iframe.js    | 130 +++++++++------------
 javascript/src/utilities/emailUtilities.js |  28 +++++
 2 files changed, 80 insertions(+), 78 deletions(-)

diff --git a/javascript/src/iframe/viamapi-iframe.js b/javascript/src/iframe/viamapi-iframe.js
index 2c94611..48e2ff4 100644
--- a/javascript/src/iframe/viamapi-iframe.js
+++ b/javascript/src/iframe/viamapi-iframe.js
@@ -1,4 +1,7 @@
-import { parseSMIME } from "../utilities/emailUtilities";
+import {
+  parseSMIME,
+  prepareVCardParts
+} from "../utilities/emailUtilities";
 import {
   stringToUtf8ByteArray,
   utf8ByteArrayToString,
@@ -1406,17 +1409,17 @@ const connection = Penpal.connectToParent({
 
       return encodeResponse("200", "", "Document signed");
     },
-    // passportUUID - passport to sign the vCard
-    // text, html - the text and html part of the email
-    // related, attachments - array of objects, containing hashes of images/objects, related to the html in format:
+    // passportUUID - String - passport to sign the vCard
+    // text, html - String - the text and html part of the email, both optional
+    // parts - array of objects, representing the MIME structure in format:
     // {
     //   headers: {
-    //     "Content-Type": "application/hash; algorithm=SHA-256",
-    //     "Original-Content-Type": "image/jpeg", //original content type
-    //     "Content-Disposition": "inline" or "attachment",
-    //     ... //other headers
+    //     "Content-Type": "image/jpeg",
+    //     "Content-Disposition": "inline" or "attachment" with additional attributes,
+    //     ... //other headers from MIME
     //   },
-    //   body: "base64 encoded hash"
+    //   body: String if it is a text part (Content-Type = "text/...") or Uint8Array otherwise; filled for leaf MIME nodes
+    //   parts: array of instances of the same object; filled for container MIME nodes (Content-Type = "multipart/...")
     // }
     signVCard: async (passportUUID, text, html, parts = null) => {
       const authenticationPublicKey = localStorage.getItem(
@@ -1431,6 +1434,13 @@ const connection = Penpal.connectToParent({
         return encodeResponse("400", "", "Identity not authenticated");
       }
 
+      const messageUUID = makeid();
+
+      const vCardAttribs = {
+        passportUUID: passportUUID,
+        messageUUID: messageUUID
+      };
+
       let vCardImageData;
       let vCardImageClaimValue;
 
@@ -1454,7 +1464,8 @@ const connection = Penpal.connectToParent({
         vCardImageClaimValue = vCardClaimResponse.data;
       }
 
-      var coordinates = {fromL: -1, fromR:-1, toL: -1, toR: -1}
+      let qrCodeImageData;
+      let qrCodeCoordinates = {fromL: -1, fromR:-1, toL: -1, toR: -1};
 
       if (vCardImageClaimValue && "state" in vCardImageClaimValue && vCardImageClaimValue.state === "disabled") {
         vCardImageData = new ImageData({
@@ -1480,21 +1491,16 @@ const connection = Penpal.connectToParent({
           return encodeResponse("400", "", "Content type of vCard mmust be 'image/png'");
         }
 
-        coordinates = vCardImageResponse.data.QRCodeCoordinates
+        qrCodeCoordinates = vCardImageResponse.data.QRCodeCoordinates;
+        const qrCodeBase64Content = await generateQrCode("https://" + location.host + "/check/" + messageUUID);
+        qrCodeImageData = new ImageData(
+          {
+            contentType: "image/png",
+            content: qrCodeBase64Content
+          }
+        );
       }
 
-      var messageUUID = makeid()
-
-      var qrCodeBase64Content = await generateQrCode("https://" + location.host + "/check/" + messageUUID)
-
-      var qrCode = new ImageData(
-        {
-          contentType: "image/png",
-          content: qrCodeBase64Content,
-        }
-      )
-
-
       if (!parts) {
         parts = [];
       }
@@ -1504,7 +1510,7 @@ const connection = Penpal.connectToParent({
           headers: {
             "Content-Type": "text/html"
           },
-          body: stringToUtf8Base64(html)
+          body: html
         };
         parts.unshift(htmlPart);
       }
@@ -1514,11 +1520,13 @@ const connection = Penpal.connectToParent({
           headers: {
             "Content-Type": "text/plain"
           },
-          body: stringToUtf8Base64(text)
+          body: text
         };
         parts.unshift(textPart);
       }
 
+      prepareVCardParts(parts);
+
       const certResponse = await getCertificateForPassport(passportUUID, true);
 
       if (certResponse.code !== "200") {
@@ -1550,8 +1558,8 @@ const connection = Penpal.connectToParent({
 
       passportChain.reverse();
 
-      console.log(qrCode);
-      console.log(coordinates);
+      console.log(qrCodeImageData);
+      console.log(qrCodeCoordinates);
 
       const signVCardResponse = await executeRestfulFunction(
         "private",
@@ -1562,8 +1570,9 @@ const connection = Penpal.connectToParent({
         privateKeyOneTime,
         passportChain,
         parts,
-        qrCode,
-        coordinates,
+        vCardAttribs,
+        qrCodeImageData,
+        qrCodeCoordinates,
       );
       if (signVCardResponse.code !== "200") {
         return encodeResponse("400", "", signVCardResponse.status);
@@ -1572,25 +1581,16 @@ const connection = Penpal.connectToParent({
       const signedVCardImageData = new ImageData(signVCardResponse.data);
       return encodeResponse("200", {
         image: signedVCardImageData,
-        messageUUID: messageUUID,
+        messageUUID: messageUUID
       }, "vCard signed");
     },
-    // vCardImageData = {
-    //   contentType: "image/png",
-    //   contentBase64: base 64 encoded image
-    // };
-    // text, html - the text and html part of the email
-    // related, attachments - array of objects, containing hashes of images/objects, related to the html in format:
+    // mime - String - the MIME of the email message
+    // vCardAttribs - optional attributes for the verification procedure in format
     // {
-    //   headers: {
-    //     "Content-Type": "application/hash; algorithm=SHA-256",
-    //     "Original-Content-Type": "image/jpeg", //original content type
-    //     "Content-Disposition": "inline" or "attachment",
-    //     ... //other headers
-    //   },
-    //   body: "base64 encoded hash"
-    // }
-    validateVCard: async (vCardImageData, text, html, parts = null) => {
+    //   passportUUID: passportUUID,
+    //   messageUUID: messageUUID
+    // };
+    validateVMime: async (mime, vCardAttribs = null) => {
       const authenticationPublicKey = localStorage.getItem(
         "authenticatedIdentity"
       );
@@ -1603,47 +1603,21 @@ const connection = Penpal.connectToParent({
         return encodeResponse("400", "", "Identity not authenticated");
       }
 
-      //vCardImageData = new ImageData(vCardImageData);
-
-      if (!parts) {
-        parts = [];
-      }
-
-      if (html) {
-        const htmlPart = {
-          headers: {
-            "Content-Type": "text/html"
-          },
-          body: stringToUtf8Base64(html)
-        };
-        parts.unshift(htmlPart);
-      }
-
-      if (text) {
-        const textPart = {
-          headers: {
-            "Content-Type": "text/plain"
-          },
-          body: stringToUtf8Base64(text)
-        };
-        parts.unshift(textPart);
-      }
-
-
-      const validateVCardResponse = await executeRestfulFunction(
+      const validateVMimeResponse = await executeRestfulFunction(
         "private",
         window.viamApi,
-        window.viamApi.signValidateVCard,
+        window.viamApi.signValidateVMime,
         null,
-        parts
+        mime,
+        vCardAttribs
       );
-      if (validateVCardResponse.code !== "200") {
-        return encodeResponse("400", "", validateVCardResponse.status);
+      if (validateVMimeResponse.code !== "200") {
+        return encodeResponse("400", "", validateVMimeResponse.status);
       }
 
       //TODO - what will be the response?
 
-      const signedVCardImageData = new ImageData(validateVCardResponse.data);
+      const signedVCardImageData = new ImageData(validateVMimeResponse.data);
       return encodeResponse("200", signedVCardImageData, "vCard signed");
     },
     generateQrCode,
diff --git a/javascript/src/utilities/emailUtilities.js b/javascript/src/utilities/emailUtilities.js
index 6cade6b..b1aae25 100644
--- a/javascript/src/utilities/emailUtilities.js
+++ b/javascript/src/utilities/emailUtilities.js
@@ -15,6 +15,10 @@ import {
   getCertificateChain,
   parseSignedData
 } from "./signingUtilities";
+import {
+  byteArrayToBase64,
+  stringToUtf8Base64
+} from "./stringUtilities";
 
 export const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature";
 export const DEFAULT_ATTACHMENT_NAME = "attachment";
@@ -131,3 +135,27 @@ export const extractHtmlBodyFromString = string => {
     .replace(/<!--[\s\S]*?-->/gm, "")
     .trim();
 };
+
+export const prepareVCardParts = parts => {
+  if (!parts) {
+    return;
+  }
+  for (const part of parts) {
+    if (part.body) {
+      if (part.body instanceof String) {
+        part.body = stringToUtf8Base64(part.body);
+      } else
+      if (part.body instanceof Uint8Array) {
+        part.body = byteArrayToBase64(part.body);
+      } else
+      if (part.body instanceof ArrayBuffer) {
+        part.body = byteArrayToBase64(new Uint8Array(part.body));
+      } else {
+        throw new Error('part body is neither string, nor Uint8Array, nor ArrayBuffer');
+      }
+    }
+    if (part.parts) {
+      prepareVCardParts(part.parts);
+    }
+  }
+};
-- 
GitLab