feat(offline): add decrypt as zip util
This commit is contained in:
parent
7b2537e355
commit
8b5abe02e2
140
src/utils/zip.ts
140
src/utils/zip.ts
@ -1,6 +1,12 @@
|
|||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import { toast } from 'react-toastify'
|
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.
|
* 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.).
|
* @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.
|
* @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,
|
zip: JSZip,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
outputType: T
|
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 {
|
try {
|
||||||
return await JSZip.loadAsync(data)
|
return await JSZip.loadAsync(data)
|
||||||
} catch (err) {
|
} 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user