495 lines
11 KiB
C#
495 lines
11 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation. For more information,
|
|
* see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Linq;
|
|
|
|
namespace OpenRA.FileFormats
|
|
{
|
|
/* possibly the fugliest C# i've ever seen. */
|
|
|
|
class BlowfishKeyProvider
|
|
{
|
|
const string pubkeyStr = "AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V";
|
|
|
|
class PublicKey
|
|
{
|
|
public uint[] key1 = new uint[64];
|
|
public uint[] key2 = new uint[64];
|
|
public uint len;
|
|
}
|
|
PublicKey pubkey = new PublicKey();
|
|
|
|
uint[] glob1 = new uint[64];
|
|
uint glob1_bitlen, glob1_len_x2;
|
|
uint[] glob2 = new uint[130];
|
|
uint[] glob1_hi = new uint[4];
|
|
uint[] glob1_hi_inv = new uint[4];
|
|
uint glob1_hi_bitlen;
|
|
uint glob1_hi_inv_lo, glob1_hi_inv_hi;
|
|
|
|
static void init_bignum(uint[] n, uint val, uint len)
|
|
{
|
|
for (var i = 0; i < len; i++) n[i] = 0;
|
|
n[0] = val;
|
|
}
|
|
|
|
static void move_key_to_big(uint[] n, byte[] key, uint klen, uint blen)
|
|
{
|
|
byte sign;
|
|
|
|
if ((key[0] & 0x80) != 0) sign = 0xff;
|
|
else sign = 0;
|
|
|
|
unsafe
|
|
{
|
|
fixed (uint* _pn = &n[0])
|
|
{
|
|
var pn = (byte*)_pn;
|
|
var i = blen * 4;
|
|
for (; i > klen; i--) pn[i - 1] = (byte)sign;
|
|
for (; i > 0; i--) pn[i - 1] = key[klen - i];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void key_to_bignum(uint[] n, byte[] key, uint len)
|
|
{
|
|
uint keylen;
|
|
int i;
|
|
|
|
var j = 0;
|
|
|
|
if (key[j] != 2) return;
|
|
j++;
|
|
|
|
if ((key[j] & 0x80) != 0)
|
|
{
|
|
keylen = 0;
|
|
for (i = 0; i < (key[j] & 0x7f); i++) keylen = (keylen << 8) | key[j + i + 1];
|
|
j += (key[j] & 0x7f) + 1;
|
|
}
|
|
else
|
|
{
|
|
keylen = key[j];
|
|
j++;
|
|
}
|
|
if (keylen <= len * 4)
|
|
move_key_to_big(n, key.Skip(j).ToArray(), keylen, len);
|
|
}
|
|
|
|
static uint len_bignum(uint[] n, uint len)
|
|
{
|
|
uint i;
|
|
i = len - 1;
|
|
while ((i >= 0) && (n[i] == 0)) i--;
|
|
return i + 1;
|
|
}
|
|
|
|
static uint bitlen_bignum(uint[] n, uint len)
|
|
{
|
|
uint ddlen, bitlen, mask;
|
|
ddlen = len_bignum(n, len);
|
|
if (ddlen == 0) return 0;
|
|
bitlen = ddlen * 32;
|
|
mask = 0x80000000;
|
|
while ((mask & n[ddlen - 1]) == 0)
|
|
{
|
|
mask >>= 1;
|
|
bitlen--;
|
|
}
|
|
return bitlen;
|
|
}
|
|
|
|
void init_pubkey()
|
|
{
|
|
init_bignum(pubkey.key2, 0x10001, 64);
|
|
|
|
key_to_bignum(pubkey.key1, Convert.FromBase64String(pubkeyStr), 64);
|
|
pubkey.len = bitlen_bignum(pubkey.key1, 64) - 1;
|
|
}
|
|
|
|
uint len_predata()
|
|
{
|
|
var a = (pubkey.len - 1) / 8;
|
|
return (55 / a + 1) * (a + 1);
|
|
}
|
|
|
|
static int cmp_bignum(uint[] n1, uint[] n2, uint len)
|
|
{
|
|
|
|
while (len > 0)
|
|
{
|
|
--len;
|
|
if (n1[len] < n2[len]) return -1;
|
|
if (n1[len] > n2[len]) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void mov_bignum(uint[] dest, uint[] src, uint len)
|
|
{
|
|
Array.Copy(src, dest, len);
|
|
}
|
|
|
|
static void shr_bignum(uint[] n, int bits, int len)
|
|
{
|
|
int i; var i2 = bits / 32;
|
|
|
|
if (i2 > 0)
|
|
{
|
|
for (i = 0; i < len - i2; i++) n[i] = n[i + i2];
|
|
for (; i < len; i++) n[i] = 0;
|
|
bits = bits % 32;
|
|
}
|
|
if (bits == 0) return;
|
|
for (i = 0; i < len - 1; i++) n[i] = (n[i] >> bits) | (n[i + 1] << (32 -
|
|
bits));
|
|
n[i] = n[i] >> bits;
|
|
}
|
|
|
|
static void shl_bignum(uint[] n, int bits, int len)
|
|
{
|
|
int i, i2;
|
|
|
|
i2 = bits / 32;
|
|
if (i2 > 0)
|
|
{
|
|
for (i = len - 1; i > i2; i--) n[i] = n[i - i2];
|
|
for (; i > 0; i--) n[i] = 0;
|
|
bits = bits % 32;
|
|
}
|
|
if (bits == 0) return;
|
|
for (i = len - 1; i > 0; i--) n[i] = (n[i] << bits) | (n[i - 1] >> (32 -
|
|
bits));
|
|
n[0] <<= bits;
|
|
}
|
|
|
|
static uint sub_bignum(uint[] dest, uint[] src1, uint[] src2, uint carry, int len)
|
|
{
|
|
uint i1, i2;
|
|
|
|
len += len;
|
|
unsafe
|
|
{
|
|
fixed (uint* _ps1 = &src1[0])
|
|
fixed (uint* _ps2 = &src2[0])
|
|
fixed (uint* _pd = &dest[0])
|
|
{
|
|
var ps1 = (ushort*)_ps1;
|
|
var ps2 = (ushort*)_ps2;
|
|
var pd = (ushort*)_pd;
|
|
|
|
while (--len != -1)
|
|
{
|
|
i1 = *ps1++;
|
|
i2 = *ps2++;
|
|
*pd++ = (ushort)(i1 - i2 - carry);
|
|
if (((i1 - i2 - carry) & 0x10000) != 0) carry = 1; else carry = 0;
|
|
}
|
|
}
|
|
}
|
|
return carry;
|
|
}
|
|
|
|
static unsafe uint sub_bignum(uint* dest, uint* src1, uint* src2, uint carry, int len)
|
|
{
|
|
uint i1, i2;
|
|
|
|
len += len;
|
|
|
|
var ps1 = (ushort*)src1;
|
|
var ps2 = (ushort*)src2;
|
|
var pd = (ushort*)dest;
|
|
|
|
while (--len != -1)
|
|
{
|
|
i1 = *ps1++;
|
|
i2 = *ps2++;
|
|
*pd++ = (ushort)(i1 - i2 - carry);
|
|
if (((i1 - i2 - carry) & 0x10000) != 0) carry = 1; else carry = 0;
|
|
|
|
}
|
|
return carry;
|
|
}
|
|
|
|
static void inv_bignum(uint[] n1, uint[] n2, uint len)
|
|
{
|
|
var n_tmp = new uint[64];
|
|
uint n2_bytelen, bit;
|
|
int n2_bitlen;
|
|
|
|
var j = 0;
|
|
|
|
init_bignum(n_tmp, 0, len);
|
|
init_bignum(n1, 0, len);
|
|
n2_bitlen = (int)bitlen_bignum(n2, len);
|
|
bit = ((uint)1) << (n2_bitlen % 32);
|
|
j = ((n2_bitlen + 32) / 32) - 1;
|
|
n2_bytelen = (uint)((n2_bitlen - 1) / 32) * 4;
|
|
n_tmp[n2_bytelen / 4] |= ((uint)1) << ((n2_bitlen - 1) & 0x1f);
|
|
|
|
while (n2_bitlen > 0)
|
|
{
|
|
n2_bitlen--;
|
|
shl_bignum(n_tmp, 1, (int)len);
|
|
if (cmp_bignum(n_tmp, n2, len) != -1)
|
|
{
|
|
sub_bignum(n_tmp, n_tmp, n2, 0, (int)len);
|
|
n1[j] |= bit;
|
|
}
|
|
bit >>= 1;
|
|
if (bit == 0)
|
|
{
|
|
j--;
|
|
bit = 0x80000000;
|
|
}
|
|
}
|
|
init_bignum(n_tmp, 0, len);
|
|
}
|
|
|
|
static void inc_bignum(uint[] n, uint len)
|
|
{
|
|
var i = 0;
|
|
while ((++n[i] == 0) && (--len > 0)) i++;
|
|
}
|
|
|
|
void init_two_dw(uint[] n, uint len)
|
|
{
|
|
mov_bignum(glob1, n, len);
|
|
glob1_bitlen = bitlen_bignum(glob1, len);
|
|
glob1_len_x2 = (glob1_bitlen + 15) / 16;
|
|
mov_bignum(glob1_hi, glob1.Skip((int)len_bignum(glob1, len) - 2).ToArray(), 2);
|
|
glob1_hi_bitlen = bitlen_bignum(glob1_hi, 2) - 32;
|
|
shr_bignum(glob1_hi, (int)glob1_hi_bitlen, 2);
|
|
inv_bignum(glob1_hi_inv, glob1_hi, 2);
|
|
shr_bignum(glob1_hi_inv, 1, 2);
|
|
glob1_hi_bitlen = (glob1_hi_bitlen + 15) % 16 + 1;
|
|
inc_bignum(glob1_hi_inv, 2);
|
|
if (bitlen_bignum(glob1_hi_inv, 2) > 32)
|
|
{
|
|
shr_bignum(glob1_hi_inv, 1, 2);
|
|
glob1_hi_bitlen--;
|
|
}
|
|
glob1_hi_inv_lo = (ushort)glob1_hi_inv[0];
|
|
glob1_hi_inv_hi = (ushort)(glob1_hi_inv[0] >> 16);
|
|
}
|
|
|
|
static unsafe void mul_bignum_word(ushort* pn1, uint[] n2, uint mul, uint len)
|
|
{
|
|
uint i, tmp;
|
|
unsafe
|
|
{
|
|
fixed (uint* _pn2 = &n2[0])
|
|
{
|
|
var pn2 = (ushort*)_pn2;
|
|
|
|
tmp = 0;
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
tmp = mul * (*pn2) + (*pn1) + tmp;
|
|
*pn1 = (ushort)tmp;
|
|
pn1++;
|
|
pn2++;
|
|
tmp >>= 16;
|
|
}
|
|
*pn1 += (ushort)tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mul_bignum(uint[] dest, uint[] src1, uint[] src2, uint len)
|
|
{
|
|
uint i;
|
|
|
|
unsafe
|
|
{
|
|
fixed( uint * _psrc2 = &src2[0] )
|
|
fixed(uint* _pdest = &dest[0])
|
|
{
|
|
var psrc2 = (ushort*)_psrc2;
|
|
var pdest = (ushort*)_pdest;
|
|
|
|
init_bignum(dest, 0, len * 2);
|
|
for (i = 0; i < len * 2; i++)
|
|
mul_bignum_word(pdest++, src1, *psrc2++, len * 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void not_bignum(uint[] n, uint len)
|
|
{
|
|
uint i;
|
|
for (i = 0; i < len; i++) n[i] = ~n[i];
|
|
}
|
|
|
|
static void neg_bignum(uint[] n, uint len)
|
|
{
|
|
not_bignum(n, len);
|
|
inc_bignum(n, len);
|
|
}
|
|
|
|
unsafe uint get_mulword(uint* n)
|
|
{
|
|
var wn = (ushort*)n;
|
|
var i = (uint)((((((((((*(wn - 1) ^ 0xffff) & 0xffff) * glob1_hi_inv_lo + 0x10000) >> 1)
|
|
+ (((*(wn - 2) ^ 0xffff) * glob1_hi_inv_hi + glob1_hi_inv_hi) >> 1) + 1)
|
|
>> 16) + ((((*(wn - 1) ^ 0xffff) & 0xffff) * glob1_hi_inv_hi) >> 1) +
|
|
(((*wn ^ 0xffff) * glob1_hi_inv_lo) >> 1) + 1) >> 14) + glob1_hi_inv_hi
|
|
* (*wn ^ 0xffff) * 2) >> (int)glob1_hi_bitlen);
|
|
if (i > 0xffff) i = 0xffff;
|
|
return i & 0xffff;
|
|
}
|
|
|
|
static void dec_bignum(uint[] n, uint len)
|
|
{
|
|
var i = 0;
|
|
while ((--n[i] == 0xffffffff) && (--len > 0))
|
|
i++;
|
|
}
|
|
|
|
void calc_a_bignum(uint[] n1, uint[] n2, uint[] n3, uint len)
|
|
{
|
|
uint g2_len_x2, len_diff;
|
|
unsafe
|
|
{
|
|
fixed( uint* g1 = &glob1[0])
|
|
fixed (uint* g2 = &glob2[0])
|
|
{
|
|
mul_bignum(glob2, n2, n3, len);
|
|
glob2[len * 2] = 0;
|
|
g2_len_x2 = len_bignum(glob2, len * 2 + 1) * 2;
|
|
if (g2_len_x2 >= glob1_len_x2)
|
|
{
|
|
inc_bignum(glob2, len * 2 + 1);
|
|
neg_bignum(glob2, len * 2 + 1);
|
|
len_diff = g2_len_x2 + 1 - glob1_len_x2;
|
|
var esi = ((ushort*)g2) + (1 + g2_len_x2 - glob1_len_x2);
|
|
var edi = ((ushort*)g2) + (g2_len_x2 + 1);
|
|
for (; len_diff != 0; len_diff--)
|
|
{
|
|
edi--;
|
|
var tmp = get_mulword((uint*)edi);
|
|
esi--;
|
|
if (tmp > 0)
|
|
{
|
|
mul_bignum_word(esi, glob1, tmp, 2 * len);
|
|
if ((*edi & 0x8000) == 0)
|
|
{
|
|
if (0 != sub_bignum((uint*)esi, (uint*)esi, g1, 0, (int)len)) (*edi)--;
|
|
}
|
|
}
|
|
}
|
|
neg_bignum(glob2, len);
|
|
dec_bignum(glob2, len);
|
|
}
|
|
mov_bignum(n1, glob2, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
void clear_tmp_vars(uint len)
|
|
{
|
|
init_bignum(glob1, 0, len);
|
|
init_bignum(glob2, 0, len);
|
|
init_bignum(glob1_hi_inv, 0, 4);
|
|
init_bignum(glob1_hi, 0, 4);
|
|
glob1_bitlen = 0;
|
|
glob1_hi_bitlen = 0;
|
|
glob1_len_x2 = 0;
|
|
glob1_hi_inv_lo = 0;
|
|
glob1_hi_inv_hi = 0;
|
|
}
|
|
|
|
void calc_a_key(uint[] n1, uint[] n2, uint[] n3, uint[] n4, uint len)
|
|
{
|
|
var n_tmp = new uint[64];
|
|
uint n3_len, n4_len;
|
|
int n3_bitlen;
|
|
uint bit_mask;
|
|
|
|
unsafe
|
|
{
|
|
fixed (uint* _pn3 = &n3[0])
|
|
{
|
|
var pn3 = _pn3;
|
|
|
|
init_bignum(n1, 1, len);
|
|
n4_len = len_bignum(n4, len);
|
|
init_two_dw(n4, n4_len);
|
|
n3_bitlen = (int)bitlen_bignum(n3, n4_len);
|
|
n3_len = (uint)((n3_bitlen + 31) / 32);
|
|
bit_mask = (((uint)1) << ((n3_bitlen - 1) % 32)) >> 1;
|
|
pn3 += n3_len - 1;
|
|
n3_bitlen--;
|
|
mov_bignum(n1, n2, n4_len);
|
|
while (--n3_bitlen != -1)
|
|
{
|
|
if (bit_mask == 0)
|
|
{
|
|
bit_mask = 0x80000000;
|
|
pn3--;
|
|
}
|
|
calc_a_bignum(n_tmp, n1, n1, n4_len);
|
|
if ((*pn3 & bit_mask) != 0)
|
|
calc_a_bignum(n1, n_tmp, n2, n4_len);
|
|
else
|
|
mov_bignum(n1, n_tmp, n4_len);
|
|
bit_mask >>= 1;
|
|
}
|
|
init_bignum(n_tmp, 0, n4_len);
|
|
clear_tmp_vars(len);
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsafe void memcpy(byte* dest, byte* src, int len)
|
|
{
|
|
while (len-- != 0) *dest++ = *src++;
|
|
}
|
|
|
|
unsafe void process_predata(byte* pre, uint pre_len, byte *buf)
|
|
{
|
|
var n2 = new uint[64];
|
|
var n3 = new uint[64];
|
|
|
|
var a = (pubkey.len - 1) / 8;
|
|
while (a + 1 <= pre_len)
|
|
{
|
|
init_bignum(n2, 0, 64);
|
|
fixed( uint * pn2 = &n2[0] )
|
|
memcpy((byte *)pn2, pre, (int)a + 1);
|
|
calc_a_key(n3, n2, pubkey.key2, pubkey.key1, 64);
|
|
|
|
fixed( uint * pn3 = &n3[0] )
|
|
memcpy(buf, (byte *)pn3, (int)a);
|
|
|
|
pre_len -= a + 1;
|
|
pre += a + 1;
|
|
buf += a;
|
|
}
|
|
}
|
|
|
|
public byte[] DecryptKey(byte[] src)
|
|
{
|
|
init_pubkey();
|
|
var dest = new byte[256];
|
|
|
|
unsafe
|
|
{
|
|
fixed (byte* pdest = &dest[0])
|
|
fixed (byte* psrc = &src[0])
|
|
process_predata(psrc, len_predata(), pdest);
|
|
}
|
|
return dest.Take(56).ToArray();
|
|
}
|
|
}
|
|
}
|