Overgram/lib/src/main/java/io/github/lonamiwebs/overgram/crypto/Authenticator.java

83 lines
3.1 KiB
Java

package io.github.lonamiwebs.overgram.crypto;
import io.github.lonamiwebs.overgram.network.MTProtoPlainSender;
import io.github.lonamiwebs.overgram.tl.Abstract;
import io.github.lonamiwebs.overgram.tl.Functions;
import io.github.lonamiwebs.overgram.tl.Types;
import io.github.lonamiwebs.overgram.utils.RSA;
import io.github.lonamiwebs.overgram.utils.Utils;
import javafx.util.Pair;
import java.io.IOException;
import java.math.BigInteger;
public class Authenticator {
public static void doAuthentication(final MTProtoPlainSender sender) throws IOException, ClassNotFoundException {
final BigInteger nonce = new BigInteger(Utils.randomBytes(16));
final Types.ResPQ resPq = sender.send(
new Functions.ReqPqMulti().nonce(nonce));
if (!resPq.nonce().equals(nonce)) {
throw new SecurityException("Step 1 response nonce invalid");
}
// Note that Java uses big endian, which is correct here
final BigInteger pq = new BigInteger(resPq.pq());
final Pair<BigInteger, BigInteger> pqPair = Utils.factorize(pq);
final BigInteger p = pqPair.getKey();
final BigInteger q = pqPair.getValue();
// Once again, as big endian
final byte[] pBytes = p.toByteArray();
final byte[] qBytes = q.toByteArray();
final BigInteger newNonce = new BigInteger(Utils.randomBytes(32));
final byte[] pqInnerData = new Types.PQInnerData()
.pq(resPq.pq())
.p(pBytes)
.q(qBytes)
.nonce(resPq.nonce())
.serverNonce(resPq.serverNonce())
.newNonce(newNonce)
.serializeToBytes();
byte[] cipherText = null;
long targetFingerprint = 0;
for (final long fingerprint : resPq.serverPublicKeyFingerprints()) {
cipherText = RSA.encrypt(fingerprint, pqInnerData);
if (cipherText != null) {
targetFingerprint = fingerprint;
break;
}
}
if (cipherText == null) {
throw new SecurityException("Step 2 could not find a known RSA key");
}
final Abstract.ServerDHParams abstractDhParams = sender.send(
new Functions.ReqDHParams()
.nonce(resPq.nonce())
.serverNonce(resPq.serverNonce())
.p(pBytes)
.q(qBytes)
.publicKeyFingerprint(targetFingerprint)
.encryptedData(cipherText)
);
if (abstractDhParams instanceof Types.ServerDHParamsFail) {
// We could also check its nonce etc. but not needed
throw new SecurityException("Step 2 generation of DH params failed");
}
final Types.ServerDHParamsOk dhParams = (Types.ServerDHParamsOk) abstractDhParams;
if (!dhParams.nonce().equals(resPq.nonce())) {
throw new SecurityException("Step 2 DH params have invalid nonce");
}
if (!dhParams.serverNonce().equals(resPq.serverNonce())) {
throw new SecurityException("Step 2 DH params have invalid nonce");
}
}
}