{"id":17395073,"url":"https://github.com/samuel-lucas6/tango12","last_synced_at":"2025-06-11T13:12:31.043Z","repository":{"id":113903082,"uuid":"337553872","full_name":"samuel-lucas6/Tango12","owner":"samuel-lucas6","description":"A stream cipher based on BLAKE2b.","archived":false,"fork":false,"pushed_at":"2022-04-11T15:33:25.000Z","size":136,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-09T23:46:47.851Z","etag":null,"topics":["blake2","blake2b","blake2b-hash-algorithm","cipher","crypto","cryptography","encryption","keystream","stream-cipher"],"latest_commit_sha":null,"homepage":"https://samuellucas.com/Tango12","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samuel-lucas6.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-09T22:29:52.000Z","updated_at":"2024-03-08T15:18:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"7d467a5b-838d-4b07-bf6e-00e49e325753","html_url":"https://github.com/samuel-lucas6/Tango12","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuel-lucas6%2FTango12","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuel-lucas6%2FTango12/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuel-lucas6%2FTango12/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuel-lucas6%2FTango12/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samuel-lucas6","download_url":"https://codeload.github.com/samuel-lucas6/Tango12/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuel-lucas6%2FTango12/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259271878,"owners_count":22832100,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blake2","blake2b","blake2b-hash-algorithm","cipher","crypto","cryptography","encryption","keystream","stream-cipher"],"created_at":"2024-10-16T11:04:10.823Z","updated_at":"2025-06-11T13:12:31.034Z","avatar_url":"https://github.com/samuel-lucas6.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/samuel-lucas6/Tango12/blob/main/LICENSE)\n[![CodeQL](https://github.com/samuel-lucas6/Tango12/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/samuel-lucas6/Tango12/actions)\n\n# Tango12\nA stream cipher based on BLAKE2b. There are two modes:\n\n1. Tango12d: *deterministic* mode. No nonce is required.\n2. Tango12p: *probabilistic* mode. A nonce is required.\n\n## Should I use this?\n⚠️**NO, this is a demo of how a keyed hash function can be turned into a stream cipher.**\n\n## How do I play around with this?\n1. 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).\n2. Download the latest [release](https://github.com/samuel-lucas6/Tango12/releases).\n3. Move the downloaded DLL file into your Visual Studio project folder.\n3. Click on the ```Project``` tab and ```Add Project Reference...``` in Visual Studio.\n4. Go to ```Browse```, click the ```Browse``` button, and select the downloaded DLL file.\n\nNote that the [libsodium](https://doc.libsodium.org/) library requires the [Visual C++ Redistributable for Visual Studio 2015-2019](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) to work on Windows. If you want your program to be portable, then you must keep the relevant (x86 or x64) ```vcruntime140.dll``` file in the same folder as your executable on Windows.\n\n### Tango12d\n⚠️**WARNING: Never reuse a key.**\n```c#\nconst string message = \"This is a test.\";\nconst int keyLength = 64;\n\n// The message could be a file\nbyte[] message = Encoding.UTF8.GetBytes(message);\n\n// The key should be random for each message\nbyte[] key = SodiumCore.GetRandomBytes(keyLength);\n\n// Encrypt the message\nbyte[] ciphertext = Tango12d.Encrypt(message, key);\n\n// Decrypt the ciphertext\nbyte[] plaintext = Tango12d.Decrypt(ciphertext, key);\n```\n\n### Tango12p\n⚠️**WARNING: Never reuse a nonce with the same key.**\n```c#\nconst string message = \"This is a test.\";\nconst int nonceLength = 24;\nconst int keyLength = 32;\n\n// The message could be a file\nbyte[] message = Encoding.UTF8.GetBytes(message);\n\n// The nonce can be random. Increment the nonce for each message encrypted using the same key\nbyte[] nonce = SodiumCore.GetRandomBytes(nonceLength);\n\n// The key can be random or derived using a KDF (e.g. Argon2, HKDF, etc)\nbyte[] key = SodiumCore.GetRandomBytes(keyLength);\n\n// Encrypt the message\nbyte[] ciphertext = Tango12p.Encrypt(message, nonce, key);\n\n// Decrypt the ciphertext\nbyte[] plaintext = Tango12p.Decrypt(ciphertext, nonce, key);\n```\n\n## How does it work?\n### Constants\n```c#\ninternal const int BlockSize = 64;\ninternal const int CounterLength = 64;\ninternal const int NonceLength = 24;\ninternal const int KeyLength = 64;\n```\n\n### Tango12d\n1. An empty 64 byte counter is created.\n```c#\nvar counter = new byte[Constants.CounterLength];\n```\n2. The message is read in blocks of 64 bytes.\n```c#\nvar buffer = new byte[Constants.BlockSize];\nusing var memoryStream = new MemoryStream(message);\nwhile ((bytesRead = memoryStream.Read(buffer, offset: 0, buffer.Length)) \u003e 0)\n```\n3. For each block, BLAKE2b-512, with the counter as the message and the key as the key, is used to generate a keystream block.\n```c#\nbyte[] keystreamBlock = GenericHash.Hash(counter, key, Constants.BlockSize);\n```\n4. The message block and keystream block are XORed together to produce the ciphertext block.\n```c#\ninternal static byte[] Xor(byte[] message, byte[] keystream)\n{\n    var ciphertext = new byte[message.Length];\n    for (int i = 0; i \u003c ciphertext.Length; i++)\n    {\n        ciphertext[i] = (byte)(message[i] ^ keystream[i]);\n    }\n    return ciphertext;\n}\n```\n5. The counter is incremented.\n```c#\ncounter = Utilities.Increment(counter);\n```\n6. This continues until the end of the message is reached.\n\n### Tango12p\n\n1. An empty 64 byte counter is created.\n```c#\nvar counter = new byte[Constants.CounterLength];\n```\n2. The counter and 24 byte nonce are concatenated together.\n```c#\nvar counter = new byte[Constants.CounterLength];\ncounter = Arrays.Concat(counter, nonce);\n```\n3. The message is read in blocks of 64 bytes.\n```c#\nvar buffer = new byte[Constants.BlockSize];\nusing var memoryStream = new MemoryStream(message);\nwhile ((bytesRead = memoryStream.Read(buffer, offset: 0, buffer.Length)) \u003e 0)\n```\n4. 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.\n```c#\nbyte[] keystreamBlock = GenericHash.Hash(counter, key, Constants.BlockSize);\n```\n4. The message block and keystream block are XORed together to produce the ciphertext block.\n```c#\ninternal static byte[] Xor(byte[] message, byte[] keystream)\n{\n    var ciphertext = new byte[message.Length];\n    for (int i = 0; i \u003c ciphertext.Length; i++)\n    {\n        ciphertext[i] = (byte)(message[i] ^ keystream[i]);\n    }\n    return ciphertext;\n}\n```\n5. The counter is incremented.\n```c#\ncounter = Utilities.Increment(counter);\n```\n6. This continues until the end of the message is reached.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamuel-lucas6%2Ftango12","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamuel-lucas6%2Ftango12","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamuel-lucas6%2Ftango12/lists"}