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; window.axios = require('axios'); //********************************************************************************* 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"; const OID_PKCS9_SigningTime = "1.2.840.113549.1.9.5" const defaultAlgorithms = { hashAlg: "SHA-256", signAlg: "RSASSA-PKCS1-v1_5", keyLength: 2048 } 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); 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") } 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) { 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(() => { throw("Wrong pincode") }) } //********************************************************************************* 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 } return createCertificate(certData, null) } function createOneTimePassportCertificate(commonNameArg, emailArg, privateKeyIssuerArg, certicateIssuerArg) { var certData = null 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 } 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) { if(string == "id") { return "ID" } if(string == "mime") { return "MIME"; } return string.charAt(0).toUpperCase() + string.slice(1); } function capitalizeHeader(string) { result = "" tokens = string.split("-") for(var i = 0; i < tokens.length; i++) { result += capitalizeFirstLetter(tokens[i]) if(i != tokens.length - 1) { result += "-" } } return result } function signEmail(mime, signingCert, certificateChain, privateKey) { signingCertObj = parseCertificate(signingCert) certificateChainObj = [] certificateChainObj[0] = parseCertificate(signingCert) for(var i = 0; i < certificateChain.length; i++) { certificateChainObj[i + 1] = parseCertificate(certificateChain[i]) } //console.log(certificateChainObj) return parsePrivateKey(privateKey).then(privateKeyDecoded => { return signEmailObjects(mime, signingCertObj, certificateChainObj, privateKeyDecoded); //console.log(vereignIntermediateKey) }); } 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" ] mime = mime.replace(newline, '\r\n'); 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) { let mimeHeaders = {} let mimeBody = mime.substring(headersEnd + 4) let mimeHeadersStr = mime.substring(0, headersEnd) let headers = libmime.decodeHeaders(mimeHeadersStr) for (var i = 0; i < mimeHeadersTitles.length; i++) { let key = mimeHeadersTitles[i].toLowerCase() if(key in headers) { 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'; } } 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 } let dataBuffer = pvutils.stringToArrayBuffer(mime); 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 }, new Uint8Array(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); let boundary = makeBoundary() 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() { 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])) } } CryptoData.prototype.serialize = function() { return JSON.stringify(this) } CryptoData.prototype.deserialize = function(serialized) { var obj = JSON.parse(serialized) this.set(obj) } CryptoData.prototype.setPublicKey = function(publicKey) { this["publicKey"] = publicKey } CryptoData.prototype.getPublicKey = function() { return this["publicKey"] } CryptoData.prototype.setPrivateKey = function(privateKey) { this["privateKey"] = privateKey } CryptoData.prototype.getPrivateKey = function() { return this["privateKey"] } CryptoData.prototype.setx509Certificate = function(x509Certificate) { this["x509Certificate"] = x509Certificate } CryptoData.prototype.getx509Certificate = function() { return this["x509Certificate"] } CryptoData.prototype.setKeyUUID = function(keyUUID) { this["keyUUID"] = keyUUID } CryptoData.prototype.getKeyUUID = function() { return this["keyUUID"] } CryptoData.prototype.setChain = function(chain) { this["chain"] = chain } CryptoData.prototype.getChain = function() { return this["chain"] } function Identity() { } Identity.prototype.set = function(obj) { for(var member in obj) { this[member] = JSON.parse(JSON.stringify(obj[member])) } } Identity.prototype.serialize = function() { return JSON.stringify(this) } Identity.prototype.deserialize = function(serialized) { var obj = JSON.parse(serialized) this.set(obj) } Identity.prototype.setAuthentication = function(cryptoData) { this["authentication"] = cryptoData } Identity.prototype.getAuthentication = function() { return this["authentication"] } Identity.prototype.setPinCode = function(pinCode) { this["pinCode"] = pinCode } Identity.prototype.getPinCode = function() { return this["pinCode"] } Identity.prototype.setPassport = function(passportUUID, cryptoData) { if(this["passports"] === undefined || this["passports"] === null) { this["passports"] = {} } this["passports"][passportUUID] = cryptoData } Identity.prototype.getPassport = function(passportUUID) { if(this["passports"] === undefined || this["passports"] === null) { this["passports"] = {} } return this["passports"][passportUUID] } var identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"] function getNextColor() { var colorIndex = localStorage.getItem("colorIndex"); if (colorIndex == null || colorIndex == "") { colorIndex = 0 } var color = identityColors[colorIndex] colorIndex++; colorIndex = colorIndex % identityColors.length localStorage.setItem("colorIndex", colorIndex) return color } function setKeyForUUID(uuid, key) { var storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid) if(storedIdentityForUuid != key && storedIdentityForUuid != null && storedIdentityForUuid != "") { destroyIdentityFromLocalStorage(storedIdentityForUuid) } localStorage.setItem("keyperuuid/" + uuid, key) } function getColorForIdentity(key) { var storedColor = localStorage.getItem("colors/" + key) if(storedColor == null || storedColor == "") { storedColor = getNextColor() console.log("Setting new color: " + storedColor) localStorage.setItem("colors/" + key, storedColor) } return storedColor } function setIdentityInLocalStorage(identityToStore, extendKey = true) { //console.log(getStack()) var pinCode = identityToStore.pinCode; const serializedIdentity = JSON.stringify(identityToStore); const key = identityToStore.authentication.publicKey; if(pinCode == null || pinCode == "") { pinCode = getPincode(key) } if(pinCode == null || pinCode == "") { console.log("Can not set identity") return null; } return encryptMessage(serializedIdentity, pinCode, "identity").then((encryptedIdentity) => { var success = true if(extendKey === true) { success = extendPinCodeTtl(key, pinCode) } 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 => { if(executeResult.code == "200") { console.log("In promise") console.log(executeResult) var listItem = {}; console.log(identity) listItem.identityColor = getColorForIdentity(identity.authentication.publicKey) listItem.initials = executeResult.data.initials if(listItem.initials === null || listItem.initials === "") { listItem.initials = "JD"; } 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) { console.log("No such identity for public key") return Promise.resolve(null) } return decryptMessage(encryptedIdentity, pinCode).then((serializedIdentity) => { var parsedIdentity = JSON.parse(serializedIdentity); parsedIdentity["pinCode"] = "" //console.log(parsedIdentity) if(extendTtl === true) { var success = extendPinCodeTtl(key, pinCode) if (success == true) { return parsedIdentity } else { console.log("Can not extend pincode ttl") return null } } else { return parsedIdentity } }); } function listIdentitiesFromLocalStorage() { var serializedIdentitiesList = localStorage.getItem("identities") var identities = JSON.parse(serializedIdentitiesList) var identitiesResult = {} for(var key in identities) { var profile = JSON.parse(JSON.stringify(localStorage.getItem("profiles/" + key))) console.log("Getting profile") console.log(profile) if(profile != null && profile != "") { console.log("Setting profile for key: " + key) //console.log(profile) identitiesResult[key] = JSON.parse(profile) //console.log(identitiesResult) } else { console.log("Setting empty key for profile: " + key) identitiesResult[key] = {} //console.log(identitiesResult) } } console.log("In list identities from local storage") console.log(identitiesResult) return identitiesResult } function getStack() { try { throw new Error(); } catch(e) { return e.stack; } } 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)) { clearPinCodeTtl(key) return false } else { var ttl = now.getTime() + 10 * 60 * 1000; window.sessionStorage.setItem("pincodettls/" + key, ttl); } } else { var now = new Date(); var ttl = now.getTime() + 10 * 60 * 1000; window.sessionStorage.setItem("pincodettls/" + key, ttl); window.sessionStorage.setItem("pincodes/" + key, pinCode); } return true; } function clearPinCodeTtl(key) { //console.log("Clearing ttl for key: " + key) 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); if (ttl == null || ttl == "") { return null } else { if(nowMillis >= parseInt(ttl)) { 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) { localStorage.removeItem(key) localStorage.removeItem("profiles/" + key) localStorage.removeItem("colors/" + key) var serializedIdentitiesList = localStorage.getItem("identities") var identities = JSON.parse(serializedIdentitiesList) identities[key] = null delete identities[key] localStorage.setItem("identities", JSON.stringify(identities)) } window.loadedIdentities = {} window.viamApi = new ViamAPI(); window.viamAnonymousApi = new ViamAPI(); window.currentlyAuthenticatedIdentity = null window.currentlyLoadedIdentity = null window.lastTimeGetProfile = 0 function executeRestfulFunction(type, that, fn, ...args) { if(type == "private") { return new Penpal.Promise(executeResult => { fn.apply(that, args).then((response) => { if (response.data.code == "400" && response.data.status == "Bad session") { console.log("Trying to login again") if(currentlyAuthenticatedIdentity != "" && currentlyAuthenticatedIdentity != null) { viamApi.identityLogin("previousaddeddevice").then((response1) => { if (response1.data.code == "200") { //console.log(response.data.data) var uuid = response1.data.data["Uuid"] var token = response1.data.data["Session"] //console.log(uuid + " " + token) viamApi.setSessionData(uuid, token) localStorage.setItem("uuid", uuid) localStorage.setItem("token", token) localStorage.setItem("authenticatedIdentity", currentlyAuthenticatedIdentity.authentication.publicKey) currentlyAuthenticatedIdentity = loadedIdentities[currentlyAuthenticatedIdentity.authentication.publicKey] setKeyForUUID(uuid, currentlyAuthenticatedIdentity.authentication.publicKey) lastTimeGetProfile = 0; fn.apply(null, args).then((response2) => { executeResult(response2.data) }); } else { executeResult(response1.data) } }); } else { if(currentlyLoadedIdentity != "" && currentlyLoadedIdentity != null) { viamApi.identityLogin("previousaddeddevice").then((response1) => { if (response1.data.code == "200") { //console.log(response.data.data) var uuid = response1.data.data["Uuid"] var token = response1.data.data["Session"] //console.log(uuid + " " + token) viamApi.setSessionData(uuid, token) localStorage.setItem("uuid", uuid) localStorage.setItem("token", token) localStorage.setItem("authenticatedIdentity", currentlyLoadedIdentity.authentication.publicKey) currentlyAuthenticatedIdentity = loadedIdentities[currentlyLoadedIdentity.authentication.publicKey] setKeyForUUID(uuid, currentlyLoadedIdentity.authentication.publicKey) lastTimeGetProfile = 0; fn.apply(null, args).then((response2) => { executeResult(response2.data) }); } else { executeResult(response1.data) } }); } else { executeResult(response.data) } } } else { executeResult(response.data) } }); }); } else { return new Penpal.Promise(executeResult => { fn.apply(that, args).then((response) => { executeResult(response.data) }); }); } } function loadIdentityInternal(identityKey, pinCode) { return new Penpal.Promise(result => { console.log("Loading identity with pincode: " + pinCode) getIdentityFromLocalStorage(identityKey, pinCode).then((loadedIdentity) => { if (loadedIdentity == null) { result({ "data": "", "code": "400", "status": "Can not load identity" }) } var copiedIdentity = JSON.parse(JSON.stringify(loadedIdentity)) loadedIdentities[identityKey] = loadedIdentity if (identityKey === localStorage.getItem("authenticatedIdentity")) { currentlyAuthenticatedIdentity = copiedIdentity viamApi.setIdentity(identityKey) var uuid = localStorage.getItem("uuid") var token = localStorage.getItem("token") //console.log("Loading " + uuid + " " + token) viamApi.setSessionData(uuid, token) } //console.log("Set loaded identity in load identity") currentlyLoadedIdentity = copiedIdentity viamAnonymousApi.setIdentity(currentlyLoadedIdentity.authentication.publicKey) copiedIdentity.pinCode = "" copiedIdentity.authentication.privateKey = "" result({ "data": copiedIdentity, "code": "200", "status": "Identity loaded" }) }).catch((e) => { result({ "data": "", "code": "400", "status": "Can not load entity:" + e }) }) }); } function changeIdentityPinCodeInternal(key, oldPinCode, newPinCode) { return new Penpal.Promise(result => { getIdentityFromLocalStorage(key, oldPinCode, false).then((identity) => { identity.pinCode = newPinCode; console.log("Storing identity with pincode: " + identity.pinCode) setIdentityInLocalStorage(identity).then(() => { }).catch((e) => { result({ "data": "", "code": "400", "status": "Cannot store identity " + e }); }); }); }); } function getCertificateForPassport(passportUUID, internal) { return new Penpal.Promise(certificateResult => { if (currentlyAuthenticatedIdentity === null) { return {"data" : "", "code" : "400", "status" : "Identity not authenticated" } } var passportIdentity = new Identity() passportIdentity.set(currentlyAuthenticatedIdentity) //console.log("Getting: " + passportUUID) //console.log(identity) var passport = passportIdentity.getPassport(passportUUID) if(passport === undefined || passport === null) { createPassportCertificate(passportUUID).then(function(keys){ var cryptoData = new CryptoData() //console.log(keys) 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 => { if(executeResult.code == "200") { var signedCertificate = atob(executeResult.data["SignedCertificate"]) //download("passportCertificateAfterSigning.crt", "text/plain", signedCertificate) var keyUUID = executeResult.data["CertificateUUID"] var encodedChain = executeResult.data["Chain"] //download("rootCertificate.crt", "text/plain", atob(encodedChain[0])) 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) cryptoData.setx509Certificate(signedCertificate) cryptoData.setKeyUUID(keyUUID) cryptoData.setChain(chain) passportIdentity.setPassport(passportUUID, cryptoData) getProfileData(passportIdentity).then(executeResult1 => { console.log("Profile updated in set identity") setIdentityInLocalStorage(passportIdentity).then(() => { currentlyAuthenticatedIdentity = passportIdentity lastTimeGetProfile = 0; //console.log("Set loaded identity in passport"); currentlyLoadedIdentity = passportIdentity var 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) 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: { createIdentity(pinCode) { return new Penpal.Promise(result => { createPassportCertificate(makeid()).then(function(keys){ 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") currentlyLoadedIdentity = newIdentity loadedIdentities[newIdentity.authentication.publicKey] = newIdentity extendPinCodeTtl(newIdentity.authentication.publicKey, pinCode) viamAnonymousApi.setIdentity(newIdentity.authentication.publicKey) result({"data" : newIdentity, "code" : "200", "status" : "Identity created" }) }); }) }, listIdentities() { return new Penpal.Promise(result => { 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 => { 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 => { var identitiesTemp = listIdentitiesFromLocalStorage() //console.log(identitiesTemp.length) for(var i in identitiesTemp) { destroyIdentityFromLocalStorage(i) } result({"data" : "", "code" : "200", "status" : "Identities cleared" }) }); }, register(registerIdentity, email, name, surname, family, phoneNumber) { return new Penpal.Promise(result => { viamApi.setIdentity(registerIdentity.authentication.publicKey) executeRestfulFunction("public", viamApi, viamApi.identityRegister, email, name,surname, family, phoneNumber).then(executeResult => { console.log("Profile updated in set identity") 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 => { viamApi.setIdentity(identity.authentication.publicKey) executeRestfulFunction("public", viamApi, viamApi.identityResendConfirmationCode,identificatorArg).then(executeResult => { result(executeResult); }); }); }, login(loginIdentity, mode, code, actionID) { return new Penpal.Promise(result => { if (loadedIdentities[loginIdentity.authentication.publicKey] === null) { result({"data" : "", "code" : "400", "status" : "Identity not loaded" }) } //console.log("After loaded check") viamApi.setIdentity(loginIdentity.authentication.publicKey) executeRestfulFunction("public", viamApi, viamApi.identityLogin, mode, code, actionID).then(executeResult => { // console.log(executeResult) //console.log(mode) switch(mode) { case "sms" : { if (executeResult.code === "200") { //console.log("In if") var uuid = executeResult.data["Uuid"] var token = executeResult.data["Session"] viamApi.setSessionData(uuid, token) localStorage.setItem("uuid", uuid) localStorage.setItem("token", token) localStorage.setItem("authenticatedIdentity", loginIdentity.authentication.publicKey) setKeyForUUID(uuid, loginIdentity.authentication.publicKey) currentlyAuthenticatedIdentity = loadedIdentities[loginIdentity.authentication.publicKey] lastTimeGetProfile = 0; delete executeResult.data["Uuid"] delete executeResult.data["Session"] getProfileData(loginIdentity).then(executeResult1 => { result(executeResult); }); } else { //console.log("In else") result(executeResult); } break; } case "previousaddeddevice" : { if (executeResult.code === "200") { //console.log(response.data.data) var uuid = executeResult.data["Uuid"] var token = executeResult.data["Session"] //console.log(uuid + " " + token) viamApi.setSessionData(uuid, token) localStorage.setItem("uuid", uuid) localStorage.setItem("token", token) localStorage.setItem("authenticatedIdentity", loginIdentity.authentication.publicKey) setKeyForUUID(uuid, loginIdentity.authentication.publicKey) currentlyAuthenticatedIdentity = loadedIdentities[loginIdentity.authentication.publicKey] lastTimeGetProfile = 0; delete executeResult.data["Uuid"] delete executeResult.data["Session"] getProfileData(loginIdentity).then(executeResult1 => { result(executeResult); }); } else { result(executeResult); } break; } case "newdevice" : { if (executeResult.code === "200") { //console.log(executeResult) var actionID = executeResult.data["ActionID"] var QrCode = executeResult.data["QrCode"] //console.log(uuid + " " + token) QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { executeResult.data["image"] = url //console.log(executeResult) result(executeResult); }) } else { //console.log(executeResult) result(executeResult); } break; } default : { result(executeResult); break; } } }); }); }, identityAddNewDevice() { return new Penpal.Promise(result => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } if (loadedIdentities[authenticationPublicKey] === null) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } var success = extendPinCodeTtl(authenticationPublicKey) if(success == false) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } executeRestfulFunction("private", viamApi, viamApi.identityAddNewDevice).then(executeResult => { if (executeResult.code == "200") { //console.log(response.data.data) var actionID = executeResult.data["ActionID"] var QrCode = executeResult.data["QrCode"] //console.log(uuid + " " + token) QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { executeResult.data["image"] = url result(executeResult); }) } else { result(executeResult); } }); }); }, identityDestroyKeysForDevice(authenticationPublicKeyArg) { return new Penpal.Promise(result => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } if (loadedIdentities[authenticationPublicKey] === null) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } var success = extendPinCodeTtl(authenticationPublicKey) if(success == false) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } executeRestfulFunction("private", viamApi, viamApi.identityDestroyKeysForDevice, btoa(authenticationPublicKeyArg)).then(executeResult => { result(executeResult); }); }); }, logout() { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } if (loadedIdentities[authenticationPublicKey] === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } return new Penpal.Promise(result => { executeRestfulFunction("private", viamApi, viamApi.identityLogout).then(executeResult => { viamApi.setIdentity("") viamApi.setSessionData("", "") clearPinCodeTtl(authenticationPublicKey) authenticationPublicKey = localStorage.getItem("authenticatedIdentity") localStorage.removeItem("uuid") localStorage.removeItem("token") localStorage.removeItem("authenticatedIdentity") delete loadedIdentities[authenticationPublicKey] //console.log("Set loaded identity in logout") currentlyLoadedIdentity = null currentlyAuthenticatedIdentity = null lastTimeGetProfile = 0; result(executeResult); }); }); }, identityRestoreAccess(restoreAccessIdentity, identificator) { return new Penpal.Promise(result => { viamApi.setIdentity(restoreAccessIdentity.authentication.publicKey) executeRestfulFunction("public", viamApi, viamApi.identityRestoreAccess, identificator).then(executeResult => { if (executeResult.code === "200") { setIdentityInLocalStorage(restoreAccessIdentity) result(executeResult); } else { result(executeResult); } }); }); }, getCurrentlyLoggedInUUID() { return new Penpal.Promise(result => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } if (loadedIdentities[authenticationPublicKey] === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } var success = extendPinCodeTtl(authenticationPublicKey) 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 => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } if (loadedIdentities[authenticationPublicKey] === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } var success = extendPinCodeTtl(authenticationPublicKey) 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 => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } if (loadedIdentities[authenticationPublicKey] === null) { return {"data" : "", "code" : "400", "status" : "Identity not loaded" } } var success = extendPinCodeTtl(authenticationPublicKey) if(success == false) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } getCertificateForPassport(passportUUID, true).then(certificateResult => { //console.log(certificateResult) if(certificateResult.code == "200") { var passportCertificate = certificateResult.data["x509Certificate"] var passportPrivateKey = certificateResult.data["privateKey"] var passportChain = certificateResult.data["chain"] createOneTimePassportCertificate(makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate).then(function(keys){ var publicKeyOneTime = keys["publicKeyPEM"] var privateKeyOneTime = keys["privateKeyPEM"] var certificateOneTime = keys["certificatePEM"] passportChain.push(passportCertificate) var oneTimeCryptoData = new CryptoData(); 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 => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { return {"data" : "", "code" : "400", "status" : "Identity not authenticated" } } if (loadedIdentities[authenticationPublicKey] === null) { return {"data" : "", "code" : "400", "status" : "Identity not authenticated" } } var success = extendPinCodeTtl(authenticationPublicKey) if(success == false) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } getCertificateForPassport(passportUUID, true).then(certificateResult => { console.log("Certificate for passport") console.log(certificateResult) if(certificateResult.code == "200") { var passportCertificate = certificateResult.data["x509Certificate"] var passportPrivateKey = certificateResult.data["privateKey"] var passportChain = certificateResult.data["chain"] createOneTimePassportCertificate(makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate).then(function(keys){ var publicKeyOneTime = keys["publicKeyPEM"] var privateKeyOneTime = keys["privateKeyPEM"] var certificateOneTime = keys["certificatePEM"] //download("certificateOneTime.crt", "text/plain", certificateOneTime) passportChain.push(passportCertificate) //console.log("Before sign email") //console.log(certificateOneTime) //console.log(passportChain) //console.log(privateKeyOneTime) executeRestfulFunction("private", viamApi, viamApi.passportGetEmailWithHeaderByPassport, passportUUID, window.btoa(emailMessage)).then(executeResult2 => { var emailWithHeader = window.atob(executeResult2.data) //console.log(emailWithHeader) //download("withheader.eml", "message/rfc822", emailWithHeader) var signedEmailPromise = signEmail(emailWithHeader, certificateOneTime, passportChain, privateKeyOneTime) signedEmailPromise.then(signedEmail => { executeRestfulFunction("private", viamApi, viamApi.signResignEmail, passportUUID, window.btoa(signedEmail)).then(executeResult => { result({"data" : window.atob(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 => { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if (authenticationPublicKey === null) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }); } if (loadedIdentities[authenticationPublicKey] === null) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }); } var success = extendPinCodeTtl(authenticationPublicKey) if(success == false) { result({"data" : "", "code" : "400", "status" : "Identity not authenticated" }) } executeRestfulFunction("private", viamApi, viamApi.identityHasSession).then(executeResult => { result(executeResult); }); }); }, marketingSignUpIdentificator(identificator, reference) { return new Penpal.Promise(result => { viamApi.setIdentity("marketingapppublickey") executeRestfulFunction("public", viamApi, viamApi.marketingSignUpIdentificator, identificator, reference).then(executeResult => { viamApi.setIdentity("") viamApi.setSessionData("", "") result(executeResult); }); }); }, marketingGetIdentificatorProfile(identificator, pincode) { return new Penpal.Promise(result => { viamApi.setIdentity("marketingapppublickey") executeRestfulFunction("public", viamApi, viamApi.marketingGetIdentificatorProfile, identificator, pincode).then(executeResult => { viamApi.setIdentity("") viamApi.setSessionData("", "") result(executeResult); }); }); }, marketingЕxecuteEventForIdentificator(identificator, pincode, event) { return new Penpal.Promise(result => { viamApi.setIdentity("marketingapppublickey") executeRestfulFunction("public", viamApi, viamApi.marketingExecuteEventForIdentificator, identificator, pincode, event).then(executeResult => { viamApi.setIdentity("") viamApi.setSessionData("", "") result(executeResult); }); }); }, getCurrentlyAuthenticatedIdentity() { return new Penpal.Promise(result => { result({"data" : currentlyAuthenticatedIdentity, "code" : "200", "status" : "Currently authenticated identity" }) }); }, //{{methods}} } }); connection.promise.then(parent => { var identities = localStorage.getItem("identities") console.log("Library loaded at: " + new Date().toISOString()) if (identities === "" || identities === null) { //console.log("Setting up empty version") localStorage.setItem("identities", JSON.stringify({})) } if (localStorage.getItem("uuid") === null || localStorage.getItem("token") === null || localStorage.getItem("authenticatedIdentity") === null) { localStorage.removeItem("uuid") localStorage.removeItem("token") localStorage.removeItem("authenticatedIdentity") } else { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") var pinCode = getPincode(authenticationPublicKey) if(pinCode == "" || pinCode == null) { loadIdentityInternal(authenticationPublicKey, "00000000").then(result => { if(result.code != "200") { console.log(result) //var event = createEvent("CanNotLoadIdentity", "ErrorDuringLoadingIdentity", [authenticationPublicKey]) //parent.onEvent(event) var event = createEvent("CanNotGetPincodeForAuthenticatedIdentity", "IdentityNotLoaded", [authenticationPublicKey]) parent.onEvent(event) } }); //var event = createEvent("CanNotGetPincodeForAuthenticatedIdentity", "IdentityNotLoaded", [authenticationPublicKey]) //parent.onEvent(event) } else { loadIdentityInternal(authenticationPublicKey, pinCode).then(result => { if(result.code != "200") { var event = createEvent("CanNotLoadIdentity", "ErrorDuringLoadingIdentity", [authenticationPublicKey]) parent.onEvent(event) } }); } } var anynomousDeviceKeyEventsProcessing = false var maxDeviceKeyAnonymousEventTime = 0 var eventsDeviceEventsProcessing = false var maxDeviceKeyEventTime = 0 var eventsEntityEventsProcessing = false var maxEntityEventTime = 0 var identityLoadedEvent = false var identityAuthenticatedEvent = false setInterval(function() { if(currentlyAuthenticatedIdentity != null) { var pinCode = getPincode(currentlyAuthenticatedIdentity.authentication.publicKey) if(pinCode != null && pinCode != "") { getIdentityFromLocalStorage(currentlyAuthenticatedIdentity.authentication.publicKey, pinCode, false).then((gotIdentity) => { currentlyAuthenticatedIdentity = gotIdentity //console.log("Set loaded identity in pincode check interval") currentlyLoadedIdentity = gotIdentity if(identityAuthenticatedEvent === false && gotIdentity != null) { var event = createEvent("IdentityAuthenticated", "Authenticated", [gotIdentity.authentication.publicKey]) parent.onEvent(event) identityAuthenticatedEvent = true } }) } else { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") if(authenticationPublicKey != null && authenticationPublicKey != "") { /*var event = createEvent("CanNotLoadPincodeForAuthenticatedIdentity", "IdentityNotLoaded", [currentlyAuthenticatedIdentity.authentication.publicKey]) parent.onEvent(event) clearPinCodeTtl(authenticationPublicKey) currentlyAuthenticatedIdentity = null*/ loadIdentityInternal(authenticationPublicKey, "00000000").then(result => { if(result.code != "200") { console.log(result) var event = createEvent("CanNotGetPincodeForAuthenticatedIdentity", "IdentityNotLoaded", [authenticationPublicKey]) parent.onEvent(event) clearPinCodeTtl(authenticationPublicKey) currentlyAuthenticatedIdentity = null } }); } identityAuthenticatedEvent = false //console.log("Set loaded identity in cycle for not authenticated value") currentlyLoadedIdentity = null } } if(currentlyLoadedIdentity != null) { var pinCode = getPincode(currentlyLoadedIdentity.authentication.publicKey) if(pinCode == "" || pinCode == null) { if(identityLoadedEvent === false) { /*var event = createEvent("CanNotLoadPincodeForLoadedIdentity", "IdentityNotLoaded", [currentlyLoadedIdentity.authentication.publicKey]) parent.onEvent(event) identityLoadedEvent = true*/ loadIdentityInternal(currentlyLoadedIdentity.authentication.publicKey, "00000000").then(result => { if(result.code != "200") { var event = createEvent("CanNotLoadPincodeForLoadedIdentity", "IdentityNotLoaded", [currentlyLoadedIdentity.authentication.publicKey]) parent.onEvent(event) identityLoadedEvent = true } }); } } else { identityLoadedEvent = false } } if (currentlyAuthenticatedIdentity != null) { var now = new Date().getTime() if(now - lastTimeGetProfile > 30000) { var identityToStore = currentlyAuthenticatedIdentity getProfileData(identityToStore).then(executeResult => { console.log("Profile updated in cycle") console.log(executeResult) }); lastTimeGetProfile = now } } }, 50) setInterval(function() { if (currentlyLoadedIdentity != null && anynomousDeviceKeyEventsProcessing == false) { anynomousDeviceKeyEventsProcessing = true executeRestfulFunction("public", viamAnonymousApi, viamAnonymousApi.eventGetNewEventsWithoutSession, "devicekey").then(executeResult => { if(executeResult.code == "200") { var eventsLen = executeResult.data.length changedMaxDeviceKeyAnonymousEventTime = false for(var i = 0; i < eventsLen; i++) { var event = executeResult.data[i] //console.log("Received event anynomous: " + event) switch(event.type) { case "Authenticated" : { console.log("Sending authenticated event") var uuid = event.payloads[0] var token = event.payloads[1] viamApi.setSessionData(uuid, token) localStorage.setItem("uuid", uuid) localStorage.setItem("token", token) localStorage.setItem("authenticatedIdentity", currentlyLoadedIdentity.authentication.publicKey) setKeyForUUID(uuid, currentlyLoadedIdentity.authentication.publicKey) currentlyAuthenticatedIdentity = currentlyLoadedIdentity lastTimeGetProfile = 0; var identityToStore = currentlyAuthenticatedIdentity event.payloads = [] console.log(identityToStore) setIdentityInLocalStorage(identityToStore).then(() => { console.log(identityToStore) getProfileData(identityToStore).then(executeResult2 => { console.log("Profile updated in setInterval function") console.log(executeResult2) parent.onEvent(event) }); }); break; } case "QRCodeUpdated" : { var actionID = event["actionID"] var QrCode = event["payloads"][1] var eventCopy = JSON.parse(JSON.stringify(event)) QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { eventCopy["payloads"].push(url) parent.onEvent(eventCopy) }) break } case "KeyDeleted" : { authenticationPublicKey = localStorage.getItem("authenticatedIdentity") clearPinCodeTtl(authenticationPublicKey) localStorage.removeItem("uuid") localStorage.removeItem("token") localStorage.removeItem("authenticatedIdentity") delete loadedIdentities[authenticationPublicKey] //console.log("Set loaded identity in key deleted") currentlyLoadedIdentity = null currentlyAuthenticatedIdentity = null lastTimeGetProfile = 0; destroyIdentityFromLocalStorage(authenticationPublicKey) break } default : { parent.onEvent(event) } } changedMaxDeviceKeyAnonymousEventTime = true maxDeviceKeyAnonymousEventTime = Math.max(maxDeviceKeyAnonymousEventTime, event.stamp) } if(changedMaxDeviceKeyAnonymousEventTime) { //console.log("Updating max time anonymous device with " + maxDeviceKeyAnonymousEventTime ) executeRestfulFunction("public", viamAnonymousApi, viamAnonymousApi.eventUpdateLastViewedWithoutSession, "devicekey", maxDeviceKeyAnonymousEventTime.toString()).then(executeResult1 => { anynomousDeviceKeyEventsProcessing = false }) } else { anynomousDeviceKeyEventsProcessing = false } } else { anynomousDeviceKeyEventsProcessing = false //console.log("Error during query anynomous device") } }); } /*if(currentlyAuthenticatedIdentity === null) { console.log("Currently authenticated identity is null") }*/ if (currentlyAuthenticatedIdentity != null && eventsDeviceEventsProcessing == false) { eventsDeviceEventsProcessing = true executeRestfulFunction("private", viamApi, viamApi.eventGetNewEvents, "devicekey").then(executeResult => { if(executeResult.code == "200") { var eventsLen = executeResult.data.length changedMaxDeviceKeyEventTime = false for(var i = 0; i < eventsLen; i++) { var event = executeResult.data[i] //console.log("Received event device key: " + event) if(event.type == "QRCodeUpdated") { var actionID = event["actionID"] var QrCode = event["payloads"][1] var eventCopy = JSON.parse(JSON.stringify(event)) QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { eventCopy["payloads"].push(url) parent.onEvent(eventCopy) }) } else { parent.onEvent(event) } maxDeviceKeyEventTime = Math.max(maxDeviceKeyEventTime, event.stamp) } if(changedMaxDeviceKeyEventTime) { //console.log("Updating max time authenticated device") executeRestfulFunction("private", viamApi, viamApi.eventUpdateLastViewed, "devicekey", maxDeviceKeyEventTime.toString()).then(executeResult1 => { eventsDeviceEventsProcessing = false }) } else { eventsDeviceEventsProcessing = false } } else { //console.log("Error during query device") eventsDeviceEventsProcessing = false } }); } if (currentlyAuthenticatedIdentity != null && eventsEntityEventsProcessing == false) { eventsEntityEventsProcessing = true executeRestfulFunction("private", viamApi, viamApi.eventGetNewEvents, "entity").then(executeResult => { if(executeResult.code == "200") { var eventsLen = executeResult.data.length changedMaxEntityEventTime = false for(var i = 0; i < eventsLen; i++) { event = executeResult.data[i] if(event.type == "QRCodeUpdated") { var actionID = event["actionID"] var QrCode = event["payloads"][1] var eventCopy = JSON.parse(JSON.stringify(event)) QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { eventCopy["payloads"].push(url) parent.onEvent(eventCopy) }) continue } //console.log("Received event entity: " + event) parent.onEvent(event) changedMaxEntityEventTime = true //console.log(maxEntityEventTime + " " + event.stamp) maxEntityEventTime = Math.max(maxEntityEventTime, event.stamp) } if(changedMaxEntityEventTime) { //console.log("Updating max time entity" + maxEntityEventTime) executeRestfulFunction("private", viamApi, viamApi.eventUpdateLastViewed, "entity", maxEntityEventTime.toString()).then(executeResult1 => { eventsEntityEventsProcessing = false }) } else { eventsEntityEventsProcessing = false } } else { //console.log("Error during query entity") eventsEntityEventsProcessing = false } }); } }, 1000); });