Skip to content

Commit 09e65ca

Browse files
committed
feat: Add download of encrypted zip file, unzip and import trace through <downloadId>::<password>
1 parent 3c21f34 commit 09e65ca

File tree

2 files changed

+77
-15
lines changed

2 files changed

+77
-15
lines changed

src/common/common_trace_index.nim

+6-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ const SQL_CREATE_TABLE_STATEMENTS = @[
4040
calltrace integer,
4141
calltraceMode string,
4242
date text,
43-
downloadId string,
44-
controlId string,
45-
passwordKey string,
46-
expireTime integer);""",
43+
remoteShareDownloadId string,
44+
remoteShareControlId string,
45+
remoteShareExpireTime integer);""",
4746
"""CREATE TABLE IF NOT EXISTS trace_values (
4847
id integer,
4948
maxTraceID integer,
@@ -60,6 +59,9 @@ const SQL_INITIAL_INSERT_STATEMENTS = @[
6059
const SQL_ALTER_TABLE_STATEMENTS: seq[string] = @[
6160
# example: adding a new column
6261
"""ALTER TABLE traces ADD COLUMN calltraceMode text;""",
62+
"""ALTER TABLE traces ADD COLUMN remoteShareDownloadId text;""",
63+
"""ALTER TABLE traces ADD COLUMN remoteShareControlId text;""",
64+
"""ALTER TABLE traces ADD COLUMN remoteShareExpireTime text;""",
6365
"""ALTER TABLE traces RENAME COLUMN callgraph TO calltrace"""
6466
# """ALTER TABLE traces ADD COLUMN love integer;"""
6567
]

src/ct/codetracer.nim

+71-11
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,16 @@ proc pkcs7Pad(data: seq[byte], blockSize: int): seq[byte] =
699699
let padLen = blockSize - (data.len mod blockSize)
700700
result = data & repeat(cast[byte](padLen), padLen)
701701

702+
proc pkcs7Unpad(data: seq[byte]): seq[byte] =
703+
if data.len == 0:
704+
raise newException(ValueError, "Data is empty, cannot unpad")
705+
706+
let padLen = int64(data[^1]) # Convert last byte to int64 safely
707+
if padLen <= 0 or padLen > data.len:
708+
raise newException(ValueError, "Invalid padding")
709+
710+
result = data[0 ..< data.len - padLen]
711+
702712
func toBytes(s: string): seq[byte] =
703713
## Convert a string to the corresponding byte sequence - since strings in
704714
## nim essentially are byte sequences without any particular encoding, this
@@ -711,6 +721,24 @@ func toBytes(s: string): seq[byte] =
711721
else:
712722
@(s.toOpenArrayByte(0, s.high))
713723

724+
proc decryptZip(encryptedFile: string, password: string, outputFile: string) =
725+
var encData = readFile(encryptedFile).toBytes()
726+
if encData.len < 16:
727+
raise newException(ValueError, "Invalid encrypted data (too short)")
728+
729+
let iv = password.toBytes()[0 ..< 16]
730+
let ciphertext = encData[16 .. ^1]
731+
let key = password.toBytes()
732+
733+
var aes: CBC[aes256]
734+
aes.init(key, iv)
735+
736+
var decrypted = newSeq[byte](encData.len)
737+
aes.decrypt(encData, decrypted.toOpenArray(0, len(decrypted) - 1))
738+
739+
var depaddedData = pkcs7Unpad(decrypted)
740+
writeFile(outputFile, depaddedData)
741+
714742
proc encryptZip(zipFile, password: string) =
715743
var iv: seq[byte] = password.toBytes()[0..15]
716744

@@ -724,7 +752,21 @@ proc encryptZip(zipFile, password: string) =
724752
aes.encrypt(paddedData, encrypted.toOpenArray(0, len(encrypted) - 1))
725753
writeFile(zipFile & ".enc", encrypted)
726754

727-
proc zipFileWithPassword(inputFile: string, outputZip: string, password: string) =
755+
proc unzipDecryptedFile(zipFile: string, outputDir: string): (string, int) =
756+
var zip: ZipArchive
757+
if not zip.open(zipFile, fmRead):
758+
raise newException(IOError, "Failed to open decrypted ZIP: " & zipFile)
759+
760+
let traceId = trace_index.newID(false)
761+
let outPath = outputDir / "trace-" & $traceId
762+
763+
createDir(outPath)
764+
zip.extractAll(outPath)
765+
766+
zip.close()
767+
return (outPath, traceId)
768+
769+
proc zipFileWithEncryption(inputFile: string, outputZip: string, password: string) =
728770
var zip: ZipArchive
729771
if not zip.open(outputZip, fmWrite):
730772
raise newException(IOError, "Failed to create zip file: " & outputZip)
@@ -748,15 +790,15 @@ proc uploadTrace(trace: Trace) =
748790
let outputZip = trace.outputFolder / "tmp.zip"
749791
var aesKey = generateSecurePassword()
750792

751-
zipFileWithPassword(trace.outputFolder, outputZip, aesKey)
793+
zipFileWithEncryption(trace.outputFolder, outputZip, aesKey)
752794

753795
let (output, exitCode) = uploadEncyptedZip(outputZip)
754796
let jsonMessage = parseJson(output)
797+
let downloadId = jsonMessage["DownloadId"].getStr("") & "::" & aesKey
755798

756-
updateField(trace.id, "passwordKey", aesKey, false)
757-
updateField(trace.id, "downloadId", jsonMessage["DownloadId"].getStr(""), false)
758-
updateField(trace.id, "controlId", jsonMessage["ControlId"].getStr(""), false)
759-
updateField(trace.id, "expireTime", jsonMessage["Expires"].getStr(""), false)
799+
updateField(trace.id, "remoteShareDownloadId", downloadId, false)
800+
updateField(trace.id, "remoteShareControlId", jsonMessage["ControlId"].getStr(""), false)
801+
updateField(trace.id, "remoteShareExpireTime", jsonMessage["Expires"].getStr(""), false)
760802

761803
# TODO: Uncomment when finished implementing
762804
removeFile(outputZip & ".enc")
@@ -1212,11 +1254,29 @@ proc console(
12121254

12131255

12141256
proc downloadCommand(traceRegistryId: string) =
1215-
let config = loadConfig(folder=getCurrentDir(), inTest=false)
1216-
let localPath = os.getHomeDir() / ".local" / "share" / "codetracer" / "tmp.zip"
1217-
let cmd = &"curl -s -o {localPath} {config.webApiRoot}/download?DownloadId={traceRegistryId}"
1218-
let (output, exitCode) = execCmdEx(cmd)
1219-
quit(exitCode)
1257+
# We expect a traceRegistryId to have <downloadId>::<passwordKey>
1258+
let stringSplit = traceRegistryId.split("::")
1259+
if stringSplit.len() != 2:
1260+
quit(1)
1261+
else:
1262+
let downloadId = stringSplit[0]
1263+
let password = stringSplit[1]
1264+
let zipPath = "/tmp/tmp.zip"
1265+
let config = loadConfig(folder=getCurrentDir(), inTest=false)
1266+
let localPath = "/tmp" / "tmp.zip.enc"
1267+
# TODO: Plug in an http client
1268+
let cmd = &"curl -s -o {localPath} {config.webApiRoot}/download?DownloadId={downloadId}"
1269+
let (output, exitCode) = execCmdEx(cmd)
1270+
1271+
decryptZip(localPath, password, zipPath)
1272+
let (traceFolder, traceId) = unzipDecryptedFile(zipPath, os.getHomeDir() / ".local" / "share" / "codetracer")
1273+
let tracePath = traceFolder / "trace.json"
1274+
let traceMetadataPath = traceFolder / "trace_metadata.json"
1275+
discard importDbTrace(traceMetadataPath, traceId, LangNoir, DB_SELF_CONTAINED_DEFAULT)
1276+
removeFile(localPath)
1277+
removeFile(zipPath)
1278+
1279+
quit(exitCode)
12201280

12211281
proc runWithRestart(
12221282
test: bool,

0 commit comments

Comments
 (0)