Skip to content

Commit ca49b65

Browse files
author
Incharajayaram
committed
changes in backend
1 parent 05471b9 commit ca49b65

1 file changed

Lines changed: 128 additions & 11 deletions

File tree

Email_client/QuMail.EmailProtocol/Controllers/EmailController.cs

Lines changed: 128 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,13 @@ public async Task<IActionResult> SendPqcEncryptedEmail([FromBody] SendPqcEncrypt
238238

239239
// PHASE 2: Apply AES encryption to PQC-encrypted data
240240
_logger.LogInformation("Applying AES encryption to PQC data");
241-
var aesSubject = await EncryptWithAESGCMAsync(request.PqcEncryptedSubject);
242-
var aesBody = await EncryptWithAESGCMAsync(request.PqcEncryptedBody);
241+
242+
// Prepare PQC JSON envelopes for AES encryption (base64 encode to preserve structure)
243+
var pqcSubjectForAES = PreparePqcEnvelopeForAES(request.PqcEncryptedSubject);
244+
var pqcBodyForAES = PreparePqcEnvelopeForAES(request.PqcEncryptedBody);
245+
246+
var aesSubject = await EncryptWithAESGCMAsync(pqcSubjectForAES);
247+
var aesBody = await EncryptWithAESGCMAsync(pqcBodyForAES);
243248

244249
// PHASE 3: Apply OTP encryption to AES-encrypted data
245250
_logger.LogInformation("Applying OTP encryption to AES data");
@@ -257,10 +262,13 @@ public async Task<IActionResult> SendPqcEncryptedEmail([FromBody] SendPqcEncrypt
257262
// Step 1: Encrypt with PQC (ContentBase64 is already base64, use directly!)
258263
var pqcEnvelope = await EncryptSingleWithPQC3LayerAsync(a.ContentBase64, recipient.PqcPublicKey);
259264

260-
// Step 2: Wrap PQC envelope with AES (NEW architecture)
261-
var aesEnvelope = await EncryptWithAESGCMAsync(pqcEnvelope);
265+
// Step 2: Prepare PQC JSON envelope for AES encryption (base64 encode to preserve structure)
266+
var pqcEnvelopeForAES = PreparePqcEnvelopeForAES(pqcEnvelope);
262267

263-
// Step 3: Wrap AES envelope with OTP (NEW architecture)
268+
// Step 3: Wrap PQC envelope with AES (NEW architecture)
269+
var aesEnvelope = await EncryptWithAESGCMAsync(pqcEnvelopeForAES);
270+
271+
// Step 4: Wrap AES envelope with OTP (NEW architecture)
264272
var finalEnvelope = await EncryptBodyAsync(aesEnvelope);
265273
encrypted.Add(new { fileName = a.FileName, contentType = a.ContentType, envelope = finalEnvelope });
266274
}
@@ -816,14 +824,19 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
816824

817825
if (!subjectDecryptionFailed && TryParseAESEnvelope(aesSubject, out var aesSubjectEnvelope))
818826
{
819-
pqcSubject = await DecryptAESAsync(aesSubjectEnvelope);
827+
var aesSubjectResult = await DecryptAESAsync(aesSubjectEnvelope);
820828
// Check if AES decryption failed
821-
if (pqcSubject.StartsWith("AES decryption failed"))
829+
if (aesSubjectResult.StartsWith("AES decryption failed"))
822830
{
823831
_logger.LogWarning("AES decryption failed for subject, using fallback message");
824832
pqcSubject = "[Decryption Failed - AES decryption error]";
825833
subjectDecryptionFailed = true;
826834
}
835+
else
836+
{
837+
// Restore the PQC JSON envelope from AES decryption result
838+
pqcSubject = RestorePqcEnvelopeFromAES(aesSubjectResult);
839+
}
827840
}
828841
else
829842
{
@@ -836,14 +849,19 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
836849

837850
if (!bodyDecryptionFailed && TryParseAESEnvelope(aesBody, out var aesBodyEnvelope))
838851
{
839-
pqcBody = await DecryptAESAsync(aesBodyEnvelope);
852+
var aesBodyResult = await DecryptAESAsync(aesBodyEnvelope);
840853
// Check if AES decryption failed
841-
if (pqcBody.StartsWith("AES decryption failed"))
854+
if (aesBodyResult.StartsWith("AES decryption failed"))
842855
{
843856
_logger.LogWarning("AES decryption failed for body, using fallback message");
844857
pqcBody = "[Decryption Failed - AES decryption error. The encryption key for this message is no longer available.]";
845858
bodyDecryptionFailed = true;
846859
}
860+
else
861+
{
862+
// Restore the PQC JSON envelope from AES decryption result
863+
pqcBody = RestorePqcEnvelopeFromAES(aesBodyResult);
864+
}
847865
}
848866
else
849867
{
@@ -893,15 +911,18 @@ public async Task<IActionResult> DecryptToPqc(Guid emailId)
893911
// Then decrypt AES layer to get PQC envelope
894912
if (TryParseAESEnvelope(aesEnvelope, out var aesEnvelopeObj))
895913
{
896-
var pqcEnvelope = await DecryptAESAsync(aesEnvelopeObj);
914+
var aesDecryptedResult = await DecryptAESAsync(aesEnvelopeObj);
897915

898916
// Check if AES decryption failed
899-
if (pqcEnvelope.StartsWith("AES decryption failed"))
917+
if (aesDecryptedResult.StartsWith("AES decryption failed"))
900918
{
901919
_logger.LogWarning("AES decryption failed for attachment {Index}: {FileName}, skipping", idx, fileName);
902920
continue;
903921
}
904922

923+
// Restore the PQC JSON envelope from AES decryption result
924+
var pqcEnvelope = RestorePqcEnvelopeFromAES(aesDecryptedResult);
925+
905926
decryptedAttachments.Add(new { fileName, contentType, pqcEnvelope });
906927
_logger.LogInformation("Successfully decrypted attachment {Index}: {FileName}", idx, fileName);
907928
}
@@ -1792,6 +1813,102 @@ private async Task<EncryptionResult> EncryptWithPQC3LayerAsync(string subject, s
17921813
return new EncryptionResult { SubjectEnvelope = subjectEnvelope, BodyEnvelope = bodyEnvelope, AttachmentsJson = attachmentsJson };
17931814
}
17941815

1816+
/// <summary>
1817+
/// Prepares PQC JSON envelope for AES encryption by base64 encoding it
1818+
/// This ensures the full PQC envelope is preserved through AES encryption/decryption
1819+
/// </summary>
1820+
/// <param name="pqcJsonEnvelope">PQC JSON envelope containing encrypted data</param>
1821+
/// <returns>Base64 encoded PQC JSON envelope for AES encryption</returns>
1822+
private string PreparePqcEnvelopeForAES(string pqcJsonEnvelope)
1823+
{
1824+
try
1825+
{
1826+
_logger.LogInformation("Preparing PQC JSON envelope for AES encryption");
1827+
1828+
// Validate that the input is a valid JSON envelope
1829+
if (string.IsNullOrWhiteSpace(pqcJsonEnvelope))
1830+
{
1831+
_logger.LogWarning("Empty PQC envelope provided, returning as-is");
1832+
return pqcJsonEnvelope;
1833+
}
1834+
1835+
// Quick validation - check if it's valid JSON
1836+
using var testDoc = JsonDocument.Parse(pqcJsonEnvelope);
1837+
if (!testDoc.RootElement.TryGetProperty("encryptedBody", out _))
1838+
{
1839+
_logger.LogWarning("PQC envelope missing 'encryptedBody' property, returning as-is");
1840+
return pqcJsonEnvelope;
1841+
}
1842+
1843+
// Convert the JSON envelope to base64 so it can be safely encrypted with AES
1844+
// This preserves the entire PQC envelope structure through the AES layer
1845+
var base64Encoded = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(pqcJsonEnvelope));
1846+
_logger.LogInformation("Successfully prepared PQC envelope for AES encryption (size: {Size} chars)", base64Encoded.Length);
1847+
return base64Encoded;
1848+
}
1849+
catch (JsonException ex)
1850+
{
1851+
_logger.LogError(ex, "Invalid JSON in PQC envelope, using original");
1852+
return pqcJsonEnvelope;
1853+
}
1854+
catch (Exception ex)
1855+
{
1856+
_logger.LogError(ex, "Failed to prepare PQC envelope for AES, using original");
1857+
return pqcJsonEnvelope; // Fallback to original if conversion fails
1858+
}
1859+
}
1860+
1861+
/// <summary>
1862+
/// Restores PQC JSON envelope from AES decryption result
1863+
/// This reverses the base64 encoding applied before AES encryption
1864+
/// </summary>
1865+
/// <param name="aesDecryptedResult">Base64 encoded PQC JSON envelope from AES decryption</param>
1866+
/// <returns>Original PQC JSON envelope</returns>
1867+
private string RestorePqcEnvelopeFromAES(string aesDecryptedResult)
1868+
{
1869+
try
1870+
{
1871+
_logger.LogInformation("Restoring PQC JSON envelope from AES decryption result");
1872+
1873+
// Validate input
1874+
if (string.IsNullOrWhiteSpace(aesDecryptedResult))
1875+
{
1876+
_logger.LogWarning("Empty AES decryption result provided, returning as-is");
1877+
return aesDecryptedResult;
1878+
}
1879+
1880+
// Convert base64 back to JSON envelope
1881+
var jsonBytes = Convert.FromBase64String(aesDecryptedResult);
1882+
var originalJsonEnvelope = System.Text.Encoding.UTF8.GetString(jsonBytes);
1883+
1884+
// Validate that the result is valid JSON with expected structure
1885+
using var testDoc = JsonDocument.Parse(originalJsonEnvelope);
1886+
if (!testDoc.RootElement.TryGetProperty("encryptedBody", out _))
1887+
{
1888+
_logger.LogWarning("Restored envelope missing 'encryptedBody' property, using original");
1889+
return aesDecryptedResult;
1890+
}
1891+
1892+
_logger.LogInformation("Successfully restored PQC envelope from AES decryption (size: {Size} chars)", originalJsonEnvelope.Length);
1893+
return originalJsonEnvelope;
1894+
}
1895+
catch (FormatException ex)
1896+
{
1897+
_logger.LogError(ex, "Invalid base64 format in AES decryption result, using original");
1898+
return aesDecryptedResult;
1899+
}
1900+
catch (JsonException ex)
1901+
{
1902+
_logger.LogError(ex, "Invalid JSON after base64 decode, using original");
1903+
return aesDecryptedResult;
1904+
}
1905+
catch (Exception ex)
1906+
{
1907+
_logger.LogError(ex, "Failed to restore PQC envelope from AES, using original: {Error}", ex.Message);
1908+
return aesDecryptedResult; // Fallback to original if conversion fails
1909+
}
1910+
}
1911+
17951912
private async Task<string> EncryptWithAESGCMAsync(string plaintext)
17961913
{
17971914
return await RetryAsync(async () =>

0 commit comments

Comments
 (0)