Skip to content
Snippets Groups Projects

Resolve "[Document Sealing] Add document proto buffers"

Merged Zdravko Iliev requested to merge 28-document-sealing-add-document-proto-buffers into master
1 unresolved thread
Compare and
14 files
+ 1932
465
Compare changes
  • Side-by-side
  • Inline
Files
14
import { describe, it, expect } from "@jest/globals";
import QrCodeDataService from "../src/services/QrCodeDataService";
import { CryptoService } from "../src/index";
import { CommonUtils, CryptoService } from "../src/index";
import Utils, {
base64ToArrayBuffer,
arrayBufferToBase64,
} from "../src/utils/common";
import { MessageData } from "../src";
import { MessageData, DocumentData } from "../src";
const emailData: MessageData = {
statusId: "123",
sender: {
name: "Arty",
email: "arty@mail.ru",
@@ -42,118 +41,304 @@ const emailData: MessageData = {
senderPublicKeyUuid: "<uuid>",
};
const documentData: DocumentData = {
author: "Craig McCracken",
documentTitle: "The_Powerpuff_Girls",
documentDescription: "cartoon for kids",
creationDate: new Date().toDateString(),
documentPages: 10,
ipfs: {
head: "hhdsgaxcg=",
key: "acasdca=", //encoded key pair ( seal doc key and seal doc iv)
cid: "QmYe9irfecLtwYMbTxELYNBD1buPQgZYe6XJBaPSXvkvkA",
},
};
const redisMock = {};
const ipfsMock = {};
const ipfsSealTailCidMock = "my-random-cid";
const ipfsIndexObjectCidMock = "my-index-object-mock";
const proxyServiceResponseMock = {
id: {
docHash: "hhg=",
sealHash: "hhgcvb=",
},
hashBatch: "xpvgg=",
blockchainIdentifier: "_ae",
blockHeight: "546641",
transactionId: "th_eQ4k1qfKtumhDa3k1mMJUFN9oT7ZNLhfUp1PiHHyw2uF2icGt",
};
/**
* Algorithm spec
* https://code.vereign.com/internal/product/-/issues/70#note_56356
*/
describe("QrCodeDataService", () => {
it("performs the whole QR code data breakage/assembling routine", async () => {
/**
* Encode
*/
const encodedEmailData = QrCodeDataService.encodeEmailData(emailData);
const compressedEmailData = Utils.compressData(encodedEmailData);
const {
data,
iv: sessionIv,
key: sessionKey,
} = await CryptoService.encryptAESGCM(
arrayBufferToBase64(compressedEmailData)
);
// 32 is a head bytes size. It's the minimum, and the bigger is better.
// Increase it as long as you can maintain good appearance of the QR code
const { head, tail } = QrCodeDataService.breakQrCodeData(data, 32);
const encodedSessionKey = QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(sessionKey),
data: arrayBufferToBase64(sessionIv),
});
describe("Email", () => {
it("performs the whole QR code data breakage/assembling routine", async () => {
/**
* Encode
*/
const encodedEmailData = QrCodeDataService.encodeEmailData(emailData);
const compressedEmailData = Utils.compressData(encodedEmailData);
const {
data,
iv: sessionIv,
key: sessionKey,
} = await CryptoService.encryptAESGCM(
arrayBufferToBase64(compressedEmailData)
);
// 32 is a head bytes size. It's the minimum, and the bigger is better.
// Increase it as long as you can maintain good appearance of the QR code
const { head, tail } = QrCodeDataService.breakQrCodeData(data, 32);
const encodedSessionKey = QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(sessionKey),
data: arrayBufferToBase64(sessionIv),
});
// In client apps, RSA encryption happens using HSM service.
const { privateKeyPEM, publicKeyPEM } =
await CryptoService.generateRSAKeys();
const encryptedSessionKey = await CryptoService.encryptRSA(
publicKeyPEM,
base64ToArrayBuffer(encodedSessionKey)
);
const {
data: doubleEncryptedSessionKey,
iv: storageIv,
key: storageKey,
} = await CryptoService.encryptAESGCM(
arrayBufferToBase64(encryptedSessionKey)
);
// Put base64 of this into QR code data as `?q=` param
const encodedStorageKeyAndHead = QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(storageKey),
data: arrayBufferToBase64(head),
});
const encodedDoubleEncryptedSessionKey =
QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(doubleEncryptedSessionKey),
data: arrayBufferToBase64(storageIv),
});
// Send this to putQRCodeData api
const backblazeData = {
sessionKey: encodedDoubleEncryptedSessionKey,
tail: arrayBufferToBase64(tail),
};
/**
* Decode
*/
// In client apps, RSA encryption happens using HSM service.
const {
privateKeyPEM,
publicKeyPEM,
} = await CryptoService.generateRSAKeys();
const encryptedSessionKey = await CryptoService.encryptRSA(
publicKeyPEM,
base64ToArrayBuffer(encodedSessionKey)
);
const {
data: doubleEncryptedSessionKey,
iv: storageIv,
key: storageKey,
} = await CryptoService.encryptAESGCM(
arrayBufferToBase64(encryptedSessionKey)
);
// Put base64 of this into QR code data as `?q=` param
const encodedStorageKeyAndHead = QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(storageKey),
data: arrayBufferToBase64(head),
// Retrieve base64 of this from QR code URL
const decodedStorageKeyAndHead = QrCodeDataService.decodeKeyDataPair(
encodedStorageKeyAndHead
);
// decode this from backblaze data
const { key: doubleEncryptedSessionKeyDecoded, data: storageIvDecoded } =
QrCodeDataService.decodeKeyDataPair(backblazeData.sessionKey);
const doubleDecryptedSessionKey = await CryptoService.decryptAESGCM(
base64ToArrayBuffer(doubleEncryptedSessionKeyDecoded),
base64ToArrayBuffer(decodedStorageKeyAndHead.key),
base64ToArrayBuffer(storageIvDecoded)
);
// In client apps, RSA decryption happens using HSM service.
const decryptedSessionKey = await CryptoService.decryptRSA(
privateKeyPEM,
base64ToArrayBuffer(doubleDecryptedSessionKey)
);
const decodedSessionKey =
QrCodeDataService.decodeKeyDataPair(decryptedSessionKey);
const assembledData = QrCodeDataService.assembleQrCodeData(
base64ToArrayBuffer(decodedStorageKeyAndHead.data),
base64ToArrayBuffer(backblazeData.tail)
);
const decryptedEmailData = await CryptoService.decryptAESGCM(
assembledData,
base64ToArrayBuffer(decodedSessionKey.key),
base64ToArrayBuffer(decodedSessionKey.data)
);
const decompressedEmailData = Utils.decompressData(decryptedEmailData);
const decodedEmailData = QrCodeDataService.decodeEmailData(
decompressedEmailData
);
expect(decodedEmailData).toEqual(emailData);
});
});
const encodedDoubleEncryptedSessionKey = QrCodeDataService.encodeKeyDataPair(
{
key: arrayBufferToBase64(doubleEncryptedSessionKey),
describe("Documents", () => {
it("performs the whole QR code data breakage/assembling routine ", async () => {
//encode
//1. generate doc_hash, doc_head, doc_tail, doc_key and doc_iv
//2. put doc_tail inside ipfs and get cid
//should I mock the seal data as I already have it ?? YES
//2. create seal data
//3. generate seal_hash, seal_head, seal_tail, seal_iv, seal_key
//4. cache the resut from 3
//5. mock blockchain result
//6. asseble index object
//7. encrypt index object and encode the key and iv aka storage key
//8. put the encrupted index object in ipfs and get the cid
//9. encode step7, seal_head and result from step8 encodeSealHead
//decode
//1. decodeSealHead -> seal_head, index file cid, storage_key
//2. get file from ipfs
//3. decode storage_key to key and iv
//4. decrypt ipfs index file
//5. decrupt seal
//6. decrupt pdf file
//check if encoded === decoded
const encodedDocumentData =
QrCodeDataService.encodeDocumentData(documentData);
const compressedDocumentData = Utils.compressData(encodedDocumentData);
const {
data: sealBytes,
iv: sealIv,
key: sealKey,
} = await CryptoService.encryptAESGCM(
arrayBufferToBase64(compressedDocumentData)
);
const { head: sealHead, tail: sealTail } =
QrCodeDataService.breakQrCodeData(sealBytes, 32);
const sealHashBytes = await CryptoService.SHA256(sealBytes);
const sealHash = CommonUtils.arrayBufferToBase64(sealHashBytes);
const encodedSealKey = QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(sealKey),
data: arrayBufferToBase64(sealIv),
});
//mock seal tail pushed in ipfs
ipfsMock[ipfsSealTailCidMock] = arrayBufferToBase64(sealTail);
// mock storage in redis with seal_hash as a key
//encode sealIv with sealKey and set them as one field
redisMock[sealHash] = {
sealHead,
sealTailCid: ipfsSealTailCidMock,
encodedSealKey,
};
// on this step we call new proxy for documents the object
// the object should look like
// { 'id': { docHash, sealHash } }
//mock seal hash
proxyServiceResponseMock.id.sealHash = sealHash;
// wait for response -> proxyServiceResponseMock
// get seal data from cache based on seal hash in the mocked response
const sealData = redisMock[proxyServiceResponseMock.id.sealHash];
//createIndexObject
//add seal head
const indexObjectData = {
sealKey: sealData.encodedSealKey,
sealTailCid: ipfsSealTailCidMock,
chain: proxyServiceResponseMock.blockchainIdentifier,
block: proxyServiceResponseMock.blockHeight,
transactionId: proxyServiceResponseMock.transactionId,
hashBatchId: proxyServiceResponseMock.hashBatch,
};
const encodedIndex =
QrCodeDataService.encodeSealIndexObject(indexObjectData);
const compressedIndexData = Utils.compressData(encodedIndex);
const {
data: encryptedIndexObject,
iv: storageIv,
key: storageKey,
} = await CryptoService.encryptAESGCM(
arrayBufferToBase64(compressedIndexData)
);
//put index object in ipfs and get cid
ipfsMock[ipfsIndexObjectCidMock] =
arrayBufferToBase64(encryptedIndexObject);
const encodedStorageKey = QrCodeDataService.encodeKeyDataPair({
key: arrayBufferToBase64(storageKey),
data: arrayBufferToBase64(storageIv),
}
);
// Send this to putQRCodeData api
const backblazeData = {
sessionKey: encodedDoubleEncryptedSessionKey,
tail: arrayBufferToBase64(tail),
};
/**
* Decode
*/
// Retrieve base64 of this from QR code URL
const decodedStorageKeyAndHead = QrCodeDataService.decodeKeyDataPair(
encodedStorageKeyAndHead
);
// decode this from backblaze data
const {
key: doubleEncryptedSessionKeyDecoded,
data: storageIvDecoded,
} = QrCodeDataService.decodeKeyDataPair(backblazeData.sessionKey);
const doubleDecryptedSessionKey = await CryptoService.decryptAESGCM(
base64ToArrayBuffer(doubleEncryptedSessionKeyDecoded),
base64ToArrayBuffer(decodedStorageKeyAndHead.key),
base64ToArrayBuffer(storageIvDecoded)
);
// In client apps, RSA decryption happens using HSM service.
const decryptedSessionKey = await CryptoService.decryptRSA(
privateKeyPEM,
base64ToArrayBuffer(doubleDecryptedSessionKey)
);
const decodedSessionKey = QrCodeDataService.decodeKeyDataPair(
decryptedSessionKey
);
const assembledData = QrCodeDataService.assembleQrCodeData(
base64ToArrayBuffer(decodedStorageKeyAndHead.data),
base64ToArrayBuffer(backblazeData.tail)
);
const decryptedEmailData = await CryptoService.decryptAESGCM(
assembledData,
base64ToArrayBuffer(decodedSessionKey.key),
base64ToArrayBuffer(decodedSessionKey.data)
);
const decompressedEmailData = Utils.decompressData(decryptedEmailData);
const decodedEmailData = QrCodeDataService.decodeEmailData(
decompressedEmailData
);
expect(decodedEmailData).toEqual(emailData);
});
const sealHeadData = {
key: encodedStorageKey,
data: arrayBufferToBase64(sealData.sealHead),
ipfsIndexCid: ipfsIndexObjectCidMock,
};
const encodedHead = QrCodeDataService.encodeSealHead(sealHeadData);
//============================= DECODING ================================================
const sealHeadDataDecoded = QrCodeDataService.decodeSealHead(encodedHead);
const encryptedIndexObjectFromIpfs = ipfsMock[ipfsIndexObjectCidMock];
const decodedStorageIvAndKey = QrCodeDataService.decodeKeyDataPair(
sealHeadDataDecoded.key
);
const decryptedIndexObjectData = await CryptoService.decryptAESGCM(
base64ToArrayBuffer(encryptedIndexObjectFromIpfs),
base64ToArrayBuffer(decodedStorageIvAndKey.key),
base64ToArrayBuffer(decodedStorageIvAndKey.data)
);
const decompressedIndexData = Utils.decompressData(
decryptedIndexObjectData
);
const decodedIndexObj = QrCodeDataService.decodeSealIndexObject(
decompressedIndexData
);
const sealTailResult = ipfsMock[decodedIndexObj.sealTailCid];
const decodedSealKeyIv = QrCodeDataService.decodeKeyDataPair(
decodedIndexObj.sealKey
);
const assembledData = QrCodeDataService.assembleQrCodeData(
base64ToArrayBuffer(sealHeadDataDecoded.data),
base64ToArrayBuffer(sealTailResult)
);
const decryptedSealData = await CryptoService.decryptAESGCM(
assembledData,
base64ToArrayBuffer(decodedSealKeyIv.key),
base64ToArrayBuffer(decodedSealKeyIv.data)
);
const compressedSealData = Utils.decompressData(decryptedSealData);
const result = QrCodeDataService.decodeDocumentData(compressedSealData);
expect(result).toEqual(documentData);
});
});
});
Loading