The Walter NuGet Package
Welcome to the WALTER Framework documentation! WALTER stands for Workable Algorithms for Location-aware Transmission, Encryption Response, offering a suite of NuGet packages designed for .NET Standard, Core, and various C++ environments. Let's explore its capabilities and how it can enhance your development process.
About this Nuget Package
This package is designed for projects ranging from Web to MAUI, Native Windows, Linux, or Mac applications. Although not intended for direct use, it includes useful features and extension methods. Discover more and see code samples on our GitHub page.
Ahead-Of-Time (AOT) Compilation Compliance
Json Extensions
using var stream = File.OpenRead("AppData\\LinkedIn.json"); var sr = new Walter.JsonStreamReader<TCPIPNetwork>(stream, TCPIPNetworkJsonContext.Default.TCPIPNetwork); int i = 0; var items = new List<TCPIPNetwork>(); await foreach (TCPIPNetwork item in sr.ReadAsync()) { i++; items.Add(item); if (sr.Errors.Count > 0) { _logger.LogError(sr.Errors[^1], "File line {line}, failed so entry {i} is skipped as it failed due to {error}", sr.Errors[^1].LineNumber, i + 1, sr.Errors[^1].Message); } }
The sample shows fetching a JSON stream from a web service streaming endpoint and processing the data row by row:
using System.Net.Http; using System.IO; using System.Threading.Tasks; // ... other necessary namespaces public async Task ProcessJsonDataFromWebServiceAsync() { using var httpClient = new HttpClient(); try { var response = await httpClient.GetStreamAsync("https://example.com/api/datastream"); var sr = new Walter.JsonStreamReader<TCPIPNetwork>(response, TCPIPNetworkJsonContext.Default.TCPIPNetwork); int i = 0; await foreach (TCPIPNetwork item in sr.ReadAsync()) { i++; // Process each item // ... if (sr.Errors.Count > 0) { _logger.LogError(sr.Errors[i - 1], "File line {line}, failed so entry {i} is skipped as it failed due to {error}", sr.Errors[i - 1].LineNumber, i, sr.Errors[i - 1].Message); } } } catch (HttpRequestException e) { _logger.LogError("Error fetching data from web service: {Message}", e.Message); } }
Read a file in one time and process all entries skip those that would fail.
using var stream = File.OpenRead("TestData\\LinkedIn.json"); var sr = new Walter.JsonStreamReader<List<TCPIPNetwork>>(stream,TCPIPNetworkListJsonContext.Default.ListTCPIPNetwork); var list = sr.Read(); foreach (var item in list) { ... }
Secure Encryption and Decryption
// Sample to demonstrate GDPR-compliant encryption of sensitive data using deterministic encryption // for storage in a third-party hosted SQL server. // Define the company name to be encrypted. string companyName = "Undefined Corp"; // Create an instance of the symmetric encryption service with a secure password and salt. // Note: In a production environment, securely manage the password and salt, avoiding hardcoded values. var encryptionService = new Walter.Cypher.DeterministicEncryption( password: "My $ectet Pa$w0rd", salt: "123456789+*ç%&/" ); // Encrypt the company name into a byte array. byte[] encryptedBytes = encryptionService.Encrypt(companyName.ToBytes()); // Prepare the T-SQL command for data insertion, using the encrypted company name. var tsql = @$" declare @UndefinedCorp Varbinary(64) = {encryptedBytes.ToSqlBinaryString()}; declare @checksum int = CHECKSUM(@UndefinedCorp); // Check for the existence of the company and insert if not present. if not exists(select * from [dbo].[Companies] where [CompanyName] = @UndefinedCorp and [cs_CompanyName] = @checksum) BEGIN INSERT [dbo].[Companies] ([CompanyName],[cs_CompanyName],[TrueUpDays],[AutoInvoice],[ApplicableLicenseExcempt]) Values(@UndefinedCorp, @checksum, -1, 0, 1); END "; // Execute the T-SQL command to store the encrypted data. using var con = new SqlConnection(config.GetConnectionString("Billing")); using var cmd = con.CreateCommand(); cmd.CommandText = tsql; cmd.CommandType = System.Data.CommandType.Text; con.Open(); cmd.ExecuteNonQuery();
Extension Methods
var encryptedDataString = encryptedBytes.ToSqlBinaryString(); var tsql = @$" declare @EncryptedData Varbinary(64) = {encryptedDataString}; ... INSERT INTO MyTable (EncryptedColumn) VALUES (@EncryptedData); ";
Sample Test Cases for Understanding the Symmetric Encryption
Why Test Cases?
Hands-On Learning: By setting breakpoints and stepping through these tests, you can gain a hands-on understanding of how the encryption and decryption process works.
Debugging and Inspection: It's an excellent opportunity to inspect the flow of data, observe how the encryption algorithm behaves, and understand how different components interact.
Real-World Examples: These tests are more than theoretical scenarios; they represent real-world use cases, helping you relate the functionality to practical applications.
What's in the Sample?
Encryption Consistency Test:Encrypt_WithSamePassword_ShouldGenerateDifferentCiphertexts ensures that the encryption process is secure and generates different ciphertexts for the same plaintext.
Cross-Instance Compatibility Test:EncryptAndDecrypt_WithDifferentInstances_ShouldBeCompatible confirms that the encrypted data by one instance can be decrypted by another, ensuring consistency across different instances.
How to Use the Sample
- Install the 'walter' NuGet Package: Start by adding the 'walter' package to your C# project. This package is essential as it contains the components you'll need for the encryption examples.
- Navigate to your Tests: Copy and paste the test cases in the project's test project.
- Set Breakpoints: Place breakpoints at critical points in the tests.
- Debug and Step Through: Run the tests in debug mode and step through the code to observe how the encryption process is executed and validated.
We encourage you to explore these tests to deepen your understanding of symmetric encryption in a .NET environment.
[TestClass] public class SymmetricEncryptionTests { // This test verifies that the same text encrypted with the same password generates different byte arrays. // This is important to ensure that the encryption algorithm uses a unique initialization vector (IV) for each encryption, // which enhances security by producing different ciphertexts for the same plaintext. [TestMethod] public void Encrypt_WithSamePassword_ShouldGenerateDifferentCiphertexts() { var secretText = "Hello World"; var encryptionInstance1 = new SymmetricEncryption("TestPassword"); var encryptionInstance2 = new SymmetricEncryption("TestPassword"); byte[] encryptedBytes1 = encryptionInstance1.Encrypt(Encoding.UTF8.GetBytes(secretText)); byte[] encryptedBytes2 = encryptionInstance2.Encrypt(Encoding.UTF8.GetBytes(secretText)); string ciphertext1 = Encoding.UTF8.GetString(encryptedBytes1); string ciphertext2 = Encoding.UTF8.GetString(encryptedBytes2); Assert.AreNotEqual(ciphertext1, ciphertext2, "Encrypted bytes should be different for the same input text."); string decryptedText1 = Encoding.UTF8.GetString(encryptionInstance1.Decrypt(encryptedBytes1)); string decryptedText2 = Encoding.UTF8.GetString(encryptionInstance2.Decrypt(encryptedBytes2)); Assert.AreEqual(decryptedText1, decryptedText2, "Decrypted texts should match the original secret text."); } // This test ensures that text encrypted by one instance of the SymmetricEncryption class // can be decrypted by another instance using the same password. This is crucial for verifying // that the encryption and decryption processes are compatible and consistent across different instances. [TestMethod] public void EncryptAndDecrypt_WithDifferentInstances_ShouldBeCompatible() { var secretText = "Hello World"; var encryptionInstanceClient = new SymmetricEncryption("TestPassword"); var encryptionInstanceServer = new SymmetricEncryption("TestPassword"); string ciphertext = encryptionInstanceClient.EncryptString(secretText); string decryptedText = encryptionInstanceServer.DecryptString(ciphertext); Assert.AreEqual(secretText, decryptedText, "Decrypted text should match the original secret text."); } }
AoT Exceptions Extension for .NET
The ExceptionsExtension class is a powerful utility for .NET developers, enhancing exception handling with additional diagnostic information. This extension provides methods to extract class names, method names, file names, and approximate line numbers from exceptions especially useful in AoT.
Features:
- ClassName: Retrieves the class name where the exception originated.
- MethodName: Obtains the method name that generated the exception.
- FileName:Gets the filename of the class that generated the exception.
- CodeLineNumber: Provides the actual or approximate line number where the exception was thrown.
try { _ = File.Open("A:\\doesNotExist.txt", FileMode.Open); } catch (Exception e) { //if the binary is AoT compiled the line number is this line _logger.LogError(e, "{Class}.{method} (line {line}) failed with a {exception}:{message}",e.ClassName(), e.MethodName(), e.CodeLineNumber(),e.GetType().Name,e.Message); }

