From 0131c27ae0944550231c0e2ed976ff76425efbbe Mon Sep 17 00:00:00 2001
From: igor <igor.markin@vereign.com>
Date: Mon, 16 Nov 2020 13:21:45 +0300
Subject: [PATCH] Implement nonce signature

---
 javascript/src/iframe/viamapi-iframe.js | 43 ++++++++++++++++-------
 javascript/src/utilities/cryptoUtils.js | 45 +++++++++++++++++++++++++
 main.go                                 | 19 ++++++-----
 3 files changed, 86 insertions(+), 21 deletions(-)
 create mode 100644 javascript/src/utilities/cryptoUtils.js

diff --git a/javascript/src/iframe/viamapi-iframe.js b/javascript/src/iframe/viamapi-iframe.js
index ba3b561..d7c90e2 100644
--- a/javascript/src/iframe/viamapi-iframe.js
+++ b/javascript/src/iframe/viamapi-iframe.js
@@ -47,6 +47,7 @@ import {
   checkRecoveryKeyCombine,
   encryptShare
 } from "../utilities/secrets";
+import {generateNonce, signRSA} from "../utilities/cryptoUtils";
 
 const penpalMethods = require("../../temp/penpal-methods").default;
 const WopiAPI = require("./wopiapi-iframe");
@@ -129,6 +130,22 @@ function setIdentityInLocalStorage(identityToStore, extendKey = true) {
   );
 }
 
+async function setCurrentlyLoadedIdentity(identity) {
+  window.currentlyLoadedIdentity = identity;
+
+  if (identity) {
+    let nonce = window.viamApi.getNonce();
+
+    if (!nonce) {
+      nonce = generateNonce();
+      const privateKey = window.currentlyLoadedIdentity.authentication.privateKey;
+      const nonceSignature = await signRSA(privateKey, nonce);
+      window.viamApi.setNonce(Buffer.from(nonce).toString("base64"));
+      window.viamApi.setNonceSignature(Buffer.from(nonceSignature).toString("base64"));
+    }
+  }
+}
+
 function getProfileData(identity) {
   return new Penpal.Promise(executeResultUpper => {
     executeRestfulFunction(
@@ -260,7 +277,7 @@ const destroyIdentity = () => {
     const { publicKey } = window.currentlyLoadedIdentity.authentication;
 
     delete window.loadedIdentities[publicKey];
-    window.currentlyLoadedIdentity = null;
+    setCurrentlyLoadedIdentity(null);
     destroyIdentityFromLocalStorage(publicKey);
   }
 };
@@ -393,7 +410,7 @@ function loadIdentityInternal(identityKey, pinCode) {
         localStorage.removeItem("attempt");
 
         window.loadedIdentities[identityKey] = loadedIdentity;
-        window.currentlyLoadedIdentity = loadedIdentity;
+        await setCurrentlyLoadedIdentity(loadedIdentity)
 
         if (identityKey === localStorage.getItem("authenticatedIdentity")) {
           window.currentlyAuthenticatedIdentity = loadedIdentity;
@@ -479,10 +496,10 @@ function getCertificateForPassport(passportUUID, internal) {
 
             getProfileData(passportIdentity).then(executeResult1 => {
               setIdentityInLocalStorage(passportIdentity)
-                .then(() => {
+                .then(async () => {
                   window.currentlyAuthenticatedIdentity = passportIdentity;
                   window.lastTimeGetProfile = 0;
-                  window.currentlyLoadedIdentity = passportIdentity;
+                  await setCurrentlyLoadedIdentity(passportIdentity)
                   const copyOfCryptoData = JSON.parse(
                     JSON.stringify(cryptoData)
                   );
@@ -559,7 +576,7 @@ const connection = Penpal.connectToParent({
     ...penpalMethods,
     createIdentity(pinCode) {
       return new Penpal.Promise(result => {
-        createPassportCertificate(makeid()).then(function (keys) {
+        createPassportCertificate(makeid()).then(async function (keys) {
           const newIdentity = new Identity();
           const cryptoData = new CryptoData();
           cryptoData.setPublicKey(keys["publicKeyPEM"]);
@@ -568,7 +585,7 @@ const connection = Penpal.connectToParent({
           newIdentity.setAuthentication(cryptoData);
           newIdentity.setPinCode(pinCode);
 
-          window.currentlyLoadedIdentity = newIdentity;
+          await setCurrentlyLoadedIdentity(newIdentity)
           localStorage.setItem(
             "currentlyLoadedIdentity",
             JSON.stringify(newIdentity)
@@ -629,7 +646,7 @@ const connection = Penpal.connectToParent({
           identity.pinCode = newPinCode;
           await setIdentityInLocalStorage(identity);
           window.currentlyAuthenticatedIdentity = identity;
-          window.currentlyLoadedIdentity = identity;
+          await setCurrentlyLoadedIdentity(identity);
 
           return encodeResponse("200", null, "Successfully changed pincode");
         } else {
@@ -758,7 +775,7 @@ const connection = Penpal.connectToParent({
           let sequence = Promise.resolve();
           if (executeResult.code === "200") {
             sequence = sequence.then(() => {
-              setIdentityInLocalStorage(registerIdentity);
+              setIdentityInLocalStorage(window.currentlyLoadedIdentity);
             });
           }
           sequence
@@ -995,7 +1012,7 @@ const connection = Penpal.connectToParent({
         "currentlyLoadedIdentity"
       );
       const identity = new Identity(currentlyLoadedIdentity);
-      window.currentlyLoadedIdentity = identity;
+      await setCurrentlyLoadedIdentity(identity);
       const { publicKey } = identity.authentication;
       window.loadedIdentities[publicKey] = identity;
       window.viamAnonymousApi.setIdentity(publicKey);
@@ -2489,7 +2506,7 @@ connection.promise.then(parent => {
     if (event.key === "authenticatedIdentity" && event.newValue === null) {
       const publicKey =
         window.currentlyAuthenticatedIdentity.authentication.publicKey;
-      window.currentlyLoadedIdentity = null;
+      setCurrentlyLoadedIdentity(null);
       window.currentlyAuthenticatedIdentity = null;
       const event = createEvent("LogoutFromAnotherTab", "Logout", [publicKey]);
       parent.onEvent(event);
@@ -2572,7 +2589,7 @@ connection.promise.then(parent => {
           false
         );
 
-        window.currentlyLoadedIdentity = identity;
+        await setCurrentlyLoadedIdentity(identity);
 
         if (!identityAuthenticatedEvent && identity) {
           const event = createEvent("IdentityAuthenticated", "Authenticated", [
@@ -2604,7 +2621,7 @@ connection.promise.then(parent => {
         }
 
         identityAuthenticatedEvent = false;
-        window.currentlyLoadedIdentity = null;
+        setCurrentlyLoadedIdentity(null);
       }
 
       localStorage.removeItem("currentlyLoadedIdentity");
@@ -2711,7 +2728,7 @@ connection.promise.then(parent => {
               localStorage.removeItem("token");
               localStorage.removeItem("authenticatedIdentity");
               delete window.loadedIdentities[authenticationPublicKey];
-              window.currentlyLoadedIdentity = null;
+              setCurrentlyLoadedIdentity(null);
               window.currentlyAuthenticatedIdentity = null;
               window.lastTimeGetProfile = 0;
 
diff --git a/javascript/src/utilities/cryptoUtils.js b/javascript/src/utilities/cryptoUtils.js
new file mode 100644
index 0000000..cee8229
--- /dev/null
+++ b/javascript/src/utilities/cryptoUtils.js
@@ -0,0 +1,45 @@
+const webcryptoLiner = require("webcrypto-liner/build/index");
+
+export const generateNonce = () => {
+  return webcryptoLiner.crypto.getRandomValues(new Buffer(12));
+}
+
+const convertPemToBinary = (pem) => {
+  const lines = pem.split("\n");
+  let encoded = "";
+  for (let i = 0; i < lines.length; i++) {
+    if (
+      lines[i].trim().length > 0 &&
+      lines[i].indexOf("-BEGIN PRIVATE KEY-") < 0 &&
+      lines[i].indexOf("-BEGIN PUBLIC KEY-") < 0 &&
+      lines[i].indexOf("-END PRIVATE KEY-") < 0 &&
+      lines[i].indexOf("-END PUBLIC KEY-") < 0
+    ) {
+      encoded += lines[i].trim();
+    }
+  }
+  return Buffer.from(encoded, "base64")
+};
+
+export const signRSA = async (privateKeyPEM, data) => {
+  const privateKey = await webcryptoLiner.crypto.subtle.importKey(
+    "pkcs8",
+    convertPemToBinary(privateKeyPEM),
+    {
+      name: "RSA-PSS",
+      hash: "SHA-256"
+    },
+    true,
+    ["sign"]
+  );
+
+  return await webcryptoLiner.crypto.subtle.sign(
+    {
+      name: "RSA-PSS",
+      // hash: "SHA-256",
+      saltLength: 32
+    },
+    privateKey,
+    data
+  );
+};
diff --git a/main.go b/main.go
index 99092e7..02d1ede 100644
--- a/main.go
+++ b/main.go
@@ -82,7 +82,9 @@ func buildViamAPI() string {
 		"            'publicKey': '',\n" +
 		"            'uuid': '',\n" +
 		"            'deviceHash': '',\n" +
-		"            'token': ''\n" +
+		"            'token': '',\n" +
+		"            'nonce': '',\n" +
+		"            'nonceSignature': ''\n" +
 		"        }\n" +
 		"    }\n" +
 		"}\n\n"
@@ -100,19 +102,20 @@ func buildViamAPI() string {
 		"    this.config.headers.publicKey = window.btoa(authenticationPublicKey);\n" +
 		"};\n\n"
 
-	result += "ViamAPI.prototype.setPrivateKey = function(privateKey) {\n" +
-		"    this.privateKey = privateKey;\n" +
+	result += "ViamAPI.prototype.setNonceSignature = function(nonceSignature) {\n" +
+		"    this.config.headers.nonceSignature = nonceSignature;\n" +
 		"};\n\n"
 
+    result += "ViamAPI.prototype.setNonce = function(nonce) {\n" +
+            "    this.config.headers.nonce = nonce;\n" +
+            "};\n\n"
+    result += "ViamAPI.prototype.getNonce = function() {\n" +
+                "    return this.config.headers.nonce;\n" +
+                "};\n\n"
 	result += "ViamAPI.prototype.setPrivateKey = function(privateKey) {\n" +
 		"    this.privateKey = privateKey;\n" +
 		"};\n\n"
 
-	result += "this.generateNonce = function() {\n" +
-		"    var privateKey = config.privateKey;\n" +
-		"    return this.config;\n" +
-		"};\n\n"
-
 	for i := 0; i < keysLen; i++ {
 		url := keys[i]
 		if endPoints[url].Form != nil {
-- 
GitLab