@@ -699,6 +699,16 @@ proc pkcs7Pad(data: seq[byte], blockSize: int): seq[byte] =
699
699
let padLen = blockSize - (data.len mod blockSize)
700
700
result = data & repeat (cast [byte ](padLen), padLen)
701
701
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
+
702
712
func toBytes (s: string ): seq [byte ] =
703
713
# # Convert a string to the corresponding byte sequence - since strings in
704
714
# # nim essentially are byte sequences without any particular encoding, this
@@ -711,6 +721,24 @@ func toBytes(s: string): seq[byte] =
711
721
else :
712
722
@ (s.toOpenArrayByte (0 , s.high))
713
723
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
+
714
742
proc encryptZip (zipFile, password: string ) =
715
743
var iv: seq [byte ] = password.toBytes ()[0 .. 15 ]
716
744
@@ -724,7 +752,21 @@ proc encryptZip(zipFile, password: string) =
724
752
aes.encrypt (paddedData, encrypted.toOpenArray (0 , len (encrypted) - 1 ))
725
753
writeFile (zipFile & " .enc" , encrypted)
726
754
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 ) =
728
770
var zip: ZipArchive
729
771
if not zip.open (outputZip, fmWrite):
730
772
raise newException (IOError , " Failed to create zip file: " & outputZip)
@@ -748,15 +790,15 @@ proc uploadTrace(trace: Trace) =
748
790
let outputZip = trace.outputFolder / " tmp.zip"
749
791
var aesKey = generateSecurePassword ()
750
792
751
- zipFileWithPassword (trace.outputFolder, outputZip, aesKey)
793
+ zipFileWithEncryption (trace.outputFolder, outputZip, aesKey)
752
794
753
795
let (output, exitCode) = uploadEncyptedZip (outputZip)
754
796
let jsonMessage = parseJson (output)
797
+ let downloadId = jsonMessage[" DownloadId" ].getStr (" " ) & " ::" & aesKey
755
798
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 )
760
802
761
803
# TODO : Uncomment when finished implementing
762
804
removeFile (outputZip & " .enc" )
@@ -1212,11 +1254,29 @@ proc console(
1212
1254
1213
1255
1214
1256
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)
1220
1280
1221
1281
proc runWithRestart (
1222
1282
test: bool ,
0 commit comments