I'm trying to implement the MTProto transport obfuscation capability described here.
I've followed instructions letter per letter, to no avail: data is sent, e.g. for ReqPqMulti
, but upon reading the response, e.g. ResPQ
, the input stream blocks forever, which means no data is received.
The initialization payload and the AES Cipher are created as follow. See also comments.
fun buildInitPayload(header: Byte?): Pair<AES, ByteArray> {
// Add the padded transport header to the reserved words
val reservedWords = if (header != null) {
reservedWords + byteArrayOf(header, header, header, header)
} else {
reservedWords
}
val primaryPayload = ByteArray(64)
val secureRandom = SecureRandom()
// Generate 64-byte random initialization payload
do {
secureRandom.nextBytes(primaryPayload)
} while (!isInitPayloadValid(reservedWords, primaryPayload))
if (header != null) {
primaryPayload[56] = header
primaryPayload[57] = header
primaryPayload[58] = header
primaryPayload[59] = header
}
// Extract two keys from both initialization payloads, using bytes at offsets 8-40:
// the key extracted from the primary payload is used as encryption key
val encryptionKey = primaryPayload.copyOfRange(8, 40)
// Generate a secondary initialization payload by reversing the primary payload
val secondaryPayload = ByteArray(48)
for (i in 0..47) secondaryPayload[i] = primaryPayload[55 - i]
// The key extracted from the secondary payload is used as decryption key
val decryptionKey = secondaryPayload.copyOf(32)
// Extract two IVs from both initialization payloads, using bytes at offsets 40-56:
// the IV extracted from the primary payload is used as encryption IV,
// the IV extracted from the secondary payload is used as decryption IV.
val encryptionIV = primaryPayload.copyOfRange(40, 56)
val decryptionIV = secondaryPayload.copyOfRange(32, 48)
val aes = AES(encryptionKey, encryptionIV, decryptionKey, decryptionIV)
val encryptedPayload = aes.encryptCTR(primaryPayload)
return aes to primaryPayload.copyOf(56) + encryptedPayload.copyFrom(56)
}
The AES-CTR Cipher is initialized using
private val encryptionCipher = Cipher.getInstance("AES/CTR/NoPadding")
init {
encryptionCipher.init(
Cipher.ENCRYPT_MODE,
SecretKeySpec(encryptionKey, "AES"),
IvParameterSpec(encryptionIV)
)
}
Using the Abridged transport (non-obfuscated) works fine.
If using the abridged transport works and the obfuscated transport simply never respond it's likely you've missed the padding needed for the padded intermediate transport with obfuscation as described here: https://core.telegram.org/mtproto/mtproto-transports#padded-intermediate