Encrypt Discord token at rest in settings file (machine-bound)#1491
Encrypt Discord token at rest in settings file (machine-bound)#1491
Conversation
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
|
@copilot additional requirement: the token should be decryptable only on the machine it was encrypted for. If |
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent Discord tokens from being stored in plain text inside Settings.dat by introducing transparent encryption at the JSON serialization layer and updating UI tooltips to clarify the security properties.
Changes:
- Added
TokenEncryptionConverter(JsonConverter<string?>) that encrypts/decryptsLastTokenvalues using AES-CBC with a randomized IV and randomized plaintext prefix, storing values asenc:<base64(...)>. - Annotated
SettingsService.LastTokenwith the new converter for backward-compatible read and automatic re-save in encrypted form. - Updated localization strings for
PersistTokenTooltipacross all supported languages to mention encrypted-at-rest storage.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| DiscordChatExporter.Gui/Services/TokenEncryptionConverter.cs | Introduces the token encryption/decryption converter and on-disk format (enc: + Base64 blob). |
| DiscordChatExporter.Gui/Services/SettingsService.cs | Applies the converter to LastToken so persisted settings no longer store the token in plaintext. |
| DiscordChatExporter.Gui/Localization/LocalizationManager.{English,French,German,Spanish,Ukrainian}.cs | Updates tooltip text to reflect encrypted-at-rest storage and its limitations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var decrypted = aes.DecryptCbc(data[17..], data[..16]); | ||
| return Encoding.UTF8.GetString(decrypted[data[16]..]); |
There was a problem hiding this comment.
Read() allocates multiple temporary arrays/strings via range slicing (data[17..], data[..16], and decrypted[...]). Since DecryptCbc and Encoding.UTF8.GetString accept spans, consider using data.AsSpan(...) and decrypted.AsSpan(padLen) (and cache padLen) to avoid these allocations.
| var decrypted = aes.DecryptCbc(data[17..], data[..16]); | |
| return Encoding.UTF8.GetString(decrypted[data[16]..]); | |
| var ivSpan = data.AsSpan(0, 16); | |
| var padLen = data[16]; | |
| var cipherSpan = data.AsSpan(17); | |
| var decrypted = aes.DecryptCbc(cipherSpan, ivSpan); | |
| return Encoding.UTF8.GetString(decrypted.AsSpan(padLen)); |
|
|
||
| try | ||
| { | ||
| var data = Convert.FromBase64String(value[Prefix.Length..]); |
There was a problem hiding this comment.
We use a hex string for encoding bytes in other places, can we do that here too?
There was a problem hiding this comment.
Switched to Convert.FromHexString / Convert.ToHexStringLower in 1f791ee, consistent with the pattern in ExportAssetDownloader.cs.
| aes.Key = Key; | ||
|
|
||
| // Layout: IV (16 bytes) | padLen (1 byte) | ciphertext | ||
| var decrypted = aes.DecryptCbc(data[17..], data[..16]); |
There was a problem hiding this comment.
Why did you choose CBC? Can we use GCM? Or is it for a specific reason?
There was a problem hiding this comment.
Switched to AES-GCM in 1f791ee. CBC was the initial choice for simplicity, but GCM is strictly better here — it gives authenticated encryption (integrity via the 16-byte tag) so a tampered or wrong-key ciphertext is detected cleanly. The new layout is Nonce[12] | padLen[1] | Tag[16] | Ciphertext.
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Done in 1721ac4. The encryption key is now derived from a machine-specific identifier via PBKDF2-SHA256 (10,000 iterations):
If Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
DiscordChatExporter.Gui/Localization/LocalizationManager.English.cs
Outdated
Show resolved
Hide resolved
… annotation Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot Note the build errors |
|
@copilot can you fix the build errors please? |
…ormat exception filter Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Fixed in 93d4a63 — the CSharpier format check was failing because multiline string assignments in 5 localization files used
Done in 93d4a63 — the format job failure is fixed. All 6 affected files were reformatted by CSharpier and committed. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot currently getting this: |
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
|
@copilot new error popped up so I changed the secret to not contain |
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Read():ciphernaming (encrypted data being read)Write():cipherSourcevariable for plaintext span before in-place encryption; layout comment with size annotationPersistTokenTooltip = """inline)TokenEncryptionConverter.cs(exception filter style + trailing newline)💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.