Home telegram - security arithmetics
Post
Cancel

telegram - security arithmetics

RsaFingerprint in PublicKey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
uint64_t MTRsaFingerprint(NSString *key) {
    BIO *keyBio = BIO_new(BIO_s_mem());
    
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    BIO_write(keyBio, keyData.bytes, (int)keyData.length);
    RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
    
    int nBytes = BN_num_bytes(rsaKey->n);
    int eBytes = BN_num_bytes(rsaKey->e);
    
    MTBuffer *buffer = [[MTBuffer alloc] init];
    
    NSMutableData *nData = [[NSMutableData alloc] initWithLength:nBytes];
    BN_bn2bin(rsaKey->n, nData.mutableBytes);
    [buffer appendTLBytes:nData];
    
    NSMutableData *eData = [[NSMutableData alloc] initWithLength:eBytes];
    BN_bn2bin(rsaKey->e, eData.mutableBytes);
    [buffer appendTLBytes:eData];
    
    NSData *sha1Data = MTSha1(buffer.data);
    static uint8_t sha1Buffer[20];
    [sha1Data getBytes:sha1Buffer length:20];
    
    uint64_t fingerprint = (((uint64_t) sha1Buffer[19]) << 56) |
    (((uint64_t) sha1Buffer[18]) << 48) |
    (((uint64_t) sha1Buffer[17]) << 40) |
    (((uint64_t) sha1Buffer[16]) << 32) |
    (((uint64_t) sha1Buffer[15]) << 24) |
    (((uint64_t) sha1Buffer[14]) << 16) |
    (((uint64_t) sha1Buffer[13]) << 8) |
    ((uint64_t) sha1Buffer[12]);
    RSA_free(rsaKey);
    BIO_free(keyBio);
    
    return fingerprint;
}

Sha1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NSData *MTSha1(NSData *data)
{
    uint8_t digest[20];
    CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
    
    return [[NSData alloc] initWithBytes:digest length:20];
}

NSData *MTSubdataSha1(NSData *data, NSUInteger offset, NSUInteger length)
{
    uint8_t digest[20];
    CC_SHA1(((uint8_t *)data.bytes) + offset, (CC_LONG)length, digest);
    
    return [[NSData alloc] initWithBytes:digest length:20];
}

void MTRawSha1(void const *inData, NSUInteger length, void *outData)
{
    CC_SHA1(inData, (CC_LONG)length, outData);
}

Sha256

1
2
3
4
5
6
7
8
9
10
11
12
NSData *MTSha256(NSData *data)
{
    uint8_t digest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
    
    return [[NSData alloc] initWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
}

void MTRawSha256(void const *inData, NSUInteger length, void *outData)
{
    CC_SHA256(inData, (CC_LONG)length, outData);
}

MurmurHash3_x86_32

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
static void MurmurHash3_x86_32 ( const void * key, int len,
                                uint32_t seed, void * out )
{
    const uint8_t * data = (const uint8_t*)key;
    const int nblocks = len / 4;
    
    uint32_t h1 = seed;
    
    const uint32_t c1 = 0xcc9e2d51;
    const uint32_t c2 = 0x1b873593;
    
    //----------
    // body
    
    const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
    
    for(int i = -nblocks; i; i++)
    {
        uint32_t k1 = getblock(blocks,i);
        
        k1 *= c1;
        k1 = ROTL32(k1,15);
        k1 *= c2;
        
        h1 ^= k1;
        h1 = ROTL32(h1,13);
        h1 = h1*5+0xe6546b64;
    }
    
    //----------
    // tail
    
    const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
    
    uint32_t k1 = 0;
    
    switch(len & 3)
    {
        case 3: k1 ^= tail[2] << 16;
        case 2: k1 ^= tail[1] << 8;
        case 1: k1 ^= tail[0];
            k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
    };
    
    //----------
    // finalization
    
    h1 ^= len;
    
    h1 = fmix(h1);
    
    *(uint32_t*)out = h1;
}

MurMurHash32

1
2
3
4
5
6
7
8
9
10
11
12
13
int32_t MTMurMurHash32(**const** **void** *bytes, **int** length)

{

  int32_t result = 0;

  MurmurHash3_x86_32(bytes, length, -137723950, &result);

   

  **return** result;

}

AesEncryptInplace

1
2
3
4
5
6
7
8
9
10
void MTAesEncryptInplace(NSMutableData *data, NSData *key, NSData *iv)
{
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv.bytes, iv.length);
    
    void *outData = malloc(data.length);
    MyAesIgeEncrypt(data.bytes, (int)data.length, outData, key.bytes, (int)key.length, aesIv);
    memcpy(data.mutableBytes, outData, data.length);
    free(outData);
}

AesEncryptInplaceAndModifyIv

1
2
3
4
5
6
7
8
9
10
11
12
void MTAesEncryptInplaceAndModifyIv(NSMutableData *data, NSData *key, NSMutableData *iv)
{
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv.bytes, iv.length);
    
    void *outData = malloc(data.length);
    MyAesIgeEncrypt(data.bytes, (int)data.length, outData, key.bytes, (int)key.length, aesIv);
    memcpy(data.mutableBytes, outData, data.length);
    free(outData);
    
    memcpy(iv.mutableBytes, aesIv, 16 * 2);
}

AesEncryptBytesInplaceAndModifyIv

1
2
3
4
5
6
7
8
9
10
11
void MTAesEncryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *key, void *iv) {
    unsigned char aesIv[32];
    memcpy(aesIv, iv, 32);
    
    void *outData = malloc(length);
    MyAesIgeEncrypt(data, (int)length, outData, key.bytes, (int)key.length, aesIv);
    memcpy(data, outData, length);
    free(outData);
    
    memcpy(iv, aesIv, 32);
}

AesEncryptRaw

1
2
3
4
5
6
void MTAesEncryptRaw(void const *data, void *outData, NSInteger length, void const *key, void const *iv) {
    unsigned char aesIv[32];
    memcpy(aesIv, iv, 32);
    
    MyAesIgeEncrypt(data, (int)length, outData, key, 32, aesIv);
}

AesDecryptRaw

1
2
3
4
5
6
void MTAesDecryptRaw(void const *data, void *outData, NSInteger length, void const *key, void const *iv) {
    unsigned char aesIv[32];
    memcpy(aesIv, iv, 32);
    
    MyAesIgeDecrypt(data, (int)length, outData, key, 32, aesIv);
}

AesDecryptInplaceAndModifyIv

1
2
3
4
5
6
7
8
9
10
11
12
void MTAesDecryptInplaceAndModifyIv(NSMutableData *data, NSData *key, NSMutableData *iv)
{
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv.bytes, iv.length);
    
    void *outData = malloc(data.length);
    MyAesIgeDecrypt(data.bytes, (int)data.length, outData, key.bytes, (int)key.length, aesIv);
    memcpy(data.mutableBytes, outData, data.length);
    free(outData);
    
    memcpy(iv.mutableBytes, aesIv, 16 * 2);
}

MTAesDecryptBytesInplaceAndModifyIv

1
2
3
4
5
6
7
8
9
10
11
void MTAesDecryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *key, void *iv) {
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv, 16 * 2);
    
    void *outData = malloc(length);
    MyAesIgeDecrypt(data, (int)length, outData, key.bytes, (int)key.length, aesIv);
    memcpy(data, outData, length);
    free(outData);
    
    memcpy(iv, aesIv, 16 * 2);
}

AesDecryptRawInplaceAndModifyIv

1
2
3
4
5
6
7
8
9
10
11
void MTAesDecryptRawInplaceAndModifyIv(void *data, NSInteger length, void *key, void *iv) {
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv, 16 * 2);
    
    void *outData = malloc(length);
    MyAesIgeDecrypt(data, (int)length, outData, key, 32, aesIv);
    memcpy(data, outData, length);
    free(outData);
    
    memcpy(iv, aesIv, 16 * 2);
}

AesEncrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSData *MTAesEncrypt(NSData *data, NSData *key, NSData *iv)
{
    if (key == nil || iv == nil)
    {
        if (MTLogEnabled()) {
            MTLog(@"***** MTAesEncrypt: empty key or iv");
        }
        return nil;
    }
    
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv.bytes, iv.length);
    
    void *outData = malloc(data.length);
    MyAesIgeEncrypt(data.bytes, (int)data.length, outData, key.bytes, (int)key.length, aesIv);
    return [[NSData alloc] initWithBytesNoCopy:outData length:data.length freeWhenDone:true];
}

AesDecrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NSData *MTAesDecrypt(NSData *data, NSData *key, NSData *iv)
{
    if (key == nil || iv == nil)
    {
        if (MTLogEnabled()) {
            MTLog(@"***** MTAesEncrypt: empty key or iv");
        }
        return nil;
    }
    
    NSMutableData *resultData = [[NSMutableData alloc] initWithLength:data.length];
    
    unsigned char aesIv[16 * 2];
    memcpy(aesIv, iv.bytes, iv.length);
    MyAesIgeDecrypt(data.bytes, (int)data.length, resultData.mutableBytes, key.bytes, (int)key.length, aesIv);
    
    return resultData;
}

RsaEncrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
NSData *MTRsaEncrypt(NSString *publicKey, NSData *data)
{
#if TARGET_OS_IOS
    NSMutableData *updatedData = [[NSMutableData alloc] initWithData:data];
    while (updatedData.length < 256) {
        uint8_t zero = 0;
        [updatedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&zero length:1];
    }
    return [MTRsa encryptData:updatedData publicKey:publicKey];
#else
    BIO *keyBio = BIO_new(BIO_s_mem());
    const char *keyData = [publicKey UTF8String];
    BIO_write(keyBio, keyData, (int)publicKey.length);
    RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
    BIO_free(keyBio);
    
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *a = BN_bin2bn(data.bytes, (int)data.length, NULL);
    BIGNUM *r = BN_new();
    
    BN_mod_exp(r, a, rsaKey->e, rsaKey->n, ctx);
    
    unsigned char *res = malloc((size_t)BN_num_bytes(r));
    int resLen = BN_bn2bin(r, res);
    
    BN_CTX_free(ctx);
    BN_free(a);
    BN_free(r);
    
    RSA_free(rsaKey);
    
    NSData *result = [[NSData alloc] initWithBytesNoCopy:res length:(NSUInteger)resLen freeWhenDone:true];
    
    return result;
#endif
}

Exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
NSData *MTExp(NSData *base, NSData *exp, NSData *modulus)
{
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnBase = BN_bin2bn(base.bytes, (int)base.length, NULL);
    BN_set_flags(bnBase, BN_FLG_CONSTTIME);

    BIGNUM *bnExp = BN_bin2bn(exp.bytes, (int)exp.length, NULL);
    BN_set_flags(bnExp, BN_FLG_CONSTTIME);
    
    BIGNUM *bnModulus = BN_bin2bn(modulus.bytes, (int)modulus.length, NULL);
    BN_set_flags(bnModulus, BN_FLG_CONSTTIME);
    
    BIGNUM *bnRes = BN_new();
    BN_set_flags(bnModulus, BN_FLG_CONSTTIME);
    
    BN_mod_exp(bnRes, bnBase, bnExp, bnModulus, ctx);
    
    unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
    int resLen = BN_bn2bin(bnRes, res);
    
    BN_CTX_free(ctx);
    BN_free(bnBase);
    BN_free(bnExp);
    BN_free(bnModulus);
    BN_free(bnRes);
    
    NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
    free(res);
    
    return result;
}

ModSub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NSData *MTModSub(NSData *a, NSData *b, NSData *modulus) {
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
    
    BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
    
    BIGNUM *bnModulus = BN_bin2bn(modulus.bytes, (int)modulus.length, NULL);
    
    BIGNUM *bnRes = BN_new();
    
    BN_mod_sub(bnRes, bnA, bnB, bnModulus, ctx);
    
    unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
    int resLen = BN_bn2bin(bnRes, res);
    
    BN_CTX_free(ctx);
    BN_free(bnA);
    BN_free(bnB);
    BN_free(bnModulus);
    BN_free(bnRes);
    
    NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
    free(res);
    
    return result;
}

ModMul

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NSData *MTModMul(NSData *a, NSData *b, NSData *modulus) {
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
    
    BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
    
    BIGNUM *bnModulus = BN_bin2bn(modulus.bytes, (int)modulus.length, NULL);
    
    BIGNUM *bnRes = BN_new();
    
    BN_mod_mul(bnRes, bnA, bnB, bnModulus, ctx);
    
    unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
    int resLen = BN_bn2bin(bnRes, res);
    
    BN_CTX_free(ctx);
    BN_free(bnA);
    BN_free(bnB);
    BN_free(bnModulus);
    BN_free(bnRes);
    
    NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
    free(res);
    
    return result;
}

Mul

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NSData *MTMul(NSData *a, NSData *b) {
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
    
    BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
    
    BIGNUM *bnRes = BN_new();
    
    BN_mul(bnRes, bnA, bnB, ctx);
    
    unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
    int resLen = BN_bn2bin(bnRes, res);
    
    BN_CTX_free(ctx);
    BN_free(bnA);
    BN_free(bnB);
    BN_free(bnRes);
    
    NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
    free(res);
    
    return result;
}

Add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NSData *MTAdd(NSData *a, NSData *b) {
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnA = BN_bin2bn(a.bytes, (int)a.length, NULL);
    
    BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
    
    BIGNUM *bnRes = BN_new();
    
    BN_add(bnRes, bnA, bnB);
    
    unsigned char *res = malloc((size_t)BN_num_bytes(bnRes));
    int resLen = BN_bn2bin(bnRes, res);
    
    BN_CTX_free(ctx);
    BN_free(bnA);
    BN_free(bnB);
    BN_free(bnRes);
    
    NSData *result = [[NSData alloc] initWithBytes:res length:(NSUInteger)resLen];
    free(res);
    
    return result;
}

IsZero

1
2
3
4
5
6
7
8
9
10
11
bool MTIsZero(NSData *value) {
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnValue = BN_bin2bn(value.bytes, (int)value.length, NULL);
    
    bool isZero = BN_is_zero(bnValue);
    
    BN_free(bnValue);
    BN_CTX_free(ctx);
    
    return isZero;
}

CheckIsSafeB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool MTCheckIsSafeB(NSData *b, NSData *p) {
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnB = BN_bin2bn(b.bytes, (int)b.length, NULL);
    BIGNUM *bnP = BN_bin2bn(p.bytes, (int)p.length, NULL);
    BIGNUM *bnZero = BN_new();
    BN_zero(bnZero);
    
    bool result = BN_cmp(bnB, bnZero) == 1 && BN_cmp(bnB, bnP) == -1;
    
    BN_free(bnB);
    BN_free(bnP);
    BN_free(bnZero);
    BN_CTX_free(ctx);
    
    return result;
}

GCD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static inline uint64_t mygcd(uint64_t a, uint64_t b)
{
    while (a != 0 && b != 0)
    {
        while ((b & 1) == 0)
        {
            b >>= 1;
        }
        
        while ((a & 1) == 0)
        {
            a >>= 1;
        }
        
        if (a > b)
            a -= b;
        else
            b -= a;
    }
    return b == 0 ? a : b;
}

Factorize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
bool MTFactorize(uint64_t what, uint64_t *resA, uint64_t *resB)
{
    int it = 0;
    uint64_t g = 0;
    for (int i = 0; i < 3 || it < 1000; i++)
    {
        int q = ((lrand48() & 15) + 17) % what;
        uint64_t x = (uint64_t)lrand48 () % (what - 1) + 1, y = x;
        int lim = 1 << (i + 18);
        int j;
        for (j = 1; j < lim; j++)
        {
            ++it;
            unsigned long long a = x, b = x, c = (unsigned long long)q;
            while (b)
            {
                if (b & 1)
                {
                    c += a;
                    if (c >= what)
                    {
                        c -= what;
                    }
                }
                a += a;
                if (a >= what)
                {
                    a -= what;
                }
                b >>= 1;
            }
            x = c;
            unsigned long long z = x < y ? what + x - y : x - y;
            g = mygcd(z, what);
            if (g != 1)
            {
                break;
            }
            if (!(j & (j - 1)))
            {
                y = x;
            }
        }
        
        if (g > 1 && g < what)
            break;
    }
    
    if (g > 1 && g < what)
    {
        uint64_t p1 = g;
        uint64_t p2 = what / g;
        if (p1 > p2)
        {
            uint64_t tmp = p1;
            p1 = p2;
            p2 = tmp;
        }
        
        if (resA != NULL)
            *resA = p1;
        if (resB != NULL)
            *resB = p2;
        
        return true;
    }
    else
    {
        if (MTLogEnabled()) {
            MTLog(@"Factorization failed for %lld", (long long int)what);
        }
        
        return false;
    }
}

CheckIsSafeG

1
2
3
4
bool MTCheckIsSafeG(unsigned int g)
{
    return g >= 2 && g <= 7;
}

HexStringFromData

1
2
3
4
5
6
7
8
9
static NSString *hexStringFromData(NSData *data)
{
    NSMutableString *string = [[NSMutableString alloc] initWithCapacity:data.length * 2];
    for (NSUInteger i = 0; i < data.length; i++)
    {
        [string appendFormat:@"%02x", ((uint8_t *)data.bytes)[i]];
    }
    return string;
}

CheckIsSafePrime

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain)
{
    NSString *primeKey = [[NSString alloc] initWithFormat:@"isPrimeSafe_%@", hexStringFromData(numberBytes)];
    
    NSNumber *nCachedResult = [keychain objectForKey:primeKey group:@"primes"];
    if (nCachedResult != nil)
        return [nCachedResult boolValue];
    
    if (numberBytes.length != 256)
    {
        [[NSUserDefaults standardUserDefaults] setObject:[[NSNumber alloc] initWithBool:false] forKey:primeKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
        return false;
    }
    
    if (!(((uint8_t *)numberBytes.bytes)[0] & (1 << 7)))
    {
        [[NSUserDefaults standardUserDefaults] setObject:[[NSNumber alloc] initWithBool:false] forKey:primeKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
        return false;
    }
    
    unsigned char goodPrime0[] = {
        0xc7, 0x1c, 0xae, 0xb9, 0xc6, 0xb1, 0xc9, 0x04, 0x8e, 0x6c, 0x52, 0x2f,
        0x70, 0xf1, 0x3f, 0x73, 0x98, 0x0d, 0x40, 0x23, 0x8e, 0x3e, 0x21, 0xc1,
        0x49, 0x34, 0xd0, 0x37, 0x56, 0x3d, 0x93, 0x0f, 0x48, 0x19, 0x8a, 0x0a,
        0xa7, 0xc1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xd2, 0x25, 0x30, 0xf4, 0xdb,
        0xfa, 0x33, 0x6f, 0x6e, 0x0a, 0xc9, 0x25, 0x13, 0x95, 0x43, 0xae, 0xd4,
        0x4c, 0xce, 0x7c, 0x37, 0x20, 0xfd, 0x51, 0xf6, 0x94, 0x58, 0x70, 0x5a,
        0xc6, 0x8c, 0xd4, 0xfe, 0x6b, 0x6b, 0x13, 0xab, 0xdc, 0x97, 0x46, 0x51,
        0x29, 0x69, 0x32, 0x84, 0x54, 0xf1, 0x8f, 0xaf, 0x8c, 0x59, 0x5f, 0x64,
        0x24, 0x77, 0xfe, 0x96, 0xbb, 0x2a, 0x94, 0x1d, 0x5b, 0xcd, 0x1d, 0x4a,
        0xc8, 0xcc, 0x49, 0x88, 0x07, 0x08, 0xfa, 0x9b, 0x37, 0x8e, 0x3c, 0x4f,
        0x3a, 0x90, 0x60, 0xbe, 0xe6, 0x7c, 0xf9, 0xa4, 0xa4, 0xa6, 0x95, 0x81,
        0x10, 0x51, 0x90, 0x7e, 0x16, 0x27, 0x53, 0xb5, 0x6b, 0x0f, 0x6b, 0x41,
        0x0d, 0xba, 0x74, 0xd8, 0xa8, 0x4b, 0x2a, 0x14, 0xb3, 0x14, 0x4e, 0x0e,
        0xf1, 0x28, 0x47, 0x54, 0xfd, 0x17, 0xed, 0x95, 0x0d, 0x59, 0x65, 0xb4,
        0xb9, 0xdd, 0x46, 0x58, 0x2d, 0xb1, 0x17, 0x8d, 0x16, 0x9c, 0x6b, 0xc4,
        0x65, 0xb0, 0xd6, 0xff, 0x9c, 0xa3, 0x92, 0x8f, 0xef, 0x5b, 0x9a, 0xe4,
        0xe4, 0x18, 0xfc, 0x15, 0xe8, 0x3e, 0xbe, 0xa0, 0xf8, 0x7f, 0xa9, 0xff,
        0x5e, 0xed, 0x70, 0x05, 0x0d, 0xed, 0x28, 0x49, 0xf4, 0x7b, 0xf9, 0x59,
        0xd9, 0x56, 0x85, 0x0c, 0xe9, 0x29, 0x85, 0x1f, 0x0d, 0x81, 0x15, 0xf6,
        0x35, 0xb1, 0x05, 0xee, 0x2e, 0x4e, 0x15, 0xd0, 0x4b, 0x24, 0x54, 0xbf,
        0x6f, 0x4f, 0xad, 0xf0, 0x34, 0xb1, 0x04, 0x03, 0x11, 0x9c, 0xd8, 0xe3,
        0xb9, 0x2f, 0xcc, 0x5b
    };
    
    if (memcmp(goodPrime0, numberBytes.bytes, 256) == 0)
        return true;
    
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnNumber = BN_bin2bn(numberBytes.bytes, (int)numberBytes.length, NULL);
    
    int result = BN_is_prime_ex(bnNumber, 30, ctx, NULL);
    
    if (result == 1)
    {
        BIGNUM *bnNumberMinus1 = BN_new();
        BN_sub(bnNumberMinus1, bnNumber, BN_value_one());
        BIGNUM *bnNumberMinus1DivBy2 = BN_new();
        BN_rshift1(bnNumberMinus1DivBy2, bnNumberMinus1);
        
        result = BN_is_prime_ex(bnNumberMinus1DivBy2, 30, ctx, NULL);
        
        BN_free(bnNumberMinus1);
        BN_free(bnNumberMinus1DivBy2);
    }
    
    BN_free(bnNumber);
    BN_CTX_free(ctx);
    
    [keychain setObject:@(result == 1) forKey:primeKey group:@"primes"];
    
    return result == 1;
}

CheckIsSafeGAOrB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool MTCheckIsSafeGAOrB(NSData *gAOrB, NSData *p)
{
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnNumber = BN_bin2bn(gAOrB.bytes, (int)gAOrB.length, NULL);
    BIGNUM *bnP = BN_bin2bn(p.bytes, (int)p.length, NULL);
    
    bool result = false;
    
    if (BN_cmp(bnNumber, BN_value_one()) == 1)
    {
        BIGNUM *pMinus1 = BN_new();
        BN_sub(pMinus1, bnP, BN_value_one());
        
        if (BN_cmp(bnNumber, pMinus1) == -1)
        {
            result = true;
        }
        
        BN_free(pMinus1);
    }
    
    BN_free(bnNumber);
    BN_free(bnP);
    BN_CTX_free(ctx);
    
    return result;
}

CheckMod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain)
{
    NSString *modKey = [[NSString alloc] initWithFormat:@"isPrimeModSafe_%@_%d", hexStringFromData(numberBytes), g];
    NSNumber *nCachedResult = [keychain objectForKey:modKey group:@"primes"];
    if (nCachedResult != nil)
        return [nCachedResult boolValue];
    
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bnNumber = BN_bin2bn(numberBytes.bytes, (int)numberBytes.length, NULL);
    
    bool result = false;
    
    switch (g)
    {
        case 2:
        {
            BN_ULONG modResult = BN_mod_word(bnNumber, 8);
            result = modResult == 7;
            
            break;
        }
        case 3:
        {
            BN_ULONG modResult = BN_mod_word(bnNumber, 3);
            result = modResult == 2;
            
            break;
        }
        case 4:
        {
            result = true;
            
            break;
        }
        case 5:
        {
            BN_ULONG modResult = BN_mod_word(bnNumber, 5);
            result = modResult == 1 || modResult == 4;
            
            break;
        }
        case 6:
        {
            BN_ULONG modResult = BN_mod_word(bnNumber, 24);
            result = modResult == 19 || modResult == 23;
            
            break;
        }
        case 7:
        {
            BN_ULONG modResult = BN_mod_word(bnNumber, 7);
            result = modResult == 3 || modResult == 5 || modResult == 6;
            
            break;
        }
        default:
            break;
    }
    
    BN_free(bnNumber);
    BN_CTX_free(ctx);
    
    [keychain setObject:@(result) forKey:modKey group:@"primes"];
    
    return result;
}

AesCtrDecrypt

1
2
3
4
5
6
NSData *MTAesCtrDecrypt(NSData *data, NSData *key, NSData *iv) {
    MTAesCtr *ctr = [[MTAesCtr alloc] initWithKey:key.bytes keyLength:32 iv:iv.bytes decrypt:true];
    NSMutableData *outData = [[NSMutableData alloc] initWithLength:data.length];
    [ctr encryptIn:data.bytes out:outData.mutableBytes len:data.length];
    return outData;
}

RsaEncryptPKCS1OAEP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
NSData *MTRsaEncryptPKCS1OAEP(NSString *key, NSData *data) {
    BIO *keyBio = BIO_new(BIO_s_mem());
    
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    BIO_write(keyBio, keyData.bytes, (int)keyData.length);
    RSA *rsaKey = PEM_read_bio_RSA_PUBKEY(keyBio, NULL, NULL, NULL);
    if (rsaKey == nil) {
        BIO_free(keyBio);
        return nil;
    }
    
    NSMutableData *outData = [[NSMutableData alloc] initWithLength:data.length + 2048];
    
    int encryptedLength = RSA_public_encrypt((int)data.length, data.bytes, outData.mutableBytes, rsaKey, RSA_PKCS1_OAEP_PADDING);
    RSA_free(rsaKey);
    BIO_free(keyBio);
    
    if (encryptedLength < 0) {
        return nil;
    }
    
    assert(encryptedLength <= outData.length);
    [outData setLength:encryptedLength];
    
    return outData;
}

decrypt_TL_data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
static NSData *decrypt_TL_data(unsigned char buffer[256]) {
    NSString *keyString = @"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAufikXLCtGLztM/mJLXARHGqXafsxErw6pGig/PhsLvMzDTO1GOJf\n"
"On2kHU6Cvp0y4i7eCVjl+Ke1uBy4QSnplt6QQ3z9LZ9i67h5Srm0uXIfeB6nnmRq\n"
"iT7oDlo02nEQ2p3BiwglWO2nlWvbXTcHP+fk31zXFLKATHxHautA9yyVvD7EatcU\n"
"vqfem1oqsXBQ3o9aRGhqaRI4kl2bc46LmJKMhfpLtp7qkIiVeAwQhbWBV1DFyh0H\n"
"FOqPf+GwxwIAcPoH2jwLiClAHrRxkef+7t0O1CpAEZgmMk3Uy62w74Odzmyv7R1F\n"
"TrV6mebLbSCp5r/k9DgVTjXfWgZ3dyTZtwIDAQAB\n"
"-----END RSA PUBLIC KEY-----";
    NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
    
    BIO *keyBio = BIO_new(BIO_s_mem());
    BIO_write(keyBio, keyData.bytes, (int)keyData.length);
    
    RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL);
    if (rsaKey == nil) {
        return nil;
    }
    
    BIGNUM x, y;
    BN_CTX *bnContext = BN_CTX_new();
    uint8_t *bytes = buffer;
    BN_init(&x);
    BN_init(&y);
    BN_bin2bn(bytes, 256, &x);
    
    NSData *result = nil;
    if (BN_mod_exp(&y, &x, rsaKey->e, rsaKey->n, bnContext) == 1) {
        unsigned l = 256 - BN_num_bytes(&y);
        memset(bytes, 0, l);
        if (BN_bn2bin(&y, bytes + l) == 256 - l) {
            AES_KEY aeskey;
            unsigned char iv[16];
            memcpy(iv, bytes + 16, 16);
            AES_set_decrypt_key(bytes, 256, &aeskey);
            AES_cbc_encrypt(bytes + 32, bytes + 32, 256 - 32, &aeskey, iv, AES_DECRYPT);
            
            EVP_MD_CTX ctx;
            unsigned char sha256_out[32];
            unsigned olen = 0;
            EVP_MD_CTX_init(&ctx);
            EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL);
            EVP_DigestUpdate(&ctx, bytes + 32, 256 - 32 - 16);
            EVP_DigestFinal_ex(&ctx, sha256_out, &olen);
            EVP_MD_CTX_cleanup(&ctx);
            if (olen == 32) {
                if (memcmp(bytes + 256 - 16, sha256_out, 16) == 0) {
                    unsigned data_len = *(unsigned *) (bytes + 32);
                    if (data_len && data_len <= 256 - 32 - 16 && !(data_len & 3)) {
                        result = [NSData dataWithBytes:buffer + 32 + 4 length:data_len];
                    } else {
                        if (MTLogEnabled()) {
                            MTLog(@"TL data length field invalid - %d", data_len);
                        }
                    }
                } else {
                    if (MTLogEnabled()) {
                        MTLog(@"RSA signature check FAILED (SHA256 mismatch)");
                    }
                }
            }
        }
    }
    BN_free(&x);
    BN_free(&y);
    RSA_free(rsaKey);
    BIO_free(keyBio);
    BN_CTX_free(bnContext);
    return result;
}

IPDataDecode

class MTBackupDatacenterAddress

@property (nonatomic, readonly) int32_t datacenterId;
@property (nonatomic, strong, readonly) NSString *ip;
@property (nonatomic, readonly) int32_t port;
@property (nonatomic, strong, readonly) NSData *secret;

class MTBackupDatacenterData

@property (nonatomic, readonly) int32_t timestamp;
@property (nonatomic, readonly) int32_t expirationDate;
@property (nonatomic, strong, readonly) NSArray<MTBackupDatacenterAddress *> *addressList;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
MTBackupDatacenterData *MTIPDataDecode(NSData *data, NSString *phoneNumber) {
    unsigned char buffer[256];
    memcpy(buffer, data.bytes, 256);
    NSData *result = decrypt_TL_data(buffer);
    
    if (result != nil) {
        MTBufferReader *reader = [[MTBufferReader alloc] initWithData:result];
        int32_t signature = 0;
        if (![reader readInt32:&signature]) {
            return nil;
        }
        if (signature == 0xd997c3c5) {
            int32_t timestamp = 0;
            int32_t expirationDate = 0;
            int32_t datacenterId = 0;
            if (![reader readInt32:&timestamp]) {
                return nil;
            }
            if (![reader readInt32:&expirationDate]) {
                return nil;
            }
            if (![reader readInt32:&datacenterId]) {
                return nil;
            }
            int32_t vectorSignature = 0;
            if (![reader readInt32:&vectorSignature]) {
                return nil;
            }
            if (vectorSignature != 0x1cb5c415) {
                return nil;
            }
            
            NSMutableArray<MTBackupDatacenterAddress *> *addressList = [[NSMutableArray alloc] init];
            int32_t count = 0;
            if (![reader readInt32:&count]) {
                return nil;
            }
            
            for (int i = 0; i < count; i++) {
                int32_t ip = 0;
                int32_t port = 0;
                if (![reader readInt32:&ip]) {
                    return nil;
                }
                if (![reader readInt32:&port]) {
                    return nil;
                }
                [addressList addObject:[[MTBackupDatacenterAddress alloc] initWithDatacenterId:datacenterId ip:[NSString stringWithFormat:@"%d.%d.%d.%d", (int)((ip >> 24) & 0xFF), (int)((ip >> 16) & 0xFF), (int)((ip >> 8) & 0xFF), (int)((ip >> 0) & 0xFF)] port:port secret:nil]];
            }
            
            return [[MTBackupDatacenterData alloc] initWithTimestamp:timestamp expirationDate:expirationDate addressList:addressList];
        } else if (signature == 0x5a592a6c) {
            int32_t timestamp = 0;
            int32_t expirationDate = 0;
            if (![reader readInt32:&timestamp]) {
                return nil;
            }
            if (![reader readInt32:&expirationDate]) {
                return nil;
            }
            
            NSMutableArray<MTBackupDatacenterAddress *> *addressList = [[NSMutableArray alloc] init];
            int32_t count = 0;
            if (![reader readInt32:&count]) {
                return nil;
            }
            
            for (int32_t i = 0; i < count; i++) {
                int32_t signature = 0;
                if (![reader readInt32:&signature]) {
                    return nil;
                }
                if (signature != 0x4679b65f) {
                    return nil;
                }
                NSString *phonePrefixRules = nil;
                if (![reader readTLString:&phonePrefixRules]) {
                    return nil;
                }
                int32_t datacenterId = 0;
                if (![reader readInt32:&datacenterId]) {
                    return nil;
                }
                
                int32_t ipCount = 0;
                if (![reader readInt32:&ipCount]) {
                    return nil;
                }
                
                NSMutableArray<MTBackupDatacenterAddress *> *ruleAddressList = [[NSMutableArray alloc] init];
                
                for (int j = 0; j < ipCount; j++) {
                    int32_t signature = 0;
                    if (![reader readInt32:&signature]) {
                        return nil;
                    }
                    if (signature == 0xd433ad73) {
                        int32_t ip = 0;
                        int32_t port = 0;
                        if (![reader readInt32:&ip]) {
                            return nil;
                        }
                        if (![reader readInt32:&port]) {
                            return nil;
                        }
                        [ruleAddressList addObject:[[MTBackupDatacenterAddress alloc] initWithDatacenterId:datacenterId ip:[NSString stringWithFormat:@"%d.%d.%d.%d", (int)((ip >> 24) & 0xFF), (int)((ip >> 16) & 0xFF), (int)((ip >> 8) & 0xFF), (int)((ip >> 0) & 0xFF)] port:port secret:nil]];
                    } else if (signature == 0x37982646) {
                        int32_t ip = 0;
                        int32_t port = 0;
                        if (![reader readInt32:&ip]) {
                            return nil;
                        }
                        if (![reader readInt32:&port]) {
                            return nil;
                        }
                        NSData *secret = nil;
                        if (![reader readTLBytes:&secret]) {
                            return nil;
                        }
                        [ruleAddressList addObject:[[MTBackupDatacenterAddress alloc] initWithDatacenterId:datacenterId ip:[NSString stringWithFormat:@"%d.%d.%d.%d", (int)((ip >> 24) & 0xFF), (int)((ip >> 16) & 0xFF), (int)((ip >> 8) & 0xFF), (int)((ip >> 0) & 0xFF)] port:port secret:secret]];
                    } else {
                        return nil;
                    }
                }
                
                bool includeIp = true;
                for (NSString *rule in [phonePrefixRules componentsSeparatedByString:@","]) {
                    if (rule.length == 0) {
                        includeIp = true;
                    } else if ([rule characterAtIndex:0] == '+' && [phoneNumber hasPrefix:[rule substringFromIndex:1]]) {
                        includeIp = true;
                    } else if ([rule characterAtIndex:0] == '-' && [phoneNumber hasPrefix:[rule substringFromIndex:1]]) {
                        includeIp = false;
                    } else {
                        includeIp = false;
                    }
                }
                if (includeIp) {
                    [addressList addObjectsFromArray:ruleAddressList];
                }
            }
            
            return [[MTBackupDatacenterData alloc] initWithTimestamp:timestamp expirationDate:expirationDate addressList:addressList];
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

PBKDF2

1
2
3
4
5
6
7
8
9
10
11
12
NSData * _Nullable MTPBKDF2(NSData * _Nonnull data, NSData * _Nonnull salt, int rounds) {
    if (rounds < 2) {
        return nil;
    }
    const size_t hashLength = 64;
    NSMutableData *result = [[NSMutableData alloc] initWithLength:hashLength];
    CCStatus status = CCKeyDerivationPBKDF(kCCPBKDF2, data.bytes, data.length, salt.bytes, salt.length, kCCPRFHmacAlgSHA512, rounds, result.mutableBytes, hashLength);
    if (status != kCCSuccess) {
        return nil;
    }
    return result;
}
This post is licensed under CC BY 4.0 by the author.