Newer
Older
import { parseSMIME, prepareVCardParts } from "../utilities/emailUtilities";
import {
stringToUtf8ByteArray,
utf8ByteArrayToString,
stringToUtf8Base64,
utf8Base64ToString,
base64ToByteArray,
byteArrayToBase64
import { extractMessageID } from "../helpers/mailparser";
Markin Igor
committed
import {
createDeviceHash,
destroyIdentityFromLocalStorage,
encodeResponse,
} from "../utilities/appUtility";
import { LOGIN_MODES } from "../constants/authentication";
CertificateData,
createOneTimePassportCertificate,
createPassportCertificate,
decryptMessage,
encryptMessage,
parseCertificate,
} from "../utilities/signingUtilities";
import { signPdf } from "../utilities/pdfUtilities";
import CryptoData from "../CryptoData";
import Identity from "../Identity";
import {
STATUS_DEVICE_REVOKED,
STATUS_USER_BLOCKED
} from "../constants/statuses";
import generateQrCode from "../utilities/generateQrCode";
getRecoveryKeyShares,
checkRecoveryKeyCombine,
const penpalMethods = require("../../temp/penpal-methods").default;
const WopiAPI = require("./wopiapi-iframe");
const CollaboraAPI = require("./collaboraapi-iframe");
const ViamAPI = require("../../temp/viamapi");
const identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"];
}
function setKeyForUUID(uuid, key) {
const storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid);
if (
storedIdentityForUuid !== key &&
storedIdentityForUuid != null &&
storedIdentityForUuid !== ""
) {
destroyIdentityFromLocalStorage(storedIdentityForUuid);
}
function getColorForIdentity(key) {
let storedColor = localStorage.getItem("colors/" + key);
}
function setIdentityInLocalStorage(identityToStore, extendKey = true) {
const serializedIdentity = JSON.stringify(identityToStore);
const key = identityToStore.authentication.publicKey;
if (pinCode == null || pinCode === "") {
pinCode = getPincode(key);
return encryptMessage(serializedIdentity, pinCode, "identity").then(
encryptedIdentity => {
if (extendKey === true) {
success = extendPinCodeTtl(key, pinCode);
}
if (success === true) {
localStorage.setItem(key, encryptedIdentity);
const serializedIdentitiesList = localStorage.getItem("identities");
const identities = JSON.parse(serializedIdentitiesList);
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,
null
).then(executeResult => {
listItem.identityColor = getColorForIdentity(
identity.authentication.publicKey
);
localStorage.setItem(
"profiles/" + identity.authentication.publicKey,
JSON.stringify(listItem)
);
executeResultUpper(listItem);
async function getIdentityFromLocalStorage(key, pinCode, extendTtl = true) {
const encryptedIdentity = localStorage.getItem(key);
if (!encryptedIdentity) {
const serializedIdentity = await decryptMessage(encryptedIdentity, pinCode);
const identity = new Identity(serializedIdentity);
if (extendTtl) {
const success = extendPinCodeTtl(key, pinCode);
if (!success) {
console.log("Can not extend pincode ttl");
return null;
}
}
return identity;
}
function extendPinCodeTtl(key, pinCode) {
const now = new Date();
const nowMillis = now.getTime();
const ttl = window.sessionStorage.getItem("pincodettls/" + key);
if (ttl == null || ttl === "" || nowMillis >= parseInt(ttl)) {
window.sessionStorage.setItem("pincodettls/" + key, ttl);
}
} else {
const now = new Date();
const ttl = now.getTime() + 4 * 60 * 60 * 1000;
window.sessionStorage.setItem("pincodettls/" + key, ttl);
window.sessionStorage.setItem("pincodes/" + key, pinCode);
}
return true;
}
window.extendPinCodeTtl = extendPinCodeTtl;
window.sessionStorage.removeItem("pincodettls/" + key);
const now = new Date();
const nowMillis = now.getTime();
const ttl = window.sessionStorage.getItem("pincodettls/" + key);
} else {
return window.sessionStorage.getItem("pincodes/" + key);
}
}
}
function createEvent(actionId, type, payloads) {
return {
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
const destroyAuthentication = () => {
const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
window.viamApi.setIdentity("");
window.viamApi.setSessionData("", "");
clearPinCodeTtl(authenticationPublicKey);
localStorage.removeItem("uuid");
localStorage.removeItem("token");
localStorage.removeItem("authenticatedIdentity");
window.currentlyAuthenticatedIdentity = null;
window.lastTimeGetProfile = 0;
};
const destroyIdentity = () => {
destroyAuthentication();
if (window.currentlyLoadedIdentity) {
const { publicKey } = window.currentlyLoadedIdentity.authentication;
delete window.loadedIdentities[publicKey];
window.currentlyLoadedIdentity = null;
destroyIdentityFromLocalStorage(publicKey);
}
};
window.wopiAPI = new WopiAPI();
window.collaboraApi = new CollaboraAPI();
window.viamApi = new ViamAPI();
window.viamAnonymousApi = new ViamAPI();
window.currentlyAuthenticatedIdentity = null;
window.currentlyLoadedIdentity = null;
window.lastTimeGetProfile = 0;
const handleIdentityLogin = (identity, uuid, token) => {
console.log(loadedIdentities[publicKey]);
console.log("handleIdentityLogin", { identity });
viamApi.setSessionData(uuid, token);
localStorage.setItem("uuid", uuid);
localStorage.setItem("token", token);
localStorage.setItem("authenticatedIdentity", publicKey);
window.lastTimeGetProfile = 0;
setKeyForUUID(uuid, publicKey);

Alexey Lunin
committed
async function executeRestfulFunction(type, that, fn, config, ...args) {
currentlyLoadedIdentity
let response;
try {
response = await fn.apply(that, [config, ...args]);
} catch (error) {
if (error.response) {
//Resposnse with status code != 2xx still has valid response
response = error.response;
} else {
//Connection error or similar
const data = {
status: error.message
const identity = currentlyAuthenticatedIdentity || currentlyLoadedIdentity;

Alexey Lunin
committed
const { code, status } = response.data;
const deviceRevoked =
type === "private" && code === "401" && status === STATUS_DEVICE_REVOKED;

Alexey Lunin
committed
if (deviceRevoked) {
destroyIdentity();

Alexey Lunin
committed
const event = createEvent("", "DeviceRevoked");
iframeParent.onEvent(event);

Alexey Lunin
committed
return response.data;
}
const userNotActivated =
type === "private" &&
code === "400" &&
(status === STATUS_USER_NOT_ACTIVATED || status === STATUS_USER_BLOCKED);
if (userNotActivated) {
destroyIdentity();
iframeParent.onEvent(event);
return response.data;
}
const badSession =
type === "private" &&
identity &&
code === "400" &&
status === "Bad session";
const loginResponse = await viamApi.identityLogin(
null,
"previousaddeddevice"
);
if (loginResponse.data.code !== "200") {
return loginResponse.data;
}
const uuid = loginResponse.data.data["Uuid"];
const token = loginResponse.data.data["Session"];
handleIdentityLogin(identity, uuid, token);
try {
response = await fn.apply(that, [config, ...args]);
} catch (error) {
response = error.response;
}
return response.data;
}
window.executeRestfulFunction = executeRestfulFunction;
function loadIdentityInternal(identityKey, pinCode) {
return new Penpal.Promise(result => {
.then(async loadedIdentity => {
if (loadedIdentity == null) {
result({
data: "",
code: "400",
status:
"Please restore or authorize your account via another device."
});
}
localStorage.removeItem("attempt");
window.loadedIdentities[identityKey] = loadedIdentity;
window.currentlyLoadedIdentity = loadedIdentity;
if (identityKey === localStorage.getItem("authenticatedIdentity")) {
window.currentlyAuthenticatedIdentity = loadedIdentity;
const uuid = localStorage.getItem("uuid");
const token = localStorage.getItem("token");
const deviceHash = await createDeviceHash(identityKey);
window.viamApi.setIdentity(identityKey);
window.viamApi.setDeviceHash(deviceHash);
window.viamApi.setSessionData(uuid, token);
}
window.viamAnonymousApi.setIdentity(
window.currentlyLoadedIdentity.authentication.publicKey
);
const { publicKey, x509Certificate } = loadedIdentity.authentication;
x509Certificate
}
status: "Identity loaded"
status: "" + e
});
}
function getCertificateForPassport(passportUUID, internal) {
return new Penpal.Promise(certificateResult => {
if (window.currentlyAuthenticatedIdentity === null) {
return { data: "", code: "400", status: "Identity not authenticated" };
const passportIdentity = window.currentlyAuthenticatedIdentity;
const passport = passportIdentity.getPassport(passportUUID);
createPassportCertificate(passportUUID).then(function (keys) {
cryptoData.setPublicKey(keys["publicKeyPEM"]);
cryptoData.setPrivateKey(keys["privateKeyPEM"]);
//download("passportCertificateBeforeSigning.crt", "text/plain", certificate)
//cryptoData.setx509Certificate(keys["certificate"])
executeRestfulFunction(
"private",
viamApi,
viamApi.signSignCertificate,
null,
btoa(certificate),
passportUUID
).then(executeResult => {
//download("passportCertificateAfterSigning.crt", "text/plain", signedCertificate)
const keyUUID = executeResult.data["CertificateUUID"];
const encodedChain = executeResult.data["Chain"];
//download("rootCertificate.crt", "text/plain", atob(encodedChain[0]))
cryptoData.setx509Certificate(signedCertificate);
cryptoData.setKeyUUID(keyUUID);
cryptoData.setChain(chain);
passportIdentity.setPassport(passportUUID, cryptoData);
getProfileData(passportIdentity).then(executeResult1 => {
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"
status: "Can not store certificate " + e
certificateResult(executeResult);
const 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: {
initialize: (apiUrl, wopiUrl, collaboraUrl) => {
Alexey Lunin
committed
if (!apiUrl) {
apiUrl = `${window.location.origin}/api/`;
console.warn(`API host URL not specified. Fall back to ${apiUrl}`); // eslint-disable-line no-console
}
if (!wopiUrl) {
wopiUrl = `${window.location.origin}/wopi/`;
console.warn(`WOPI host URL not specified. Fall back to ${wopiUrl}`); // eslint-disable-line no-console
}
if (!collaboraUrl) {
collaboraUrl = window.location.origin;
console.warn(
`Collabora host URL not specified. Fall back to ${collaboraUrl}`
); // eslint-disable-line no-console
Alexey Lunin
committed
}
window.API_HOST =
apiUrl.charAt(apiUrl.length - 1) === "/" ? apiUrl : apiUrl + "/";
window.WOPI_URL =
wopiUrl.charAt(wopiUrl.length - 1) === "/" ? wopiUrl : wopiUrl + "/";
window.COLLABORA_URL =
collaboraUrl.charAt(collaboraUrl.length - 1) === "/"
? collaboraUrl
: collaboraUrl + "/";
...penpalMethods,
return new Penpal.Promise(result => {
createPassportCertificate(makeid()).then(function (keys) {
const newIdentity = new Identity();
const cryptoData = new CryptoData();
cryptoData.setPublicKey(keys["publicKeyPEM"]);
cryptoData.setPrivateKey(keys["privateKeyPEM"]);
cryptoData.setx509Certificate(keys["certificatePEM"]);
newIdentity.setAuthentication(cryptoData);
newIdentity.setPinCode(pinCode);
window.currentlyLoadedIdentity = newIdentity;
localStorage.setItem(
"currentlyLoadedIdentity",
JSON.stringify(newIdentity)
);
const { publicKey, x509Certificate } = newIdentity.authentication;
extendPinCodeTtl(newIdentity.authentication.publicKey, pinCode);
window.viamAnonymousApi.setIdentity(
newIdentity.authentication.publicKey
);
data: {
authentication: {
publicKey,
x509Certificate
}
status: "Identity created"
return new Penpal.Promise(result => {
result({ data: identities, code: "200", status: "Identities listed" });
});
},
loadIdentity(identityKey, pinCode) {
checkIdentityPinCode: async (key, pinCode) => {
try {
const identity = await getIdentityFromLocalStorage(key, pinCode, false);
if (identity) {
return encodeResponse("200", null, "Pincode check successful");
} else {
return encodeResponse("400", null, "Pincode check failed");
}
} catch (e) {
return encodeResponse("400", e, "Pincode check error");
}
},
changeIdentityPinCode: async (key, oldPinCode, newPinCode) => {
try {
const identity = await getIdentityFromLocalStorage(
key,
oldPinCode,
false
);
if (identity) {
identity.pinCode = newPinCode;
await setIdentityInLocalStorage(identity);
window.currentlyAuthenticatedIdentity = identity;
window.currentlyLoadedIdentity = identity;
return encodeResponse("200", null, "Successfully changed pincode");
} else {
return encodeResponse("400", null, "Identity not found");
}
} catch (e) {
return encodeResponse("400", e.message, "Change pincode error");
},
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" });
result({
data: JSON.parse(serializedProfile),
code: "200",
status: "Identities cleared"
clearIdentities: async () => {
destroyAuthentication();
const identitiesTemp = listIdentitiesFromLocalStorage();
for (const i in identitiesTemp) {
destroyIdentityFromLocalStorage(i);
}
return encodeResponse("200", "", "Identities cleared");
confirmIdentificator(identity, confirmationCodeArg) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityConfirmIdentificator,
null,
confirmationCodeArg
).then(executeResult => {
result(executeResult);
});
});
},
identityGetIdentificatorByRegisterToken(identity, tokenArg) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityGetIdentificatorByRegisterToken,
null,
tokenArg
).then(executeResult => {
result(executeResult);
});
});
},
submitIdentificator(identity, identificatorArg, registerToken) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identitySubmitIdentificator,
null,
identificatorArg,
registerToken
).then(executeResult => {
result(executeResult);
});
});
},
submitRegisterClaims(
identity,
givennameArg,
familynameArg,
emailArg,
phonenumberArg
) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identitySubmitRegisterClaims,
null,
givennameArg,
familynameArg,
emailArg,
phonenumberArg
).then(executeResult => {
result(executeResult);
});
});
},
finalizeEmployeeRegistration: async (identity, identifier) => {
viamApi.setIdentity(identity.authentication.publicKey);
return executeRestfulFunction(
"public",
viamApi,
viamApi.identityFinalizeEmployeeRegistration,
null,
identifier
);
},
agreeOnRegistration(registerIdentity) {
return new Penpal.Promise(result => {
viamApi.setIdentity(registerIdentity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityAgreeOnRegistration,
null
).then(executeResult => {
if (executeResult.code === "200") {
sequence = sequence.then(() => {
status: "Can not store identity: " + e
});
});
},
resendConfirmationCode(identity, identificatorArg) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityResendConfirmationCode,
null,
identificatorArg
).then(executeResult => {
result(executeResult);
});
});
},
login: async (loginIdentity, mode, requestCode, requestActionID) => {
if (!window.loadedIdentities[loginIdentity.authentication.publicKey]) {
return {
data: "",
code: "400",
status: "Identity not loaded"
const deviceHash = await createDeviceHash(
loginIdentity.authentication.publicKey
);
window.viamApi.setSessionData("", "");
window.viamApi.setIdentity(loginIdentity.authentication.publicKey);
const identityLoginResponse = await executeRestfulFunction(
"public",
window.viamApi,
window.viamApi.identityLogin,
null,
mode,
requestCode,
requestActionID
);
const { code, data } = identityLoginResponse;
const responseToClient = Object.assign({}, identityLoginResponse);
if (code === "200") {
if (
mode === LOGIN_MODES.SMS ||
mode === LOGIN_MODES.PREVIOUSLY_ADDED_DEVICE
) {
handleIdentityLogin(loginIdentity, data.Uuid, data.Session);
if (mode === LOGIN_MODES.SMS) {
Object.assign(responseToClient.data, { image: dataUrl });
}
}
return responseToClient;
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
status: "Identity not authenticated"
if (window.loadedIdentities[authenticationPublicKey] === null) {
status: "Identity not authenticated"
const success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
executeRestfulFunction(
"private",
viamApi,
viamApi.identityAddNewDevice,
null
).then(async executeResult => {
const actionID = executeResult.data["ActionID"];
const QrCode = executeResult.data["QrCode"];
const dataUrl = await generateQrCode(actionID + "," + QrCode);
executeResult.data["image"] = dataUrl;
result(executeResult);
} else {
result(executeResult);
}
});
});
},
identityDestroyKeysForDevice(authenticationPublicKeyArg) {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
status: "Identity not authenticated"
if (window.loadedIdentities[authenticationPublicKey] === null) {
status: "Identity not authenticated"
const success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
executeRestfulFunction(
"private",
viamApi,
viamApi.identityDestroyKeysForDevice,
null,
btoa(authenticationPublicKeyArg)
).then(executeResult => {
result(executeResult);
});
});
},
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey]
) {
return {
data: "",
code: "400",
status: "Identity not loaded"
// Clone headers to be able destroy authentication first.
// We need it because clients should be able reload page right after logout invocation and not wait until request completed
destroyAuthentication();
"private",
window.viamApi,
window.viamApi.identityLogout,
status: e.message
identityRestoreAccess(restoreAccessIdentity, identificator) {
return new Penpal.Promise(result => {
viamApi.setSessionData("", "");
viamApi.setIdentity(restoreAccessIdentity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityRestoreAccess,
null,
identificator
).then(executeResult => {
result(executeResult);
});
identityInitiateSocialRecovery: async accessToken => {
const response = await executeRestfulFunction(
"public",
viamApi,
viamApi.identityInitiateSocialRecovery,
null,
accessToken
);
return response;
},
contactsCheckAccountRecoveryStatus: async () => {
const currentlyLoadedIdentity = localStorage.getItem(
"currentlyLoadedIdentity"
);
const parsedIdentity = JSON.parse(currentlyLoadedIdentity);
window.currentlyLoadedIdentity = parsedIdentity;
const { publicKey } = parsedIdentity.authentication;
window.loadedIdentities[publicKey] = parsedIdentity;
window.viamAnonymousApi.setIdentity(publicKey);
window.viamApi.setSessionData("", "");
window.viamApi.setIdentity(publicKey);
const timeout = ms => new Promise(resolve => setTimeout(resolve, ms));
async function checkAccountRecoveryStatus() {
const response = await executeRestfulFunction(
"public",
viamApi,
viamApi.contactsCheckAccountRecoveryStatus,
null
);
if (response.data === 0) {
await timeout(1000);
await checkAccountRecoveryStatus();
return;
const deviceHash = await createDeviceHash(publicKey);
window.viamApi.setDeviceHash(deviceHash);
const identityLoginResponse = await executeRestfulFunction(
"public",
window.viamApi,
window.viamApi.identityLogin,
null,
"previousaddeddevice"
);
console.log({ identityLoginResponse });
const { code, data } = identityLoginResponse;
if (code === "200") {
handleIdentityLogin(parsedIdentity, data.Uuid, data.Session);
await getProfileData(parsedIdentity);
await setIdentityInLocalStorage(parsedIdentity);
localStorage.removeItem("currentlyLoadedIdentity");
contactsGetTrusteeContactsPublicKeys: async () => {
try {
const response = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.contactsGetTrusteeContactsPublicKeys,
null
);
if (response.code !== "200") {
return response;
}
const trusteesDevices = Object.values(responseData);
/** Check if there are new trustees without added secret part */
const deviceData = Object.values(device);
return deviceData.some(data => data.hasShamir === "0");
// Generate and split recovery key
const trusteesUuids = Object.keys(responseData);
const trusteesToDevices = Object.entries(responseData);
const sharesNumber = trusteesUuids.length;
const recoveryKey = generateRecoveryKey();
const recoveryKeyShares = getRecoveryKeyShares(
recoveryKey,
sharesNumber
);
const sanityCheckResponse = checkRecoveryKeyCombine(
if (sanityCheckResponse.code !== "200") {
return sanityCheckResponse;
// Encrypt each share with every publicKey of each contact device
const shamirPartsList = await Promise.all(
trusteesToDevices.map(async ([contactUuid, device], index) => {
const deviceIdsToPublicKeys = Object.entries(device);
// Encrypt secret shares in parallel
const deviceIdsToEncryptedPartsList = await Promise.all(
deviceIdsToPublicKeys.map(async ([deviceId, { content }]) => {
const encryptedShare = await encryptShare(
recoveryKeyShares[index],
);
return [deviceId, encryptedShare];
})
);
// Turn deviceIdsToEncryptedPartsList array to object
const deviceIdsToEncryptedParts = Object.fromEntries(
deviceIdsToEncryptedPartsList
return [contactUuid, deviceIdsToEncryptedParts];
const shamirParts = Object.fromEntries(shamirPartsList);
// Save Shamir parts to database
const saveShamirPartsResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.contactsSaveShamirParts,
null,
shamirParts
);
if (saveShamirPartsResponse !== "200") {
return saveShamirPartsResponse;
}
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
return { data: "", code: "400", status: "Identity not loaded" };
if (window.loadedIdentities[authenticationPublicKey] === null) {
return { data: "", code: "400", status: "Identity not loaded" };
const success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
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 => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
return { data: "", code: "400", status: "Identity not loaded" };
if (window.loadedIdentities[authenticationPublicKey] === null) {
return { data: "", code: "400", status: "Identity not loaded" };
const success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
certificateResult => {
});
},
getOneTimeCertificateByPassport(passportUUID, emailArg) {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
return { data: "", code: "400", status: "Identity not loaded" };
if (window.loadedIdentities[authenticationPublicKey] === null) {
return { data: "", code: "400", status: "Identity not loaded" };
const success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
certificateResult => {
const passportPrivateKey = certificateResult.data["privateKey"];
const passportChain = certificateResult.data["chain"];
createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
emailArg,
passportPrivateKey,
passportCertificate
const publicKeyOneTime = keys["publicKeyPEM"];
const privateKeyOneTime = keys["privateKeyPEM"];
const certificateOneTime = keys["certificatePEM"];
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"
verifySMIME: async smimeString => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
//TODO cache (for some time) the root certificate
// either as PEM or as certificate object (preferred)
const rootCaResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.signRetrieveRootCertificate,
null
);
if (rootCaResponse.code !== "200") {
return encodeResponse("400", "", rootCaResponse.status);
}
const rootCaPem = rootCaResponse.data;
const verificationResult = await verifySMIME(smimeString, rootCaPem);
return encodeResponse(
"200",
verificationResult.verified,
verificationResult.message
);
validateDocument: async (documentUUID, contentType) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const validateDocumentResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentValidateDocumentByUUID,
null,
documentUUID,
contentType
);
if (validateDocumentResponse.code !== "200") {
return encodeResponse("400", "", validateDocumentResponse.status);
}
const signatures = validateDocumentResponse.data;
if (signatures) {
for (const signature of signatures) {
const certificateChain = signature.certificateChainPEM.map(
certificatePEM => {
const certificate = parseCertificate(certificatePEM);
const certificateData = new CertificateData(certificate);
return certificateData;
}
);
signature.certificateChain = certificateChain;
}
}
return validateDocumentResponse;
},
signEmail: async (passportUUID, emailArg, emailMessage) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
let response = await getCertificateForPassport(passportUUID, true);
if (response.code !== "200") {
return encodeResponse("400", "", response.status);
}
const {
x509Certificate: passportCertificate,
privateKey: passportPrivateKey,
chain: passportChain
} = response.data;
const keys = await createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
emailArg,
passportPrivateKey,
passportCertificate
);
certificatePEM: certificateOneTime
passportChain.push(passportCertificate);
response = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.passportGetEmailWithHeaderByPassport,
null,
passportUUID,
emailMessage
);
if (response.code !== "200") {
return encodeResponse("400", "", response.status);
}
const signedEmail = await signEmail(
response.data,
certificateOneTime,
passportChain,
privateKeyOneTime
);
response = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.signResignEmail,
null,
passportUUID,
signedEmail
);
if (response.code !== "200") {
return encodeResponse("400", "", response.status);
}
return encodeResponse("200", response.data, "Email signed");
signDocument: async (passportUUID, documentUUID, documentContentType) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const certResponse = await getCertificateForPassport(passportUUID, true);
if (certResponse.code !== "200") {
return encodeResponse("400", "", certResponse.status);
}
const {
x509Certificate: passportCertificate,
privateKey: passportPrivateKey,
chain: passportChain
const keys = await createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
null,
passportPrivateKey,
passportCertificate
);
certificatePEM: certificateOneTime
if (documentContentType !== pdfContentType) {
const convResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentConvertDocumentByUUID,
null,
documentUUID,
documentContentType,
pdfContentType
);
if (convResponse.code !== "200") {
return encodeResponse("400", "", convResponse.status);
}
}
const downloadResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentGetDocumentByUUID,
null,
documentUUID,
pdfContentType
);
if (downloadResponse.code !== "200") {
return encodeResponse("400", "", downloadResponse.status);
}
const pdfRaw = base64ToByteArray(downloadResponse.data);
signedPdf = await signPdf(
pdfRaw,
certificateOneTime,
passportChain,
privateKeyOneTime
);
} catch (err) {
console.error(err);
return encodeResponse("500", "", err.message);
}
const signedPdfB64 = byteArrayToBase64(signedPdf);
const uploadResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentPutDocumentByUUID,
null,
documentUUID,
pdfContentType,
signedPdfB64
);
if (uploadResponse.code !== "200") {
return encodeResponse("400", "", uploadResponse.status);
}
const signResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentSignDocumentByUUID,
null,
passportUUID,
documentUUID,
pdfContentType
);
if (signResponse.code !== "200") {
return encodeResponse("400", "", signResponse.status);
}
return encodeResponse("200", "", "Document signed");
signDocumentJava: async (
passportUUID,
documentUUID,
documentContentType
) => {
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const certResponse = await getCertificateForPassport(passportUUID, true);
if (certResponse.code !== "200") {
return encodeResponse("400", "", certResponse.status);
}
const {
x509Certificate: passportCertificate,
privateKey: passportPrivateKey,
chain: passportChain
} = certResponse.data;
const keys = await createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
passportPrivateKey,
passportCertificate
);
const {
privateKeyPEM: privateKeyOneTime,
certificatePEM: certificateOneTime
passportChain.push(passportCertificate);
passportChain.push(certificateOneTime);
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
const pdfContentType = "application/pdf";
if (documentContentType !== pdfContentType) {
const convResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentConvertDocumentByUUID,
null,
documentUUID,
documentContentType,
pdfContentType
);
if (convResponse.code !== "200") {
return encodeResponse("400", "", convResponse.status);
}
}
const signResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentSignDocumentJavaService,
null,
privateKeyOneTime,
passportChain,
passportUUID,
documentUUID,
pdfContentType
);
if (signResponse.code !== "200") {
return encodeResponse("400", "", signResponse.status);
}
return encodeResponse("200", "", "Document signed");
},
// passportUUID - String - passport to sign the vCard
// senderEmail - the email address of the sender
// attribs - additional attributes, to be added to the vCard. Recognized attribs are:
// - emailService: "GMail", "Outlook 365", ...
// text, html - String - the text and html part of the email, both optional
// parts - array of objects, representing the MIME structure in format:
// "Content-Type": "image/jpeg",
// "Content-Disposition": "inline" or "attachment" with additional attributes,
// ... //other headers from MIME
// body: String if it is a text part (Content-Type = "text/...") or Uint8Array otherwise; filled for leaf MIME nodes
// parts: array of instances of the same object; filled for container MIME nodes (Content-Type = "multipart/...")
signVCard: async (
passportUUID,
senderEmail,
attribs,
textBody,
htmlBody,
parts
) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const messageUUID = makeid();
const vCardAttribs = {
let vCardImageData;
let vCardImageClaimValue;
const vCardImageClaimName = "vCardImage";
const defaultTagName = "notag";
const vCardClaimResponse = await executeRestfulFunction(
vCardImageClaimName,
defaultTagName,
// if (vCardClaimResponse.code !== "200") {
// return encodeResponse("400", "", vCardClaimResponse.status);
// }
if (vCardClaimResponse.code === "200") {
vCardImageClaimValue = vCardClaimResponse.data;
}
let qrCodeCoordinates = { fromL: -1, fromR: -1, toL: -1, toR: -1 };
if (
vCardImageClaimValue &&
"state" in vCardImageClaimValue &&
vCardImageClaimValue.state === "disabled"
) {
vCardImageData = new ImageData({
contentType: "image/png",
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" //1x1px transparent pixel
});
} else {
const vCardImageResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.passportGetVCardImage,
null,
passportUUID
);
if (vCardImageResponse.code !== "200") {
return encodeResponse("400", "", vCardImageResponse.status);
}
vCardImageData = new ImageData(vCardImageResponse.data.Image);
if (vCardImageData.contentType !== "image/png") {
return encodeResponse(
"400",
"",
"Content type of vCard mmust be 'image/png'"
);
qrCodeCoordinates = vCardImageResponse.data.QRCodeCoordinates;
const qrCodeBase64Content = await generateQrCode(
"https://" + location.host + "/check/" + messageUUID
qrCodeImageData = new ImageData({
contentType: "image/png",
content: qrCodeBase64Content
if (typeof parts === "undefined" || parts === null) {
if (htmlBody) {
if (typeof htmlBody !== "string") {
throw new Error("htmlBody is not string"); // should not happen
}
"Content-Type": "text/html"
body: htmlBody
console.log("Html body is not passed to signVCard, its value is ", {
html: htmlBody
if (textBody) {
if (typeof textBody !== "string") {
throw new Error("textBody is not string"); // should not happen
}
"Content-Type": "text/plain"
body: textBody
console.log("Text body is not passed to signVCard, its value is ", {
text: textBody
const count = await prepareVCardParts(parts);
if (count.textParts === 0) {
return encodeResponse("400", "", "No text parts passed to signVCard");
}
if (count.htmlParts === 0) {
return encodeResponse("400", "", "No html parts passed to signVCard");
}
const certResponse = await getCertificateForPassport(passportUUID, true);
if (certResponse.code !== "200") {
return encodeResponse("400", "", certResponse.status);
}
const {
x509Certificate: passportCertificate,
privateKey: passportPrivateKey,
chain: passportChain
} = certResponse.data;
const keys = await createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
passportPrivateKey,
passportCertificate
);
const {
privateKeyPEM: privateKeyOneTime,
certificatePEM: certificateOneTime
} = keys;
passportChain.reverse();
passportChain.push(passportCertificate);
passportChain.push(certificateOneTime);
passportChain.reverse();
console.log(qrCodeImageData);
console.log(qrCodeCoordinates);
const signVCardResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.signSignVCardForChain,
null,
vCardImageData,
privateKeyOneTime,
passportChain,
vCardAttribs,
qrCodeImageData,
qrCodeCoordinates
);
if (signVCardResponse.code !== "200") {
return encodeResponse("400", "", signVCardResponse.status);
}
const signedVCardImageData = new ImageData(signVCardResponse.data);
return encodeResponse(
"200",
{
image: signedVCardImageData,
messageUUID: messageUUID
},
"vCard signed"
);
// mime - String - the MIME of the email message
// vCardAttribs - optional attributes for the verification procedure in format
// passportUUID: passportUUID,
// messageUUID: messageUUID
// };
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const validateVMimeResponse = await executeRestfulFunction(
window.viamApi.signValidateVMime,
if (validateVMimeResponse.code !== "200") {
return encodeResponse("400", "", validateVMimeResponse.status);
const { signatures } = validationResult;
if (signatures) {
for (const signature of signatures) {
const certificateChain = signature.certificateChainPEM.map(
certificatePEM => {
const certificate = parseCertificate(certificatePEM);
const certificateData = new CertificateData(certificate);
return certificateData;
}
);
signature.certificateChain = certificateChain;
return encodeResponse(
"200",
validationResult,
"Validation result retrieved"
);
documentCreateDocument: async (passportUUID, path, contentType, title) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
path = encodeURI(path);
contentType = encodeURI(contentType);
title = encodeURI(title);
const config = {
headers: {
path,
passportuuid: passportUUID,
contentType,
const response = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentCreateDocument,
config
);
if (response.code !== "200") {
return encodeResponse("400", "", response.status);
}
return encodeResponse("200", response.data, "Document created");
},
documentPutDocument: async (
passportUUID,
resourceid,
contentType,
) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
resourceid = encodeURI(resourceid);
contentType = encodeURI(contentType);
const config = {
headers: {
passportuuid: passportUUID,
upload
}
const response = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentPutDocument,
config,
file
);
if (response.code !== "200") {
return encodeResponse("400", "", response.status);
}
return encodeResponse("200", response.data, "Document stored");
},
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
status: "Identity not authenticated"
if (window.loadedIdentities[authenticationPublicKey] === null) {
status: "Identity not authenticated"
const success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
executeRestfulFunction(
"private",
viamApi,
viamApi.identityHasSession,
null
).then(executeResult => {
result(executeResult);
});
});
},
marketingSignUpIdentificator(identificator, reference) {
return new Penpal.Promise(result => {
executeRestfulFunction(
"public",
viamApi,
viamApi.marketingSignUpIdentificator,
null,
identificator,
reference
).then(executeResult => {
result(executeResult);
});
});
},
marketingGetIdentificatorProfile(identificator, pincode) {
return new Penpal.Promise(result => {
executeRestfulFunction(
"public",
viamApi,
viamApi.marketingGetIdentificatorProfile,
null,
identificator,
pincode
).then(executeResult => {
result(executeResult);
});
});
},
marketingExecuteEventForIdentificator(identificator, pincode, event) {
return new Penpal.Promise(result => {
executeRestfulFunction(
"public",
viamApi,
viamApi.marketingExecuteEventForIdentificator,
null,
identificator,
pincode,
event
).then(executeResult => {
result(executeResult);
});
});
},
getCurrentlyAuthenticatedIdentity() {
x509Certificate
return encodeResponse(
"200",
{
authentication: {
publicKey,
x509Certificate
}
},
"Currently authenticated identity"
);
return new Penpal.Promise(result => {
result(extractMessageID(mime));
});
},
stringToUtf8ByteArray(str) {
return new Penpal.Promise(result => {
result(stringToUtf8ByteArray(str));
});
},
utf8ByteArrayToString(ba) {
return new Penpal.Promise(result => {
result(utf8ByteArrayToString(ba));
});
},
stringToUtf8Base64(str) {
return new Penpal.Promise(result => {
Loading
Loading full blame...