From f7306bc440ebf000b31d302868fbd1997b2ae69f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:20:20 +0000 Subject: [PATCH 1/4] Initial plan From aacaeb28adb882ff6a70ab321752a8a76219136e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:24:55 +0000 Subject: [PATCH 2/4] Replace System.Random with cryptographically secure HMACSHA256-based PRNG Co-authored-by: tuduce <73273884+tuduce@users.noreply.github.com> --- JoinFS.Tests/ProgramCode.cs | 48 ++++++++++++++++++++++++++++++++++++- JoinFS/Program.cs | 48 ++++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/JoinFS.Tests/ProgramCode.cs b/JoinFS.Tests/ProgramCode.cs index f09fdeb9..5f8e0f50 100644 --- a/JoinFS.Tests/ProgramCode.cs +++ b/JoinFS.Tests/ProgramCode.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using System.Security.Cryptography; namespace JoinFS.Tests { @@ -9,6 +10,51 @@ namespace JoinFS.Tests /// public static class ProgramCode { + /// + /// Cryptographically secure random number generator seeded with a key + /// + private class SeededCryptoRandom + { + private readonly byte[] keyState; + private int counter; + + public SeededCryptoRandom(int key) + { + // Create deterministic initial state from key using SHA256 + byte[] keyBytes = BitConverter.GetBytes(key); + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + keyState = sha256.ComputeHash(keyBytes); + } + counter = 0; + } + + /// + /// Generate a deterministic but cryptographically derived random number in range [0, maxValue) + /// + public int Next(int maxValue) + { + if (maxValue <= 0) + throw new ArgumentOutOfRangeException(nameof(maxValue)); + + // Derive random bytes deterministically from key state and counter + byte[] input = new byte[keyState.Length + 4]; + Buffer.BlockCopy(keyState, 0, input, 0, keyState.Length); + Buffer.BlockCopy(BitConverter.GetBytes(counter++), 0, input, keyState.Length, 4); + + // Use HMACSHA256 for cryptographically strong deterministic generation + byte[] output; + using (var hmac = new System.Security.Cryptography.HMACSHA256(keyState)) + { + output = hmac.ComputeHash(input); + } + + // Convert first 4 bytes to int and ensure it's in the desired range + uint randomValue = BitConverter.ToUInt32(output, 0); + return (int)(randomValue % (uint)maxValue); + } + } + public static string? Code(string? s, bool bToCode, int nKey) { if (s == null) @@ -70,7 +116,7 @@ public static class ProgramCode int nInc = 0; int k; const int nPasses = 11; - Random rnd = new Random(nKey); + SeededCryptoRandom rnd = new SeededCryptoRandom(nKey); for (int j = 0; j < nPasses; ++j) { k = (bToCode) ? j : nPasses - j - 1; diff --git a/JoinFS/Program.cs b/JoinFS/Program.cs index 8bfdc276..3bb02288 100644 --- a/JoinFS/Program.cs +++ b/JoinFS/Program.cs @@ -12,6 +12,7 @@ using System.Configuration; using System.Globalization; using System.Reflection; +using System.Security.Cryptography; using JoinFS.Properties; namespace JoinFS @@ -1935,6 +1936,51 @@ public void MonitorAircraftDetails() static class Program { + /// + /// Cryptographically secure random number generator seeded with a key + /// + private class SeededCryptoRandom + { + private readonly byte[] keyState; + private int counter; + + public SeededCryptoRandom(int key) + { + // Create deterministic initial state from key using SHA256 + byte[] keyBytes = BitConverter.GetBytes(key); + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + keyState = sha256.ComputeHash(keyBytes); + } + counter = 0; + } + + /// + /// Generate a deterministic but cryptographically derived random number in range [0, maxValue) + /// + public int Next(int maxValue) + { + if (maxValue <= 0) + throw new ArgumentOutOfRangeException(nameof(maxValue)); + + // Derive random bytes deterministically from key state and counter + byte[] input = new byte[keyState.Length + 4]; + Buffer.BlockCopy(keyState, 0, input, 0, keyState.Length); + Buffer.BlockCopy(BitConverter.GetBytes(counter++), 0, input, keyState.Length, 4); + + // Use HMACSHA256 for cryptographically strong deterministic generation + byte[] output; + using (var hmac = new System.Security.Cryptography.HMACSHA256(keyState)) + { + output = hmac.ComputeHash(input); + } + + // Convert first 4 bytes to int and ensure it's in the desired range + uint randomValue = BitConverter.ToUInt32(output, 0); + return (int)(randomValue % (uint)maxValue); + } + } + public static string Code(string s, bool bToCode, int nKey) { if (s == null) @@ -1996,7 +2042,7 @@ public static string Code(string s, bool bToCode, int nKey) int nInc = 0; int k; const int nPasses = 11; - Random rnd = new Random(nKey); + SeededCryptoRandom rnd = new SeededCryptoRandom(nKey); for (int j = 0; j < nPasses; ++j) { k = (bToCode) ? j : nPasses - j - 1; From 99c3d02d872a11503bad2894aeb2d27b7c0d3700 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:26:37 +0000 Subject: [PATCH 3/4] Address code review: Simplify using statements and add modulo bias note Co-authored-by: tuduce <73273884+tuduce@users.noreply.github.com> --- JoinFS.Tests/ProgramCode.cs | 6 ++++-- JoinFS/Program.cs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/JoinFS.Tests/ProgramCode.cs b/JoinFS.Tests/ProgramCode.cs index 5f8e0f50..c09d3518 100644 --- a/JoinFS.Tests/ProgramCode.cs +++ b/JoinFS.Tests/ProgramCode.cs @@ -22,7 +22,7 @@ public SeededCryptoRandom(int key) { // Create deterministic initial state from key using SHA256 byte[] keyBytes = BitConverter.GetBytes(key); - using (var sha256 = System.Security.Cryptography.SHA256.Create()) + using (var sha256 = SHA256.Create()) { keyState = sha256.ComputeHash(keyBytes); } @@ -44,12 +44,14 @@ public int Next(int maxValue) // Use HMACSHA256 for cryptographically strong deterministic generation byte[] output; - using (var hmac = new System.Security.Cryptography.HMACSHA256(keyState)) + using (var hmac = new HMACSHA256(keyState)) { output = hmac.ComputeHash(input); } // Convert first 4 bytes to int and ensure it's in the desired range + // Note: Using modulo introduces slight bias, but we maintain this approach + // for backward compatibility with the original implementation uint randomValue = BitConverter.ToUInt32(output, 0); return (int)(randomValue % (uint)maxValue); } diff --git a/JoinFS/Program.cs b/JoinFS/Program.cs index 3bb02288..1b5ebbc6 100644 --- a/JoinFS/Program.cs +++ b/JoinFS/Program.cs @@ -1948,7 +1948,7 @@ public SeededCryptoRandom(int key) { // Create deterministic initial state from key using SHA256 byte[] keyBytes = BitConverter.GetBytes(key); - using (var sha256 = System.Security.Cryptography.SHA256.Create()) + using (var sha256 = SHA256.Create()) { keyState = sha256.ComputeHash(keyBytes); } @@ -1970,12 +1970,14 @@ public int Next(int maxValue) // Use HMACSHA256 for cryptographically strong deterministic generation byte[] output; - using (var hmac = new System.Security.Cryptography.HMACSHA256(keyState)) + using (var hmac = new HMACSHA256(keyState)) { output = hmac.ComputeHash(input); } // Convert first 4 bytes to int and ensure it's in the desired range + // Note: Using modulo introduces slight bias, but we maintain this approach + // for backward compatibility with the original implementation uint randomValue = BitConverter.ToUInt32(output, 0); return (int)(randomValue % (uint)maxValue); } From 9fb5f9cd4af58895e22c578e277e5675f6484919 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:38:29 +0000 Subject: [PATCH 4/4] Simplify SeededCryptoRandom: Remove SHA256 layer, use HMACSHA256 directly Co-authored-by: tuduce <73273884+tuduce@users.noreply.github.com> --- JoinFS.Tests/ProgramCode.cs | 30 ++++++++---------------------- JoinFS/Program.cs | 30 ++++++++---------------------- 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/JoinFS.Tests/ProgramCode.cs b/JoinFS.Tests/ProgramCode.cs index c09d3518..c5122f56 100644 --- a/JoinFS.Tests/ProgramCode.cs +++ b/JoinFS.Tests/ProgramCode.cs @@ -15,17 +15,12 @@ public static class ProgramCode /// private class SeededCryptoRandom { - private readonly byte[] keyState; + private readonly byte[] seed; private int counter; public SeededCryptoRandom(int key) { - // Create deterministic initial state from key using SHA256 - byte[] keyBytes = BitConverter.GetBytes(key); - using (var sha256 = SHA256.Create()) - { - keyState = sha256.ComputeHash(keyBytes); - } + seed = BitConverter.GetBytes(key); counter = 0; } @@ -37,23 +32,14 @@ public int Next(int maxValue) if (maxValue <= 0) throw new ArgumentOutOfRangeException(nameof(maxValue)); - // Derive random bytes deterministically from key state and counter - byte[] input = new byte[keyState.Length + 4]; - Buffer.BlockCopy(keyState, 0, input, 0, keyState.Length); - Buffer.BlockCopy(BitConverter.GetBytes(counter++), 0, input, keyState.Length, 4); - - // Use HMACSHA256 for cryptographically strong deterministic generation - byte[] output; - using (var hmac = new HMACSHA256(keyState)) + // Use HMACSHA256 to derive random value from seed and counter + using (var hmac = new HMACSHA256(seed)) { - output = hmac.ComputeHash(input); + byte[] counterBytes = BitConverter.GetBytes(counter++); + byte[] hash = hmac.ComputeHash(counterBytes); + uint randomValue = BitConverter.ToUInt32(hash, 0); + return (int)(randomValue % (uint)maxValue); } - - // Convert first 4 bytes to int and ensure it's in the desired range - // Note: Using modulo introduces slight bias, but we maintain this approach - // for backward compatibility with the original implementation - uint randomValue = BitConverter.ToUInt32(output, 0); - return (int)(randomValue % (uint)maxValue); } } diff --git a/JoinFS/Program.cs b/JoinFS/Program.cs index 1b5ebbc6..bf099134 100644 --- a/JoinFS/Program.cs +++ b/JoinFS/Program.cs @@ -1941,17 +1941,12 @@ static class Program /// private class SeededCryptoRandom { - private readonly byte[] keyState; + private readonly byte[] seed; private int counter; public SeededCryptoRandom(int key) { - // Create deterministic initial state from key using SHA256 - byte[] keyBytes = BitConverter.GetBytes(key); - using (var sha256 = SHA256.Create()) - { - keyState = sha256.ComputeHash(keyBytes); - } + seed = BitConverter.GetBytes(key); counter = 0; } @@ -1963,23 +1958,14 @@ public int Next(int maxValue) if (maxValue <= 0) throw new ArgumentOutOfRangeException(nameof(maxValue)); - // Derive random bytes deterministically from key state and counter - byte[] input = new byte[keyState.Length + 4]; - Buffer.BlockCopy(keyState, 0, input, 0, keyState.Length); - Buffer.BlockCopy(BitConverter.GetBytes(counter++), 0, input, keyState.Length, 4); - - // Use HMACSHA256 for cryptographically strong deterministic generation - byte[] output; - using (var hmac = new HMACSHA256(keyState)) + // Use HMACSHA256 to derive random value from seed and counter + using (var hmac = new HMACSHA256(seed)) { - output = hmac.ComputeHash(input); + byte[] counterBytes = BitConverter.GetBytes(counter++); + byte[] hash = hmac.ComputeHash(counterBytes); + uint randomValue = BitConverter.ToUInt32(hash, 0); + return (int)(randomValue % (uint)maxValue); } - - // Convert first 4 bytes to int and ensure it's in the desired range - // Note: Using modulo introduces slight bias, but we maintain this approach - // for backward compatibility with the original implementation - uint randomValue = BitConverter.ToUInt32(output, 0); - return (int)(randomValue % (uint)maxValue); } }