From 046ff0e8141bf474c0e2e850079bef354e9ec0ae Mon Sep 17 00:00:00 2001 From: fatshotty <238659+fatshotty@users.noreply.github.com> Date: Tue, 30 Jun 2026 18:03:22 +0200 Subject: [PATCH 1/2] perf(crypto): add ARMv8 AES-IGE decrypt fast path --- crypto/cipher_decrypt.go | 7 ++- crypto/hwaes_arm.go | 32 +++++++++++++ crypto/hwaes_arm_test.go | 58 ++++++++++++++++++++++ crypto/hwaes_ige_arm.c | 101 +++++++++++++++++++++++++++++++++++++++ crypto/hwaes_stub.go | 5 ++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 crypto/hwaes_arm.go create mode 100644 crypto/hwaes_arm_test.go create mode 100644 crypto/hwaes_ige_arm.c create mode 100644 crypto/hwaes_stub.go diff --git a/crypto/cipher_decrypt.go b/crypto/cipher_decrypt.go index f8e1db2a95..93fdff2c6e 100644 --- a/crypto/cipher_decrypt.go +++ b/crypto/cipher_decrypt.go @@ -72,11 +72,16 @@ func (c Cipher) decryptMessage(k AuthKey, encrypted *EncryptedMessage) ([]byte, } key, iv := Keys(k.Value, encrypted.MsgKey, c.encryptSide.DecryptSide()) + plaintext := make([]byte, len(encrypted.EncryptedData)) + + if hwIGEDecrypt(key[:], iv[:], plaintext, encrypted.EncryptedData) { + return plaintext, nil + } + cipher, err := aes.NewCipher(key[:]) if err != nil { return nil, err } - plaintext := make([]byte, len(encrypted.EncryptedData)) ige.DecryptBlocks(cipher, iv[:], plaintext, encrypted.EncryptedData) return plaintext, nil diff --git a/crypto/hwaes_arm.go b/crypto/hwaes_arm.go new file mode 100644 index 0000000000..f287d8f7d7 --- /dev/null +++ b/crypto/hwaes_arm.go @@ -0,0 +1,32 @@ +//go:build arm && cgo && (linux || android) + +package crypto + +/* +#cgo CFLAGS: -O3 -march=armv8-a+crypto -mfpu=crypto-neon-fp-armv8 +#include +int aes256_ige_decrypt(const uint8_t *key, const uint8_t *iv, + uint8_t *dst, const uint8_t *src, int len); +*/ +import "C" +import "unsafe" + +// hwIGEDecrypt decrypts AES-256-IGE using the ARMv8 Cryptography Extension when +// the running CPU exposes it. It returns false when the hardware path is not +// available or the input is invalid, allowing the caller to use the Go fallback. +func hwIGEDecrypt(key, iv, dst, src []byte) bool { + if len(src) == 0 || len(src)%16 != 0 { + return false + } + if len(key) < 32 || len(iv) < 32 || len(dst) < len(src) { + return false + } + rc := C.aes256_ige_decrypt( + (*C.uint8_t)(unsafe.Pointer(&key[0])), + (*C.uint8_t)(unsafe.Pointer(&iv[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + (*C.uint8_t)(unsafe.Pointer(&src[0])), + C.int(len(src)), + ) + return rc == 0 +} diff --git a/crypto/hwaes_arm_test.go b/crypto/hwaes_arm_test.go new file mode 100644 index 0000000000..dd3b13ac2a --- /dev/null +++ b/crypto/hwaes_arm_test.go @@ -0,0 +1,58 @@ +//go:build arm && cgo && (linux || android) + +package crypto + +import ( + "crypto/aes" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/gotd/ige" +) + +func TestHWIGEDecryptMatchesSoftware(t *testing.T) { + key, iv, src := newHWIGETestVector(16 * 16) + + block, err := aes.NewCipher(key) + require.NoError(t, err) + + want := make([]byte, len(src)) + ige.DecryptBlocks(block, iv, want, src) + + got := make([]byte, len(src)) + if !hwIGEDecrypt(key, iv, got, src) { + t.Skip("ARMv8 AES extension is not available") + } + require.Equal(t, want, got) +} + +func BenchmarkHWIGEDecrypt(b *testing.B) { + key, iv, src := newHWIGETestVector(1024 * 1024) + dst := make([]byte, len(src)) + if !hwIGEDecrypt(key, iv, dst, src) { + b.Skip("ARMv8 AES extension is not available") + } + + b.SetBytes(int64(len(src))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + hwIGEDecrypt(key, iv, dst, src) + } +} + +func newHWIGETestVector(size int) ([]byte, []byte, []byte) { + key := make([]byte, 32) + iv := make([]byte, 32) + src := make([]byte, size) + for i := range key { + key[i] = byte(i*3 + 1) + } + for i := range iv { + iv[i] = byte(i*5 + 2) + } + for i := range src { + src[i] = byte(i*7 + 3) + } + return key, iv, src +} diff --git a/crypto/hwaes_ige_arm.c b/crypto/hwaes_ige_arm.c new file mode 100644 index 0000000000..3fe867c127 --- /dev/null +++ b/crypto/hwaes_ige_arm.c @@ -0,0 +1,101 @@ +//go:build arm && cgo && (linux || android) + +// AES-256 IGE decrypt using ARMv8 Cryptography Extension on AArch32. +#include +#include +#include +#include + +#ifndef AT_HWCAP2 +#define AT_HWCAP2 26 +#endif +#ifndef HWCAP2_AES +#define HWCAP2_AES (1 << 0) +#endif + +static const uint8_t sbox[256] = { +0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, +0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, +0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, +0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, +0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, +0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, +0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, +0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, +0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, +0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, +0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, +0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, +0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, +0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, +0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, +0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16 +}; + +static int aes_hw_available(void) { + unsigned long hw2 = getauxval(AT_HWCAP2); + return (hw2 & HWCAP2_AES) ? 1 : 0; +} + +// AES-256 key expansion to 15 encryption round keys. +static void expand_key(const uint8_t *key, uint8_t ek[15][16]) { + uint8_t w[60][4]; + memcpy(w, key, 32); + uint8_t rcon = 1; + for (int i = 8; i < 60; i++) { + uint8_t t[4]; + memcpy(t, w[i - 1], 4); + if (i % 8 == 0) { + uint8_t tmp = t[0]; + t[0] = t[1]; + t[1] = t[2]; + t[2] = t[3]; + t[3] = tmp; + for (int j = 0; j < 4; j++) t[j] = sbox[t[j]]; + t[0] ^= rcon; + rcon = (uint8_t)((rcon << 1) ^ ((rcon >> 7) * 0x1b)); + } else if (i % 8 == 4) { + for (int j = 0; j < 4; j++) t[j] = sbox[t[j]]; + } + for (int j = 0; j < 4; j++) w[i][j] = w[i - 8][j] ^ t[j]; + } + for (int r = 0; r < 15; r++) memcpy(ek[r], w[4 * r], 16); +} + +// IGE decrypt, gotd convention: c=iv[:16] (previous ciphertext), +// m=iv[16:] (previous plaintext). len must be a multiple of 16. +// Returns 0 on success, -1 if hardware AES is unavailable, -2 on bad length. +int aes256_ige_decrypt(const uint8_t *key, const uint8_t *iv, + uint8_t *dst, const uint8_t *src, int len) { + if (!aes_hw_available()) return -1; + if (len % 16 != 0) return -2; + + uint8_t ek[15][16]; + expand_key(key, ek); + + uint8x16_t drk[15]; + drk[0] = vld1q_u8(ek[14]); + for (int j = 1; j < 14; j++) drk[j] = vaesimcq_u8(vld1q_u8(ek[14 - j])); + drk[14] = vld1q_u8(ek[0]); + + uint8x16_t c = vld1q_u8(iv); + uint8x16_t m = vld1q_u8(iv + 16); + + for (int o = 0; o < len; o += 16) { + uint8x16_t ct = vld1q_u8(src + o); + uint8x16_t s = veorq_u8(ct, m); + s = vaesdq_u8(s, drk[0]); + s = vaesimcq_u8(s); + for (int i = 1; i < 13; i++) { + s = vaesdq_u8(s, drk[i]); + s = vaesimcq_u8(s); + } + s = vaesdq_u8(s, drk[13]); + s = veorq_u8(s, drk[14]); + s = veorq_u8(s, c); + vst1q_u8(dst + o, s); + m = s; + c = ct; + } + return 0; +} diff --git a/crypto/hwaes_stub.go b/crypto/hwaes_stub.go new file mode 100644 index 0000000000..fb1d280567 --- /dev/null +++ b/crypto/hwaes_stub.go @@ -0,0 +1,5 @@ +//go:build !arm || !cgo || (!linux && !android) + +package crypto + +func hwIGEDecrypt(key, iv, dst, src []byte) bool { return false } From 96a39f8f2ef15b055f155b81d17eb70d8fb9340c Mon Sep 17 00:00:00 2001 From: fatshotty <238659+fatshotty@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:23:53 +0200 Subject: [PATCH 2/2] refactor(crypto): use ige AES-256 decrypt helper --- crypto/cipher_decrypt.go | 13 +---- crypto/hwaes_arm.go | 32 ------------- crypto/hwaes_arm_test.go | 58 ---------------------- crypto/hwaes_ige_arm.c | 101 --------------------------------------- crypto/hwaes_stub.go | 5 -- go.mod | 2 +- go.sum | 5 +- 7 files changed, 4 insertions(+), 212 deletions(-) delete mode 100644 crypto/hwaes_arm.go delete mode 100644 crypto/hwaes_arm_test.go delete mode 100644 crypto/hwaes_ige_arm.c delete mode 100644 crypto/hwaes_stub.go diff --git a/crypto/cipher_decrypt.go b/crypto/cipher_decrypt.go index 93fdff2c6e..b64cdb5a31 100644 --- a/crypto/cipher_decrypt.go +++ b/crypto/cipher_decrypt.go @@ -1,8 +1,6 @@ package crypto import ( - "crypto/aes" - "github.com/go-faster/errors" "github.com/gotd/ige" @@ -73,16 +71,7 @@ func (c Cipher) decryptMessage(k AuthKey, encrypted *EncryptedMessage) ([]byte, key, iv := Keys(k.Value, encrypted.MsgKey, c.encryptSide.DecryptSide()) plaintext := make([]byte, len(encrypted.EncryptedData)) - - if hwIGEDecrypt(key[:], iv[:], plaintext, encrypted.EncryptedData) { - return plaintext, nil - } - - cipher, err := aes.NewCipher(key[:]) - if err != nil { - return nil, err - } - ige.DecryptBlocks(cipher, iv[:], plaintext, encrypted.EncryptedData) + ige.DecryptAES256Blocks(key[:], iv[:], plaintext, encrypted.EncryptedData) return plaintext, nil } diff --git a/crypto/hwaes_arm.go b/crypto/hwaes_arm.go deleted file mode 100644 index f287d8f7d7..0000000000 --- a/crypto/hwaes_arm.go +++ /dev/null @@ -1,32 +0,0 @@ -//go:build arm && cgo && (linux || android) - -package crypto - -/* -#cgo CFLAGS: -O3 -march=armv8-a+crypto -mfpu=crypto-neon-fp-armv8 -#include -int aes256_ige_decrypt(const uint8_t *key, const uint8_t *iv, - uint8_t *dst, const uint8_t *src, int len); -*/ -import "C" -import "unsafe" - -// hwIGEDecrypt decrypts AES-256-IGE using the ARMv8 Cryptography Extension when -// the running CPU exposes it. It returns false when the hardware path is not -// available or the input is invalid, allowing the caller to use the Go fallback. -func hwIGEDecrypt(key, iv, dst, src []byte) bool { - if len(src) == 0 || len(src)%16 != 0 { - return false - } - if len(key) < 32 || len(iv) < 32 || len(dst) < len(src) { - return false - } - rc := C.aes256_ige_decrypt( - (*C.uint8_t)(unsafe.Pointer(&key[0])), - (*C.uint8_t)(unsafe.Pointer(&iv[0])), - (*C.uint8_t)(unsafe.Pointer(&dst[0])), - (*C.uint8_t)(unsafe.Pointer(&src[0])), - C.int(len(src)), - ) - return rc == 0 -} diff --git a/crypto/hwaes_arm_test.go b/crypto/hwaes_arm_test.go deleted file mode 100644 index dd3b13ac2a..0000000000 --- a/crypto/hwaes_arm_test.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build arm && cgo && (linux || android) - -package crypto - -import ( - "crypto/aes" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/gotd/ige" -) - -func TestHWIGEDecryptMatchesSoftware(t *testing.T) { - key, iv, src := newHWIGETestVector(16 * 16) - - block, err := aes.NewCipher(key) - require.NoError(t, err) - - want := make([]byte, len(src)) - ige.DecryptBlocks(block, iv, want, src) - - got := make([]byte, len(src)) - if !hwIGEDecrypt(key, iv, got, src) { - t.Skip("ARMv8 AES extension is not available") - } - require.Equal(t, want, got) -} - -func BenchmarkHWIGEDecrypt(b *testing.B) { - key, iv, src := newHWIGETestVector(1024 * 1024) - dst := make([]byte, len(src)) - if !hwIGEDecrypt(key, iv, dst, src) { - b.Skip("ARMv8 AES extension is not available") - } - - b.SetBytes(int64(len(src))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - hwIGEDecrypt(key, iv, dst, src) - } -} - -func newHWIGETestVector(size int) ([]byte, []byte, []byte) { - key := make([]byte, 32) - iv := make([]byte, 32) - src := make([]byte, size) - for i := range key { - key[i] = byte(i*3 + 1) - } - for i := range iv { - iv[i] = byte(i*5 + 2) - } - for i := range src { - src[i] = byte(i*7 + 3) - } - return key, iv, src -} diff --git a/crypto/hwaes_ige_arm.c b/crypto/hwaes_ige_arm.c deleted file mode 100644 index 3fe867c127..0000000000 --- a/crypto/hwaes_ige_arm.c +++ /dev/null @@ -1,101 +0,0 @@ -//go:build arm && cgo && (linux || android) - -// AES-256 IGE decrypt using ARMv8 Cryptography Extension on AArch32. -#include -#include -#include -#include - -#ifndef AT_HWCAP2 -#define AT_HWCAP2 26 -#endif -#ifndef HWCAP2_AES -#define HWCAP2_AES (1 << 0) -#endif - -static const uint8_t sbox[256] = { -0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, -0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, -0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, -0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, -0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, -0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, -0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, -0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, -0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, -0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, -0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, -0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, -0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, -0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, -0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, -0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16 -}; - -static int aes_hw_available(void) { - unsigned long hw2 = getauxval(AT_HWCAP2); - return (hw2 & HWCAP2_AES) ? 1 : 0; -} - -// AES-256 key expansion to 15 encryption round keys. -static void expand_key(const uint8_t *key, uint8_t ek[15][16]) { - uint8_t w[60][4]; - memcpy(w, key, 32); - uint8_t rcon = 1; - for (int i = 8; i < 60; i++) { - uint8_t t[4]; - memcpy(t, w[i - 1], 4); - if (i % 8 == 0) { - uint8_t tmp = t[0]; - t[0] = t[1]; - t[1] = t[2]; - t[2] = t[3]; - t[3] = tmp; - for (int j = 0; j < 4; j++) t[j] = sbox[t[j]]; - t[0] ^= rcon; - rcon = (uint8_t)((rcon << 1) ^ ((rcon >> 7) * 0x1b)); - } else if (i % 8 == 4) { - for (int j = 0; j < 4; j++) t[j] = sbox[t[j]]; - } - for (int j = 0; j < 4; j++) w[i][j] = w[i - 8][j] ^ t[j]; - } - for (int r = 0; r < 15; r++) memcpy(ek[r], w[4 * r], 16); -} - -// IGE decrypt, gotd convention: c=iv[:16] (previous ciphertext), -// m=iv[16:] (previous plaintext). len must be a multiple of 16. -// Returns 0 on success, -1 if hardware AES is unavailable, -2 on bad length. -int aes256_ige_decrypt(const uint8_t *key, const uint8_t *iv, - uint8_t *dst, const uint8_t *src, int len) { - if (!aes_hw_available()) return -1; - if (len % 16 != 0) return -2; - - uint8_t ek[15][16]; - expand_key(key, ek); - - uint8x16_t drk[15]; - drk[0] = vld1q_u8(ek[14]); - for (int j = 1; j < 14; j++) drk[j] = vaesimcq_u8(vld1q_u8(ek[14 - j])); - drk[14] = vld1q_u8(ek[0]); - - uint8x16_t c = vld1q_u8(iv); - uint8x16_t m = vld1q_u8(iv + 16); - - for (int o = 0; o < len; o += 16) { - uint8x16_t ct = vld1q_u8(src + o); - uint8x16_t s = veorq_u8(ct, m); - s = vaesdq_u8(s, drk[0]); - s = vaesimcq_u8(s); - for (int i = 1; i < 13; i++) { - s = vaesdq_u8(s, drk[i]); - s = vaesimcq_u8(s); - } - s = vaesdq_u8(s, drk[13]); - s = veorq_u8(s, drk[14]); - s = veorq_u8(s, c); - vst1q_u8(dst + o, s); - m = s; - c = ct; - } - return 0; -} diff --git a/crypto/hwaes_stub.go b/crypto/hwaes_stub.go deleted file mode 100644 index fb1d280567..0000000000 --- a/crypto/hwaes_stub.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build !arm || !cgo || (!linux && !android) - -package crypto - -func hwIGEDecrypt(key, iv, dst, src []byte) bool { return false } diff --git a/go.mod b/go.mod index 01143561ca..175248ed64 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/go-openapi/inflect v0.21.6 github.com/google/uuid v1.6.0 github.com/gotd/getdoc v0.53.0 - github.com/gotd/ige v0.2.2 + github.com/gotd/ige v0.3.0 github.com/gotd/log v0.1.0 github.com/gotd/log/logzap v0.1.1 github.com/gotd/neo v0.1.5 diff --git a/go.sum b/go.sum index 845c9bf98e..77fddd88b6 100644 --- a/go.sum +++ b/go.sum @@ -33,7 +33,6 @@ github.com/go-faster/jx v1.2.0 h1:T2YHJPrFaYu21fJtUxC9GzmluKu8rVIFDwwGBKTDseI= github.com/go-faster/jx v1.2.0/go.mod h1:UWLOVDmMG597a5tBFPLIWJdUxz5/2emOpfsj9Neg0PE= github.com/go-faster/sdk v0.34.0 h1:fDqrfjdNAZ/gu1yaAxGgEatguVjHhO0bfoQrDnp7gM4= github.com/go-faster/sdk v0.34.0/go.mod h1:B9UTh3g1aku6MuNJlO5v0AgJX3W6tzQuzGZEkK3aBmM= -github.com/go-faster/xor v0.3.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ= github.com/go-faster/xor v1.0.0 h1:2o8vTOgErSGHP3/7XwA5ib1FTtUsNtwCoLLBjl31X38= github.com/go-faster/xor v1.0.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ= github.com/go-faster/yaml v0.4.6 h1:lOK/EhI04gCpPgPhgt0bChS6bvw7G3WwI8xxVe0sw9I= @@ -52,8 +51,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gotd/getdoc v0.53.0 h1:jO3QEFPM8oz9PAcMMXAjB2sVqSOeYUrUNcPvGyAGG/4= github.com/gotd/getdoc v0.53.0/go.mod h1:dGzxEXIknZKAjGTbm9Axgx5LJqR/FBvHkoaPfnstmNw= -github.com/gotd/ige v0.2.2 h1:XQ9dJZwBfDnOGSTxKXBGP4gMud3Qku2ekScRjDWWfEk= -github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0= +github.com/gotd/ige v0.3.0 h1:4f6LEHWsVDLBG0bT9wWG2/9TZb5aWm265G8ZlTXmRRU= +github.com/gotd/ige v0.3.0/go.mod h1:FE9bTaQtvfArizAcZuI4sS6gXaEUBmixdUufVHoCKac= github.com/gotd/log v0.1.0 h1:4LJUEvafD1xtBwx2QkrlzFnRgbYXTlWqJPDi8BvrLbU= github.com/gotd/log v0.1.0/go.mod h1:5ilhdu1Ux0QvDY/FF3Ojfw24Ws3SlCtyLwOpXy8KYXs= github.com/gotd/log/logzap v0.1.1 h1:O6l7d8HUbODe+UMcrM47eXYDwdJ6RNmpQejLjrlcEIQ=