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 ;
2022-01-25 16:37:09 +01:00
using System.Reflection ;
2021-09-18 07:26:06 +02:00
using System.Text.Json ;
2024-03-28 12:13:56 +01:00
using System.Text.Json.Serialization ;
2022-01-11 04:14:23 +01:00
using System.Threading ;
using System.Threading.Tasks ;
2021-08-04 00:40:09 +02:00
2024-03-26 12:07:03 +01:00
#if NET8_0_OR_GREATER
[JsonSerializable(typeof(WTelegram.Session))]
internal partial class WTelegramContext : JsonSerializerContext { }
#endif
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>
2022-01-13 03:06:22 +01:00
public static readonly JsonSerializerOptions JsonOptions = new ( ) { IncludeFields = true , WriteIndented = true ,
2024-03-26 12:07:03 +01:00
#if NET8_0_OR_GREATER
2024-03-28 12:13:56 +01:00
TypeInfoResolver = JsonSerializer . IsReflectionEnabledByDefault ? null : WTelegramContext . Default ,
2024-03-26 12:07:03 +01:00
#endif
2024-03-28 12:13:56 +01:00
IgnoreReadOnlyProperties = true , DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull } ;
2021-08-04 00:40:09 +02:00
2024-03-08 11:52:30 +01:00
private static readonly ConsoleColor [ ] LogLevelToColor = [ ConsoleColor . DarkGray , ConsoleColor . DarkCyan ,
ConsoleColor . Cyan , ConsoleColor . Yellow , ConsoleColor . Red , ConsoleColor . Magenta , ConsoleColor . DarkBlue ] ;
2021-08-04 10:11:07 +02:00
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
2022-10-01 13:56:43 +02:00
long value = 0 ;
System . Security . Cryptography . RandomNumberGenerator . Fill ( System . Runtime . InteropServices . MemoryMarshal . AsBytes (
System . Runtime . InteropServices . MemoryMarshal . CreateSpan ( ref value , 1 ) ) ) ;
return value ;
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 ;
}
2022-10-16 11:49:16 +02:00
internal static ulong PQFactorize ( ulong pq ) // ported from https://github.com/tdlib/td/blob/master/tdutils/td/utils/crypto.cpp#L103
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 + + ;
2022-10-16 11:49:16 +02:00
// x = (q + x * x) % pq
ulong res = q , a = x ;
while ( x ! = 0 )
2021-08-04 00:40:09 +02:00
{
2022-10-16 11:49:16 +02:00
if ( ( x & 1 ) ! = 0 )
res = ( res + a ) % pq ;
a = ( a + a ) % pq ;
x > > = 1 ;
2021-08-04 00:40:09 +02:00
}
2022-10-16 11:49:16 +02:00
x = res ;
2021-08-04 00:40:09 +02:00
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 ;
2022-10-16 11:49:16 +02:00
static ulong gcd ( ulong left , ulong right )
2021-08-04 00:40:09 +02:00
{
2022-10-16 11:49:16 +02:00
while ( right ! = 0 )
2021-08-04 00:40:09 +02:00
{
2022-10-16 11:49:16 +02:00
ulong num = left % right ;
left = right ;
right = num ;
2021-08-04 00:40:09 +02:00
}
2022-10-16 11:49:16 +02:00
return left ;
2021-08-04 00:40:09 +02:00
}
}
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-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 ;
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 ;
}
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
2024-03-08 11:52:30 +01:00
[
2021-12-28 09:38:22 +01:00
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
2024-03-08 11:52:30 +01:00
] ;
2022-01-20 02:44:43 +01:00
2022-01-25 16:37:09 +01:00
internal static string GetSystemVersion ( )
{
var os = System . Runtime . InteropServices . RuntimeInformation . OSDescription ;
int space = os . IndexOf ( ' ' ) + 1 , dot = os . IndexOf ( '.' ) ;
return os [ ( os . IndexOf ( ' ' , space ) < 0 ? 0 : space ) . . ( dot < 0 ? os . Length : dot ) ] ;
}
internal static string GetAppVersion ( )
= > ( Assembly . GetEntryAssembly ( ) ? ? Array . Find ( AppDomain . CurrentDomain . GetAssemblies ( ) , a = > a . EntryPoint ! = null ) ) ? . GetName ( ) . Version . ToString ( ) ? ? "0.0" ;
2024-03-12 19:07:48 +01:00
public class IndirectStream ( Stream innerStream ) : Stream
2022-01-20 02:44:43 +01:00
{
2022-01-21 01:40:10 +01:00
public long? ContentLength ;
2024-03-12 19:07:48 +01:00
protected readonly Stream _innerStream = innerStream ;
2022-01-21 01:40:10 +01:00
public override bool CanRead = > _innerStream . CanRead ;
2023-10-24 17:05:39 +02:00
public override bool CanSeek = > ContentLength . HasValue | | _innerStream . CanSeek ;
2022-01-21 01:40:10 +01:00
public override bool CanWrite = > _innerStream . CanWrite ;
public override long Length = > ContentLength ? ? _innerStream . Length ;
public override long Position { get = > _innerStream . Position ; set = > _innerStream . Position = value ; }
public override void Flush ( ) = > _innerStream . Flush ( ) ;
public override long Seek ( long offset , SeekOrigin origin ) = > _innerStream . Seek ( offset , origin ) ;
public override void SetLength ( long value ) = > _innerStream . SetLength ( value ) ;
public override void Write ( byte [ ] buffer , int offset , int count ) = > _innerStream . Write ( buffer , offset , count ) ;
2023-05-27 18:05:24 +02:00
public override int Read ( byte [ ] buffer , int offset , int count ) = > _innerStream . Read ( buffer , offset , count ) ;
2022-01-21 01:40:10 +01:00
protected override void Dispose ( bool disposing ) = > _innerStream . Dispose ( ) ;
2022-01-20 02:44:43 +01:00
}
2021-08-04 00:40:09 +02:00
}
2023-04-02 13:44:23 +02:00
public class WTException : ApplicationException
{
public WTException ( string message ) : base ( message ) { }
public WTException ( string message , Exception innerException ) : base ( message , innerException ) { }
}
2021-08-04 00:40:09 +02:00
}