littleshop/TeleBot/TeleBot/Services/PrivacyService.cs
SysAdmin 32d80e4b54 Fix: Currency display consistency and remove PGP security vulnerability
## Critical Bug Fixes

### Currency Display (£ vs $)
- Fix MenuBuilder.cs: Replace $ with £ for product prices (line 60) and order totals (line 329)
- Fix ProductCarouselService.cs: Replace $ with £ in product captions and multi-buy offers (lines 317, 325)
- Fix CallbackHandler.cs: Replace $ with £ in order confirmation message (line 800)

### Payment Amount Display Bug
- Fix MessageFormatter.cs: Remove flawed crypto detection logic (< 1.0m check)
- Bug: Order for £700 in ETH displayed as "£1.66" instead of "1.66 ETH"
- Root cause: RequiredAmount is always stored as crypto amount, not fiat
- Solution: Always display RequiredAmount with crypto symbol
- Impact: Fixes display for XMR, DOGE, LTC, and large ETH amounts

## Security: Remove PGP Encryption Feature

### Critical Security Issue Resolved
- PGP "encryption" was only Base64 encoding - NOT real encryption
- Shipping addresses stored as easily decoded text
- False sense of security for users

### Changes Made
- Mark EncryptWithPGP method as [Obsolete] in PrivacyService.cs
- Remove PGP encryption logic from order creation (LittleShopService.cs)
- Mark PGP properties as [Obsolete] in UserSession.cs models
- Disable EnablePGPEncryption feature flag in appsettings.json
- Add comments explaining feature removal

### Recommendation
Implement proper PGP encryption using BouncyCastle in future, or keep removed.

## Testing Required
- Verify all prices display with £ symbol
- Verify crypto payments show correct amount format (e.g., "1.66000000 ETH")
- Verify no PGP options appear in UI
- Test order creation without PGP encryption

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 17:36:10 +01:00

181 lines
6.6 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PgpCore;
namespace TeleBot.Services
{
public interface IPrivacyService
{
string HashIdentifier(long telegramId);
string GenerateAnonymousReference();
Task<string?> EncryptWithPGP(string data, string publicKey);
Task<HttpClient> CreateTorHttpClient();
byte[] EncryptData(byte[] data, string key);
byte[] DecryptData(byte[] encryptedData, string key);
void SanitizeLogMessage(ref string message);
}
public class PrivacyService : IPrivacyService
{
private readonly IConfiguration _configuration;
private readonly ILogger<PrivacyService> _logger;
private readonly string _salt;
public PrivacyService(IConfiguration configuration, ILogger<PrivacyService> logger)
{
_configuration = configuration;
_logger = logger;
_salt = configuration["Privacy:HashSalt"] ?? "TeleBot-Privacy-Salt-2024";
}
public string HashIdentifier(long telegramId)
{
using var sha256 = SHA256.Create();
var bytes = Encoding.UTF8.GetBytes($"{telegramId}:{_salt}");
var hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
public string GenerateAnonymousReference()
{
// Generate a random reference that can't be linked back to user
var bytes = new byte[16];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(bytes);
var reference = Convert.ToBase64String(bytes)
.Replace("+", "")
.Replace("/", "")
.Replace("=", "")
.Substring(0, 12)
.ToUpper();
return $"ANON-{reference}";
}
/// <summary>
/// ⚠️ REMOVED: PGP ENCRYPTION FEATURE HAS BEEN REMOVED ⚠️
/// This method was not properly implemented and provided a false sense of security.
/// It only performed Base64 encoding, not real encryption.
/// Feature removed to eliminate security vulnerability.
/// </summary>
[Obsolete("PGP encryption feature removed - was not properly implemented")]
public async Task<string?> EncryptWithPGP(string data, string publicKey)
{
_logger.LogWarning("⚠️ PGP encryption feature has been removed - returning null");
await Task.CompletedTask;
return null;
}
public Task<HttpClient> CreateTorHttpClient()
{
if (!_configuration.GetValue<bool>("Privacy:EnableTor"))
{
// Return regular HttpClient if Tor is disabled
return Task.FromResult(new HttpClient());
}
try
{
// Use existing Tor SOCKS proxy if available
var torPort = _configuration.GetValue<int>("Privacy:TorSocksPort", 9050);
var proxy = new WebProxy($"socks5://localhost:{torPort}");
var handler = new HttpClientHandler
{
Proxy = proxy,
UseProxy = true
};
return Task.FromResult(new HttpClient(handler));
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to create Tor HTTP client, falling back to regular client");
return Task.FromResult(new HttpClient());
}
}
public byte[] EncryptData(byte[] data, string key)
{
using var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
// Derive key from string
using var sha256 = SHA256.Create();
aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
var nonce = new byte[12];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(nonce);
aes.IV = nonce;
using var encryptor = aes.CreateEncryptor();
var encrypted = encryptor.TransformFinalBlock(data, 0, data.Length);
// Combine nonce and encrypted data
var result = new byte[nonce.Length + encrypted.Length];
Buffer.BlockCopy(nonce, 0, result, 0, nonce.Length);
Buffer.BlockCopy(encrypted, 0, result, nonce.Length, encrypted.Length);
return result;
}
public byte[] DecryptData(byte[] encryptedData, string key)
{
using var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
// Derive key from string
using var sha256 = SHA256.Create();
aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
// Extract nonce
var nonce = new byte[12];
Buffer.BlockCopy(encryptedData, 0, nonce, 0, nonce.Length);
aes.IV = nonce;
// Extract encrypted portion
var encrypted = new byte[encryptedData.Length - nonce.Length];
Buffer.BlockCopy(encryptedData, nonce.Length, encrypted, 0, encrypted.Length);
using var decryptor = aes.CreateDecryptor();
return decryptor.TransformFinalBlock(encrypted, 0, encrypted.Length);
}
public void SanitizeLogMessage(ref string message)
{
// Remove potential PII from log messages
message = System.Text.RegularExpressions.Regex.Replace(
message,
@"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
"[EMAIL_REDACTED]"
);
message = System.Text.RegularExpressions.Regex.Replace(
message,
@"\b\d{3}[-.]?\d{3}[-.]?\d{4}\b",
"[PHONE_REDACTED]"
);
message = System.Text.RegularExpressions.Regex.Replace(
message,
@"\b\d{16}\b",
"[CARD_REDACTED]"
);
// Remove Telegram user IDs
message = System.Text.RegularExpressions.Regex.Replace(
message,
@"telegram_id[:=]\d+",
"telegram_id=[REDACTED]"
);
}
}
}