Skip to content

Commit e92469d

Browse files
author
Incharajayaram
committed
fixed pqc attachments issue
1 parent 3ad277a commit e92469d

10 files changed

Lines changed: 170 additions & 12 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Database Configuration
22
DB_HOST=localhost
3-
DB_PORT=5437
3+
DB_PORT=5432
44
DB_NAME=your_database_name
55
DB_USERNAME=your_db_username
66
DB_PASSWORD=your_db_password
0 Bytes
Binary file not shown.

Email_client/QuMail.EmailProtocol/Controllers/EmailController.cs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,42 @@ public async Task<IActionResult> DecryptToPqc2(Guid emailId)
657657
pqcBody = email.Body;
658658
}
659659

660+
// Decrypt attachments from OTP layer to PQC layer
661+
_logger.LogInformation("Decrypting attachments for PQC_2_LAYER email");
662+
string? pqcAttachmentsJson = null;
663+
if (!string.IsNullOrWhiteSpace(email.Attachments))
664+
{
665+
try
666+
{
667+
var attachmentsList = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(email.Attachments, _jsonOptions);
668+
if (attachmentsList != null && attachmentsList.Count > 0)
669+
{
670+
var decryptedAttachments = new List<object>();
671+
foreach (var item in attachmentsList)
672+
{
673+
var fileName = item.ContainsKey("fileName") ? item["fileName"]?.ToString() : null;
674+
var contentType = item.ContainsKey("contentType") ? item["contentType"]?.ToString() : null;
675+
var envelope = item.ContainsKey("envelope") ? item["envelope"]?.ToString() : null;
676+
677+
if (!string.IsNullOrWhiteSpace(fileName) && !string.IsNullOrWhiteSpace(envelope))
678+
{
679+
// Decrypt OTP layer to get PQC envelope
680+
if (TryParseEnvelope(envelope, out var otpEnvelope))
681+
{
682+
var pqcEnvelope = await DecryptOTPAsync(otpEnvelope);
683+
decryptedAttachments.Add(new { fileName, contentType, pqcEnvelope });
684+
}
685+
}
686+
}
687+
pqcAttachmentsJson = JsonSerializer.Serialize(decryptedAttachments, _jsonOptions);
688+
}
689+
}
690+
catch (Exception ex)
691+
{
692+
_logger.LogError(ex, "Failed to decrypt attachments for PQC_2_LAYER email");
693+
}
694+
}
695+
660696
// Return PQC-encrypted data to frontend
661697
_logger.LogInformation("Returning PQC-encrypted data to frontend for client-side decryption");
662698

@@ -668,6 +704,7 @@ public async Task<IActionResult> DecryptToPqc2(Guid emailId)
668704
recipientEmail = email.RecipientEmail,
669705
pqcEncryptedSubject = pqcSubject, // PQC-encrypted, to be decrypted on frontend
670706
pqcEncryptedBody = pqcBody, // PQC-encrypted, to be decrypted on frontend
707+
pqcAttachmentsJson = pqcAttachmentsJson, // PQC-encrypted attachments for frontend decryption
671708
sentAt = email.SentAt,
672709
isRead = email.IsRead,
673710
encryptionMethod = email.EncryptionMethod,
@@ -765,7 +802,49 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
765802
pqcBody = aesBody;
766803
}
767804

768-
// PHASE 3: Return PQC-encrypted data to frontend
805+
// PHASE 3: Decrypt attachments from OTP+AES layers to PQC layer
806+
_logger.LogInformation("Decrypting attachments for PQC_3_LAYER email");
807+
string? pqcAttachmentsJson = null;
808+
if (!string.IsNullOrWhiteSpace(email.Attachments))
809+
{
810+
try
811+
{
812+
var attachmentsList = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(email.Attachments, _jsonOptions);
813+
if (attachmentsList != null && attachmentsList.Count > 0)
814+
{
815+
var decryptedAttachments = new List<object>();
816+
foreach (var item in attachmentsList)
817+
{
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))
823+
{
824+
// Decrypt OTP layer first
825+
if (TryParseEnvelope(envelope, out var otpEnvelope))
826+
{
827+
var aesEnvelope = await DecryptOTPAsync(otpEnvelope);
828+
829+
// Then decrypt AES layer to get PQC envelope
830+
if (TryParseAESEnvelope(aesEnvelope, out var aesEnvelopeObj))
831+
{
832+
var pqcEnvelope = await DecryptAESAsync(aesEnvelopeObj);
833+
decryptedAttachments.Add(new { fileName, contentType, pqcEnvelope });
834+
}
835+
}
836+
}
837+
}
838+
pqcAttachmentsJson = JsonSerializer.Serialize(decryptedAttachments, _jsonOptions);
839+
}
840+
}
841+
catch (Exception ex)
842+
{
843+
_logger.LogError(ex, "Failed to decrypt attachments for PQC_3_LAYER email");
844+
}
845+
}
846+
847+
// PHASE 4: Return PQC-encrypted data to frontend
769848
_logger.LogInformation("Returning PQC-encrypted data to frontend for client-side decryption");
770849

771850
return Ok(new {
@@ -776,6 +855,7 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
776855
recipientEmail = email.RecipientEmail,
777856
pqcEncryptedSubject = pqcSubject, // PQC-encrypted, to be decrypted on frontend
778857
pqcEncryptedBody = pqcBody, // PQC-encrypted, to be decrypted on frontend
858+
pqcAttachmentsJson = pqcAttachmentsJson, // PQC-encrypted attachments for frontend decryption
779859
sentAt = email.SentAt,
780860
isRead = email.IsRead,
781861
encryptionMethod = email.EncryptionMethod,
@@ -1674,12 +1754,13 @@ private async Task<string> EncryptSingleWithPQC2LayerAsync(string plaintext, str
16741754
{
16751755
encryptedBody = encrypted.EncryptedBody,
16761756
pqcCiphertext = encrypted.PQCCiphertext,
1757+
encryptedKeyId = encrypted.EncryptedKeyId, // REQUIRED for proper decryption
16771758
algorithm = encrypted.Algorithm,
16781759
keyId = encrypted.KeyId,
16791760
securityLevel = "Kyber512",
16801761
useAES = false
16811762
};
1682-
1763+
16831764
return JsonSerializer.Serialize(envelope, _jsonOptions);
16841765
}
16851766
catch (Exception ex)
@@ -1705,12 +1786,13 @@ private async Task<string> EncryptSingleWithPQC3LayerAsync(string plaintext, str
17051786
{
17061787
encryptedBody = encrypted.EncryptedBody,
17071788
pqcCiphertext = encrypted.PQCCiphertext,
1789+
encryptedKeyId = encrypted.EncryptedKeyId, // REQUIRED for proper decryption
17081790
algorithm = encrypted.Algorithm,
17091791
keyId = encrypted.KeyId,
17101792
securityLevel = encrypted.SecurityLevel,
17111793
useAES = encrypted.UseAES
17121794
};
1713-
1795+
17141796
return JsonSerializer.Serialize(envelope, _jsonOptions);
17151797
}
17161798
catch (Exception ex)

README.md

0 Bytes

Create a .env file in the root directory:

# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_PORT=5437
DB_NAME=quantum_auth
DB_USERNAME=postgres
DB_PASSWORD=your_postgres_password_here

# JWT Configuration
JWT_SECRET_KEY=your-super-secret-jwt-key-here-must-be-at-least-32-characters-long-for-security
JWT_ISSUER=QuMail
JWT_AUDIENCE=QuMail-Users
JWT_EXPIRES_MINUTES=60

# Application Secret (Generate a new 32-character base64 key)
APP_SECRET_KEY=your-base64-encoded-secret-key-here

4. Build and Start Services

Option A: Automated Setup (Windows)

# Start all services automatically
start_server.bat

auth/auth_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
# Database configuration
3030
DB_HOST = os.getenv('DB_HOST', 'postgres')
31-
DB_PORT = os.getenv('DB_PORT', '5437')
31+
DB_PORT = os.getenv('DB_PORT', '5432')
3232
DB_NAME = os.getenv('DB_NAME', 'quantum_auth')
3333
DB_USERNAME = os.getenv('DB_USERNAME', 'postgres')
3434
DB_PASSWORD = os.getenv('DB_PASSWORD', 'quantum_secure_password_2024')

docker/database/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /etc/postgresql
1616
RUN chown -R postgres:postgres /etc/postgresql
1717

1818
# Expose port
19-
EXPOSE 5437
19+
EXPOSE 5432
2020

2121
# Add health check
2222
HEALTHCHECK --interval=10s --timeout=5s --retries=5 \

docker/database/postgresql.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Connection Settings
44
listen_addresses = '*'
5-
port = 5437
5+
port = 5432
66
max_connections = 100
77

88
# Memory Settings

docker/docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ services:
8686
environment:
8787
- FLASK_ENV=production
8888
- DB_HOST=postgres
89-
- DB_PORT=5437
89+
- DB_PORT=5432
9090
- DB_NAME=quantum_auth
9191
- DB_USERNAME=postgres
9292
- DB_PASSWORD=${DB_PASSWORD:-quantum_secure_password_2024}
@@ -116,7 +116,7 @@ services:
116116
- ASPNETCORE_ENVIRONMENT=Production
117117
- ASPNETCORE_URLS=http://+:5001
118118
- DB_HOST=postgres
119-
- DB_PORT=5437
119+
- DB_PORT=5432
120120
- DB_NAME=quantum_auth
121121
- DB_USERNAME=postgres
122122
- DB_PASSWORD=${DB_PASSWORD:-quantum_secure_password_2024}

env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Database Configuration
22
DB_HOST=localhost
3-
DB_PORT=5437
3+
DB_PORT=5432
44
DB_NAME=quantum_auth
55
DB_USERNAME=postgres
66
DB_PASSWORD=your_postgres_password_here

frontend/lib/services/email_service.dart

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,14 @@ class EmailService {
433433
print('[EmailService] Recipient validated, public key: ${recipientPublicKey.substring(0, 50)}...');
434434

435435
// Step 2: Encrypt subject and body with PQC on frontend
436-
final pqcEncryptedSubject = await encryptWithPqcFrontend(subject, recipientPublicKey);
437-
final pqcEncryptedBody = await encryptWithPqcFrontend(body, recipientPublicKey);
436+
// Use placeholder for empty subject/body to avoid backend validation errors
437+
final subjectToEncrypt = subject.trim().isEmpty ? "(No Subject)" : subject;
438+
final bodyToEncrypt = body.trim().isEmpty ? " " : body;
439+
print('[EmailService] Subject to encrypt: "$subjectToEncrypt"');
440+
print('[EmailService] Body to encrypt: "${bodyToEncrypt.substring(0, bodyToEncrypt.length > 50 ? 50 : bodyToEncrypt.length)}..."');
441+
442+
final pqcEncryptedSubject = await encryptWithPqcFrontend(subjectToEncrypt, recipientPublicKey);
443+
final pqcEncryptedBody = await encryptWithPqcFrontend(bodyToEncrypt, recipientPublicKey);
438444

439445
if (pqcEncryptedSubject == null || pqcEncryptedBody == null) {
440446
print('[EmailService] PQC encryption failed');
@@ -734,13 +740,48 @@ class EmailService {
734740
return null;
735741
}
736742

743+
// Decrypt attachments (if present)
744+
final List<EmailAttachment> decryptedAttachments = [];
745+
final pqcAttachmentsJson = emailData['pqcAttachmentsJson'] as String?;
746+
if (pqcAttachmentsJson != null && pqcAttachmentsJson.isNotEmpty) {
747+
try {
748+
print('[EmailService] Found pqcAttachmentsJson, decrypting attachments...');
749+
final attachmentsList = jsonDecode(pqcAttachmentsJson) as List;
750+
print('[EmailService] Found ${attachmentsList.length} encrypted attachments');
751+
752+
for (int i = 0; i < attachmentsList.length; i++) {
753+
final attachmentData = attachmentsList[i] as Map<String, dynamic>;
754+
final fileName = attachmentData['fileName'] as String;
755+
final contentType = attachmentData['contentType'] as String? ?? 'application/octet-stream';
756+
final pqcEnvelope = attachmentData['pqcEnvelope'] as String;
757+
758+
print('[EmailService] Decrypting attachment $i: $fileName');
759+
final decryptedBase64 = await decryptWithPqcFrontend(pqcEnvelope);
760+
761+
if (decryptedBase64 != null) {
762+
decryptedAttachments.add(EmailAttachment(
763+
fileName: fileName,
764+
contentType: contentType,
765+
contentBase64: decryptedBase64,
766+
));
767+
print('[EmailService] ✅ Successfully decrypted attachment: $fileName');
768+
} else {
769+
print('[EmailService] ❌ Failed to decrypt attachment: $fileName');
770+
}
771+
}
772+
} catch (e) {
773+
print('[EmailService] Error decrypting attachments: $e');
774+
}
775+
}
776+
737777
// Create Email object with decrypted data
738778
return Email(
739779
id: emailData['id'] as String,
740780
senderEmail: emailData['senderEmail'] as String,
741781
recipientEmail: emailData['recipientEmail'] as String,
742782
subject: decryptedSubject,
743783
body: decryptedBody,
784+
attachments: decryptedAttachments,
744785
sentAt: DateTime.parse(emailData['sentAt'] as String),
745786
isRead: emailData['isRead'] as bool,
746787
encryptionMethod: emailData['encryptionMethod'] as String,
@@ -784,13 +825,48 @@ class EmailService {
784825
return null;
785826
}
786827

828+
// Decrypt attachments (if present)
829+
final List<EmailAttachment> decryptedAttachments = [];
830+
final pqcAttachmentsJson = emailData['pqcAttachmentsJson'] as String?;
831+
if (pqcAttachmentsJson != null && pqcAttachmentsJson.isNotEmpty) {
832+
try {
833+
print('[EmailService] Found pqcAttachmentsJson, decrypting attachments...');
834+
final attachmentsList = jsonDecode(pqcAttachmentsJson) as List;
835+
print('[EmailService] Found ${attachmentsList.length} encrypted attachments');
836+
837+
for (int i = 0; i < attachmentsList.length; i++) {
838+
final attachmentData = attachmentsList[i] as Map<String, dynamic>;
839+
final fileName = attachmentData['fileName'] as String;
840+
final contentType = attachmentData['contentType'] as String? ?? 'application/octet-stream';
841+
final pqcEnvelope = attachmentData['pqcEnvelope'] as String;
842+
843+
print('[EmailService] Decrypting attachment $i: $fileName');
844+
final decryptedBase64 = await decryptWithPqcFrontend(pqcEnvelope);
845+
846+
if (decryptedBase64 != null) {
847+
decryptedAttachments.add(EmailAttachment(
848+
fileName: fileName,
849+
contentType: contentType,
850+
contentBase64: decryptedBase64,
851+
));
852+
print('[EmailService] ✅ Successfully decrypted attachment: $fileName');
853+
} else {
854+
print('[EmailService] ❌ Failed to decrypt attachment: $fileName');
855+
}
856+
}
857+
} catch (e) {
858+
print('[EmailService] Error decrypting attachments: $e');
859+
}
860+
}
861+
787862
// Create Email object with decrypted data
788863
return Email(
789864
id: emailData['id'] as String,
790865
senderEmail: emailData['senderEmail'] as String,
791866
recipientEmail: emailData['recipientEmail'] as String,
792867
subject: decryptedSubject,
793868
body: decryptedBody,
869+
attachments: decryptedAttachments,
794870
sentAt: DateTime.parse(emailData['sentAt'] as String),
795871
isRead: emailData['isRead'] as bool,
796872
encryptionMethod: emailData['encryptionMethod'] as String,

0 commit comments

Comments
 (0)