Skip to content

Instantly share code, notes, and snippets.

@hossambarakat
Forked from lski/XXTea.cs
Created November 29, 2015 22:31
Show Gist options
  • Save hossambarakat/0b7bfcdc99f2bf78dd07 to your computer and use it in GitHub Desktop.
Save hossambarakat/0b7bfcdc99f2bf78dd07 to your computer and use it in GitHub Desktop.
A c# implementation of XXTea encryption algorithm based on the javascript version by Chris Veness
using System;
using System.Text;
/// <summary>
/// A class for encrypting and decrypting a string into base64 format which makes it safe for transfer
/// between applications.
///
/// Reference:
/// Based upon the javascript implementation of xxtea by: Chris Veness
/// www.movable-type.co.uk/tea-block.html
///
/// Using the corrected block tea algorithm developed by: David Wheeler & Roger Needham
///
/// But written for c# by me :D
/// </summary>
public class XXTea {
/// <summary>
/// Encryption using corrected Block TEA (xxtea) algorithm
/// </summary>
/// <param name="text">String to be encrypted (multi-byte safe)</param>
/// <param name="password">Password to be used for encryption (1st 16 chars)</param>
/// <returns></returns>
public static String Encrypt(String text, String password) {
if (text.Length == 0)
return ""; // nothing to encrypt
// Check the user has passed a large enough salt to encrypt the data
if (password.Length < 8) {
throw new ArgumentException("The salt for encryption is too short");
}
// The salt needs to be at least 16 chars in size so if less than 16 double it until it reaches that size
while (password.Length < 16) { password += password; }
// Convert the text into UTF-8 encoding (byte size)
var v = ToLongs((new UTF8Encoding()).GetBytes(text));
// algorithm doesn't work for n<2 so fudge by adding an ascii null
if (v.Length == 1) { v[0] = 0; }
// Simply convert first 16 chars of password as key
var k = ToLongs((new UTF8Encoding()).GetBytes(password.Substring(0, 16)));
// Use UInt32 as the original is based on 'unsigned long' in C, which is equiv to UInt32 in .Net (and not ulong)
UInt32 n = (UInt32)v.Length,
z = v[n - 1],
y = v[0],
delta = 0x9e3779b9,
e,
q = (UInt32)(6 + (52 / n)),
sum = 0,
p = 0;
while (q-- > 0) {
sum += delta;
e = sum >> 2 & 3;
for (p = 0; p < (n - 1); p++) {
y = v[(p + 1)];
z = v[p] += (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
}
y = v[0];
z = v[n - 1] += (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
}
// Convert to Base64 so that Control characters doesnt break it
return Convert.ToBase64String(ToBytes(v));
}
/// <summary>
/// Decryption using Corrected Block TEA (xxtea) algorithm
/// </summary>
/// <param name="ciphertext">String to be decrypted</param>
/// <param name="password">Password to be used for decryption (1st 16 chars)</param>
/// <returns></returns>
public static String Decrypt(String ciphertext, String password) {
if (ciphertext.Length == 0) { return ""; }
var v = ToLongs(Convert.FromBase64String(ciphertext));
var k = ToLongs((new UTF8Encoding()).GetBytes(password.Substring(0, 16)));
UInt32 n = (UInt32)v.Length,
z = v[n - 1],
y = v[0],
delta = 0x9e3779b9,
e,
q = (UInt32)(6 + (52 / n)),
sum = q * delta,
p = 0;
while (sum != 0) {
e = sum >> 2 & 3;
for (p = (n - 1); p > 0; p--) {
z = v[p - 1];
y = v[p] -= (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
}
z = v[n - 1];
y = v[0] -= (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
sum -= delta;
}
var plaintext = (new UTF8Encoding()).GetString(ToBytes(v));
return plaintext;
}
/// <summary>
/// convert utf-8 byte to array of longs, each containing 4 chars to be manipulated
/// </summary>
/// <param name="s"></param>
private static UInt32[] ToLongs(byte[] s) {
// note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long
var l = new UInt32[(int)Math.Ceiling(((decimal)s.Length / 4))];
// Create an array of long, each long holding the data of 4 characters, if the last block is less than 4
// characters in length, fill with ascii null values
for (int i = 0; i < l.Length; i++) {
// Note: little-endian encoding - endianness is irrelevant as long as it is the same in ToBytes()
l[i] = ((s[i * 4])) +
((i * 4 + 1) >= s.Length ? (UInt32)0 << 8 : ((UInt32)s[i * 4 + 1] << 8)) +
((i * 4 + 2) >= s.Length ? (UInt32)0 << 16 : ((UInt32)s[i * 4 + 2] << 16)) +
((i * 4 + 3) >= s.Length ? (UInt32)0 << 24 : ((UInt32)s[i * 4 + 3] << 24));
}
return l;
}
/// <summary>
/// Convert array of longs back to utf-8 byte array
/// </summary>
/// <returns></returns>
private static byte[] ToBytes(UInt32[] l) {
byte[] b = new byte[l.Length * 4];
// Split each long value into 4 separate characters (bytes) using the same format as ToLongs()
for (Int32 i = 0; i < l.Length; i++) {
b[(i * 4)] = (byte)(l[i] & 0xFF);
b[(i * 4) + 1] = (byte)(l[i] >> (8 & 0xFF));
b[(i * 4) + 2] = (byte)(l[i] >> (16 & 0xFF));
b[(i * 4) + 3] = (byte)(l[i] >> (24 & 0xFF));
}
return b;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment