Skip to content

Commit ce275d6

Browse files
author
Incharajayaram
committed
added downloading feature
1 parent e999829 commit ce275d6

7 files changed

Lines changed: 282 additions & 62 deletions

File tree

Email_client/QuMail.EmailProtocol/Controllers/EmailController.cs

Lines changed: 110 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,16 @@ public async Task<IActionResult> DecryptToPqc2(Guid emailId)
640640
if (TryParseEnvelope(email.Subject, out var otpSubjectEnvelope))
641641
{
642642
pqcSubject = await DecryptOTPAsync(otpSubjectEnvelope);
643+
// Check if OTP decryption failed
644+
if (pqcSubject == "OTP decryption failed" || pqcSubject == "Decryption failed")
645+
{
646+
_logger.LogError("OTP decryption failed for subject");
647+
return BadRequest(new {
648+
success = false,
649+
message = "Failed to decrypt email: OTP decryption failed for subject",
650+
detail = "The OTP service may be unavailable or the encryption key may have expired"
651+
});
652+
}
643653
}
644654
else
645655
{
@@ -650,6 +660,16 @@ public async Task<IActionResult> DecryptToPqc2(Guid emailId)
650660
if (TryParseEnvelope(email.Body, out var otpBodyEnvelope))
651661
{
652662
pqcBody = await DecryptOTPAsync(otpBodyEnvelope);
663+
// Check if OTP decryption failed
664+
if (pqcBody == "OTP decryption failed" || pqcBody == "Decryption failed")
665+
{
666+
_logger.LogError("OTP decryption failed for body");
667+
return BadRequest(new {
668+
success = false,
669+
message = "Failed to decrypt email: OTP decryption failed for body",
670+
detail = "The OTP service may be unavailable or the encryption key may have expired"
671+
});
672+
}
653673
}
654674
else
655675
{
@@ -757,10 +777,19 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
757777
// PHASE 1: Decrypt OTP layer (outermost)
758778
_logger.LogInformation("Decrypting OTP layer for subject and body");
759779
string aesSubject, aesBody;
780+
bool subjectDecryptionFailed = false;
781+
bool bodyDecryptionFailed = false;
760782

761783
if (TryParseEnvelope(email.Subject, out var otpSubjectEnvelope))
762784
{
763785
aesSubject = await DecryptOTPAsync(otpSubjectEnvelope);
786+
// Check if OTP decryption failed
787+
if (aesSubject == "OTP decryption failed" || aesSubject == "Decryption failed")
788+
{
789+
_logger.LogWarning("OTP decryption failed for subject, using fallback message");
790+
aesSubject = "[Decryption Failed - OTP key may have expired]";
791+
subjectDecryptionFailed = true;
792+
}
764793
}
765794
else
766795
{
@@ -771,38 +800,65 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
771800
if (TryParseEnvelope(email.Body, out var otpBodyEnvelope))
772801
{
773802
aesBody = await DecryptOTPAsync(otpBodyEnvelope);
803+
// Check if OTP decryption failed
804+
if (aesBody == "OTP decryption failed" || aesBody == "Decryption failed")
805+
{
806+
_logger.LogWarning("OTP decryption failed for body, using fallback message");
807+
aesBody = "[Decryption Failed - OTP key may have expired. The encryption key for this message is no longer available.]";
808+
bodyDecryptionFailed = true;
809+
}
774810
}
775811
else
776812
{
777813
_logger.LogWarning("Failed to parse OTP envelope for body");
778814
aesBody = email.Body;
779815
}
780816

781-
// PHASE 2: Decrypt AES layer (middle)
817+
// PHASE 2: Decrypt AES layer (middle) - skip if OTP decryption failed
782818
_logger.LogInformation("Decrypting AES layer for subject and body");
783819
string pqcSubject, pqcBody;
784820

785-
if (TryParseAESEnvelope(aesSubject, out var aesSubjectEnvelope))
821+
if (!subjectDecryptionFailed && TryParseAESEnvelope(aesSubject, out var aesSubjectEnvelope))
786822
{
787823
pqcSubject = await DecryptAESAsync(aesSubjectEnvelope);
824+
// Check if AES decryption failed
825+
if (pqcSubject.StartsWith("AES decryption failed"))
826+
{
827+
_logger.LogWarning("AES decryption failed for subject, using fallback message");
828+
pqcSubject = "[Decryption Failed - AES decryption error]";
829+
subjectDecryptionFailed = true;
830+
}
788831
}
789832
else
790833
{
791-
_logger.LogWarning("Failed to parse AES envelope for subject");
834+
if (!subjectDecryptionFailed)
835+
{
836+
_logger.LogWarning("Failed to parse AES envelope for subject");
837+
}
792838
pqcSubject = aesSubject;
793839
}
794840

795-
if (TryParseAESEnvelope(aesBody, out var aesBodyEnvelope))
841+
if (!bodyDecryptionFailed && TryParseAESEnvelope(aesBody, out var aesBodyEnvelope))
796842
{
797843
pqcBody = await DecryptAESAsync(aesBodyEnvelope);
844+
// Check if AES decryption failed
845+
if (pqcBody.StartsWith("AES decryption failed"))
846+
{
847+
_logger.LogWarning("AES decryption failed for body, using fallback message");
848+
pqcBody = "[Decryption Failed - AES decryption error. The encryption key for this message is no longer available.]";
849+
bodyDecryptionFailed = true;
850+
}
798851
}
799852
else
800853
{
801-
_logger.LogWarning("Failed to parse AES envelope for body");
854+
if (!bodyDecryptionFailed)
855+
{
856+
_logger.LogWarning("Failed to parse AES envelope for body");
857+
}
802858
pqcBody = aesBody;
803859
}
804860

805-
// PHASE 3: Decrypt attachments from OTP+AES layers to PQC layer
861+
// PHASE 3: Decrypt attachments from OTP+AES layers to PQC layer - continue even if some fail
806862
_logger.LogInformation("Decrypting attachments for PQC_3_LAYER email");
807863
string? pqcAttachmentsJson = null;
808864
if (!string.IsNullOrWhiteSpace(email.Attachments))
@@ -813,29 +869,64 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
813869
if (attachmentsList != null && attachmentsList.Count > 0)
814870
{
815871
var decryptedAttachments = new List<object>();
816-
foreach (var item in attachmentsList)
872+
for (int idx = 0; idx < attachmentsList.Count; idx++)
817873
{
818-
var fileName = item.ContainsKey("fileName") ? item["fileName"]?.ToString() : null;
819-
var contentType = item.ContainsKey("contentType") ? item["contentType"]?.ToString() : null;
820-
var envelope = item.ContainsKey("envelope") ? item["envelope"]?.ToString() : null;
821-
822-
if (!string.IsNullOrWhiteSpace(fileName) && !string.IsNullOrWhiteSpace(envelope))
874+
try
823875
{
824-
// Decrypt OTP layer first
825-
if (TryParseEnvelope(envelope, out var otpEnvelope))
876+
var item = attachmentsList[idx];
877+
var fileName = item.ContainsKey("fileName") ? item["fileName"]?.ToString() : null;
878+
var contentType = item.ContainsKey("contentType") ? item["contentType"]?.ToString() : null;
879+
var envelope = item.ContainsKey("envelope") ? item["envelope"]?.ToString() : null;
880+
881+
if (!string.IsNullOrWhiteSpace(fileName) && !string.IsNullOrWhiteSpace(envelope))
826882
{
827-
var aesEnvelope = await DecryptOTPAsync(otpEnvelope);
883+
_logger.LogInformation("Processing attachment {Index}: {FileName}", idx, fileName);
828884

829-
// Then decrypt AES layer to get PQC envelope
830-
if (TryParseAESEnvelope(aesEnvelope, out var aesEnvelopeObj))
885+
// Decrypt OTP layer first
886+
if (TryParseEnvelope(envelope, out var otpEnvelope))
831887
{
832-
var pqcEnvelope = await DecryptAESAsync(aesEnvelopeObj);
833-
decryptedAttachments.Add(new { fileName, contentType, pqcEnvelope });
888+
var aesEnvelope = await DecryptOTPAsync(otpEnvelope);
889+
890+
// Check if OTP decryption failed
891+
if (aesEnvelope == "OTP decryption failed" || aesEnvelope == "Decryption failed")
892+
{
893+
_logger.LogWarning("OTP decryption failed for attachment {Index}: {FileName}, skipping", idx, fileName);
894+
continue;
895+
}
896+
897+
// Then decrypt AES layer to get PQC envelope
898+
if (TryParseAESEnvelope(aesEnvelope, out var aesEnvelopeObj))
899+
{
900+
var pqcEnvelope = await DecryptAESAsync(aesEnvelopeObj);
901+
902+
// Check if AES decryption failed
903+
if (pqcEnvelope.StartsWith("AES decryption failed"))
904+
{
905+
_logger.LogWarning("AES decryption failed for attachment {Index}: {FileName}, skipping", idx, fileName);
906+
continue;
907+
}
908+
909+
decryptedAttachments.Add(new { fileName, contentType, pqcEnvelope });
910+
_logger.LogInformation("Successfully decrypted attachment {Index}: {FileName}", idx, fileName);
911+
}
912+
else
913+
{
914+
_logger.LogWarning("Failed to parse AES envelope for attachment {Index}: {FileName}", idx, fileName);
915+
}
916+
}
917+
else
918+
{
919+
_logger.LogWarning("Failed to parse OTP envelope for attachment {Index}: {FileName}", idx, fileName);
834920
}
835921
}
836922
}
923+
catch (Exception attEx)
924+
{
925+
_logger.LogError(attEx, "Failed to decrypt individual attachment {Index}, continuing with others", idx);
926+
}
837927
}
838928
pqcAttachmentsJson = JsonSerializer.Serialize(decryptedAttachments, _jsonOptions);
929+
_logger.LogInformation("Successfully decrypted {Count} out of {Total} attachments", decryptedAttachments.Count, attachmentsList.Count);
839930
}
840931
}
841932
catch (Exception ex)

Email_client/QuMail.EmailProtocol/Controllers/PQCController.cs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ public class PQCController : ControllerBase
1717
{
1818
private readonly Level3KyberPQC _kyberPQC;
1919
private readonly Level3PQCEmailService _pqcEmailService;
20+
private readonly Level3HybridEncryption _hybridEncryption;
2021
private readonly AuthDbContext _context;
2122

2223
public PQCController(
2324
Level3KyberPQC kyberPQC,
2425
Level3PQCEmailService pqcEmailService,
26+
Level3HybridEncryption hybridEncryption,
2527
AuthDbContext context)
2628
{
2729
_kyberPQC = kyberPQC ?? throw new ArgumentNullException(nameof(kyberPQC));
2830
_pqcEmailService = pqcEmailService ?? throw new ArgumentNullException(nameof(pqcEmailService));
31+
_hybridEncryption = hybridEncryption ?? throw new ArgumentNullException(nameof(hybridEncryption));
2932
_context = context ?? throw new ArgumentNullException(nameof(context));
3033
}
3134

@@ -157,17 +160,34 @@ public IActionResult DecryptEmail([FromBody] PQCDecryptRequest request)
157160
});
158161
}
159162

160-
// Try to decrypt with the provided encryptedKeyId
161-
// If it fails and the encryptedKeyId looks like a plain keyId (starts with "PQC-"),
162-
// we'll handle it as a legacy email
163+
// Route to appropriate decryption method based on UseAES flag
164+
// UseAES=true: 3-layer encryption (PQC + AES + OTP) using Level3HybridEncryption
165+
// UseAES=false: 2-layer encryption (PQC + OTP) using Level3PQCEmailService
163166
string decrypted;
164167
try
165168
{
166-
decrypted = _pqcEmailService.DecryptEmail(
167-
request.EncryptedBody,
168-
request.PQCCiphertext,
169-
request.EncryptedKeyId,
170-
request.PrivateKey);
169+
if (request.UseAES)
170+
{
171+
// 3-layer decryption path (PQC + AES + OTP)
172+
Console.WriteLine("[PQCController] Using 3-layer decryption (PQC + AES + OTP)");
173+
decrypted = _hybridEncryption.DecryptAsync(
174+
request.EncryptedBody,
175+
request.PQCCiphertext,
176+
request.EncryptedKeyId,
177+
request.PrivateKey,
178+
request.Algorithm ?? "Kyber512",
179+
usedAES: true).Result;
180+
}
181+
else
182+
{
183+
// 2-layer decryption path (PQC + OTP)
184+
Console.WriteLine("[PQCController] Using 2-layer decryption (PQC + OTP)");
185+
decrypted = _pqcEmailService.DecryptEmail(
186+
request.EncryptedBody,
187+
request.PQCCiphertext,
188+
request.EncryptedKeyId,
189+
request.PrivateKey);
190+
}
171191
}
172192
catch (Exception) when (request.EncryptedKeyId?.StartsWith("PQC-") == true)
173193
{
@@ -435,6 +455,8 @@ public class PQCDecryptRequest
435455
public string PQCCiphertext { get; set; } = string.Empty;
436456
public string EncryptedKeyId { get; set; } = string.Empty; // NEW: Required for KeyManager
437457
public string PrivateKey { get; set; } = string.Empty;
458+
public string? Algorithm { get; set; } // NEW: PQC algorithm used (Kyber512/Kyber1024)
459+
public bool UseAES { get; set; } = false; // NEW: True for 3-layer (PQC+AES+OTP), false for 2-layer (PQC+OTP)
438460
}
439461

440462
public class ValidateKeyRequest

frontend/lib/screens/compose_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class _ComposeScreenState extends State<ComposeScreen> {
5757
final recipientEmail = _toController.text.trim();
5858
if (recipientEmail.isNotEmpty) {
5959
try {
60-
final resp = await http.get(Uri.parse('http://localhost:5001/api/email/pqc/public-key/${Uri.encodeComponent(recipientEmail)}'));
60+
final resp = await http.get(Uri.parse('https://quantum.pointblank.club/api/email/pqc/public-key/${Uri.encodeComponent(recipientEmail)}'));
6161
if (resp.statusCode == 200) {
6262
final data = jsonDecode(resp.body) as Map<String, dynamic>;
6363
final pk = (data['data'] as Map<String, dynamic>)['publicKey'] as String;

0 commit comments

Comments
 (0)