2021-08-04 00:40:09 +02:00
using System ;
using System.Collections.Generic ;
2022-01-11 04:14:23 +01:00
using System.IO ;
2021-08-10 08:25:37 +02:00
using System.Numerics ;
2021-09-18 07:26:06 +02:00
using System.Text.Json ;
2022-01-11 04:14:23 +01:00
using System.Threading ;
using System.Threading.Tasks ;
2021-08-04 00:40:09 +02:00
namespace WTelegram
{
public static class Helpers
{
2021-11-12 06:10:26 +01:00
/// <summary>Callback for logging a line (string) with its associated <see href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel">severity level</see> (int)</summary>
2021-08-06 01:54:29 +02:00
public static Action < int , string > Log { get ; set ; } = DefaultLogger ;
2021-11-06 05:22:33 +01:00
/// <summary>For serializing indented Json with fields included</summary>
2021-12-04 00:14:15 +01:00
public static readonly JsonSerializerOptions JsonOptions = new ( ) { IncludeFields = true , WriteIndented = true , IgnoreReadOnlyProperties = true } ;
2021-08-04 00:40:09 +02:00
2021-08-04 10:11:07 +02:00
private static readonly ConsoleColor [ ] LogLevelToColor = new [ ] { ConsoleColor . DarkGray , ConsoleColor . DarkCyan , ConsoleColor . Cyan ,
ConsoleColor . Yellow , ConsoleColor . Red , ConsoleColor . Magenta , ConsoleColor . DarkBlue } ;
private static void DefaultLogger ( int level , string message )
{
Console . ForegroundColor = LogLevelToColor [ level ] ;
Console . WriteLine ( message ) ;
Console . ResetColor ( ) ;
}
2022-01-07 00:24:47 +01:00
public static V GetOrCreate < K , V > ( this Dictionary < K , V > dictionary , K key ) where V : new ( )
= > dictionary . TryGetValue ( key , out V value ) ? value : dictionary [ key ] = new V ( ) ;
2021-11-06 05:22:33 +01:00
/// <summary>Get a cryptographic random 64-bit value</summary>
2021-08-05 16:29:58 +02:00
public static long RandomLong ( )
{
2021-08-16 22:30:45 +02:00
#if NETCOREAPP2_1_OR_GREATER
2021-08-05 16:29:58 +02:00
Span < long > span = stackalloc long [ 1 ] ;
System . Security . Cryptography . RandomNumberGenerator . Fill ( System . Runtime . InteropServices . MemoryMarshal . AsBytes ( span ) ) ;
return span [ 0 ] ;
2021-08-16 22:30:45 +02:00
#else
var span = new byte [ 8 ] ;
Encryption . RNG . GetBytes ( span ) ;
return BitConverter . ToInt64 ( span , 0 ) ;
#endif
2021-08-05 16:29:58 +02:00
}
2022-01-11 04:14:23 +01:00
public static async Task < int > FullReadAsync ( this Stream stream , byte [ ] buffer , int length , CancellationToken ct )
{
for ( int offset = 0 ; offset < length ; )
{
#pragma warning disable CA1835
var read = await stream . ReadAsync ( buffer , offset , length - offset , ct ) ;
#pragma warning restore CA1835
if ( read = = 0 ) return offset ;
offset + = read ;
}
return length ;
}
2021-11-06 05:22:33 +01:00
internal static byte [ ] ToBigEndian ( ulong value ) // variable-size buffer
2021-08-04 00:40:09 +02:00
{
2022-01-07 00:24:47 +01:00
int i = 1 ;
for ( ulong temp = value ; ( temp > > = 8 ) ! = 0 ; ) i + + ;
2021-08-04 00:40:09 +02:00
var result = new byte [ i ] ;
2022-01-07 00:24:47 +01:00
for ( ; - - i > = 0 ; value > > = 8 )
result [ i ] = ( byte ) value ;
2021-08-04 00:40:09 +02:00
return result ;
}
2021-11-06 05:22:33 +01:00
internal static ulong FromBigEndian ( byte [ ] bytes ) // variable-size buffer
2021-08-04 00:40:09 +02:00
{
2021-08-06 01:54:29 +02:00
if ( bytes . Length > 8 ) throw new ArgumentException ( $"expected bytes length <= 8 but got {bytes.Length}" ) ;
2021-08-04 00:40:09 +02:00
ulong result = 0 ;
foreach ( byte b in bytes )
result = ( result < < 8 ) + b ;
return result ;
}
2021-08-20 02:13:58 +02:00
internal static byte [ ] To256Bytes ( this BigInteger bi )
{
var bigEndian = bi . ToByteArray ( true , true ) ;
if ( bigEndian . Length = = 256 ) return bigEndian ;
var result = new byte [ 256 ] ;
bigEndian . CopyTo ( result , 256 - bigEndian . Length ) ;
return result ;
}
2021-08-07 06:23:13 +02:00
internal static ulong PQFactorize ( ulong pq ) // ported from https://github.com/tdlib/td/blob/master/tdutils/td/utils/crypto.cpp#L90
2021-08-04 00:40:09 +02:00
{
if ( pq < 2 ) return 1 ;
var random = new Random ( ) ;
ulong g = 0 ;
for ( int i = 0 , iter = 0 ; i < 3 | | iter < 1000 ; i + + )
{
ulong q = ( ulong ) random . Next ( 17 , 32 ) % ( pq - 1 ) ;
ulong x = ( ( ulong ) random . Next ( ) + ( ulong ) random . Next ( ) < < 31 ) % ( pq - 1 ) + 1 ;
ulong y = x ;
int lim = 1 < < ( Math . Min ( 5 , i ) + 18 ) ;
for ( int j = 1 ; j < lim ; j + + )
{
iter + + ;
ulong a = x ;
ulong b = x ;
ulong c = q ;
// c += a * b
while ( b ! = 0 )
{
if ( ( b & 1 ) ! = 0 )
{
c + = a ;
if ( c > = pq )
c - = pq ;
}
a + = a ;
if ( a > = pq )
a - = pq ;
b > > = 1 ;
}
x = c ;
ulong z = x < y ? pq + x - y : x - y ;
g = gcd ( z , pq ) ;
if ( g ! = 1 )
break ;
if ( ( j & ( j - 1 ) ) = = 0 )
y = x ;
}
if ( g > 1 & & g < pq )
break ;
}
if ( g ! = 0 )
{
ulong other = pq / g ;
if ( other < g )
g = other ;
}
return g ;
static ulong gcd ( ulong a , ulong b )
{
if ( a = = 0 ) return b ;
if ( b = = 0 ) return a ;
int shift = 0 ;
while ( ( a & 1 ) = = 0 & & ( b & 1 ) = = 0 )
{
a > > = 1 ;
b > > = 1 ;
shift + + ;
}
while ( true )
{
while ( ( a & 1 ) = = 0 )
a > > = 1 ;
while ( ( b & 1 ) = = 0 )
b > > = 1 ;
if ( a > b )
a - = b ;
else if ( b > a )
b - = a ;
else
return a < < shift ;
}
}
}
2021-08-10 08:25:37 +02:00
2021-08-20 02:33:43 +02:00
public static int MillerRabinIterations { get ; set ; } = 64 ; // 64 is OpenSSL default for 2048-bits numbers
2021-09-01 23:03:04 +02:00
private static readonly HashSet < BigInteger > GoodPrimes = new ( ) ;
2021-11-06 05:22:33 +01:00
/// <summary>Miller– Rabin primality test</summary>
/// <param name="n">The number to check for primality</param>
2021-08-20 02:33:43 +02:00
public static bool IsProbablePrime ( this BigInteger n )
2021-08-10 08:25:37 +02:00
{
2021-08-20 02:13:58 +02:00
var n_minus_one = n - BigInteger . One ;
if ( n_minus_one . Sign < = 0 ) return false ;
2021-09-01 23:03:04 +02:00
if ( GoodPrimes . Contains ( n ) ) return true ;
2021-08-20 02:13:58 +02:00
int s ;
var d = n_minus_one ;
for ( s = 0 ; d . IsEven ; s + + ) d > > = 1 ;
var bitLen = n . GetBitLength ( ) ;
var randomBytes = new byte [ bitLen / 8 + 1 ] ;
var lastByteMask = ( byte ) ( ( 1 < < ( int ) ( bitLen % 8 ) ) - 1 ) ;
BigInteger a ;
2021-08-20 02:33:43 +02:00
if ( MillerRabinIterations < 15 ) // 15 is the minimum recommended by Telegram
Log ( 3 , $"MillerRabinIterations ({MillerRabinIterations}) is below the minimal level of safety (15)" ) ;
for ( int i = 0 ; i < MillerRabinIterations ; i + + )
2021-08-20 02:13:58 +02:00
{
do
{
Encryption . RNG . GetBytes ( randomBytes ) ;
randomBytes [ ^ 1 ] & = lastByteMask ; // we don't want more bits than necessary
a = new BigInteger ( randomBytes ) ;
}
while ( a < 3 | | a > = n_minus_one ) ;
a - - ;
var x = BigInteger . ModPow ( a , d , n ) ;
if ( x . IsOne | | x = = n_minus_one ) continue ;
int r ;
for ( r = s - 1 ; r > 0 ; r - - )
{
x = BigInteger . ModPow ( x , 2 , n ) ;
if ( x . IsOne ) return false ;
if ( x = = n_minus_one ) break ;
}
if ( r = = 0 ) return false ;
}
2021-09-01 23:03:04 +02:00
GoodPrimes . Add ( n ) ;
2021-08-20 02:13:58 +02:00
return true ;
2021-08-10 08:25:37 +02:00
}
2021-12-28 09:38:22 +01:00
internal static readonly byte [ ] StrippedThumbJPG = // see https://core.telegram.org/api/files#stripped-thumbnails
{
0xff , 0xd8 , 0xff , 0xe0 , 0x00 , 0x10 , 0x4a , 0x46 , 0x49 ,
0x46 , 0x00 , 0x01 , 0x01 , 0x00 , 0x00 , 0x01 , 0x00 , 0x01 , 0x00 , 0x00 , 0xff , 0xdb , 0x00 , 0x43 , 0x00 , 0x28 , 0x1c ,
0x1e , 0x23 , 0x1e , 0x19 , 0x28 , 0x23 , 0x21 , 0x23 , 0x2d , 0x2b , 0x28 , 0x30 , 0x3c , 0x64 , 0x41 , 0x3c , 0x37 , 0x37 ,
0x3c , 0x7b , 0x58 , 0x5d , 0x49 , 0x64 , 0x91 , 0x80 , 0x99 , 0x96 , 0x8f , 0x80 , 0x8c , 0x8a , 0xa0 , 0xb4 , 0xe6 , 0xc3 ,
0xa0 , 0xaa , 0xda , 0xad , 0x8a , 0x8c , 0xc8 , 0xff , 0xcb , 0xda , 0xee , 0xf5 , 0xff , 0xff , 0xff , 0x9b , 0xc1 , 0xff ,
0xff , 0xff , 0xfa , 0xff , 0xe6 , 0xfd , 0xff , 0xf8 , 0xff , 0xdb , 0x00 , 0x43 , 0x01 , 0x2b , 0x2d , 0x2d , 0x3c , 0x35 ,
0x3c , 0x76 , 0x41 , 0x41 , 0x76 , 0xf8 , 0xa5 , 0x8c , 0xa5 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 ,
0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 ,
0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 ,
0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xf8 , 0xff , 0xc0 , 0x00 , 0x11 , 0x08 , 0x00 , 0x00 , 0x00 , 0x00 , 0x03 , 0x01 , 0x22 , 0x00 ,
0x02 , 0x11 , 0x01 , 0x03 , 0x11 , 0x01 , 0xff , 0xc4 , 0x00 , 0x1f , 0x00 , 0x00 , 0x01 , 0x05 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 ,
0x09 , 0x0a , 0x0b , 0xff , 0xc4 , 0x00 , 0xb5 , 0x10 , 0x00 , 0x02 , 0x01 , 0x03 , 0x03 , 0x02 , 0x04 , 0x03 , 0x05 , 0x05 ,
0x04 , 0x04 , 0x00 , 0x00 , 0x01 , 0x7d , 0x01 , 0x02 , 0x03 , 0x00 , 0x04 , 0x11 , 0x05 , 0x12 , 0x21 , 0x31 , 0x41 , 0x06 ,
0x13 , 0x51 , 0x61 , 0x07 , 0x22 , 0x71 , 0x14 , 0x32 , 0x81 , 0x91 , 0xa1 , 0x08 , 0x23 , 0x42 , 0xb1 , 0xc1 , 0x15 , 0x52 ,
0xd1 , 0xf0 , 0x24 , 0x33 , 0x62 , 0x72 , 0x82 , 0x09 , 0x0a , 0x16 , 0x17 , 0x18 , 0x19 , 0x1a , 0x25 , 0x26 , 0x27 , 0x28 ,
0x29 , 0x2a , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3a , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4a , 0x53 ,
0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5a , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6a , 0x73 , 0x74 , 0x75 ,
0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8a , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 ,
0x97 , 0x98 , 0x99 , 0x9a , 0xa2 , 0xa3 , 0xa4 , 0xa5 , 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa , 0xb2 , 0xb3 , 0xb4 , 0xb5 , 0xb6 ,
0xb7 , 0xb8 , 0xb9 , 0xba , 0xc2 , 0xc3 , 0xc4 , 0xc5 , 0xc6 , 0xc7 , 0xc8 , 0xc9 , 0xca , 0xd2 , 0xd3 , 0xd4 , 0xd5 , 0xd6 ,
0xd7 , 0xd8 , 0xd9 , 0xda , 0xe1 , 0xe2 , 0xe3 , 0xe4 , 0xe5 , 0xe6 , 0xe7 , 0xe8 , 0xe9 , 0xea , 0xf1 , 0xf2 , 0xf3 , 0xf4 ,
0xf5 , 0xf6 , 0xf7 , 0xf8 , 0xf9 , 0xfa , 0xff , 0xc4 , 0x00 , 0x1f , 0x01 , 0x00 , 0x03 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 ,
0x09 , 0x0a , 0x0b , 0xff , 0xc4 , 0x00 , 0xb5 , 0x11 , 0x00 , 0x02 , 0x01 , 0x02 , 0x04 , 0x04 , 0x03 , 0x04 , 0x07 , 0x05 ,
0x04 , 0x04 , 0x00 , 0x01 , 0x02 , 0x77 , 0x00 , 0x01 , 0x02 , 0x03 , 0x11 , 0x04 , 0x05 , 0x21 , 0x31 , 0x06 , 0x12 , 0x41 ,
0x51 , 0x07 , 0x61 , 0x71 , 0x13 , 0x22 , 0x32 , 0x81 , 0x08 , 0x14 , 0x42 , 0x91 , 0xa1 , 0xb1 , 0xc1 , 0x09 , 0x23 , 0x33 ,
0x52 , 0xf0 , 0x15 , 0x62 , 0x72 , 0xd1 , 0x0a , 0x16 , 0x24 , 0x34 , 0xe1 , 0x25 , 0xf1 , 0x17 , 0x18 , 0x19 , 0x1a , 0x26 ,
0x27 , 0x28 , 0x29 , 0x2a , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3a , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4a ,
0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5a , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6a , 0x73 , 0x74 ,
0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8a , 0x92 , 0x93 , 0x94 ,
0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9a , 0xa2 , 0xa3 , 0xa4 , 0xa5 , 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa , 0xb2 , 0xb3 , 0xb4 ,
0xb5 , 0xb6 , 0xb7 , 0xb8 , 0xb9 , 0xba , 0xc2 , 0xc3 , 0xc4 , 0xc5 , 0xc6 , 0xc7 , 0xc8 , 0xc9 , 0xca , 0xd2 , 0xd3 , 0xd4 ,
0xd5 , 0xd6 , 0xd7 , 0xd8 , 0xd9 , 0xda , 0xe2 , 0xe3 , 0xe4 , 0xe5 , 0xe6 , 0xe7 , 0xe8 , 0xe9 , 0xea , 0xf2 , 0xf3 , 0xf4 ,
0xf5 , 0xf6 , 0xf7 , 0xf8 , 0xf9 , 0xfa , 0xff , 0xda , 0x00 , 0x0c , 0x03 , 0x01 , 0x00 , 0x02 , 0x11 , 0x03 , 0x11 , 0x00 ,
0x3f , 0x00
} ;
2021-08-04 00:40:09 +02:00
}
}