feat(offline): add decrypt as zip util

This commit is contained in:
enes 2025-01-17 21:06:55 +01:00
parent 7b2537e355
commit 8b5abe02e2

View File

@ -1,6 +1,12 @@
import JSZip from 'jszip'
import { toast } from 'react-toastify'
import { InputFileFormat, OutputByType, OutputType } from '../types'
import { InputFileFormat, Meta, OutputByType, OutputType } from '../types'
import { NavigateOptions, To } from 'react-router-dom'
import { appPublicRoutes } from '../routes'
import { NostrController } from '../controllers'
import { decryptArrayBuffer } from './crypto'
import { hexToNpub, parseJson, SigitStatus, timeout } from '.'
import { SignerService } from '../services'
/**
* Read the content of a file within a zip archive.
@ -9,7 +15,7 @@ import { InputFileFormat, OutputByType, OutputType } from '../types'
* @param outputType The type of output to return (e.g., 'string', 'arraybuffer', 'uint8array', etc.).
* @returns A Promise resolving to the content of the file, or null if an error occurs.
*/
const readContentOfZipEntry = async <T extends OutputType>(
export const readContentOfZipEntry = async <T extends OutputType>(
zip: JSZip,
filePath: string,
outputType: T
@ -34,7 +40,7 @@ const readContentOfZipEntry = async <T extends OutputType>(
})
}
const loadZip = async (data: InputFileFormat): Promise<JSZip | null> => {
export const loadZip = async (data: InputFileFormat): Promise<JSZip | null> => {
try {
return await JSZip.loadAsync(data)
} catch (err) {
@ -46,4 +52,130 @@ const loadZip = async (data: InputFileFormat): Promise<JSZip | null> => {
}
}
export { readContentOfZipEntry, loadZip }
export const decrypt = async (file: File) => {
const nostrController = NostrController.getInstance()
const zip = await loadZip(file)
if (!zip) return
const keysFileContent = await readContentOfZipEntry(
zip,
'keys.json',
'string'
)
if (!keysFileContent) return null
const parsedKeysJson = await parseJson<{ sender: string; keys: string[] }>(
keysFileContent
).catch((err) => {
console.log(`Error parsing content of keys.json:`, err)
toast.error(err.message || `Error parsing content of keys.json`)
return null
})
if (!parsedKeysJson) return
const encryptedArrayBuffer = await readContentOfZipEntry(
zip,
'compressed.sigit',
'arraybuffer'
)
if (!encryptedArrayBuffer) return
const { keys, sender } = parsedKeysJson
for (const key of keys) {
// decrypt the encryptionKey, with timeout (duration = 60 seconds)
const encryptionKey = await Promise.race([
nostrController.nip04Decrypt(sender, key),
timeout(60000)
])
.then((res) => {
return res
})
.catch((err) => {
console.log('err :>> ', err)
return null
})
// Return if encryption failed
if (!encryptionKey) continue
const arrayBuffer = await decryptArrayBuffer(
encryptedArrayBuffer,
encryptionKey
).catch((err) => {
console.log('err in decryption:>> ', err)
return null
})
if (arrayBuffer) return arrayBuffer
}
return null
}
type NavigateArgs = { to: To; options?: NavigateOptions }
export const navigateFromZip = async (file: File, pubkey: `npub1${string}`) => {
if (!file.name.endsWith('.sigit.zip')) {
toast.error(`Not a SiGit zip file: ${file.name}`)
}
try {
let zip = await JSZip.loadAsync(file)
if (!zip) {
return null
}
let arrayBuffer: ArrayBuffer | undefined
if ('keys.json' in zip.files) {
// Decrypt
const decryptedArrayBuffer = await decrypt(file).catch((err) => {
console.error(`error occurred in decryption`, err)
toast.error(err.message || `error occurred in decryption`)
})
if (decryptedArrayBuffer) {
// Replace the zip and continue processing
zip = await JSZip.loadAsync(decryptedArrayBuffer)
arrayBuffer = decryptedArrayBuffer
}
}
if ('meta.json' in zip.files) {
// Check where we need to navigate
// Find Meta and process it for signer state
const metaContent = await readContentOfZipEntry(
zip,
'meta.json',
'string'
)
if (metaContent) {
const meta = JSON.parse(metaContent) as Meta
const signerService = new SignerService(meta)
const to =
signerService.getSignedStatus() === SigitStatus.Complete
? appPublicRoutes.verify
: signerService.getNavigate(hexToNpub(pubkey))
return {
to,
options: {
state: { uploadedZip: arrayBuffer || file }
}
} as NavigateArgs
}
}
return null
} catch (err) {
console.error('err in processing sigit zip file :>> ', err)
if (err instanceof Error) {
toast.error(err.message || 'An error occurred in loading zip file.')
}
return null
}
}