A one-time pad algoritmust viszonylag könnyű implementálni. Csupán arra kell ügyelni, hogy a kulcs generálásakor kapott számok tényleg véletlenszámok legyenek.
Az algoritmus implementálására készítettem egy extension metódust:
public static async Task OneTimePad(this Stream input,
Stream output,
Stream keyOutput,
IProgress<float>? progress = null,
CancellationToken cancellationToken = default)
{
long position = 0;
long size = input.Length;
int read = 0;
byte[] buffer = new byte[BufferSize];
byte[] randomKey = new byte[BufferSize];
do
{
cancellationToken.ThrowIfCancellationRequested();
RandomNumberGenerator.Fill(randomKey);
read = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
BufferPad(buffer, randomKey);
await output.WriteAsync(buffer, 0, read, cancellationToken);
await keyOutput.WriteAsync(randomKey, 0, read, cancellationToken);
position += read;
progress?.Report((float)position / size);
}
while (read > 0);
}
A BufferSize
egy konstans az algoritmusban, 4096 byte mérettel. A BufferPad
metódus végzi a buffer
és a randomKey
XOR kombinálását, vagyis lényegében ez valósítja meg a one-time pad funkciót:
private static void BufferPad(byte[] buffer, byte[] randomKey)
{
for (int i=0; i<buffer.Length; i++)
{
buffer[i] = (byte)(buffer[i] ^ randomKey[i]);
}
}
A visszafejtés hasonlóan egyszerű. A beolvasott kulcs és a titkosított bemenet XOR eredménye ismételten titkosítatlan adatot eredményez:
public static async Task OneTimeDePad(this Stream input,
Stream keyInput,
Stream output,
IProgress<float>? progress = null,
CancellationToken cancellationToken = default)
{
long position = 0;
long size = input.Length;
int read = 0;
int keyRead = 0;
byte[] buffer = new byte[BufferSize];
byte[] randomKey = new byte[BufferSize];
do
{
cancellationToken.ThrowIfCancellationRequested();
read = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
keyRead = await keyInput.ReadAsync(randomKey, 0, read, cancellationToken);
if (read != keyRead)
throw new InvalidDataException();
BufferPad(buffer, randomKey);
await output.WriteAsync(buffer, 0, read, cancellationToken);
position += read;
progress?.Report((float)position / size);
}
while (read > 0);
}