This PR should implement importing other people's records in the app. To clarify how the sharing works, I'll document it here in this PR and we can decide to move this into a READ.me once we have everything finalized.
Sharing of contacts
The contacts sharing info is created the following way:
We extract the public key from the secure on-device keystore and we upload it to ipfs.
We store the ipfs hash in the on-device regular storage to make sure we don't re-upload multiple times, if the user re-opens the sharing screen. This local cache of the ipfs location of the public key gets reset if the user revokes the keys.
We take the user's public contact info from auth0: {email, nickname, avatar link}
The final share contact hash is created by doing base64(public contact info) + ':' + (ipfs hash). This is what we display in the QR code.
The QR code with default error checking parameters allows us to store up to around 10,000 ASCII characters which should suffice for this purpose.
When we import the contact we do the following:
We first check to see if the scanned QR code contains ':' (our contact/ipfs hash separator). If it doesn't we immediately reject the code.
Then we try to parse the two parts, doing the opposite procedure from above. We base64 decode the first part, we JSON.parse it, and then we take the second part - the ipfs public key hash. If we hit an exception during any of this, the QR code is deemed in the wrong format.
Sharing of the QR code
Since we don't have corald yet or dapp, the transfer of contacts/records is done manually, either through scanning the QR codes or sharing the QR code information through other means, e.g. email, text message, Telegram...
When the user hits the share button on the QR display screen a link to our app is created with the following URL structure:
coralhealth://applink?type={contact|record}&data={the qr code displayed hash}
Most of the code for scanning a contact QR is in:
store.qrCodeContactHelper()
Creation of the contact info and uploading the public key to IPFS is done in:
SharedRecordsScreen onPressHandler for 'My Record Sharing Information'
Sharing of records
Sharing of records follows similar procedure as the sharing of contacts above, with one minor exception: The record metadata info is too large to be displayed in the QR code with maintaining the default error correction and it would be too large to be shared in an URL. To mitigate this we do one more wrap in IPFS.
Here's how the shared record information is created:
We take the contact's public key and encrypt the record data, metadata and the per contact/record generated symmetric keys.
When the encryption is done, the record data is uploaded to IPFS and we get a hash back just like for regular creation of records.
We then take that hash, store it on the record metadata and base64 encode the whote thing. This base64 encoded metadata now fully contains everything we need to share with the target third-party.
We upload the base64 encoded metadata to IPFS and get a new metadata hash back.
Just like with the sharing of contacts, the final shared record hash is created by doing base64(public contact info) + ':' + (metadata ipfs hash). This is what we display at the end in the shared record QR code.
Most of the code for creating the shared metadata info is in DelegateAccessScreen.js
onContactSelected(contact)
Here's an annotated code extract (I removed all JavaScript UI responsiveness code for better clarity)
// We take our encrypted IPFS hash which is stored on the record in on-device storage
ipfs.cat(record.hash)
.then(async (dataUri) => {
// We just downloaded the file from IPFS, so we decrypt the record data with our key
let { decryptedUri } = await cryptoHelpers.decryptFile(dataUri, record.encryptionInfo.key, record.encryptionInfo.iv);
// We load it as a string (base64 encoded for images) and delete the temp file
let data = await FileSystem.readAsStringAsync(decryptedUri);
await FileSystem.deleteAsync(decryptedUri, { idempotent: true });
// We now encrypt the record by using the third-party public key
let encryptedInfo = await cryptoHelpers.encryptFile(data, record.metadata, publicKeyPem);
// We add the data part of the record to IPFS (to match what we do for our records)
let hash = await ipfs.add(encryptedInfo.uri);
// Clean-up the temp file
await FileSystem.deleteAsync(encryptedInfo.uri, { idempotent: true });
// We create the shared record data with the original id, the data IPFS hash, the encrypted metadata and the encrypted decryption symmetric keys
let sharedRecord = {
id: record.id,
hash,
metadata: encryptedInfo.encryptedMetadata,
encryptionInfo: { key: encryptedInfo.encryptedKey, iv: encryptedInfo.encryptedIv }
};
// This just does JSON.stringify() and encodes in base64
let sharedInfo = store.thirdPartySharedRecordInfo(sharedRecord);
// We add the shared record information to IPFS so we can generate the final sharing hash
let sharedRecordHash = await ipfs.add(sharedInfo);
sharedRecord.sharedHash = sharedRecordHash;
// Finally we store the shared record in our on-device storage. This is what's used to populate the Shared Record Tab screen.
await store.shareRecord(contact.name, sharedRecord);
resolve(sharedRecordHash);
});
This PR should implement importing other people's records in the app. To clarify how the sharing works, I'll document it here in this PR and we can decide to move this into a READ.me once we have everything finalized.
Sharing of contacts
The contacts sharing info is created the following way:
The QR code with default error checking parameters allows us to store up to around 10,000 ASCII characters which should suffice for this purpose.
When we import the contact we do the following:
Sharing of the QR code
Since we don't have corald yet or dapp, the transfer of contacts/records is done manually, either through scanning the QR codes or sharing the QR code information through other means, e.g. email, text message, Telegram...
When the user hits the share button on the QR display screen a link to our app is created with the following URL structure:
coralhealth://applink?type={contact|record}&data={the qr code displayed hash}
Most of the code for scanning a contact QR is in:
Creation of the contact info and uploading the public key to IPFS is done in:
Sharing of records
Sharing of records follows similar procedure as the sharing of contacts above, with one minor exception: The record metadata info is too large to be displayed in the QR code with maintaining the default error correction and it would be too large to be shared in an URL. To mitigate this we do one more wrap in IPFS.
Here's how the shared record information is created:
Most of the code for creating the shared metadata info is in DelegateAccessScreen.js
Here's an annotated code extract (I removed all JavaScript UI responsiveness code for better clarity)