I have ported two libraries from open source projects that I needed for porting an app from Xamarin to Fuse. I confirmed that the results in Fuse for some basic tests were the same as what I was seeing in my Xamarin app (ie… so the Sha256 hasing results match)
Examples of usage:
public class PasswordTools
{
public static string HashPassword (string password, string salt)
{
string salt_start = "app_salt_here" + salt.ToLower ();
string salt_end = salt.ToLower()+ "app_pepper_here";
// merge password and salt together
string sHashWithSalt = salt_start + password + salt_end;
// convert this merged value to a byte array
byte[] saltedHashBytes = Utf8.GetBytes (sHashWithSalt);
// use hash algorithm to compute the hash
var algorithm = new SHA256 ();
byte[] result = algorithm.ComputeHash (saltedHashBytes);
// return the has as a base 64 encoded string
return Base64.GetString (result).Replace ('/', '%');
}
public string GetOneTimePassword(int instance, string challenge)
{
OneTimePassword otp = new OneTimePassword(instance, "SOME SALT" + challenge + "SOME PEPPER");
return otp.GetCurrent();
}
}
using Uno;
using Uno.Collections;
using Uno.IO;
using Uno.Text;
using Fuse;
namespace Community.Cryptography
{
/CODE ORIGINALLY
FROM CODEPROJECT ARTICLE : http://www.codeproject.com/Articles/592275/OTP-One-Time-Password-Demystified
UNDER LICENSE: http://www.codeproject.com/info/cpol10.aspx
PORTED TO UNO WITH BASIC REFACTORING: Sean McKay, 2015
/
struct SHA_TRANSF
{
public long A;
public long B;
public long C;
public long D;
public long E;
public long T;
public long[] W;
public int idxW;
public new string ToString()
{
return "::" + A + "::" + B + "::" + C + "::" + D + "::" + E + "::" + T + "::" + idxW;
}
}
public class mem
{
public static void _set(ref byte[] data, int first, byte val, int count)
{
for (int nI = 0; nI < count; nI++)
{
data[nI + first] = val;
}
}
public static void _cpy(ref byte[] dest, int dest_first, byte[] srce, int srce_first, int count)
{
for (int nI = 0; nI < count; nI++)
{
dest[dest_first + nI] = srce[nI + srce_first];
}
}
}
/// <summary>
/// This class implements the SHA1 hash algorithm
/// </summary>
public class Sha1
{
#region SHA f()-functions
static long andOrNot(long x, long y, long z)
{
return ((x & y) | (~x & z));
}
static long xorEm(long x, long y, long z)
{
return (x ^ y ^ z);
}
static long andOr(long x, long y, long z)
{
return ((x & y) | (x & z) | (y & z));
}
static long xorEm2(long x, long y, long z)
{
return (x ^ y ^ z);
}
static long f(long n, long x, long y, long z)
{
switch (n)
{
case 1:
{
return andOrNot(x, y, z);
}
case 2:
{
return xorEm(x, y, z);
}
case 3:
{
return andOr(x, y, z);
}
case 4:
{
return xorEm2(x, y, z);
}
default:
throw new Exception("Wrong parameter");
}
}
#endregion
#region SHA constants
static uint[] CONST = new uint[4]
{
0x5a827999,
0x6ed9eba1,
0x8f1bbcdc,
0xca62c1d6
};
#endregion
static long Mask32Bit(long x)
{
unchecked
{
return (x & 0xFFFFFFFF);
}
}
static long Rotate32Bit(long x, int n)
{
return Mask32Bit(((x << n) | (x >> (32 - n))));
}
#region Unraveled Rotation functions
static void ModifyTB(int n, ref SHA_TRANSF t)
{
t.T = Mask32Bit(Rotate32Bit(t.A, 5) + f(n, t.B, t.C, t.D) + t.E + t.W[t.idxW++] + CONST[n - 1]);
t.B = Rotate32Bit(t.B, 30);
}
static void ModifyEA(int n, ref SHA_TRANSF t)
{
t.E = Mask32Bit(Rotate32Bit(t.T, 5) + f(n, t.A, t.B, t.C) + t.D + t.W[t.idxW++] + CONST[n - 1]);
t.A = Rotate32Bit(t.A, 30);
}
static void ModifyDT(int n, ref SHA_TRANSF t)
{
t.D = Mask32Bit(Rotate32Bit(t.E, 5) + f(n, t.T, t.A, t.B) + t.C + t.W[t.idxW++] + CONST[n - 1]);
t.T = Rotate32Bit(t.T, 30);
}
static void ModifyCE(int n, ref SHA_TRANSF t)
{
t.C = Mask32Bit(Rotate32Bit(t.D, 5) + f(n, t.E, t.T, t.A) + t.B + t.W[t.idxW++] + CONST[n - 1]);
t.E = Rotate32Bit(t.E, 30);
}
static void ModifyBD(int n, ref SHA_TRANSF t)
{
t.B = Mask32Bit(Rotate32Bit(t.C, 5) + f(n, t.D, t.E, t.T) + t.A + t.W[t.idxW++] + CONST[n - 1]);
t.D = Rotate32Bit(t.D, 30);
}
static void ModifyAT(int n, ref SHA_TRANSF t)
{
t.A = Mask32Bit(Rotate32Bit(t.B, 5) + f(n, t.C, t.D, t.E) + t.T + t.W[t.idxW++] + CONST[n - 1]);
t.C = Rotate32Bit(t.C, 30);
}
#endregion
private void sha_transform()
{
int
i,
idx = 0;
SHA_TRANSF tf = new SHA_TRANSF();
tf.W = new long[80];
/ SHA_BYTE_ORDER == 1234 /
for (i = 0; i < 16; ++i)
{
tf.T = ((long)data[idx++]) & 0x000000ff;
tf.T += (((long)data[idx++]) << 8) & 0x0000ff00;
tf.T += (((long)data[idx++]) << 16) & 0x00ff0000;
tf.T += (((long)data[idx++]) << 24) & 0xff000000;
tf.W[i] = ((tf.T << 24) & 0xff000000) | ((tf.T << 8) & 0x00ff0000) |
((tf.T >> 8) & 0x0000ff00) | ((tf.T >> 24) & 0x000000ff);
}
for (i = 16; i < 80; ++i)
{
tf.W[i] = tf.W[i - 3] ^ tf.W[i - 8] ^ tf.W[i - 14] ^ tf.W[i - 16];
tf.W[i] = Rotate32Bit(tf.W[i], 1);
}
tf.A = digest[0];
tf.B = digest[1];
tf.C = digest[2];
tf.D = digest[3];
tf.E = digest[4];
tf.idxW = 0;
// UNRAVEL
//debug_log "::BEFORE::MODIFYTB::" + tf.ToString();
ModifyTB(1, ref tf);
//debug_log "::AFTER::MODIFYTB::" + tf.ToString(); ModifyEA(1, ref tf);
ModifyDT(1, ref tf);
ModifyCE(1, ref tf);
ModifyBD(1, ref tf);
ModifyAT(1, ref tf);
ModifyTB(1, ref tf);
ModifyEA(1, ref tf);
ModifyDT(1, ref tf);
ModifyCE(1, ref tf);
ModifyBD(1, ref tf);
ModifyAT(1, ref tf);
ModifyTB(1, ref tf);
ModifyEA(1, ref tf);
ModifyDT(1, ref tf);
ModifyCE(1, ref tf);
ModifyBD(1, ref tf);
ModifyAT(1, ref tf);
ModifyTB(1, ref tf);
ModifyEA(1, ref tf);
ModifyDT(2, ref tf);
ModifyCE(2, ref tf);
ModifyBD(2, ref tf);
ModifyAT(2, ref tf);
ModifyTB(2, ref tf);
ModifyEA(2, ref tf);
ModifyDT(2, ref tf);
ModifyCE(2, ref tf);
ModifyBD(2, ref tf);
ModifyAT(2, ref tf);
ModifyTB(2, ref tf);
ModifyEA(2, ref tf);
ModifyDT(2, ref tf);
ModifyCE(2, ref tf);
ModifyBD(2, ref tf);
ModifyAT(2, ref tf);
ModifyTB(2, ref tf);
ModifyEA(2, ref tf);
ModifyDT(2, ref tf);
ModifyCE(2, ref tf);
ModifyBD(3, ref tf);
ModifyAT(3, ref tf);
ModifyTB(3, ref tf);
ModifyEA(3, ref tf);
ModifyDT(3, ref tf);
ModifyCE(3, ref tf);
ModifyBD(3, ref tf);
ModifyAT(3, ref tf);
ModifyTB(3, ref tf);
ModifyEA(3, ref tf);
ModifyDT(3, ref tf);
ModifyCE(3, ref tf);
ModifyBD(3, ref tf);
ModifyAT(3, ref tf);
ModifyTB(3, ref tf);
ModifyEA(3, ref tf);
ModifyDT(3, ref tf);
ModifyCE(3, ref tf);
ModifyBD(3, ref tf);
ModifyAT(3, ref tf);
ModifyTB(4, ref tf);
ModifyEA(4, ref tf);
ModifyDT(4, ref tf);
ModifyCE(4, ref tf);
ModifyBD(4, ref tf);
ModifyAT(4, ref tf);
ModifyTB(4, ref tf);
ModifyEA(4, ref tf);
ModifyDT(4, ref tf);
ModifyCE(4, ref tf);
ModifyBD(4, ref tf);
ModifyAT(4, ref tf);
ModifyTB(4, ref tf);
ModifyEA(4, ref tf);
ModifyDT(4, ref tf);
ModifyCE(4, ref tf);
ModifyBD(4, ref tf);
ModifyAT(4, ref tf);
ModifyTB(4, ref tf);
ModifyEA(4, ref tf);
HmacSha1.debugBytes(digest, "sha_transform::PRESET");
//debug_log "--> TF HAS: " + tf.ToString();
digest[0] = Mask32Bit(digest[0] + tf.E);
digest[1] = Mask32Bit(digest[1] + tf.T);
digest[2] = Mask32Bit(digest[2] + tf.A);
digest[3] = Mask32Bit(digest[3] + tf.B);
digest[4] = Mask32Bit(digest[4] + tf.C);
HmacSha1.debugBytes(digest, "sha_transform::POSTSET");
}
public const ushort LITTLE_INDIAN = 1234;
public const ushort BYTE_ORDER = LITTLE_INDIAN;
public const int SHA_BLOCKSIZE = 64;
public const int SHA_DIGESTSIZE = 20;
#region Replaces the SHA_INFO structure
private long[] digest;
/ message digest /
private long count_lo, count_hi;
/ 64-bit bit count /
private byte[] data;
/ SHA data buffer /
private int local;
/ unprocessed amount in data /
#endregion
public Sha1()
{
}
/// <summary>
/// Initialize the SHA digest
/// </summary>
public void Init()
{
data = new byte[SHA_BLOCKSIZE];
digest = new long[5];
digest[0] = 1732584193L;
digest[1] = 4023233417L;
digest[2] = 2562383102L;
digest[3] = 271733878L;
digest[4] = 3285377520L;
//LOG: ::INIT::DIGEST::1732584193:-271733879:-1732584194:271733878:-1009589776:
HmacSha1.debugBytes(digest, "::INIT::DIGEST");
count_lo = 0L;
count_hi = 0L;
local = 0;
}
/// <summary>
/// Update the SHA digest
/// </summary>
/// <param name="buffer">Data to be processed</param>
public void Update(byte[]buffer)
{
int i;
long clo;
int count = buffer.Length;
int buf_idx = 0;
clo = Mask32Bit(count_lo + ((long)count << 3));
if (clo < count_lo)
{
++count_hi;
}
count_lo = clo;
count_hi += (long)count >> 29;
if (local != 0)
{
i = SHA_BLOCKSIZE - local;
if (i > count)
{
i = count;
}
//mem._cpy(ref data, local, buffer, buf_idx, i);
for (int nI = 0; nI < i; nI++)
{
data[local + nI] = buffer[nI + buf_idx];
}
count -= i;
buf_idx += i;
local += i;
if (local == SHA_BLOCKSIZE)
{
sha_transform();
}
else
{
return;
}
}
while (count >= SHA_BLOCKSIZE)
{
//mem._cpy(ref data, 0, buffer, buf_idx, SHA_BLOCKSIZE);
for (int nI = 0; nI < SHA_BLOCKSIZE; nI++)
{
data[nI] = buffer[nI + buf_idx];
}
buf_idx += SHA_BLOCKSIZE;
count -= SHA_BLOCKSIZE;
sha_transform();
}
//mem._cpy(ref data, 0, buffer, buf_idx, count);
for (int nI = 0; nI < count; nI++)
{
data[nI] = buffer[nI + buf_idx];
}
local = count;
}
/// <summary>
/// Finish computing the SHA digest
/// </summary>
/// <param name="result"></param>
public byte[] Final()
{
byte[] result = new byte[SHA_DIGESTSIZE];
int count;
long lo_bit_count, hi_bit_count;
lo_bit_count = count_lo;
hi_bit_count = count_hi;
count = (int)((lo_bit_count >> 3) & 0x3f);
data[count++] = 0x80;
if (count > SHA_BLOCKSIZE - 8)
{
//mem._set(ref data, count, 0, SHA_BLOCKSIZE - count);
for (int nI = 0; nI < SHA_BLOCKSIZE - count; nI++)
{
data[nI + count] = 0;
}
sha_transform();
//mem._set(ref data, 0, 0, SHA_BLOCKSIZE - 8);
for (int nI = 0; nI < SHA_BLOCKSIZE - 8; nI++)
{
data[nI ] = 0;
}
}
else
{
//mem._set(ref data, count, 0, SHA_BLOCKSIZE - 8 - count);
for (int nI = 0; nI < SHA_BLOCKSIZE - 8 - count; nI++)
{
data[nI + count] = 0;
}
}
data[56] = (byte)((hi_bit_count >> 24) & 0xff);
data[57] = (byte)((hi_bit_count >> 16) & 0xff);
data[58] = (byte)((hi_bit_count >> 8) & 0xff);
data[59] = (byte)((hi_bit_count >> 0) & 0xff);
data[60] = (byte)((lo_bit_count >> 24) & 0xff);
data[61] = (byte)((lo_bit_count >> 16) & 0xff);
data[62] = (byte)((lo_bit_count >> 8) & 0xff);
data[63] = (byte)((lo_bit_count >> 0) & 0xff);
HmacSha1.debugBytes(digest, "BEFORE FINAL::MEM::TRANSFORM::DIGEST");
sha_transform();
HmacSha1.debugBytes(digest, "After FINAL::MEM::TRANSFORM::DIGEST");
result[0] = (byte)((digest[0] >> 24) & 0xff);
result[1] = (byte)((digest[0] >> 16) & 0xff);
result[2] = (byte)((digest[0] >> 8) & 0xff);
result[3] = (byte)((digest[0]) & 0xff);
result[4] = (byte)((digest[1] >> 24) & 0xff);
result[5] = (byte)((digest[1] >> 16) & 0xff);
result[6] = (byte)((digest[1] >> 8) & 0xff);
result[7] = (byte)((digest[1]) & 0xff);
result[8] = (byte)((digest[2] >> 24) & 0xff);
result[9] = (byte)((digest[2] >> 16) & 0xff);
result[10] = (byte)((digest[2] >> 8) & 0xff);
result[11] = (byte)((digest[2]) & 0xff);
result[12] = (byte)((digest[3] >> 24) & 0xff);
result[13] = (byte)((digest[3] >> 16) & 0xff);
result[14] = (byte)((digest[3] >> 8) & 0xff);
result[15] = (byte)((digest[3]) & 0xff);
result[16] = (byte)((digest[4] >> 24) & 0xff);
result[17] = (byte)((digest[4] >> 16) & 0xff);
result[18] = (byte)((digest[4] >> 8) & 0xff);
result[19] = (byte)((digest[4]) & 0xff);
HmacSha1.debugBytes(result, "After FINAL::RESULT");
return result;
}
public byte[] Final_dss_padding()
{
byte[] result = new byte[SHA_DIGESTSIZE];
int count;
long lo_bit_count, hi_bit_count;
lo_bit_count = count_lo;
hi_bit_count = count_hi;
count = (int)((lo_bit_count >> 3) & 0x3f);
if (count > SHA_BLOCKSIZE)
{
//mem._set(ref data, count, 0, SHA_BLOCKSIZE - count);
for (int nI = 0; nI < SHA_BLOCKSIZE-count; nI++)
{
data[nI + count] = 0;
}
sha_transform();
//mem._set(ref data, 0, 0, SHA_BLOCKSIZE);
for (int nI = 0; nI < SHA_BLOCKSIZE; nI++)
{
data[nI] = 0;
}
}
else
{
//mem._set(ref data, count, 0, SHA_BLOCKSIZE - count);
for (int nI = 0; nI < SHA_BLOCKSIZE -count; nI++)
{
data[nI + count] = 0;
}
}
sha_transform();
result[0] = (byte)((digest[0] >> 24) & 0xff);
result[1] = (byte)((digest[0] >> 16) & 0xff);
result[2] = (byte)((digest[0] >> 8) & 0xff);
result[3] = (byte)((digest[0]) & 0xff);
result[4] = (byte)((digest[1] >> 24) & 0xff);
result[5] = (byte)((digest[1] >> 16) & 0xff);
result[6] = (byte)((digest[1] >> 8) & 0xff);
result[7] = (byte)((digest[1]) & 0xff);
result[8] = (byte)((digest[2] >> 24) & 0xff);
result[9] = (byte)((digest[2] >> 16) & 0xff);
result[10] = (byte)((digest[2] >> 8) & 0xff);
result[11] = (byte)((digest[2]) & 0xff);
result[12] = (byte)((digest[3] >> 24) & 0xff);
result[13] = (byte)((digest[3] >> 16) & 0xff);
result[14] = (byte)((digest[3] >> 8) & 0xff);
result[15] = (byte)((digest[3]) & 0xff);
result[16] = (byte)((digest[4] >> 24) & 0xff);
result[17] = (byte)((digest[4] >> 16) & 0xff);
result[18] = (byte)((digest[4] >> 8) & 0xff);
result[19] = (byte)((digest[4]) & 0xff);
return result;
}
/// <summary>
/// Returns the version
/// </summary>
/// <returns></returns>
public static string version()
{
return "SHA-1";
}
}
/// <summary>
/// This class provides the HMAC SHA1 algorithm
/// </summary>
public class HmacSha1
{
private const int HMAC_SHA1_PAD_SIZE = 64;
private const int HMAC_SHA1_DIGEST_SIZE = 20;
private const int HMAC_SHA1_128_DIGEST_SIZE = 16;
private Sha1 sha_ctx;
private byte[] key_ctx;
private int key_len_ctx;
private byte[] temp_key_ctx = new byte[Sha1.SHA_DIGESTSIZE];
/ in case key exceeds 64 bytes /
public static byte[] GetHmacSha1Bytes(byte[] key, byte[] text)
{
HmacSha1 sha = new HmacSha1(key, text);
return sha.Final();
}
private HmacSha1(byte[] key, byte[] text)
{
byte[] k_ipad = new byte[HMAC_SHA1_PAD_SIZE];
int i, key_len = key.Length;
sha_ctx = new Sha1();
/ if key is longer than 64 bytes reset it to key=SHA-1(key) /
if (key_len > HMAC_SHA1_PAD_SIZE)
{
sha_ctx.Init();
sha_ctx.Update(key);
temp_key_ctx = sha_ctx.Final();
key = temp_key_ctx;
key_len = HMAC_SHA1_DIGEST_SIZE;
}
/
the HMAC_SHA1 transform looks like:
SHA1(K XOR opad, SHA1(K XOR ipad, text))
where K is an n byte keyUpdate
ipad is the byte 0x36 repeated 64 times
opad is the byte 0x5c repeated 64 times
and text is the data being protected
/
/ start out by storing key in pads /
mem._set(ref k_ipad, 0, 0, k_ipad.Length);
mem._cpy(ref k_ipad, 0, key, 0, key_len);
byte xorKeyInit = (byte) 0x36;
// XOR key with ipad and opad values /
/
for (i = 0; i < k_ipad.Length; i++)
{
k_ipad[i] ^= xorKeyInit;
}
/
for (i = 0; i < k_ipad.Length; i++)
{
k_ipad[i] = (byte)(k_ipad[i] ^ xorKeyInit);
}
/
perform inner SHA1
/
sha_ctx.Init(); / init context for 1st pass /
/ start with inner pad /
sha_ctx.Update(k_ipad);
/ Stash the key and it's length into the context. /
key_ctx = key;
key_len_ctx = key_len;
sha_ctx.Update(text);
}
private byte[] Final()
{
byte[] digest;
/ outer padding - key XORd with opad /
byte[] k_opad = new byte[HMAC_SHA1_PAD_SIZE]; int i;
byte xorKeyInit = (byte)0x5c;
mem._set(ref k_opad, 0, 0, k_opad.Length);
mem._cpy(ref k_opad, 0, key_ctx, 0, key_len_ctx);
/ XOR key with ipad and opad values /
/
for (i = 0; i < k_opad.Length; i++)
{
k_opad[i] ^= xorKeyInit;
}
/ for (i = 0; i < k_opad.Length; i++)
{
k_opad[i] =(byte) (k_opad[i] ^ xorKeyInit);
}
digest = sha_ctx.Final(); / finish up 1st pass /
debugBytes(digest, "After DIGEST"); /
perform outer SHA1
/
sha_ctx.Init(); / init context for 2nd pass /
/ start with outer pad /
sha_ctx.Update(k_opad);
/ then results of 1st hash /
sha_ctx.Update(digest); digest = sha_ctx.Final(); / finish up 2nd pass /
debugBytes(digest, "After FINAL DIGEST");
return digest;
}
public static void debugBytes<T>(T[] toPrint, string msg)
{
/
StringBuilder sb = new StringBuilder();
foreach(T b in toPrint)
{
sb.Append(b.ToString());
sb.Append(":");
}
debug_log msg + "::" + sb.ToString();
/
}
}
public class OneTimePassword
{
public const int SECRETLENGTH = 20;
private const string MSG_SECRETLENGTH = "Secret must be at least 20 bytes";
private const string MSG_COUNTER_MINVALUE = "Counter min value is 1";
private static int[] _checksumSkipTable = new int[] { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
private byte[] _secretKey;
private ulong _counter = 0x0000000000000001;
public OneTimePassword()
{
_secretKey = getDefaultSecretKey();
}
public OneTimePassword(ulong counter = 1, string secret = null)
{
if (secret != null)
{
byte[] secretKey = Uno.Text.Utf8.GetBytes(secret);
if (secretKey.Length < SECRETLENGTH)
{
throw new Exception(MSG_SECRETLENGTH);
}
this._secretKey = secretKey;
}
else
this._secretKey = getDefaultSecretKey();
if (counter < 1)
{
throw new Exception(MSG_COUNTER_MINVALUE);
}
this._counter = counter;
}
public OneTimePassword(ulong counter = 1, byte[] secretKey = null)
{
if (secretKey != null)
{
if (secretKey.Length < SECRETLENGTH)
{
throw new Exception(MSG_SECRETLENGTH);
}
this._secretKey = secretKey;
}
else
this._secretKey = getDefaultSecretKey();
if (counter < 1)
{
throw new Exception(MSG_COUNTER_MINVALUE);
}
this._counter = counter;
}
byte[] getDefaultSecretKey()
{
return new byte[]
{
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43
};
}
private static int getChecksum(int codeDigits)
{
int digitMillions = (codeDigits / 1000000) % 10;
int digitHundredThousands = (codeDigits / 100000) % 10;
int digitTenThousands = (codeDigits / 10000) % 10;
int digitThousands = (codeDigits / 1000) % 10;
int digitHundreds = (codeDigits / 100) % 10;
int digitTens = (codeDigits / 10) % 10;
int digitOnes = codeDigits % 10;
return (10 -
((_checksumSkipTable[digitMillions] + digitHundredThousands +
_checksumSkipTable[digitTenThousands] + digitThousands +
_checksumSkipTable[digitHundreds] + digitTens +
_checksumSkipTable[digitOnes]) % 10)) % 10;
}
/// <summary>
/// Formats the OneTimePassword. This is the OneTimePassword algorithm.
/// </summary>
/// <param name="hmac">HMAC value</param>
/// <returns>8 digits OTP</returns>
private static string FormatOneTimePassword(byte[] hmac)
{
StringBuilder sb = new StringBuilder();
foreach(byte b in hmac)
{
sb.Append(b.ToString());
sb.Append(":");
}
//debug_log "HMAC::" + sb.ToString();
int offset = hmac[19] & 0xf;
int bin_code = (hmac[offset] & 0x7f) << 24
| (hmac[offset + 1] & 0xff) << 16
| (hmac[offset + 2] & 0xff) << 8
| (hmac[offset + 3] & 0xff);
int Code_Digits = bin_code % 10000000;
int csum = getChecksum(Code_Digits);
int OTP = Code_Digits 10 + csum;
//debug_log "FOTP::" + offset + "::" + bin_code + "::" + Code_Digits + "::" + csum + "::" + OTP + "::" + hmac.Length;
return string.Format("{0:d08}", OTP);
}
public static byte[] ToByteArray(string otp)
{
byte[] baOTP = new byte[otp.Length];
char[] arOTP = otp.ToCharArray();
for (int nI = 0; nI < otp.Length; nI++)
{
baOTP[nI] = (byte)arOTP[nI];
}
return baOTP;
}
public byte[] CounterArray
{
get
{
return BitConverter.GetBytes(_counter);
}
set
{
_counter = BitConverter.ToUInt64(value, 0);
}
}
/// <summary>
/// Set the OTP secret
/// </summary>
/// <param name="secret"></param>
public byte[] Secret
{
set
{
if (value.Length < SECRETLENGTH)
{
throw new Exception(MSG_SECRETLENGTH);
}
_secretKey = value;
}
}
/// <summary>
/// Get the current one time password value
/// </summary>
/// <returns></returns>
public string GetCurrent()
{
return FormatOneTimePassword(HmacSha1.GetHmacSha1Bytes(_secretKey, CounterArray));
}
/// <summary>
/// Get the next OTP value
/// </summary>
/// <returns></returns>
public string GetNextOneTimePassword()
{
// increment the counter
++_counter;
return GetCurrent();
}
/// <summary>
/// Get the counter value
/// </summary>
/// <returns></returns>
public ulong Counter
{
get
{
return _counter;
}
set
{
_counter = value;
}
} //Counter
} //OnteTimePassword
public class BitConverter
{
public static void Clear(byte[] arr, int index, int length)
{
for(int i = index; i < index+length; i++)
{
arr[i] = 0;
}
}
public static string ToString(byte[] toConvert)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in toConvert)
{
sb.Append(b.ToString());
sb.Append("-");
}
return sb.ToString().TrimEnd('-');
}
public static byte[] GetBytes(ulong val)
{
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write(val);
byte[] buff= ms.GetBuffer();
byte[] longSizedBuff = new byte[8];
Array.Copy(buff,longSizedBuff,8);
return longSizedBuff;
}
}
return null;
}
public static ulong ToUInt64(byte[] bytes, int startingIndex)
{
using (MemoryStream ms = new MemoryStream())
{
ms.Write(bytes,startingIndex, bytes.Length);
using (BinaryReader br = new BinaryReader(ms))
{
return br.ReadULong();
}
}
return 0;
}//ToUInt64
}//BitConverter
}//Namespace
'''
'''
using Uno;
using Uno.Collections;
using Uno.Text;
using Fuse;
namespace Community.Cryptography
{
//Sha256 algorithm from: http://hashlib.codeplex.com/
//License: http://hashlib.codeplex.com/license CDDL
class Converters
{
public static string ConvertBytesToHexString (byte[] a_in, bool a_group = true)
{
string hex = BitConverter.ToString (a_in).ToUpper ();
if (a_group) {
string[] ar = BitConverter.ToString (a_in).ToUpper ().Split (new char[] { '-' });
hex = "";
for (int i = 0; i < ar.Length / 4; i++) {
if (i != 0)
hex += "-";
hex += ar [i 4] + ar [i 4 + 1] + ar [i 4 + 2] + ar [i 4 + 3];
}
} else
hex = hex.Replace ("-", "");
return hex;
}
public static byte[] ConvertUIntsToBytesSwapOrder (uint[] a_in, int a_index = 0, int a_length = -1)
{
if (a_length == -1)
a_length = a_in.Length;
byte[] result = new byte[a_length 4];
for (int j = 0; a_length > 0; a_index++) {
result [j++] = (byte)(a_in [a_index] >> 24);
result [j++] = (byte)(a_in [a_index] >> 16);
result [j++] = (byte)(a_in [a_index] >> 8);
result [j++] = (byte)a_in [a_index];
a_length--;
}
return result;
}
public static void ConvertBytesToUIntsSwapOrder (byte[] a_in, int a_index, int a_length, uint[] a_result, int a_index_out)
{
for (int i = a_index_out; a_length > 0; a_length -= 4) {
a_result [i++] =
((uint)a_in [a_index++] << 24) |
((uint)a_in [a_index++] << 16) |
((uint)a_in [a_index++] << 8) |
a_in [a_index++];
}
}
public static void ConvertULongToBytesSwapOrder (ulong a_in, byte[] a_out, int a_index)
{
//Debug.Assert (a_index + 8 <= a_out.Length);
a_out [a_index++] = (byte)(a_in >> 56);
a_out [a_index++] = (byte)(a_in >> 48);
a_out [a_index++] = (byte)(a_in >> 40);
a_out [a_index++] = (byte)(a_in >> 32);
a_out [a_index++] = (byte)(a_in >> 24);
a_out [a_index++] = (byte)(a_in >> 16);
a_out [a_index++] = (byte)(a_in >> 8);
a_out [a_index++] = (byte)a_in;
}
}
internal class SHA256
{
protected readonly uint[] m_state = new uint[8];
protected readonly HashBuffer m_buffer;
protected ulong m_processed_bytes;
private readonly int m_block_size;
private readonly int m_hash_size;
public static int BUFFER_SIZE = 64 1024;
#region Consts
private static readonly uint[] s_K = new uint[] {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
#endregion
#region ctors
public SHA256 ()
: this (32)
{
}
public SHA256 (int a_hash_size)
: this (a_hash_size, 64)
{
Initialize ();
}
protected SHA256 (int a_hash_size, int a_block_size, int a_buffer_size = -1)
{
if (a_buffer_size == -1)
a_buffer_size = a_block_size;
m_buffer = new HashBuffer (a_buffer_size);
m_processed_bytes = 0;
//Debug.Assert ((a_block_size > 0) || (a_block_size == -1));
//Debug.Assert (a_hash_size > 0);
m_block_size = a_block_size;
m_hash_size = a_hash_size;
}
#endregion
public void Initialize ()
{
m_state [0] = 0x6a09e667;
m_state [1] = 0xbb67ae85;
m_state [2] = 0x3c6ef372;
m_state [3] = 0xa54ff53a;
m_state [4] = 0x510e527f;
m_state [5] = 0x9b05688c;
m_state [6] = 0x1f83d9ab;
m_state [7] = 0x5be0cd19;
m_buffer.Initialize ();
m_processed_bytes = 0;
}
public byte[] ComputeHash (byte[] input)
{
Initialize ();
TransformBytes (input, 0, input.Length);
return TransformFinal ();
}
protected byte[] GetResult ()
{
return Converters.ConvertUIntsToBytesSwapOrder (m_state);
}
protected void Finish ()
{
ulong bits = m_processed_bytes 8;
int padindex = (m_buffer.Pos < 56) ? (56 - m_buffer.Pos) : (120 - m_buffer.Pos);
byte[] pad = new byte[padindex + 8];
pad [0] = 0x80;
Converters.ConvertULongToBytesSwapOrder (bits, pad, padindex);
padindex += 8;
TransformBytes (pad, 0, padindex);
}
protected void TransformBlock (byte[] a_data, int a_index)
{
uint[] data = new uint[64];
Converters.ConvertBytesToUIntsSwapOrder (a_data, a_index, m_block_size, data, 0);
uint A = m_state [0];
uint B = m_state [1];
uint C = m_state [2];
uint D = m_state [3];
uint E = m_state [4];
uint F = m_state [5];
uint G = m_state [6];
uint H = m_state [7];
for (int r = 16; r < 64; r++) {
uint T = data [r - 2];
uint T2 = data [r - 15];
data [r] = (((T >> 17) | (T << 15)) ^ ((T >> 19) | (T << 13)) ^ (T >> 10)) + data [r - 7] +
(((T2 >> 7) | (T2 << 25)) ^ ((T2 >> 18) | (T2 << 14)) ^ (T2 >> 3)) + data [r - 16];
}
for (int r = 0; r < 64; r++) {
uint T = s_K [r] + data [r] + H + (((E >> 6) | (E << 26)) ^ ((E >> 11) | (E << 21)) ^ ((E >> 25) |
(E << 7))) + ((E & F) ^ (~E & G));
uint T2 = (((A >> 2) | (A << 30)) ^ ((A >> 13) | (A << 19)) ^
((A >> 22) | (A << 10))) + ((A & B) ^ (A & C) ^ (B & C));
H = G;
G = F;
F = E;
E = D + T;
D = C;
C = B;
B = A;
A = T + T2;
}
m_state [0] += A;
m_state [1] += B;
m_state [2] += C;
m_state [3] += D;
m_state [4] += E;
m_state [5] += F;
m_state [6] += G;
m_state [7] += H;
}
public void TransformBytes (byte[] a_data, int a_index, int a_length)
{
//Debug.Assert (a_index >= 0);
//Debug.Assert (a_length >= 0);
//Debug.Assert (a_index + a_length <= a_data.Length);
if (!m_buffer.IsEmpty) {
if (m_buffer.Feed (a_data, ref a_index, ref a_length, ref m_processed_bytes))
TransformBlock (m_buffer.GetBytes (), 0);
}
while (a_length >= m_buffer.Length) {
m_processed_bytes += (ulong)m_buffer.Length;
TransformBlock (a_data, a_index);
a_index += m_buffer.Length;
a_length -= m_buffer.Length;
}
if (a_length > 0)
m_buffer.Feed (a_data, ref a_index, ref a_length, ref m_processed_bytes);
}
public byte[] TransformFinal ()
{
Finish ();
//Debug.Assert (m_buffer.IsEmpty);
byte[] result = GetResult ();
//Debug.Assert (result.Length == HashSize);
Initialize ();
return result;
}
}
internal class HashBuffer
{
private byte[] m_data;
private int m_pos;
public HashBuffer (int a_length)
{
//Debug.Assert (a_length > 0);
m_data = new byte[a_length];
Initialize ();
}
public void Initialize ()
{
m_pos = 0;
}
public byte[] GetBytes ()
{
//Debug.Assert (IsFull);
m_pos = 0;
return m_data;
}
public byte[] GetBytesZeroPadded ()
{
BitConverter.Clear (m_data, m_pos, m_data.Length - m_pos);
m_pos = 0;
return m_data;
}
public bool Feed (byte[] a_data, ref int a_start_index, ref int a_length, ref ulong a_processed_bytes)
{
//Debug.Assert (a_start_index >= 0);
//Debug.Assert (a_length >= 0);
//Debug.Assert (a_start_index + a_length <= a_data.Length);
//Debug.Assert (!IsFull);
if (a_data.Length == 0)
return false;
if (a_length == 0)
return false;
int length = m_data.Length - m_pos;
if (length > a_length)
length = a_length;
Array.Copy (a_data, a_start_index, m_data, m_pos, length);
m_pos += length;
a_start_index += length;
a_length -= length;
a_processed_bytes += (ulong)length;
return IsFull;
}
public bool Feed (byte[] a_data, int a_length)
{
//Debug.Assert (a_length >= 0);
//Debug.Assert (a_length <= a_data.Length);
//Debug.Assert (!IsFull);
if (a_data.Length == 0)
return false;
if (a_length == 0)
return false;
int length = m_data.Length - m_pos;
if (length > a_length)
length = a_length;
Array.Copy (a_data, 0, m_data, m_pos, length);
m_pos += length;
return IsFull;
}
public bool IsEmpty {
get {
return m_pos == 0;
}
}
public int Pos {
get {
return m_pos;
}
}
public int Length {
get {
return m_data.Length;
}
}
public bool IsFull {
get {
return (m_pos == m_data.Length);
}
}
public override string ToString ()
{
return String.Format ("HashBuffer, Legth: {0}, Pos: {1}, IsEmpty: {2}", Length, Pos, IsEmpty);
}
}
}