Skip to content

Commit

Permalink
Merge 5ae3cba into 8dd9922
Browse files Browse the repository at this point in the history
  • Loading branch information
seaerchin committed May 18, 2021
2 parents 8dd9922 + 5ae3cba commit 5c03361
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 23 deletions.
61 changes: 39 additions & 22 deletions src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
decryptContent,
verifySignedMessage,
generateKeypair,
areAttachmentFieldIdsValid,
convertEncryptedAttachmentToFileContent,
} from './util/crypto'

export default class Crypto {
Expand Down Expand Up @@ -215,7 +217,8 @@ export default class Crypto {
const decryptedRecords: DecryptedAttachments = {}
const filenames: Record<string, string> = {}

const attachmentRecords: EncryptedAttachmentRecords = decryptParams.attachmentDownloadUrls ?? {}
const attachmentRecords: EncryptedAttachmentRecords =
decryptParams.attachmentDownloadUrls ?? {}
const decryptedContent = this.decrypt(formSecretKey, decryptParams)
if (decryptedContent === null) return null

Expand All @@ -226,37 +229,51 @@ export default class Crypto {
}
})

const downloadPromises : Array<Promise<void>> = []
for (let fieldId in attachmentRecords) {
// Original name for the file is not found
if (filenames[fieldId] === undefined) return null
const fieldIds = Object.keys(attachmentRecords)
// Check if all fieldIds are within filenames
if (!areAttachmentFieldIdsValid(fieldIds, filenames)) {
return null
}

downloadPromises.push(
axios.get(attachmentRecords[fieldId], { responseType: 'json' })
.then((downloadResponse) => {
const encryptedAttachment: EncryptedAttachmentContent = downloadResponse.data
const encryptedFile: EncryptedFileContent = {
submissionPublicKey: encryptedAttachment.encryptedFile.submissionPublicKey,
nonce: encryptedAttachment.encryptedFile.nonce,
binary: decodeBase64(encryptedAttachment.encryptedFile.binary),
}
const downloadPromises = fieldIds.map((fieldId) => {
return (
axios
// Retrieve all the attachments as JSON
.get<EncryptedAttachmentContent>(attachmentRecords[fieldId], {
responseType: 'json',
})
// Decrypt all the attachments
.then(async ({ data: downloadResponse }) => {
const encryptedFile =
convertEncryptedAttachmentToFileContent(downloadResponse)

return this.decryptFile(formSecretKey, encryptedFile)
}).then((decryptedFile) => {
if (decryptedFile === null) throw new Error("Attachment decryption failed")
decryptedRecords[fieldId] = { filename: filenames[fieldId], content: decryptedFile }
}))
}
const decryptedFile = await this.decryptFile(
formSecretKey,
encryptedFile
)

// If there is content, extract and put
if (decryptedFile) {
decryptedRecords[fieldId] = {
filename: filenames[fieldId],
content: decryptedFile,
}
} else {
throw new Error('Attachment decryption failed')
}
})
)
})

try {
await Promise.all(downloadPromises)
} catch (e) {
} catch {
return null
}

return {
content: decryptedContent,
attachments: decryptedRecords
attachments: decryptedRecords,
}
}
}
33 changes: 32 additions & 1 deletion src/util/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { encodeBase64, decodeBase64, encodeUTF8 } from 'tweetnacl-util'
import nacl from 'tweetnacl'

import { Keypair, EncryptedContent } from '../types'
import {
Keypair,
EncryptedContent,
EncryptedAttachmentContent,
EncryptedFileContent,
} from '../types'

/**
* Helper method to generate a new keypair for encryption.
Expand Down Expand Up @@ -79,3 +84,29 @@ export const verifySignedMessage = (
throw new Error('Failed to open signed message with given public key')
return JSON.parse(encodeUTF8(openedMessage))
}

/**
* Helper method to check if all the field IDs given are within the filenames
* @param fieldIds the list of fieldIds to check
* @param filenames the filenames that should contain the fields
* @returns boolean indicating whether the fields are valid
*/
export const areAttachmentFieldIdsValid = (
fieldIds: string[],
filenames: Record<string, string>
): boolean => {
return fieldIds.every((fieldId) => filenames[fieldId])
}

/**
* Converts an encrypted attachment to encrypted file content
* @param encryptedAttachment The encrypted attachment
* @returns EncryptedFileContent The encrypted file content
*/
export const convertEncryptedAttachmentToFileContent = (
encryptedAttachment: EncryptedAttachmentContent
): EncryptedFileContent => ({
submissionPublicKey: encryptedAttachment.encryptedFile.submissionPublicKey,
nonce: encryptedAttachment.encryptedFile.nonce,
binary: decodeBase64(encryptedAttachment.encryptedFile.binary),
})

0 comments on commit 5c03361

Please sign in to comment.