skip to content
Relatively General .NET

Debugging a WebView2 component using Playwright in .NET

by Gérald Barré

posted on: February 14, 2022

To write end-to-end tests of an application hosting a WebView2 component, you may want to use Playwright. Playwright allows to control the browser and to interact with the DOM. This is a well-known tool to test web applications.To attach Playwright to a WebView2 component, you need to use the Chrom

Badly implementing encryption

by Oren Eini

posted on: February 11, 2022

One of the important adages in computer programming is: “Thou shall not write your own encryption”. That is because building your own encryption system is fraught will all sorts of really subtle details that are easy to get wrong, which will entirely eliminate any security that you have in place. For that matter, professional cryptographers are getting those details wrong often enough that WEP has been completely broken. Do not make use of any code that I’m showing in this series of posts to do anything except expand your horizons. This is incredibly insecure. If you want to use encryption, go and use something like Sodium, which is a well reviewed and thought-out encryption library. With that said, let’s see what kind of encryption I can implement. My math skills are lousy, so there is going to be absolutely no math whatsoever in this series. That sounds like it makes it tough to implement encryption, but it isn’t so bad. The absolute best encryption method, in terms of secrecy, is  the One Time Pad. There is a proof that it is unbreakable, which I linked but won’t discuss (nor do I claim to understand). The idea is simple, given a random set of bits, we can XOR that with our message, and be sure that there is no way to derive what the original message was. All possible values are equally likely. While One Time Pad is ideal, its use in practice has severe limitations: You can use the One Time Pad once. Using it twice invalidates the entire scheme. Here is some unfortunate history on Russian spies that reused the pads. You need to have a source of true randomness. Your One Time Pad must be at least as big as the message. The key problem with One Time Pad (pun intended) is how you distributed the pad material. Your security is only as valid as the pad transfer mechanism. And if you want to exchange complex messages, you need big pads. The key to modern encryption is that while we can’t mathematically prove that our method is as secured as One Time Pad, we can become confident enough in our methods to make practical use of that. A One Time Pad is a shared source of truly random data. If we had a way to create a random stream of bits for a smaller shared state, that might do it, right? It wouldn’t be true randomness, of course, but if there is no way to predict what the output should be without knowing a shared secret, that should be enough, we hope. How can we generate some random data from a shared secret? Well, I’m going to use the MD5 primitive as the key function here. That is a cryptographic hash function that you can feed it some buffer and it will compute a cryptographic hash of the buffer. The output of the hash function is pseudo random, so that might be a good building block to create a stream of random bits. I’m using MD5 intentionally here. I assume that by now anyone that pays any attention to cryptography will know that MD5 is considered broken. I don’t want anyone to use this method. In general, the concept that I’m going for is something like this: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters const Md5 = std.crypto.hash.Md5; pub fn generateStream(key: [Md5.digest_length]u8, size: usize, output: std.fs.File.Writer) !void { var buffer: [Md5.digest_length]u8 = undefined; Md5.hash(&key, &buffer, .{}); while (size > 0) : (size -= Md5.digest_length) { if (size < Md5.digest_length) { try output.write(buffer[0..size]); break; } try output.write(buffer); Md5.hash(&buffer, &buffer, .{}); } } view raw keyStreamGen.zig hosted with ❤ by GitHub In other words, I’m generating a stream of bits by continuously feeding the output of Md5 into itself, starting from the provided key. Again, this is not secured, I’ll touch on exactly why in a future post. A small optimization that I can make is that I don’t actually need to store all the full key bits-stream ahead of time, I can generate that on the fly, as we get data to encrypt. Here is what the encryption algorithm looks like, we generate the key stream and compute the XOR over the provided data: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters const Md5 = std.crypto.hash.Md5; const MyCryptoAlgo = struct { state: [Md5.digest_length]u8, pos: u8, pub fn init(key: [Md5.digest_length]u8) MyCryptoAlgo { var self = MyCryptoAlgo{ .state = undefined, .pos = 0 }; Md5.hash(&key, &self.state, .{}); return self; } pub fn run(self: *MyCryptoAlgo, data: []u8) void { for (data) |c, i| { if (self.pos == Md5.digest_length) { std.crypto.hash.Md5.hash(&self.state, &self.state, .{}); self.pos = 0; } data[i] = self.state[self.pos] ^ c; self.pos += 1; } } }; view raw badlyEncrpt.zig hosted with ❤ by GitHub Note that we don’t have a distinction here between encryption and decryption. We generate the key stream and XOR it with the caller’s data. If the data is plain text, it is encrypted, if it is encrypted text, it is decrypted. Here is how this will work: Key (base64) +oupDG0cMVQr7hWqctWZEA== Text attack at dawn! Encrypted (hex) 4771EC89753EEB16A899E2F79DE9D6 And the code to make it happen: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters var mca = MyCryptoAlgo.init(key); mca.run(data); view raw usage.zig hosted with ❤ by GitHub We mutate the data in place, I should note. Given that we are not changing the size of the data, that is the simplest way to write the API. This “encryption” method works, and we go from a reasonable looking plain text to something that certainly looks like it is encrypted. This is also a fairly horrible encryption scheme, of course, but I’ll touch on that in my next post.

RavenDB

by Oren Eini

posted on: February 07, 2022

My talk at the Carnegie Mellon Database Group about the internals of Voron and how we build a transactional storage engine.

Avoid DNS issues with HttpClient in .NET

by Gérald Barré

posted on: February 07, 2022

HttpClient allows to send HTTP requests. HttpClient is intended to be instantiated once and re-used throughout the life of an application. Indeed, the HttpClient has a connection pool to reuse connections and reduce the number of TCP connections. So, if you send multiple requests to the same host,

Parallelize test cases execution in xUnit

by Gérald Barré

posted on: January 31, 2022

If you have read my introduction to xUnit.NET, you know that tests are executed sequentially inside a collection. By default, a collection is created per each class. So, all tests in a class are executed sequentially. You may want to execute all tests in parallel to reduce the execution time. A wor

Talking at Carnegie Mellon Database Group: Practical Considerations for ACID/MVCC Storage Engines

by Oren Eini

posted on: January 28, 2022

Next week I’ll be talking at CMU about how to build storage engines and what you need to consider when building them. The talk is open to the public (don’t have to be at CMU to be there).Here are the details:Event Date: Monday January 31, 2022Event Time: 04:30pm ESTLocation: https://cmu.zoom.us/j/95002789605?pwd=eEtJRnRaNnQ1bFZmTnUwbDRMaXBRQT09In this talk, Oren Eini, founder of RavenDB, will discuss the design decisons and the manner in which RavenDB deals with storing data on disk.Achieving highly concurrent and transactional system can be a challenging task. RavenDB solves this issue using a storage engine called Voron. We’ll go over the design of Voron and how it is able to achieve both high performance and maintain ACID integrity.This talk is part of the Vaccination Database (Booster) Tech Talk Seminar Series.Zoom Link: https://cmu.zoom.us/j/95002789605 (Passcode 982149)

Web API DTO Considerations

by Ardalis

posted on: January 25, 2022

When designing web APIs, it's important to think about how the data being passed to and from the endpoint will be structured. How important…Keep Reading →