Fix utils and create MTProtoPlainSender

This commit is contained in:
Lonami Exo 2018-07-24 00:15:15 +02:00
parent 11755fb266
commit 80ef876d92
8 changed files with 274 additions and 41 deletions

View File

@ -1,14 +1,23 @@
package io.github.lonamiwebs.overgram; package io.github.lonamiwebs.overgram;
import io.github.lonamiwebs.overgram.crypto.Authenticator;
import io.github.lonamiwebs.overgram.network.MTProtoPlainSender;
import io.github.lonamiwebs.overgram.network.connection.Connection; import io.github.lonamiwebs.overgram.network.connection.Connection;
import io.github.lonamiwebs.overgram.network.connection.TcpFull; import io.github.lonamiwebs.overgram.network.connection.TcpFull;
import io.github.lonamiwebs.overgram.utils.BinaryWriter;
import java.io.IOException; import java.io.IOException;
public class Overgram { public class Overgram {
public static void main(final String... args) throws IOException { public static void main(final String... args) throws IOException, ClassNotFoundException {
final Connection connection = new TcpFull(); final Connection connection = new TcpFull();
try {
connection.connect("149.154.167.91", 443); connection.connect("149.154.167.91", 443);
final MTProtoPlainSender sender = new MTProtoPlainSender(connection);
Authenticator.doAuthentication(sender);
} finally {
connection.disconnect(); connection.disconnect();
} }
}
} }

View File

@ -0,0 +1,22 @@
package io.github.lonamiwebs.overgram.crypto;
import io.github.lonamiwebs.overgram.network.MTProtoPlainSender;
import io.github.lonamiwebs.overgram.tl.Functions;
import io.github.lonamiwebs.overgram.tl.TLObject;
import io.github.lonamiwebs.overgram.tl.Types;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
public class Authenticator {
public static void doAuthentication(final MTProtoPlainSender sender) throws IOException, ClassNotFoundException {
final byte[] nonceBytes = new byte[16];
new SecureRandom().nextBytes(nonceBytes);
final BigInteger nonce = new BigInteger(nonceBytes);
final TLObject result = sender.send(new Functions.ReqPqMulti().nonce(nonce));
assert result instanceof Types.ResPQ;
}
}

View File

@ -0,0 +1,57 @@
package io.github.lonamiwebs.overgram.network;
import io.github.lonamiwebs.overgram.network.connection.Connection;
import io.github.lonamiwebs.overgram.tl.TLObject;
import io.github.lonamiwebs.overgram.tl.TLRequest;
import io.github.lonamiwebs.overgram.utils.BinaryReader;
import io.github.lonamiwebs.overgram.utils.BinaryWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MTProtoPlainSender {
private final Connection connection;
private final MTProtoState state;
public MTProtoPlainSender(final Connection connection) {
this.connection = connection;
this.state = new MTProtoState();
}
public TLObject send(final TLRequest request) throws IOException, ClassNotFoundException {
final long msgId = state.getNewMsgId();
final BinaryWriter writer = new BinaryWriter();
request.serialize(writer);
final byte[] requestBytes = writer.toBytes();
writer.clear();
writer.write(0L);
writer.write(msgId);
writer.write(requestBytes.length);
writer.writeRaw(requestBytes);
connection.send(writer.toBytes());
final byte[] body = connection.recv();
if (body[0] == (byte) 0x6c
&& body[1] == (byte) 0xfe
&& body[2] == (byte) 0xff
&& body[3] == (byte) 0xff) {
// -404 as little endian, broken authorization
throw new RuntimeException();
}
final BinaryReader reader = new BinaryReader(ByteBuffer.wrap(body).order(ByteOrder.LITTLE_ENDIAN));
final long authKeyId = reader.readLong();
assert authKeyId == 0;
final long serverMsgId = reader.readLong();
assert serverMsgId != 0;
final int length = reader.readInt();
assert length > 0;
return reader.readTl();
}
}

View File

@ -0,0 +1,64 @@
package io.github.lonamiwebs.overgram.network;
import io.github.lonamiwebs.overgram.tl.TLObject;
import java.security.SecureRandom;
public class MTProtoState {
private final long id;
private long timeOffset;
private int sequence;
private long lastMsgId;
public MTProtoState() {
id = new SecureRandom().nextLong();
timeOffset = 0;
sequence = 0;
lastMsgId = 0;
// TODO auth_key, salt
}
public Object createMessage(final TLObject object, final long afterId) {
throw new UnsupportedOperationException();
}
public static Object calcKey(final Object authKey, final byte[] msgKey, final boolean client) {
throw new UnsupportedOperationException();
}
public byte[] packMessage(final Object message) {
throw new UnsupportedOperationException();
}
public Object unpackMessage(final byte[] body) {
throw new UnsupportedOperationException();
}
public long getNewMsgId() {
final long now = System.currentTimeMillis();
long newMsgId = (((now / 1000) + timeOffset) << 32) | ((now % 1000) << 2);
if (lastMsgId >= newMsgId) {
newMsgId = lastMsgId + 4;
}
lastMsgId = newMsgId;
return newMsgId;
}
public void updateTimeOffset(long correctMsgId) {
final long now = System.currentTimeMillis() / 1000L;
final long correct = correctMsgId >> 32;
timeOffset = correct - now;
lastMsgId = 0;
}
public int getSeqNo(final boolean contentRelated) {
if (contentRelated) {
return 1 + 2 * sequence++;
} else {
return 2 * sequence;
}
}
}

View File

@ -31,8 +31,7 @@ public class TcpFull extends Connection {
public void send(final byte[] data) throws IOException { public void send(final byte[] data) throws IOException {
final int length = data.length + 12; final int length = data.length + 12;
final ByteBuffer buffer = ByteBuffer.allocate(length); final ByteBuffer buffer = ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(length); buffer.putInt(length);
buffer.putInt(counter); buffer.putInt(counter);
@ -48,7 +47,7 @@ public class TcpFull extends Connection {
@Override @Override
public byte[] recv() throws IOException { public byte[] recv() throws IOException {
final ByteBuffer buffer = ByteBuffer.wrap(client.read(8)); final ByteBuffer buffer = ByteBuffer.wrap(client.read(8)).order(ByteOrder.LITTLE_ENDIAN);
final int length = buffer.getInt(); final int length = buffer.getInt();
final int seq = buffer.getInt(); final int seq = buffer.getInt();
final byte[] body = client.read(length - 12); final byte[] body = client.read(length - 12);

View File

@ -6,5 +6,5 @@ import io.github.lonamiwebs.overgram.utils.BinaryWriter;
public abstract class TLObject { public abstract class TLObject {
public abstract void serialize(final BinaryWriter writer); public abstract void serialize(final BinaryWriter writer);
public abstract void deserialize(final BinaryReader reader); public abstract void deserialize(final BinaryReader reader) throws ClassNotFoundException;
} }

View File

@ -1,5 +1,9 @@
package io.github.lonamiwebs.overgram.utils; package io.github.lonamiwebs.overgram.utils;
import io.github.lonamiwebs.overgram.tl.TLObject;
import io.github.lonamiwebs.overgram.tl.Types;
import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
@ -40,6 +44,18 @@ public class BinaryReader {
return result; return result;
} }
public BigInteger readInt128() {
final byte[] bytes = new byte[16];
buffer.get(bytes);
return new BigInteger(bytes);
}
public BigInteger readInt256() {
final byte[] bytes = new byte[32];
buffer.get(bytes);
return new BigInteger(bytes);
}
public double readDouble() { public double readDouble() {
return buffer.getDouble(); return buffer.getDouble();
} }
@ -63,7 +79,9 @@ public class BinaryReader {
final byte[] data = new byte[length]; final byte[] data = new byte[length];
buffer.get(data); buffer.get(data);
buffer.position(buffer.position() + padding); if (padding > 0) {
buffer.position(buffer.position() + 4 - padding);
}
return data; return data;
} }
@ -91,14 +109,16 @@ public class BinaryReader {
return result; return result;
} }
public Object readTl() { public TLObject readTl() throws ClassNotFoundException {
return null; final TLObject object = Types.getFromId(readInt());
object.deserialize(this);
return object;
} }
public List<?> readTlList() { public List<?> readTlList() throws ClassNotFoundException {
buffer.position(buffer.position() + 4); // vector code buffer.position(buffer.position() + 4); // vector code
final int size = buffer.getInt(); final int size = buffer.getInt();
final List<Object> result = new ArrayList<>(size); final List<TLObject> result = new ArrayList<>(size);
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
result.add(readTl()); result.add(readTl());
} }

View File

@ -3,55 +3,100 @@ package io.github.lonamiwebs.overgram.utils;
import io.github.lonamiwebs.overgram.tl.TLObject; import io.github.lonamiwebs.overgram.tl.TLObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class BinaryWriter { public class BinaryWriter {
private final ByteBuffer buffer;
private static final ByteOrder ORDER = ByteOrder.LITTLE_ENDIAN;
private final ByteArrayOutputStream buffer;
public BinaryWriter() { public BinaryWriter() {
buffer = ByteBuffer.allocate(128); buffer = new ByteArrayOutputStream(128);
buffer.order(ByteOrder.LITTLE_ENDIAN);
} }
public void write(final int value) { public void write(final int value) {
buffer.putInt(value); try {
buffer.write(ByteBuffer.allocate(4).order(ORDER).putInt(value).array());
} catch (IOException ignored) {
throw new RuntimeException();
}
} }
public void write(final long value) { public void write(final long value) {
buffer.putLong(value); try {
buffer.write(ByteBuffer.allocate(8).order(ORDER).putLong(value).array());
} catch (IOException ignored) {
throw new RuntimeException();
}
}
public void write(final BigInteger value) {
final byte[] bytes = value.toByteArray();
// It's really unlike an int256 has only 16 bytes so we're good
final int size = bytes.length > 16 ? 32 : 16;
if (ORDER == ByteOrder.BIG_ENDIAN) {
for (int i = 0; i < size; ++i) {
buffer.write(i < bytes.length ? bytes[i] : 0);
}
} else {
for (int i = size; i-- != 0; ) {
buffer.write(i < bytes.length ? bytes[i] : 0);
}
}
} }
public void write(final double value) { public void write(final double value) {
buffer.putDouble(value); try {
buffer.write(ByteBuffer.allocate(8).order(ORDER).putDouble(value).array());
} catch (IOException ignored) {
throw new RuntimeException();
}
} }
public void write(final boolean value) { public void write(final boolean value) {
buffer.putInt(value ? 0x997275b5 : 0xbc799737); write(value ? 0x997275b5 : 0xbc799737);
} }
public void write(final byte[] bytes) { public void write(final byte[] bytes) {
try {
int padding; int padding;
if (bytes.length < 0xfe) { if (bytes.length < 0xfe) {
padding = (bytes.length + 1) % 4; padding = (bytes.length + 1) % 4;
buffer.put((byte) bytes.length); buffer.write(bytes.length);
buffer.put(bytes); buffer.write(bytes);
} else { } else {
padding = bytes.length % 4; padding = bytes.length % 4;
buffer.putInt(bytes.length << 8 | 0xfe); write(bytes.length << 8 | 0xfe);
buffer.put(bytes); buffer.write(bytes);
} }
if (padding != 0) { if (padding != 0) {
for (padding = 4 - padding; padding-- != 0; ) { for (padding = 4 - padding; padding-- != 0; ) {
buffer.put((byte) 0); buffer.write(0);
} }
} }
} catch (IOException ignored) {
throw new RuntimeException();
}
}
public void writeRaw(final byte[] bytes) {
try {
buffer.write(bytes);
} catch (IOException ignored) {
}
} }
public void write(final String string) { public void write(final String string) {
buffer.put(StandardCharsets.UTF_8.encode(string)); write(StandardCharsets.UTF_8.encode(string).array());
} }
// TODO Handle boxed vs unboxed types (and vector<>) // TODO Handle boxed vs unboxed types (and vector<>)
@ -88,4 +133,21 @@ public class BinaryWriter {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
public void clear() {
buffer.reset();
}
public int size() {
return buffer.size();
}
public byte[] toBytes() {
return buffer.toByteArray();
}
@Override
public String toString() {
return Arrays.toString(toBytes());
}
} }