Skip to content
Snippets Groups Projects
viamapi-iframe.js 89.3 KiB
Newer Older
import {createDeviceHash} from '../utilities/appUtility';

const libmime = require('libmime');
const QRCode = require('qrcode');
const pkijs = require('pkijs');
const asn1js = require('asn1js');
const pvutils = require('pvutils');
const Penpal = require('penpal').default;
const penpalMethods = require('../../temp/penpal-methods').default;
const WopiAPI = require('./wopiapi-iframe');
const ViamAPI = require('../../temp/viamapi');
//*********************************************************************************

const CERTIFIATE_Version_1 = 0;
const CERTIFIATE_Version_3 = 2;

//these bit fields are reversed, WTF!
const KEY_USAGE_DigitalSignature	= 0x80;//01;
const KEY_USAGE_NonRepudiation		= 0x40;//02;
const KEY_USAGE_KeyEncipherment		= 0x20;//04;
const KEY_USAGE_DataEncipherment	= 0x10;//08;
const KEY_USAGE_KeyAgreement		= 0x08;//10;
const KEY_USAGE_KeyCertSign			= 0x04;//20;
const KEY_USAGE_CRLSign				= 0x02;//40;
//const KEY_USAGE_EncipherOnly		= 0x01;//80; // Not used for now. Must be used together with KEY_USAGE_KeyAgreement (maybe should be ORed as a constant directly?)
//const KEY_USAGE_DecipherOnly		= 0x80;//0100; // If used, modify "KeyUsage" extension array buffer size and appropriate bit operators to accomodate for extra byte

const KEY_USAGE_LeafCertificate		= KEY_USAGE_DigitalSignature | KEY_USAGE_NonRepudiation | KEY_USAGE_KeyEncipherment | KEY_USAGE_DataEncipherment;
const KEY_USAGE_CertificateAuthority= KEY_USAGE_DigitalSignature | KEY_USAGE_KeyCertSign | KEY_USAGE_CRLSign;


const OID_EXT_KEY_USAGE_Any     	= "2.5.29.37.0";
const OID_ID_PKIX_ServerAuth		= "1.3.6.1.5.5.7.3.1";
const OID_ID_PKIX_ClientAuth		= "1.3.6.1.5.5.7.3.2";
const OID_ID_PKIX_CodeSigning		= "1.3.6.1.5.5.7.3.3";
const OID_ID_PKIX_EmailProtection	= "1.3.6.1.5.5.7.3.4";
const OID_ID_PKIX_TimeStamping	    = "1.3.6.1.5.5.7.3.8";
const OID_ID_PKIX_OCSPSigning		= "1.3.6.1.5.5.7.3.9";
// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.1"; // Microsoft Certificate Trust List signing
// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.4";  // Microsoft Encrypted File System
const OID_PKCS7_Data                = "1.2.840.113549.1.7.1";
const OID_PKCS7_SignedData          = "1.2.840.113549.1.7.2";
const OID_PKCS7_EnvelopedData       = "1.2.840.113549.1.7.3";
const OID_PKCS9_EmailAddress        = "1.2.840.113549.1.9.1";
const OID_PKCS9_ContentType         = "1.2.840.113549.1.9.3";
const OID_PKCS9_MessageDigest       = "1.2.840.113549.1.9.4";
Markin Igor's avatar
Markin Igor committed
const OID_PKCS9_SigningTime         = "1.2.840.113549.1.9.5";

const defaultAlgorithms = {
  hashAlg: "SHA-256",
  signAlg: "RSASSA-PKCS1-v1_5",
  keyLength: 2048
Markin Igor's avatar
Markin Igor committed
};

const AES_encryptionVariant_Password  = 2;
const encryptionAlgorithm = {
  name: "AES-CBC",
  length: 128
};

//*********************************************************************************
// Returns promise, resolved to keyPair object {publicKey, privateKey}
//*********************************************************************************
function generateKeys(algorithms) {
  //region Get a "crypto" extension
  const crypto = pkijs.getCrypto();
  if (typeof crypto === "undefined") {
    return Promise.reject("No WebCrypto extension found");
  }
  //endregion Get a "crypto" extension

  if (!algorithms) {
    algorithms = defaultAlgorithms;
  } else {
    if (!algorithms.hashAlg) {
      algorithms.hashAlg = defaultAlgorithms.hashAlg;
    }
    if (!algorithms.signAlg) {
      algorithms.signAlg = defaultAlgorithms.signAlg;
    }
    if (!algorithms.keyLength) {
      algorithms.keyLength = defaultAlgorithms.keyLength;
    }
  }

  //region Get default algorithm parameters for key generation
  const algorithm = pkijs.getAlgorithmParameters(algorithms.signAlg, "generatekey");
  if("hash" in algorithm.algorithm) {
    algorithm.algorithm.hash.name = algorithms.hashAlg;
  }
  algorithm.algorithm.modulusLength = algorithms.keyLength;
  //endregion

  return crypto.generateKey(algorithm.algorithm, true, algorithm.usages);
}

//*********************************************************************************
function createCertificate(certData, issuerData = null)
{

  if (typeof certData === "undefined") {
    return Promise.reject("No Certificate data provided");
  }

  if (typeof certData.subject === "undefined") {
    return Promise.reject("No Certificate subject data provided");
  }


  //region Get a "crypto" extension
  const crypto = pkijs.getCrypto();

  if (typeof crypto === "undefined") {
    return Promise.reject("No WebCrypto extension found");
  }
  //endregion Get a "crypto" extension

  //region Initial variables
  let sequence = Promise.resolve();

  const certificate = new pkijs.Certificate();
  let publicKey;
  let privateKey;

  let certificateBuffer;// = new ArrayBuffer(0); // ArrayBuffer with loaded or created CERT
  let privateKeyBuffer;// = new ArrayBuffer(0);
  let publicKeyBuffer;// = new ArrayBuffer(0);

  //endregion Initial variables

  if (certData.keyPair) {
    //region Create a new key pair
    sequence = sequence.then(() =>
    {
      return certData.keyPair;
    });
    //endregion Create a new key pair

  } else {
    //region Create a new key pair
    sequence = sequence.then(() =>
    {
      return generateKeys(certData.algorithms);
    });
    //endregion Create a new key pair
  }

  //region Store new key in an interim variables
  sequence = sequence.then(keyPair =>
  {
    publicKey = keyPair.publicKey;
    privateKey = keyPair.privateKey;
  }, error => Promise.reject(`Error during key generation: ${error}`));
  //endregion Store new key in an interim variables

  //region Exporting public key into "subjectPublicKeyInfo" value of certificate
  sequence = sequence.then(() =>
    certificate.subjectPublicKeyInfo.importKey(publicKey)
  );
  //endregion Exporting public key into "subjectPublicKeyInfo" value of certificate

  sequence = sequence.then(
    () => crypto.digest({ name: "SHA-1" }, certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex),
    error => Promise.reject(`Error during importing public key: ${error}`)
  );

  //region Fill in cert data
  sequence = sequence.then(subjKeyIdBuffer =>
  {

    //region Put a static values
    certificate.version = CERTIFIATE_Version_3;

    const serialNumberBuffer = new ArrayBuffer(20);
    const serialNumberView = new Uint8Array(serialNumberBuffer);
Markin Igor's avatar
Markin Igor committed
    pkijs.getRandomValues(serialNumberView);
    // noinspection JSUnresolvedFunction
    certificate.serialNumber = new asn1js.Integer({ valueHex: serialNumberView });
    //endregion Put a static values

    //region Subject
    // For reference http://oidref.com/2.5.4.3
    if (certData.subject.commonName) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: "2.5.4.3", // Common name
        value: new asn1js.PrintableString({ value: certData.subject.commonName })
      }));
    }

    if (certData.subject.country) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: "2.5.4.6", // Country name
        value: new asn1js.PrintableString({ value: certData.subject.country })
      }));
    }

    if (certData.subject.locality) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: "2.5.4.7", // Locality Name
        value: new asn1js.PrintableString({ value: certData.subject.locality })
      }));
    }

    if (certData.subject.state) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: "2.5.4.8", // State or Province name
        value: new asn1js.PrintableString({ value: certData.subject.state })
      }));
    }

    if (certData.subject.organization) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: "2.5.4.10", // Organization name
        value: new asn1js.PrintableString({ value: certData.subject.organization })
      }));
    }

    if (certData.subject.organizationUnit) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: "2.5.4.11", // Organization unit name
        value: new asn1js.PrintableString({ value: certData.subject.organizationUnit })
      }));
    }

    if (certData.subject.email) {
      // noinspection JSUnresolvedFunction
      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
        type: OID_PKCS9_EmailAddress, // Email, deprecated but still widely used
        value: new asn1js.IA5String({ value: certData.subject.email })
      }));
    }
    //endregion Subject


    //region Issuer
    if (issuerData && issuerData.certificate) {
      certificate.issuer = issuerData.certificate.subject;
    } else {
      certificate.issuer = certificate.subject;
    }
    //endregion Issuer

    //region Validity
    if (!certData.validity) {
      certData.validity = {}
    }

    if (certData.validity.notBefore) {
      certificate.notBefore.value = certData.validity.notBefore; //date
    } else {
      const tmp = new Date();
      certificate.notBefore.value = new Date(tmp.getFullYear(), tmp.getMonth(), tmp.getDate(), 0, 0, 0);
    }

    if (certData.validity.notAfter) {
      certificate.notAfter.value = certData.validity.notAfter; //date
    } else {
      const tmp = certificate.notBefore.value;
      const validYears = certData.validity.validYears || 1;
      certificate.notAfter.value = new Date(tmp.getFullYear() + validYears, tmp.getMonth(), tmp.getDate(), 23, 59, 59);
    }
    //endregion Validity

    //region Extensions
    certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array

    //region "BasicConstraints" extension
    const basicConstr = new pkijs.BasicConstraints({
      cA: !!certData.isCA,
      //pathLenConstraint: 0 //TODO add logic for leaf CA
    });

    certificate.extensions.push(new pkijs.Extension({
      extnID: "2.5.29.19",
      critical: true,
      extnValue: basicConstr.toSchema().toBER(false),
      parsedValue: basicConstr // Parsed value for well-known extensions
    }));
    //endregion "BasicConstraints" extension

    //region "KeyUsage" extension
    const keyUsageBuffer = new ArrayBuffer(1);
    const keyUsageBitView = new Uint8Array(keyUsageBuffer);

    keyUsageBitView[0] = !!certData.isCA ? KEY_USAGE_CertificateAuthority : KEY_USAGE_LeafCertificate;

    // noinspection JSUnresolvedFunction
    const keyUsage = new asn1js.BitString({ valueHex: keyUsageBuffer });

    certificate.extensions.push(new pkijs.Extension({
      extnID: "2.5.29.15",
      critical: true,
      extnValue: keyUsage.toBER(false),
      parsedValue: keyUsage // Parsed value for well-known extensions
    }));
    //endregion "KeyUsage" extension

    //region "ExtKeyUsage" extension
    if (!certData.isCA && certData.subject.email) {
      const extKeyUsage = new pkijs.ExtKeyUsage({
        keyPurposes: [
          OID_ID_PKIX_EmailProtection
        ]
      });

      certificate.extensions.push(new pkijs.Extension({
        extnID: "2.5.29.37",
        critical: false,
        extnValue: extKeyUsage.toSchema().toBER(false),
        parsedValue: extKeyUsage // Parsed value for well-known extensions
      }));
    }
    //endregion "ExtKeyUsage" extension

    //region "SubjAltName" extension
    if (certData.subject.email || certData.subject.url) {

      const names = [];

      if (certData.subject.email) {
        names.push(new pkijs.GeneralName({
          type: 1, // rfc822Name
          value: certData.subject.email
        }));
      }

      if (certData.subject.url) {
        names.push(new pkijs.GeneralName({
          type: 2, // dNSName
          value: certData.subject.url
        }));
      }

      const subjAltNames = new pkijs.GeneralNames({
        names: names
      });

      certificate.extensions.push(new pkijs.Extension({
        extnID: "2.5.29.17",
        critical: false,
        extnValue: subjAltNames.toSchema().toBER(false),
        parsedValue: subjAltNames // Parsed value for well-known extensions
      }));
    }
    //endregion "SubjAltName" extension


    //region "SubjectKeyIdentifier" extension
    const subjKeyId = new asn1js.OctetString({ valueHex: subjKeyIdBuffer });

    certificate.extensions.push(new pkijs.Extension({
      extnID: "2.5.29.14",
      critical: false,
      extnValue: subjKeyId.toBER(false),
      parsedValue: subjKeyId // Parsed value for well-known extensions
    }));
    //endregion "SubjectKeyIdentifier" extension

    /* COULD NOT GET IT WORKING
        //region "AuthorityKeyIdentifier" extension
        if (issuerData && issuerData.certificate) {

          let issuerSubjKeyExt = null;

          let extLength = issuerData.certificate.extensions.length;
          for (var i = 0; i < extLength; i++) {
            let ext = issuerData.certificate.extensions[i];
            if (ext.extnID == "2.5.29.14") {
              issuerSubjKeyExt = ext;
              break;
            }
          }

          if (issuerSubjKeyExt) {

            const asn1 = asn1js.fromBER(issuerSubjKeyExt.extnValue);

            const authKeyIdentifier = new AuthorityKeyIdentifier({
              keyIdentifier: new asn1js.OctetString({
                //isHexOnly: true,
                //valueHex: issuerSubjKeyExt.parsedValue.valueBlock.valueHex
                value: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
              })
            });
            // const authKeyIdentifier = new AuthorityKeyIdentifier({
            // 	//keyIdentifier: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })

            // });

            certificate.extensions.push(new Extension({
              extnID: "2.5.29.35",
              critical: false,
              extnValue: authKeyIdentifier.toSchema().toBER(false),
              parsedValue: authKeyIdentifier // Parsed value for well-known extensions
            }));
          }
        }
        //endregion "AuthorityKeyIdentifier" extension
    */
    //endregion Extensions
  });
  //region Fill in cert data


  //region Signing final certificate
  sequence = sequence.then(() => {
      let signerKey = (issuerData && issuerData.privateKey) ? issuerData.privateKey : privateKey;
      //console.log(signerKey)
      return certificate.sign(signerKey, (certData.algorithms && certData.algorithms.hashAlg) ? certData.algorithms.hashAlg : defaultAlgorithms.hashAlg)
    },
    error => Promise.reject(`Error during exporting public key: ${error}`));
  //endregion

  //region Encode and store certificate
  sequence = sequence.then(() =>
  {
    //console.log(certificate)
    certificateBuffer = certificate.toSchema(true).toBER(false);
  }, error => Promise.reject(`Error during signing: ${error}`));
  //endregion

  //region Exporting public key
  sequence = sequence.then(() =>
    crypto.exportKey("spki", publicKey)
  );
  //endregion

  //region Store exported public key on Web page
  sequence = sequence.then(result =>
  {
    publicKeyBuffer = result;
  }, error => Promise.reject(`Error during exporting of public key: ${error}`));
  //endregion

  //region Exporting private key
  sequence = sequence.then(() =>
    crypto.exportKey("pkcs8", privateKey)
  );
  //endregion

  //region Store exported key on Web page
  sequence = sequence.then(result =>
  {
    privateKeyBuffer = result;
  }, error => Promise.reject(`Error during exporting of private key: ${error}`));
  //endregion

  return sequence.then(() => {

    const result = {
      certificate: certificate,
      certificatePEM: encodePEM(certificateBuffer, "CERTIFICATE"),
      publicKey: publicKey,
      publicKeyPEM: encodePEM(publicKeyBuffer, "PUBLIC KEY"),
      privateKey: privateKey,
      privateKeyPEM: encodePEM(privateKeyBuffer, "PRIVATE KEY")
Markin Igor's avatar
Markin Igor committed
    };
    return result;
  });
}

function formatPEM(pemString) {
  const lineWidth = 64;
  let resultString = "";
  let start = 0;
  let piece;
  while ((piece = pemString.substring(start, start + lineWidth)).length > 0) {
    start += lineWidth;
    resultString += piece + '\r\n';
  }
  return resultString;
}

function encodePEM(buffer, label) {
  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));

  const header = `-----BEGIN ${label}-----\r\n`;
  const base64formatted = formatPEM(window.btoa(bufferString));
  const footer = `-----END ${label}-----\r\n`;
  const resultString = header + base64formatted + footer;

  return resultString;
}

function decodePEM(pemString) {
  const pemStripped = pemString.replace(/(-----(BEGIN|END) [a-zA-Z ]*-----|\r|\n)/g, '');
  const pemDecoded = window.atob(pemStripped);
  const buffer = pvutils.stringToArrayBuffer(pemDecoded);
  return buffer;
}

//*********************************************************************************
function parseCertificate(certificatePEM) {
  const certificateBuffer = decodePEM(certificatePEM);
  const asn1 = asn1js.fromBER(certificateBuffer);
  const certificate = new pkijs.Certificate({ schema: asn1.result });
  return certificate;
}

//*********************************************************************************
function parsePublicKey(publicKeyPEM) {
  const publicKeyBuffer = decodePEM(publicKeyPEM);
  const crypto = pkijs.getCrypto();
  const publicKeyPromise = crypto.importKey(
    "spki",
    publicKeyBuffer,
    {   //these are the algorithm options
      name: "RSASSA-PKCS1-v1_5",
      hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
    },
    true,
    ["verify"]
  );
  return publicKeyPromise;
}

//*********************************************************************************
function encryptMessage(message, password, label) {
  const buffer = pvutils.stringToArrayBuffer(message);
  const secret = pvutils.stringToArrayBuffer(password);

  const enveloped = new pkijs.EnvelopedData();
  enveloped.addRecipientByPreDefinedData(secret, {}, AES_encryptionVariant_Password);
  return enveloped.encrypt(encryptionAlgorithm, buffer).
  then(
    () => {
      const content = new pkijs.ContentInfo();
      content.contentType = OID_PKCS7_EnvelopedData;
      content.content = enveloped.toSchema();
      const ber = content.toSchema().toBER(false);
      return encodePEM(ber, label)
    },
    error => Promise.reject(`encryption error: ${error}`)
  )
}

//*********************************************************************************
function decryptMessage(message, password) {
  if (canTryPincode()) {
    const secret = pvutils.stringToArrayBuffer(password);
    const buffer = decodePEM(message);

    const asn1 = asn1js.fromBER(buffer);
    const content = new pkijs.ContentInfo({schema: asn1.result});
    const enveloped = new pkijs.EnvelopedData({schema: content.content});
    return enveloped.decrypt(0, {preDefinedData: secret}).then(result => {
      return pvutils.arrayBufferToString(result);
    }).catch(() => {
      return Promise.reject(failPincodeAttempt(password));
Olgun Cengiz's avatar
Olgun Cengiz committed
    return Promise.reject(getTimeLeftInLocalStorage());
  }
}

//*********************************************************************************
function getBlockFinishTimeInLocalStorage() {
Olgun Cengiz's avatar
Olgun Cengiz committed
  return localStorage.getItem("blockFinishTime") || getCurrentTime();
}

function getCurrentTime() {
  return Math.floor(new Date().getTime() / 1000);
}

function getTimeLeftInLocalStorage() {
  const blockFinishTime = getBlockFinishTimeInLocalStorage();
  const timeNow = getCurrentTime();
  const seconds = (blockFinishTime - timeNow) % 60;
  let minutes = Math.floor((blockFinishTime - timeNow) / 60);
  minutes %= 60;

  const left = "Your identity has been locked. Try again in " + minutes + " minutes and " + seconds + " seconds.";
  return left;
}

function failPincodeAttempt(password) {
  let message = "Wrong pincode";
  if (password !== '00000000') {
    let attempt = localStorage.getItem("attempt") || 1;
    attempt = parseInt(attempt);
    if (attempt === 9) {
Olgun Cengiz's avatar
Olgun Cengiz committed
      const identitiesTemp = listIdentitiesFromLocalStorage();
Olgun Cengiz's avatar
Olgun Cengiz committed
      for (let i in identitiesTemp) {
Olgun Cengiz's avatar
Olgun Cengiz committed
        destroyIdentityFromLocalStorage(i);
      }
      message = "9 failed attempts. Identity is revoked!";
      localStorage.removeItem("attempt");
    } else if (attempt % 3 === 0) {
      const timeNow = getCurrentTime();
Olgun Cengiz's avatar
Olgun Cengiz committed
      const blockFinishTime = timeNow + 300;
      localStorage.setItem("blockFinishTime", blockFinishTime);
      localStorage.setItem("attempt", attempt + 1);
      message = "3 failed attempts. Identity is locked!";
    } else {
      localStorage.setItem("attempt", attempt + 1);
    }
  }
Olgun Cengiz's avatar
Olgun Cengiz committed
  return message;
}

function canTryPincode() {
  const timeNow = getCurrentTime();
Olgun Cengiz's avatar
Olgun Cengiz committed
  const blockFinishTime = getBlockFinishTimeInLocalStorage();
  if (blockFinishTime <= timeNow) {
    localStorage.removeItem("blockFinishTime");
    return true;
  } else {
    return false;
  }
}

//*********************************************************************************
function parsePrivateKey(privateKeyPEM) {
  const privateKeyBuffer = decodePEM(privateKeyPEM);
  const crypto = pkijs.getCrypto();
  const privateKeyPromise = crypto.importKey(
    "pkcs8",
    privateKeyBuffer,
    {   //these are the algorithm options
      name: "RSASSA-PKCS1-v1_5",
      hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
    },
    true,
    ["sign"]
  );
  return privateKeyPromise;
}


function createPassportCertificate(commonNameArg) {
  const certData = {
    algorithms: {
      hashAlg: "SHA-256",
      signAlg: "RSASSA-PKCS1-v1_5",
      keyLength: 2048
    },
    //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
    subject: {
      commonName: commonNameArg + "-userdevice", //optional for leaf, recommended for CA
      country: "CH", //optional for leaf, recommended for CA
      locality: "Zug", //optional for leaf, recommended for CA
      state: "Zug", //optional for leaf, recommended for CA
      organization: "Vereign AG", //optional for leaf, recommended for CA
      organizationUnit:"Business Dep", //optional for leaf, recommended for CA
      //email: "damyan.mitev@vereign.com", // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
      //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
    },
    validity: {
      //notBefore: new Date() // optional, defaults to today at 00:00:00
      //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
      validYears: 5 //optional, defaults to 1
    },
    isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
Markin Igor's avatar
Markin Igor committed
  };

  return createCertificate(certData, null)
}

function createOneTimePassportCertificate(commonNameArg, emailArg, privateKeyIssuerArg, certicateIssuerArg) {
Markin Igor's avatar
Markin Igor committed
  var certData = null;
Markin Igor's avatar
Markin Igor committed
  if(emailArg != null && emailArg !== "") {
    certData = {
      algorithms: {
        hashAlg: "SHA-256",
        signAlg: "RSASSA-PKCS1-v1_5",
        keyLength: 2048
      },
      //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
      subject: {
        commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
        country: "CH", //optional for leaf, recommended for CA
        locality: "Zug", //optional for leaf, recommended for CA
        state: "Zug", //optional for leaf, recommended for CA
        organization: "Vereign AG", //optional for leaf, recommended for CA
        organizationUnit:"Business Dep", //optional for leaf, recommended for CA
        email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
        //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
      },
      validity: {
        //notBefore: new Date() // optional, defaults to today at 00:00:00
        //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
        validYears: 5 //optional, defaults to 1
      },
      isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
    }
  } else {
    certData = {
      algorithms: {
        hashAlg: "SHA-256",
        signAlg: "RSASSA-PKCS1-v1_5",
        keyLength: 2048
      },
      //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
      subject: {
        commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
        country: "CH", //optional for leaf, recommended for CA
        locality: "Zug", //optional for leaf, recommended for CA
        state: "Zug", //optional for leaf, recommended for CA
        organization: "Vereign AG", //optional for leaf, recommended for CA
        organizationUnit:"Business Dep", //optional for leaf, recommended for CA
        //email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
        //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
      },
      validity: {
        //notBefore: new Date() // optional, defaults to today at 00:00:00
        //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
        validYears: 5 //optional, defaults to 1
      },
      isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
    }
  }

  return parsePrivateKey(privateKeyIssuerArg).then(privateKeyDecoded => {
    const issuerData = {
      certificate: parseCertificate(certicateIssuerArg),// vereignCACertPEM),
      privateKey: privateKeyDecoded
Markin Igor's avatar
Markin Igor committed
    };
    return createCertificate(certData, issuerData);
    //console.log(vereignIntermediateKey)
  });
}

function download(filename, contentType, text) {
  var element = document.createElement('a');
  element.setAttribute('href', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', filename);

  element.style.display = 'none';
  document.body.appendChild(element);

  element.click();

  document.body.removeChild(element);
}

function arrayBufferToBase64Formatted(buffer) {
  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
  const base64formatted = formatPEM(window.btoa(bufferString));
  return base64formatted;
}

function arrayBufferToBase64(buffer) {
  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
  const base64 = window.btoa(bufferString);
  return base64;
}

const newline = /\r\n|\r|\n/g;

function capitalizeFirstLetter(string) {
Markin Igor's avatar
Markin Igor committed
  if(string === "id") {
Markin Igor's avatar
Markin Igor committed
  if(string === "mime") {
    return "MIME";
  }

  return string.charAt(0).toUpperCase() + string.slice(1);
}

function capitalizeHeader(string) {
  let result = "";
  const tokens = string.split("-");
  for (let i = 0; i < tokens.length; i++) {
Markin Igor's avatar
Markin Igor committed
    result += capitalizeFirstLetter(tokens[i]);
    if (i !== tokens.length - 1) {
      result += "-";
}

function signEmail(mime, signingCert, certificateChain, privateKey) {
  const signingCertObj = parseCertificate(signingCert);
  const certificateChainObj = [];
Markin Igor's avatar
Markin Igor committed
  certificateChainObj[0] = parseCertificate(signingCert);
  for (let i = 0; i < certificateChain.length; i++) {
    certificateChainObj[i + 1] = parseCertificate(certificateChain[i])
  }

  return parsePrivateKey(privateKey).then(privateKeyDecoded => {
    return signEmailObjects(mime, signingCertObj, certificateChainObj, privateKeyDecoded);
  });
}

function signEmailObjects(mime, signingCert, certificateChain, privateKey) {

  //region Get a "crypto" extension
  const crypto = pkijs.getCrypto();
  if (typeof crypto === "undefined") {
    return Promise.reject("No WebCrypto extension found");
  }
  //endregion Get a "crypto" extension

  let template =
    `{{headers}}Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="{{boundary}}"
MIME-Version: 1.0

This is a cryptographically signed message in MIME format.

--{{boundary}}
{{mime}}
--{{boundary}}
Content-Type: application/pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
Content-Description: S/MIME Cryptographic Signature

{{signature}}
--{{boundary}}--

Vereign - Authentic Communication
`.replace(newline, '\r\n');

  const detachedSignature = true;
  const addExt = true;
  const hashAlg = "SHA-256";
  let cmsSignedSimpl;

  var mimeHeadersTitles = [
    "Content-Type",
    "Content-Transfer-Encoding",
    "Content-ID",
    "Content-Description",
    "Content-Disposition",
    "Content-Language",
    "Content-Location"
Markin Igor's avatar
Markin Igor committed
  ];

  mime = mime.replace(newline, '\r\n');

Markin Igor's avatar
Markin Igor committed
  let newHeaderLines = "";
  let headersEnd = mime.indexOf('\r\n\r\n'); //the first empty line

  if (headersEnd < 0 && mime.startsWith('\r\n')) {

    mime = mime.substring(2) //should not happen

  } else
  if (headersEnd >= 0) {

Markin Igor's avatar
Markin Igor committed
    let mimeHeaders = {};
    let mimeBody = mime.substring(headersEnd + 4);
Markin Igor's avatar
Markin Igor committed
    let mimeHeadersStr = mime.substring(0, headersEnd);
Markin Igor's avatar
Markin Igor committed
    let headers = libmime.decodeHeaders(mimeHeadersStr);
    for (var i = 0; i < mimeHeadersTitles.length; i++) {
Markin Igor's avatar
Markin Igor committed
      let key = mimeHeadersTitles[i].toLowerCase();
      if(key in headers) {
Markin Igor's avatar
Markin Igor committed
        mimeHeaders[key] = headers[key];
        delete headers[key]
      }
    }

    for(let key in headers) {
      if(!(key === "" || key === "MIME-Version".toLowerCase())) { //we have MIME-Version in the template
        newHeaderLines += capitalizeHeader(key) + ": " + headers[key] + '\r\n';
      }
    }

Markin Igor's avatar
Markin Igor committed
    let newMimeHeaderLines = "";
    for(let key in mimeHeaders) {
      if(!(key === "")) {
        newMimeHeaderLines += capitalizeHeader(key) + ": " + mimeHeaders[key] + '\r\n';
      }
    }

    if (newMimeHeaderLines === "") {
      newMimeHeaderLines = 'Content-Type: text/plain\r\n' //should not happen
    }

    mime = newMimeHeaderLines + '\r\n' + mimeBody
  }

Markin Igor's avatar
Markin Igor committed
  let dataBuffer = Buffer.from(mime,'utf-8');

  let sequence = Promise.resolve();

  //region Check if user wants us to include signed extensions
  if(addExt)
  {
    //region Create a message digest
    sequence = sequence.then(
      () => crypto.digest({ name: hashAlg }, dataBuffer)
    );
    //endregion

    //region Combine all signed extensions
    sequence = sequence.then(
      messageHash =>
      {
        const signedAttr = [];
        /*
                1.2.840.113549.1.9.1 - e-mailAddress
                1.2.840.113549.1.9.2 - PKCS-9 unstructuredName
                1.2.840.113549.1.9.3 - contentType
                1.2.840.113549.1.9.4 - messageDigest
                1.2.840.113549.1.9.5 - Signing Time
                1.2.840.113549.1.9.6 - counterSignature
        */
        signedAttr.push(new pkijs.Attribute({
          type: OID_PKCS9_ContentType, //contentType
          values: [
            new asn1js.ObjectIdentifier({ value: OID_PKCS7_Data}) //data
          ]
          /*
                      1.2.840.113549.1.7.1 - data
                      1.2.840.113549.1.7.2 - signedData
                      1.2.840.113549.1.7.3 - envelopedData
                      1.2.840.113549.1.7.4 - signedAndEnvelopedData
                      1.2.840.113549.1.7.5 - digestedData
                      1.2.840.113549.1.7.6 - encryptedData
          */
        })); // contentType

        signedAttr.push(new pkijs.Attribute({
          type: OID_PKCS9_SigningTime, //Signing Time
          values: [
            new asn1js.UTCTime({ valueDate: new Date() })
          ]
        })); // signingTime

        signedAttr.push(new pkijs.Attribute({
          type: OID_PKCS9_MessageDigest, //messageDigest
          values: [
            new asn1js.OctetString({ valueHex: messageHash })
          ]
        })); // messageDigest

        return signedAttr;
      }
    );
    //endregion
  }
  //endregion

  //region Initialize CMS Signed Data structures and sign it
  sequence = sequence.then(
    signedAttr =>
    {
      cmsSignedSimpl = new pkijs.SignedData({
        version: 1,
        encapContentInfo: new pkijs.EncapsulatedContentInfo({
          eContentType: OID_PKCS7_Data // "data" content type
        }),
        signerInfos: [
          new pkijs.SignerInfo({
            version: 1,
            sid: new pkijs.IssuerAndSerialNumber({
              issuer: signingCert.issuer,
              serialNumber: signingCert.serialNumber
            })
          })
        ],
        certificates: certificateChain //array
      });

      if(addExt)
      {
        cmsSignedSimpl.signerInfos[0].signedAttrs = new pkijs.SignedAndUnsignedAttributes({
          type: 0,
          attributes: signedAttr
        });
      }

      if(detachedSignature === false)
      {
        const contentInfo = new pkijs.EncapsulatedContentInfo({
          eContent: new asn1js.OctetString({ valueHex: dataBuffer })
        });

        cmsSignedSimpl.encapContentInfo.eContent = contentInfo.eContent;

        return cmsSignedSimpl.sign(privateKey, 0, hashAlg);
      }

      return cmsSignedSimpl.sign(privateKey, 0, hashAlg, dataBuffer);
    }
  );
  //endregion

  //region Create final result
  sequence = sequence.then(
    (result) =>
    {
      const cmsSignedSchema = cmsSignedSimpl.toSchema(true);

      const cmsContentSimp = new pkijs.ContentInfo({
        contentType: OID_PKCS7_SignedData, //signedData
        content: cmsSignedSchema
      });

      const _cmsSignedSchema = cmsContentSimp.toSchema();

      //region Make length of some elements in "indefinite form"
      _cmsSignedSchema.lenBlock.isIndefiniteForm = true;

      const block1 = _cmsSignedSchema.valueBlock.value[1];
      block1.lenBlock.isIndefiniteForm = true;

      const block2 = block1.valueBlock.value[0];
      block2.lenBlock.isIndefiniteForm = true;

      if(detachedSignature === false)
      {
        const block3 = block2.valueBlock.value[2];
        block3.lenBlock.isIndefiniteForm = true;
        block3.valueBlock.value[1].lenBlock.isIndefiniteForm = true;
        block3.valueBlock.value[1].valueBlock.value[0].lenBlock.isIndefiniteForm = true;
      }
      //endregion

      const cmsSignedBuffer = _cmsSignedSchema.toBER(false);
      return cmsSignedBuffer;
    },
    error => Promise.reject(`Erorr during signing of CMS Signed Data: ${error}`)
  );
  //endregion

  sequence = sequence.then(
    (cmsSignedBuffer) =>
    {
      let signature = arrayBufferToBase64Formatted(cmsSignedBuffer);
Markin Igor's avatar
Markin Igor committed
      let boundary = makeBoundary();
Markin Igor's avatar
Markin Igor committed
      template = template.replace(/{{boundary}}/g, boundary);
      template = template.replace("{{signature}}", signature);
      template = template.replace("{{headers}}", newHeaderLines);
      template = template.replace("{{mime}}", mime);

      //template = template.replace(newline, '\r\n')
      return template
    }
  );

  return sequence;
}

function makeBoundary() {
Markin Igor's avatar
Markin Igor committed
  let len = 20 + Math.random() * 20;
  return 'W0RyLiBEYW15YW4gTWl0ZXZd--' + makeid(len)
}

function makeid(len) {
  if (typeof len === 'undefined') {
    len = 10
  }
  var text = "";
  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

  for (var i = 0; i < len; i++)
    text += possible.charAt(Math.floor(Math.random() * possible.length));

  return text;
}

function CryptoData() {
}

CryptoData.prototype.set = function(obj) {
  for(var member in obj) {
    this[member] = JSON.parse(JSON.stringify(obj[member]))
  }
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.serialize = function() {
  return JSON.stringify(this)
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.deserialize = function(serialized) {
Markin Igor's avatar
Markin Igor committed
  var obj = JSON.parse(serialized);
  this.set(obj)
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.setPublicKey = function(publicKey) {
  this["publicKey"] = publicKey
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.getPublicKey = function() {
  return this["publicKey"]
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.setPrivateKey = function(privateKey) {
  this["privateKey"] = privateKey
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.getPrivateKey = function() {
  return this["privateKey"]
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.setx509Certificate = function(x509Certificate) {
  this["x509Certificate"] = x509Certificate
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.getx509Certificate = function() {
  return this["x509Certificate"]
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.setKeyUUID = function(keyUUID) {
  this["keyUUID"] = keyUUID
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.getKeyUUID = function() {
  return this["keyUUID"]
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.setChain = function(chain) {
  this["chain"] = chain
Markin Igor's avatar
Markin Igor committed
};

CryptoData.prototype.getChain = function() {
  return this["chain"]
Markin Igor's avatar
Markin Igor committed
};

function Identity() {
}

Identity.prototype.set = function(obj) {
  for(var member in obj) {
    this[member] = JSON.parse(JSON.stringify(obj[member]))
  }
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.serialize = function() {
  return JSON.stringify(this)
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.deserialize = function(serialized) {
Markin Igor's avatar
Markin Igor committed
  var obj = JSON.parse(serialized);
  this.set(obj)
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.setAuthentication = function(cryptoData) {
  this["authentication"] = cryptoData
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.getAuthentication = function() {
  return this["authentication"]
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.setPinCode = function(pinCode) {
  this["pinCode"] = pinCode
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.getPinCode = function() {
  return this["pinCode"]
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.setPassport = function(passportUUID, cryptoData) {
  if(this["passports"] === undefined || this["passports"] === null) {
    this["passports"] = {}
  }

  this["passports"][passportUUID] = cryptoData
Markin Igor's avatar
Markin Igor committed
};

Identity.prototype.getPassport = function(passportUUID) {
  if(this["passports"] === undefined || this["passports"] === null) {
    this["passports"] = {}
  }

  return this["passports"][passportUUID]
Markin Igor's avatar
Markin Igor committed
};
Markin Igor's avatar
Markin Igor committed
var identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"];

function getNextColor() {
  var colorIndex = localStorage.getItem("colorIndex");
Markin Igor's avatar
Markin Igor committed
  if (colorIndex == null || colorIndex === "") {
Markin Igor's avatar
Markin Igor committed
  var color = identityColors[colorIndex];
Markin Igor's avatar
Markin Igor committed
  colorIndex = colorIndex % identityColors.length;
Markin Igor's avatar
Markin Igor committed
  localStorage.setItem("colorIndex", colorIndex);

  return color
}

function setKeyForUUID(uuid, key) {
Markin Igor's avatar
Markin Igor committed
  var storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid);
Markin Igor's avatar
Markin Igor committed
  if(storedIdentityForUuid !== key && storedIdentityForUuid != null && storedIdentityForUuid !== "") {
    destroyIdentityFromLocalStorage(storedIdentityForUuid)
  }

  localStorage.setItem("keyperuuid/" + uuid, key)
}

function getColorForIdentity(key) {
Markin Igor's avatar
Markin Igor committed
  var storedColor = localStorage.getItem("colors/" + key);
Markin Igor's avatar
Markin Igor committed
  if(storedColor == null || storedColor === "") {
Markin Igor's avatar
Markin Igor committed
    storedColor = getNextColor();
    console.log("Setting new color: " + storedColor);
    localStorage.setItem("colors/" + key, storedColor)
  }

  return storedColor
}

function setIdentityInLocalStorage(identityToStore, extendKey = true) {
  var pinCode = identityToStore.pinCode;
  const serializedIdentity = JSON.stringify(identityToStore);
  const key = identityToStore.authentication.publicKey;

Markin Igor's avatar
Markin Igor committed
  if(pinCode == null || pinCode === "") {
    pinCode = getPincode(key)
  }

Markin Igor's avatar
Markin Igor committed
  if(pinCode == null || pinCode === "") {
Markin Igor's avatar
Markin Igor committed
    console.log("Can not set identity");
    return null;
  }

  return encryptMessage(serializedIdentity, pinCode, "identity").then((encryptedIdentity) => {
Markin Igor's avatar
Markin Igor committed
    var success = true;
    if(extendKey === true) {
      success = extendPinCodeTtl(key, pinCode)
    }
Markin Igor's avatar
Markin Igor committed
    if (success === true) {
      localStorage.setItem(key, encryptedIdentity);
      let serializedIdentitiesList = localStorage.getItem("identities");
      let identities = JSON.parse(serializedIdentitiesList);
      identities[key] = true;

      localStorage.setItem("identities", JSON.stringify(identities))
    } else {
      console.log("Can not extend pincode ttl")
    }
  });
}

function getProfileData(identity) {
  return new Penpal.Promise(executeResultUpper => {
    executeRestfulFunction("private", viamApi,
      viamApi.identityGetIdentityProfileData).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
      if(executeResult.code === "200") {
Markin Igor's avatar
Markin Igor committed
        console.log("In promise");
        console.log(executeResult);
        var listItem = {};

Markin Igor's avatar
Markin Igor committed
        console.log(identity);
        listItem.identityColor = getColorForIdentity(identity.authentication.publicKey);
        listItem.initials = executeResult.data.initials;

        if(listItem.initials === null || listItem.initials === "") {
          listItem.initials = "JD";
        }
Markin Igor's avatar
Markin Igor committed
        console.log("Item");
        console.log(listItem);
        localStorage.setItem("profiles/" + identity.authentication.publicKey, JSON.stringify(listItem));
        executeResultUpper(listItem)
      } else {
        executeResultUpper({})
      }
    });
  });
}

function getIdentityFromLocalStorage(key, pinCode, extendTtl = true) {
  const encryptedIdentity = localStorage.getItem(key);
  if (encryptedIdentity == null) {
Markin Igor's avatar
Markin Igor committed
    console.log("No such identity for public key");
    return Promise.resolve(null)
  }
  return decryptMessage(encryptedIdentity, pinCode).then((serializedIdentity) => {
    var parsedIdentity = JSON.parse(serializedIdentity);
Markin Igor's avatar
Markin Igor committed
    parsedIdentity["pinCode"] = "";
    //console.log(parsedIdentity)
    if(extendTtl === true) {
Markin Igor's avatar
Markin Igor committed
      var success = extendPinCodeTtl(key, pinCode);
Markin Igor's avatar
Markin Igor committed
      if (success === true) {
        return parsedIdentity
      } else {
Markin Igor's avatar
Markin Igor committed
        console.log("Can not extend pincode ttl");
        return null
      }
    } else {
      return parsedIdentity
    }
  });

}

function listIdentitiesFromLocalStorage() {
Markin Igor's avatar
Markin Igor committed
  var serializedIdentitiesList = localStorage.getItem("identities");
  var identities = JSON.parse(serializedIdentitiesList);
  var identitiesResult = {};

  for(var key in identities) {
Markin Igor's avatar
Markin Igor committed
    var profile = JSON.parse(JSON.stringify(localStorage.getItem("profiles/" + key)));
    console.log("Getting profile");
    console.log(profile);
Markin Igor's avatar
Markin Igor committed
    if(profile != null && profile !== "") {
Markin Igor's avatar
Markin Igor committed
      console.log("Setting profile for key: " + key);
      //console.log(profile)
      identitiesResult[key] = JSON.parse(profile)
      //console.log(identitiesResult)
    } else {
Markin Igor's avatar
Markin Igor committed
      console.log("Setting empty key for profile: " + key);
      identitiesResult[key] = {}
      //console.log(identitiesResult)
    }
  }

Markin Igor's avatar
Markin Igor committed
  console.log("In list identities from local storage");
  console.log(identitiesResult);
  return identitiesResult
}

function extendPinCodeTtl(key, pinCode) {
  //console.log("Extending pincode ttl")
  //console.log(getStack())
  //console.log("Extending pincode ttl for key: " + key)
  //console.log(pinCode)
  if(pinCode == null || pinCode === "") {
    var now = new Date();
    var nowMillis = now.getTime();
    var ttl = window.sessionStorage.getItem("pincodettls/" + key);
    if (ttl == null || ttl === "" || nowMillis >= parseInt(ttl)) {
Markin Igor's avatar
Markin Igor committed
      clearPinCodeTtl(key);
      var ttl = now.getTime() + 4 * 60 * 60 * 1000;
      window.sessionStorage.setItem("pincodettls/" + key, ttl);
    }
  } else {
    var now = new Date();
    var ttl = now.getTime() + 4 * 60 * 60 * 1000;
    window.sessionStorage.setItem("pincodettls/" + key, ttl);
    window.sessionStorage.setItem("pincodes/" + key, pinCode);
  }

  return true;
}

window.extendPinCodeTtl = extendPinCodeTtl;

function clearPinCodeTtl(key) {
  //console.log("Clearing ttl for key: " + key)
Markin Igor's avatar
Markin Igor committed
  window.sessionStorage.removeItem("pincodettls/" + key);
  window.sessionStorage.removeItem("pincodes/" + key)
}

function getPincode(key) {
  var now = new Date();
  var nowMillis = now.getTime();
  var ttl = window.sessionStorage.getItem("pincodettls/" + key);
Markin Igor's avatar
Markin Igor committed
  if (ttl == null || ttl === "") {
    return null
  } else {
    if(nowMillis >= parseInt(ttl)) {
Markin Igor's avatar
Markin Igor committed
      clearPinCodeTtl(key);
      return null
    } else {
      return window.sessionStorage.getItem("pincodes/" + key);
    }
  }
}

function createEvent(actionId, type, payloads) {
  return {
    "actionID": actionId,
    "type": type,
    "stamp": new Date().getTime(),
    "payloads" : payloads
  }
}

function destroyIdentityFromLocalStorage(key) {
Markin Igor's avatar
Markin Igor committed
  localStorage.removeItem(key);
  localStorage.removeItem("profiles/" + key);
  localStorage.removeItem("colors/" + key);
Markin Igor's avatar
Markin Igor committed
  var serializedIdentitiesList = localStorage.getItem("identities");
Markin Igor's avatar
Markin Igor committed
  var identities = JSON.parse(serializedIdentitiesList);
Markin Igor's avatar
Markin Igor committed
  identities[key] = null;
Markin Igor's avatar
Markin Igor committed
  delete identities[key];

  localStorage.setItem("identities", JSON.stringify(identities))
}

Markin Igor's avatar
Markin Igor committed
window.loadedIdentities = {};
window.wopiAPI = new WopiAPI();
window.viamApi = new ViamAPI();
window.viamAnonymousApi = new ViamAPI();
Markin Igor's avatar
Markin Igor committed
window.currentlyAuthenticatedIdentity = null;
window.currentlyLoadedIdentity = null;
window.lastTimeGetProfile = 0;
const handleIdentityLogin = async (identity, uuid, token) => {
  try {
    const { loadedIdentities, viamApi } = window;
    const { publicKey } = identity.authentication;

    const deviceHash = await createDeviceHash(publicKey);

    viamApi.setSessionData(uuid, token, deviceHash);
    localStorage.setItem("uuid", uuid);
    localStorage.setItem("token", token);
    localStorage.setItem("authenticatedIdentity", publicKey);
    window.currentlyAuthenticatedIdentity = loadedIdentities[publicKey];
    window.lastTimeGetProfile = 0;
    setKeyForUUID(uuid, publicKey);
  } catch (error) {
    console.warn(error);
  }
function executeRestfulFunction(type, that, fn, ...args) {
  const { currentlyAuthenticatedIdentity, viamApi, currentlyLoadedIdentity } = window;

  return new Penpal.Promise(executeResult => {
    fn.apply(that, args).then((response) => {
      const identity = currentlyAuthenticatedIdentity || currentlyLoadedIdentity;

      if (type === "private" && identity && response.data.code === "400" && response.data.status === "Bad session") {
        viamApi.identityLogin("previousaddeddevice")
          .then(async (response) => {
            if (response.data.code === "200") {
              const uuid = response.data.data["Uuid"];
              const token = response.data.data["Session"];
              await handleIdentityLogin(identity, uuid, token);
              // TODO: Previously there was fn.apply(null, args) where null is probably wrong context for fn.apply()
              fn.apply(that, args).then(({data}) => executeResult(data));
            } else {
              executeResult(response.data);
            }
          })
          .catch(console.warn);
      } else {
        executeResult(response.data);
}

window.executeRestfulFunction = executeRestfulFunction;

function loadIdentityInternal(identityKey, pinCode) {
  return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
    console.log("Loading identity with pincode: " + pinCode);
    getIdentityFromLocalStorage(identityKey, pinCode).then(async (loadedIdentity) => {
      if (loadedIdentity == null) {
        result({
          "data": "",
          "code": "400",
Olgun Cengiz's avatar
Olgun Cengiz committed
          "status": "Please restore or authorize your account via another device."
Olgun Cengiz's avatar
Olgun Cengiz committed
      localStorage.removeItem("attempt");
      const copiedIdentity = JSON.parse(JSON.stringify(loadedIdentity));
      window.loadedIdentities[identityKey] = loadedIdentity;

      if (identityKey === localStorage.getItem("authenticatedIdentity")) {
        window.currentlyAuthenticatedIdentity = copiedIdentity;
        window.viamApi.setIdentity(identityKey);
        const uuid = localStorage.getItem("uuid");
        const token = localStorage.getItem("token");
        const deviceHash = await createDeviceHash(identityKey);
        window.viamApi.setSessionData(uuid, token, deviceHash);
      }

      //console.log("Set loaded identity in load identity")
      window.currentlyLoadedIdentity = copiedIdentity;
      window.viamAnonymousApi.setIdentity(window.currentlyLoadedIdentity.authentication.publicKey);
Markin Igor's avatar
Markin Igor committed
      copiedIdentity.pinCode = "";
      copiedIdentity.authentication.privateKey = "";

      result({
        "data": copiedIdentity,
        "code": "200",
        "status": "Identity loaded"
    }).catch((e) => {
      result({
        "data": "",
        "code": "400",
Olgun Cengiz's avatar
Olgun Cengiz committed
        "status": "" + e
  });
}

function changeIdentityPinCodeInternal(key, oldPinCode, newPinCode) {

  return new Penpal.Promise(result => {
    getIdentityFromLocalStorage(key, oldPinCode, false).then((identity) => {

      identity.pinCode = newPinCode;

Markin Igor's avatar
Markin Igor committed
      console.log("Storing identity with pincode: " + identity.pinCode);
      setIdentityInLocalStorage(identity).then(() => {
        result({
          "data": "",
          "code": "200",
          "status": "Successfully changed pincode"
        });
      }).catch((e) => {
        result({
          "data": "",
          "code": "400",
          "status": "Cannot store identity " + e
        });
      });
    }).catch((e) => {
      result({
        "data": "",
        "code": "400",
        "status": "Cannot get identity " + e
      });
    });
  });
}

function getCertificateForPassport(passportUUID, internal) {

  return new Penpal.Promise(certificateResult => {
    if (window.currentlyAuthenticatedIdentity === null) {
      return {"data" : "",
        "code" : "400",
        "status" : "Identity not authenticated"
      }
    }

Markin Igor's avatar
Markin Igor committed
    var passportIdentity = new Identity();
    passportIdentity.set(window.currentlyAuthenticatedIdentity);
    //console.log("Getting: " + passportUUID)
    //console.log(identity)
Markin Igor's avatar
Markin Igor committed
    var passport = passportIdentity.getPassport(passportUUID);
    if(passport === undefined || passport === null) {
      createPassportCertificate(passportUUID).then(function(keys){
Markin Igor's avatar
Markin Igor committed
        var cryptoData = new CryptoData();
        //console.log(keys)
Markin Igor's avatar
Markin Igor committed
        cryptoData.setPublicKey(keys["publicKeyPEM"]);
        cryptoData.setPrivateKey(keys["privateKeyPEM"]);
        var certificate = keys["certificatePEM"];
        //download("passportCertificateBeforeSigning.crt", "text/plain", certificate)
        //console.log(certificate)
        //cryptoData.setx509Certificate(keys["certificate"])
        executeRestfulFunction("private", viamApi, viamApi.signSignCertificate, btoa(certificate), passportUUID).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          if(executeResult.code === "200") {
Markin Igor's avatar
Markin Igor committed
            var signedCertificate = atob(executeResult.data["SignedCertificate"]);
            //download("passportCertificateAfterSigning.crt", "text/plain", signedCertificate)
Markin Igor's avatar
Markin Igor committed
            var keyUUID = executeResult.data["CertificateUUID"];
            var encodedChain = executeResult.data["Chain"];
            //download("rootCertificate.crt", "text/plain", atob(encodedChain[0]))

Markin Igor's avatar
Markin Igor committed
            var chain = [];

            for(var i = 0; i < encodedChain.length; i++) {
              chain.push(atob(encodedChain[i]))
            }

            //console.log("Chain from server")
            //console.log(chain)
            //console.log(signedCertificate)
            //console.log(certificate)
            //console.log(keyUUID)
Markin Igor's avatar
Markin Igor committed
            cryptoData.setx509Certificate(signedCertificate);
            cryptoData.setKeyUUID(keyUUID);
            cryptoData.setChain(chain);
Markin Igor's avatar
Markin Igor committed
            passportIdentity.setPassport(passportUUID, cryptoData);

            getProfileData(passportIdentity).then(executeResult1 => {
Markin Igor's avatar
Markin Igor committed
              console.log("Profile updated in set identity");
              setIdentityInLocalStorage(passportIdentity).then(() => {
                window.currentlyAuthenticatedIdentity = passportIdentity;
                window.lastTimeGetProfile = 0;
                window.currentlyLoadedIdentity = passportIdentity;
                const copyOfCryptoData = JSON.parse(JSON.stringify(cryptoData));

                if (internal === false) {
                  copyOfCryptoData["privateKey"] = "";
                }

                certificateResult({
                  "data": copyOfCryptoData,
                  "code": "200",
                  "status": "Certificate got"
                });
              }).catch((e) => {
                certificateResult({
                  "data": "",
                  "code": "400",
                  "status": "Can not store certificate " + e
                });
              });
            });
          } else {
            certificateResult(executeResult);
          }
        });
      });
    } else {
      //console.log(passport)
Markin Igor's avatar
Markin Igor committed
      var copyOfCryptoData = JSON.parse(JSON.stringify(passport));

      if(internal === false) {
        copyOfCryptoData["privateKey"] = ""
      }

      certificateResult({"data" : copyOfCryptoData,
        "code" : "200",
        "status" : "Certificate got"
      });
    }
  });
}

const connection = Penpal.connectToParent({
  // Methods child is exposing to parent
  methods: {
Markin Igor's avatar
Markin Igor committed
    initialize: (apiUrl, wopiUrl) => {
      window.API_HOST = apiUrl.charAt(apiUrl.length - 1) === "/" ? apiUrl : apiUrl + "/";
Markin Igor's avatar
Markin Igor committed
      window.WOPI_URL = `${wopiUrl.charAt(wopiUrl.length - 1) === "/" ? wopiUrl : wopiUrl + "/"}getPassports`;
    createIdentity(pinCode) {
      return new Penpal.Promise(result => {
        createPassportCertificate(makeid()).then(function(keys){
Markin Igor's avatar
Markin Igor committed
          var newIdentity = new Identity();
          var cryptoData = new CryptoData();
          cryptoData.setPublicKey(keys["publicKeyPEM"]);
          cryptoData.setPrivateKey(keys["privateKeyPEM"]);
          cryptoData.setx509Certificate(keys["certificatePEM"]);
          newIdentity.setAuthentication(cryptoData);
          newIdentity.setPinCode(pinCode);

          //console.log("Set loaded identity in createIdentity")
          window.currentlyLoadedIdentity = newIdentity;
          window.loadedIdentities[newIdentity.authentication.publicKey] = newIdentity;
Markin Igor's avatar
Markin Igor committed
          extendPinCodeTtl(newIdentity.authentication.publicKey, pinCode);
          window.viamAnonymousApi.setIdentity(newIdentity.authentication.publicKey);

          result({"data" : newIdentity,
            "code" : "200",
            "status" : "Identity created"
          })
      })
    },
    listIdentities() {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        var identities = listIdentitiesFromLocalStorage();
        console.log("Before return of identities");
        console.log(identities);
        result({"data" : identities,
          "code" : "200",
          "status" : "Identities listed"
        })
      });
    },
    loadIdentity(identityKey, pinCode) {
      return loadIdentityInternal(identityKey, pinCode)
    },
    changeIdentityPinCode(key, oldPinCode, newPinCode) {
      return changeIdentityPinCodeInternal(key, oldPinCode, newPinCode)
    },
    getIdentityProfile(identityKey) {
      return new Penpal.Promise(result => {
        const serializedProfile = localStorage.getItem("profiles/" + identityKey);
        if (serializedProfile === null || serializedProfile === "") {
          result({"data" : "",
            "code" : "400",
            "status" : "Profile is empty"
          });
        } else {
          result({"data" : JSON.parse(serializedProfile),
            "code" : "200",
            "status" : "Identities cleared"
          })
        }
      });
    },
    clearIdentities() {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        var identitiesTemp = listIdentitiesFromLocalStorage();
        //console.log(identitiesTemp.length)
        for(var i in identitiesTemp) {
          destroyIdentityFromLocalStorage(i)
        }
        result({"data" : "",
          "code" : "200",
          "status" : "Identities cleared"
        })
      });
    },
    confirmIdentificator(identity, confirmationCodeArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);

        executeRestfulFunction("public", viamApi, viamApi.identityConfirmIdentificator,confirmationCodeArg).then(executeResult => {
          result(executeResult);
        });
      });
    },
    identityGetIdentificatorByRegisterToken(identity, tokenArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);

        executeRestfulFunction("public", viamApi, viamApi.identityGetIdentificatorByRegisterToken,tokenArg).then(executeResult => {
          result(executeResult);
        });
      });
    },
    submitIdentificator(identity, identificatorArg, registerToken) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);

        executeRestfulFunction("public", viamApi, viamApi.identitySubmitIdentificator,identificatorArg, registerToken).then(executeResult => {
          result(executeResult);
        });
      });
    },
    submitRegisterClaims(identity, givennameArg,familynameArg,emailArg,phonenumberArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);

        executeRestfulFunction("public", viamApi, viamApi.identitySubmitRegisterClaims,givennameArg,familynameArg,emailArg,phonenumberArg).then(executeResult => {
          result(executeResult);
        });
      });
    },
    agreeOnRegistration(registerIdentity) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(registerIdentity.authentication.publicKey);
        executeRestfulFunction("public", viamApi, viamApi.identityAgreeOnRegistration).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          console.log("Profile updated in set identity");
Markin Igor's avatar
Markin Igor committed
          let sequence = Promise.resolve();
          if (executeResult.code === "200") {
            sequence = sequence.then(() => {
                setIdentityInLocalStorage(registerIdentity)
              }
            )
          }
          sequence.then(() => {
            result(executeResult);
          }).catch((e) => {
            result({
              "data": "",
              "code": "400",
              "status": "Can not store identity: " + e
            })
          })
        });
      });
    },
    resendConfirmationCode(identity, identificatorArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);

        executeRestfulFunction("public", viamApi, viamApi.identityResendConfirmationCode,identificatorArg).then(executeResult => {
          result(executeResult);
        });
      });
    },
    login(loginIdentity, mode, code, actionID) {
      return new Penpal.Promise(async setIdentityResult => {
        if (window.loadedIdentities[loginIdentity.authentication.publicKey] === null) {
          setIdentityResult({"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          })
        }

        const deviceHash = await createDeviceHash(loginIdentity.authentication.publicKey);
        window.viamApi.setSessionData('', '', deviceHash);
        window.viamApi.setIdentity(loginIdentity.authentication.publicKey);
        executeRestfulFunction("public", viamApi, viamApi.identityLogin, mode, code, actionID).then(async executeResult => {
          // console.log(executeResult)
          //console.log(mode)
          switch (mode) {
            case "sms" : {
              if (executeResult.code === "200") {
                const uuid = executeResult.data["Uuid"];
                const token = executeResult.data["Session"];
                await handleIdentityLogin(loginIdentity, uuid, token);
Markin Igor's avatar
Markin Igor committed
                delete executeResult.data["Uuid"];
                delete executeResult.data["Session"];
                getProfileData(loginIdentity).then(executeResult1 => {
                  setIdentityInLocalStorage(loginIdentity).then(() => {
                    setIdentityResult(executeResult);
                setIdentityResult(executeResult);
              }

              break;
            }
            case "previousaddeddevice" : {
              if (executeResult.code === "200") {
                const uuid = executeResult.data["Uuid"];
                const token = executeResult.data["Session"];
                await handleIdentityLogin(loginIdentity, uuid, token);
Markin Igor's avatar
Markin Igor committed
                delete executeResult.data["Uuid"];
                delete executeResult.data["Session"];
                getProfileData(loginIdentity).then(executeResult1 => {
                  setIdentityResult(executeResult);
                setIdentityResult(executeResult);
              }

              break;
            }

            case "newdevice" : {
              if (executeResult.code === "200") {
                //console.log(executeResult)
Markin Igor's avatar
Markin Igor committed
                var actionID = executeResult.data["ActionID"];
                var QrCode = executeResult.data["QrCode"];
                //console.log(uuid + " " + token)
                QRCode.toDataURL(actionID + "," + QrCode, function (err, url) {
Markin Igor's avatar
Markin Igor committed
                  executeResult.data["image"] = url;
                  //console.log(executeResult)
                  setIdentityResult(executeResult);
                })
              } else {
                //console.log(executeResult)
                setIdentityResult(executeResult);
              setIdentityResult(executeResult);
              break;
            }
          }
        });
      });
    },
    identityAddNewDevice() {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        if (window.loadedIdentities[authenticationPublicKey] === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

Markin Igor's avatar
Markin Igor committed
        var success = extendPinCodeTtl(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
        if(success === false) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        executeRestfulFunction("private", viamApi, viamApi.identityAddNewDevice).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          if (executeResult.code === "200") {
            //console.log(response.data.data)
Markin Igor's avatar
Markin Igor committed
            var actionID = executeResult.data["ActionID"];
            var QrCode = executeResult.data["QrCode"];
            //console.log(uuid + " " + token)
            QRCode.toDataURL(actionID + "," + QrCode, function (err, url) {
Markin Igor's avatar
Markin Igor committed
              executeResult.data["image"] = url;
              result(executeResult);
            })
          } else {
            result(executeResult);
          }
        });
      });
    },
    identityDestroyKeysForDevice(authenticationPublicKeyArg) {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }
        if (window.loadedIdentities[authenticationPublicKey] === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

Markin Igor's avatar
Markin Igor committed
        var success = extendPinCodeTtl(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
        if(success === false) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        executeRestfulFunction("private", viamApi, viamApi.identityDestroyKeysForDevice, btoa(authenticationPublicKeyArg)).then(executeResult => {
          result(executeResult);
        });
      });
    },
    logout() {
      window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
      if (window.authenticationPublicKey === null) {
        return {"data" : "",
          "code" : "400",
          "status" : "Identity not loaded"
        }
      }
      if (window.loadedIdentities[authenticationPublicKey] === null) {
        return {"data" : "",
          "code" : "400",
          "status" : "Identity not loaded"
        }
      }

      return new Penpal.Promise(result => {
        executeRestfulFunction("private", viamApi, viamApi.identityLogout).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          viamApi.setIdentity("");
          viamApi.setSessionData("", "", "", "");
Markin Igor's avatar
Markin Igor committed
          clearPinCodeTtl(authenticationPublicKey);

          window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
Markin Igor's avatar
Markin Igor committed
          localStorage.removeItem("uuid");
          localStorage.removeItem("token");
          localStorage.removeItem("authenticatedIdentity");
          delete window.loadedIdentities[authenticationPublicKey];
          //console.log("Set loaded identity in logout")
          window.currentlyLoadedIdentity = null;
          window.currentlyAuthenticatedIdentity = null;
          window.lastTimeGetProfile = 0;

          result(executeResult);
        });
      });
    },
    identityRestoreAccess(restoreAccessIdentity, identificator) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(restoreAccessIdentity.authentication.publicKey);

        executeRestfulFunction("public", viamApi, viamApi.identityRestoreAccess, identificator).then(executeResult => {
            result(executeResult);
        });
      });
    },
    getCurrentlyLoggedInUUID() {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          }
        }
        if (window.loadedIdentities[authenticationPublicKey] === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          }
        }

Markin Igor's avatar
Markin Igor committed
        var success = extendPinCodeTtl(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
        if(success === false) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        if(localStorage.getItem("uuid") === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Not logged in UUID"
          })
        }
        result({"data" : localStorage.getItem("uuid"),
          "code" : "200",
          "status" : "UUID loaded"
        })
      });
    },
    getCertificateByPassport(passportUUID) {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          }
        }
        if (window.loadedIdentities[authenticationPublicKey] === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          }
        }

Markin Igor's avatar
Markin Igor committed
        var success = extendPinCodeTtl(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
        if(success === false) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        getCertificateForPassport(passportUUID, false).then(certificateResult => {
          //console.log(certificateResult)
          result(certificateResult)
        })
      });
    },
    getOneTimeCertificateByPassport(passportUUID, emailArg) {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          }
        }
        if (window.loadedIdentities[authenticationPublicKey] === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not loaded"
          }
        }

Markin Igor's avatar
Markin Igor committed
        var success = extendPinCodeTtl(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
        if(success === false) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        getCertificateForPassport(passportUUID, true).then(certificateResult => {
          //console.log(certificateResult)
Markin Igor's avatar
Markin Igor committed
          if(certificateResult.code === "200") {
Markin Igor's avatar
Markin Igor committed
            var passportCertificate = certificateResult.data["x509Certificate"];
            var passportPrivateKey = certificateResult.data["privateKey"];
            var passportChain = certificateResult.data["chain"];

            createOneTimePassportCertificate(makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate).then(function(keys){
Markin Igor's avatar
Markin Igor committed
              var publicKeyOneTime = keys["publicKeyPEM"];
              var privateKeyOneTime = keys["privateKeyPEM"];
              var certificateOneTime = keys["certificatePEM"];
              passportChain.push(passportCertificate);

              var oneTimeCryptoData = new CryptoData();
Markin Igor's avatar
Markin Igor committed
              oneTimeCryptoData.setx509Certificate(certificateOneTime);
              oneTimeCryptoData.setPrivateKey(privateKeyOneTime);
              oneTimeCryptoData.setPublicKey(publicKeyOneTime);
              oneTimeCryptoData.setChain(passportChain);

              result({"data" : oneTimeCryptoData,
                "code" : "200",
                "status" : "One time certificate generated"
              })
              // Prints PEM formatted signed certificate
              // -----BEGIN CERTIFICATE-----MIID....7Hyg==-----END CERTIFICATE-----

            });
          } else {
            result({"data" : "",
              "code" : "400",
              "status" : "Can not generate one time certificate"
            })
          }
        })
      });
    },
    signEmail(passportUUID, emailArg, emailMessage) {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          }
        }
        if (window.loadedIdentities[authenticationPublicKey] === null) {
          return {"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          }
        }

Markin Igor's avatar
Markin Igor committed
        var success = extendPinCodeTtl(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
        if(success === false) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          })
        }

        getCertificateForPassport(passportUUID, true).then(certificateResult => {
Markin Igor's avatar
Markin Igor committed
          console.log("Certificate for passport");
          console.log(certificateResult);
Markin Igor's avatar
Markin Igor committed
          if(certificateResult.code === "200") {
Markin Igor's avatar
Markin Igor committed
            var passportCertificate = certificateResult.data["x509Certificate"];
            var passportPrivateKey = certificateResult.data["privateKey"];
            var passportChain = certificateResult.data["chain"];

            createOneTimePassportCertificate(makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate).then(function(keys){
Markin Igor's avatar
Markin Igor committed
              var publicKeyOneTime = keys["publicKeyPEM"];
              var privateKeyOneTime = keys["privateKeyPEM"];
              var certificateOneTime = keys["certificatePEM"];
              //download("certificateOneTime.crt", "text/plain", certificateOneTime)

Markin Igor's avatar
Markin Igor committed
              passportChain.push(passportCertificate);

              //console.log("Before sign email")
              //console.log(certificateOneTime)
              //console.log(passportChain)
              //console.log(privateKeyOneTime)

              executeRestfulFunction("private", viamApi, viamApi.passportGetEmailWithHeaderByPassport,
                passportUUID, emailMessage).then(executeResult2 => {
Markin Igor's avatar
Markin Igor committed
                var emailWithHeader = executeResult2.data;
                //console.log(emailWithHeader)
                //download("withheader.eml", "message/rfc822", emailWithHeader)
                var signedEmailPromise = signEmail(emailWithHeader,
                  certificateOneTime,
                  passportChain,
Markin Igor's avatar
Markin Igor committed
                  privateKeyOneTime);

                signedEmailPromise.then(signedEmail => {
                  executeRestfulFunction("private", viamApi, viamApi.signResignEmail,
                    passportUUID, signedEmail).then(executeResult => {
                    result({"data" : executeResult.data,
                      "code" : "200",
                      "status" : "Email signed"
                    })
                  });
                  /*result({"data" : signedEmail,
                      "code" : "200",
                      "status" : "Email signed"
                  })*/
                });
              });
              // Prints PEM formatted signed certificate
              // -----BEGIN CERTIFICATE-----MIID....7Hyg==-----END CERTIFICATE-----

            });
          } else {
            result({"data" : "",
              "code" : "400",
              "status" : "Can not sign email"
            })
          }
        })
      });
    },
    hasSession() {
      return new Penpal.Promise(result => {
        window.authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
        if (window.authenticationPublicKey === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
          });
        }
        if (window.loadedIdentities[authenticationPublicKey] === null) {
          result({"data" : "",
            "code" : "400",
            "status" : "Identity not authenticated"
Loading
Loading full blame...