Newer
Older
console.log("before checkAccountRecoveryStatus");
console.log({ 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"
);
const { code, data } = identityLoginResponse;
if (code === "200") {
await setIdentityInLocalStorage(parsedIdentity);
handleIdentityLogin(parsedIdentity, data.Uuid, data.Session);
await getProfileData(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();
let recoveryKeyShares = [recoveryKey];
// Split the secret when sharesNumber is more than 1 because VereignPublicKey is always returned
if (sharesNumber > 1) {
recoveryKeyShares = getRecoveryKeyShares(recoveryKey, sharesNumber);
const sanityCheckResponse = checkRecoveryKeyCombine(
recoveryKey,
recoveryKeyShares
);
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.code !== "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");
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
/**
* Checks the state of the vCard. Current states are "disabled" and "enabled".
* @param passportUUID
* @returns {Promise<{code: *, data: *, status: *}>}
*/
async checkVCardState(passportUUID) {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
let vCardImageClaimValue;
const vCardImageClaimName = "vCardImage";
const defaultTagName = "notag";
const vCardClaimResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.entityGetClaim,
null,
vCardImageClaimName,
defaultTagName,
passportUUID
);
// You may not have vCard image claim, but backend will still return blank image from passportGetVCardImage
// if (vCardClaimResponse.code !== "200") {
// return encodeResponse("400", "", vCardClaimResponse.status);
// }
if (vCardClaimResponse.code === "200") {
vCardImageClaimValue = vCardClaimResponse.data;
}
if (vCardImageClaimValue && "state" in vCardImageClaimValue) {
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
1613
1614
1615
1616
1617
1618
1619
1620
1621
return encodeResponse("200", vCardImageClaimValue.state, "OK");
}
return encodeResponse("200", "enabled", "OK");
},
/**
*
* @param passportUUID
* @param documentUUID
* @param documentContentType
* @param signatureData -
* @returns {Promise<{code: *, data: *, status: *}>}
*
// Image position in pixels
message Position {
int32 left = 1; // x
int32 top = 2; // y
}
// Image size in pixels
message Size {
uint32 width = 1;
uint32 height = 2;
}
// Image size in pixels
message Bounds {
int32 left = 1; // x
int32 top = 2; // y
uint32 width = 3;
uint32 height = 4;
}
message SignatureData {
ImageData signatureImageData = 1;
Bounds signatureBounds = 2;
uint32 pageNumber = 3;
Size pageSize = 4;
}
*
*/
signDocumentJava: async (
passportUUID,
documentUUID,
documentContentType,
signatureData = null,
qrCodeUrl = null
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
// Get vCard and QR Code Coordinates
let vCardImageData;
let vCardImageClaimValue;
let qrCodeImageData;
let qrCodeCoordinates = { fromL: -1, fromR: -1, toL: -1, toR: -1 };
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
if (signatureData) {
const vCardImageClaimName = "vCardImage";
const defaultTagName = "notag";
const vCardClaimResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.entityGetClaim,
null,
vCardImageClaimName,
defaultTagName,
passportUUID
);
// if (vCardClaimResponse.code !== "200") {
// return encodeResponse("400", "", vCardClaimResponse.status);
// }
if (vCardClaimResponse.code === "200") {
vCardImageClaimValue = vCardClaimResponse.data;
}
if (
vCardImageClaimValue &&
"state" in vCardImageClaimValue &&
vCardImageClaimValue.state === "disabled"
) {
vCardImageData = 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 must be 'image/png'"
);
}
if (qrCodeUrl) {
qrCodeCoordinates = vCardImageResponse.data.QRCodeCoordinates;
const qrCodeBase64Content = await generateQrCode(qrCodeUrl);
qrCodeImageData = new ImageData({
contentType: "image/png",
content: qrCodeBase64Content
});
}
}
// Fill signature data onject
signatureData.signatureImageData = vCardImageData;
}
// Generate certificate chain
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);
// eventually convert document
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);
}
}
// Sign document
const signResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentSignDocumentJavaService,
null,
privateKeyOneTime,
passportChain,
passportUUID,
documentUUID,
pdfContentType,
signatureData,
qrCodeImageData,
qrCodeCoordinates
);
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;
let qrCodeImageData;
let qrCodeCoordinates = { fromL: -1, fromR: -1, toL: -1, toR: -1 };
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;
}
if (
vCardImageClaimValue &&
"state" in vCardImageClaimValue &&
vCardImageClaimValue.state === "disabled"
) {
vCardImageData = 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 must 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();
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