-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
73fae8d
commit 668367d
Showing
1 changed file
with
139 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,140 @@ | ||
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) | ||
# Tango12 | ||
A stream cipher based on BLAKE2b. | ||
A stream cipher based on BLAKE2b. There are two modes: | ||
|
||
1. Tango12d: *deterministic* mode. No nonce is required. | ||
2. Tango12p: *probabilistic* mode. A nonce is required. | ||
|
||
## Should I use this? | ||
⚠️**NO, this is a demo of how a keyed hash function can be turned into a stream cipher.** | ||
|
||
## How do I play around with this? | ||
1. Install the [Sodium.Core](https://www.nuget.org/packages/Sodium.Core) NuGet package in [Visual Studio](https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio). | ||
2. Download the latest [release](https://github.com/samuel-lucas6/Tango12/releases). | ||
3. Move the downloaded DLL file into your Visual Studio project folder. | ||
3. Click on the ```Project``` tab and ```Add Project Reference...``` in Visual Studio. | ||
4. Go to ```Browse```, click the ```Browse``` button, and select the downloaded DLL file. | ||
|
||
### Tango12d | ||
⚠️**WARNING: Never reuse a key.** | ||
```c# | ||
const string message = "This is a test."; | ||
const int keyLength = 64; | ||
|
||
// The message could be a file | ||
byte[] message = Encoding.UTF8.GetBytes(message); | ||
|
||
// The key should be random for each message | ||
byte[] key = SodiumCore.GetRandomBytes(keyLength); | ||
|
||
// Encrypt the message | ||
byte[] ciphertext = Tango12d.Encrypt(message, key); | ||
|
||
// Decrypt the ciphertext | ||
byte[] plaintext = Tango12d.Decrypt(ciphertext, key); | ||
``` | ||
|
||
### Tango12p | ||
⚠️**WARNING: Never reuse a nonce with the same key.** | ||
```c# | ||
const string message = "This is a test."; | ||
const int nonceLength = 24; | ||
const int keyLength = 32; | ||
|
||
// The message could be a file | ||
byte[] message = Encoding.UTF8.GetBytes(message); | ||
|
||
// The nonce can be random. Increment the nonce for each message encrypted using the same key | ||
byte[] nonce = SodiumCore.GetRandomBytes(nonceLength); | ||
|
||
// The key can be random or derived using a KDF (e.g. Argon2, HKDF, etc) | ||
byte[] key = SodiumCore.GetRandomBytes(keyLength); | ||
|
||
// Encrypt the message | ||
byte[] ciphertext = Tango12p.Encrypt(message, nonce, key); | ||
|
||
// Decrypt the ciphertext | ||
byte[] plaintext = Tango12p.Decrypt(ciphertext, nonce, key); | ||
``` | ||
|
||
## How does it work? | ||
### Constants | ||
```c# | ||
internal const int BlockSize = 64; | ||
internal const int CounterLength = 64; | ||
internal const int NonceLength = 24; | ||
internal const int KeyLength = 64; | ||
``` | ||
|
||
### Tango12d | ||
1. An empty 64 byte counter is created. | ||
```c# | ||
var counter = new byte[Constants.CounterLength]; | ||
``` | ||
2. The message is read in blocks of 64 bytes. | ||
```c# | ||
var buffer = new byte[Constants.BlockSize]; | ||
using var memoryStream = new MemoryStream(message); | ||
while ((bytesRead = memoryStream.Read(buffer, offset: 0, buffer.Length)) > 0) | ||
``` | ||
3. For each block, BLAKE2b-512, with the counter as the message and the key as the key, is used to generate a keystream block. | ||
```c# | ||
byte[] keystreamBlock = GenericHash.Hash(counter, key, Constants.BlockSize); | ||
``` | ||
4. The message block and keystream block are XORed together to produce the ciphertext block. | ||
```c# | ||
internal static byte[] Xor(byte[] message, byte[] keystream) | ||
{ | ||
var ciphertext = new byte[message.Length]; | ||
for (int i = 0; i < ciphertext.Length; i++) | ||
{ | ||
ciphertext[i] = (byte)(message[i] ^ keystream[i]); | ||
} | ||
return ciphertext; | ||
} | ||
``` | ||
5. The counter is incremented. | ||
```c# | ||
counter = Utilities.Increment(counter); | ||
``` | ||
6. This continues until the end of the message is reached. | ||
|
||
### Tango12p | ||
|
||
1. An empty 64 byte counter is created. | ||
```c# | ||
var counter = new byte[Constants.CounterLength]; | ||
``` | ||
2. The counter and 24 byte nonce are concatenated together. | ||
```c# | ||
var counter = new byte[Constants.CounterLength]; | ||
counter = Arrays.Concat(counter, nonce); | ||
``` | ||
3. The message is read in blocks of 64 bytes. | ||
```c# | ||
var buffer = new byte[Constants.BlockSize]; | ||
using var memoryStream = new MemoryStream(message); | ||
while ((bytesRead = memoryStream.Read(buffer, offset: 0, buffer.Length)) > 0) | ||
``` | ||
4. For each block, BLAKE2b-512, with the counter and nonce as the message and the key as the key, is used to generate a keystream block. | ||
```c# | ||
byte[] keystreamBlock = GenericHash.Hash(counter, key, Constants.BlockSize); | ||
``` | ||
4. The message block and keystream block are XORed together to produce the ciphertext block. | ||
```c# | ||
internal static byte[] Xor(byte[] message, byte[] keystream) | ||
{ | ||
var ciphertext = new byte[message.Length]; | ||
for (int i = 0; i < ciphertext.Length; i++) | ||
{ | ||
ciphertext[i] = (byte)(message[i] ^ keystream[i]); | ||
} | ||
return ciphertext; | ||
} | ||
``` | ||
5. The counter is incremented. | ||
```c# | ||
counter = Utilities.Increment(counter); | ||
``` | ||
6. This continues until the end of the message is reached. | ||
|